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.
- package/README.md +9 -10
- package/cordis.patch.yml +10 -0
- package/docs/ext.md +64 -2
- package/lib/client-registry.js +633 -50
- package/lib/client.js +633 -50
- package/lib/contract-D4BWZ7Xe.js +13 -0
- package/lib/ext-prompt/index.js +527 -0
- package/lib/ext-prompt/locale/en.json +6 -0
- package/lib/ext-prompt/locale/zh.json +6 -0
- package/lib/ext-prompt/package.json +5 -0
- package/lib/ext-test/index.js +1 -12
- package/lib/index.js +286 -127
- package/lib/types/ext/contract.d.ts +113 -0
- package/lib/types/ext-prompt/content.d.ts +64 -0
- package/lib/types/ext-prompt/index.d.ts +53 -0
- package/lib/types/ext-prompt/logo.d.ts +17 -0
- package/lib/types/ext-prompt/mount.d.ts +51 -0
- package/lib/types/ext-prompt/plugin.d.ts +32 -0
- package/lib/types/ext-prompt/render.d.ts +105 -0
- package/lib/types/host-version.d.ts +7 -8
- package/lib/types/index.d.ts +1 -1
- package/lib/types/shared.d.ts +33 -0
- package/package.json +17 -7
- package/src/client/EndpointTab.tsx +15 -18
- package/src/client/ExtDetail.tsx +552 -9
- package/src/client/index.tsx +7 -28
- package/src/client/router.css +132 -62
- package/src/ext/contract.ts +111 -0
- package/src/ext-prompt/assembly.test.ts +79 -0
- package/src/ext-prompt/content.ts +181 -0
- package/src/ext-prompt/ext-prompt.test.ts +432 -0
- package/src/ext-prompt/index.ts +104 -0
- package/src/ext-prompt/locale/en.json +6 -0
- package/src/ext-prompt/locale/zh.json +6 -0
- package/src/ext-prompt/logo.svg +14 -0
- package/src/ext-prompt/logo.ts +18 -0
- package/src/ext-prompt/mount.test.ts +95 -0
- package/src/ext-prompt/mount.ts +79 -0
- package/src/ext-prompt/plugin.ts +223 -0
- package/src/ext-prompt/render.test.ts +256 -0
- package/src/ext-prompt/render.ts +216 -0
- package/src/ext-prompt/row.json +5 -0
- package/src/ext-route.test.ts +78 -0
- package/src/host-version.test.ts +21 -28
- package/src/host-version.ts +39 -39
- package/src/index.ts +216 -94
- package/src/llm/adapter.test.ts +19 -42
- package/src/llm/adapter.ts +20 -47
- package/src/shared.ts +34 -0
- package/src/suppliers/registry.test.ts +56 -0
- 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.
|
|
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.
|
|
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
|
-
>
|
|
102
|
-
>
|
|
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
|
|
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)
|
|
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.
|
|
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.
|
|
246
|
+
"peerDependencies": { "@deepseek-ai/cordis": "^4.0.4" }
|
|
185
247
|
}
|
|
186
248
|
```
|
|
187
249
|
|