DeepSeek Harness 插件

tcgbp/dock-flash

Star 数 ★ 0 分类 UI 增强 收录于 2026-09-20

DSH Web 的快捷控制面板:可停靠或浮动,开关项可由其它插件通过 quickControl 服务注册;内置主题/皮肤切换与细粒度 NO_PROXY 代理控制,可搭配 dock-base 或独立运行。

安装

# Release 预构建包

dsh plugin --profile web add "https://github.com/tcgbp/dock-flash/releases/latest/download/dock-flash.tgz"

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:tcgbp/dock-flash

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWEDERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yamlallowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

DSH 快捷控制插件 —— 可独立运行或配合 dock-base 使用。点击⚡图标打开快捷控制悬浮窗口,支持其他插件动态注册快捷开关

运行模式

模式 条件 界面 可用开关
工作台模式 已安装 dock-base ⚡ 图标在活动栏 → 侧边栏/浮窗面板 外观 + 系统开关
独立模式 未安装 dock-base ⚡ 触发按钮注入所选会话槽位(默认:输入框右侧)→ 浮动面板 外观 + 布局 + 系统

功能

内置开关

内置开关按外观 / 布局 / 系统三组分类显示(紧凑双列布局)。其中「布局」组仅独立模式会出现:

分组 开关 类型 说明 独立模式
🎨 外观 外观 select 浅色 / 深色 / 跟随系统,切换 DSH 全局外观(含 wxj-black-hole 主题冲突重试机制)
🎨 外观 皮肤 select 动态扫描已安装皮肤插件并切换(需要 dsh-market)
🎨 外观 时间线移到左侧 toggle 把 DSH 自带的轮次导航栏从右侧挪到左侧。仅当屏幕上显示的是 DSH 自己的轨、且没有别的 timeline 插件接管时才出现
🎨 外观 全屏 toggle 浏览器 Fullscreen API,全屏/退出全屏
🎨 外观 日志下载按钮 toggle 显示/隐藏会话日志下载按钮
📐 布局 失焦关闭 buttongroup 关闭 / 开启,点击面板外部时自动关闭浮窗。标题栏上也有同一开关(两种模式)
📐 布局 触发位置 select 输入框左 / 输入框右 / 会话标题栏操作区 / 会话标题栏工具区 —— 独立模式 ⚡ 触发按钮的位置;或「对话区右上角」—— 浮在对话区内的按钮,可拖动到区内任意位置
⚙️ 系统 语言 buttongroup 中文 / English,切换 DSH 全局 UI 语言
⚙️ 系统 系统代理 select 全部代理 / 仅 API 绕过 / 全部绕过 / 自定义 — 细粒度 NO_PROXY 控制。自定义会校验取值:主机名 / 域名后缀 / IP / 主机:端口,逗号或空格分隔;空值与 CIDR 会被拒绝
⚙️ 系统 测试 URL select Google 204 / GitHub / DeepSeek API / 自定义 —— 测试连接所探测的地址,并在下拉框下方单独一行显示当前生效的 URL(自定义地址因此可见)。归属代理簇,可折叠收起
⚙️ 系统 诊断日志 log 只读多行日志,显示最近一次测试结果 —— 一行一件事。按下「测试连接」的瞬间即出现,首行就是正在尝试的地址,随后被完整报告替换(从不追加);点 ✕ 清空,不受 30 秒 TTL 限制

「布局」分组只在独立模式出现。 集成模式下 close-on-blur 只以面板标题栏按钮的形式存在,因此没有任何内置开关带 group: 'layout',该分类会被整体跳过。

停靠布局不在这里配置。 停靠边、自动隐藏、保留空间、图标缩放都是 dock-base 自己的设置项 —— dock-flash 有意不重复提供。布局分组里 dock-flash 只拥有 trigger-positionclose-on-blur,两者都仅独立模式存在。

「时间线移到左侧」只在屏幕上显示的是 DSH 自带那条轨时才出现。 没有会话、会话还没有轮次、被 DSH 自己的 @container (width<=900px) 规则隐藏、或已被别的 timeline 插件接管时都不显示 —— dsh-codex-timeline 是原地增强原生轨,屏幕上那条属于它,dock-flash 不与它争同一个位置。浏览器控制台执行 __dockFlashTurnRail() 可打印判定结果与原因。

