dsh-plugin-mobile-link
Host-side connector for the DSH Mobile iOS app: dials a self-hostable WSS relay so the phone can reach the DSH sessions on this computer over 4G/5G, without a public IP or a VPN; the relay only authenticates and forwards.
Install
dsh plugin --profile web add dsh-plugin-mobile-link
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-plugin-mobile-link




面向:使用者与实现者 · 状态:stable · 最近核对:2026-09-20
**The computer half of DSH Mobile: it
dials a relay over WSS, so the native iOS client reaches this DSH instance even
though the computer has no public IP.**
iPhone App ──WSS──▶ Relay(公网,只鉴权与转发)──WSS──▶ this plugin ──HTTP+WS──▶ 127.0.0.1:<port>
The connector always talks to the local DSH as 127.0.0.1:<port>, so the host's
authority and its trust fence are unaffected: no --trusted-host, no rewritten
Host, no new listening port on this machine.
plugins/mobile-link/ ├── package.json # type: module, dsh.bundle.patch, bin: dsh-mobile-link ├── cordis.patch.yml # bundle patch: the loader row + config reference ├── lib/ │ ├── index.js # cordis plugin: starts the agent, registers 5 routes │ ├── link.js # the DLP agent transport: identity, socket, heartbeat, reconnect │ ├── router.js # frames ⇄ local DSH mux streams, per-device `$events` │ ├── dlp.js # DLP v1 frame codec (shared with the relay) │ ├── files.js # phone → computer file transfers (`_link/file*` methods) │ ├── enroll.js # open enrollment (no invite code), writes ~/.dsh/mobile-link/agent.json │ ├── journal.js # lifecycle journal: why a relay link was dropped, after the fact │ ├── routes.js # status, enroll, pair-code, pair-code/regenerate, devices/revoke │ ├── qr.js # dependency-free QR SVG │ └── cli.js # dsh-mobile-link: enroll / mint-pair-code └── test/ # node --test, 100+ tests (no DSH, no network)
Install
The connector runs in whichever host runs your DSH. There are two supported hosts today —
the official desktop app and the command-line web host — and each keeps its own
profile, so you install the plugin into the one you actually use. The connector discovers
the local DSH by itself in both (the host hands it the authenticated loopback address);
you never type an address or a token.
Command-line web host
dsh plugin --profile web add dsh-plugin-mobile-link
Official desktop app — install through the app's own plugin page, or with the CLI that
the app ships (…/Resources/runtime/cli/bin/dsh, installable from its settings). The npm
dsh refuses this profile on purpose (`profile "desktop" is managed exclusively by the
Electron application`). Two prerequisites matter in practice: let the app start once so it
initializes its profile, and quit it completely before installing — otherwise the
profile is locked.
# quoting the app's bundled CLI; see docs/ONBOARDING.md for the section titles "<DeepSeek Harness.app>/Contents/Resources/runtime/cli/bin/dsh" \ plugin --profile desktop add dsh-plugin-mobile-link
Then restart the host (or reload the profile). From a checkout the same command takes
a path — the leading ./ matters, because DSH anchors a relative spec against
your current directory instead of resolving it inside the profile:
dsh plugin --profile web add ./plugins/mobile-link
Use
1. Register this computer with a relay (once). Open the 移动端连接 panel at the
bottom of the DSH sidebar: the relay address is prefilled (editable) from the
installation default; click 登记. The connector derives the computer name
automatically, writes ~/.dsh/mobile-link/agent.json and reconnects in place —
no terminal and no DSH restart. Open enrollment is the default; the relay
rate-limits by source. If an installation has no panel (headless, CI), the CLI
still works:
dsh-mobile-link enroll --relay wss://<你的站点>/dsh-link
…or register it on the relay host with `dsh-relay admin agent-register
--write-config ~/.dsh/mobile-link/agent.json`.
2. The same panel shows the pairing code and its QR once the relay link is up.
The CLI can still mint one directly:
dsh-mobile-link --mint-pair-code # prints JSON: code + relay + agentId
The code is persistent: it stays valid until you click 重新生成. Each paired device
has a quiet 撤销 action; revoking one device leaves the code and other devices alone.
3. Scan it in the iOS app. The code can be claimed again for another phone, a reinstall
or a new device; only 重新生成 revokes it (already-paired devices stay connected).
After that the phone reconnects on its own — nothing else has to be run by hand.
GET /mobile-link/status reports whether the link is up, which devices are
attached, and whether this computer is registered yet. Its enroll block also
carries panel (this connector can enroll from the DSH panel), relayPrefill
and relayLocked (the address to prefill and whether it came from config).
POST /mobile-link/enroll performs that enrollment (open enrollment; no invite code); it returns the agent id and
name, never agentSecret, and is behind the same DSH request fence as pairing. GET /mobile-link/devices lists the full paired roster (including offline) for the panel, and
POST /mobile-link/devices/revoke revokes one paired device by deviceId through the relay; both are behind
the fence and authenticated with the agent secret. Its journal block carries
the current process's newest lifecycle events (link opened/closed with the reason,
device attached/detached); the counters in it reset when the plugin hot-reloads, so
the full history — across restarts —
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::10,最后更新 2026-11-01