whaletv-workbench
WhaleTV 工作台 —— DeepSeek Harness(dsh)的站外 Web 插件:把日常工作的网页、文档、应用、技能集中到一个面板,一键跳转 / 打开 / 使用,减少来回切换场景的时间;并内置点击即用的自更新(git pull → 重建 → 热注入),项目更新后用户点一下即可完成更新并重新插入 harness,无需重装。
特性
- 零侵入安装:不改动 deepseek-harness 源码,通过 dsh 官方的 profile 用户层(
$DSH_HOME/profiles/web)安装;安装/卸载改变 bundle 层栈,重启 dsh web 生效(托盘 → 重启 Web 服务);插件运行期的自更新仍是clientModules.rebuilt热注入,无需重启。 - 侧边栏入口:侧边栏底部(设置旁)新增「WhaleTV 工作台」入口(工作台图标 + 文案;折叠为窄栏时只显示图标)。
- 工作台面板(
shell.overlay悬浮层):- 分组卡片:网页 / 文档 / 应用 / 技能,支持搜索过滤;
- 网页 → 新标签页打开;应用/文件 → 调用宿主系统默认程序打开;
- 技能 → 一键复制提示词 + 新建会话,把任务直接交给对应 skill;
- 未配置的条目显示「待配置」徽标,按钮禁用;
- 面板内编辑:右上角「编辑」进入编辑模式,直接增删改条目与分组(名称 / 描述 / 类型 url·path·prompt / 目标值),保存即写回配置并刷新,无需手工编辑 JSON。
- 工作台技能专区(对接
ctx.skills):面板底部列出所有 dsh 已发现的技能(SkillRegistry.snapshot()),显示来源徽标。- 自带 SkillProvider:工作台在
ctx.skills.registerProvider上注册一个 fallback provider(rank 450),主动扫$DSH_HOME/skills——即使 dsh 官方 provider 出问题,"工作台技能"仍能列出安装的技能。 - 「使用」:把
/<技能名>直接填进当前会话的输入框(走 dsh 的/技能触发器,ctx.conversation.input.for(scope).setDraft),并关闭工作台面板,由你确认后回车发送——在当前会话里直接引用该 skill,而不是新开会话。未打开任何会话时提示先打开/新建会话。 - 「+ 新建技能」两种模式:
- 手写正文:填入 name / description / 正文 → 自动包 YAML frontmatter 写到
$DSH_HOME/skills/<name>/SKILL.md。 - 从 Git 仓库导入:URL + 可选 ref + 可选子路径 → 浅克隆到 staging → 自动识别三种形态:bundle(含 SKILL.md 的目录,连同 assets 一起复制)/ flat(单
.md)/ batch(parent 目录下多个<child>/SKILL.md,一次装全)。
- 手写正文:填入 name / description / 正文 → 自动包 YAML frontmatter 写到
- 生命周期管理(v0.8.20):已装技能可停用 / 启用(文件移出技能根目录到停放区,dsh 与模型都不再看到,随时启用移回)与卸载(移入回收站,可随时恢复,或彻底删除——唯一的硬删路径,走危险色确认弹窗)。项目/agent/内置技能保持只读。
- 诊断:
GET /whaletv/workbench/skills/debug返回本地$DSH_HOME/skills路径、目录内容、相关环境变量、dsh 官方注册表 snapshot——用来定位"文件在盘上但面板看不到"的问题。
- 自带 SkillProvider:工作台在
- 偏好设置:dsh ≥ 0.1.7 把插件的
Configschema(updateRepo,声明为 volatile)直接投影成 Plugins 设置页的在线表单,无需自研卡片;插件的自持数据(技能版本记录installed-skills.json、跳过的更新头update-state.json)放在$DSH_HOME/whaletv-workbench/自己的 JSON 文档里,不占 settings 文档。 - 检查更新 / 一键更新(按安装形态分流):git 检出安装——「检查更新」执行
git fetch+ ahead/behind 对比并展示新提交列表,「更新」执行git pull --ff-only→pnpm install+ 重建 bundle →clientModules.rebuilt热注入,附提交级跳过/回滚;tarball 安装(桌面端 /plugin add github:)——对比本地与更新仓库的版本号,「更新」在 profile 目录执行pnpm add github:<repo>在线装新版本(v0.8.1 起直连 pnpm,绕开插件管理器的误报),重启生效。版本探测走 raw / jsDelivr / GitHub API 三源回退链。 - 更新闭环(v0.8.9):更新过程实时显示阶段(拉取 → 安装依赖 → 重建 bundle → 热注入,tarball 为
pnpm add,含已耗时秒数);服务端代码变更时结果里带needRestart提示,请手动重启 dsh web(托盘菜单 → 重启 Web 服务)后生效;底部可展开完整更新历史(时间 / 成功失败 / before→after / 是否需重启 / 错误)。 - 工作台图标:
assets/workbench.svg(品牌红面板网格)构建时内嵌为 SVG data URL,矢量缩放不因压缩模糊,用于入口图标与面板头部(scripts/gen-icon.mjs)。
架构
双面插件(与 harness 内 ui-workspace 等 client 包同构):
┌─ Host 半(Node)──────────────────────────────────────────────┐
│ src/index.ts │
│ GET /whaletv/workbench/state 版本 / git 状态 / 条目配置 │
│ POST /whaletv/workbench/config 校验并保存条目配置 │
│ POST /whaletv/workbench/update git pull + pnpm install │
│ + pnpm run bundle │
│ + clientModules.rebuilt() │
│ GET /whaletv/workbench/update/progress 更新阶段(轮询) │
└────────────────────────────────────────────────────────────────┘
┌─ Browser 半(client bundle,dsh.client 扫描进 __DSH_BOOT__)───┐
│ src/client/index.ts │
│ sidebar.footer.action → SidebarEntry(入口按钮) │
│ shell.overlay → WorkbenchPanel(工作台面板) │
│ 共享 createWorkbenchStore(面板开关 / 搜索 / 更新状态) │
└────────────────────────────────────────────────────────────────┘
依赖解析与 harness 官方站外插件路径一致:安装时 dsh 启动器已把内置包平铺到 $DSH_HOME/profiles/node_modules,本项目用 scripts/link-harness-deps.mjs 将该回退镜像为 node_modules junction(幂等)。dsh 0.1.2 起该回退只修复"当前安装代"的包,被移除/改名的历史包会留下悬挂 junction——脚本会清理它们,并直接从 harness checkout(DSH_HARNESS_ROOT 或同级 ../deepseek-harness)补链 peerDependencies 里缺失的包。
安装
前置:Node ≥ 22、pnpm、dsh 已运行过一次(生成 $DSH_HOME/profiles/web)。
推荐:官方 dsh plugin CLI(一句话)
本包 package.json 声明了 dsh.bundle.patch(指向根目录 cordis.patch.yml),因此 dsh plugin add 会自动把它作为 bundle 挂到 dsh.profile.bundles 层栈里。
从本地目录安装(推荐用于开发):
# lib/ 已随仓库提交;改了源码才需要重新构建
pnpm install; pnpm run bundle
# 一键安装
dsh plugin --profile web add e:\DeepSeek\whaletv-workbench
从 GitHub 直装(推荐用于用户侧):
dsh plugin --profile web add github:KK-Irving/whaletv-workbench
lib/ 产物已随仓库提交(0.7.2 起),安装包不含任何构建脚本——不会触发 pnpm 的 allowBuilds 门,也不需要现场编译,任何 profile 开箱即装。
桌面端(Electron)安装:profiles/desktop 由桌面应用独占管理(CLI 会对该 profile 报 is managed exclusively by the Electron application),安装入口在桌面端的插件管理界面,同样开箱即装、无需任何 allowlist 配置。
一句话卸载:
dsh plugin --profile web remove whaletv-workbench
安装 / 卸载后重启 dsh web(托盘菜单 → 重启 Web 服务)再刷新 http://127.0.0.1:3080。bundle 层栈写在 profile 的 package.json,当前 dsh 只对用户 patch 层(cordis.patch.yml)做热重载。
备选:自研 install-profile.mjs(旧版兼容)
无 dsh CLI,或希望复用 link-harness-deps + 一键把 profile 装成 link: 协议时可用。它做的事等价于 dsh plugin add ./checkout + link-harness-deps,但走的是直接编辑 profile 的 package.json 与 cordis.patch.yml:
pnpm install; pnpm run bundle
node scripts/install-profile.mjs web
卸载:从 $DSH_HOME/profiles/web/cordis.patch.yml 删除对应 insert 行,从 $DSH_HOME/profiles/web/package.json 移除依赖后 pnpm install。
配置工作台条目
直接在面板里改:打开工作台 → 右上角「编辑」进入编辑模式:
- 条目:每个卡片出现「编辑 / 删除」按钮;分组头部出现「重命名 / + 条目 / 删除分组」;底部「+ 新建分组」。
- 编辑表单:名称(必填)、描述、目标类型(网页 URL / 本机路径 / 技能提示词)、目标值。保存立即写回配置并刷新面板。
配置文件写入 $DSH_HOME/whaletv-workbench/workbench.json(本地用户文件,不入 git,由面板自动创建)。仓库里只带模板 config/workbench.example.json:首次读取没有用户文件时自动使用模板,第一次在面板里保存后生成自己的配置。因为配置存到 $DSH_HOME 而不是插件目录,「面板改配置」与「一键更新」的 git pull 永不冲突。旧版把 workbench.json 存在插件目录内的用户会在下次读取时被自动迁移到新路径。
每条目的动作由字段决定(三者取一):url → 新标签页打开网页(在 dsh 桌面客户端(Electron,需 v2.0.6+ 且带 dsh-tab 处理逻辑)内作为客户端内新标签页打开;普通浏览器环境下则在默认浏览器新标签页打开);path → 宿主默认程序打开本地文件/应用;prompt → 技能(复制提示词 + 新建会话)。都为空则显示「待配置」。
一键更新
- 本目录已是 git 仓库,只需关联远程:
git remote add origin <仓库地址>并推送(未配置远程时点击更新会给出明确提示)。 - 面板顶部「更新」→
git pull --ff-only;有提交时自动pnpm install+ 重建 bundle + 热注入。 - 更新日志展示在面板底部;
needRestart提示出现时(服务端代码变更),重启dsh web后生效。
目录结构
├── package.json # dsh.bundle.patch + dsh.client 声明 + 构建脚本
├── AGENTS.md # 跨 Agent 仓库协作规则(架构边界 / 安全不变量 / 变更纪律)
├── cordis.patch.yml # Bundle patch layer(`dsh plugin add` 自动挂载)
├── tsdown.config.ts # 双面构建(Host lib + 浏览器 closure-factory bundle)
├── lib/ # 构建产物(随仓库分发,零构建安装)
├── config/workbench.example.json # 条目模板(面板保存后生成本地 workbench.json)
├── LICENSE # MIT
├── CHANGELOG.md # 版本日志(每次发版一条条目)
├── assets/workbench.svg # 工作台图标(矢量,构建时内嵌)
├── .github/ISSUE_TEMPLATE/ # Issue 表单(bug / feature)
├── docs/
│ ├── DESIGN.md # 设计与 UI 规划
│ ├── ROADMAP.md # 路线图与执行状态
│ └── adr/ # 架构决策记录(0001 配置落 $DSH_HOME / 0002 provider rank)
├── scripts/
│ ├── gen-icon.mjs # 图标 SVG → data URL 模块
│ ├── link-harness-deps.mjs # 镜像 harness 依赖 junction(跨版本容错)
│ ├── check-version.mjs # 版本三处一致性校验
│ ├── check-lib-drift.mjs # lib/ 漂移门禁(提交的产物必须等于一次全新构建)
│ ├── make-release-notes.mjs # 发版说明生成(兼容 dsh 行 + CHANGELOG 摘录 + SHA256SUMS)
│ ├── install-profile.mjs # 一键安装到 dsh web profile
│ ├── unit.mjs # 纯函数单测(node --test,免构建 / 免 peer)
│ ├── smoke-client.mjs # client bundle 冒烟测试
│ ├── smoke-apply.mjs # apply() 注册契约测试
│ └── smoke-host.mjs # Host 路由 + 安全边界测试
├── eslint.config.mjs # 最小 lint 门禁(ts + react-hooks)
└── src/
├── index.ts # Host 入口:Config + apply + 路由分发
├── host-plumbing.ts # 共享管道(exec/git/JSON/路径)
├── semver.ts # 版本比较纯函数(零依赖叶子,供单测直接导入)
├── update.ts # 更新域(git + tarball 双通道 + 历史 + 回滚)
├── skills.ts # 技能域(provider + 导入 + 版本记录)
├── extras.ts # 附加域(最近使用 + 可达性 + favicon)
├── shared.ts # 双面共享 wire 类型
└── client/
├── index.ts # Browser 入口(slot 注册 + inject 面)
├── WorkbenchPanel.tsx # 面板壳:store 读取 + hook 编排 + JSX
├── panel/ # 面板领域拆分(0.8.16)
│ ├── entry-helpers.ts # 条目目标解析/草稿/重排纯函数
│ ├── notices.ts # 通知窗口/热注入存活标记
│ ├── ItemForm.tsx / ItemCard.tsx / RecentBar.tsx
│ ├── UpdateCheckBanner.tsx / ConfirmDialog.tsx
│ ├── usePanelData.ts # 数据加载 + 可达性
│ ├── useUpdateFlow.ts # 更新流 + 进度轮询 + 通知自动关闭
│ └── useEntriesEditing.ts# 编辑态 + 持久化 + 拖拽/键盘重排
├── SkillsSection.tsx # 技能专区(安装/导入/检查更新/生命周期)
├── SidebarEntry.tsx # 侧边栏入口
├── net.ts # 在途请求注册表(面板卸载统一 abort)
├── store.ts # 共享 store(面板开关/搜索/更新状态)
├── contract.ts # inject 面类型
└── icon.ts # 图标 data URL
已知边界(初版)
- 配置为面板可编辑的本地 JSON 文件(
$DSH_HOME/whaletv-workbench/workbench.json);改动立即生效,无需重建。 - 浏览器侧更新依赖 dev 模式的 HMR(
pnpm run dev:web)自动刷新;生产模式更新后手动刷新页面一次。 - Host 半刻意保持轻量稳定,使绝大多数更新只需重建 client bundle;服务端变更需重启 dsh。
安全说明
静态扫描会在以下位置标注告警,均为本插件核心能力所必需,且已按安全模式实现:
src/index.ts/scripts/install-profile.mjs使用child_process:为「一键自更新」流水线所需(git pull→pnpm install→pnpm run bundle→ 热注入)。全部使用execFile/execFileSync+ 数组参数 + 硬编码命令,不拼接用户输入,无 shell 注入路径。Windows 上对pnpm/npm启用shell: true是因为它们是.cmdshim,此时参数亦全部硬编码。- HTTP 路由
/whaletv/workbench/*:由 dsh 的webServer挂载在一个kind: 'prefix'位上(内部按子路径分发),默认仅绑定127.0.0.1(同源);POST /config的 JSON body 只用于写workbench.json,经白名单校验(字段白名单、id 唯一、条目/分组数量上限、512KB body 上限),不进入任何 exec 参数。POST /skills/install只写$DSH_HOME/skills/<name>/SKILL.md(对 name 做 kebab-case 强校验,skill之类的保留字直接拒),不接受任意路径;POST /skills/import只克隆到$DSH_HOME/whaletv-workbench/.staging/的临时目录再复制到$DSH_HOME/skills/,URL 通过白名单(仅http(s)/ssh,拒绝file://),子路径拒..;POST /session/followup只调agent.followup()(模型输入通道,不执行代码)。 - Git 子进程:所有
execFile传给git/pnpm的 env 都会剥掉两类噪音:pnpm 11 的NPM_CONFIG_MANAGE_PACKAGE_MANAGER_VERSIONS(避免 npm warn)与交互式凭据环境(GIT_TERMINAL_PROMPT=0+GCM_INTERACTIVE=Never,确保私有仓库鉴权失败时快速报错而不是挂在等 tty)。 src/client/index.tswindow.open:使用_blank+noopener,noreferrer(阻断新页面对 opener 的引用)。在 dsh 桌面客户端内追加dsh-tab窗口 feature 标记,由客户端的 window-open 处理器把 URL 收进客户端自己的标签页(不改变 sandbox 边界,只是「在哪个浏览器里开」的路由约定)。process.env读取:仅DSH_HOME(安装目录定位)与NODE_ENV(构建期常量)两处,标准用法。
Learn more
本项目对齐了 DeepSeek Harness 官方规范;深入了解各契约与 API 的原始文档:
- Package and install —
dsh.bundle.patch与dsh plugin add的入库机制(本项目cordis.patch.yml的来源) - CLI behavior reference — profile boot / plugin management / web alias / 层叠优先级
- Your first plugin —
apply(ctx)与ctx.effect/inject契约(本项目 Host 半基础) - Skills subsystem —
ctx.skills.registerProvider/ SkillCandidate / SkillDefinition / discovery rank(工作台自带 fallback provider 背后的 API) - User Settings + Adding a settings card —
ctx.settings命名空间 + Web 设置页卡片 - Web server —
ctx.webServer的 exact / prefix 路由匹配(本项目所有 HTTP 路由) - Client modules —
dsh.client声明与WebBootGraph(浏览器半自动发现的机制)
版本
当前版本 v0.8.20(对齐 dsh 0.2.0-rc.2)。每次发版的变更详见 CHANGELOG.md。面板顶部会显示实际运行的版本号(读自 package.json),跟这里对齐即可(pnpm run check:version 会在冒烟链里校验三处一致)。
License
MIT — 详见 LICENSE。
No comments yet. Be the first to write one.