@khorsheed/dsh-mobile 0.1.0 → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # 变更记录
2
+
3
+ ## 0.1.2(2026-09-28)
4
+
5
+ - **public tunnel 恢复**,QR 码按隧道可达性门禁——隧道不通时不再展示一张扫不开的二维码
6
+
7
+ ## 0.1.1(2026-09-27)
8
+
9
+ 按活体试用反馈修复六处移动端问题;README 中英统一为中文主版并落地实拍截图(两张从未存在的引用删除)。对宿主 `0.1.7-rc.2` 类型面构建+测试通过(2026-09-27)。
10
+
11
+ - 冷会话标题水合修复;能力目录富化加上限
12
+ - Room 邀请弹层的模型选择器与表单字段间距对齐
13
+ - Room 底部弹层支持触摸下滑关闭;文本标题的面包屑适配
14
+ - 官方统一输入框形态下恢复工具面板(tools sheet)
15
+ - 无编辑器焦点时恢复头部动作与「打开」命令
16
+ - 工作区页脚对齐与有界列表滚动修复
17
+
18
+ ## 0.1.0(2026-09-26)
19
+
20
+ 首个公开发布。
package/README.en.md ADDED
@@ -0,0 +1,118 @@
1
+ # @khorsheed/dsh-mobile
2
+
3
+ English | [中文](README.md)
4
+
5
+ Your dsh sessions, in your pocket — the Mac still runs the sessions, models, skills and tools; the phone just gets an interface built for it.
6
+
7
+ The official Web client is desktop-shaped: a sidebar that eats the screen, a dense composer, touch targets made for a mouse. This plugin re-presents the very same client for narrow touch screens — a conversation library with grouping and search, a wrapping composer with reachable controls, a sheet for picking directories on the computer — and adds an optional [iOS shell](../../apps/ios/README.md) that embeds that same official Web client and pairs to it over a QR login link. Everything stays removable: uninstalling restores the official desktop UI exactly, and no Host or sibling-plugin state is ever written.
8
+
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/mobile-library.png" width="640" alt="the mobile conversation library: time and workspace grouping, the bottom search field and the QR pairing entry">
10
+
11
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/mobile-conversation.png" width="640" alt="a conversation in mobile layout: the session header with its Members tab, the message flow, and the composer toolbar at the bottom">
12
+
13
+ ## Features
14
+
15
+ - **Conversation library home** — time/workspace grouping, collapsible workspace sections and bottom search. Grouping stays local to this browser (`dsh.mobile.grouping`); the official chat and your draft remain mounted behind navigation.
16
+ - **A real mobile layout, client-local** — full-width right-panel details, mobile settings, and a wrapping composer toolbar with larger touch targets (reading text ≥ 17px, primary controls 16–17px). The official conversation renderer, composer, model/permission selectors, attachments and streaming transport stay in place.
17
+ - **Pick a computer directory from your phone** — saved workspaces, one-level folder browsing, parent navigation, a hidden-folder toggle and a validated absolute-path fallback. Workspace adoption and Room member picks share this sheet; desktop clients keep the native chooser.
18
+ - **Connect a phone by QR** — an authenticated Web settings section mints the official login link for your deployment's HTTPS origin and shows it as a locally encoded QR; the iOS shell scans, confirms the host, and is in.
19
+ - **Versioned native bridge** — an authenticated `GET /api/mobile/handshake` plus a bridge-version-1 message channel for presentation status and optional native settings/scanning. Foreground recovery rides the official connection service and never replays a send command; brief interruptions and initial connecting states avoid redundant reconnects, while a suspension of 5 s or more still refreshes the stream generation.
20
+ - **Plays well with the ecosystem, requires none of it** — no community plugin is a boot dependency. Long-press recognized user messages for message-tools' original copy/edit/withdraw; the plus sheet opens Room's original invite form; TaskPilot and Local Agent surfaces keep their owners.
21
+
22
+ This is a development baseline, not a released or real-device-qualified app — see Known Limitations.
23
+
24
+ ## Install
25
+
26
+ ```sh
27
+ dsh plugin --profile web add @khorsheed/dsh-mobile
28
+ ```
29
+
30
+ The package self-mounts its own row — do not edit profile YAML. Restart the web instance and reload the client to activate.
31
+
32
+ ```sh
33
+ dsh plugin --profile web remove @khorsheed/dsh-mobile
34
+ ```
35
+
36
+ Unload removes owned styles, frame markers, observers, listeners, routes and slot contributions. It does not delete sessions, cancel Host tasks, close the shared connection, revoke official cookies or uninstall the App/VPN. Preferences may remain for reinstall.
37
+
38
+ A narrow touch screen (`max-width: 760px` with a coarse pointer) or the native shell enables the layout automatically. `?mobile=1` explicitly enables it; `?mobile=0` disables it. The mobile settings choice persists only in this browser under `dsh.mobile.display`; after selecting desktop layout, `?mobile=1` restores the settings entry. The login exchange redirects to `/`, so apply a query override after authentication if needed.
39
+
40
+ ## Connect a phone from Web settings
41
+
42
+ The authenticated Web client exposes **Settings → Connect phone** through the public `settings.section` slot — available on desktop without enabling the mobile layout. Configure the phone-reachable **clean HTTPS origin** as the plugin's `publicOrigin` option, or set `DSH_MOBILE_PUBLIC_ORIGIN` on the **Host process** (the plugin option wins). Add the same hostname to the Host's `--trusted-host` list; the ingress `PUBLIC_ORIGIN` must also match. These are deployment settings, not phone preferences.
43
+
44
+ ```sh
45
+ DSH_MOBILE_PUBLIC_ORIGIN=https://YOUR-HOST.trycloudflare.com dsh web --no-open --port 3181 --trusted-host YOUR-HOST.trycloudflare.com
46
+ ```
47
+
48
+ 1. Open Web **Settings → Connect phone** and check the displayed host.
49
+ 2. Click **Show login QR code**.
50
+ 3. In the iOS App, open **Connection Settings → Scan to connect**, scan and confirm the host.
51
+
52
+ `GET /api/mobile/connect` returns configuration status only. An authenticated, same-origin JSON `POST` obtains the official `connection.authenticatedUrl()` for the deployment-owned origin — browser input cannot override the destination. Both routes ride the official Connection registry, preserving its cookie and Host/Origin checks, and answer `no-store`. QR encoding happens locally in the browser: no third-party QR service, no secret logging, no browser persistence. Leaving the section, hiding the page or waiting two minutes conceals the code and aborts pending requests. Missing/invalid origin, an unsupported Host capability or an untrusted destination shows setup guidance instead of generating a login link.
53
+
54
+ **Concealment is not credential expiry.** The QR carries the official process login token, valid until that Host process restarts — not a short-lived or one-use pairing token. Hiding or regenerating the QR does not revoke a copied link or a logged-in phone, and existing cookies keep their Host-owned lifetime.
55
+
56
+ Different physical networks need an independently configured HTTPS/WSS ingress to the loopback Host port, valid official authentication, an awake/online Mac and a reachable phone — starting the Host alone does not establish remote access, and the App does not configure networking or credentials for you. Debug simulator builds allow HTTP on loopback only; Release requires HTTPS.
57
+
58
+
59
+ ### Desktop Quick Tunnel recovery
60
+
61
+ The connection page anonymously probes the configured public root before offering or generating a QR. An unavailable origin returns an actionable state without issuing a login URL. A displayed QR is checked every 15 seconds and hidden if the endpoint fails or changes. This is a computer-side reachability check, not a guarantee about the phone's network.
62
+
63
+ For this repository's supervised 3080 deployment, `pnpm mobile:tunnel` runs an opt-in desktop recovery manager; `--repair-now` skips the initial three-check delay. It checks every 30 seconds, waits through a local host outage, and rebuilds only after three consecutive public failures. Recovery attempts have a ten-minute cooldown. It starts an exact-origin loopback ingress and a new Quick Tunnel, then calls `pnpm deploy:3080 --package packages/mobile --mobile-origin https://new-link.trycloudflare.com`. This runs the normal build/test and guarded `reconfigure` path, updating the existing public-origin/trusted-host bindings and preserving other launch settings. It restarts the host; failure retains the guard's restore-previous behavior. Changed mobile/deployment source files block automated deployment.
64
+
65
+ Keep the manager running under the computer's service manager (for example, a per-user macOS LaunchAgent). It is separate from the host process and is not installed by the plugin. `DSH_HOME` selects the existing supervised deployment; `CLOUDFLARED_BIN` optionally supplies the installed cloudflared binary. State and diagnostic logs live under `$DSH_HOME/state/mobile-tunnel/`; they contain no login tokens. Only an existing explicit Quick Tunnel binding is supported: no wildcard trusted hosts, account provisioning, or automatic changes to the phone. After rotation, reopen **Connect phone** and scan its new QR.
66
+
67
+ ## Optional: a Quick Tunnel preview
68
+
69
+ Networking is deployment configuration, not a plugin dependency — bring your own HTTPS reverse tunnel/server or private networking; the plugin embeds no domain, server or Cloudflare account. For temporary previews, [Cloudflare Quick Tunnels](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/) mint a temporary HTTPS address without an account or domain: no uptime guarantee, 200 concurrent requests, no SSE (the tested conversation transport uses WebSocket).
70
+
71
+ ```sh
72
+ cloudflared tunnel --url http://127.0.0.1:3182 --protocol quic --no-autoupdate
73
+ PUBLIC_ORIGIN=https://YOUR-HOST.trycloudflare.com HOST_PORT=3181 INGRESS_PORT=3182 node packages/mobile/examples/https-ingress.mjs
74
+ dsh web --no-open --port 3181 --trusted-host YOUR-HOST.trycloudflare.com # own DSH_HOME, workspace, model
75
+ ```
76
+
77
+ The bundled `examples/https-ingress.mjs` binds loopback only, preserves the public Host/Origin for official authentication, requires the HTTPS forwarded scheme, adds `Secure` to upstream cookies and forwards HTTP streams plus WebSocket upgrades. It reads no Host private keys and adds no pairing or device-revocation system — run it only behind the HTTPS tunnel; authorization stays with the Host. For phone login, replace only the origin of the Host's launch URL with the tunnel origin, preserving `?token=...`; keep that credential-bearing URL private. A new tunnel allocates a new hostname: update ingress origin, Host trusted-host and mobile public origin together, then log in again — old cookies are bound to their old authority. Stopping the tunnel closes this public path; removing the plugin alone does not. See the [Quick Tunnel acceptance record](../../docs/acceptance/mobile-quick-tunnel-2026-09-11.md).
78
+
79
+ ## Optional collaboration surfaces
80
+
81
+ Install message-tools, TaskPilot, Room and the Local Agent family separately; mobile reads their public capabilities and never requires them to boot. Long-press recognized user messages for the original copy/edit/withdraw actions; unrecognized renderers keep their original controls. The plus sheet opens **Room's original invite form** styled as a mobile bottom sheet — provider discovery, authentication, random names, first-task dispatch and removal confirmation all stay with Room; there is no second mobile invite/edit API. Mobile temporarily adapts the public `uiWorkspace.pickDirectory` method in this browser so Room's original form consumes the sheet's result unchanged, and restores it on unload. Room and Local Agent keep their own composers, so not every ordinary-session TaskPilot pill is promised inside a Room. A known Room revision can leave the composer election stale after its first asynchronous cache fill; a declining mobile chain entry refreshes the public slot election without replacing its winner. See the [collaboration acceptance](../../docs/acceptance/mobile-collaboration-2026-09-12.md).
82
+
83
+ ## Compatibility
84
+
85
+ - npm release line (`@deepseek-ai/dsh@0.1.5-rc.1`): ⚠️ usable with documented degradations — `dsh.compat.minHost` is `0.1.5-rc.1`. Local browser/Host validation and the iOS simulator build pass on `0.1.5-rc.1` (`183f08e9c6`), but the mobile layout and iOS shell are still under validation; **Safari live-reply resume on 0.1.5-rc.1 requires the opt-in bundled ingress adapter** (`MOBILE_SAFARI_COMPAT=1`, see Internals); **device pairing and push are not provided**. Older hosts: stay below `minHost` at your own risk — none are supported.
86
+ - source line (deepseek-harness master): build+test green against the pinned `0.1.7-rc.2` surface (2026-09-27), but per `dsh.compat` no `verifiedHost` is claimed while the real-device/network acceptance matrix is incomplete — audit before adopting other hosts.
87
+
88
+ The library uses the optional official sessions/workspaces/uiWorkspace services, filtering archived sessions and subagent rows while retaining ordinary forks; a missing service falls back to the basic official sidebar. The public frame/slot DOM anchors are checked before the layout enables; an unknown frame keeps the official page — a changed structure can reduce mobile usability without breaking Host execution. Native bridge version changes require an App compatibility decision; compatible Web updates do not automatically require a new IPA. See the [navigation/QR acceptance](../../docs/acceptance/mobile-navigation-2026-09-11.md), the [rc1 acceptance evidence](../../docs/acceptance/mobile-rc1-2026-09-11.md) and the [proposal](../../proposals/active/2026-08-19-mobile-access.md). No upstream or sibling source changes are required.
89
+
90
+ ## Known Limitations
91
+
92
+ - **Development baseline** — camera/file picking, sharing/downloads, background recovery and community-plugin combinations are not yet qualified. The iOS scanner accepts existing official HTTPS login links and requires host confirmation; camera acceptance is still pending.
93
+ - **No per-device credentials** — one-time device pairing, per-device revocation, Keychain pairing and APNs push are all unimplemented; hiding the QR never revokes anything (above).
94
+ - **No hot uninstall qualification** — dependency changes require a controlled Host restart and client reload.
95
+ - **Browser-local preferences only** — layout choice (`dsh.mobile.display`) and library grouping (`dsh.mobile.grouping`) never leave this browser; nothing is written to the Host.
96
+ - **Library search is shallow** — it filters session titles and workspace paths, not message contents.
97
+ - **Foreground recovery has a floor** — healthy brief interruptions skip redundant reconnects, but an already-dead reply subscription does not recover by reconnecting the socket alone.
98
+
99
+ ## Internals
100
+
101
+ <details>
102
+ <summary>Architecture, security boundaries and the Safari adapter (click to expand)</summary>
103
+
104
+ **Host face.** Three routes on the official authenticated Connection registry, attached only when `connection` is composed (`ctx.inject(['connection'])`): `GET/POST /api/mobile/connect` (QR login minting — the POST additionally requires a same-origin JSON action and a `ready` destination, so a navigation or form can never reveal a login URL, and provider errors are never echoed back), `GET /api/mobile/directories` (read-only directory names/paths within the operator's existing filesystem access — never file contents, never creates directories; bounded to 1,000 rows / 10,000 scanned entries with a truncation flag) and `GET /api/mobile/handshake` (read-only feature discovery: bridge version, capabilities, `devicePairing: false`, `pushNotifications: false`). All responses are `no-store`.
105
+
106
+ **Client face.** One browser-owned `MobilePresentation` toggles `data-dsh-mobile` on the document element, owns the stylesheet, observers and listeners, and activates only when the official frame anchors (`[data-slot="root"]` with main/sidebar) match the expected arrangement — otherwise the official page is kept. Slot contributions all go through `slots.inject` (never a bare register): `settings.section`/`mobile-connect`, `shell.overlay`/`mobile-directory` and `/mobile-navigation`, `conversation.input.dock` + `conversation.session.header.actions`/`mobile-send-focus` (dismisses the empty focused editor on successful admission; rejected submissions and newer drafts keep focus), `conversation.session.header.actions`/`mobile-room-queue`, a welcome mark in `conversation.hero.brand.mark`, `conversation.input.left`/`mobile-input-tools` (the plus menu grouping the official attachment/command/permission triggers), and priority-−100 shadows of the two public `directoryFlow` slots while mobile mode is active. The Room composer-election refresh is a declining `conversation.composer` entry that never replaces the winner. Dictionaries ship in `en`/`zh`.
107
+
108
+ **Native bridge (version 1).** The shell advertises `window.__DSH_MOBILE_SHELL__`; the client posts `ready`/`unloaded` with layout and anchor diagnostics, and accepts only matching-origin main-frame messages for `chrome`, `settings` and `scan`. New native actions require advertised capabilities; older shells keep their fallback controls. The bridge grants no file or command privileges.
109
+
110
+ **Safari live-stream compatibility adapter.** Host `0.1.5-rc.1` ships a JSON validator that rejects ordinary objects in Safari when restoring an in-progress reply. The bundled ingress accepts `MOBILE_SAFARI_COMPAT=1` as an opt-in, delivery-layer adaptation — not a Host source patch: only WebKit `GET` requests for `/plugins/` JavaScript are examined, a SHA-256 fingerprint of the complete known validator gates replacement, and unknown or already-fixed code passes through unchanged. Authentication, RPC bodies, WebSocket frames and Host execution are untouched; a direct LAN connection or another ingress receives no adaptation. Bounded JavaScript is gzip-compressed for gzip-accepting clients (assets above 16 MiB, non-JS, auth failures and identity-encoding mismatches pass through); adapted assets carry corrected lengths and no stale ETag/digest under `private/no-store`. Inspect the `x-dsh-mobile-compat` header and the ingress's value-only compatibility log when qualifying a Host upgrade, do not broaden the fingerprint automatically, and remove the flag once an upstream-fixed build passes real WebKit foreground/reconnect acceptance. Run the ingress from an installed tarball's `examples/`, not a development checkout.
111
+
112
+ **Unload.** Disposal removes only owned resources — styles, the `data-dsh-mobile` frame markers, observers, listeners, routes and slot contributions — and restores the adapted `pickDirectory` method. Sessions, Host tasks, the shared connection, official cookies and the App/VPN are all left alone.
113
+
114
+ </details>
115
+
116
+ ## Development
117
+
118
+ Part of the [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo (`packages/mobile`). Issues and contributions welcome there.
package/README.i18n.yaml CHANGED
@@ -1,6 +1,6 @@
1
1
  # Bilingual-pair consistency record: the git blob hash of each side as of the
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record with:
4
- # pnpm run verify-translation-pairing --write packages/mobile/README.md
5
- packages/mobile/README.md: 3ee04dfa141d5163163baa7d574385f05b53e20a
6
- packages/mobile/README.zh.md: 6032673a68af225e9f88b7ef5c7f453345484509
4
+ # pnpm run verify-translation-pairing --write packages/mobile/README.en.md
5
+ packages/mobile/README.en.md: aedcdd10fd6a33f134e2b47764852c251e8571ae
6
+ packages/mobile/README.md: 872d2ff32cf881a39fa40404ed7f5edd17d06c41
package/README.md CHANGED
@@ -1,132 +1,118 @@
1
- # DSH Mobile
1
+ # @khorsheed/dsh-mobile
2
2
 
3
- English | [中文](README.zh.md)
3
+ [English](README.en.md) | 中文
4
4
 
5
- An independently removable mobile presentation for DeepSeek Harness. The Mac continues to own sessions, models, skills, tools and plugin execution. The optional [iOS shell](../../apps/ios/README.md) embeds the same official Web client.
5
+ 把 dsh 会话装进口袋——会话、模型、skill、工具仍由 Mac 跑,手机只拿走一个真正为它做的界面。
6
6
 
7
- ## Features
7
+ 官方 Web 客户端是桌面形态:侧栏吃掉半个屏幕、输入区密密麻麻、触控目标按鼠标设计。这个插件把同一套客户端重新呈现给窄屏触控设备——带分组和搜索的会话首页、可换行且够得着的输入工具栏、在手机上选电脑目录的面板——再配上一个可选的 [iOS 薄壳](../../apps/ios/README.zh.md):嵌入同一套官方 Web 客户端,扫码登录即用。一切都可以干净移除:卸载即精确还原官方桌面界面,绝不写入宿主或兄弟插件的状态。
8
8
 
9
- - A conversation library with time/workspace grouping, collapsible workspace sections and bottom search. Grouping stays local to this browser; the official chat and draft remain mounted behind navigation.
10
- - Client-local mobile layout, full-width right-panel details, mobile settings and a wrapping composer toolbar with larger touch targets.
11
- - Official conversation renderer, composer, model/permission selectors, attachments and streaming transport stay in place.
12
- - Choose a **computer** directory in a mobile sheet: saved workspaces, one-level folder browsing, parent navigation, hidden-folder toggle and a validated absolute-path fallback. Workspace adoption and Room member picks share this sheet; desktop clients keep the native chooser.
13
- - Authenticated `GET /api/mobile/handshake`; versioned native bridge for presentation status and optional native settings/QR scanning. Foreground recovery uses the official connection service, never replays a send command. Healthy brief interruptions and initial connecting signals avoid redundant reconnects; suspension of at least 5 s still refreshes the stream generation.
14
- - No required community plugins. Existing message-tools/member/preview surfaces retain their owners; their mobile combinations still require acceptance.
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/mobile-library.png" width="640" alt="移动端会话首页:时间与工作区分组、底部搜索框与扫码入口">
15
10
 
16
- This is a development baseline, not a released or real-device-qualified app. Camera/file picking, sharing/downloads, background recovery and community combinations are not yet qualified. The iOS scanner accepts existing official HTTPS login links and requires host confirmation; camera acceptance is still pending. No one-time device pairing, per-device credential revocation or APNs is provided.
11
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/mobile-conversation.png" width="640" alt="移动布局下的会话页:会话头部与成员 tab、消息流、底部输入工具栏">
17
12
 
18
- ## Install and remove
13
+ ## 特性
19
14
 
20
- Build and pack from a repository worktree first:
15
+ - **会话首页**——时间/工作区分组、可折叠的工作区分节和底部搜索。分组偏好只存在当前浏览器(`dsh.mobile.grouping`);导航覆盖期间官方聊天与草稿保持挂载。
16
+ - **真正的移动布局,仅作用于当前客户端**——全宽右栏详情、移动设置、可换行且触控区域更大的输入工具栏(正文最低 17px,主要控件 16–17px)。官方正文渲染、输入框、模型/权限选择、附件与流式传输原样保留。
17
+ - **在手机上选电脑目录**——已有工作区、逐级浏览、返回上级、显示隐藏目录、校验完整路径。工作区接入和 Room 成员目录选择共用此面板;桌面客户端保留原生选择器。
18
+ - **扫码连接手机**——已认证的 Web 设置分区为部署的 HTTPS origin 现场生成官方登录链接,并在浏览器本地编码成二维码;iOS 壳扫码、确认主机即连上。
19
+ - **带版本的原生桥接**——受认证的 `GET /api/mobile/handshake` 加 bridge version 1 消息通道,提供展示状态与可选原生设置/扫码入口。回前台经官方连接服务恢复,绝不重放发送命令;短暂切换且连接正常、初次正在连接时不重复重连,后台停留至少 5 秒仍刷新流式连接代际。
20
+ - **与生态共处,但不强依赖**——没有任何社区插件是启动依赖。长按已识别的用户消息调用 message-tools 原有的复制/编辑/撤回;加号面板打开 Room 原有邀请表单;TaskPilot 与 Local Agent 界面各归其主。
21
21
 
22
- ```sh
23
- pnpm --filter @khorsheed/dsh-mobile build
24
- pnpm --filter @khorsheed/dsh-mobile test
25
- pnpm exec tsx scripts/pack-dist.ts --package packages/mobile --scope @khorsheed --version 0.1.0 --out packages/mobile/.dev/dist
26
- ```
22
+ 当前是开发基线,尚未发布或通过真机资格验收——见已知限制。
27
23
 
28
- Install the resulting tarball with `dsh plugin --profile web add /path/to/package.tgz`; remove with `dsh plugin --profile web remove @khorsheed/dsh-mobile`. The package self-mounts; do not edit profile YAML. On the tested rc1 instance, dependency changes require a controlled Host restart and client reload; hot uninstall is not qualified. Use an isolated `DSH_HOME` and test port during development. Production 3080 still goes through the repository deployment gate.
24
+ ## 安装
29
25
 
30
- A narrow touch screen or the native shell enables the layout automatically. `?mobile=1` explicitly enables it; `?mobile=0` disables it. The mobile settings choice persists only in this browser under `dsh.mobile.display`; library grouping uses `dsh.mobile.grouping`. After selecting desktop layout, `?mobile=1` restores the settings entry. The login exchange redirects to `/`, so apply a query override after authentication if needed.
26
+ ```sh
27
+ dsh plugin --profile web add @khorsheed/dsh-mobile
28
+ ```
31
29
 
32
- Unload removes owned styles, frame markers, observers, listeners, routes and slot contributions. It does not delete sessions, cancel Host tasks, close the shared connection, revoke official cookies or uninstall the App/VPN. Preferences may remain for reinstall. Browser hot-unload and tarball acceptance are recorded separately below.
30
+ 插件自挂载自身行——不要手改 profile YAML。重启 web 实例并刷新客户端后生效。
33
31
 
34
- ## Connection and authentication
32
+ ```sh
33
+ dsh plugin --profile web remove @khorsheed/dsh-mobile
34
+ ```
35
35
 
36
- The App accepts a configured HTTPS origin or official launch-token login URL. It persists the clean origin only; WebKit stores the official browser session cookie. The bridge accepts only matching-origin main-frame messages with bridge version 1, and supports `ready`, `unloaded`, `chrome`, `settings` and `scan`. New native actions require advertised capabilities; older shells keep their fallback controls. It does not grant file or command privileges.
36
+ 卸载移除自有样式、布局标记、observer、监听、路由和槽位贡献;不删除会话、不取消宿主任务、不关闭共享连接、不撤销官方 Cookie、不卸载 App/VPN。偏好可保留用于重装。
37
37
 
38
- Different physical networks need an independently configured HTTPS/WSS ingress to the loopback Host port (3080 in production), valid official authentication, an awake/online Mac and reachable phone. Starting 3080 alone does not establish remote access. The App does not configure networking or credentials on your behalf. Debug simulator builds allow HTTP on loopback only; Release requires HTTPS. Clear local App data and server-side device revocation are different operations.
38
+ 窄屏触控设备(`max-width: 760px` 且粗指针)或原生壳自动启用移动布局。`?mobile=1` 显式启用,`?mobile=0` 停用。移动设置中的选择仅保存在当前浏览器的 `dsh.mobile.display`;切到桌面布局后,可用 `?mobile=1` 恢复设置入口。官方登录交换会跳转 `/`,需要时在认证后再加参数。
39
39
 
40
- ## Connect a phone from Web settings
40
+ ## 从 Web 设置连接手机
41
41
 
42
- The authenticated Web client exposes **Settings → Connect phone** through the public `settings.section` slot. This is available on desktop without enabling mobile layout. Configure the phone-reachable **clean HTTPS origin** in the mobile plugin's `publicOrigin` option, or set `DSH_MOBILE_PUBLIC_ORIGIN` on the **Host process**. The plugin option takes precedence. Add the same hostname to the Host's `--trusted-host` list; the ingress `PUBLIC_ORIGIN` must also match. These are deployment settings, not phone preferences.
42
+ 已登录的 Web 客户端通过公开 `settings.section` 槽位提供 **设置 → 连接手机**——桌面无需开启移动布局。将手机可达的**无凭据 HTTPS origin** 配置为插件的 `publicOrigin` 选项,或在 **Host 进程**上设置 `DSH_MOBILE_PUBLIC_ORIGIN`(插件选项优先)。同一域名需加入 Host 的 `--trusted-host`,ingress 的 `PUBLIC_ORIGIN` 也必须一致。这些属于部署设置,不是手机偏好。
43
43
 
44
44
  ```sh
45
45
  DSH_MOBILE_PUBLIC_ORIGIN=https://YOUR-HOST.trycloudflare.com dsh web --no-open --port 3181 --trusted-host YOUR-HOST.trycloudflare.com
46
46
  ```
47
47
 
48
- Use an isolated profile for the example; change production launch configuration through its supervised cutover flow. Missing/invalid origin, unsupported Host capability and an untrusted destination show setup guidance without generating a login link.
48
+ 1. 打开 Web「设置 → 连接手机」,核对展示的主机。
49
+ 2. 点击**显示登录二维码**。
50
+ 3. 在 iOS App 进入**连接设置 → 扫码连接电脑**,扫描后确认主机。
49
51
 
50
- 1. Open Web Settings → Connect phone and check the displayed host.
51
- 2. Click **Show login QR code**.
52
- 3. In the iOS App, open **Connection Settings → Scan to connect**, scan and confirm the host.
52
+ `GET /api/mobile/connect` 仅返回配置状态。已认证、同源的 JSON `POST` 为部署者配置的 origin 调用官方 `connection.authenticatedUrl()` 取登录链接——浏览器输入不能覆盖目标地址。两个接口都经官方 Connection 注册,保留 Cookie 与 Host/Origin 校验,响应均为 `no-store`。二维码在浏览器本地编码:不调用第三方二维码服务、不记录凭据日志、不写浏览器存储。离开设置分区、页面隐藏或展示两分钟后会隐藏二维码并取消未完成的请求。地址缺失或无效、宿主能力不支持、目标域名未受信任时,只显示配置引导,不生成登录链接。
53
53
 
54
- `GET /api/mobile/connect` returns configuration status only. An authenticated, same-origin JSON `POST` obtains the official `connection.authenticatedUrl()` for the deployment-owned origin; browser input cannot override the destination. Both routes use the official Connection registry, preserving its cookie and Host/Origin checks. Responses are no-store. QR encoding happens locally in the browser; no third-party QR service, secret logging or browser persistence is used. Leaving the section, hiding the page or waiting two minutes conceals the code and aborts pending requests.
54
+ **隐藏不等于凭据过期。** 二维码携带的是宿主进程的官方登录 token,在该进程重启前有效——不是短时或一次性配对码。隐藏或重新生成二维码不会撤销已复制的链接,也不会登出手机;已有 Cookie 的有效期仍由宿主控制。
55
55
 
56
- **Concealment is not credential expiry.** This is the official process login token, valid until that Host process restarts, not a short-lived/one-use pairing token. Existing cookies have their own Host-owned lifetime. Hiding or regenerating the QR does not revoke a copied link or a logged-in phone. Per-device credentials, revocation, Keychain pairing and push remain unimplemented.
56
+ 跨物理网络需要独立配置的 HTTPS/WSS 入口转发到 loopback Host 端口、有效官方认证、保持唤醒联网的 Mac 和网络可达的手机——仅启动 Host 不等于远程可用,App 也不会替你配置网络或凭据。Debug 模拟器构建仅允许 loopback HTTP,Release 要求 HTTPS。
57
57
 
58
- If the entry is unreachable, restore the HTTPS tunnel first. A running tunnel process with zero active connections cannot carry requests. When a temporary domain changes, update ingress origin, Host trusted hostname and mobile public origin together, then generate a new login QR; old cookies are bound to their old authority. A successful local 3080 response alone does not prove the phone can connect. Prefer a stable HTTPS entry for regular use. Scanning does not renew an expired iOS development signature.
59
58
 
60
- ## Optional Quick Tunnel preview
59
+ ### 电脑端 Quick Tunnel 自动恢复
61
60
 
62
- Networking is deployment configuration, not a plugin dependency. Community users can choose Quick Tunnel for temporary previews, their own HTTPS reverse tunnel/server, or private networking. No personal domain, server or Cloudflare account is embedded in the plugin. A stable managed relay would be a separate service with its own operating costs.
61
+ 连接手机页面在提供或生成二维码前,会匿名检查已配置的公网入口。不可达时返回明确状态,不签发登录链接。正在展示的二维码每 15 秒复查一次,入口失效或变化时收起。这是电脑侧连通性检查,不能保证手机所处网络同样可达。
63
62
 
64
- [Cloudflare Quick Tunnels](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/) create a temporary HTTPS address without a Cloudflare account or your own domain. They are for testing, have no uptime guarantee, allow 200 concurrent requests and do not support SSE. The tested rc1 conversation transport uses WebSocket; this does not qualify unrelated plugins that require SSE.
63
+ 本仓库受监督的 3080 部署可选择运行 `pnpm mobile:tunnel`,`--repair-now` 可跳过初始三次检测的等待。管理进程每 30 秒检测一次,本地宿主不可用时等待;公网连续三次失败才重建,恢复尝试之间至少间隔十分钟。它启动严格绑定新域名的 loopback 转发和新 Quick Tunnel,再调用 `pnpm deploy:3080 --package packages/mobile --mobile-origin https://new-link.trycloudflare.com`,通过正常构建、测试及受控 `reconfigure` 更新原有公网地址和信任域名,其余启动设置不变。此过程会重启宿主,失败保留守护进程的回退机制。移动包或部署代码存在未提交修改时,自动部署会被阻止。
65
64
 
66
- Use an isolated Host profile with the mobile tarball installed. Install `cloudflared` from its official distribution and use Node 22+. From the repository root, start the tunnel first (the origin can be offline while the address is allocated):
65
+ 通过电脑的服务管理器保持运行,例如 macOS 的用户级 LaunchAgent。它独立于宿主进程,不由插件自动安装。`DSH_HOME` 指向现有受监督部署,`CLOUDFLARED_BIN` 可指定已安装的 cloudflared。状态和诊断日志位于 `$DSH_HOME/state/mobile-tunnel/`,不包含登录 token。仅支持已明确配置的 Quick Tunnel:不增加通配信任、不注册账号、不自动修改手机地址。域名切换后重新打开「连接手机」,扫描新二维码即可。
67
66
 
68
- ```sh
69
- cloudflared tunnel --url http://127.0.0.1:3182 --protocol quic --no-autoupdate
70
- ```
67
+ ## 可选:Quick Tunnel 预览
71
68
 
72
- Protocol choice depends on the local network. If the registered QUIC tunnel stalls on real requests, try `--protocol http2`; HTTP/2 can also fail on another network, so neither protocol is a universal default. A registered tunnel or a healthy loopback origin alone is not enough evidence. Verify public login, the session list and a full WebSocket reply before sharing the preview. A new Quick Tunnel allocates another hostname: update both the ingress origin and the Host trusted-host through the guarded launch cutover, then reconnect the app. Keep these processes running independently of short-lived automation sessions.
73
-
74
- Copy its generated hostname in place of `YOUR-HOST.trycloudflare.com` below. Start the standalone example in a second terminal:
69
+ 网络接入属于部署配置,不是插件依赖——自建 HTTPS 反向隧道/服务器或私网连接均可;插件不嵌入任何域名、服务器或 Cloudflare 账号。临时预览可用 [Cloudflare Quick Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/):无需账号或自有域名即可生成临时 HTTPS 地址,但不保证在线时间、限 200 并发、不支持 SSE(实测会话传输走 WebSocket)。
75
70
 
76
71
  ```sh
72
+ cloudflared tunnel --url http://127.0.0.1:3182 --protocol quic --no-autoupdate
77
73
  PUBLIC_ORIGIN=https://YOUR-HOST.trycloudflare.com HOST_PORT=3181 INGRESS_PORT=3182 node packages/mobile/examples/https-ingress.mjs
74
+ dsh web --no-open --port 3181 --trusted-host YOUR-HOST.trycloudflare.com # 独立 DSH_HOME、工作区、模型
78
75
  ```
79
76
 
80
- Then start the isolated Host with its own `DSH_HOME`, workspace and configured model, adding the exact public authority through the official flag:
77
+ 随包分发的 `examples/https-ingress.mjs` 只绑定 loopback,保留公网 Host/Origin 交给官方认证,要求转发协议为 HTTPS,为上游 Cookie 补充 `Secure`,并透传 HTTP 流与 WebSocket 升级。它不读取 Host 私钥,也不增加配对或逐设备撤销系统——仅在 HTTPS 隧道之后运行,授权始终由 Host 负责。手机登录时,仅将 Host 启动链接的 origin 替换为隧道 origin,保留 `?token=...`;带凭据链接请私下保存。新建隧道会分配新域名:同步更新 ingress origin、Host trusted-host 和 mobile 公网 origin 后重新登录——旧 Cookie 绑定旧 authority。停止隧道进程即关闭这条公网路径;仅卸载插件不会。参见 [Quick Tunnel 验收记录](../../docs/acceptance/mobile-quick-tunnel-2026-09-11.md)。
81
78
 
82
- ```sh
83
- dsh web --no-open --port 3181 --trusted-host YOUR-HOST.trycloudflare.com
84
- ```
85
-
86
- The example is also included under `examples/` in the package tarball. It binds only to loopback, preserves the public Host and Origin for official authentication, requires an HTTPS forwarded scheme, adds Secure to upstream cookies, and forwards HTTP streams and WebSocket upgrades. It neither reads Host private keys nor adds a pairing or device-revocation system. Run it only behind the HTTPS tunnel; a local header is not proof of an authenticated user. The Host remains responsible for authorization.
87
-
88
- For phone login, replace only the origin of the Host's launch URL with the tunnel HTTPS origin, preserving its `?token=...`. Open it in Safari or paste it into the iOS shell. Keep that credential-bearing URL private. Login redirects to a clean `/`; use `?mobile=1` afterwards if the device does not enable mobile layout automatically.
89
-
90
- Keep the Mac awake and all three services running. Stop the tunnel process to close this public access path; removing the mobile plugin alone does not stop the tunnel or revoke official sessions. A newly allocated hostname requires updating both `PUBLIC_ORIGIN` and `--trusted-host` and logging in again. This preview does not establish long-term cellular availability. See the [Quick Tunnel acceptance record](../../docs/acceptance/mobile-quick-tunnel-2026-09-11.md).
91
-
92
- Desktop mode keeps a “Return to mobile layout” button even after reload. Mobile entry suppresses automatic composer focus until an explicit input gesture; rc1 DOM-anchor changes fall back to official focus behavior. Ordinary session titles are deduplicated while ancestor/lineage controls remain visible. These Web-only fixes do not require reinstalling the native App.
79
+ ## 可选协作界面
93
80
 
94
- The polished mobile view retains composer statistics and official message actions, hides the unselected trajectory entry, and contributes its welcome through the public brand seat. Existing session workspace/preset labels are read-only on rc1; only new sessions offer the official pickers. The native settings page now controls layout through the validated `dsh-mobile-display` event. Unknown header geometry preserves the original header and ancestor navigation.
81
+ message-tools、TaskPilot、Room 与 Local Agent 家族独立安装;移动端读取它们的公开能力,从不把它们作为启动依赖。长按已识别的用户消息调用原有复制/编辑/撤回;无法识别的渲染器保留原控件。加号面板打开 **Room 原有邀请表单**,仅调整为移动端底部面板样式——代理发现、认证、随机命名、首个任务派发和移除确认都由 Room 负责,不存在第二套移动邀请/编辑 API。移动端在当前浏览器内临时适配公开的 `uiWorkspace.pickDirectory` 方法,让 Room 原表单原样消费面板返回的结果,卸载时恢复。Room 与 Local Agent 保留各自输入框,因此不承诺 Room 内展示全部普通会话的 TaskPilot 胶囊。已知 Room 版本首次异步填充缓存时可能未刷新输入框选举;移动端用一个始终让出的 chain 条目刷新公开槽位选举,不替换胜者。参见[协作验收](../../docs/acceptance/mobile-collaboration-2026-09-12.md)。
95
82
 
96
- The composer’s plus menu groups the existing attachment, command and permission triggers. The current permission name and available options remain official. Checked rc1 button anchors retain the original callbacks and confirmation flow, with original controls restored if the structure is unknown. Workspace/preset metadata sits below the mobile title; known desktop header controls are hidden, while unknown plugin contributions remain available.
97
-
98
- ## Optional collaboration surfaces
99
-
100
- Install message-tools, TaskPilot, Room and the Local Agent family separately. Mobile reads their public capabilities and does not require them to boot. Long-press recognized user messages for the original copy/edit/withdraw actions; assistant messages use their own turn actions. Confirmations, disabled actions and mutation semantics remain with their owners. Unrecognized renderers retain their original controls.
101
-
102
- The plus sheet opens Room's **original invite form**, styled as a mobile bottom sheet. The member shortcut reads the roster and opens existing child sessions; its settings action opens the original Room members view and edit form. Provider discovery, authentication, random names, first-task dispatch, directory adoption, changed-field patches, removal confirmation and business errors belong to Room. There is no second mobile invite/edit API implementation. Mobile temporarily adapts the public `uiWorkspace.pickDirectory` method in this browser to resolve a selected path from its sheet; the original Room form consumes that result unchanged. Unloading restores the method, and unknown read-only facades keep their original behavior. Blank first task means idle invitation; a supplied first task retains Room's dispatch behavior. Missing checked UI anchors retain the original Members tab as fallback.
83
+ ## Compatibility
103
84
 
104
- Room and Local Agent keep their own composers. The Room queue panel uses the official scoped Conversation API and authoritative `row.text` editability, deduplicates admitted/pending request IDs, and closes an edit when its row disappears or becomes immutable. Goal/todo and TaskPilot entries keep their original owners. Room's own composer replaces the ordinary dock on both platforms, so mobile does not promise that every ordinary TaskPilot pill appears inside a Room.
85
+ - npm 发布线(`@deepseek-ai/dsh@0.1.5-rc.1`):⚠️ 可用,带已声明的降级——`dsh.compat.minHost` 为 `0.1.5-rc.1`。本地浏览器/Host 验证与 iOS 模拟器构建在 `0.1.5-rc.1`(`183f08e9c6`)上通过,但移动布局与 iOS 壳仍在验收中;**Safari 在 0.1.5-rc.1 上恢复进行中回复需要选择启用的随包 ingress 适配层**(`MOBILE_SAFARI_COMPAT=1`,见「实现原理」);**不提供设备配对与推送**。更旧的宿主:低于 `minHost` 自行承担风险——均不受支持。
86
+ - 源码线(deepseek-harness master):对固定的 `0.1.7-rc.2` 类型面构建+测试通过(2026-09-27),但按 `dsh.compat` 的声明,真机/网络验收矩阵完成前不声明 `verifiedHost`——在其他宿主上采用前请先审计。
105
87
 
106
- Mobile reading text uses a 17px floor, primary controls 16–17px and captions approximately 14px, derived from host typography without persisting a host preference. Invite/edit controls remain at least 44px high and their sheet uses the visible viewport when the keyboard opens.
88
+ 会话首页使用可选的官方 sessions/workspaces/uiWorkspace 服务,过滤已归档会话和子代理行、保留普通分叉会话;缺少服务时回退基础官方侧栏。启用布局前检查公开 frame/slot DOM 锚点;未知结构保留官方页面——结构变化可能降低移动可用性,但不改变 Host 执行。原生桥接版本变化需要判断 App 兼容性;兼容的 Web 更新不自动要求重发 IPA。参见[导航/扫码验收](../../docs/acceptance/mobile-navigation-2026-09-11.md)、[rc1 验收证据](../../docs/acceptance/mobile-rc1-2026-09-11.md)与[提案](../../proposals/active/2026-08-19-mobile-access.md)。无需修改官方或兄弟插件源码。
107
89
 
108
- The library preserves workspace identity and shows member counts for both room and ordinary sessions. Visible rows fetch optional Room metadata; failures remain unknown rather than displaying a false count. In this Room revision, an asynchronous initial cache fill can leave the composer election stale; a declining mobile chain entry refreshes the public slot election without replacing its winner. Pending interactions still take precedence.
90
+ ## 已知限制
109
91
 
110
- Successful local message admission dismisses the same empty focused editor; rejected submissions and newer drafts retain focus. The context meter stays beside Send. Glass-like surfaces use host light/dark colors with opaque text and a reduced-transparency fallback. See [collaboration acceptance](../../docs/acceptance/mobile-collaboration-2026-09-12.md).
92
+ - **开发基线**——相机/文件选择、分享/下载、后台恢复和社区插件组合尚未完成验收。iOS 扫码接收已有官方 HTTPS 登录链接并要求确认主机;相机实测验收仍待完成。
93
+ - **没有逐设备凭据**——一次性设备配对、逐设备撤销、Keychain 配对和 APNs 推送均未实现;隐藏二维码不撤销任何东西(见上)。
94
+ - **热卸载未通过验收**——依赖变化需要受控重启 Host 并刷新客户端。
95
+ - **偏好仅存在当前浏览器**——布局选择(`dsh.mobile.display`)与首页分组(`dsh.mobile.grouping`)绝不离开此浏览器,不写入宿主。
96
+ - **首页搜索是浅搜索**——只过滤会话标题和工作区路径,不搜消息正文。
97
+ - **回前台恢复有下限**——健康的短暂切换不重复重连,但已经停止的正文订阅不能仅靠重连 socket 恢复。
111
98
 
99
+ ## 实现原理
112
100
 
113
- ## Safari live-stream compatibility adapter
101
+ <details>
102
+ <summary>架构、安全边界与 Safari 适配层(点击展开)</summary>
114
103
 
115
- Host `0.1.5-rc.1` includes a JSON validator that rejects ordinary objects in Safari when restoring an in-progress reply. For this known defect, the bundled HTTPS ingress accepts `MOBILE_SAFARI_COMPAT=1`. Restart the ingress with that environment variable and have the client reload after the adapter is active. Existing dead reply subscriptions do not recover just by reconnecting the socket.
104
+ **Host 面。** 三条路由挂在官方受认证的 Connection 注册表上,且仅在组合中存在 `connection` 时才挂载(`ctx.inject(['connection'])`):`GET/POST /api/mobile/connect`(扫码登录签发——POST 额外要求同源 JSON 动作与 `ready` 状态的目标地址,导航或表单永远无法引出登录链接,provider 错误绝不回显)、`GET /api/mobile/directories`(只读目录名/路径,不超出操作者现有文件系统权限——不读文件内容、不创建目录;上限 1,000 行目录 / 10,000 个扫描条目并带截断标记)、`GET /api/mobile/handshake`(只读能力发现:桥接版本、能力清单、`devicePairing: false`、`pushNotifications: false`)。所有响应均为 `no-store`。
116
105
 
117
- This is opt-in delivery adaptation, not a Host source patch: only WebKit GET requests for `/plugins/` JavaScript are examined. A SHA-256 fingerprint of the complete known validator gates replacement; unknown code and already-fixed code pass unchanged. The adapter compares against the current engine's native constructor formatting without changing `Function.prototype.toString`. Authentication, RPC bodies, WebSocket frames and the Host's execution are preserved. A direct LAN connection or another ingress does not receive this adapter.
106
+ **Client 面。** 一个浏览器持有的 `MobilePresentation` 切换文档元素上的 `data-dsh-mobile`,持有样式表、observer 与监听器,且仅在官方 frame 锚点(`[data-slot="root"]` 且含 main/sidebar)符合预期结构时启用——否则保留官方页面。槽位贡献全部经 `slots.inject`(绝不裸 register):`settings.section`/`mobile-connect`、`shell.overlay`/`mobile-directory` 与 `/mobile-navigation`、`conversation.input.dock` + `conversation.session.header.actions`/`mobile-send-focus`(本机消息确认提交后收起同一个空白编辑器;提交失败和后续草稿保留焦点)、`conversation.session.header.actions`/`mobile-room-queue`、`conversation.hero.brand.mark` 的欢迎内容、`conversation.input.left`/`mobile-input-tools`(加号菜单归并官方附件/指令/权限入口),以及移动模式激活期间对两个公开 `directoryFlow` 槽位的 priority −100 遮蔽。Room 输入框选举刷新使用一个始终让出的 `conversation.composer` 条目,绝不替换胜者。词典随包提供 `en`/`zh`。
118
107
 
119
- Bounded JavaScript is gzip-compressed for clients that accept gzip after inspection, including unchanged bundles, to avoid sending multi-megabyte identity assets through the mobile tunnel. Responses remain private/no-store; adapted assets have corrected byte lengths and no stale ETag/digest. Assets above 16 MiB, non-JS responses, authentication failures and upstream responses that ignore the requested identity encoding pass through unchanged. Inspect `x-dsh-mobile-compat` and the ingress's value-only compatibility log when qualifying a Host upgrade. Do not broaden the fingerprint automatically. Remove the flag after an upstream-fixed build passes real WebKit foreground/reconnect acceptance. Run the ingress from an installed tarball's `examples/`, not a development checkout, for a reproducible deployment.
108
+ **原生桥接(version 1)。** 壳声明 `window.__DSH_MOBILE_SHELL__`;客户端上报 `ready`/`unloaded` 及布局与锚点诊断,仅接受同 origin 主框架的 `chrome`、`settings`、`scan` 消息。新增原生动作需壳声明能力,旧壳保留回退控件。桥接不授予文件或命令权限。
120
109
 
121
- The read-only `GET /api/mobile/directories` companion uses the official Connection authentication and Host/Origin checks. It lists directory names and paths, including enterable symlinks, within the authenticated operator’s existing filesystem access; it never reads file contents or creates directories. Listings are bounded to 1,000 directory rows / 10,000 scanned entries and identify truncation. This supports native-picker hosts without changing their shared backend. A missing route or disconnected host shows an inline error with retry; the served HTTP carrier is required.
110
+ **Safari 流式同步兼容层。** 宿主 `0.1.5-rc.1` 的 JSON 校验会在 Safari 还原进行中回复时误拒绝普通对象。随包 ingress 接受 `MOBILE_SAFARI_COMPAT=1`,作为选择启用的交付层适配——不修改宿主源码:只检查 WebKit 对 `/plugins/` JavaScript 的 GET 请求,由完整已知校验函数的 SHA-256 指纹决定是否替换,未知代码和已修复代码原样透传。认证、RPC 请求体、WebSocket 帧和宿主执行保持不变;直连局域网或其他 ingress 不会获得此适配。有界 JavaScript 向支持 gzip 的客户端压缩输出(超过 16 MiB、非 JS、认证失败、identity 编码不匹配的响应原样透传);适配后的资源带校正后的字节长度、无旧 ETag/digest,保持 `private/no-store`。升级宿主时检查 `x-dsh-mobile-compat` 响应头与 ingress 不含请求内容的兼容日志,不自动扩大指纹范围;上游修复通过真实 WebKit 前后台/重连验收后移除该开关。正式部署从已安装 tarball 的 `examples/` 启动 ingress,不用开发检出。
122
111
 
123
- ## Compatibility
112
+ **卸载。** dispose 只移除自有资源——样式、`data-dsh-mobile` 布局标记、observer、监听、路由和槽位贡献——并恢复被适配的 `pickDirectory` 方法。会话、宿主任务、共享连接、官方 Cookie 和 App/VPN 全部原样保留。
124
113
 
125
- | Host line | Verdict |
126
- |---|---|
127
- | Official `0.1.5-rc.1`, `183f08e9c6` | Local browser/Host validation; iOS simulator build passes. Full device/network qualification pending. |
128
- | Other npm RCs / Harness master | Not verified. Audit before adopting. |
114
+ </details>
129
115
 
130
- `dsh.compat.minHost` is `0.1.5-rc.1`; no `verifiedHost` is claimed while the release matrix is incomplete. The library uses the optional official sessions/workspaces/uiWorkspace services; it filters archived sessions and subagent rows while retaining ordinary forks. Missing services fall back to the basic official sidebar. Search here filters titles and workspace paths, not message contents. The public frame/slot DOM anchors are checked before enabling layout; an unknown frame retains the official page. A changed structure can reduce mobile usability without breaking Host execution. Native bridge version changes require an App compatibility decision; compatible Web updates do not automatically require a new IPA.
116
+ ## 开发
131
117
 
132
- See [navigation/QR acceptance](../../docs/acceptance/mobile-navigation-2026-09-11.md), [acceptance evidence](../../docs/acceptance/mobile-rc1-2026-09-11.md) and the [proposal](../../proposals/active/2026-08-19-mobile-access.md). No upstream or sibling source changes are required.
118
+ 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/mobile`)。问题与贡献请移步该仓库。