核心能力

  • 🧩 动态发现 — 其他插件通过 quickControl 服务注册自己的快捷开关,面板自动渲染
  • 🌐 国际化 — 完整的中英文本地化,自动跟随 DSH 语言设置(通过 <html lang> MutationObserver 实时同步)
  • 🎨 皮肤系统 — 多层发现 + 分类切换(CSS / 托管 / 排除)
  • 🛡️ 错误边界 — 所有面板组件包裹在 PanelErrorBoundary 中,防止渲染错误崩溃整个 dock-base WorkbenchRoot
  • 🔌 独立运行 — 无需 dock-base 即可运行:⚡ 触发按钮注入到所选会话槽位,点击展开悬浮面板
  • 📝 最近修改 — 自动记录开关操作(30 秒 TTL),以 "旧值 → 新值" 格式展示
  • 🩺 连接诊断 — 只读多行日志,显示最近一次代理测试(链路走向、重定向链、耗时、响应体大小、socket 错误码),一行一件事。按下按钮的瞬间即出现(首行是正在尝试的地址),答案返回后被完整报告替换,从不追加
  • 🧮 面板排序与隐藏 — 工作台与扩展页签头部的 ⇅ 图标进入排序模式:用 ▲▼ 调整分组与开关的先后,结果保存在 DSH 配置里,换浏览器或换机器都会跟着走。旁边的 ◉ 图标进入显示/隐藏模式:每一行都有一个 ●/○ 勾选框,取消勾选就把不用的开关从面板上收起,但它仍列在这里,随时可以放回来。两个配置页都会列出全部已注册控件,包括插件当前自行停用的那些 —— DSH 轨道不在时的「时间线靠左」、没配代理时的代理探测 —— 这些行会灰显并在悬停时说明原因,所以一时用不上的控件同样可以排序或隐藏。两个模式互斥 —— 进入一个另一个的入口就不再显示 —— 且各自有独立的 ↺ 重置:恢复顺序不会把隐藏的行放出来,恢复显示也不会打乱你的顺序。声明了 cluster 的开关整簇作为一个单位移动、簇内顺序固定,并渲染成一张卡片、成员可折叠 —— 系统代理那组就是动因:它们要配置了代理才起作用

Host 端功能

src/index.ts(Host 半)提供:

  • 注册 dock-flash 设置命名空间(proxyMode 字符串 + customNoProxy 字符串 + testUrl 字符串)
  • 监听代理模式变更,通过 @deepseek-ai/dsh-http-proxy 重新安装 undici 全局 dispatcher,使出站 fetch() 请求遵循用户设定的 NO_PROXY 规则
  • 提供 HTTP 路由:
    • GET /plugins/dock-flash/proxy-status — 返回当前 proxyModecustomNoProxytestUrl 及实际 NO_PROXY 环境变量值
    • POST /plugins/dock-flash/test-connection — 执行诊断式连通性探测;可选用 { "url": "..." } 请求体覆盖已存目标
连接诊断

POST /plugins/dock-flash/test-connection 返回的是一份结构化诊断报告,而不是简单的成功/失败:

字段 含义
proxy { mode, noProxy, httpProxy, proxied, routeError } —— dsh-http-proxy 会如何路由这个具体 URL
redirects 重定向链,逐跳手动跟随(redirect: 'manual')记录;超出上限时 redirectLimitHit 为真
status / statusText 最终响应状态 —— 只要能拿到 HTTP 响应就算 ok,因为它已经证明网络通路是通的
headersMs / bodyMs / elapsedMs 响应头耗时、响应体耗时、总耗时
bodyBytes / bodySnippet 响应体大小,以及文本型响应体前 200 字节 —— 企业代理自己的「已拦截」页面就出现在这里
error { name, message, code, causeName, causeMessage, causeCode, causeErrno } —— 内层 undici cause 才携带 ENOTFOUNDECONNREFUSEDUND_ERR_CONNECT_TIMEOUTDEPTH_ZERO_SELF_SIGNED_CERT 等真实原因

