@deepseek-ai/dsh-web-app 0.2.1-alpha.1 → 0.2.1-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 CHANGED
@@ -9,8 +9,8 @@
9
9
  en: a85684740ecd278a
10
10
  zh: 7c54099b9edccf01
11
11
  /deepseek-ai-dsh-web-app/summary:
12
- en: fd97ee0c14fb5378
13
- zh: 0e1ccba47bc408d9
12
+ en: 0a2a9876194543e5
13
+ zh: ff472776a44ae260
14
14
  /deepseek-ai-dsh-web-app/table-of-contents:
15
15
  en: d152484eb41ac6b4
16
16
  zh: 09388d293f9be9cb
@@ -18,38 +18,35 @@
18
18
  en: 3bd37b85139d2fa7
19
19
  zh: ba86c807eb83866e
20
20
  /deepseek-ai-dsh-web-app/use-this-package/starting-the-web-gui:
21
- en: b7ab01cfe9fed40f
22
- zh: 23703ab7727c3ab5
21
+ en: e3cac911f080d8ef
22
+ zh: 60749e9f5c39c1bc
23
23
  /deepseek-ai-dsh-web-app/use-this-package/configuration:
24
- en: a9ee366c5af68a6c
25
- zh: 2711d7a3926e1101
24
+ en: 2b09528c8fc8d370
25
+ zh: 7b374ebda8841172
26
26
  /deepseek-ai-dsh-web-app/use-this-package/listening-trust-and-public-deployments:
27
- en: a79add86cb73d966
28
- zh: 78b58259f98ed063
27
+ en: 177400de33509826
28
+ zh: dd88bc096accffc8
29
29
  /deepseek-ai-dsh-web-app/use-this-package/running-over-ssh:
30
- en: 45db677dcc91c5c9
31
- zh: 75b765f7f1e15035
30
+ en: df205a26b6d49168
31
+ zh: e46ed7cf9e06248a
32
32
  /deepseek-ai-dsh-web-app/use-this-package/per-session-agent-setup:
33
33
  en: ccd6fc264b51e44b
34
34
  zh: 3265c68477a89ec9
35
35
  /deepseek-ai-dsh-web-app/understand-the-implementation:
36
- en: fb819d10930089a8
37
- zh: f2dee0ff170a4d41
36
+ en: 2ea88398028ebff8
37
+ zh: 0e589c297aeb9d43
38
38
  /deepseek-ai-dsh-web-app/understand-the-implementation/patch-semantics:
39
- en: 954a424ea7182c8f
40
- zh: 9339bc21dba44d40
39
+ en: 8a2554b3d17daa1f
40
+ zh: 66f18946791cabb6
41
41
  /deepseek-ai-dsh-web-app/understand-the-implementation/advertised-application-url:
42
42
  en: ca977d2b2bcf72c5
43
43
  zh: 2b81b377c133c1a8
44
44
  /deepseek-ai-dsh-web-app/understand-the-implementation/readiness:
45
45
  en: 973128fd346869df
46
46
  zh: 54e6792f9f82a192
47
- /deepseek-ai-dsh-web-app/understand-the-implementation/lan-trust-sampling:
48
- en: b5ed8ff0056ca9da
49
- zh: fdc1fa4f14cb00b3
50
47
  /deepseek-ai-dsh-web-app/understand-the-implementation/source-map:
51
- en: e41444bed021a121
52
- zh: 44088a90df50d5b4
48
+ en: d4c7f39a9fd90683
49
+ zh: ac2dbf75b2ceb255
53
50
  /deepseek-ai-dsh-web-app/further-exploration:
54
51
  en: f81694f7424e2988
55
52
  zh: 6643f9227039e228
@@ -69,8 +66,8 @@
69
66
  en: 1d7d87f54b20b563
70
67
  zh: d150a51615d278e8
71
68
  /deepseek-ai-dsh-web-app/known-limitations-and-deferred-work:
72
- en: d6de1d07249ef3b0
73
- zh: 49d01329256e2eb3
69
+ en: 635c1336e2f4e821
70
+ zh: caabdecb02e2df14
74
71
  /deepseek-ai-dsh-web-app/known-limitations-and-deferred-work/dev-note:
75
72
  en: 3f81b4f3beb74a0d
76
73
  zh: 2e377ca40822d093
package/README.md CHANGED
@@ -13,7 +13,7 @@ Desktop analytics schedules partial batches every 30 seconds, with a 15-second e
13
13
 
14
14
  ## Summary
15
15
 
16
- Run `dsh --profile web` for browser chat, model and settings management, and session history, using the same model access, tools, and safety defaults as other dsh surfaces. Startup prints a tokenized URL and normally opens the default browser; SSH sessions and `--no-open` require manual opening. You can change the port and allow extra hosts, but cannot bind all network interfaces. Remote access supports an advertised public HTTP(S) URL behind a prefix-stripping proxy. For one-shot command-line tasks, use `dsh-headless`.
16
+ Run `dsh --profile web` for browser chat, model and settings management, and session history, with the same model access, tools, and safety defaults as other dsh surfaces. Startup prints a tokenized URL and normally opens the default browser; SSH sessions and `--no-open` require manual opening. You can change the port, allow extra authorities, bind one concrete local IP, and serve HTTPS from a certificate you supply; wildcard addresses are rejected. Remote access uses an advertised HTTP(S) URL behind a prefix-stripping proxy, listener TLS, or plain HTTP on a non-loopback bind. Use the headless profile for one-shot command-line tasks.
17
17
 
18
18
  ## Table of Contents
19
19
 
@@ -40,36 +40,49 @@ dsh --profile web --no-open --port 8080
40
40
 
41
41
  After startup you see a `dsh web:` line whose root URL carries a fresh process token. Unless `--no-open` or an SSH session suppresses it, the default browser opens that URL, receives a signed cookie, and redirects to the same directory without the token. You know it worked when the page loads and you can chat with the agent. Two failures to expect: if the frontend is not built, startup stops with a build hint (`pnpm run build` in a checkout); if the browser cannot be opened, a credential-free diagnostic prints to stderr while the server keeps running — open the printed startup URL yourself.
42
42
 
