dsh-cherry-bridge 0.1.11 → 0.2.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.en.md +15 -8
- package/README.md +13 -6
- package/dist/index.d.mts +32 -3
- package/dist/index.mjs +1563 -168
- package/docs/e2e-checklist.md +50 -2
- package/lib/client.js +311 -31
- package/package.json +4 -3
- package/src/AGENTS.md +949 -0
- package/src/connection.ts +287 -11
- package/src/deviceStore.ts +518 -6
- package/src/diagnostics.ts +30 -0
- package/src/httpApi.ts +351 -23
- package/src/index.ts +203 -8
- package/src/pairing.ts +504 -112
- package/src/rpc/jsonRpcClient.ts +19 -1
- package/src/subscriptions.ts +10 -2
- package/src/text.ts +158 -0
- package/src/tools.ts +329 -23
- package/ui/AGENTS.md +44 -0
- package/ui/src/App.tsx +26 -10
- package/ui/src/DeviceCard.tsx +141 -4
- package/ui/src/PairingWizard.tsx +80 -5
- package/ui/src/ToolScopePanel.tsx +19 -6
- package/ui/src/api.ts +111 -17
- package/ui/src/i18n.ts +49 -0
- package/ui/src/styles.css +24 -0
- package/ui/src/types.ts +16 -1
- package/src/urlHost.ts +0 -15
package/README.en.md
CHANGED
|
@@ -28,23 +28,25 @@ flowchart TB
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
- Package: `dsh-cherry-bridge`, plugin id: `cherry-remote`
|
|
31
|
-
- Transport: `ws://` + Noise XX (encryption lives in the Noise record layer;
|
|
31
|
+
- Transport: `ws://` + Noise XX by default (encryption lives in the Noise record layer); a desktop behind a TLS front is reached over `wss://`, with `wsTls` supplying the private-CA trust
|
|
32
32
|
- Tool face: 14 `cherry_*` tools, enabled **per workspace**, and by default **nobody has them** (see “Scope”)
|
|
33
33
|
- Settings page and HTTP face: the same-origin prefix `/cherry-remote/api` on the host web server
|
|
34
34
|
|
|
35
35
|
## Install
|
|
36
36
|
|
|
37
37
|
> **DSH version requirement**: `>=0.2.0-rc.2 <0.3.0-0` (declared via `engines.dsh` in `package.json`; when unmet the bundle is skipped and its settings-page row disabled — `dsh plugin allow-version` grants an explicit exemption; 0.3.0 needs a fresh compatibility pass before the range widens).
|
|
38
|
+
>
|
|
39
|
+
> **Cherry Studio compatibility**: desktop **2.1.4 – 2.1.10 is verified** — the `v3` custom-address QR needs **2.1.8+**, and the `v2` QR of 2.1.4 – 2.1.7 works too. 2.1.9/2.1.10 differ from 2.1.8 on the remote-access face by exactly one robustness fix in the desktop's write chain (PR #22593, no client impact), and were smoke-tested on a real 2.1.10 (create a session → send → wait → read). Outside that range is unverified: the handshake negotiates protocol version `[1]` only, so an incompatible peer fails loudly as `unreachable` instead of misbehaving. The protocol packages are pinned to `0.7.0` / `0.2.5` — 2.1.10 still ships that same pair.
|
|
38
40
|
|
|
39
41
|
```bash
|
|
40
42
|
# From npm (recommended, pinned)
|
|
41
|
-
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge@0.1
|
|
43
|
+
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge@0.2.1
|
|
42
44
|
|
|
43
45
|
# Install the latest npm release
|
|
44
46
|
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge
|
|
45
47
|
|
|
46
48
|
# From GitHub source (same commit as the npm release)
|
|
47
|
-
npx @deepseek-ai/dsh plugin --profile web add github:SAXEM1997/dsh-cherry-bridge#v0.1
|
|
49
|
+
npx @deepseek-ai/dsh plugin --profile web add github:SAXEM1997/dsh-cherry-bridge#v0.2.1
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
The package ships `cordis.patch.yml` (`package.json`'s `dsh.bundle.patch` points
|
|
@@ -93,6 +95,9 @@ cherry_interactions → see pending question/approval cards; cherry_respond answ
|
|
|
93
95
|
| `dataDir` | string | `$DSH_HOME/cherry-remote` (fallback `~/.dsh/cherry-remote`) | Plugin data directory holding `identity.key` and `devices.json` (both `0600`). |
|
|
94
96
|
| `perSessionCacheBytes` | number | `2097152` (2 MiB) | Per-session cap on the **local** subscription event cache; beyond it the oldest events are dropped LRU-style with a `warn`. |
|
|
95
97
|
| `rateBudgetPerSecond` | number | `32` | Per-device smoothed request budget (req/s). The desktop's shared allowance is 64 req/s through 2.1.6 and 256 req/s from 2.1.7, and only the newer generation downgrades an overrun to a single `RATE_LIMITED` refusal (the older one terminates the session with close `1011`) — hence a value sized for the harsher generation. Content reads (`agent.content.read`) derive their own tier at 8:1, **capped at 512/s** (that allowance is the same in both generations). |
|
|
98
|
+
| `wsTls` | object | — | **Flat** `tls.connect` options for every `wss` dial (`rejectUnauthorized` / `ca` only): operator-supplied trust for a desktop behind a private CA or a self-signed TLS front. Applied to BOTH dial sites — pairing and reconnect. **Never written to the device archive, never logged, never in diagnostics.** The whole object applies as written or is ignored entirely: an unknown key or unusable value warns by name and falls back to Node's default trust store (no partial application). |
|
|
99
|
+
|
|
100
|
+
> `wsTls.ca` is **PEM CONTENT** (the certificate text itself), not a file path.
|
|
96
101
|
|
|
97
102
|
> ⚠️ `cordis.patch.yml`'s `config` is **whole-replace**, not merged with the
|
|
98
103
|
> defaults. Removing a key gets you the plugin default; a key that exists but is
|
|
@@ -102,7 +107,7 @@ cherry_interactions → see pending question/approval cards; cherry_respond answ
|
|
|
102
107
|
|
|
103
108
|
| Tool | What it does |
|
|
104
109
|
| --- | --- |
|
|
105
|
-
| `cherry_devices` | List every paired device and its connection state (`connected`/`connecting`/`offline`/`disabled`/`identity-mismatch`). |
|
|
110
|
+
| `cherry_devices` | List every paired device and its connection state (`connected`/`connecting`/`offline`/`disabled`/`identity-mismatch`). `action: "forget"` makes named addresses go away (removed from the dial list, and not re-learned when the desktop advertises them again, until `remember` or a re-pair); `action: "remember"` lifts that mark so the next handshake may learn them again. Both write actions require `device` and `hosts`. |
|
|
106
111
|
| `cherry_agents` | List the agents available on a device (paginated). A truncated answer always carries `truncated` + `truncationReason` (`limit` or `safety-bound`, the latter the 1000-item internal ceiling); when the stop point can be named it also carries a `resumeCursor`: replay it as `cursor` with the same device and filters to continue from that stop point (`limit` then bounds the next answer). A token from another tool, device or filter set is refused **locally**, never forwarded. |
|
|
107
112
|
| `cherry_workspaces` | List an agent's **registered** workspaces, and whether a system workspace exists. A truncated answer always carries `truncated` + `truncationReason`; when the stop point can be named it also carries a `resumeCursor` — replay it as `cursor` with the same filters to continue. |
|
|
108
113
|
| `cherry_sessions` | List an agent's sessions, optionally filtered by workspace. A truncated answer always carries `truncated` + `truncationReason` (`limit` or `safety-bound`); when the stop point can be named it also carries a `resumeCursor`: replay it as `cursor` with the same device and **identical filters** to continue (`limit` then bounds the next answer); a token from another tool, device or filter set is refused **locally**. |
|
|
@@ -200,7 +205,9 @@ subscription was lost and could not be rebuilt, so the outcome is
|
|
|
200
205
|
- **Known limitations**: `cherry_respond` lets a DSH agent approve Cherry-side
|
|
201
206
|
approval cards on the user's behalf (the MVP adds no interception layer and
|
|
202
207
|
trusts DSH's own tool-call approvals); only the `agent` capability is
|
|
203
|
-
requested per device; `ws://`
|
|
208
|
+
requested per device; transport is `ws://` by default, and `wss://` requires a
|
|
209
|
+
TLS front in front of the desktop (this plugin never terminates TLS; `wsTls`
|
|
210
|
+
carries the private-CA trust).
|
|
204
211
|
|
|
205
212
|
> Engineering detail — replay rules, the 32 MiB/≈3× memory contract, empty-page
|
|
206
213
|
> and cursor tolerance, integrity semantics — lives in [`AGENTS.md`](AGENTS.md).
|
|
@@ -210,9 +217,9 @@ subscription was lost and could not be rebuilt, so the outcome is
|
|
|
210
217
|
The plugin is also a **web client bundle** (`package.json`'s `dsh.client`,
|
|
211
218
|
exporting `./client`). Through the `settings.section` slot it registers a
|
|
212
219
|
**`cherry-remote`** page (nav label “Cherry Bridge”): device cards
|
|
213
|
-
(enable/disable/remove
|
|
214
|
-
scope panel, and live state pushed over the
|
|
215
|
-
(`/cherry-remote/api/events`).
|
|
220
|
+
(enable/disable/remove, plus forget/restore for an address), a pairing wizard
|
|
221
|
+
(paste → 6-digit code → verdict), a scope panel, and live state pushed over the
|
|
222
|
+
same-origin SSE feed (`/cherry-remote/api/events`).
|
|
216
223
|
|
|
217
224
|
The 14 tools appear **per workspace**, by **hiding, not refusing** (in an
|
|
218
225
|
out-of-scope session they never enter the tool table, saving context):
|
package/README.md
CHANGED
|
@@ -26,23 +26,25 @@ flowchart TB
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
- 包名:`dsh-cherry-bridge`,插件 id:`cherry-remote`
|
|
29
|
-
-
|
|
29
|
+
- 传输:默认 `ws://` + Noise XX(加密由 Noise 记录层提供);桌面端位于 TLS 前置之后时拨 `wss://`,私有 CA 的信任由 `wsTls` 提供
|
|
30
30
|
- 工具面:14 个 `cherry_*` 工具,**按工作区**启用,默认**谁都没有**(见「启用范围」)
|
|
31
31
|
- 设置页与 HTTP 面:宿主 Web 服务器上的同源前缀 `/cherry-remote/api`
|
|
32
32
|
|
|
33
33
|
## 安装
|
|
34
34
|
|
|
35
35
|
> **DSH 版本需求**:`>=0.2.0-rc.2 <0.3.0-0`(由 `package.json` 的 `engines.dsh` 声明;不满足时 bundle 被跳过、设置页里该插件的行被禁用,确需在区间外使用可用 `dsh plugin allow-version` 精确豁免。0.3.0 需重新评估兼容性后再放开)。
|
|
36
|
+
>
|
|
37
|
+
> **Cherry Studio 兼容范围**:桌面端 **2.1.4 – 2.1.10 已核对**——`v3` 自定义地址二维码需 **2.1.8+**,2.1.4–2.1.7 的 `v2` 二维码同样可用。2.1.9/2.1.10 与 2.1.8 在 remote-access 面上只差桌面端发送链的一处健壮性修复(PR #22593,不影响客户端),并在 2.1.10 真机上跑过「建会话 → 发送 → 等待 → 读取」冒烟。区间外未经验证:握手只协商协议版本 `[1]`,不兼容时会以 `unreachable` 明确失败,不会静默出错。协议包钉在 `0.7.0` / `0.2.5`,2.1.10 携带的还是同一对版本。
|
|
36
38
|
|
|
37
39
|
```bash
|
|
38
40
|
# 从 npm 安装(推荐,钉住版本)
|
|
39
|
-
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge@0.1
|
|
41
|
+
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge@0.2.1
|
|
40
42
|
|
|
41
43
|
# 安装 npm 最新发布版
|
|
42
44
|
npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge
|
|
43
45
|
|
|
44
46
|
# 从 GitHub 源码安装(与 npm 发布同一提交)
|
|
45
|
-
npx @deepseek-ai/dsh plugin --profile web add github:SAXEM1997/dsh-cherry-bridge#v0.1
|
|
47
|
+
npx @deepseek-ai/dsh plugin --profile web add github:SAXEM1997/dsh-cherry-bridge#v0.2.1
|
|
46
48
|
```
|
|
47
49
|
|
|
48
50
|
本包自带 `cordis.patch.yml`(`package.json` 的 `dsh.bundle.patch` 指向它),
|
|
@@ -85,6 +87,9 @@ cherry_interactions → 看待答的提问/审批卡;cherry_respond 作答
|
|
|
85
87
|
| `dataDir` | string | `$DSH_HOME/cherry-remote`(回退 `~/.dsh/cherry-remote`) | 插件数据目录,存放 `identity.key` 与 `devices.json`(均 `0600`)。 |
|
|
86
88
|
| `perSessionCacheBytes` | number | `2097152`(2 MiB) | 每个会话的**本地**订阅事件缓存上限,超出按 LRU 丢弃最旧事件并 `warn`。 |
|
|
87
89
|
| `rateBudgetPerSecond` | number | `32` | 每设备平滑请求预算(请求/秒)。桌面端共享额度在 ≤2.1.6 是 64 req/s、≥2.1.7 是 256 req/s,且只有新版本才把超限降级为单请求 `RATE_LIMITED`(旧版本直接 close `1011` 终止会话),故按更严格的一代取保守值。内容读取(`agent.content.read`)按 8:1 派生独立一档,且**封顶 512/s**(该额度两代一致)。 |
|
|
90
|
+
| `wsTls` | object | — | 每次 `wss` 拨号的**扁平** `tls.connect` 选项(只接受 `rejectUnauthorized` / `ca`),供桌面端位于私有 CA 或自签 TLS 前置之后时由运维提供信任;配对与重连**两个拨号点**共用。**不写入设备档案、不写日志、不进诊断报告。** 整个对象要么按原样生效、要么整体忽略:未知键或不可用值按名 `warn` 后回退 Node 默认信任库(不部分应用)。 |
|
|
91
|
+
|
|
92
|
+
> `wsTls.ca` 是 **PEM 内容**(证书文本本身),不是文件路径。
|
|
88
93
|
|
|
89
94
|
> ⚠️ `cordis.patch.yml` 的 `config` 是**整体替换**,不与默认值合并。删掉一个键得到的是
|
|
90
95
|
> 插件默认值;键存在但值非法(`0`、负数、字符串)会按名 `warn` 后回落默认值。
|
|
@@ -93,7 +98,7 @@ cherry_interactions → 看待答的提问/审批卡;cherry_respond 作答
|
|
|
93
98
|
|
|
94
99
|
| 工具 | 作用 |
|
|
95
100
|
| --- | --- |
|
|
96
|
-
| `cherry_devices` | 列出已配对设备及连接状态(`connected`/`connecting`/`offline`/`disabled`/`identity-mismatch
|
|
101
|
+
| `cherry_devices` | 列出已配对设备及连接状态(`connected`/`connecting`/`offline`/`disabled`/`identity-mismatch`)。`action: "forget"` 可遗忘指定地址(从拨号列表移除,且桌面再广播也不会被重新学习,直到 `remember` 或重新配对);`action: "remember"` 撤销该标记(下次握手可重新学习);两者都需要 `device` 与 `hosts`。 |
|
|
97
102
|
| `cherry_agents` | 列出某设备上可用的 agent(分页)。被截断时必带 `truncated`+`truncationReason`(`limit` 或 `safety-bound`,后者是 1000 条内部上限);停点可命名时还给出 `resumeCursor`——用同一 device 与过滤条件把它回传为 `cursor` 即可从停点继续(此时 `limit` 约束的是下一次回答);跨工具/设备/过滤条件的令牌会被**本地拒绝**,不会转发给对端。 |
|
|
98
103
|
| `cherry_workspaces` | 列出某 agent 的**已注册**工作区,以及是否存在系统工作区。被截断时必带 `truncated`+`truncationReason`;停点可命名时还给出 `resumeCursor`——用相同过滤条件回传为 `cursor` 即可续读。 |
|
|
99
104
|
| `cherry_sessions` | 列出某 agent 的会话,可按工作区过滤。被截断时必带 `truncated`+`truncationReason`(`limit` 或 `safety-bound`),停点可命名时还给出 `resumeCursor`——用同一 device 与**相同过滤条件**把它回传为 `cursor` 即可从停点继续(此时 `limit` 约束的是下一次回答);跨工具/设备/过滤条件的令牌会被**本地拒绝**。 |
|
|
@@ -155,7 +160,8 @@ cherry_interactions → 看待答的提问/审批卡;cherry_respond 作答
|
|
|
155
160
|
idle 回答的历史走查仍从那一刻起算自己的预算(慢但成功的快照不会把走查挤成零);
|
|
156
161
|
代价是两段预算**分开计**,病态调用可能把两段都花掉。
|
|
157
162
|
- **已知限制**:`cherry_respond` 让 DSH agent 可代替用户批准 Cherry 端审批卡(MVP 不加拦截层,
|
|
158
|
-
信任 DSH 自身的工具审批);每设备仅申请 `agent` capability
|
|
163
|
+
信任 DSH 自身的工具审批);每设备仅申请 `agent` capability;传输默认 `ws://`,
|
|
164
|
+
`wss://` 要求桌面端位于 TLS 前置之后(本插件不做 TLS 终止;私有 CA 的信任由 `wsTls` 提供)。
|
|
159
165
|
|
|
160
166
|
> 传输重放、32 MiB/≈3× 内存、空页与游标容忍、完整性语义等工程细节见 [`AGENTS.md`](AGENTS.md)。
|
|
161
167
|
|
|
@@ -163,7 +169,8 @@ cherry_interactions → 看待答的提问/审批卡;cherry_respond 作答
|
|
|
163
169
|
|
|
164
170
|
插件同时是 **web client bundle**(`package.json` 的 `dsh.client`,导出 `./client`),通过
|
|
165
171
|
`settings.section` 槽位注册 **`cherry-remote`**(导航标签「Cherry 互联」)页面:
|
|
166
|
-
|
|
172
|
+
设备卡片(启用/停用/删除,地址的「忘记 / 恢复」)、配对向导(粘贴 → 6 位验证码 → 终态)、
|
|
173
|
+
启用范围面板,以及经
|
|
167
174
|
`/cherry-remote/api/events` SSE 推送的实时状态。
|
|
168
175
|
|
|
169
176
|
14 个工具**按工作区**决定是否出现,机制是**隐藏而非拒绝**(不在范围的会话里不进工具表,省上下文):
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { connectSecureChannel } from "@cherrystudio/remote-transport";
|
|
2
|
+
import "@cherrystudio/remote-protocol";
|
|
2
3
|
import "@cherrystudio/remote-protocol/agent";
|
|
4
|
+
//#region src/connection.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options handed to every `WebSocket` dial (issue #126).
|
|
7
|
+
*
|
|
8
|
+
* FLAT `tls.connect` options — `ws` forwards its own option bag straight to
|
|
9
|
+
* `tls.connect`, so `{ rejectUnauthorized: false }` or `{ ca: <pem> }` belong
|
|
10
|
+
* at the top level rather than under a `tls:` key.
|
|
11
|
+
*/
|
|
12
|
+
type WsDialOptions = {
|
|
13
|
+
rejectUnauthorized?: boolean;
|
|
14
|
+
ca?: string | Buffer | Array<string | Buffer>;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
3
17
|
//#region src/tools.d.ts
|
|
4
18
|
/**
|
|
5
19
|
* The plugin's tool face: the complete fourteen-tool `cherry_*` family — six
|
|
@@ -264,10 +278,24 @@ export interface CherryBridgeConfig {
|
|
|
264
278
|
* (not a protocol value). Omitted uses {@link DEFAULT_PER_SESSION_CACHE_BYTES}.
|
|
265
279
|
*/
|
|
266
280
|
perSessionCacheBytes?: number;
|
|
281
|
+
/**
|
|
282
|
+
* FLAT `tls.connect` options for every `wss` dial (issue #133): the operator's
|
|
283
|
+
* TLS trust for a desktop behind a private CA or a self-signed TLS front.
|
|
284
|
+
* `ws` forwards this bag straight to Node's TLS layer, so `{ ca: <pem> }` and
|
|
285
|
+
* `{ rejectUnauthorized: false }` belong at the TOP level — the `{ tls: {…} }`
|
|
286
|
+
* shape (`https`' wrapper) is not recognised by `ws` and is refused here.
|
|
287
|
+
* Applied to BOTH dial sites: the steady-state pool and pairing.
|
|
288
|
+
*
|
|
289
|
+
* NEVER persisted in the record, never logged, never reported: a `ca` is PEM
|
|
290
|
+
* CONTENT, and trust is a grant the operator makes, not a fact the desktop
|
|
291
|
+
* asserts about itself.
|
|
292
|
+
*/
|
|
293
|
+
wsTls?: WsDialOptions;
|
|
267
294
|
}
|
|
268
295
|
/**
|
|
269
|
-
* The
|
|
270
|
-
* applied
|
|
296
|
+
* The plugin-config values that reach constructor arguments, resolved once: the
|
|
297
|
+
* two numeric budgets with their defaults applied, and the validated TLS trust
|
|
298
|
+
* bag (see {@link dialOptions}).
|
|
271
299
|
*
|
|
272
300
|
* Resolved ONCE per `apply` and threaded explicitly, rather than re-read at each
|
|
273
301
|
* construction site: a config surface that can change between two constructions
|
|
@@ -276,8 +304,9 @@ export interface CherryBridgeConfig {
|
|
|
276
304
|
export interface ResolvedBridgeConfig {
|
|
277
305
|
rateBudgetPerSecond: number;
|
|
278
306
|
perSessionCacheBytes: number;
|
|
307
|
+
wsTls: WsDialOptions | undefined;
|
|
279
308
|
}
|
|
280
|
-
/** Applies the documented fallbacks
|
|
309
|
+
/** Applies the documented fallbacks and validates the plugin config's keys. */
|
|
281
310
|
export declare function resolveBridgeConfig(config?: CherryBridgeConfig): ResolvedBridgeConfig;
|
|
282
311
|
/** The context slice `apply` touches beyond the HTTP face's own services. */
|
|
283
312
|
export interface CherryBridgeContext extends CherryHttpContext, CherryToolsContext {
|