面板把这份报告渲染成 诊断日志 区块,一行一件事。测试一开始该区块就出现(首行就是正在尝试的地址),并且只保留最近一次:每次测试都整体替换而非追加 —— 面板很短,堆积的历史会埋掉你刚问的那一次。

测试目标是一个设置项,绝不是一个常量。 testUrl 默认 https://www.google.com/generate_204,保存在磁盘上的 DSH 配置里,因此内网地址可以配置而不会出现在本仓库中。

代理模式选项
模式 NO_PROXY 效果
全部代理 (移除) 所有流量走系统代理
仅 API 绕过 api.deepseek.com,chat.deepseek.com DeepSeek API 请求绕过代理
全部绕过 * 所有流量绕过代理(直连)
自定义 (用户自定义) 用户通过输入框指定 NO_PROXY 值
代理作用范围

此设置仅影响 DSH 进程内的 fetch() 请求。

  • 受影响:Node.js 内置 fetch()(undici)、DSH API 调用、MCP HTTP 传输层、pi-ai provider 及所有经过 globalThis.fetch 的 SDK
  • 不受影响:通过 node:http/node:https 模块发出的请求(如 OTLP 遥测)、自建传输层的 SDK(如 E2B)、操作系统的其他应用程序、浏览器或其他终端会话
  • 此设置在 Windows、macOS、Linux 上行为一致 — 修改的是 process.env 和 undici 全局 dispatcher,均为 Node.js 抽象层,无操作系统差异

结构

src/index.ts      HOST 半 — 设置命名空间 + 代理模式 + 连接测试(tsc → dist/)
lib/client.js     BROWSER 半 — quickControl 注册表 + 动态面板 + 皮肤系统 + i18n
cordis.patch.yml  bundle layer — 将宿主行插入 profile

插件契约

安装 dock-base 时,本插件遵循 dock-base 插件契约,通过 ctx.workbench 方法调用协作:

注册项 API 说明
侧边栏面板 ctx.workbench.registerPanel() 快捷控制面板(sideBar 区域)
活动栏图标 ctx.workbench.registerActivityBarItem() 闪电图标⚡,点击打开侧边栏
编辑器视图 ctx.workbench.registerEditorView() 快捷控制面板(可拖出为浮动窗口)
命令 ctx.workbench.registerCommand() dock-flash:openQuickControl 命令
快捷开关注册表 ctx.provide('quickControl', registry) 供其他插件注册开关

未安装 dock-base 时,dock-flash 自动进入独立模式:直接在 DOM 中注入悬浮⚡触发按钮和弹出面板,提供所有非布局类开关。


🧩 动态发现 API

本插件发布 quickControl 服务到 WorkbenchContext。其他插件通过 ctx.get('quickControl') 获取注册表并注册自己的快捷开关。

开关类型

类型 说明 必需字段
toggle 布尔开关 getValue(), setValue(boolean)
slider 数值滑块 getValue(), setValue(number), min, max, step
select 下拉选择 getValue(), setValue(any), options
buttongroup 按钮组 getValue(), setValue(any), options
action 操作按钮 run()

QuickSwitchDefinition

interface QuickSwitchOption {
  label: string | (() => string)
  value: any
}

interface QuickSwitchDefinition {
  /** 全局唯一 id,建议用 "插件名:开关名" 格式,如 "dock-git:show-stash" */
  id: string
  /** 显示标签(支持函数式 i18n) */
  label: string | (() => string)
  /** 图标(emoji 或文字) */
  icon?: string
  /** 开关类型 */
  type: 'toggle' | 'slider' | 'select' | 'buttongroup' | 'action'
  /** 排序权重(升序),内置项用 10-60,建议从 100 开始 */
  order?: number
  /** 内置分组:'appearance' | 'layout' | 'system'(仅 dock-flash:* 内置项有效,第三方开关忽略此字段) */
  group?: string
  /**
   * 可选的簇标签。共享同一标签的开关会被画成一张卡片,并作为一个单位排序
   * (共用一组 ▲▼),簇内顺序固定为各自的 `order` 值。卡片默认只显示首行、
   * 处于折叠状态,成员可由用户用卡片底部居中的 ▼/▲ 展开 —— 簇不会自行消失,因此面板结构与已保存的
   * 排序不会因为条件变化而改变。
   */
  cluster?: string