43
- **Settings → Models** displays **DeepSeek**, using `DEEPSEEK_API_KEY`. The default is `deepseek-official` / `deepseek-flash` (DeepSeek-V41-Flash). The [DeepSeek plugin](../../llm/llm-deepseek/README.md#endpoint-and-wire-format) uses the Messages API.
43
+ **Settings → Models** displays **DeepSeek**, using `DEEPSEEK_API_KEY`. The default is `deepseek-official` / `deepseek-flash` (DeepSeek-V4.1-Flash). The [DeepSeek plugin](../../llm/llm-deepseek/README.md#endpoint-and-wire-format) uses the Messages API.
44
44
 
45
45
  Saved model selections override the composition default. The settings card accepts a Messages-compatible API address and a credential reference.
46
46
 
47
47
  ### Configuration
48
48
 
49
- `--host` and `--port` configure the listener; `--public-url` names the advertised public HTTP(S) root the GUI is reached at behind a prefix-stripping proxy, and `--trusted-host` adds further accepted authorities. All are described under [Listening, trust, and public deployments](#public-deployments):
49
+ `--host` and `--port` configure the listener; `--tls-cert` and `--tls-key` make it serve HTTPS; `--public-url` names the advertised public HTTP(S) root the GUI is reached at behind a prefix-stripping proxy, and `--trusted-host` adds further accepted authorities. All are described under [Listening, trust, and public deployments](#public-deployments), and the `tls` field those flags set is documented with the [carrier's config](../../../docs/subsystems/web-server.md#config):
50
50
 
51
51
  | Field | Default | Meaning |
52
52
  |---|---|---|
53
53
  | `openBrowser` | `true` | Open the default browser after startup; SSH launches suppress it |
54
54
  | `printUrl` | `true` | Print the `dsh web:` URL line at startup |
55
55
  | `surfaceContext` | `true` | Give the agent GUI-orientation context and expose `DSH_WEB_URL` to its shell commands |
56
- | `publicUrl` | Unset | Advertised HTTP(S) application root; otherwise announce the listener's loopback URL |
57
- | `trustedHosts` | `[]` | Extra hosts allowed to reach the GUI from the network |
56
+ | `publicUrl` | Unset | Advertised HTTP(S) application root; otherwise announce the bind-address URL |
58
57
 
59
58
  The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-web-app) lists this runtime plugin's accepted fields and their JSDoc. The shipped composition inserts the `schedule` service row and the `ui-schedule` task page row, while the clock reading and the four reminder tools belong to the `standard`, `cordis`, and `ptc` presets. The `tool-subagent` and `tool-subagent-fork` rows in those presets deny the four tools, so a delegated child's scope never lists them.
60
59
 
61
60
  <a id="public-deployments"></a>
62
61
  ### Listening, trust, and public deployments
63
62
 
64
- By default the GUI listens on loopback and accepts connections from this machine only; repeatable `--trusted-host` adds the authorities its Host/Origin fence accepts, so a remote browser reaches it behind a prefix-stripping proxy or through a port-forwarding client that presents a trusted hostname.
63
+ By default the GUI listens on loopback and accepts connections from this machine only. Only `--host` changes the listener: it accepts one concrete local IPv4 or IPv6 address, never a wildcard. A non-loopback listener without TLS serves plain HTTP and warns at startup, even when an HTTPS `--public-url` fronts it. The Host/Origin fence accepts the bind IP itself; other non-loopback authorities require `--trusted-host`, so a remote browser reaches the GUI behind a prefix-stripping proxy or through a port-forwarding client that presents a trusted hostname. The launch-token exchange and signed session cookie authenticate every API method and WebSocket stream.
64
+
65
+ Pass `--tls-cert` and `--tls-key` together to serve HTTPS from the listener instead of plain HTTP. Each names a file resolved against the process working directory: the certificate file holds the full chain and the key file an unencrypted PEM private key. The flags are one setting — supplying only one is a usage error, and supplying neither keeps the plain-HTTP default. Startup fails when a file is missing, unreadable, empty, or not a valid certificate-and-key pair; it never falls back to HTTP. Nothing re-reads the material while the listener runs, so serving a different certificate needs a listener reload or a process restart. DSH issues, renews, and watches nothing: there is no ACME client, no self-signed fallback, and no redirect listener. The browser applies its own trust store and host-name matching, so a certificate the browser does not trust, or one whose subject alternative names miss the authority you open, fails before the page loads. The default port stays 3080, and `--tls-cert`/`--tls-key` do not follow `--public-url`: the listener's certificate covers the authority browsers dial. TLS changes nothing about authority: Connection still authenticates every request and still checks Host/Origin, and the session cookie it mints is `Secure` whenever the receiving listener serves HTTPS.
66
+
67
+ A container can bind its Pod address and advertise the ingress that fronts it:
68
+
69
+ ```sh
70
+ dsh --profile web --no-open --host "$(hostname -i | awk '{print $1}')" --public-url https://app.example/ --trusted-host app.example
71
+ ```
72
+
73
+ `hostname -i` may list several addresses; `--host` takes one, so the command selects the first. A zone ID such as `fe80::1%eth0` cannot appear in a URL, so the advertised root drops a redundant loopback zone while a non-loopback zone requires an explicit `--public-url`.
74
+
75
+ Without `--public-url`, mapped IPv6 loopback binds such as `::ffff:127.0.0.1` advertise their IPv4 form, `127.0.0.1`, so the browser recognizes a local, trustworthy origin. The listener keeps the configured bind address. A dotted-quad IPv6 tail names the address's own low 32 bits, exactly as `listen` reads it, so `::0.0.0.1` is IPv6 loopback and advertises `[::1]`, while `::127.0.0.1` is a non-loopback address, not IPv4 loopback.
65
76
 
66
77
  `--public-url` advertises the HTTP(S) root browsers use — the printed and opened startup URL, `DSH_WEB_URL`, and the web-surface orientation. Advertisement grants no trust: the browser-visible authority must also be named with `--trusted-host`. The flag configures no listener, routing, or cookie scope, because the proxy owns the external leg: [Publish the Web UI behind a reverse proxy](../../../docs/user/guide/public-deployments.md) lists what such a deployment must provide.
67
78
 
68
- The printed URL contains a process credential; share it only with intended users, and `printUrl: false` suppresses the line with or without `--public-url`.
79
+ Neither the advertised URL nor the browser-trust fence protects the port itself, so restrict the port to the trusted proxy or network.
80
+
81
+ The printed URL contains a process credential; share it only with intended users. `printUrl: false` suppresses the line with or without `--public-url`, and the credential never appears in the model context or in `DSH_WEB_URL`.
69
82
 
70
83
  ### Running over SSH
71
84
 
72
- When you launch `dsh --profile web` over SSH, the URL line still prints but the browser is not opened for you: the SSH client or editor owns the local forwarding address. Without an advertised root the printed URL names the remote host's loopback endpoint, which you reach through your forwarding address. With `--public-url` the printed URL is the authenticated advertised root; the browser handoff stays suppressed, because opening a browser on the remote host cannot reach your screen.
85
+ When you launch `dsh --profile web` over SSH, the URL line still prints but the browser is not opened for you: the SSH client or editor owns the local forwarding address. Without an advertised root the printed URL names the remote host's bind-address endpoint — loopback for a loopback bind — which you reach through your forwarding address. With `--public-url` the printed URL is the authenticated advertised root; the browser handoff stays suppressed, because opening a browser on the remote host cannot reach your screen.
73
86
 
74
87
  ### Per-session agent setup
75
88
 
@@ -83,11 +96,11 @@ Each browser session selects a shipped preset (`standard` by default). The Agent
83
96
  <details>
84
97
  <summary>Implementation internals — click to expand</summary>
85
98
 
86
- The bundle is one patch layer of five files plus one runtime glue plugin: `cordis.patch.yml` carries the host rows and the preset registry, and each `presets/<id>.patch.yml` inserts one shipped preset declaration, applied in the order `dsh.bundle.patch` lists them. The storage stack and projection cache come from `dsh-base`; the web overlay's workspace and message-feedback rows consume that shared `storageDomain` service. The patch restates the surface-specific values the base deliberately omits, inserts the web-only host rows and browser roster, then moves the agent plane behind presets. The glue plugin owns dist serving, the advertised application URL, trust sampling, prompt sections, the bash variable, and the readiness announcements. The `office-to-pdf` row mounts one lazy [Office conversion provider](../../document/office-to-pdf/README.md) for Host consumers, including Desktop compositions using this bundle. The conversion service's Remote methods authorize preview reads, while Document Preview owns the Office viewer and Client cache.
99
+ The bundle is one patch layer of five files plus one runtime glue plugin: `cordis.patch.yml` carries the host rows and the preset registry, and each `presets/<id>.patch.yml` inserts one shipped preset declaration, applied in the order `dsh.bundle.patch` lists them. The storage stack and projection cache come from `dsh-base`; the web overlay's workspace and message-feedback rows consume that shared `storageDomain` service. The patch restates the surface-specific values the base deliberately omits, inserts the web-only host rows and browser roster, then moves the agent plane behind presets. The glue plugin owns dist serving, the advertised application URL, the plain-HTTP exposure warning, prompt sections, the bash variable, and the readiness announcements. The `office-to-pdf` row mounts one lazy [Office conversion provider](../../document/office-to-pdf/README.md) for Host consumers, including Desktop compositions using this bundle. The conversion service's Remote methods authorize preview reads, while Document Preview owns the Office viewer and Client cache.
87
100
 
88
101
  ### Patch semantics
89
102
 
90
- A patch replaces the targeted row's whole `config`, so each web row restates every key it owns: the persona prefix and suffix templates, the `DSH_TOOLS_MODE` PTC mode opt-in, and the `session-query-sqlite` values on the base rows, then `insert` adds the web host rows, transport, and browser roster. The `webserver` and `web-runtime` rows inject the `webStartup` provider and read their invocation values directly; the `connection` row instead reads the bind-dependent `webRuntime` values the web-runtime row publishes, which are that provider's authorities plus the LAN literals of an all-interfaces bind. The per-agent tool rows the base mounts process-wide are disabled here and the preset roster takes over; the reasoning for each host-plane versus preset-plane decision is inline in the patch.
103
+ A patch replaces the targeted row's whole `config`, so each web row restates every key it owns: the persona prefix template, the `DSH_TOOLS_MODE` PTC mode opt-in, and the `session-query-sqlite` values on the base rows, then `insert` adds the web host rows, transport, and browser roster. The `webserver`, `web-runtime`, and `connection` rows inject the `webStartup` provider and read their invocation values directly; Connection composes its accepted authorities from `ctx.webStartup.trustedHosts`, and the Host/Origin fence accepts the listener's bind IP independently of that list. The per-agent tool rows the base mounts process-wide are disabled here and the preset roster takes over; the reasoning for each host-plane versus preset-plane decision is inline in the patch.
91
104
 
92
105
  ### Advertised application URL
93
106
 
@@ -97,23 +110,18 @@ Startup display and browser handoff receive the advertised root with its launch
97
110
 
98
111
  The URL line and browser handoff are readiness signals: supervisors RPC as soon as they observe the line, and a browser requests the page as soon as it opens, so both run only after the Loader tree settles, the required-startup audit passes, and Connection authentication is available — or immediately in a hand-built tree without a Loader. Client combo JavaScript and source maps remain unmaterialized at this point. Optional plugin failures do not suppress readiness; a required startup failure or a tree disposed mid-boot announces nothing.
99
112
 
100
- ### LAN trust sampling
101
-
102
- `resolveLanTrust` samples the network once at boot: a loopback bind (`127.0.0.1`) derives no LAN addresses, while an all-interfaces bind adds every non-internal IPv4 literal. The derived literals plus the explicit `--trusted-host` authorities form the `/api` browser-trust fence, and the printed LAN URL always matches that fence.
103
-
104
113
  ### Source map
105
114
 
106
115
  | File | Role |
107
116
  |---|---|
108
- | [`src/index.ts`](src/index.ts) | The `web-app` glue plugin: dist resolution, advertised application URL, LAN trust sampling, prompt sections, bash variable, URL line, browser handoff |
117
+ | [`src/index.ts`](src/index.ts) | The `web-app` glue plugin: dist resolution, advertised application URL, prompt sections, bash variable, URL line, browser handoff |
109
118
  | [`src/public-url.ts`](src/public-url.ts) | Advertised-root validation and trailing-slash normalization; a leaf module for local imports, not package API |
110
- | [`src/startup.ts`](src/startup.ts) | The `web-startup` provider: `--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open`, `--help` |
119
+ | [`src/startup.ts`](src/startup.ts) | The `web-startup` provider: `--host`, `--port`, `--tls-cert`, `--tls-key`, `--public-url`, `--trusted-host`, `--no-open`, `--help` |
111
120
  | [`cordis.patch.yml`](cordis.patch.yml) | The web patch: restated base values, web host rows, browser roster, preset registry |
112
121
  | [`presets/`](presets) | One `@deepseek-ai/dsh-agent-preset` declaration per shipped preset (`standard`, `ptc`, `minimal`, `cordis`), each its own patch file |
113
- | [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | Dist resolution, fallback seat, prompt sections, readiness, advertised URL publication |
122
+ | [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | Dist index anchoring, advertised-URL publication and warnings, prompt sections, readiness publication |
114
123
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree |
115
124
  | [`tests/public-url.spec.ts`](tests/public-url.spec.ts) | Advertised-root parsing and normalization |
116
- | [`tests/trusted-hosts.spec.ts`](tests/trusted-hosts.spec.ts) | LAN-trust sampling |
117
125
  | [`tests/browser-open.spec.ts`](tests/browser-open.spec.ts) | Default-browser handoff after the page is reachable |
118
126
 
119
127
  </details>
@@ -158,12 +166,12 @@ Source and Web sections follow first-party reusable instructions. Different chec
158
166
  These limits tell you what to expect in unusual setups — a source checkout, SSH sessions, or strict networks. They are current package constraints, not a general browser comparison or a task backlog.
159
167
 
160
168
  - **The frontend must be built** — a source checkout needs `pnpm run build` first; startup stops with a build hint when the dist is missing, and there is no source-serving fallback.
161
- - **The listener has no TLS** — protect the external leg with a TLS-terminating proxy; an HTTP advertised root sends credentials without encryption.
162
- - **LAN addresses are sampled once at startup** — interface changes after boot are not re-advertised; the printed LAN URL always matches what was sampled.
169
+ - **TLS is opt-in and unmanaged** — the listener serves HTTPS only when `--tls-cert` and `--tls-key` name a full chain and its unencrypted key; DSH never obtains, renews, or watches certificates, and a changed file needs a listener reload or a process restart. The browser must trust that certificate, its names must cover the authority you open, and the certificate is independent of `--public-url`.
170
+ - **Without TLS the external leg is plain HTTP** — protect it with a TLS-terminating proxy, and do not send the launch URL over a network you do not trust.
163
171
  - **Only the handoff start is observable** — the GUI reports that the browser was asked to open, not that it actually opened; a later browser exit is never reported, and the printed URL is your manual fallback.
164
- - **SSH sessions keep the URL but skip the browser handoff** — without an advertised root the printed URL names the remote host's loopback endpoint; the SSH client or editor must expose and open the local forwarded address.
172
+ - **SSH sessions keep the URL but skip the browser handoff** — without an advertised root the printed URL names the remote host's bind-address endpoint (loopback for a loopback bind); the SSH client or editor must expose and open the local forwarded address.
165
173
  - **`BROWSER` overrides only come from the environment** — a discovered `.env` cannot set `BROWSER`; only an inherited value can choose the executable for the automatic handoff.
166
- - **Binding all network interfaces is not supported** — `--host 0.0.0.0` is rejected at startup for safety; use the default loopback host.
174
+ - **Binding all network interfaces is not supported** — a wildcard `--host`, including the IPv4-mapped forms Linux reads as IPv4 any, is rejected at startup for safety; bind one concrete IPv4 or IPv6 address of a local interface instead.
167
175
 
168
176
  <a id="dev-note"></a>
169
177
  ### Dev Note
package/README.zh.md CHANGED
@@ -13,7 +13,7 @@ kind: "package-bundle"
13
13
 
14
14
  ## 概述
15
15
 
16
- 运行 `dsh --profile web`,获得浏览器内的聊天、模型与设置管理以及会话历史,并与其他 dsh 表层共用同一套模型访问、工具与安全默认值。启动时会打印带 token 的 URL,通常还会在默认浏览器中打开;SSH 会话和 `--no-open` 需要手动打开。你可以更改端口并允许额外主机,但不能绑定所有网络接口。跨机访问支持在剥离前缀的代理之后公告一个公开的 HTTP(S) URL。一次性的命令行任务应使用 `dsh-headless`。
16
+ 运行 `dsh --profile web`,获得浏览器内的聊天、模型与设置管理以及会话历史,并与其他 dsh 表层共用同一套模型访问、工具与安全默认值。启动时会打印带 token 的 URL,通常还会在默认浏览器中打开;SSH 会话和 `--no-open` 需要手动打开。你可以更改端口、允许额外 authority、绑定一个具体的本机 IP,并用自己提供的证书直接提供 HTTPS;通配地址会被拒绝。跨机访问使用剥离前缀代理后公告的 HTTP(S) URL、监听器 TLS,或非 loopback 绑定上的明文 HTTP。一次性的命令行任务应使用 headless 配置。
17
17
 
18
18
  ## 目录
19
19
 
@@ -40,36 +40,49 @@ dsh --profile web --no-open --port 8080
40
40
 
41
41
  启动后你会看到 `dsh web:` 行,其根 URL 携带新的进程 token。除非 `--no-open` 或 SSH 会话抑制,否则默认浏览器会打开该 URL、取得签名 cookie,再重定向到不含认证参数的同一目录。页面加载且你可以与 agent 对话,就说明成功了。两种可预期的失败:前端未构建时,启动会以构建提示停止(checkout 中运行 `pnpm run build`);浏览器无法打开时,stderr 会打印不含凭据的诊断,但服务器会继续运行——请自行打开已打印的启动 URL。
42
42
 
43
- **设置 → 模型**显示 **DeepSeek**,使用 `DEEPSEEK_API_KEY`。默认模型为 `deepseek-official` / `deepseek-flash`(DeepSeek-V41-Flash)。[DeepSeek 插件](../../llm/llm-deepseek/README.zh.md#endpoint-and-wire-format)使用 Messages API。
43
+ **设置 → 模型**显示 **DeepSeek**,使用 `DEEPSEEK_API_KEY`。默认模型为 `deepseek-official` / `deepseek-flash`(DeepSeek-V4.1-Flash)。[DeepSeek 插件](../../llm/llm-deepseek/README.zh.md#endpoint-and-wire-format)使用 Messages API。
44
44
 
45
45
  已保存的模型选择覆盖组合默认值。设置卡接受兼容 Messages 的 API 地址与凭据引用。
46
46
 
47
47
  ### 配置
48
48
 
49
- `--host` 与 `--port` 配置监听器;`--public-url` 指定 GUI 在剥离前缀的代理之后对外公告的公开 HTTP(S) 根,`--trusted-host` 则添加更多被接受的 authority。两者都在[监听、信任与公开部署](#public-deployments)中说明:
49
+ `--host` 与 `--port` 配置监听器;`--tls-cert` 与 `--tls-key` 让它直接提供 HTTPS;`--public-url` 指定 GUI 在剥离前缀的代理之后对外公告的公开 HTTP(S) 根,`--trusted-host` 则添加更多被接受的 authority。这些都在[监听、信任与公开部署](#public-deployments)中说明,而这些 flag 设置的 `tls` 字段记录在[载体的配置](../../../docs/subsystems/web-server.zh.md#config)中:
50
50
 
51
51
  | 字段 | 默认值 | 含义 |
52
52
  |---|---|---|
53
53
  | `openBrowser` | `true` | 启动后用默认浏览器打开;SSH 启动会抑制它 |
54
54
  | `printUrl` | `true` | 启动时打印 `dsh web:` URL 行 |
55
55
  | `surfaceContext` | `true` | 给 agent 提供 GUI 定位上下文,并把 `DSH_WEB_URL` 暴露给其 shell 命令 |
56
- | `publicUrl` | 未设置 | 对外公告的 HTTP(S) 应用根;否则公告监听器的 loopback URL |
57
- | `trustedHosts` | `[]` | 允许从网络访问 GUI 的额外主机 |
56
+ | `publicUrl` | 未设置 | 对外公告的 HTTP(S) 应用根;否则公告绑定地址 URL |
58
57
 
59
58
  生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-web-app)列出了本运行时插件接受的字段及其 JSDoc。随发行版交付的组合插入 `schedule` 服务行与 `ui-schedule` 任务页面行,时钟读数与四个提醒工具则属于 `standard`、`cordis` 与 `ptc` 三个 preset。这三个 preset 的 `tool-subagent` 与 `tool-subagent-fork` 两行都 deny 这四个工具,因此被委派子 agent 的作用域不会列出它们。
60
59
 
61
60
  <a id="public-deployments"></a>
62
61
  ### 监听、信任与公开部署
63
62
 
64
- 默认情况下 GUI 只在 loopback 上监听,只接受本机的连接;可重复的 `--trusted-host` 会添加其 Host/Origin 栅栏接受的 authority,因此远端浏览器要么经由剥离前缀的代理访问,要么通过以可信主机名呈现的端口转发客户端访问。
63
+ 默认情况下 GUI 只监听 loopback,只接受本机的连接。只有 `--host` 会改变监听器:它接受一个具体的本机 IPv4 或 IPv6 地址,不接受通配地址。未启用 TLS 的非 loopback 监听器提供明文 HTTP,并在启动时警告,即使前置 HTTPS 的 `--public-url` 也不例外。Host/Origin 栅栏接受绑定 IP 本身;其他非 loopback authority 需要 `--trusted-host`,因此远端浏览器要么经由剥离前缀的代理访问 GUI,要么通过以可信主机名呈现的端口转发客户端访问。启动 token 交换与签名会话 cookie 认证每个 API 方法与 WebSocket 流。
64
+
65
+ 同时传入 `--tls-cert` 与 `--tls-key`,监听器便以 HTTPS 而非明文 HTTP 提供服务。两者各指定一个相对进程工作目录解析的文件:证书文件包含完整证书链,密钥文件为不带口令的 PEM 私钥。它们属于同一个设置——只提供一个会报用法错误,两个都不提供则保持明文 HTTP 默认值。文件缺失、不可读、为空,或不是有效的证书与密钥对时启动失败;绝不回退到 HTTP。监听器运行期间不会重新读取这些材料,因此更换证书需要重新加载监听器或重启进程。DSH 不签发、不续期也不监视任何证书:没有 ACME 客户端、没有自签名兜底,也没有重定向监听器。浏览器使用自己的信任库与主机名匹配,因此浏览器不信任的证书,或其 subject alternative name 未覆盖你所访问 authority 的证书,会在页面加载前失败。默认端口仍为 3080,且 `--tls-cert`/`--tls-key` 不跟随 `--public-url`:监听器的证书覆盖浏览器实际访问的 authority。TLS 不改变授权模型:Connection 仍认证每个请求并仍校验 Host/Origin,且只要接收请求的监听器提供 HTTPS,它所签发的会话 cookie 就带 `Secure`。
66
+
67
+ 容器可以绑定其 Pod 地址,并公告前置的 ingress:
68
+
69
+ ```sh
70
+ dsh --profile web --no-open --host "$(hostname -i | awk '{print $1}')" --public-url https://app.example/ --trusted-host app.example
71
+ ```
72
+
73
+ `hostname -i` 可能列出多个地址;`--host` 只接受一个,因此命令取第一个。`fe80::1%eth0` 这样的 zone ID 不能出现在 URL 中,因此公告根会剥离多余的 loopback zone,而非 loopback zone 则要求显式提供 `--public-url`。
74
+
75
+ 未设置 `--public-url` 时,`::ffff:127.0.0.1` 这样的 mapped IPv6 回环绑定会公告其 IPv4 形式 `127.0.0.1`,让浏览器将其识别为本地可信来源。监听器仍使用配置的绑定地址。点分四段式的 IPv6 尾部表示该地址自身的低 32 位,与 `listen` 的读法完全一致:`::0.0.0.1` 属于 IPv6 回环并公告 `[::1]`,而 `::127.0.0.1` 是非回环地址,并非 IPv4 回环。
65
76
 
66
77
  `--public-url` 公告浏览器使用的 HTTP(S) 根——打印与打开的启动 URL、`DSH_WEB_URL` 与 web 表层定位。公告不授予任何信任:浏览器可见的 authority 还必须用 `--trusted-host` 点名。该 flag 不配置监听器、路由或 cookie 作用域,因为外部链路归代理所有:[在反向代理之后发布 Web UI](../../../docs/user/guide/public-deployments.zh.md)列出了这样的部署必须提供什么。
67
78
 
68
- 打印的 URL 包含进程凭据,只应与预期用户分享;`printUrl: false` 无论是否配置 `--public-url` 都会抑制该行。
79
+ 无论是公告 URL 还是浏览器信任栅栏都不保护端口本身,因此请把端口限制在可信代理或网络内。
80
+
81
+ 打印的 URL 包含进程凭据,只应与预期用户分享。`printUrl: false` 无论是否配置 `--public-url` 都会抑制该行,凭据也绝不会出现在模型上下文或 `DSH_WEB_URL` 中。
69
82
 
70
83
  ### 通过 SSH 运行
71
84
 
72
- 通过 SSH 启动 `dsh --profile web` 时,URL 行仍会打印,但不会为你打开浏览器:本地转发地址由 SSH 客户端或编辑器持有。没有公告根时,打印的 URL 指向远端宿主机 loopback 端点,你通过自己的转发地址访问它。配置了 `--public-url` 时,打印的 URL 是带认证的公告根;浏览器交接仍被抑制,因为在远端宿主机上打开浏览器无法到达你的屏幕。
85
+ 通过 SSH 启动 `dsh --profile web` 时,URL 行仍会打印,但不会为你打开浏览器:本地转发地址由 SSH 客户端或编辑器持有。没有公告根时,打印的 URL 指向远端宿主机的绑定地址端点——绑定 loopback 时即 loopback——你通过自己的转发地址访问它。配置了 `--public-url` 时,打印的 URL 是带认证的公告根;浏览器交接仍被抑制,因为在远端宿主机上打开浏览器无法到达你的屏幕。
73
86
 
74
87
  ### 按会话的 agent 设置
75
88
 
@@ -83,11 +96,11 @@ dsh --profile web --no-open --port 8080
83
96
  <details>
84
97
  <summary>实现细节——点击展开</summary>
85
98
 
86
- 此 bundle 由一层五个文件的补丁和一个运行时胶水插件组成:`cordis.patch.yml` 承载宿主行和 preset 注册表,每个 `presets/<id>.patch.yml` 插入一条随发行版交付的 preset 声明,按 `dsh.bundle.patch` 列出的顺序应用。存储栈与投影缓存来自 `dsh-base`;Web 叠加层的工作区和消息反馈条目消费共享的 `storageDomain` 服务。补丁重述 base 有意省略的界面专用值,插入 Web 专用宿主条目和浏览器插件列表,再将 Agent 层移到预设后面。胶水插件负责 dist 服务、公告应用 URL、信任采样、提示词段落、bash 变量和就绪通知。`office-to-pdf` 条目为宿主消费者挂载一个延迟创建引擎的 [Office 转换提供方](../../document/office-to-pdf/README.zh.md),使用此 bundle 的 Desktop 组合也共享该提供方。 转换服务的 Remote 方法负责预览读取授权,Document Preview 负责 Office 查看器和客户端缓存。
99
+ 此 bundle 由一层五个文件的补丁和一个运行时胶水插件组成:`cordis.patch.yml` 承载宿主行和 preset 注册表,每个 `presets/<id>.patch.yml` 插入一条随发行版交付的 preset 声明,按 `dsh.bundle.patch` 列出的顺序应用。存储栈与投影缓存来自 `dsh-base`;Web 叠加层的工作区和消息反馈条目消费共享的 `storageDomain` 服务。补丁重述 base 有意省略的界面专用值,插入 Web 专用宿主条目和浏览器插件列表,再将 Agent 层移到预设后面。胶水插件负责 dist 服务、公告应用 URL、明文 HTTP 暴露警告、提示词段落、bash 变量和就绪通知。`office-to-pdf` 条目为宿主消费者挂载一个延迟创建引擎的 [Office 转换提供方](../../document/office-to-pdf/README.zh.md),使用此 bundle 的 Desktop 组合也共享该提供方。 转换服务的 Remote 方法负责预览读取授权,Document Preview 负责 Office 查看器和客户端缓存。
87
100
 
88
101
  ### patch 语义
89
102
 
90
- patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己拥有的每个键:基础行上的 persona 前缀与后缀模板、`DSH_TOOLS_MODE` PTC mode 开关与 `session-query-sqlite` 值,随后 `insert` 添加 Web 宿主行、传输层与浏览器名录。`webserver` 与 `web-runtime` 行注入 `webStartup` 提供方并直接读取本次调用的取值;`connection` 行则改为读取 web-runtime 行发布的、与绑定相关的 `webRuntime` 值,即该提供方的 authority 加上全接口绑定的 LAN 字面量。base 以进程级挂载的按 agent 工具行在这里被禁用,由 preset 名录接管;每项宿主层与 preset 层归属决策的理由以行内注释写在 patch 里。
103
+ patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己拥有的每个键:基础行上的 persona 前缀模板、`DSH_TOOLS_MODE` PTC mode 开关与 `session-query-sqlite` 值,随后 `insert` 添加 Web 宿主行、传输层与浏览器名录。`webserver`、`web-runtime` 与 `connection` 行都注入 `webStartup` 提供方并直接读取本次调用的取值;Connection 的被接受 authority 全部来自 `ctx.webStartup.trustedHosts`,Host/Origin 栅栏则独立于该列表接受监听器的绑定 IP。base 以进程级挂载的按 agent 工具行在这里被禁用,由 preset 名录接管;每项宿主层与 preset 层归属决策的理由以行内注释写在 patch 里。
91
104
 
92
105
  ### 公告应用 URL
93
106
 
@@ -97,23 +110,18 @@ patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己
97
110
 
98
111
  URL 行与浏览器交接都是就绪信号:监督方一观察到该行就发起 RPC,浏览器一打开就请求页面,因此两者只在 Loader 配置树结算、通过 required 启动检查且 Connection 认证可用后运行——在没有 Loader 的手工构建树中则立即运行。此时 client combo JavaScript 和 source map 仍未物化。可选插件失败不会阻止就绪宣告;required 启动失败或启动中途被释放的树不会宣告任何内容。
99
112
 
100
- ### LAN 信任采样
101
-
102
- `resolveLanTrust` 在启动时只采样一次网络:loopback 绑定(`127.0.0.1`)不派生任何 LAN 地址,绑定所有网卡则会加入每个非 internal IPv4 字面量。派生字面量加上显式的 `--trusted-host` 权威标识组成 `/api` 浏览器信任栅栏,打印的 LAN URL 始终与该栅栏一致。
103
-
104
113
  ### 源码地图
105
114
 
106
115
  | 文件 | 职责 |
107
116
  |---|---|
108
- | [`src/index.ts`](src/index.ts) | `web-app` 粘合插件:dist 解析、公告应用 URL、LAN 信任采样、提示词段落、bash 变量、URL 行、浏览器交接 |
117
+ | [`src/index.ts`](src/index.ts) | `web-app` 粘合插件:dist 解析、公告应用 URL、提示词段落、bash 变量、URL 行、浏览器交接 |
109
118
  | [`src/public-url.ts`](src/public-url.ts) | 公告根的校验与尾斜杠归一化;供本地导入的叶子模块,不属于包 API |
110
- | [`src/startup.ts`](src/startup.ts) | `web-startup` 提供方:`--host`、`--port`、`--public-url`、`--trusted-host`、`--no-open`、`--help` |
119
+ | [`src/startup.ts`](src/startup.ts) | `web-startup` 提供方:`--host`、`--port`、`--tls-cert`、`--tls-key`、`--public-url`、`--trusted-host`、`--no-open`、`--help` |
111
120
  | [`cordis.patch.yml`](cordis.patch.yml) | Web patch:重述的基础值、Web 宿主行、浏览器名录、preset 注册表 |
112
121
  | [`presets/`](presets) | 每个随发行版交付的 preset(`standard`、`ptc`、`minimal`、`cordis`)各一条 `@deepseek-ai/dsh-agent-preset` 声明,各自一个补丁文件 |
113
- | [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | dist 解析、回退席位、提示词段落、就绪宣告、公告 URL 发布 |
122
+ | [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | dist index 锚定、公告 URL 发布与警告、提示词段落、就绪宣告发布 |
114
123
  | [`tests/startup.spec.ts`](tests/startup.spec.ts) | 在真实 Loader 树上的命令行解析 |
115
124
  | [`tests/public-url.spec.ts`](tests/public-url.spec.ts) | 公告根的解析与归一化 |
116
- | [`tests/trusted-hosts.spec.ts`](tests/trusted-hosts.spec.ts) | LAN 信任采样 |
117
125
  | [`tests/browser-open.spec.ts`](tests/browser-open.spec.ts) | 页面可达后的默认浏览器交接 |
118
126
 
119
127
  </details>
@@ -158,12 +166,12 @@ URL 行与浏览器交接都是就绪信号:监督方一观察到该行就发
158
166
  这些限制告诉你在不常见的环境下会遇到什么——源码 checkout、SSH 会话或严格网络。它们是当前包约束,不是通用的浏览器对比或任务积压。
159
167
 
160
168
  - **前端必须已构建**——源码 checkout 需要先运行 `pnpm run build`;dist 缺失时启动会以构建提示停止,且没有从源码直接服务的回退路径。
161
- - **监听器不提供 TLS**——请用终止 TLS 的代理保护外部链路;HTTP 公告根会以明文发送凭据。
162
- - **LAN 地址只在启动时采样一次**——启动后的网卡变化不会重新公告;打印的 LAN URL 始终与采样结果一致。
169
+ - **TLS 需自行启用且不代管**——只有传入 `--tls-cert` 与 `--tls-key`(完整证书链与不带口令的密钥)时监听器才提供 HTTPS;DSH 从不获取、续期或监视证书,更换文件需要重新加载监听器或重启进程。浏览器必须信任该证书,其名称必须覆盖你所访问的 authority,且证书与 `--public-url` 相互独立。
170
+ - **未启用 TLS 时外部链路是明文 HTTP**——请用终止 TLS 的代理保护它,不要在不受信任的网络上发送启动 URL。
163
171
  - **只能观察到交接的启动**——GUI 只报告浏览器被请求打开,而不是它确实打开了;之后的浏览器退出永远不会上报,打印的 URL 是你的手动回退路径。
164
- - **SSH 会话保留 URL 但跳过浏览器交接**——没有公告根时,打印的 URL 指向远端宿主机 loopback 端点;SSH 客户端或编辑器必须暴露并打开本地转发地址。
172
+ - **SSH 会话保留 URL 但跳过浏览器交接**——没有公告根时,打印的 URL 指向远端宿主机的绑定地址;SSH 客户端或编辑器必须暴露并打开本地转发地址。
165
173
  - **`BROWSER` 覆盖只能来自环境**——被发现的 `.env` 不能设置 `BROWSER`;只有继承值能为自动交接选择可执行文件。
166
- - **不支持绑定所有网络接口**——出于安全考虑,`--host 0.0.0.0` 会在启动时被拒绝;请使用默认 loopback 主机。
174
+ - **不支持绑定所有网络接口**——出于安全考虑,通配 `--host`(含 Linux 视为 IPv4 any 的 IPv4-mapped 形式)会在启动时被拒绝;请改为绑定本机某个接口的一个具体 IPv4 或 IPv6 地址。
167
175
 
168
176
  <a id="dev-note"></a>
169
177
  ### 开发备注
package/cordis.patch.yml CHANGED
@@ -7,15 +7,15 @@
7
7
  #
8
8
  # The web-startup plugin injects `cmdlineArgs` and provides `webStartup` as an
9
9
  # ordinary Cordis service. Rows configured from flags inject that service, so
10
- # Loader resolves their expressions only after it exists. The web runtime then
11
- # provides bind-dependent `webRuntime` values to the trust fence.
10
+ # Loader resolves their expressions only after it exists. The web-runtime row
11
+ # validates the advertised public URL; Connection reads invocation authorities
12
+ # directly from `webStartup`.
12
13
  # `dsh --profile web --help` provides no `webStartup`, so no server binds.
13
14
 
14
15
  # ── surface-specific values the base deliberately omits ─────────────────────
15
16
 
16
17
  - id: system-prompt
17
18
  config:
18
- personaSuffix: Your working directory is {{cwd}}.
19
19
  personaPrefix: >-
20
20
  You are a coding agent powered by the {{model}} model.
21
21
 
@@ -31,7 +31,7 @@
31
31
 
32
32
  - id: tools
33
33
  config:
34
- # TEMPORARY workaround: DSH_TOOLS_MODE (native|ptc|both) opts a whole dsh
34
+ # TEMPORARY workaround: DSH_TOOLS_MODE (native|ptc) opts a whole dsh
35
35
  # process into PTC mode while per-session tool-presentation selection is being
36
36
  # designed; unset keeps the schema default (native). Remove the env seam
37
37
  # once the web UI owns the choice per session.
@@ -174,13 +174,15 @@
174
174
  # webStartup provider, with these deployment fallbacks. The dist is served
175
175
  # by the web-runtime row below through the fallback seat. Routing and
176
176
  # browser-facing asset paths do not depend on the advertised URL, which the
177
- # web-runtime row owns.
177
+ # web-runtime row owns; Connection reads trust authorities from webStartup.
178
+ # This row rejects wildcard binds.
178
179
  - id: webserver
179
180
  name: '@deepseek-ai/dsh-host-webserver'
180
181
  inject: [webStartup]
181
182
  config:
182
183
  host: !!js ctx.webStartup.host ?? '127.0.0.1'
183
184
  port: !!js ctx.webStartup.port ?? 3080
185
+ tls: !!js ctx.webStartup.tls
184
186
  compression: gzip
185
187
  compressionLevel: 1
186
188
  compressionThresholdBytes: 1024
@@ -191,9 +193,8 @@
191
193
  # section and the bash runtime variable, prints the URL line, and opens the
192
194
  # canonical URL after the full Loader tree settles and the required-
193
195
  # entry audit passes. Invocation-only values come from the webStartup
194
- # provider; the advertised application URL is this row's `publicUrl` (the
195
- # loopback URL when unset). After the server binds, this row samples LAN
196
- # trust once and provides `webRuntime`. A complete agent-preset persona
196
+ # provider; the advertised application URL is this row's `publicUrl`,
197
+ # otherwise the bind-address URL. A complete agent-preset persona
197
198
  # suppresses the prompt section for that agent while retaining the
198
199
  # host-owned shell variable.
199
200
  - id: web-runtime
@@ -204,7 +205,6 @@
204
205
  printUrl: true
205
206
  publicUrl: !!js ctx.webStartup.publicUrl
206
207
  surfaceContext: true
207
- trustedHosts: !!js ctx.webStartup.trustedHosts
208
208
 
209
209
  # The client-plugin reload chain, always mounted: it is idle until a
210
210
  # rebuild watcher (pnpm run dev:web) actually rewrites client bundles. It
@@ -224,16 +224,16 @@
224
224
 
225
225
  # Owns both ends of the web transport: node half binds the gateway to the
226
226
  # webserver under /api; browser half is the fetch/SSE client. Every API
227
- # request and upgrade passes the loopback plus trustedHosts policy; an
228
- # advertised public URL contributes nothing to it.
227
+ # request and upgrade passes the loopback, listener-bind, or trustedHosts
228
+ # policy; an advertised public URL contributes nothing to it.
229
229
  - id: connection
230
230
  name: '@deepseek-ai/dsh-client-connection'
231
- inject: [webRuntime]
231
+ inject: [webStartup]
232
232
  config:
233
- # LAN literals derived from the active bind plus --trusted-host extras.
234
- # A deployment adding authorities keeps this expression and concatenates
235
- # its literals, for example: ['app.internal', ...ctx.webRuntime.trustedHosts].
236
- trustedHosts: !!js ctx.webRuntime.trustedHosts
233
+ # Keep invocation authorities (each --trusted-host value) when adding
234
+ # deployment values, for example: ['app.internal', ...ctx.webStartup.trustedHosts].
235
+ # The listener's bind IP is accepted separately from this list.
236
+ trustedHosts: !!js ctx.webStartup.trustedHosts
237
237
 
238
238
  # Raw Blob and ReadableStream uploads run independently of Connection's
239
239
  # RPC and generation service.
@@ -472,8 +472,8 @@
472
472
  disabled: true
473
473
 
474
474
  # The background-job REGISTRY stays on the host plane; only the model-facing
475
- # `job_*` controls move. Its producers — `tool-bash` here, `tool-terminal` and a
476
- # non-continuable `tool-subagent` elsewhere — are preset rows that resolve it
475
+ # `job_*` controls move. Its producers — `tool-bash` here and `tool-terminal`
476
+ # elsewhere — are preset rows that resolve it
477
477
  # with `ctx.get`, and an entry-local realm around the registry is invisible to
478
478
  # every sibling row outside that realm, so `run_in_background` answered
479
479
  # "background jobs unavailable" while the controls sat in the catalog. That is
@@ -558,12 +558,6 @@
558
558
  - id: tool-workflow
559
559
  disabled: true
560
560
 
561
- # `base` ships this row disabled; the preset-plane gate reads declared row ids
562
- # without regard to `disabled`, so this row must stay to keep `tool-ralph` off
563
- # the host plane and out of collision with every preset's row.
564
- - id: tool-ralph
565
- disabled: true
566
-
567
561
  - id: agent-instructions
568
562
  disabled: true
569
563