dsh-router-core 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +9 -10
  2. package/cordis.patch.yml +10 -0
  3. package/docs/ext.md +64 -2
  4. package/lib/client-registry.js +633 -50
  5. package/lib/client.js +633 -50
  6. package/lib/contract-D4BWZ7Xe.js +13 -0
  7. package/lib/ext-prompt/index.js +527 -0
  8. package/lib/ext-prompt/locale/en.json +6 -0
  9. package/lib/ext-prompt/locale/zh.json +6 -0
  10. package/lib/ext-prompt/package.json +5 -0
  11. package/lib/ext-test/index.js +1 -12
  12. package/lib/index.js +286 -127
  13. package/lib/types/ext/contract.d.ts +113 -0
  14. package/lib/types/ext-prompt/content.d.ts +64 -0
  15. package/lib/types/ext-prompt/index.d.ts +53 -0
  16. package/lib/types/ext-prompt/logo.d.ts +17 -0
  17. package/lib/types/ext-prompt/mount.d.ts +51 -0
  18. package/lib/types/ext-prompt/plugin.d.ts +32 -0
  19. package/lib/types/ext-prompt/render.d.ts +105 -0
  20. package/lib/types/host-version.d.ts +7 -8
  21. package/lib/types/index.d.ts +1 -1
  22. package/lib/types/shared.d.ts +33 -0
  23. package/package.json +17 -7
  24. package/src/client/EndpointTab.tsx +15 -18
  25. package/src/client/ExtDetail.tsx +552 -9
  26. package/src/client/index.tsx +7 -28
  27. package/src/client/router.css +132 -62
  28. package/src/ext/contract.ts +111 -0
  29. package/src/ext-prompt/assembly.test.ts +79 -0
  30. package/src/ext-prompt/content.ts +181 -0
  31. package/src/ext-prompt/ext-prompt.test.ts +432 -0
  32. package/src/ext-prompt/index.ts +104 -0
  33. package/src/ext-prompt/locale/en.json +6 -0
  34. package/src/ext-prompt/locale/zh.json +6 -0
  35. package/src/ext-prompt/logo.svg +14 -0
  36. package/src/ext-prompt/logo.ts +18 -0
  37. package/src/ext-prompt/mount.test.ts +95 -0
  38. package/src/ext-prompt/mount.ts +79 -0
  39. package/src/ext-prompt/plugin.ts +223 -0
  40. package/src/ext-prompt/render.test.ts +256 -0
  41. package/src/ext-prompt/render.ts +216 -0
  42. package/src/ext-prompt/row.json +5 -0
  43. package/src/ext-route.test.ts +78 -0
  44. package/src/host-version.test.ts +21 -28
  45. package/src/host-version.ts +39 -39
  46. package/src/index.ts +216 -94
  47. package/src/llm/adapter.test.ts +19 -42
  48. package/src/llm/adapter.ts +20 -47
  49. package/src/shared.ts +34 -0
  50. package/src/suppliers/registry.test.ts +56 -0
  51. package/src/suppliers/registry.ts +40 -10
package/README.md CHANGED
@@ -5,8 +5,7 @@
5
5
  <p align="center">
6
6
  <a href="https://www.npmjs.com/package/dsh-router-core"><img src="https://img.shields.io/npm/v/dsh-router-core?style=flat-square&logo=npm&label=npm" alt="npm version"></a>
7
7
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-10b981?style=flat-square" alt="MIT license"></a>
8
- <a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="DSH 0.1.5" src="https://img.shields.io/badge/DSH-0.1.5-4d6bfe?style=flat-square" /></a>
9
- <a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="DSH 0.1.7-alpha.2 支持" src="https://img.shields.io/badge/DSH-0.1.7--alpha.2-e400dd?style=flat-square" /></a>
8
+ <a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="DSH 0.2.0" src="https://img.shields.io/badge/DSH-0.2.0-4d6bfe?style=flat-square" /></a>
10
9
  <a href="#agent-team-支持"><img alt="Agent Team" src="https://img.shields.io/badge/Agent%20Team-%E2%9C%93-10b981?style=flat-square" /></a>
11
10
  </p>
12
11
 
