dsh-plugin-bridge 0.2.11 → 0.3.1
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 +24 -12
- package/README.zh.md +24 -12
- package/docs/design.md +5 -3
- package/docs/guide.zh.md +16 -6
- package/docs/native-webui-feasibility.md +56 -28
- package/lib/api-rpc.d.ts +5 -2
- package/lib/api-rpc.js +10 -2
- package/lib/cli.js +12 -10
- package/lib/client-contract.d.ts +87 -0
- package/lib/client-contract.js +487 -0
- package/lib/client.d.ts +16 -0
- package/lib/client.js +1295 -0
- package/lib/client.js.map +1 -0
- package/lib/command.d.ts +10 -20
- package/lib/command.js +139 -18
- package/lib/dsh-alpha-host.d.ts +67 -0
- package/lib/dsh-alpha-host.js +276 -0
- package/lib/host.d.ts +177 -0
- package/lib/host.js +102 -0
- package/lib/index.d.ts +2 -3
- package/lib/index.js +6 -8
- package/lib/migrate.d.ts +17 -38
- package/lib/migrate.js +114 -71
- package/package.json +54 -4
- package/reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md +64 -0
- package/reports/native-workbench-2026-08-25.md +30 -0
- package/reports/native-workbench-2026-08-25.raw.json +34 -0
package/README.md
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
[](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](package.json)
|
|
12
|
-
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
13
13
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
14
|
+
[](https://dshfind.com/en/plugins/Totoro-qaq/dsh-plugin-bridge?ref=badge)
|
|
14
15
|
|
|
15
16
|
English | [中文](README.zh.md)
|
|
16
17
|
|
|
@@ -34,7 +35,7 @@ dsh plugin --profile web add dsh-plugin-bridge
|
|
|
34
35
|
Pinned GitHub fallback:
|
|
35
36
|
|
|
36
37
|
```bash
|
|
37
|
-
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.
|
|
38
|
+
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.1
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
Then type in the official WebUI:
|
|
@@ -47,12 +48,16 @@ Then type in the official WebUI:
|
|
|
47
48
|
/bridge code --go --continue restate and start work in the same target request
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
On DSH rc.7 and later, the official WebUI renders `/bridge` as a native card. **Text** exposes the fixed five sections as ordinary fields and list rows; **Markdown** preserves full source freedom; **Preview** renders Markdown or a complete JSON tree. Long content scrolls inside the card while the action bar stays reachable. **Confirm migration** opens the created target session.
|
|
52
|
+
|
|
53
|
+
UIs that implement the official `conversation.chat.commandview` slot receive the same card automatically. Other custom UIs retain the complete server result, summary-file workflow, and target title/session-ID fallback; UI authors can reuse the framework-free `dsh-plugin-bridge/client-contract` export instead of reimplementing the wire. On an older or non-slot client, correct the printed summary file and run:
|
|
51
54
|
|
|
52
55
|
```text
|
|
53
56
|
/bridge code --go --file <path>
|
|
54
57
|
```
|
|
55
58
|
|
|
59
|
+
Preview edits are temporary until migration is confirmed. Restarting the client or system may discard them; the source session remains untouched, and you can regenerate the preview.
|
|
60
|
+
|
|
56
61
|
Uninstall with `dsh plugin --profile web remove dsh-plugin-bridge`, then restart `dsh web`.
|
|
57
62
|
|
|
58
63
|
## Why Bridge
|
|
@@ -78,6 +83,8 @@ The release gate is intentionally small and reproducible; these are regression r
|
|
|
78
83
|
| Confirm / `--continue` target request shape | **2 · 1** to first useful work |
|
|
79
84
|
| Confirm extra, paired nominal median | **+8.1%** vs `--continue` |
|
|
80
85
|
| Summary worker share of clean acceptance components | **20.74% nominal** |
|
|
86
|
+
| Native WebUI repeat gate (preview / target facts) | **3/3 · 3/3**, five facts each |
|
|
87
|
+
| DSH 0.1.2-alpha.2 npm install / doctor / target | **clean · 13/13 · PTC paused** |
|
|
81
88
|
|
|
82
89
|
The token percentage varies widely with preset, response length, and cache state. The worker share is composition, not causal overhead versus no Bridge; the stable product claim is one additional confirmation request. Read the [design and evidence boundaries](docs/design.md), [full release report](reports/v0.2.3-e2e-report.md), and [vision report](reports/v0.2.6-rc11-vision-report.md).
|
|
83
90
|
|
|
@@ -105,23 +112,24 @@ The five sections are Goal, Current state, Key decisions and conventions, Key fi
|
|
|
105
112
|
|
|
106
113
|
## Compatibility
|
|
107
114
|
|
|
108
|
-
| DSH baseline |
|
|
115
|
+
| DSH baseline | Server handoff | Native card | Verification boundary |
|
|
109
116
|
|---|---:|---:|---|
|
|
110
|
-
| 0.1.0-rc.6
|
|
111
|
-
| 0.1.0-rc.8 | Yes |
|
|
112
|
-
| 0.1.1-rc.2 | Yes | Yes |
|
|
117
|
+
| 0.1.0-rc.6 | Yes | No | Narrow RPC contract and text compatibility tests |
|
|
118
|
+
| 0.1.0-rc.7 / rc.8 | Yes | Contract-checked | Client-module/command-slot contract plus server fallback |
|
|
119
|
+
| 0.1.1-rc.2 | Yes | Yes | Installed official WebUI: doctor 13/13, edit/confirm/auto-open, three-run repeat gate |
|
|
120
|
+
| 0.1.2-alpha.2 | Yes | Yes | Official npm install: typed controllers 13/13, split-client build, edit/confirm/PTC auto-open, clean removal |
|
|
113
121
|
|
|
114
122
|
CI covers Node.js 22 and 24. Run `/bridge --doctor` after every Harness upgrade; it names missing required gateway methods instead of failing vaguely.
|
|
115
123
|
|
|
116
124
|
Current limits:
|
|
117
125
|
|
|
118
126
|
- installation needs one WebUI restart;
|
|
119
|
-
-
|
|
120
|
-
-
|
|
127
|
+
- the native card auto-opens the created target through the official Session runtime; older clients still receive the title and session ID fallback;
|
|
128
|
+
- progress appears immediately while the worker runs; the current fixed three-run sample took 7.4–12.8 seconds of worker time, while `previewTimeoutMs` remains the hard bound;
|
|
121
129
|
- text-only models cannot inspect unresolved images;
|
|
122
|
-
-
|
|
130
|
+
- the native-card repeat gate is still only three fixed runs, so it is release evidence rather than a statistical guarantee.
|
|
123
131
|
|
|
124
|
-
The server command stays the compatibility core.
|
|
132
|
+
The server command stays the compatibility core. The same package now adds an optional official client half for rendered editing and navigation; if that prerelease client contract fails to load, `/bridge` still returns the complete server result. See the [implementation boundary](docs/native-webui-feasibility.md).
|
|
125
133
|
|
|
126
134
|
## Documentation
|
|
127
135
|
|
|
@@ -129,6 +137,8 @@ The server command stays the compatibility core. A native migration card is tech
|
|
|
129
137
|
- [Chinese install, configuration, rollback, and FAQ](docs/guide.zh.md)
|
|
130
138
|
- [Release acceptance report](reports/v0.2.3-e2e-report.md)
|
|
131
139
|
- [Vision migration report](reports/v0.2.6-rc11-vision-report.md)
|
|
140
|
+
- [Native WebUI repeat acceptance](reports/native-workbench-2026-08-25.md)
|
|
141
|
+
- [DSH 0.1.2-alpha.2 compatibility acceptance](reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md)
|
|
132
142
|
- [Historical compression benchmark](docs/benchmark.md)
|
|
133
143
|
|
|
134
144
|
## Development
|
|
@@ -138,10 +148,12 @@ npm ci
|
|
|
138
148
|
npm run verify
|
|
139
149
|
```
|
|
140
150
|
|
|
141
|
-
`verify` builds and type-checks
|
|
151
|
+
`verify` builds and type-checks both plugin halves, runs 167 tests, checks generated `lib/` and datasets, then packs, installs, and imports the actual npm tarball. Tests spend no model tokens. `prepublishOnly` runs the same gate; GitHub releases also require the tag to match `package.json` before trusted npm publishing.
|
|
142
152
|
|
|
143
153
|
Community listings: [Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) · [Awesome DeepSeek Harness](https://github.com/Dominic789654/awesome-deepseek-harness)
|
|
144
154
|
|
|
155
|
+
Ecosystem discovery: [dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI). Bridge remains a standard DSH plugin; TUI/std conformance is tracked separately.
|
|
156
|
+
|
|
145
157
|
## License
|
|
146
158
|
|
|
147
159
|
MIT
|
package/README.zh.md
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
[](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](package.json)
|
|
12
|
-
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
13
13
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
14
|
+
[](https://dshfind.com/zh/plugins/Totoro-qaq/dsh-plugin-bridge?ref=badge)
|
|
14
15
|
|
|
15
16
|
[English](README.md) | 中文
|
|
16
17
|
|
|
@@ -34,7 +35,7 @@ dsh plugin --profile web add dsh-plugin-bridge
|
|
|
34
35
|
GitHub 固定版本备用路径:
|
|
35
36
|
|
|
36
37
|
```bash
|
|
37
|
-
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.
|
|
38
|
+
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.1
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
然后在官方 WebUI 输入:
|
|
@@ -47,12 +48,16 @@ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.2.10
|
|
|
47
48
|
/bridge code --go --continue 在同一次目标请求里复述并开始工作
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
DSH rc.7 及以上会在官方 WebUI 原生卡片中渲染 `/bridge`。「文本编辑」把固定五段变成普通文本框和逐条列表;「Markdown」保留完整源码自由;「预览」渲染 Markdown 或完整 JSON 树。长内容只在卡片正文内滚动,操作按钮保持可达;点「确认迁移」后自动打开目标会话。
|
|
52
|
+
|
|
53
|
+
实现官方 `conversation.chat.commandview` slot 的第三方 UI 会自动得到同一张卡片。其他自定义 UI 仍保留完整服务端结果、摘要文件流程和目标标题/session ID 回退;UI 作者还可以复用无 React 的 `dsh-plugin-bridge/client-contract` 导出,而无需重写协议。旧客户端可修改输出里打印的摘要文件,再执行:
|
|
51
54
|
|
|
52
55
|
```text
|
|
53
56
|
/bridge code --go --file <路径>
|
|
54
57
|
```
|
|
55
58
|
|
|
59
|
+
预览中的修改在确认迁移前只是临时草稿。重启客户端或系统可能使其丢失;源会话始终不受影响,重新运行预览即可生成新的草稿。
|
|
60
|
+
|
|
56
61
|
卸载:`dsh plugin --profile web remove dsh-plugin-bridge`,然后重启 `dsh web`。
|
|
57
62
|
|
|
58
63
|
## 为什么是 Bridge
|
|
@@ -78,6 +83,8 @@ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.2.10
|
|
|
78
83
|
| Confirm / `--continue` 到首次有效工作的目标请求数 | **2 · 1** |
|
|
79
84
|
| Confirm 相对 `--continue` 的 nominal 配对中位额外成本 | **+8.1%** |
|
|
80
85
|
| 摘要 worker 在干净验收组件中的 nominal 占比 | **20.74%** |
|
|
86
|
+
| 原生 WebUI 重复门禁(预览 / 目标事实) | **3/3 · 3/3**,每次五项 |
|
|
87
|
+
| DSH 0.1.2-alpha.2 npm 安装 / doctor / 目标 | **干净 · 13/13 · PTC 已暂停** |
|
|
81
88
|
|
|
82
89
|
token 百分比会随 preset、回复长度和缓存状态大幅波动;worker 占比是组成,不是相对“无 Bridge”的因果开销。稳定结论是默认确认多一个请求。边界和原始证据见[设计与证据说明](docs/design.md)、[完整 release report](reports/v0.2.3-e2e-report.md)和[视觉迁移报告](reports/v0.2.6-rc11-vision-report.md)。
|
|
83
90
|
|
|
@@ -105,23 +112,24 @@ token 百分比会随 preset、回复长度和缓存状态大幅波动;worker
|
|
|
105
112
|
|
|
106
113
|
## 兼容性
|
|
107
114
|
|
|
108
|
-
| DSH 基线 |
|
|
115
|
+
| DSH 基线 | 服务端交接 | 原生卡片 | 验证边界 |
|
|
109
116
|
|---|---:|---:|---|
|
|
110
|
-
| 0.1.0-rc.6
|
|
111
|
-
| 0.1.0-rc.8 | 支持 |
|
|
112
|
-
| 0.1.1-rc.2 | 支持 | 支持 | 官方 WebUI
|
|
117
|
+
| 0.1.0-rc.6 | 支持 | 不支持 | 窄 RPC 契约与文本兼容测试 |
|
|
118
|
+
| 0.1.0-rc.7 / rc.8 | 支持 | 契约核对 | client module / command slot 契约与服务端回退 |
|
|
119
|
+
| 0.1.1-rc.2 | 支持 | 支持 | 官方 WebUI 实装:doctor 13/13、编辑/确认/自动跳转、三次重复门禁 |
|
|
120
|
+
| 0.1.2-alpha.2 | 支持 | 支持 | 官方 npm 实装:typed controllers 13/13、拆分 client 构建、编辑/确认/PTC 自动跳转、干净卸载 |
|
|
113
121
|
|
|
114
122
|
CI 覆盖 Node.js 22/24。每次升级 Harness 后先跑 `/bridge --doctor`;缺哪个必要网关方法会被直接点名。
|
|
115
123
|
|
|
116
124
|
当前边界:
|
|
117
125
|
|
|
118
126
|
- 安装后需要重启一次 WebUI;
|
|
119
|
-
-
|
|
120
|
-
-
|
|
127
|
+
- 原生卡片通过官方 Session runtime 自动打开目标;旧客户端仍回退为标题和 ID;
|
|
128
|
+
- worker 运行时立即显示进度;本次三次固定样本的 worker 用时为 7.4–12.8 秒,`previewTimeoutMs` 仍是硬上限;
|
|
121
129
|
- 纯文本模型无法读取未解析原图;
|
|
122
|
-
-
|
|
130
|
+
- 原生卡片重复门禁也只有三次固定输入,是发布证据,不是统计保证。
|
|
123
131
|
|
|
124
|
-
|
|
132
|
+
服务端命令仍是兼容核心。同一个包现在附带可选的官方 WebUI client half,负责渲染、编辑和跳转;即使 prerelease 客户端契约加载失败,`/bridge` 的完整服务端结果仍在。详见[实现边界](docs/native-webui-feasibility.md)。
|
|
125
133
|
|
|
126
134
|
## 文档
|
|
127
135
|
|
|
@@ -129,6 +137,8 @@ CI 覆盖 Node.js 22/24。每次升级 Harness 后先跑 `/bridge --doctor`;
|
|
|
129
137
|
- [中文安装、配置、回退与 FAQ](docs/guide.zh.md)
|
|
130
138
|
- [完整 release acceptance](reports/v0.2.3-e2e-report.md)
|
|
131
139
|
- [视觉迁移报告](reports/v0.2.6-rc11-vision-report.md)
|
|
140
|
+
- [原生 WebUI 重复验收](reports/native-workbench-2026-08-25.md)
|
|
141
|
+
- [DSH 0.1.2-alpha.2 兼容性验收](reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md)
|
|
132
142
|
- [历史压缩档位 benchmark](docs/benchmark.md)
|
|
133
143
|
|
|
134
144
|
## 开发验证
|
|
@@ -138,10 +148,12 @@ npm ci
|
|
|
138
148
|
npm run verify
|
|
139
149
|
```
|
|
140
150
|
|
|
141
|
-
`verify`
|
|
151
|
+
`verify` 会构建并类型检查插件两端、运行 167 项测试、核对 `lib/` 与数据集,再把真实 npm tarball 打包、安装并导入。测试不消耗模型 token。`prepublishOnly` 使用同一个 gate;GitHub Release 还会先检查 tag 与 `package.json` 版本一致,再走可信 npm 发布。
|
|
142
152
|
|
|
143
153
|
社区收录:[Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) · [Awesome DeepSeek Harness](https://github.com/Dominic789654/awesome-deepseek-harness)
|
|
144
154
|
|
|
155
|
+
生态发现入口:[dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI)。Bridge 仍按标准 DSH 插件安装;TUI/std 一致性适配另行跟踪。
|
|
156
|
+
|
|
145
157
|
## License
|
|
146
158
|
|
|
147
159
|
MIT
|
package/docs/design.md
CHANGED
|
@@ -75,8 +75,10 @@ Stored tool calls are valid only under the preset composition that produced them
|
|
|
75
75
|
|
|
76
76
|
Model and reasoning-effort changes are separate from preset migration and may still happen within one session.
|
|
77
77
|
|
|
78
|
-
##
|
|
78
|
+
## Adapter boundary
|
|
79
79
|
|
|
80
|
-
Bridge
|
|
80
|
+
Bridge's preview, safety, and migration engine depends on the typed `BridgeHost` port, not DSH RPC route names. Product/version-specific mapping is isolated in the in-process `ctx.apiProxy` and HTTP adapters. The legacy `Rpc` function remains accepted through a compatibility wrapper for the 0.2.x line.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
`/bridge --doctor` checks thirteen required semantic capabilities and names missing capabilities before mutation. Attachment recovery and goal clearing are optional enhancements and do not raise that baseline. Any future adapter must implement `BridgeHost` and pass the same fail-closed fixtures.
|
|
83
|
+
|
|
84
|
+
The server-side slash command remains the compatibility core. The package also ships an optional WebUI client half that occupies the official per-command slot, reuses `MarkdownText` / `JsonTree`, offers lossless Text/Markdown editing, submits the reviewed summary back to the same host command, and opens the created target through the official Session runtime. The React-free `dsh-plugin-bridge/client-contract` export lets other UIs reuse the same parser and wire while unknown UIs retain the server/file fallback. No folding, attachment, goal, or fail-closed policy is reimplemented in the browser. The version boundary and fallback are documented in [native-webui-feasibility.md](native-webui-feasibility.md).
|
package/docs/guide.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 1. 安装
|
|
6
6
|
|
|
7
|
-
前置:已安装 dsh(`dsh --version` 能输出版本,本插件已核对 0.1.0-rc.6 / rc.7 / rc.8
|
|
7
|
+
前置:已安装 dsh(`dsh --version` 能输出版本,本插件已核对 0.1.0-rc.6 / rc.7 / rc.8,在 0.1.1-rc.2 完成真实视觉迁移,并用官方 npm `0.1.2-alpha.2` 完成 typed-controller 安装、编辑、迁移与卸载验收),并有一个可跑的 web profile(跑过一次 `dsh web` 即会自动初始化)。
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
dsh plugin --profile web add dsh-plugin-bridge
|
|
@@ -13,7 +13,7 @@ dsh plugin --profile web add dsh-plugin-bridge
|
|
|
13
13
|
需要固定 GitHub tag 或 npm 暂时不可用时:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.
|
|
16
|
+
dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.1
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
发生了什么(不用手动干预):
|
|
@@ -57,7 +57,7 @@ dsh plugin --profile web remove dsh-plugin-bridge # 重启后生效
|
|
|
57
57
|
|
|
58
58
|
`/bridge code` 会:拉取并折叠会话历史 → 按字符预算取材(用户消息全文 + 最近几轮结论 + 最近一次 compaction 底稿)→ 起一个临时的压缩工人生成**固定五段摘要**(目标 / 当前状态 / 关键决策与约定 / 关键文件 / 下一步)→ 工人用完即归档 → 把摘要贴给你。
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
卡片会立即显示经过秒数;模型耗时取决于取材与路由。本次 `0.1.1-rc.2` 三次固定输入的 worker 用时为 7.4–12.8 秒,超出 `previewTimeoutMs` 仍会被取消。**认真扫一眼**:
|
|
61
61
|
|
|
62
62
|
- 目标对不对;
|
|
63
63
|
- 关键约定在不在——尤其是**数字**,漂移里最主要的一类就是端口被补全成 3000/8080;
|
|
@@ -66,7 +66,17 @@ dsh plugin --profile web remove dsh-plugin-bridge # 重启后生效
|
|
|
66
66
|
|
|
67
67
|
### 3.2 要改摘要
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
rc.7+ 的官方 WebUI 提供三种模式:
|
|
70
|
+
|
|
71
|
+
- 「预览」:渲染 Markdown;完整 JSON 文档显示为可展开的树;
|
|
72
|
+
- 「文本编辑」:把目标、当前状态、关键决策、关键文件、下一步拆成普通字段,列表逐项增删,不需要输入 Markdown 符号;
|
|
73
|
+
- 「Markdown」:保留完整源码和附录修改能力,适合开发者或自定义结构。
|
|
74
|
+
|
|
75
|
+
长内容只在卡片正文内滚动,顶部模式切换、复制和确认按钮不会随内容滚走。非标准五段、JSON 或自定义标题不会被猜测性转换:文本模式会提示改用 Markdown,原稿保持不变。切回「预览」核对,再点「确认迁移」,校对稿会逐字成为目标的唯一事实源并自动打开目标会话。
|
|
76
|
+
|
|
77
|
+
兼容官方 `conversation.chat.commandview` slot 的第三方 UI 会自动加载这张卡片。完全自研 UI 可以导入 `dsh-plugin-bridge/client-contract` 复用纯解析与编辑 contract;没有实现 slot 时仍走下面的服务端/文件回退,不影响迁移。
|
|
78
|
+
|
|
79
|
+
旧客户端或 CLI 仍可走文件回退:预览会把摘要写进一个文件并打印路径。改那个文件,然后:
|
|
70
80
|
|
|
71
81
|
```
|
|
72
82
|
/bridge code --go --file /tmp/dsh-bridge-xxxx/summary-....md
|
|
@@ -128,10 +138,10 @@ dsh-bridge migrate --to code --summary-file <path>
|
|
|
128
138
|
## 6. 常见问题
|
|
129
139
|
|
|
130
140
|
**Q:`/bridge` 打了没反应 / 提示未知命令?**
|
|
131
|
-
A:确认重启过 `dsh web`(插件在启动时挂载),并且用的是 `web` profile。命令注册依赖 `commands`
|
|
141
|
+
A:确认重启过 `dsh web`(插件在启动时挂载),并且用的是 `web` profile。命令注册依赖 `commands` 服务;执行时优先用 rc.2 的 `apiProxy`,alpha 则使用 typed Session / Workspace / Preset / Goal controllers。必要能力不完整时 `/bridge --doctor` 会逐项报告,不会假装可用。
|
|
132
142
|
|
|
133
143
|
**Q:升级了 dsh 之后还能用吗?**
|
|
134
|
-
A:先打一次 `/bridge --doctor`,它会告诉你这套 host
|
|
144
|
+
A:先打一次 `/bridge --doctor`,它会告诉你这套 host 暴露了十三项能力里的哪几项、当前模式是什么、生效配置是什么。全绿就是好的。缺能力它会点名,把那行连同你的 dsh 版本发到 issues 就行。rc.6 / rc.7 / rc.8 已逐条核对;0.1.1-rc.2 的原图迁移已实测;官方 npm `0.1.2-alpha.2` 的 typed-controller 13/13、三态编辑、PTC 自动跳转、暂停目标与干净卸载也已实测。
|
|
135
145
|
|
|
136
146
|
**Q:图片迁移需要怎么选模型?**
|
|
137
147
|
A:图片已有助手分析时,Bridge 逐字搬这段视觉证据,不需要再次烧视觉 token。图片还没被分析时,源会话应选 `deepseek-v4-flash-vision-exp` 等视觉路由;Bridge 会在 kickoff 前把源 provider/model/reasoning effort 复制到目标,再搬原图。文本模型仍不能识图,Bridge 也不会暗中启动本地视觉模型。
|
|
@@ -1,44 +1,72 @@
|
|
|
1
|
-
# Native WebUI migration card:
|
|
1
|
+
# Native WebUI migration card: implementation boundary
|
|
2
2
|
|
|
3
|
-
Status: **
|
|
3
|
+
Status: **implemented on main; the server command remains the compatibility fallback**.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Verified extension path
|
|
6
6
|
|
|
7
|
-
The official rc.
|
|
7
|
+
The official rc.7 through `0.1.1-rc.2` WebUI and the split `0.1.2-alpha.2` client packages support third-party browser modules through a package-level `dsh.client` manifest. The renderer exposes a slot registry, and official plugins register occupants such as conversation views, input docks, settings items, and command renderers. Bridge uses that path; it does not patch the DOM or fork the WebUI.
|
|
8
8
|
|
|
9
|
-
The
|
|
9
|
+
The host half hard-injects only `commands`: it prefers rc.2's `apiProxy` when present and otherwise maps alpha's typed Session, Workspace, Preset, and Goal controllers into `BridgeHost`. The client half registers the `bridge` key in `conversation.chat.commandview`, parses only the host result, renders Markdown or complete JSON, offers lossless five-section Text/Markdown editing, and sends the reviewed summary back through `/bridge --go --summary64`. `recordInput: false` keeps that encoded payload out of the durable command log.
|
|
10
10
|
|
|
11
11
|
## Product shape
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The native surface expresses migration verification rather than copying a generic dashboard:
|
|
14
14
|
|
|
15
|
-
1.
|
|
16
|
-
2.
|
|
17
|
-
3.
|
|
18
|
-
4.
|
|
19
|
-
5.
|
|
15
|
+
1. the ordinary `/bridge <preset>` command chooses the target and starts the preview;
|
|
16
|
+
2. the running command row immediately shows elapsed time and states that the source is untouched;
|
|
17
|
+
3. the settled card renders the five-section handoff and its warnings;
|
|
18
|
+
4. the user can switch among rendered Preview, non-developer Text fields, and exact Markdown source;
|
|
19
|
+
5. explicit confirmation invokes the same host command and then opens the created session through `ctx.sessions.open`.
|
|
20
20
|
|
|
21
|
-
The
|
|
21
|
+
The server result always retains the created title and session ID, and the file-edit path remains available to older clients and the CLI.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The package also exports `dsh-plugin-bridge/client-contract`, a React-free card/parser/editor model. A custom UI that implements the official keyed command-view slot receives the packaged card; another UI can map the pure contract to its own components. A UI that implements neither path still receives the complete server text and file fallback. Bridge never patches an unknown UI DOM to simulate compatibility.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- Adding a browser face introduces React and multiple DSH client peer dependencies to a package that currently needs only the host contract.
|
|
27
|
-
- A UI must not create a second migration implementation. The host-side preview, safety checks, and fail-closed behavior need to stay authoritative.
|
|
28
|
-
- Current WebUI navigation does not provide Bridge with a stable, documented way to jump to the newly created session, so a card cannot yet promise seamless navigation.
|
|
25
|
+
Custom clients do not need React or any DSH browser package to reuse the wire:
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
```ts
|
|
28
|
+
import {
|
|
29
|
+
buildBridgeMigrationCommand,
|
|
30
|
+
parseBridgeCard,
|
|
31
|
+
parseBridgeTextProjection,
|
|
32
|
+
} from 'dsh-plugin-bridge/client-contract'
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
const card = parseBridgeCard(commandOutcome)
|
|
35
|
+
if (card.phase === 'preview') {
|
|
36
|
+
const textFields = parseBridgeTextProjection(card.summary) // undefined => keep raw Markdown
|
|
37
|
+
const confirmLine = buildBridgeMigrationCommand(
|
|
38
|
+
card.targetPreset,
|
|
39
|
+
reviewedSummary,
|
|
40
|
+
card.lang,
|
|
41
|
+
card.previewId ?? '',
|
|
42
|
+
)
|
|
43
|
+
// Dispatch confirmLine through this UI's ordinary slash-command transport.
|
|
44
|
+
}
|
|
45
|
+
```
|
|
33
46
|
|
|
34
|
-
|
|
47
|
+
`previewId` binds confirmation to the exact preview that produced the card. Custom UIs should not cache or invent it; an absent ID means the server/client pair is too old for in-card confirmation and should use the printed file workflow.
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
- works on both wide and narrow official WebUI layouts;
|
|
38
|
-
- exposes the exact same preview file and warnings as `/bridge`;
|
|
39
|
-
- creates no target before confirmation;
|
|
40
|
-
- proves cleanup after plugin removal and restart;
|
|
41
|
-
- keeps `/bridge --doctor` and the host command usable when the client face fails;
|
|
42
|
-
- pins and tests every supported DSH client-package version.
|
|
49
|
+
## Compatibility boundary
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
- Client packages and slot contracts remain prerelease and version-coupled. They are optional peers; rc.6 and clients that cannot load the browser half retain the complete server command result.
|
|
52
|
+
- rc.2 guards both the parent `remote` face and `remote.commands`, and its generated command wire requires an explicit image array. Bundle tests pin both details.
|
|
53
|
+
- The browser does not own a second migration engine. History folding, summary generation, attachment policy, target creation, goal pause, and rollback remain host-authoritative.
|
|
54
|
+
- Edited summaries are bounded to 24,000 characters and base64url-encoded for one command invocation. Malformed or oversized payloads fail closed before target creation; emptiness checks do not trim the transmitted Markdown.
|
|
55
|
+
- A WebUI payload is accepted only while the exact preview ID for the matching source session and target preset is pending and unexpired. The preview is consumed before migration; retrying the same completed payload returns the first target result instead of creating another session, even if a newer preview has since been generated.
|
|
56
|
+
- Once a target session exists, an unconfirmed kickoff is returned as that same navigable target with a warning and cached result. Bridge does not restore the preview and create a second target when prompt delivery is ambiguous.
|
|
57
|
+
- The client contract shapes for rc.7, rc.8, `0.1.1-rc.1`, `0.1.1-rc.2`, and `0.1.2-alpha.2` were compared. Both rc.2 and alpha.2 have installed official-WebUI evidence; alpha.2 uses the split `ui-chat`, Session Controller, renderer, and Session UI declarations.
|
|
58
|
+
|
|
59
|
+
## Acceptance evidence
|
|
60
|
+
|
|
61
|
+
The 2026-08-25 installed-WebUI gate covered:
|
|
62
|
+
|
|
63
|
+
- official client-module load and disposal;
|
|
64
|
+
- wide and narrow layout behavior;
|
|
65
|
+
- server `/bridge --doctor` fallback and native rendering;
|
|
66
|
+
- immediate progress, rendered Markdown, editable text, and JSON tree rendering;
|
|
67
|
+
- no target before confirmation;
|
|
68
|
+
- reviewed-payload migration, paused goal, and automatic target navigation.
|
|
69
|
+
|
|
70
|
+
Three fixed real-model runs preserved five facts in both preview and target, with worker times from 7.4 to 12.8 seconds. See the [native workbench report](../reports/native-workbench-2026-08-25.md). This is release evidence, not a statistical guarantee.
|
|
71
|
+
|
|
72
|
+
The 2026-08-31 npm-installed alpha.2 gate additionally covered typed-controller doctor 13/13, split-client compilation, localized primitive labels, Text-to-Markdown review, PTC auto-open, paused goal, and clean removal. See the [alpha.2 compatibility report](../reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md).
|
package/lib/api-rpc.d.ts
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* 迁移——不需要端口、不需要 `DSH_WEB_URL`、不需要 bash、也不依赖模型愿不愿意
|
|
8
8
|
* 帮忙调用。
|
|
9
9
|
*
|
|
10
|
-
* `
|
|
11
|
-
* `/bridge`
|
|
10
|
+
* 本文件把这个产品表面收敛到 `BridgeHost`;迁移核心不再认识 RPC 路由名。
|
|
11
|
+
* `/bridge` 命令、CLI(走 HTTP)和评测 harness 仍复用同一套编排。
|
|
12
12
|
*/
|
|
13
|
+
import { type BridgeHost } from './host.ts';
|
|
13
14
|
import { type Rpc } from './rpc.ts';
|
|
14
15
|
/** 一个 unary 网关方法:`(request, signal) => 信封`。 */
|
|
15
16
|
type UnaryMethod = (request: {
|
|
@@ -61,4 +62,6 @@ export declare function probeApiProxy(apiProxy: Partial<ApiProxyLike> | undefine
|
|
|
61
62
|
* @param signal - 派发方的取消信号(命令处理器拿到的那个)。
|
|
62
63
|
*/
|
|
63
64
|
export declare function createApiProxyRpc(apiProxy: ApiProxyLike, signal?: AbortSignal): Rpc;
|
|
65
|
+
/** 当前 DSH 进程内网关的 BridgeHost adapter。 */
|
|
66
|
+
export declare function createApiProxyHost(apiProxy: ApiProxyLike, signal?: AbortSignal): BridgeHost;
|
|
64
67
|
export {};
|
package/lib/api-rpc.js
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* 迁移——不需要端口、不需要 `DSH_WEB_URL`、不需要 bash、也不依赖模型愿不愿意
|
|
8
8
|
* 帮忙调用。
|
|
9
9
|
*
|
|
10
|
-
* `
|
|
11
|
-
* `/bridge`
|
|
10
|
+
* 本文件把这个产品表面收敛到 `BridgeHost`;迁移核心不再认识 RPC 路由名。
|
|
11
|
+
* `/bridge` 命令、CLI(走 HTTP)和评测 harness 仍复用同一套编排。
|
|
12
12
|
*/
|
|
13
|
+
import { createBridgeHostFromRpc } from './host.js';
|
|
13
14
|
import { RpcError } from './rpc.js';
|
|
14
15
|
/** RPC 方法名 → `ctx.apiProxy` 上的位置。 */
|
|
15
16
|
const ROUTES = {
|
|
@@ -74,3 +75,10 @@ export function createApiProxyRpc(apiProxy, signal) {
|
|
|
74
75
|
return result.value;
|
|
75
76
|
};
|
|
76
77
|
}
|
|
78
|
+
/** 当前 DSH 进程内网关的 BridgeHost adapter。 */
|
|
79
|
+
export function createApiProxyHost(apiProxy, signal) {
|
|
80
|
+
return createBridgeHostFromRpc(createApiProxyRpc(apiProxy, signal), {
|
|
81
|
+
id: 'dsh-api-proxy',
|
|
82
|
+
transport: 'in-process',
|
|
83
|
+
});
|
|
84
|
+
}
|
package/lib/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { tmpdir } from 'node:os';
|
|
|
19
19
|
import { join } from 'node:path';
|
|
20
20
|
import { SOURCE_CHAR_BUDGET, SUMMARY_CHAR_BUDGET, estimateSummaryTokens } from './compression.js';
|
|
21
21
|
import { executeMigration, findSession, listPresets, migratedTitle, previewMigration, resolveWorkerModel, titleOf, } from './migrate.js';
|
|
22
|
+
import { createBridgeHostFromRpc } from './host.js';
|
|
22
23
|
import { RpcError, createRpc, resolveApiBase } from './rpc.js';
|
|
23
24
|
const HELP = `dsh-bridge · 跨 preset 会话迁移
|
|
24
25
|
|
|
@@ -151,6 +152,7 @@ async function main(argv) {
|
|
|
151
152
|
const quiet = bool(args, 'quiet') || json;
|
|
152
153
|
const api = resolveApiBase(str(args, 'api'));
|
|
153
154
|
const rpc = createRpc({ api, prefix: 'bridge-cli' });
|
|
155
|
+
const host = createBridgeHostFromRpc(rpc, { id: 'dsh-http-api', transport: 'http' });
|
|
154
156
|
const progress = (message) => {
|
|
155
157
|
if (!quiet)
|
|
156
158
|
process.stderr.write(`… ${message}\n`);
|
|
@@ -162,7 +164,7 @@ async function main(argv) {
|
|
|
162
164
|
case 'doctor': {
|
|
163
165
|
const report = { api, sessionIdFromEnv: process.env.DSH_SESSION_ID ?? null };
|
|
164
166
|
try {
|
|
165
|
-
const presets = await listPresets(
|
|
167
|
+
const presets = await listPresets(host);
|
|
166
168
|
report.gateway = 'ok';
|
|
167
169
|
report.presets = presets.map((p) => p.id);
|
|
168
170
|
}
|
|
@@ -171,7 +173,7 @@ async function main(argv) {
|
|
|
171
173
|
}
|
|
172
174
|
const sessionId = str(args, 'session') ?? process.env.DSH_SESSION_ID;
|
|
173
175
|
if (sessionId) {
|
|
174
|
-
const row = await findSession(
|
|
176
|
+
const row = await findSession(host, sessionId).catch(() => undefined);
|
|
175
177
|
report.session = row ? { sessionId: row.sessionId, agentPreset: row.agentPreset, cwd: row.cwd } : 'not-found';
|
|
176
178
|
}
|
|
177
179
|
out(report, () => {
|
|
@@ -187,8 +189,8 @@ async function main(argv) {
|
|
|
187
189
|
}
|
|
188
190
|
case 'presets': {
|
|
189
191
|
const sessionId = str(args, 'session') ?? process.env.DSH_SESSION_ID;
|
|
190
|
-
const current = sessionId ? (await findSession(
|
|
191
|
-
const presets = await listPresets(
|
|
192
|
+
const current = sessionId ? (await findSession(host, sessionId).catch(() => undefined))?.agentPreset : undefined;
|
|
193
|
+
const presets = await listPresets(host);
|
|
192
194
|
const rows = presets.map((p) => ({ ...p, current: p.id === current }));
|
|
193
195
|
out({ current, presets: rows }, () => {
|
|
194
196
|
const lines = rows.map((p) => {
|
|
@@ -204,7 +206,7 @@ async function main(argv) {
|
|
|
204
206
|
case 'preview': {
|
|
205
207
|
const sessionId = resolveSessionId(args);
|
|
206
208
|
const to = str(args, 'to');
|
|
207
|
-
const result = await previewMigration(
|
|
209
|
+
const result = await previewMigration(host, {
|
|
208
210
|
sessionId,
|
|
209
211
|
tier: tierOf(args),
|
|
210
212
|
provider: str(args, 'provider'),
|
|
@@ -276,8 +278,8 @@ async function main(argv) {
|
|
|
276
278
|
catch (error) {
|
|
277
279
|
throw new UsageError(`读不到摘要文件 ${file}:${error instanceof Error ? error.message : String(error)}`);
|
|
278
280
|
}
|
|
279
|
-
const source = await findSession(
|
|
280
|
-
const result = await executeMigration(
|
|
281
|
+
const source = await findSession(host, sessionId).catch(() => undefined);
|
|
282
|
+
const result = await executeMigration(host, {
|
|
281
283
|
sessionId,
|
|
282
284
|
sourceSession: source,
|
|
283
285
|
to,
|
|
@@ -311,7 +313,7 @@ async function main(argv) {
|
|
|
311
313
|
case 'run': {
|
|
312
314
|
const sessionId = resolveSessionId(args);
|
|
313
315
|
const to = requireTarget(args);
|
|
314
|
-
const preview = await previewMigration(
|
|
316
|
+
const preview = await previewMigration(host, {
|
|
315
317
|
sessionId,
|
|
316
318
|
tier: tierOf(args),
|
|
317
319
|
provider: str(args, 'provider'),
|
|
@@ -323,7 +325,7 @@ async function main(argv) {
|
|
|
323
325
|
...(num(args, 'worker-timeout') === undefined ? {} : { workerTimeoutMs: num(args, 'worker-timeout') }),
|
|
324
326
|
onProgress: progress,
|
|
325
327
|
});
|
|
326
|
-
const result = await executeMigration(
|
|
328
|
+
const result = await executeMigration(host, {
|
|
327
329
|
sessionId,
|
|
328
330
|
sourceSession: preview.sourceSession,
|
|
329
331
|
to,
|
|
@@ -342,7 +344,7 @@ async function main(argv) {
|
|
|
342
344
|
case 'worker-model': {
|
|
343
345
|
// 诊断用:只回答「档位会挑到哪个模型」,不建任何会话。
|
|
344
346
|
const sessionId = resolveSessionId(args);
|
|
345
|
-
const route = await resolveWorkerModel(
|
|
347
|
+
const route = await resolveWorkerModel(host, sessionId, tierOf(args), {
|
|
346
348
|
provider: str(args, 'provider'),
|
|
347
349
|
model: str(args, 'model'),
|
|
348
350
|
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Pure wire-to-view helpers shared by the native WebUI card and Node tests. */
|
|
2
|
+
export declare const MAX_EDITED_SUMMARY_CHARS = 24000;
|
|
3
|
+
export type BridgeOutcome = {
|
|
4
|
+
kind: 'success' | 'error';
|
|
5
|
+
text?: string;
|
|
6
|
+
} | null;
|
|
7
|
+
export type BridgeCard = {
|
|
8
|
+
phase: 'running';
|
|
9
|
+
} | {
|
|
10
|
+
phase: 'preview';
|
|
11
|
+
lang: 'zh' | 'en';
|
|
12
|
+
sourcePreset: string;
|
|
13
|
+
targetPreset: string;
|
|
14
|
+
previewId?: string;
|
|
15
|
+
summary: string;
|
|
16
|
+
summaryFile?: string;
|
|
17
|
+
stats: string;
|
|
18
|
+
warnings: string[];
|
|
19
|
+
} | {
|
|
20
|
+
phase: 'migrated';
|
|
21
|
+
lang: 'zh' | 'en';
|
|
22
|
+
targetPreset: string;
|
|
23
|
+
title: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
details: string[];
|
|
26
|
+
warnings: string[];
|
|
27
|
+
} | {
|
|
28
|
+
phase: 'error';
|
|
29
|
+
text: string;
|
|
30
|
+
} | {
|
|
31
|
+
phase: 'message';
|
|
32
|
+
text: string;
|
|
33
|
+
lang: 'zh' | 'en';
|
|
34
|
+
};
|
|
35
|
+
export type BridgeTextSectionKey = 'goal' | 'currentState' | 'keyDecisions' | 'keyFiles' | 'nextStep';
|
|
36
|
+
export interface BridgeTextSection {
|
|
37
|
+
key: BridgeTextSectionKey;
|
|
38
|
+
label: string;
|
|
39
|
+
kind: 'text' | 'list';
|
|
40
|
+
/** Plain-text editor value. List markers are intentionally omitted. */
|
|
41
|
+
text: string;
|
|
42
|
+
/** Exact source span replaced when this field is edited. */
|
|
43
|
+
bodyStart: number;
|
|
44
|
+
bodyEnd: number;
|
|
45
|
+
items?: BridgeTextListItem[];
|
|
46
|
+
listStyle?: 'bullet' | 'plain';
|
|
47
|
+
textStyle?: 'plain' | 'bullets';
|
|
48
|
+
}
|
|
49
|
+
export interface BridgeTextListItem {
|
|
50
|
+
text: string;
|
|
51
|
+
contentStart: number;
|
|
52
|
+
contentEnd: number;
|
|
53
|
+
itemStart: number;
|
|
54
|
+
itemEnd: number;
|
|
55
|
+
/** Original indentation for wrapped lines, kept when that item is edited. */
|
|
56
|
+
continuationPrefixes?: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface BridgeTextProjection {
|
|
59
|
+
lang: 'zh' | 'en';
|
|
60
|
+
/** The untouched Markdown document used to enter text mode. */
|
|
61
|
+
markdown: string;
|
|
62
|
+
lineEnding: '\n' | '\r\n';
|
|
63
|
+
sections: BridgeTextSection[];
|
|
64
|
+
/** Known appendices are visible but read-only in plain-text mode. */
|
|
65
|
+
opaqueSuffix: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Project only Bridge's exact bilingual five-section schema into plain fields.
|
|
69
|
+
* Unknown structure fails closed; known visual appendices remain opaque Markdown.
|
|
70
|
+
*/
|
|
71
|
+
export declare function parseBridgeTextProjection(markdown: string): BridgeTextProjection | undefined;
|
|
72
|
+
/** Replace one editable body while preserving every byte outside that section. */
|
|
73
|
+
export declare function replaceBridgeTextSection(projection: BridgeTextProjection, key: BridgeTextSectionKey, plainText: string): string;
|
|
74
|
+
/** Replace one list item without rewriting siblings and while retaining wrapped-line indentation. */
|
|
75
|
+
export declare function replaceBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, index: number, plainText: string): string;
|
|
76
|
+
/** Remove exactly one original list-item span. */
|
|
77
|
+
export declare function removeBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, index: number): string;
|
|
78
|
+
/** Append one item using the section's existing bullet/plain convention. */
|
|
79
|
+
export declare function appendBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, plainText: string): string;
|
|
80
|
+
/** Map the official WebUI document language onto Bridge's supported UI copy. */
|
|
81
|
+
export declare function uiLanguageOf(documentLang: string | undefined): 'zh' | 'en';
|
|
82
|
+
/** Convert one durable `/bridge` outcome into the native card's view model. */
|
|
83
|
+
export declare function parseBridgeCard(outcome: BridgeOutcome): BridgeCard;
|
|
84
|
+
/** Return a value only when the complete editor document is valid JSON. */
|
|
85
|
+
export declare function parseJsonDocument(text: string): object | unknown[] | undefined;
|
|
86
|
+
/** Build the hidden-input-safe command used by the native editor confirmation. */
|
|
87
|
+
export declare function buildBridgeMigrationCommand(targetPreset: string, summary: string, lang: 'zh' | 'en', previewId: string): string;
|