  // ── toggle / slider / select / buttongroup 通用 ──
  getValue?: () => any
  setValue?: (value: any) => void

  // ── slider 专用 ──
  min?: number
  max?: number
  step?: number
  formatLabel?: (value: number) => string

  // ── select / buttongroup 专用 ──
  options?: QuickSwitchOption[] | (() => QuickSwitchOption[])

  // ── action 专用 ──
  run?: () => void | Promise<void>
  actionLabel?: string
  /** 去掉标题列、按钮占满整行(按钮自带文案时用) */
  hideLabel?: boolean
}

注册示例

// 在其他插件的 client.js factory 中:
exports.inject = ['quickControl']   // ← 声明服务依赖

exports.apply = function (ctx) {
  const registry = ctx.get('quickControl')

  ctx.effect(() => {
    const dispose = registry.registerSwitch({
      id: 'dock-git:show-stash',
      label: 'Show Stash',
      icon: '📦',
      type: 'toggle',
      order: 100,
      getValue: () => myGitState.showStash,
      setValue: (v) => { myGitState.showStash = v },  // 只更新状态,面板自动处理 UI
    })
    return dispose  // 卸载时自动反注册
  }, 'dock-git: quick-control switch')
}

服务依赖声明

第三方插件必须声明对 quickControl 服务的依赖,确保 dock-flash 先完成注册再调用 apply()。在 client half 中添加 exports.inject

// client.js — factory 内
exports.inject = ['quickControl']   // ← 必须声明,否则服务可能尚未就绪
exports.apply = function (ctx) {
  const registry = ctx.get('quickControl')  // 服务已就绪,无需 null 检查
  // …
}

同时在 package.jsondsh.client.inject 中声明模块级依赖,确保 dock-flash 的 client 脚本先于本插件加载:

{
  "dsh": {
    "client": {
      "inject": ["@deepseek-ai/dsh-client-runtime", "dock-flash/client"]
    }
  }
}

动态值更新

如果开关的值在面板外部发生变化(如定时器、服务器推送、其他 UI 操作),调用 registry.notifyChange(id) 触发面板刷新:

const registry = ctx.get('quickControl')
// 某个异步事件导致值变了
myGitState.showStash = true
registry?.notifyChange('dock-git:show-stash')

注意:用户在面板中操作开关时,dock-flash 会自动刷新该开关的 UI,无需手动调用 notifyChange。此方法仅在面板感知不到的外部值变化时使用。

变更日志

面板自动为每次用户交互(点击 toggle、拖动 slider、选择 option、点击 buttongroup / action 按钮)记录变更日志,在"最近修改"选项卡中展示(30 秒自动过期)。

插件不需要手动调用 _notifyChange。面板在渲染每个开关时已注入 _notifyChange 方法并在用户交互时自动调用。setValue() 只需更新内部状态:

// ✅ 正确:setValue 只更新状态
setValue: (v) => { myState = v }

// ❌ 错误:不要在 setValue 中调用 _notifyChange(会产生重复记录)
setValue: (v) => { myState = v; sw._notifyChange?.('旧', '新') }

_notifyChange(oldDisplay, newDisplay) 仅在插件主动改变状态(非用户面板操作)且需要记录变更日志时使用,例如:

// 定时器自动切换深色模式
setTimeout(() => {
  state.darkMode = true
  const sw = registry.getSwitches().find(s => s.id === 'my-plugin:auto-dark')
  sw?._notifyChange('浅色', '深色')
}, 3600000)

分组规则

面板采用可折叠选项卡布局:

  • ⚡ 工作台 — 内置开关(id 以 dock-flash: 开头),按 group 字段分为外观 / 布局 / 系统三个子组。集成模式下「布局」子组为空、不渲染 —— 停靠布局属性全部由 dock-base 自己的设置负责。
  • 🧩 扩展 — 第三方开关(id 不以 dock-flash: 开头),按 id 冒号前缀(即插件名)自动分子组
  • 📝 最近修改 — 最近 30 秒内的开关变更记录