@@ -33,7 +32,8 @@ dsh-router 按**会话身份**亲和选号:同会话固定落同一个连接(前
33
32
 
34
33
  ## 快速安装
35
34
 
36
- 需要 **DSH `0.1.5-rc.1` 及以上**(支持 `dsh plugin` profile 插件机制)、Node.js `>= 20`,以及 `web` profile。
35
+ 需要 **DSH `0.2.0` 及以上**、Node.js `>= 20`,以及 `web` profile。0.1.x 宿主不再支持(设置注册、
36
+ tool 消息形态都已按 0.2.0 单面实现)。
37
37
 
38
38
  ```bash
39
39
  dsh plugin --profile web add dsh-router-core
@@ -98,9 +98,9 @@ team 测试就是多个会话交错发请求。dsh-router 原生适配这个场
98
98
  > **加连接**或**限制 team 并发成员数 ≤ 连接数**。详见
99
99
  > [`docs/pool-sticky-block.md`](docs/pool-sticky-block.md) §11。
100
100
  >
101
- > 版本适配:「路由」徽章只在 **DSH ≥ 0.1.7** 出现(0.1.5 的对应座位渲染位置
102
- > 不同);0.1.5 与 0.1.7 的兼容差异(设置注册、tool 消息形态)统一按宿主版本号
103
- > 判定。
101
+ > 版本适配:插件按 **DSH 0.2.0 单面**实现(0.1.5/0.1.7 的兼容分叉已删除)。跑在
102
+ > 低于 0.2.0 的宿主上仍会加载,但会在宿主日志里打一条警告 —— 那些症状与「插件坏了」
103
+ > 无法区分,所以宁可出声。
104
104
 
105
105
  ## 面板(设置 → 路由)
106
106
 
@@ -346,7 +346,7 @@ curl -X POST http://localhost:3080/v1/chat/completions \
346
346
  │ │ ├─ SupplierDetail 供应商详情:链接池 + 加链接 + 可用模型 + 模型测试
347
347
  │ │ ├─ CombosTab / EndpointTab
348
348
  │ │ └─ ExtTab → ExtDetail 扩展列表(只列已启用)与详情(优先渲染扩展自带的面板)
349
- │ └─ LastHitDock 「路由」徽章(composer.dock 座位,≥0.1.7;按会话显示最近命中)
349
+ │ └─ LastHitDock 「路由」徽章(composer.dock 座位;按会话显示最近命中)
350
350
  ├─ 设置 → 插件(官方 plugins.detail.section 座位)
351
351
  │ ├─ (原生「包含的组件」) core patch 里声明的行:core 本体 + 内置供应商 + 内置扩展
352
352
  │ │ 宿主管的开关,关一行 = loader 不 import 它
@@ -359,7 +359,7 @@ curl -X POST http://localhost:3080/v1/chat/completions \
359
359
  │ └─ RouterAdapter(src/llm/adapter.ts) OpenAI SSE → DSH StreamChunk
360
360
  │ (usage 经 toTokenUsage 转 DSH 契约,见 docs/suppliers.md)
361
361
  │ (带 x-dsh-router-session 头:宿主 sessionId → 会话亲和)
362
- ├─ host-version(src/host-version.ts) 按宿主版本号适配 0.1.5 / 0.1.7
362
+ ├─ host-version(src/host-version.ts) 报宿主版本 + 守 0.2.0 版本地板(低于则告警)
363
363
  ├─ KeysStore(src/keys.ts) 密钥库 + requireApiKey
364
364
  └─ Router(路由器) → suppliers[]
365
365
  ├─ OpenCodeSupplier(lib/suppliers/opencode/index.js) 无账号直连(Zen 免费档,需 CLI 握手)
@@ -404,8 +404,7 @@ curl -X POST http://localhost:3080/v1/chat/completions \
404
404
 
405
405
  ## 前提
406
406
 
407
- - **DSH 版本**:`0.1.5-rc.1` 及以上;插件内部按宿主版本号自动适配 0.1.5 / 0.1.7
408
- 差异(设置注册方式、tool 消息形态、底部徽章);「路由」徽章需 **≥ 0.1.7**;
407
+ - **DSH 版本**:`0.2.0` 及以上(单面支持;低于此版本会在宿主日志里告警);
409
408
  - 凭证由 dsh-router 核心统一管(SQLite 库 `<dataDir>/auths/credentials.sqlite`);
410
409
  - 供应商接入与开发见 [`docs/suppliers.md`](docs/suppliers.md);
411
410
  - 重启 DSH 后 `/v1/*` 即生效;面板管理账号、模型与密钥。
package/cordis.patch.yml CHANGED
@@ -35,3 +35,13 @@
35
35
  - id: dsh-router-ext-test
36
36
  name: 'dsh-router-core/ext-test'
37
37
  disabled: true
38
+ # 分层提示词扩展。**默认关闭**(与 ext-test 同款):行关闭时 loader 根本不
39
+ # import 它,扩展就不在 `router.ext` 表里 —— 「打开才出现这张卡片」是天然的。
40
+ #
41
+ # ⚠️ 这里来回改过一次(2026-09-29):先改成默认开启(理由是"准则是基础设施"),
42
+ # 随后又改回默认关闭 —— **决定权在用户**。判据见 ext-prompt.test.ts 的
43
+ # 「那一行默认关闭」用例(它真读本文件,改了会红)。
44
+ # 准则内容随核心分发(`src/ext-prompt/content.ts`)—— 改准则要发新版本。
45
+ - id: dsh-router-ext-prompt
46
+ name: 'dsh-router-core/ext-prompt'
47
+ disabled: true
package/docs/ext.md CHANGED
@@ -3,7 +3,8 @@
3
3
  扩展插件是**独立的 DSH 插件(npm 包)**,只提供**差异化能力** —— 目前是改写一条
4
4
  bash 命令。装好后出现在「设置 → 路由 → 扩展」,**开关不在那里** —— 启停的唯一入口是
5
5
  官方插件页「设置 → 插件 → dsh-router-core」详情页的「路由组件」一节;面板这一页只列
6
- 已启用的扩展,只读。
6
+ 已启用的扩展。**总开关**在这一层只读;但扩展自报的**子开关**(见下「子开关」一节)
7
+ 在这一层可点。
7
8
 
8
9
  **dsh-router 核心只做管理面,不做拦截。** 扩展插件自己挂监听、自己裁决、自己短路
9
10
  (2026-09 重构,见下)。
@@ -28,6 +29,49 @@ bash 命令。装好后出现在「设置 → 路由 → 扩展」,**开关不
28
29
  参考实现:[dsh-router-ext-rtk](https://github.com/CARVIN94/dsh-router-ext-rtk)
29
30
  (把命令改写成 `rtk <cmd>` 压缩输出)。
30
31
 
32
+ ## 子开关:一个扩展里有几件独立的事
33
+
34
+ 有些扩展的「开/关」不是一件事而是**几件独立的事** —— 例如 `分层提示词` 有 14 个
35
+ 准则分类,每条都该能单独关。硬塞进一个总开关 = 要么全给、要么全不给。
36
+
37
+ 扩展可在 `RouterExt` 上声明 `controls`(子开关列表)与 `setControl`(改一个):
38
+
39
+ ```ts
40
+ export function createPromptExt(deps): RouterExt {
41
+ return {
42
+ id: 'prompt',
43
+ // ... name / description / source / getState
44
+ controls: PROMPT_CATEGORIES.map((c) => ({
45
+ id: c.id, title: c.title,
46
+ body: c.body, // ← 原文:面板点标题就能读
47
+ on: resolve(c.id),
48
+ })),
49
+ setControl: (controlId, on) => { /* 写进 data 抽屉;返回 false = 失败 */ },
50
+ }
51
+ }
52
+ ```
53
+
54
+ 面板:扩展详情页(设置 → 路由 → 扩展 → 点开卡片)逐条渲染,走同一个
55
+ `PATCH /router/api/ext`,body 是 `{ id, controlId, on }`。
56
+
57
+ ⚠️ **`controls[].body` 要给原文**,不只是标题。不给的话用户是在**盲切** ——
58
+ 只看到「结构」「交付」这样的名字,不知道这一条到底写了什么,也无从判断该不该
59
+ 关掉它。面板用原生 `<details>` 折叠展示(默认收起,点标题展开),**开关留在收起
60
+ 态也可见** —— 不能要求先展开才关得掉。
61
+
62
+ ⚠️ 三条约定:
63
+
64
+ 1. **落盘形状归扩展,核心不代劳。** 核心只把 `{controlId, on}` 递进来并如实回传
65
+ 成功/失败 —— 它**不认识** `data.categories` 长什么样。核心一旦代写,就会变成
66
+ 「认识所有扩展私有数据格式」的地方。
67
+ 2. **总开关关 ⇒ 子开关一并失效,但不清零。** 面板显示为**禁用**而不是隐藏
68
+ (隐藏会让用户以为它们不存在),重新打开总开关就恢复原样。
69
+ 3. **`control.id` 是持久化键**,改名会让用户已保存的选择丢失 —— 同 `ExtId`。
70
+
71
+ ⚠️ **不适用于外部插件包(暂时)**:与下面的 `registerExtPanel` 一样,
72
+ `controls` 的读写经 `dsh-router-core/client` 才能到达外部 bundle,而该入口不导出
73
+ 这些能力。要让外部扩展用上,需要给它加一条真正的导出路径。这是已知的升级路径。
74
+
31
75
  ## 分工:管理归核心,执行归插件
32
76
 
33
77
  | | 谁负责 |
@@ -67,6 +111,19 @@ interface RouterExt {
67
111
  readonly description?: string // 面板内容区说明
68
112
  readonly icon?: string // 卡片/详情图标 URL
69
113
  readonly source?: 'builtin' // 标了 = 随核心分发(见下);不标 = 独立插件
114
+
115
+ // ↓ 以下 7 个全是**可选**的子开关能力。不实现 =「一个总开关的扩展」,
116
+ // 面板只显示总开关、不渲染子开关列表。权威定义见 src/ext/contract.ts。
117
+ readonly controls?: ExtControl[] // 子开关列表。⚠️ 必须每次读都重算(getter);
118
+ // 做成构造时的数组快照 ⇒ 改完文本刷新面板
119
+ // 拿到的还是旧值(2026-09-29 实测踩过)
120
+ setControl?(id: string, on: boolean): boolean // 开关一个。false = 失败,核心回 400 且不写盘
121
+ setControlText?(id: string, patch: {title?, body?}): boolean // 改文本(部分更新,未出现的字段保持原样)
122
+ setControlOrder?(ids: string[]): boolean // 拖动排序(改的是列表本身,不是某一条)
123
+ addCustomControl?(title: string, body: string): string | null // 新增自建条目,返回新 id;失败 null
124
+ removeCustomControl?(id: string): boolean // 删。只该允许删自建的
125
+ resetControlText?(id: string): boolean // 还原文本。自建条目无「自带内容」,应返 false
126
+
70
127
  getState(): ExtState // 运行时事实
71
128
  dispose?(): void
72
129
  }
@@ -80,6 +137,11 @@ interface ExtState {
80
137
 
81
138
  **没有 `rewrite`。** 怎么改命令是插件的实现细节,核心不感知、不调用。
82
139
 
140
+ > 子开关的**落盘形状归扩展所有**:本仓是 `data.categories[id]` 存开关、
141
+ > `data.text[id]` 存文本,**两个键分开**(开关是"开不开",文本是"写成什么样",
142
+ > 混在一个键里改标题会顺手碰到开关状态)。核心只把 `{controlId, on}` 递进来、
143
+ > 如实回传成功/失败,**不代写** —— 否则核心会变成「认识所有扩展私有数据格式」的地方。
144
+
83
145
  **`source: 'builtin'`** 标的是「随核心分发」。这类扩展同时是插件页原生「包含的组件」
84
146
  里的**一行**(自带宿主管的开关,关一行 = loader 不 import 它),因此插件页那个自绘的
85
147
  「路由组件」节**只列独立安装的扩展**,不把它们重复列一遍。判定只认这一个字段,没有别的
@@ -181,7 +243,7 @@ tsdown.config.ts
181
243
  {
182
244
  "main": "lib/index.js",
183
245
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
184
- "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1" }
246
+ "peerDependencies": { "@deepseek-ai/cordis": "^4.0.4" }
185
247
  }
186
248
  ```
187
249