@deepseek-ai/dsh-client-connection 0.1.1-rc.2 → 0.1.2-alpha.2
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.i18n.yaml +2 -2
- package/README.md +54 -6
- package/README.zh.md +54 -6
- package/lib/client.js +1936 -7453
- package/lib/index.js +387 -254
- package/lib/invariant.js +6 -5
- package/lib/types/api-path.d.ts +1 -6
- package/lib/types/api-request-trust.d.ts +2 -7
- package/lib/types/browser-auth.d.ts +46 -0
- package/lib/types/client/api.d.ts +7 -13
- package/lib/types/client/connection.d.ts +50 -28
- package/lib/types/client/fixture.d.ts +6 -43
- package/lib/types/client/index.d.ts +72 -33
- package/lib/types/client/rpc.d.ts +4 -1
- package/lib/types/http-bridge.d.ts +1 -1
- package/lib/types/index.d.ts +12 -10
- package/lib/types/rpc-host.d.ts +19 -8
- package/lib/types/rpc-schema.d.ts +30 -0
- package/lib/types/rpc.d.ts +145 -15
- package/package.json +18 -23
- package/lib/types/client/web-api-client.d.ts +0 -11
- package/lib/types/websocket-downlink.d.ts +0 -43
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/connection/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: ee0566bf811a0bad32f7d59b4c8849e26efea613
|
|
6
|
+
README.zh.md: 48425a428247b7749bf0dd75f592b5f7e4088ef2
|
package/README.md
CHANGED
|
@@ -1,17 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Browser-host wire layer for the web GUI: Remote RPC, event-stream delivery with reconnect, exact Fetch routes, the /api HTTP bridge, and the browser-trust fence."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-connection
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
The package carries browser-to-Host Remote calls, exact Fetch responses, and connection generations. The Client plugin mounts `ctx.connection` with current-page loopback state, a generic RPC carrier, the active generation and its Host facts, observable recovery state, an immediate reconnect command, and the registration point for one generation source. A generation becomes visible when its source reports ready; source completion, failure, withdrawal, or an explicit stop clears it before `ConnectionController` applies its retry policy.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Browser authentication and request trust](#browser-authentication-and-request-trust)
|
|
18
|
+
- [Connection generation](#connection-generation)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; in-process compositions provide equivalent Remote streams through `connection.rpc.open` without opening a WebSocket. The Host half owns the sole `/api` route, Fetch bridge, browser authentication, Host/Origin checks, and exact `GET`/`HEAD` route registry. Typert Gateway claims generated Remote endpoints, feature packages register non-JSON responses such as Session-log downloads, and unclaimed requests return 404. Loopback hostname classification remains package-internal to the browser-facing Client state.
|
|
29
|
+
|
|
30
|
+
-----
|
|
31
|
+
|
|
32
|
+
<a id="browser-authentication-and-request-trust"></a>
|
|
33
|
+
## Browser authentication and request trust
|
|
6
34
|
|
|
7
|
-
|
|
35
|
+
Every Host RPC method and WebSocket stream requires one browser session; there is no method-specific loopback tier. Each process mints a random launch token. `dsh-web-app` prints and opens the ordinary root URL with `?token=...`; `frontend-static` delegates root and index requests to `ctx.connection.authorizeIndex`, which accepts that token only on `GET /`, writes an authority-bound signed cookie, and redirects to clean `/`. A missing, expired, malformed, or wrong-authority cookie returns 401 before RPC dispatch. Static assets remain public. The HTTP carrier accepts no query token outside the root exchange and no Authorization-header token.
|
|
8
36
|
|
|
9
|
-
The
|
|
37
|
+
The cookie signing secret is the owner-scoped `client-connection/browser-session` grant record in `ctx.credentials`. The local provider persists it in `$DSH_HOME/.credentials.yaml`; `BrowserAuth` loads or creates the record during Connection activation and retains the secret in memory, so request authentication is synchronous. Deleting or replacing the record takes effect on the next Connection activation. Cookies carry an absolute issue/expiry interval, defaulting to 30 days through `cookieMaxAgeDays`, and bind the normalized hostname plus port in both their deterministic name and signed payload. They are host-only, `Path=/`, `HttpOnly`, and `SameSite=Strict`; they deliberately omit `Secure` because the shipped server uses loopback HTTP.
|
|
10
38
|
|
|
11
|
-
|
|
39
|
+
Before authentication, every request still passes `src/api-request-trust.ts`. Its `Host` must be loopback or match a `trustedHosts` entry: exact on `host:port`, any port on port-less entries, both sides WHATWG-normalized. An attached `Origin` must equal that Host and `sec-fetch-site: cross-site` is refused. Malformed configured authorities fail plugin load. These checks defend DNS rebinding and cross-site browser requests; they never establish identity. A failed Host/Origin check returns 403, while a trusted but unauthenticated request returns 401. `dsh web --host 0.0.0.0` remains unsupported. Decision records: [browser request trust](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md) and [browser token authentication](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.md).
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
<a id="connection-generation"></a>
|
|
42
|
+
## Connection generation
|
|
14
43
|
|
|
44
|
+
API Gateway Client registers the internal `$events` logical stream as the sole generation source, independently of whether any `$on` listener exists. The Host attaches all incremental listeners in the API Remotes source factory, then sends one `{ type: 'ready', clientId, host: { home } }` item before events. `ConnectionController` publishes that generation and calls `onConnected` only after the ready item arrives, so baseline acquisition cannot race ahead of incremental observation.
|
|
45
|
+
|
|
46
|
+
An ended `$events` stream, a Remote stream error, a non-ready opening item, or a malformed event item invalidates the current generation. While the browser reports network availability, the controller publishes `connecting` and retries with 50%–100% jitter under caps of 500ms, 1s, 2s, 4s, 8s, and 10s. It logs each attempt, asks Gateway to replace the physical WebSocket, and reopens `$events`; failure in the 10s tier publishes terminal `disconnected`. `ctx.connection.reconnect()` interrupts active work, resets the sequence, and starts retry 1 immediately. Browser `offline` aborts active work, publishes `disconnected`, and suspends automatic attempts; the next `online` transition resets the sequence and starts at the 500ms tier. A ready item publishes `connected`. The Gateway mux performs one physical connection attempt per request rather than running an independent retry schedule. The [connection recovery decision](../../../.agents/notes/implemented/feature/2026-08-28-web-connection-recovery-control.md) owns the cadence and manual recovery behavior.
|
|
47
|
+
|
|
48
|
+
<a id="model-experience"></a>
|
|
15
49
|
## Model Experience
|
|
16
50
|
|
|
17
51
|
None, as the wire consumer layer moves already-composed messages between browser and host; nothing here reaches a model request.
|
|
@@ -22,5 +56,19 @@ None; this package neither assembles nor sends a provider request.
|
|
|
22
56
|
|
|
23
57
|
## Known Limitations and Deferred Work
|
|
24
58
|
|
|
25
|
-
|
|
59
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
60
|
+
|
|
26
61
|
- **The `/api` bridge buffers each request body in memory** — `maxRequestBodyBytes` (default 300 MiB, sized for the default 200 MiB aggregate image limit after base64 expansion plus envelope headroom) is therefore also the per-request resident bound; a streaming body path would be needed to lower it without shrinking the image limits.
|
|
62
|
+
- **The browser cookie is not marked `Secure`** — loopback HTTP is the shipped transport, so exposing the same authority over plaintext networking can expose the bearer cookie in transit.
|
|
63
|
+
- **There is no logout operation** — clearing the browser cookie ends one browser session; deleting the owner credential record and restarting `dsh` revokes every session.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
<a id="dev-note"></a>
|
|
67
|
+
### Dev Note
|
|
68
|
+
|
|
69
|
+
<details>
|
|
70
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
71
|
+
|
|
72
|
+
None.
|
|
73
|
+
|
|
74
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,17 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Web GUI 的浏览器-Host 线层:Remote RPC、带重连的事件流投递、精确 Fetch 路由、/api HTTP 桥与浏览器信任栅栏。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-connection
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
本包承载浏览器到 Host 的 Remote 调用、精确 Fetch 响应与 connection generation。Client 插件挂载 `ctx.connection`,其中包含当前页面的 loopback 状态、通用 RPC carrier、当前 generation 及其 Host 信息、可观察的恢复状态、立即重连命令,以及单一 generation source 的注册点。source 报告 ready 后 generation 才可见;source 结束、失败、被撤回或显式 stop 都会清空它,再由 `ConnectionController` 执行重试策略。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [浏览器认证与请求信任](#browser-authentication-and-request-trust)
|
|
18
|
+
- [Connection generation](#connection-generation)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与暂缓事项](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
浏览器通过 HTTP POST 执行 Remote 一元调用;API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。进程内组合通过 `connection.rpc.open` 提供等价的 Remote 流,不打开 WebSocket。Host half 拥有唯一 `/api` route、Fetch bridge、浏览器认证、Host/Origin 校验与精确 `GET`/`HEAD` 路由注册表。Typert Gateway 认领生成的 Remote endpoint,功能包注册 Session 日志下载等非 JSON 响应,未认领的请求返回 404。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。
|
|
29
|
+
|
|
30
|
+
-----
|
|
31
|
+
|
|
32
|
+
<a id="browser-authentication-and-request-trust"></a>
|
|
33
|
+
## 浏览器认证与请求信任
|
|
6
34
|
|
|
7
|
-
|
|
35
|
+
每个 Host RPC 方法和 WebSocket stream 都要求同一个浏览器会话,不存在按方法区分的 loopback 层。每个进程生成一个随机启动令牌。`dsh-web-app` 打印并打开带 `?token=...` 的普通根 URL;`frontend-static` 把根路径和 index 请求交给 `ctx.connection.authorizeIndex`,后者只在 `GET /` 接受该令牌,写入绑定 authority 的签名 cookie,再重定向到干净的 `/`。缺失、过期、畸形或 authority 不匹配的 cookie 会在 RPC 分发前得到 401。静态资源保持公开。HTTP 载体不在根路径交换之外接受 query token,也不接受 Authorization header token。
|
|
8
36
|
|
|
9
|
-
|
|
37
|
+
cookie 签名密钥是 `ctx.credentials` 中由 `client-connection/browser-session` 拥有的 grant 记录。本地提供方把它持久化到 `$DSH_HOME/.credentials.yaml`;`BrowserAuth` 在 Connection 激活期间加载或创建该记录,并把密钥留在内存中,因此请求认证同步执行。删除或替换该记录会在下一次 Connection 激活时生效。cookie 携带绝对签发与过期区间,`cookieMaxAgeDays` 默认设为 30 天,并在确定性名称与签名 payload 中同时绑定规范化 hostname 和 port。它是 host-only、`Path=/`、`HttpOnly`、`SameSite=Strict`;随附服务器使用 loopback HTTP,因此刻意不设置 `Secure`。
|
|
10
38
|
|
|
11
|
-
|
|
39
|
+
认证之前,每个请求仍经过 `src/api-request-trust.ts`。其 `Host` 必须是 loopback,或与 `trustedHosts` 条目匹配:带端口的 `host:port` 精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化。若附带 `Origin`,它必须等于该 Host;`sec-fetch-site: cross-site` 一律拒绝。畸形配置 authority 会让插件加载失败。这些检查防御 DNS rebinding 与跨站浏览器请求,绝不建立身份。Host/Origin 校验失败返回 403;Host 可信但未认证的请求返回 401。`dsh web --host 0.0.0.0` 仍不受支持。决策记录:[浏览器请求信任](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.zh.md)与[浏览器令牌认证](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.zh.md)。
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
<a id="connection-generation"></a>
|
|
42
|
+
## Connection generation
|
|
14
43
|
|
|
44
|
+
API Gateway Client 把内部 `$events` logical stream 注册为唯一 generation source,与有无 `$on` 订阅无关。Host 在 API Remotes source factory 同步挂好所有增量 listener 后,先发送唯一 `{ type: 'ready', clientId, host: { home } }` 项,再发送事件。`ConnectionController` 仅在收到该 ready 项后发布 generation 并调用 `onConnected`,因此 baseline 不会跑在增量 listener 前面。
|
|
45
|
+
|
|
46
|
+
`$events` 结束、返回 Remote stream error、收到非 ready 首项或畸形事件项,都会使当前 generation 失效。浏览器报告网络可用时,Controller 发布 `connecting`,并在 500ms、1s、2s、4s、8s 与 10s 上限内采用 50%–100% 抖动重试。它记录每次尝试、要求 Gateway 替换物理 WebSocket,再重开 `$events`;10s 档失败后发布终态 `disconnected`。`ctx.connection.reconnect()` 会中断活动工作、重置序列,并立即开始 retry 1。浏览器 `offline` 会中断活动工作、发布 `disconnected` 并暂停自动尝试;下一次 `online` 转换会重置序列并从 500ms 档开始。ready 项会发布 `connected`。Gateway mux 每次收到请求只做一次物理连接尝试,不再运行另一套重试调度。[连接恢复决策](../../../.agents/notes/implemented/feature/2026-08-28-web-connection-recovery-control.zh.md)规定重试节奏和手动恢复行为。
|
|
47
|
+
|
|
48
|
+
<a id="model-experience"></a>
|
|
15
49
|
## 模型体验
|
|
16
50
|
|
|
17
51
|
无。协议消费层只在浏览器与主机之间搬运已经组合好的消息;这里没有任何内容进入模型请求。
|
|
@@ -22,5 +56,19 @@ node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-r
|
|
|
22
56
|
|
|
23
57
|
## 已知限制与暂缓事项
|
|
24
58
|
|
|
25
|
-
-
|
|
59
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
60
|
+
|
|
26
61
|
- **`/api` 桥把每个请求体整体缓冲在内存里**:`maxRequestBodyBytes`(默认 300 MiB,按默认 200 MiB 图片总量上限经 base64 膨胀加信封余量得出)因此同时是单请求的驻留内存上界;要降低它而不缩小图片限额,需要流式请求体路径。
|
|
62
|
+
- **浏览器 cookie 不带 `Secure`**:随附载体是 loopback HTTP;若部署经明文网络暴露同一 authority,bearer cookie 可能在传输中泄露。
|
|
63
|
+
- **没有 logout 操作**:清除浏览器 cookie 会结束单个浏览器会话;删除 owner 凭据记录并重启 `dsh` 会撤销全部会话。
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
<a id="dev-note"></a>
|
|
67
|
+
### 开发备注
|
|
68
|
+
|
|
69
|
+
<details>
|
|
70
|
+
<summary>维护者工作上下文——点击展开</summary>
|
|
71
|
+
|
|
72
|
+
无。
|
|
73
|
+
|
|
74
|
+
</details>
|