dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness(DSH)的 FlowUs 集成插件:通过 OAuth 2.1 浏览器授权接入 FlowUs 官方 MCP 服务器,将其工具注册为 Agent 原生工具,并提供 /flowus 命令与设置面板页面。FlowUs integration plugin for DeepSeek Harness: OAuth 2.1 browser authorization against the official MCP server, tools registered as native agent tools, a /flowus command and a settings page.
English | 中文
DeepSeek Harness(DSH)的 FlowUs 集成插件。该插件在宿主进程内完成面向 FlowUs 官方 MCP 服务器(https://mcp.flowus.cn/message)的 OAuth 2.1 授权(RFC 9728 元数据发现、RFC 7591 动态客户端注册、PKCE S256),将服务器公布的工具注册为 Agent 原生工具,并提供 /flowus 命令与设置面板页面。授权过程不使用静态令牌。
127.0.0.1 上的 loopback 监听。访问令牌与刷新令牌保存在插件私有状态文件中,由插件自动续期。mcp__flowus__<工具名>。/flowus 命令。 提供状态查询、授权、登出、工具列表、直接调用、重连与帮助。DSH 内置的 @deepseek-ai/dsh-mcp-client 支持 stdio 与 streamable-http(自定义请求头)两种接入方式,不支持 MCP OAuth。FlowUs 官方 MCP 服务器采用 OAuth 2.1,并要求客户端在无法完成授权流程时明确报告,而非退回静态令牌。本插件实现该 OAuth 客户端流程,并将发现的工具桥接至 ctx.tools。
| 项 | 要求 |
|---|---|
| DSH | 0.2.0-rc.2、0.2.1-alpha.1、0.2.1-alpha.2(见“兼容性”) |
| Node.js | >= 22 |
dsh plugin --profile web add @blksword/dsh-flowus@latest
dsh plugin add 的执行过程如下:
dsh.bundle.patch,并将 @blksword/dsh-flowus 追加至 dsh.profile.bundles(无需手工编辑 package.json);@deepseek-ai/* 实现。pnpm 11 的 minimumReleaseAge 默认值为 1440 分钟:发布不足 24 小时的版本在解析 @latest 时会被跳过,解析结果回退至上一版本,且退出码仍为成功。安装最新发布时应指定确切版本号:
dsh plugin --profile web add @blksword/dsh-flowus@0.1.1
仓库为公开仓库,可不经 registry 直接安装:
dsh plugin --profile web add github:BlkSword/dsh-flowus
该路径已完成端到端验证:pnpm 可解析 git 依赖,bundle 条目写入 dsh.profile.bundles,加载器完成组合与导入(dsh --profile <名称> --dump-config 与 --dump-config-schema 均通过)。如需可复现的安装,可使用 github:BlkSword/dsh-flowus#<commit-sha> 指定提交;后续通过 dsh plugin --profile web update @blksword/dsh-flowus 获取更新。
# 1. 打包至 profile 目录
npm pack --pack-destination "$env:USERPROFILE\.dsh\profiles\web"
# 2. 在 profile 目录中安装(使用相对路径)
dsh plugin --profile web add "file:./blksword-dsh-flowus-0.1.1.tgz"
Windows 说明:pnpm 会将
link:D:/...、file:D:/...形式的绝对路径按相对路径解析,从而生成指向...\profiles\web\D:\...的无效链接。因此应先将 tarball 打包至 profile 目录,再使用上述相对file:说明符。
重启 dsh web 并刷新浏览器页面。客户端入口图(window.__DSH_BOOT__)在进程启动时组装,默认配置下 dsh-hmr 的 root: [] 仅保留显式配置监视点,因此新增插件不会热加载进正在运行的进程。重启后可通过 /flowus status 或“设置 → FlowUs”确认加载结果。
本版本支持的 DSH 版本为 0.2.0-rc.2、0.2.1-alpha.1、0.2.1-alpha.2,不支持其他版本。peer 依赖与兼容性记录仅声明这三个版本。
| DSH 版本 | @deepseek-ai/cordis | @deepseek-ai/schemastery |
|---|---|---|
0.2.0-rc.2 | ~4.0.4 | ~3.18.4 |
0.2.1-alpha.1 | ~4.0.5-alpha.1 | ~3.18.5-alpha.1 |
0.2.1-alpha.2 | ~4.0.5-alpha.1 | ~3.18.5-alpha.1 |
peer 依赖声明方式:@deepseek-ai/dsh-* 采用精确版本析取 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2;@deepseek-ai/cordis 与 @deepseek-ai/schemastery 采用成对范围 ~4.0.4 || ~4.0.5-alpha.1 与 ~3.18.4 || ~3.18.5-alpha.1。上述 @deepseek-ai/* 包均由当前 dsh profile 提供,不应另行添加依赖。
该矩阵经实测确认:离线套件与联网冒烟测试分别在三个运行时自带的包上执行,均无失败;插件涉及的具名导出集合在三个版本间完全一致。alpha 版本的范围分支为必需项:预发布版本仅在比较符于同一 major.minor.patch 上带预发布标识时才满足范围,因此 ~4.0.4 不接受 4.0.5-alpha.1,0.2.0-rc.2 亦不接受 0.2.1-alpha.1。
未列入声明的新版本(实测记录)。 @deepseek-ai/dsh@0.2.1-alpha.3 与 alpha.4 已发布,本插件暂不声明支持:
0.2.0-rc.2 结果一致。该运行时配对的 cordis@4.0.5-alpha.2、schemastery@3.18.5-alpha.2、dsh-tools@0.2.1-alpha.3 均落在现有 ~ 与精确析取范围内,无需修改声明。@deepseek-ai/dsh-client-ui-primitives 作为运行时的依赖包(node_modules\@deepseek-ai 下已无此包),而设置页的 classic script 需要从 shell 的模块表 require 它;alpha.3 的 shell bundle 中仍可见该模块 id,故结论未定。列入支持前,需要在 alpha.3 上真正打开一次设置页确认。@deepseek-ai/dsh-atomic-write 已加入 peer 集合(可选):存在时用于状态文件的原子写与跨进程文件锁。/flowus login
127.0.0.1:42813/callback 建立一次性回调监听;授权完成后可直接以自然语言向 Agent 提出 FlowUs 相关任务。
| 命令 | 作用 |
|---|---|
/flowus 或 /flowus status | 显示授权状态、连接状态、凭据文件路径与已注册工具列表 |
/flowus login | 打开浏览器完成授权,并同步工具列表 |
/flowus logout | 删除令牌,保留动态客户端注册(避免重复注册触发限流) |
/flowus tools | 列出已注册至模型侧的 FlowUs 工具 |
/flowus call <工具> [JSON] | 直接调用指定工具,例如 /flowus call search_pages {"query":"周报"} |
/flowus reload | 重新连接并同步工具列表 |
/flowus help | 显示用法 |
插件通过 settings.section 槽位(id: flowus)向设置面板贡献一个独立页面,用于查看状态并执行授权相关操作:
StateDot 与 Tag 呈现。mcp__flowus__* 工具及其简述。页面数据来自宿主路由 GET /flowus/api/status;按钮分别向 /flowus/api/login、/flowus/api/login-cancel、/flowus/api/logout、/flowus/api/reload 发送 POST 请求。全部响应均为同一份状态快照({ ok, snapshot }),因此单一渲染路径即可覆盖所有操作。
上述路由与 DSH 其他 Web 接口一致,先经由连接服务的信任栅栏(Host/Origin 校验与登录令牌 Cookie):未认证请求直接返回 401,无法读取状态或触发授权。
授权采用先返回后轮询的方式:POST /flowus/api/login 立即返回,浏览器授权在后台执行(上限为 loginTimeoutMs);页面在等待期间每 1.5 秒刷新一次状态,因此不会长时间占用 HTTP 请求。
配置通过 profile 补丁层覆盖($DSH_HOME/profiles/web/cordis.patch.yml):
- id: flowus
config:
callbackPort: 42813
toolCallTimeoutMs: 60000
| 字段 | 默认值 | 说明 |
|---|---|---|
serverUrl | https://mcp.flowus.cn/message | FlowUs MCP 资源地址 |
serverName | flowus | 工具命名空间(mcp__<serverName>__*) |
scope | all | 请求的 OAuth scope(FlowUs 公布 all) |
autoConnect | true | 启动时若已有可用令牌,则连接并发布工具 |
failOnStartupError | false | 初始连接或工具同步失败时,是否使插件激活失败 |
statePath | 空 → <DSH_HOME>/flowus/oauth.json | 凭据与注册信息文件路径 |
clientName | DeepSeek Harness | 授权页显示的客户端名称 |
callbackHost | 127.0.0.1 | 回调监听地址 |
callbackPort | 42813 | 回调端口(固定端口可复用同一次客户端注册) |
callbackPath | /callback | 回调路径 |
loginTimeoutMs | 300000 | 等待浏览器完成授权的上限 |
openBrowser | true | 是否自动打开默认浏览器 |
toolCallTimeoutMs | 60000 | 单次工具调用超时 |
maxTools | 128 | 发布工具的数量上限(0 表示不限制) |
maxResultChars | 32000 | 单次工具结果转发给模型的字符上限,超出时保留首尾并注明省略量(0 表示不限制) |
deferToolLoading | false | 以「延迟加载」发布 FlowUs 工具:在支持该能力的路由上,schema 以 defer_loading 声明而不进入常驻上下文;不支持的路由会自动忽略该标记 |
maxInstructionChars | 32000 | 服务器 instructions 进入系统提示词前的字符上限,超出部分截断并在日志中提示(0 表示不限制) |
promptSection | true | 是否将 FlowUs 说明与服务器 instructions 并入系统提示词 |
%DSH_HOME%\flowus\oauth.json(默认 C:\Users\<用户名>\.dsh\flowus\oauth.json),内容包括动态注册的 client_id、令牌集合、PKCE verifier 与 discovery 缓存。写入优先使用宿主提供的 @deepseek-ai/dsh-atomic-write(随机后缀 + wx 独占创建 + rename,并以文件锁串行化跨进程的读-改-写;该 peer 声明为可选,缺失时回退到等价的本地实现),权限按平台能力设置为 0600。登出会顺带清理残留的 <file>.*.tmp 与 <file>.corrupt。token_endpoint_auth_method = "none",配合 PKCE S256,不存在 client secret;刷新令牌是唯一的长期凭据。logout 仅清除令牌与 verifier,保留动态客户端注册。注册端点限流较严格(实测响应头 X-RateLimit-Limit: 3),复用注册可避免重复登录时触发限流。mcp.flowus.cn(资源元数据与 MCP 数据面)、api.flowus.cn(授权、令牌、注册)。/flowus logout。/flowus login
├─ 建立 127.0.0.1:42813/callback 一次性监听(先生成 state)
├─ auth(provider, { serverUrl, scope }) ← 由 MCP SDK 驱动
│ ├─ RFC 9728 发现 /.well-known/oauth-protected-resource
│ ├─ RFC 8414 发现 /.well-known/oauth-authorization-server
│ ├─ 无注册信息时执行 RFC 7591 动态客户端注册(loopback redirect + native)
│ └─ 生成 PKCE(S256) 授权 URL → redirectToAuthorization → 打开浏览器
├─ 浏览器授权 → 回调携带 ?code&state(state 不匹配则拒绝)
├─ auth(provider, { serverUrl, authorizationCode, iss })
│ └─ 换取令牌(access + refresh)并持久化
└─ MCP initialize → tools/list → 逐项注册为 DSH 工具(mcp__flowus__*)
notifications/tools/list_changed 触发重新同步:插件接管新列表并以整代替换重新注册,失败时保留上一代。-32001,或会话端点 404),插件自动重连一次、重新注册工具列表并重放当前调用,无需人工执行 /flowus reload。mcp.flowus.cn 前面的 nginx 会给出秒级的 503 窗口(实测:所有 MCP 端点同时 503,8 秒后恢复正常)。这类网关拒绝会以指数退避 + 抖动重放(最多 3 次,500ms 起、8s 封顶);可能已被处理的模糊失败(500/429/连接重置)只对只读工具重放,写操作永不因插件自身重试而重复执行。同步失败后还会自动补试(5s / 15s / 60s),与 DSH 自带 MCP 桥的重连行为一致。/flowus status、模型看到的工具错误)只给出一句短文本:已知状态码转成人话、剥掉标记、压掉空白并截断到 200 字符,网关 HTML 原文只进日志。快照另带 lastErrorKind,设置页与 /flowus status 会标明「暂时性,插件会自动重试」。maxResultChars 截断:保留首尾并注明省略了多少字符,结构化内容同样受限,超大内联负载(base64 图片/blob)在保存的原始块中以大小占位。每个工具还提供调用卡片:kind 区分读取/搜索与写入,标题给出本次调用的对象。-32002)。插件在会话内以串行队列执行调用,并对并发拒绝执行有限次退避重试,因此模型并行发起多个只读调用不会失败(验证结果:5 个并发调用全部成功)。mcp__flowus__* 已被其他插件占用,则该次同步整体拒绝,不产生部分发布。GET,并要求唯一且匹配的 state(常量时间比较);不满足者只作答、不结束登录,因此本机任意进程或页面无法取消进行中的授权。监听地址强制为 loopback(RFC 8252 §7.3),帮助页回显的服务器文本经转义并截断。serverUrl 指向另一资源时,插件拒绝出示令牌并提示重新授权,而不会把 FlowUs 令牌发给第三方端点。ctx.webServer.register 注册 /flowus/api/*;客户端侧为 window.__ModuleLoader__ 形式的 classic script(lib/client.js,无需构建步骤),注册至 settings.section 槽位;两侧通过 fetch 与 JSON 快照通信,未引入自定义 RPC 协议。本地开发从锁文件安装,再把一份 DSH 运行时链接进来(node_modules 不入库):
npm ci --ignore-scripts --legacy-peer-deps # 按锁文件安装(含 typescript / @types/node)
npm install --no-save --no-package-lock --ignore-scripts --legacy-peer-deps @deepseek-ai/dsh@0.2.0-rc.2
npm run link-runtime # 逐包软链到上面这份运行时
npm run check # 类型检查 + 离线套件:180 项用例,177 项通过,3 项联网用例跳过
npm audit # 有锁文件后才能审计依赖
$env:DSH_FLOWUS_LIVE=1; node --test tests/flowus-live.test.js # 联网冒烟:3 项
链接方式与顺序。
scripts/link-runtime-packages.mjs为每个@deepseek-ai/*包各建一条软链(Windows 用 junction),而不是把整个 scope 目录链成一个。实测差别:npm install会删掉逐包软链本身、但保留其指向的真实包;整目录 junction 则会让 npm 穿过链接删掉运行时里的包(本仓库被这样毁过一次)。顺序上仍是先跑完所有 npm 命令、再链接;链接之后任何 npm/pnpm 命令都要重跑npm run link-runtime。
测试构成:
| 文件 | 覆盖范围 |
|---|---|
tests/tool-names.test.js | 工具命名契约(与官方 MCP 桥一致) |
tests/command-grammar.test.js | /flowus 语法解析 |
tests/store.test.js | 状态文件的原子写入(宿主原语与本地回退)、跨实例串行、损坏隔离与残留清理 |
tests/oauth-provider.test.js | OAuthClientProvider 语义、issuer 绑定(凭据不跨授权服务器出示)与状态裁剪 |
tests/callback-server.test.js | 回调校验(state、code、error、端口回退、abort) |
tests/login.test.js | 两条腿的授权交换(注入 authImpl,不触网):成功、已授权短路、无授权 URL、未完成交换、浏览器关闭、取消、超时、redirect 变更失效 |
tests/browser.test.js | 不依赖 shell 的浏览器启动与候选回退 |
tests/tool-bridge.test.js | MCP JSON Schema 至 DSH 参数 DSL 的转换与输出契约(以 DSH 自身的 schema 编译器校验) |
tests/text-bound.test.js | 结果与 instructions 的截断边界:保留首尾、注明省略量、不劈开代理对、0 表示不限制 |
tests/http-api.test.js | 设置页宿主 API:信任栅栏、方法校验、{ ok, snapshot } 封装与错误路径 |
tests/client-bundle.test.js | 浏览器半:__ModuleLoader__ 注册契约、中英文字典键一致性、槽位注册、与宿主路由的路径交叉校验、视图模型分支与完整渲染 |
tests/mcp-session.test.js | 结果归一化、并发拒绝识别、串行队列(顺序、并发上限、失败不影响后续)与授权错误分类 |
tests/plugin-apply.test.js | 真实 cordis 上下文中的装载、命令面与释放 |
tests/plugin-real-services.test.js | 装载真实的 ToolRuntime、CommandRuntime、SystemPrompt:命令注册、提示词段落组装、工具注册与完整执行管线(参数校验、输出契约、错误路径) |
tests/flowus-live.test.js | 真实端点的元数据发现、PKCE 授权 URL 与未授权连接的错误映射 |
源码修改后的本地分发方式:
npm pack --pack-destination "$env:USERPROFILE\.dsh\profiles\web"
dsh plugin --profile web add "file:./blksword-dsh-flowus-0.1.1.tgz"
正式发布参见“发布”。
测试通过 node_modules\@deepseek-ai 这一 junction 解析 @deepseek-ai/*。更换运行时进行验证时,将该 junction 指向目标版本的包目录,并在仓库副本上重新执行 node --test 与联网冒烟测试。两个版本差异需注意:
0.2.0-rc.2 将依赖嵌套于 <dsh>\node_modules\@deepseek-ai\...;0.2.1-alpha.1 与 0.2.1-alpha.2 则提升至 <runtime>\node_modules\@deepseek-ai\...,不存在嵌套目录。任何假定 rc.2 布局的脚本均需增加回退逻辑。./invariant 子路径已移除。 @deepseek-ai/dsh-tools/invariant、dsh-commands/invariant、dsh-system-prompt/invariant(及其 lib/invariant.js)在 0.2.0-rc.2 中存在,自 0.2.1-alpha.1 起被移除。本插件仅从包根导入,不应新增此类深层路径引用。npm login # 每台机器执行一次;@blksword scope 须属于该账号
npm publish --access public # prepublishOnly 会先执行离线测试
npm view @blksword/dsh-flowus version
# 对发布产物执行端到端校验
dsh plugin --profile web remove @blksword/dsh-flowus
dsh plugin --profile web add @blksword/dsh-flowus@latest
dsh plugin add 是唯一受支持的安装路径:加载器从 profile 的 node_modules 解析该包,客户端半由包内 exports["./client"] 提供。
| 现象 | 处理方式 |
|---|---|
/flowus 命令不存在 | 插件未加载。确认 dsh.profile.bundles 包含 @blksword/dsh-flowus,随后重启 dsh web |
| 设置面板中没有 FlowUs 页面 | 原因同上:客户端入口图在启动时组装。重启 dsh web 并刷新浏览器页面 |
| 设置页操作返回 401 | 页面请求需携带登录 Cookie。请使用 dsh web 输出的、含 ?token= 的地址打开 GUI |
| 授权完成后没有工具 | 执行 /flowus reload,再查看 /flowus status 或设置页中的错误信息 |
| 端口 42813 被占用 | 插件会改用临时端口,但此时需重新执行一次客户端注册(可能触及注册限流)。建议在配置中指定其他固定 callbackPort |
注册被限流(too_many_requests) | 稍后重试授权。插件复用已注册客户端,通常仅需注册一次 |
| 浏览器未打开 | 命令输出与设置页均会显示授权 URL,可手动打开 |
| 工具命名冲突 | 修改 serverName(例如 flowus2),工具命名空间随之变化 |
execution.taskSupport === "required" 的工具将被跳过,并在日志中说明原因。maxTools 上限与命名冲突保护约束。dsh-flowus/
├─ cordis.patch.yml # bundle 层:插入 flowus 插件行与默认配置
├─ package.json # dsh.bundle.patch / dsh.client / peer 依赖 / 发布白名单
├─ icon.svg
├─ locale/ # 插件列表中的显示名与简介(en/zh)
│ ├─ en.json
│ └─ zh.json
├─ lib/
│ ├─ index.js # 插件入口:Config、apply、工具发布、/flowus 命令、提示词段落、设置页路由
│ ├─ http-api.js # 设置页宿主 JSON API(/flowus/api/*,经连接信任栅栏)
│ ├─ client.js # 浏览器半:__ModuleLoader__ bundle,注册设置页
│ ├─ login.js # 两段式授权码流程编排
│ ├─ oauth-provider.js # SDK 的 OAuthClientProvider 实现与令牌摘要、过期判断
│ ├─ callback-server.js # 一次性 loopback 回调监听
│ ├─ browser.js # 不依赖 shell 的浏览器启动(含 Windows 候选回退)
│ ├─ mcp-session.js # MCP 会话:连接、发现、调用与结果归一化
│ ├─ tool-bridge.js # MCP JSON Schema 至 DSH defineTool 定义的转换
│ ├─ tool-names.js # mcp__<server>__<raw> 命名契约
│ ├─ command-grammar.js # /flowus 语法解析
│ └─ store.js # 私有状态文件(原子写入、串行化)
└─ tests/ # 离线测试与可选联网冒烟测试
Apache-2.0,详见 LICENSE。