判定规则:id.startsWith('dock-flash:') 为内置,否则为第三方。第三方开关的 group 字段在当前版本中被忽略,统一归入扩展标签页按来源插件分组。

同组内按 order 升序排列。

quickControl 服务 API

方法 说明
registerSwitch(def) 注册开关,返回 dispose 函数
unregisterSwitch(id) 按 id 反注册
getSwitches() 获取所有开关(按 order 升序)
notifyChange(id) 通知开关值已变化,触发面板刷新
recordChange(entry) 记录一条变更日志 { id, label, icon, oldDisplay, newDisplay }
getChangelog() 获取最近 30 秒内的变更记录
subscribe(fn) 订阅注册表/值变化,返回 dispose 函数
version 当前注册表版本号(每次变更递增)

🎨 皮肤系统

皮肤切换器需要 dsh-market 插件才能使用 — 没有市场插件时不显示(禁用态皮肤对 DOM 扫描不可见,列表会不完整)。

皮肤分类

分类 说明 示例
CSS 皮肤 通过 <style> / <link> 标签 + body 属性激活 maid-atelier、official-homepage
托管皮肤 自带 canvas/WebGL/粒子等生命周期,通过 mount() / unmount() 切换 Mineradio
排除项 匹配发现规则但从下拉菜单中隐藏(冲突或外部控制不可靠) bloom-theme、black-hole、theme-manager

dsh-market 集成

  • 获取已安装/禁用态的完整主题列表(/dsh-market/installed API)
  • 市场管理的主题通过 /dsh-market/use-skin API 激活(会触发页面刷新)
  • 市场禁用的主题在下拉菜单中标注「未启用」

偏好持久化

皮肤选择、面板排序与独立模式的触发位置都保存在宿主设置命名空间(配置目录 settings.yaml 里的 dock-flash),加载后自动恢复。localStorage 只作为缓存,因此这些偏好会跟着你换浏览器、换机器,而不是留在浏览器里。升级前只存在于 localStorage 的旧值,会在首次加载时一次性迁移进配置。

切换机制、排除原因、技术约束等实现细节见 AGENTS.md


🌐 国际化

面板内置中英文本地化,自动跟随 DSH 语言设置。第三方开关可使用函数式标签(label: () => t('xxx'))实现语言切换时动态刷新。


安装

需要 DSH Web 环境:

# 安装本插件
dsh plugin --profile my-profile add ./dock-flash

# (可选)安装 dock-base 以获得完整的工作台集成
dsh plugin --profile my-profile add dock-base

# 启动
dsh --profile my-profile

无 dock-base:dock-flash 以独立模式运行 — ⚡ 触发按钮注入到 trigger-position 开关所选中的会话槽位(默认:输入框右侧),点击展开快捷控制浮动面板(外观、布局(触发位置)、系统开关)。共享槽位 sidebar.footer.action 有意不提供,因为其他插件也占用它。

有 dock-base:dock-flash 集成到工作台 — ⚡图标出现在活动栏,面板可作为侧边栏或浮动窗口打开,包含外观与系统开关。停靠布局属性(停靠边、自动隐藏、保留空间、图标缩放)在 dock-base 自己的设置里配置,不在这里。

开发

pnpm install
pnpm run build      # tsc → dist/index.js
pnpm run typecheck  # 仅类型检查

开发规则、关键约束与测试规范见 AGENTS.md;各版本的改动内容与原因见 CHANGELOG.md

样式契约

本插件遵循 DSH Web 样式契约:全部颜色经 --dsw-alias-* 设计令牌引用(字面量仅作兜底),无按主题分支的 CSS 选择器。

依赖

依赖 类型 说明
dock-base >=0.1.2-0 <1.0.0-0 || >=0.2.0-0 <1.0.0-0 peer(可选) 提供 ctx.workbench 注册表服务,用于完整工作台集成
@deepseek-ai/cordis >=4.0.0-rc.1 <5.0.0-0 || >=4.0.1-0 <5.0.0-0 peer 插件框架(DSH 自带)

许可证

Apache License 2.0

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。