dsh-plugin-mobile-link
已验证dsh-plugin-mobile-link · v0.4.1 · MIT
Put your PC's DeepSeek Harness in your pocket: dials a relay over WSS so the native iOS client reaches this machine even without a public IP.
安装
dsh plugin add dsh-plugin-mobile-link 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
发布到 npm 但没有公开仓库。安装前请检查包内容。
标签
说明文档
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 3 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 # invite-code redemption, writes ~/.dsh/mobile-link/agent.json
│ ├── routes.js # GET /mobile-link/status, POST /pair-code, GET /qr
│ ├── 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
Register this computer with a relay (once). Either redeem an invite code:
dsh-mobile-link enroll --invite <邀请码> --relay wss://<你的站点>/dsh-link…or register it on the relay host with
relay/admin.py agent-register --write-config ~/.dsh/mobile-link/agent.json.Mint a pairing code, and show it as a QR the phone camera can read:
dsh-mobile-link --mint-pair-code # prints JSON: code + relay + agentIdOr open
/mobile-link/qron the local DSH (behind DSH's own authentication).Scan it in the iOS app. 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.
Configuration
All optional; the defaults are what a stock deployment needs. Set them in the
plugin's config block (cordis.patch.yml), not by editing this file.
| Key | Default | Meaning |
|---|---|---|
enabled |
true |
false stops the outbound link without uninstalling |
relayUrl |
from agent.json |
Relay base URL; a bare deployment dials <relay>/link/agent |
agentId / agentSecret |
from agent.json |
Override the enrolled identity |
stateFile |
~/.dsh/mobile-link/agent.json |
Where the identity lives |
dshUrl |
discovery | Explicit local DSH base URL (usually unnecessary — the host hands the connector its own address) |
heartbeatMs |
20000 |
DLP ping interval |
The connector does not read an endpoint handoff file any more: discovery happens inside
the host process (ctx.inject(['webServer','connection'])), which is what makes one npm
package work on both the desktop app and the command-line host. It writes one instead —
$DSH_HOME/mobile-link/endpoint.json, mode 0600 — for tools that run outside the host
(our dev/test scripts and the iOS integration helpers).
Address resolution order: this config block, then DSH_RELAY_URL (or
DSH_MOBILE_LINK_RELAY), then the enrolled identity file, then the repository
placeholder wss://relay.example.com/dsh-link — the placeholder being the
"nothing configured yet" sentinel rather than a real address.
Design notes
- No required services.
injectis empty and the routes are attached throughctx.inject([...]), so a future DSH that renames or dropswebServer/connectioncannot make the tunnel fatal on boot. - Protocol-transparent. Nothing outside
lib/is imported and no DSH business API is touched: the connector forwards frames, it does not interpret sessions. The relay has the same property. - Disable by removing the row from the profile's
cordis.patch.yml, or by settingenabled: false.
Test
cd plugins/mobile-link && npm test # zero dependencies
The wire protocol is specified in
docs/RELAY-PROTOCOL.md.
MIT.