diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index 62a1d31d0d..e1bb5b58c4 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -166,7 +166,11 @@ export interface LaunchOptions { } /** Leave the current welcome notice unacknowledged; ordinary scenarios publish it as complete before browser boot. */ welcomeNoticePending?: boolean - /** Browse through this trusted non-loopback hostname while the test server stays bound to loopback. */ + /** + * Browse through a trusted non-loopback hostname that the browser resolves + * to loopback (for example `*.localhost`). The test server stays bound to + * 127.0.0.1; a non-resolving authority fails before Host trust is exercised. + */ remoteAuthority?: string } diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml index 974e3014d6..e159696db3 100644 --- a/packages/client/connection/README.i18n.yaml +++ b/packages/client/connection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/connection/README.md -README.md: c8b7c4787cbcbf6a202fb944459a589fcadd7c8d -README.zh.md: 693420183ffa4fb20e1fecbff523a12261a45d45 +README.md: 522ae6a14a3b4b07e7f2917133d16a5e83433f69 +README.zh.md: 4eaed862df678997b328d7a4ddcab1f5254c7c60 diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md index c8b7c4787c..522ae6a14a 100644 --- a/packages/client/connection/README.md +++ b/packages/client/connection/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` seam, and the loop's sink/config types. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`/`update`/`replace`/`mutate` and `credentials.describe`/`set`/`unset`, reads included, since describing returns the exposed configuration and probing an arbitrary reference reports where a credential comes from) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform subclasses (WebApiClient/FixtureApiClient), the ConnectionController loop, and the fixture data source are package-internal — apply selects and drives them; tests reach them via src. Contract: api-contracts v3 §3. +Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` seam, and the loop's sink/config types. The dedicated `./loopback-hostname` source subpath exposes the zero-dependency predicate shared by the `/api` Host fence and browser welcome-persistence selection; client bundlers inline this source entry, while plain Node cannot load it directly, so it must remain browser-safe and dependency-free. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`/`update`/`replace`/`mutate` and `credentials.describe`/`set`/`unset`, reads included, since describing returns the exposed configuration and probing an arbitrary reference reports where a credential comes from) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform subclasses (WebApiClient/FixtureApiClient), the ConnectionController loop, and the fixture data source are package-internal — apply selects and drives them; tests reach them via src. Contract: api-contracts v3 §3. ## /api browser-trust fence diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md index 693420183f..4eaed862df 100644 --- a/packages/client/connection/README.zh.md +++ b/packages/client/connection/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 单消费方流循环启动器);导出表层携带协议契约类型、`AbstractApiClient` seam,以及循环的 sink/配置类型。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory`、`host.openPath`,以及整个配置面——`settings.describe`/`update`/`replace`/`mutate` 与 `credentials.describe`/`set`/`unset`,读取也在内,因为 describe 会返回已暴露的配置,而探测任意引用会报出某条凭据来自何处)以空信任表过信任 fence,从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台子类(WebApiClient/FixtureApiClient)、ConnectionController 循环和 fixture 数据源都属于包内部:apply 负责选择并驱动它们,测试则通过 src 访问。契约:api-contracts v3 §3。 +协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 单消费方流循环启动器);导出表层携带协议契约类型、`AbstractApiClient` seam,以及循环的 sink/配置类型。专用的 `./loopback-hostname` 源码子路径导出 `/api` Host fence 与浏览器欢迎页持久化选择共用的零依赖判定函数;客户端 bundler 会内联这一源码入口,而 plain Node 无法直接加载它,因此它必须保持浏览器安全且零依赖。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory`、`host.openPath`,以及整个配置面——`settings.describe`/`update`/`replace`/`mutate` 与 `credentials.describe`/`set`/`unset`,读取也在内,因为 describe 会返回已暴露的配置,而探测任意引用会报出某条凭据来自何处)以空信任表过信任 fence,从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台子类(WebApiClient/FixtureApiClient)、ConnectionController 循环和 fixture 数据源都属于包内部:apply 负责选择并驱动它们,测试则通过 src 访问。契约:api-contracts v3 §3。 ## /api 浏览器信任栅栏 diff --git a/packages/client/connection/src/loopback-hostname.ts b/packages/client/connection/src/loopback-hostname.ts index 8fd30445bd..5666f0714d 100644 --- a/packages/client/connection/src/loopback-hostname.ts +++ b/packages/client/connection/src/loopback-hostname.ts @@ -1,3 +1,12 @@ +/** + * Browser-safe, zero-dependency loopback classification shared by the `/api` + * Host fence and browser welcome-persistence selection. The dedicated + * `./loopback-hostname` source subpath is inlined into client bundles instead + * of loaded by plain Node, so this module must not add Node-only or runtime + * dependencies. + * @module @deepseek-ai/dsh-client-connection/loopback-hostname + */ + /** * Whether a normalized URL hostname names the local loopback authority. * @param hostname - WHATWG URL hostname (IPv6 literals retain brackets). diff --git a/packages/client/ui-settings-general/src/client/welcome-store.ts b/packages/client/ui-settings-general/src/client/welcome-store.ts index fdff28f052..c95c9e46d8 100644 --- a/packages/client/ui-settings-general/src/client/welcome-store.ts +++ b/packages/client/ui-settings-general/src/client/welcome-store.ts @@ -117,7 +117,9 @@ export class WelcomeNoticeStore { } /** - * Refresh only after the welcome step has begun reading durable state. + * Refresh only after welcome state has left idle. A memory-mode load retains + * acknowledgement so reconnect and settings-change refreshes do not reopen a + * process-local notice. * @param controller - welcome state owner whose current status decides whether to load. */ export function refreshWelcomeIfLoaded(controller: WelcomeNoticeStore): void {