dsh-composer-ux 0.1.3 → 0.1.5
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/CHANGELOG.md +17 -0
- package/README.md +28 -15
- package/lib/client.js +2 -2
- package/lib/client.js.map +2 -2
- package/lib/index.js +16 -2
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [0.1.5] — 2026-09-13
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
- **按 baseURL 识别 OpenCode 路由**:自动匹配(「作用路由」留空)原先只看路由名是否以 `opencode` 开头,因此自建别名路由(例如 `go: { baseURL: https://opencode.ai/zen/go/v1 }`)拿不到请求头。现在改为「名字前缀 **或** baseURL 主机是 `opencode.ai`(含子域)」两者取或——内置 `opencode-go` 仍走名字那条(它的 baseURL 由 pi-ai 目录内置,配置里读不到),自建别名走 URL 那条。
|
|
9
|
+
- 仿冒主机不匹配:`https://opencode.ai.evil.example/v1` 若只做子串判断会误伤,因此改为解析 URL 后比较主机名(解析失败才退回子串)。
|
|
10
|
+
- 设置页「作用路由」的说明与占位符同步更新;无命中时「状态」行会写出实际的匹配规则。
|
|
11
|
+
|
|
12
|
+
### 测试
|
|
13
|
+
- `test/host-header-mirror.mjs` 新增 3 组(9 项):按 URL 命中别名路由 / 子域命中 / 非 OpenCode 路由不动 / 仿冒主机不写入 / 显式名单优先 / URL 非法时子串回退。**35 passed, 0 failed**。
|
|
14
|
+
|
|
15
|
+
## [0.1.4] — 2026-09-13
|
|
16
|
+
|
|
17
|
+
### 文档
|
|
18
|
+
- **安装一节精简为两条命令**(安装 / 更新),GitHub 源码安装、锁定 commit、本地目录开发等做法收进折叠块——README 页面不再一上来就是四条命令。
|
|
19
|
+
- 移除 README 里的本机绝对路径,改为占位符,别人可以直接照抄。
|
|
20
|
+
- 功能代码无改动(`lib/` 与 0.1.3 一致),26 项宿主行为测试全绿。
|
|
21
|
+
|
|
5
22
|
## [0.1.3] — 2026-09-13
|
|
6
23
|
|
|
7
24
|
### 变更
|
package/README.md
CHANGED
|
@@ -20,23 +20,33 @@ DeepSeek Harness Web 输入体验增强插件:
|
|
|
20
20
|
|
|
21
21
|
## 安装
|
|
22
22
|
|
|
23
|
-
本插件按官方「[打包与安装插件](https://deepseek-harness.github.io/deepseek-harness/develop/basic/publish)」规范打包为可安装**组合包**(bundle):`package.json` 声明 `dsh.bundle.patch → ./cordis.patch.yml`,该层以**包名**插入插件行 `dsh-composer-ux`,装进 profile 后由 pnpm/Node 从 `node_modules` 解析到 `lib/index.js`。
|
|
24
|
-
|
|
25
23
|
```sh
|
|
26
|
-
#
|
|
27
|
-
dsh plugin --profile
|
|
24
|
+
# 安装
|
|
25
|
+
dsh plugin --profile web add dsh-composer-ux
|
|
26
|
+
|
|
27
|
+
# 更新到最新版
|
|
28
|
+
dsh plugin --profile web update dsh-composer-ux
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
npm 包:[dsh-composer-ux](https://www.npmjs.com/package/dsh-composer-ux) —— 预构建产物,安装期不在本地执行任何代码,也不需要 pnpm 的构建授权。(`--profile web` 换成你自己的 profile 名即可。)
|
|
28
32
|
|
|
33
|
+
<details>
|
|
34
|
+
<summary>其它安装方式(GitHub 源码 / 锁定 commit / 本地目录)</summary>
|
|
35
|
+
|
|
36
|
+
```sh
|
|
29
37
|
# 从 GitHub 安装(等价;lib/ 构建产物已提交,没有 prepare 脚本,因此不需要 pnpm 的构建授权)
|
|
30
|
-
dsh plugin --profile
|
|
38
|
+
dsh plugin --profile web add github:fangwen9527/dsh-composer-ux
|
|
31
39
|
|
|
32
40
|
# 锁定 commit 安装(更安全:后续推送无法悄悄改变实际运行的内容)
|
|
33
|
-
dsh plugin --profile
|
|
41
|
+
dsh plugin --profile web add github:fangwen9527/dsh-composer-ux#<commit-sha>
|
|
34
42
|
|
|
35
|
-
#
|
|
36
|
-
dsh plugin --profile
|
|
43
|
+
# 本地目录 / 源码开发(等价于 link;改完 src/ 先跑 node build.mjs)
|
|
44
|
+
dsh plugin --profile web add <你克隆或解压出来的目录>
|
|
37
45
|
```
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
本插件按官方「[打包与安装插件](https://deepseek-harness.github.io/deepseek-harness/develop/basic/publish)」规范打包为可安装**组合包**(bundle):`package.json` 声明 `dsh.bundle.patch → ./cordis.patch.yml`,该层以**包名**插入插件行 `dsh-composer-ux`,装进 profile 后由 pnpm/Node 从 `node_modules` 解析到 `lib/index.js`。
|
|
48
|
+
|
|
49
|
+
</details>
|
|
40
50
|
|
|
41
51
|
装完按 DSH 提示重启一次(Host 半的插件代码只在进程启动时 import),客户端半刷新页面即生效。
|
|
42
52
|
|
|
@@ -66,7 +76,10 @@ OpenCode 的接口要求客户端在每次请求里带上一个稳定的会话 I
|
|
|
66
76
|
- **设置页位置**:设置 → 输入体验 → 「OpenCode 请求头」(折叠栏目):开关 / 头名 / 值 / 作用路由,附一行宿主半回写的「状态」。
|
|
67
77
|
- **落点**:`llm-pi-ai` 的 provider profile —— `providers.<路由>.headers.<头名>`。这是 DSH 里唯一受支持的出网请求头入口:它作为 pi-ai 的 `optionsHeaders` **最后合并**(能覆盖默认头),且该适配器每次请求都重读配置,所以改完**下一次请求即生效**,不用重启、不用手工改 `settings.yaml`。
|
|
68
78
|
- **写入方式**:`settings.mutate` 的路径寻址——只动我们那一个键,绝不重述或删除你写在同一个 profile 里的其它字段(`models` / `apiKeyEnv` / 其它 headers 都不碰)。
|
|
69
|
-
-
|
|
79
|
+
- **目标路由怎么选**:profile 的 `models` 是必填项,凭空造一个只有 headers 的路由会让整份配置校验失败,所以只写**已存在**的路由。判据:
|
|
80
|
+
- 「作用路由」**留空**时自动匹配——路由名以 `opencode` 开头(DSH 内置的 `opencode-go` 走这条,它的 baseURL 由 pi-ai 目录内置、配置里读不到),**或**该路由的 `baseURL` 主机是 `opencode.ai`(含子域)。后一条是为自建别名路由准备的:例如把 OpenCode 端点配成 `go: { baseURL: https://opencode.ai/zen/go/v1 }`,名字里没有 opencode 也能被认出来。
|
|
81
|
+
- 「作用路由」**填了名单**时只看名单(逗号或空格分隔),且只保留其中确实存在的路由。
|
|
82
|
+
- 判据只看主机名:`https://opencode.ai.evil.example/v1` 这类仿冒主机不会被匹配。
|
|
70
83
|
- **撤销**:关栏目开关、关插件总开关、或改头名,都会自动清掉此前写入的那一个键;并且只清「值等于当前配置值或记账值」的,用户自己手写的同名头不会被误删。
|
|
71
84
|
- **值**:所有对话共用同一个值(栏目里可改、可「重新生成」);第一次启用若留空,会自动生成一个 UUID 并保存沿用。OpenCode 文档原话是要「每段对话」一个 ID——按会话变化的头 DSH 的配置层做不到(`llm/stream` 钩子被设计成只能读不能改,`GenerateOptions` 里没有 `headers` 字段),所以这里退一步用固定值。已知代价:所有对话挤同一个上游(没有负载分散);单段对话内的缓存命中不受影响。另外它**不保证**缓存一定命中(还取决于上游模型与网关策略)。
|
|
72
85
|
- **自校验**:写入前用与 llm-pi-ai 相同的规则(`new Headers()`)校验头名与头值;非法值被拒绝,原因写进栏目的「状态」行,而不是把整条路由弄坏。
|
|
@@ -77,7 +90,7 @@ OpenCode 的接口要求客户端在每次请求里带上一个稳定的会话 I
|
|
|
77
90
|
|
|
78
91
|
## 结构
|
|
79
92
|
|
|
80
|
-
|
|
93
|
+
仓库根目录(克隆或解压出来即是):
|
|
81
94
|
|
|
82
95
|
```
|
|
83
96
|
dsh-composer-ux/
|
|
@@ -117,16 +130,16 @@ $env:DSH_REPO_PATH='D:/DeepSeek Harness'; node build.mjs
|
|
|
117
130
|
## 加载(从 DSH 源码检出)
|
|
118
131
|
|
|
119
132
|
```sh
|
|
120
|
-
cd
|
|
121
|
-
pnpm dsh web --patch
|
|
133
|
+
cd <你的 DeepSeek Harness 检出目录>
|
|
134
|
+
pnpm dsh web --patch <本仓库的绝对路径>/cordis.dev.patch.yml
|
|
122
135
|
```
|
|
123
136
|
|
|
124
|
-
|
|
137
|
+
把尖括号替换成你的真实路径;插件行的 `name` 必须是 `file:///` 形式的绝对 URL(Windows 下 `D:/...` 裸路径无法被 ESM loader 导入)。该文件只在本机存在(已 gitignore),内容如下:
|
|
125
138
|
|
|
126
139
|
```yaml
|
|
127
140
|
- insert:
|
|
128
141
|
- id: composer-ux
|
|
129
|
-
name: 'file
|
|
142
|
+
name: 'file:///<本仓库的绝对路径>/lib/index.js'
|
|
130
143
|
```
|
|
131
144
|
|
|
132
145
|
## 开发时热更新
|
package/lib/client.js
CHANGED
|
@@ -1487,9 +1487,9 @@ function SettingsSection({ useLive, actions }) {
|
|
|
1487
1487
|
TextFieldRow,
|
|
1488
1488
|
{
|
|
1489
1489
|
title: "\u4F5C\u7528\u8DEF\u7531",
|
|
1490
|
-
desc: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u7559\u7A7A = \u81EA\u52A8\u5339\u914D opencode \
|
|
1490
|
+
desc: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u7559\u7A7A = \u81EA\u52A8\u5339\u914D OpenCode \u8DEF\u7531\uFF08\u540D\u5B57\u4EE5 opencode \u5F00\u5934\uFF0C\u6216 baseURL \u6307\u5411 opencode.ai\uFF09",
|
|
1491
1491
|
value: settings.headerRoutes,
|
|
1492
|
-
placeholder: "\u7559\u7A7A = \u81EA\u52A8\uFF08opencode
|
|
1492
|
+
placeholder: "\u7559\u7A7A = \u81EA\u52A8\uFF08opencode* \u6216 opencode.ai \u7AEF\u70B9\uFF09",
|
|
1493
1493
|
maxLength: HEADER_VALUE_MAX,
|
|
1494
1494
|
onChange: (next) => {
|
|
1495
1495
|
actions.setField(HEADER_ROUTES_FIELD, next);
|