dsh-remote-bridge 2.5.0 → 2.5.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.en.md +22 -7
- package/README.md +20 -7
- package/docs/api/asyncapi.yaml +51 -2
- package/docs/api/openapi.yaml +187 -4
- package/lib/client.cjs +1113 -509
- package/lib/index.d.ts +462 -15
- package/lib/index.js +2431 -505
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img alt="Version" src="https://img.shields.io/badge/version-v2.5.
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.5.2-087f8c">
|
|
13
13
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
14
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
15
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
@@ -102,10 +102,10 @@ dsh plugin --profile web add dsh-remote-bridge
|
|
|
102
102
|
dsh plugin --profile web add dsh-remote-bridge --registry https://registry.npmmirror.com
|
|
103
103
|
|
|
104
104
|
# 2. tarball from the GitHub release
|
|
105
|
-
dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.
|
|
105
|
+
dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.2/dsh-remote-bridge-v2.5.2.tgz
|
|
106
106
|
|
|
107
107
|
# 3. tarball from the Gitee release (faster from mainland China)
|
|
108
|
-
dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.
|
|
108
|
+
dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.2/dsh-remote-bridge-v2.5.2.tgz
|
|
109
109
|
|
|
110
110
|
npx @deepseek-ai/dsh web
|
|
111
111
|
```
|
|
@@ -122,7 +122,7 @@ Restart DSH after installation, then open settings — The section is titled **
|
|
|
122
122
|
To install a downloaded release archive:
|
|
123
123
|
|
|
124
124
|
```sh
|
|
125
|
-
dsh plugin --profile web add ./dsh-remote-bridge-v2.5.
|
|
125
|
+
dsh plugin --profile web add ./dsh-remote-bridge-v2.5.2.tgz
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
## First connection
|
|
@@ -352,15 +352,30 @@ pnpm check
|
|
|
352
352
|
pnpm pack
|
|
353
353
|
```
|
|
354
354
|
|
|
355
|
-
`pnpm check` covers type-checking, documentation consistency, and tests. It currently passes **
|
|
355
|
+
`pnpm check` covers type-checking, documentation consistency, and tests. It currently passes **67 test files / 557 cases**, plus **8 browser-layer cases** (Playwright measuring real computed styles, including the 375px narrow-viewport contract).
|
|
356
356
|
|
|
357
|
-
`npm pack` produces roughly a **0.
|
|
357
|
+
`npm pack` produces roughly a **0.61 MB** archive (v2.5.2 measured 635,316 bytes, SHA-256 `1779c02e…bdd3bd`): 2.2.0 dropped the standalone page and its editor dependencies and turned source maps off, down from 1.72 MB; it has since grown slightly with projection passthrough, the permission BFF, and the five-dimension hardening.
|
|
358
358
|
|
|
359
359
|
v2.5.0 was verified on DSH **0.2.1-rc.2** on real hardware: all 14/14 host services injected, the three jobs endpoints exercised, and the permission catalog returned the kernel's real three presets. The development baseline is upstream `dsh-v0.2.1-rc.2` (commit `687f282e8625c6b8d11ff58211a90524be2e4317`); see `engines.dsh` above for the supported range. CI covers Windows, Ubuntu, and macOS.
|
|
360
360
|
|
|
361
|
+
**v2.5.2 verification status**: 67 test files / 557 cases green, plus 8 Playwright browser cases
|
|
362
|
+
(including the 375px narrow-viewport contract); `typecheck`, `docs:check`, and the build all pass.
|
|
363
|
+
All three rounds of real-hardware tunnel regressions were reproduced and verified on a
|
|
364
|
+
React18 + Playwright click harness (one-click token issuance, the stop-tunnel escape hatch, and the
|
|
365
|
+
locked state when re-entering settings), then confirmed by manual testing on the user's machine.
|
|
366
|
+
|
|
361
367
|
## Project
|
|
362
368
|
|
|
363
|
-
- Version: `v2.5.
|
|
369
|
+
- Version: `v2.5.2`
|
|
370
|
+
- **v2.5.2 is a comprehensive hardening release**: it lands the five-dimension audit
|
|
371
|
+
(security, features, performance, UI, button feedback) — including the unauthenticated
|
|
372
|
+
`debug/host-services` information disclosure fix, dual-bucket rate limiting, broadcast backpressure,
|
|
373
|
+
weak ETags, and audit retention — and makes "start tunnel & generate token" complete inside a single
|
|
374
|
+
request. While the tunnel runs, the management surface is safely locked with only "stop tunnel"
|
|
375
|
+
available, restoring automatically afterwards. v2.5.1's security model (403 on `/manage` while a tunnel
|
|
376
|
+
runs, the four-layer admin criteria) is unchanged; see `docs/project/SECURITY.md` §「管理面的信任边界」.
|
|
377
|
+
- **v2.5.1 was the security release**: it closed the remote privilege escalation and the `.heif`
|
|
378
|
+
handoff-image 415 bug described in the changelog.
|
|
364
379
|
- Author: upstream [Hakunm](https://github.com/Hakunm); this fork maintained by [westanke](https://github.com/westanke)
|
|
365
380
|
- Repository: [GitHub](https://github.com/westanke/dsh-remote-bridge) · [Gitee](https://gitee.com/westanke/dsh-remote-bridge)
|
|
366
381
|
- npm: [`dsh-remote-bridge`](https://www.npmjs.com/package/dsh-remote-bridge) ([mainland-China mirror](https://registry.npmmirror.com/dsh-remote-bridge))
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img alt="Version" src="https://img.shields.io/badge/version-v2.5.
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.5.2-087f8c">
|
|
13
13
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
14
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
15
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
@@ -93,10 +93,10 @@ dsh plugin --profile web add dsh-remote-bridge
|
|
|
93
93
|
dsh plugin --profile web add dsh-remote-bridge --registry https://registry.npmmirror.com
|
|
94
94
|
|
|
95
95
|
# ② GitHub Release 的 tarball
|
|
96
|
-
dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.
|
|
96
|
+
dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.2/dsh-remote-bridge-v2.5.2.tgz
|
|
97
97
|
|
|
98
98
|
# ③ Gitee Release 的 tarball(国内访问更快)
|
|
99
|
-
dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.
|
|
99
|
+
dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.2/dsh-remote-bridge-v2.5.2.tgz
|
|
100
100
|
|
|
101
101
|
npx @deepseek-ai/dsh web
|
|
102
102
|
```
|
|
@@ -112,7 +112,7 @@ npx @deepseek-ai/dsh web
|
|
|
112
112
|
从 GitHub Release 下载离线安装包时,可直接安装 tarball:
|
|
113
113
|
|
|
114
114
|
```sh
|
|
115
|
-
dsh plugin --profile web add ./dsh-remote-bridge-v2.5.
|
|
115
|
+
dsh plugin --profile web add ./dsh-remote-bridge-v2.5.2.tgz
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
## 第一次使用
|
|
@@ -338,17 +338,30 @@ pnpm check
|
|
|
338
338
|
pnpm pack
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
`pnpm check` 覆盖类型检查、文档一致性与测试;当前 **
|
|
341
|
+
`pnpm check` 覆盖类型检查、文档一致性与测试;当前 **67 个测试文件 / 557 个用例**,外加 **8 个浏览器层用例**(Playwright 实测量出的计算样式,含 375px 窄屏契约),全部通过。
|
|
342
342
|
|
|
343
|
-
`npm pack` 的产物约 **0.
|
|
343
|
+
`npm pack` 的产物约 **0.61 MB**(v2.5.2 实测 635,316 字节,SHA-256 `1779c02e…bdd3bd`):2.2.0 删除独立页及其编辑器依赖、并关闭 source map 之后从 1.72 MB 降下来,之后随投影透传、权限 BFF 与五维加固小幅增长。
|
|
344
344
|
|
|
345
345
|
v2.5.0 在 DSH **0.2.1-rc.2** 上完成真机验证:14/14 宿主服务全部可注入、三个 jobs 端点实测通过、
|
|
346
346
|
权限档位目录返回内核真实三档。开发基线为上游 `dsh-v0.2.1-rc.2`(commit `687f282e8625c6b8d11ff58211a90524be2e4317`),
|
|
347
347
|
兼容范围见上文 `engines.dsh`。CI 覆盖 Windows、Ubuntu 和 macOS。
|
|
348
348
|
|
|
349
|
+
**v2.5.2 的验证状态**:67 个测试文件 / 557 个用例全绿、8 个 Playwright 浏览器用例
|
|
350
|
+
(含 375px 窄屏契约)、`typecheck`/`docs:check`/构建通过;隧道管理面三轮真机回归均用
|
|
351
|
+
React18 + Playwright 真点击台复现并验证(一键出令牌、停止出口、重进设置页锁定态),
|
|
352
|
+
用户真机手工复测通过。
|
|
353
|
+
|
|
349
354
|
## 项目信息
|
|
350
355
|
|
|
351
|
-
- 当前版本:`v2.5.
|
|
356
|
+
- 当前版本:`v2.5.2`
|
|
357
|
+
- **v2.5.2 是全量完善版**:落地五维审计(安全/功能/性能/界面/按钮反馈)——含高危的
|
|
358
|
+
`debug/host-services` 免鉴权信息泄露修复、限流双桶、广播背压、弱 ETag、审计保留策略等,
|
|
359
|
+
并把「一键开隧道并生成令牌」做成同一次请求内搭车完成;隧道运行时管理面安全锁定,
|
|
360
|
+
仅保留「停止隧道」出口,停止后自动恢复。v2.5.1 的安全模型(开隧道时 `/manage` 403、
|
|
361
|
+
四层管理判据)保持不变,见 `docs/project/SECURITY.md` §管理面的信任边界。
|
|
362
|
+
- **v2.5.1 是安全修复版**:堵住「开隧道/反代后管理面免凭据对公网开放」的远程提权(`/manage/**`
|
|
363
|
+
与回环 admin 头此前只按「TCP 来源是否回环」判定,而隧道转发进来的请求来源恒为 `127.0.0.1`),
|
|
364
|
+
并修复 `.heif` 交接图必被只读路由 415 的问题。
|
|
352
365
|
- 作者:上游 [Hakunm](https://github.com/Hakunm),本 fork 维护 [westanke](https://github.com/westanke)
|
|
353
366
|
- 仓库:[GitHub](https://github.com/westanke/dsh-remote-bridge) · [Gitee](https://gitee.com/westanke/dsh-remote-bridge)
|
|
354
367
|
- npm:[`dsh-remote-bridge`](https://www.npmjs.com/package/dsh-remote-bridge)([国内镜像](https://registry.npmmirror.com/dsh-remote-bridge))
|
package/docs/api/asyncapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
asyncapi: 3.0.0
|
|
2
2
|
info:
|
|
3
3
|
title: DeepSeek Harness Mobile Events
|
|
4
|
-
version: 2.5.
|
|
4
|
+
version: 2.5.2
|
|
5
5
|
description: |
|
|
6
6
|
Live file and authorized DSH chat events. Reconnect clients must refresh
|
|
7
7
|
REST state; unbounded replay is not provided.
|
|
@@ -10,6 +10,25 @@ info:
|
|
|
10
10
|
limit, quota exhausted, invalid API key, signed-out stop). Without it a
|
|
11
11
|
remote client sees an ordinary `chat.turn.end` and keeps waiting on a run
|
|
12
12
|
that already failed.
|
|
13
|
+
|
|
14
|
+
Connection-level behavior (source of truth: `src/host/server.ts`):
|
|
15
|
+
|
|
16
|
+
- Authentication happens **before** connection-limit counting. The
|
|
17
|
+
default cap is 32 accepted WebSocket connections (configurable); the
|
|
18
|
+
33rd authenticated upgrade is refused at the HTTP Upgrade stage with
|
|
19
|
+
`429 Too Many Requests` and `Retry-After: 5`.
|
|
20
|
+
- Frames larger than 1 MiB are refused with WebSocket close `1009`
|
|
21
|
+
(message too big).
|
|
22
|
+
- A server ping is sent every 30 seconds; a connection that misses the
|
|
23
|
+
pong for two consecutive intervals is terminated.
|
|
24
|
+
- Each connection has a 512-frame send queue. Under backpressure the
|
|
25
|
+
server first drops rebuildable `chat.message.delta` frames, then other
|
|
26
|
+
rebuildable frames queued ahead, and as a last resort the oldest state
|
|
27
|
+
frame. When any frame was dropped the server emits a best-effort
|
|
28
|
+
`connection.resyncRequired` frame (also immediately before a
|
|
29
|
+
`1013 Backpressure` close) telling the client to re-sync via REST;
|
|
30
|
+
clients must ignore the exact `dropped` value. Older clients that do
|
|
31
|
+
not know this event simply ignore it.
|
|
13
32
|
servers:
|
|
14
33
|
local:
|
|
15
34
|
host: 127.0.0.1:3090
|
|
@@ -39,7 +58,7 @@ components:
|
|
|
39
58
|
id: { type: string }
|
|
40
59
|
type:
|
|
41
60
|
type: string
|
|
42
|
-
examples: [file.changed, file.trashed, chat.message.delta, chat.message.committed, chat.session.status, chat.session.snapshot, chat.deliverables.presented, chat.approval.requested, chat.approval.resolved, chat.turn.failed, connection.ready]
|
|
61
|
+
examples: [file.changed, file.trashed, chat.message.delta, chat.message.committed, chat.session.status, chat.session.snapshot, chat.deliverables.presented, chat.approval.requested, chat.approval.resolved, chat.turn.failed, connection.ready, connection.resyncRequired]
|
|
43
62
|
time: { type: integer }
|
|
44
63
|
data:
|
|
45
64
|
oneOf:
|
|
@@ -49,6 +68,7 @@ components:
|
|
|
49
68
|
- $ref: '#/components/schemas/ChatApprovalResolved'
|
|
50
69
|
- $ref: '#/components/schemas/ChatTurnFailed'
|
|
51
70
|
- $ref: '#/components/schemas/ConnectionReadyData'
|
|
71
|
+
- $ref: '#/components/schemas/ConnectionResyncRequired'
|
|
52
72
|
- $ref: '#/components/schemas/ChatSessionSnapshot'
|
|
53
73
|
- $ref: '#/components/schemas/ChatDeliverablesPresented'
|
|
54
74
|
- type: object
|
|
@@ -231,3 +251,32 @@ components:
|
|
|
231
251
|
capabilities:
|
|
232
252
|
type: array
|
|
233
253
|
items: { type: string }
|
|
254
|
+
|
|
255
|
+
ConnectionResyncRequired:
|
|
256
|
+
type: object
|
|
257
|
+
description: |
|
|
258
|
+
Payload of a connection-scoped `connection.resyncRequired` frame. The
|
|
259
|
+
server emits it (best effort) after it had to drop queued frames for
|
|
260
|
+
this socket because the per-connection send queue (512 frames)
|
|
261
|
+
overflowed under backpressure; it is also emitted once immediately
|
|
262
|
+
before the server closes the socket with WebSocket code `1013`
|
|
263
|
+
(`Backpressure`), when even state frames no longer fit. State frames
|
|
264
|
+
(`chat.turn.*`, `chat.approval.*`, `chat.turn.failed`,
|
|
265
|
+
`connection.ready`) are preserved preferentially over
|
|
266
|
+
`chat.message.delta`.
|
|
267
|
+
|
|
268
|
+
On receipt, clients should re-fetch history and pending state via the
|
|
269
|
+
REST API (the dropped frames are never replayed). The `dropped` count
|
|
270
|
+
is diagnostic only and must not be acted on numerically; clients that
|
|
271
|
+
do not recognize this event type ignore it safely. Example:
|
|
272
|
+
`{"id":"<uuid>","type":"connection.resyncRequired","time":1761100000000,
|
|
273
|
+
"data":{"dropped":12,"reason":"backpressure"}}`.
|
|
274
|
+
required: [dropped, reason]
|
|
275
|
+
properties:
|
|
276
|
+
dropped:
|
|
277
|
+
type: integer
|
|
278
|
+
minimum: 0
|
|
279
|
+
description: Number of frames dropped for this connection since the previous resync notification.
|
|
280
|
+
reason:
|
|
281
|
+
type: string
|
|
282
|
+
const: backpressure
|
package/docs/api/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: DeepSeek Harness Mobile API
|
|
4
|
-
version: 2.5.
|
|
4
|
+
version: 2.5.2
|
|
5
5
|
description: Versioned file and chat BFF for mobile and third-party DSH clients. HTTP and HTTPS are both supported.
|
|
6
6
|
servers:
|
|
7
7
|
- url: http://127.0.0.1:3090/api/v1
|
|
@@ -24,7 +24,7 @@ paths:
|
|
|
24
24
|
properties:
|
|
25
25
|
ok: { type: boolean, const: true }
|
|
26
26
|
version: { type: string, const: v1 }
|
|
27
|
-
pluginVersion: { type: string, const: 2.
|
|
27
|
+
pluginVersion: { type: string, const: 2.5.2 }
|
|
28
28
|
/debug/host-services:
|
|
29
29
|
get:
|
|
30
30
|
operationId: hostServices
|
|
@@ -39,8 +39,14 @@ paths:
|
|
|
39
39
|
`observed` covers services confirmed present in the web profile but not yet
|
|
40
40
|
consumed (currently only `workingDirectory`, added by DSH 0.2.1).
|
|
41
41
|
|
|
42
|
-
Requires a device token or loopback admin
|
|
43
|
-
|
|
42
|
+
Requires a device token or loopback admin **with the `settings.read`
|
|
43
|
+
scope** (a loopback admin inherits all scopes): it exposes host
|
|
44
|
+
internals. Since 2.5.2 the endpoint sits behind authentication like
|
|
45
|
+
every other `/api/v1` route (SEC-H1); it previously returned before the
|
|
46
|
+
principal was resolved. The response may additionally carry a
|
|
47
|
+
best-effort `metrics` object with runtime diagnostics; it is omitted in
|
|
48
|
+
builds without the metrics collector (and a missing probe still yields
|
|
49
|
+
`404`, not `500`).
|
|
44
50
|
responses:
|
|
45
51
|
'200':
|
|
46
52
|
description: Injection probe snapshot taken while the plugin applied.
|
|
@@ -79,6 +85,14 @@ paths:
|
|
|
79
85
|
injectedAvailable: { type: integer }
|
|
80
86
|
injectedMissing: { type: array, items: { type: string } }
|
|
81
87
|
observedAvailable: { type: array, items: { type: string } }
|
|
88
|
+
metrics:
|
|
89
|
+
type: object
|
|
90
|
+
description: |
|
|
91
|
+
Optional runtime diagnostics attached to the same
|
|
92
|
+
`settings.read`-gated report (no separate route). Omitted
|
|
93
|
+
when the build has no metrics collector. Shape is
|
|
94
|
+
diagnostic and additive.
|
|
95
|
+
additionalProperties: true
|
|
82
96
|
'404':
|
|
83
97
|
description: The probe is not compiled into this build.
|
|
84
98
|
/pairings/exchange:
|
|
@@ -134,6 +148,32 @@ paths:
|
|
|
134
148
|
type: array
|
|
135
149
|
items: { $ref: '#/components/schemas/Root' }
|
|
136
150
|
'403': { $ref: '#/components/responses/Error' }
|
|
151
|
+
/roots/resolve:
|
|
152
|
+
get:
|
|
153
|
+
operationId: resolveAbsolutePath
|
|
154
|
+
summary: Map a host absolute path to its authorized root
|
|
155
|
+
description: |
|
|
156
|
+
Chat events carry server-absolute paths while `GET /roots` deliberately
|
|
157
|
+
omits root absolute paths, so clients cannot perform this mapping
|
|
158
|
+
themselves. Resolves `path` against the device's authorized roots using
|
|
159
|
+
realpath-normalized longest-prefix matching, then stat-checks the
|
|
160
|
+
target (rejecting symlinks inside the path) via the same machinery as
|
|
161
|
+
file content reads. The response never contains an absolute host path.
|
|
162
|
+
Requires `files.read`.
|
|
163
|
+
parameters:
|
|
164
|
+
- name: path
|
|
165
|
+
in: query
|
|
166
|
+
required: true
|
|
167
|
+
description: Absolute host path. Relative paths are rejected rather than guessed.
|
|
168
|
+
schema: { type: string, minLength: 1 }
|
|
169
|
+
responses:
|
|
170
|
+
'200':
|
|
171
|
+
description: Root match plus wire-relative metadata for the target.
|
|
172
|
+
content:
|
|
173
|
+
application/json:
|
|
174
|
+
schema: { $ref: '#/components/schemas/ResolvedPath' }
|
|
175
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
176
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
137
177
|
/roots/{rootId}/entries:
|
|
138
178
|
parameters:
|
|
139
179
|
- $ref: '#/components/parameters/RootId'
|
|
@@ -271,6 +311,74 @@ paths:
|
|
|
271
311
|
'200':
|
|
272
312
|
description: Item restored without overwriting an existing path.
|
|
273
313
|
'409': { $ref: '#/components/responses/Error' }
|
|
314
|
+
/image-handoff:
|
|
315
|
+
get:
|
|
316
|
+
operationId: readImageHandoff
|
|
317
|
+
summary: Fetch one handoff image for a chat session
|
|
318
|
+
description: |
|
|
319
|
+
Returns the raw bytes of an image the agent handed off (content-addressed
|
|
320
|
+
`sha256.<ext>` files written under the session working directory's
|
|
321
|
+
image-handoff subdirectory). `name` must be a bare file name only: the
|
|
322
|
+
directory is always derived by the host from the session working
|
|
323
|
+
directory, so no directory or absolute path can be supplied. Requires
|
|
324
|
+
`files.read` plus ownership of the session; "no such session" and "no
|
|
325
|
+
such file" are indistinguishable (both are 404). This authenticated REST
|
|
326
|
+
route is the device-side counterpart of the token-less browser route
|
|
327
|
+
`GET /plugin/dsh-remote-bridge/raw`.
|
|
328
|
+
parameters:
|
|
329
|
+
- name: sessionId
|
|
330
|
+
in: query
|
|
331
|
+
required: true
|
|
332
|
+
schema: { type: string }
|
|
333
|
+
- name: name
|
|
334
|
+
in: query
|
|
335
|
+
required: true
|
|
336
|
+
description: Bare handoff file name, e.g. `a1b2…e9.png`.
|
|
337
|
+
schema: { type: string, pattern: '^[0-9a-f]{64}\.[A-Za-z0-9]+$' }
|
|
338
|
+
responses:
|
|
339
|
+
'200':
|
|
340
|
+
description: Image bytes; handoff files are temporary artifacts, so the response is private and must be revalidated.
|
|
341
|
+
headers:
|
|
342
|
+
Cache-Control: { schema: { type: string }, description: private, max-age=0, must-revalidate }
|
|
343
|
+
content:
|
|
344
|
+
application/octet-stream:
|
|
345
|
+
schema: { type: string, contentEncoding: binary }
|
|
346
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
347
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
348
|
+
/attachments/{attachmentId}:
|
|
349
|
+
get:
|
|
350
|
+
operationId: readAttachment
|
|
351
|
+
summary: Fetch one session attachment's bytes
|
|
352
|
+
description: |
|
|
353
|
+
Returns the raw bytes of an image attachment referenced by a session
|
|
354
|
+
event. The attachment store is content-addressed, so `attachmentId`
|
|
355
|
+
contains a colon (typically `sha256:<hex>`) and **must be
|
|
356
|
+
percent-encoded in the URL** (`sha256%3A<hex>`). `sessionId` is a
|
|
357
|
+
required query parameter: the host verifies the session actually
|
|
358
|
+
references this attachment and validates bytes/media type against the
|
|
359
|
+
real reference stored in the session event. Requires `files.read` plus
|
|
360
|
+
ownership of the session. Bytes are immutable and may be cached
|
|
361
|
+
privately for a year.
|
|
362
|
+
parameters:
|
|
363
|
+
- name: attachmentId
|
|
364
|
+
in: path
|
|
365
|
+
required: true
|
|
366
|
+
description: Content-addressed attachment id, e.g. `sha256%3A<hex>` (the raw id is `sha256:<hex>` and must be percent-encoded).
|
|
367
|
+
schema: { type: string }
|
|
368
|
+
- name: sessionId
|
|
369
|
+
in: query
|
|
370
|
+
required: true
|
|
371
|
+
schema: { type: string }
|
|
372
|
+
responses:
|
|
373
|
+
'200':
|
|
374
|
+
description: Attachment bytes; immutable and privately cacheable.
|
|
375
|
+
headers:
|
|
376
|
+
Cache-Control: { schema: { type: string }, description: private, max-age=31536000, immutable }
|
|
377
|
+
content:
|
|
378
|
+
application/octet-stream:
|
|
379
|
+
schema: { type: string, contentEncoding: binary }
|
|
380
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
381
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
274
382
|
/chat/workspaces:
|
|
275
383
|
get:
|
|
276
384
|
operationId: listChatWorkspaces
|
|
@@ -784,6 +892,45 @@ paths:
|
|
|
784
892
|
responses:
|
|
785
893
|
'202': { description: Prompt accepted by DSH. }
|
|
786
894
|
'409': { $ref: '#/components/responses/Error' }
|
|
895
|
+
/chat/sessions/{sessionId}/attachments:
|
|
896
|
+
parameters:
|
|
897
|
+
- name: sessionId
|
|
898
|
+
in: path
|
|
899
|
+
required: true
|
|
900
|
+
schema: { type: string }
|
|
901
|
+
post:
|
|
902
|
+
operationId: uploadSessionAttachment
|
|
903
|
+
summary: Upload one attachment and receive a receipt
|
|
904
|
+
description: |
|
|
905
|
+
Uploads one attachment as base64 text and returns the receipt a later
|
|
906
|
+
message can reference. Kernel file-type content parts require this
|
|
907
|
+
`receiptId` (obtained through an upload); only image content parts may
|
|
908
|
+
be inlined as base64 directly in a message. Requires `chat.write` plus
|
|
909
|
+
ownership of the session. The request body is limited to 24 MiB.
|
|
910
|
+
requestBody:
|
|
911
|
+
required: true
|
|
912
|
+
content:
|
|
913
|
+
application/json:
|
|
914
|
+
schema:
|
|
915
|
+
type: object
|
|
916
|
+
required: [data]
|
|
917
|
+
properties:
|
|
918
|
+
data:
|
|
919
|
+
type: string
|
|
920
|
+
contentEncoding: base64
|
|
921
|
+
minLength: 1
|
|
922
|
+
description: Base64-encoded attachment bytes.
|
|
923
|
+
name:
|
|
924
|
+
type: string
|
|
925
|
+
description: Optional original file name; trimmed by the server.
|
|
926
|
+
responses:
|
|
927
|
+
'201':
|
|
928
|
+
description: Stored; the receipt id is referenced by later message file parts.
|
|
929
|
+
content:
|
|
930
|
+
application/json:
|
|
931
|
+
schema: { $ref: '#/components/schemas/AttachmentReceipt' }
|
|
932
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
933
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
787
934
|
/chat/sessions/{sessionId}/approvals:
|
|
788
935
|
get:
|
|
789
936
|
operationId: listPendingApprovals
|
|
@@ -994,6 +1141,24 @@ paths:
|
|
|
994
1141
|
schema: { $ref: '#/components/schemas/SessionModels' }
|
|
995
1142
|
'404': { $ref: '#/components/responses/Error' }
|
|
996
1143
|
/chat/sessions/{sessionId}/model:
|
|
1144
|
+
get:
|
|
1145
|
+
operationId: getSessionModelAlias
|
|
1146
|
+
description: |
|
|
1147
|
+
Alias of `GET /chat/sessions/{sessionId}/models`; the server route
|
|
1148
|
+
`model(?:s)?$` answers GET on both spellings. New clients should call
|
|
1149
|
+
`/models`.
|
|
1150
|
+
parameters:
|
|
1151
|
+
- name: sessionId
|
|
1152
|
+
in: path
|
|
1153
|
+
required: true
|
|
1154
|
+
schema: { type: string }
|
|
1155
|
+
responses:
|
|
1156
|
+
'200':
|
|
1157
|
+
description: Current selection and models routable for this DSH session.
|
|
1158
|
+
content:
|
|
1159
|
+
application/json:
|
|
1160
|
+
schema: { $ref: '#/components/schemas/SessionModels' }
|
|
1161
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
997
1162
|
put:
|
|
998
1163
|
operationId: selectSessionModel
|
|
999
1164
|
parameters:
|
|
@@ -1155,6 +1320,24 @@ components:
|
|
|
1155
1320
|
id: { type: string, format: uuid }
|
|
1156
1321
|
label: { type: string }
|
|
1157
1322
|
createdAt: { type: integer }
|
|
1323
|
+
ResolvedPath:
|
|
1324
|
+
type: object
|
|
1325
|
+
description: Result of mapping an absolute host path into an authorized root; the root's absolute path itself is never included.
|
|
1326
|
+
required: [rootId, path, kind, size, modifiedAt, contentType]
|
|
1327
|
+
properties:
|
|
1328
|
+
rootId: { type: string, format: uuid }
|
|
1329
|
+
path: { $ref: '#/components/schemas/WirePath' }
|
|
1330
|
+
kind: { type: string, enum: [file, directory] }
|
|
1331
|
+
size: { type: integer, nullable: true }
|
|
1332
|
+
modifiedAt: { type: integer, nullable: true }
|
|
1333
|
+
contentType: { type: string, nullable: true }
|
|
1334
|
+
AttachmentReceipt:
|
|
1335
|
+
type: object
|
|
1336
|
+
description: Upload receipt referenced later as a file-type content part in a message.
|
|
1337
|
+
required: [receiptId, name]
|
|
1338
|
+
properties:
|
|
1339
|
+
receiptId: { type: string }
|
|
1340
|
+
name: { type: string, nullable: true }
|
|
1158
1341
|
Device:
|
|
1159
1342
|
type: object
|
|
1160
1343
|
required: [id, name, scopes, rootIds]
|