packages/dsh-remote-web-ui
zhu1090093659/dsh-web手机/PC 远程操控 dsh web 工作区:扫码配对、令牌门控通道、SSE 实时同步,提供移动端与完整桌面 GUI 两种远程形态。
局域网手机访问网关:独立子进程反向代理 + 首次访问审批 + 设备令牌绑定 + 限流 + 手机端紧凑排版注入(输入区权限/模型小胶囊、randomUUID polyfill)。
Let phones and tablets on your LAN safely access your local DeepSeek Harness (DSH) Web UI, with mobile-friendly layout injected automatically.
中文文档: README.md · LLM index: llms.txt · Agent guide: AGENTS.md
Keywords: dsh-plugin · deepseek-harness-plugin · LAN · 局域网 · mobile · phone · reverse-proxy · remote-access · approval · gateway
| Feature | Description |
|---|---|
| 📱 Mobile adaptation | Injects data-lan-device marker + compact layout CSS + crypto.randomUUID polyfill (required for non-secure HTTP contexts) into proxied HTML. Permission & model selectors in the composer footer become compact pill buttons — no more overlap |
| 🔒 First-visit approval | A phone's first visit shows "Waiting for approval"; you must allow it from the PC. Unauthorized devices can never reach DSH |
| 🎟️ Per-device token + cookie binding | One-time token issued after approval, bound to exactly one browser |
| 🛡️ Per-IP rate limiting | Default 120 req/min per IP, 429 beyond that |
| 🏠 Loopback bypass | Loopback and the machine's own LAN IPs pass through directly — desktop experience unchanged |
| 🚀 Zero intrusion into the host | Isolated child-process gateway. DSH's own webserver stays on 127.0.0.1 and the /api trust fence is untouched; a gateway crash cannot take DSH down |
| 🧹 Plug & play / removable | Install via dsh plugin add, mount via cordis.patch.yml, or run as a dynamic plugin; unmounting/stopping terminates the gateway |
Phone http://192.168.31.108:3088
└─ Gateway (standalone Node process, 0.0.0.0:3088)
├─ Not approved → "Waiting for approval" page (shows device IP, auto-polls)
├─ Approved + cookie token → reverse-proxy to DSH Web UI (127.0.0.1:3080)
│ └─ HTML injection: data-lan-device="phone" + compact CSS + randomUUID polyfill
└─ Rate-limited → 429 page
PC http://127.0.0.1:3088/lan-gate/admin → admin page (approve / deny / revoke / set kind)
127.0.0.1 (the official CLI intentionally rejects --host 0.0.0.0 because /api has no auth layer). Only approved, token-holding devices reach DSH.dsh plugin add (standard install, official plugin ecosystem)# Install from a local checkout (run in the directory containing this repo)
dsh plugin --profile web add ./dsh-mobile-gate
This repo declares a
dsh.bundlemanifest, so the config layer activates automatically after install — no manual patch needed.
git clone https://github.com/Bernardxu123/dsh-mobile-gate.git
~/.dsh/profiles/web/cordis.patch.yml (create it if missing; if it contains only [], replace it), following cordis.patch.yml.example:
- insert:
- id: dsh-mobile-gate
name: 'file:///D:/path/to/dsh-mobile-gate/lan-gate.mjs'
0.0.0.0:3088.Register the apply logic of lan-gate.mjs as a dynamic Host Cordis plugin in a DSH session; it spawns the gateway via the subprocess service. Stopping/uninstalling the plugin terminates the gateway.
http://192.168.31.108:3088Environment variables (edit defaults at the top of lib/lan-gate-server.cjs, or set before launch):
| Variable | Default | Description |
|---|---|---|
LAN_GATE_PORT | 3088 | Gateway listen port |
LAN_GATE_HOST | 0.0.0.0 | Listen address (tighten to 127.0.0.1 for local-only) |
LAN_GATE_TARGET_PORT | 3080 | DSH Web UI port |
LAN_GATE_RATE_LIMIT | 120 | Per-IP requests per minute |
~/.dsh/lan-gate-state.json (survive restarts; the pending list is in-memory and re-triggered by a fresh visit).| Method | Path | Description |
|---|---|---|
GET | /lan-gate/status | Status JSON: URLs, pending / approved / denied devices |
POST | /lan-gate/action | {action: approve|deny|revoke|revoke-all|set-kind, ip, kind} |
curl http://127.0.0.1:3088/lan-gate/status
curl -X POST http://127.0.0.1:3088/lan-gate/action \
-H 'Content-Type: application/json' \
-d '{"action":"approve","ip":"192.168.31.125","kind":"phone"}'
Injected into proxied HTML, scoped to html[data-lan-device="phone"] only — desktop is never affected:
min-height:32px, touch-action:manipulation)crypto.randomUUID polyfill (otherwise the SPA can white-screen over plain HTTP)Q: Phone shows "connection refused"? A: Make sure both devices are on the same LAN; on Windows, allow Node.js inbound when the firewall prompt appears, or add a rule:
New-NetFirewallRule -DisplayName 'DSH Mobile Gate 3088' -Direction Inbound -LocalPort 3088 -Protocol TCP -Action Allow -Profile Any
Q: Admin page stuck on "Loading…"? A: Hard-refresh the page. (An earlier build had a quoting bug in the admin inline JS; fixed in v1.0.0. Clear cache if it persists.)
Q: Blank page after approval?
A: Usually missing crypto.randomUUID on non-secure HTTP; the gateway injects a polyfill. If it still fails, report your browser model/version.
Q: How to remove completely? A: Static mount: delete the insert entry and restart. Dynamic plugin: stop/undefine the plugin — the gateway child exits with it.
dsh-mobile-gate/
├── lan-gate.mjs # Cordis plugin entry (spawns the gateway via subprocess)
├── lib/
│ └── lan-gate-server.cjs # Standalone gateway server (zero-dependency, single file ~30KB)
├── cordis.patch.yml # dsh.bundle config layer (for `dsh plugin add`)
├── cordis.patch.yml.example # Static mount example
├── AGENTS.md # AI agent repository guide
├── llms.txt / llms-full.txt # LLM doc index / full text
├── package.json # npm metadata + dsh.bundle manifest
├── README.md / README.en.md # Bilingual docs
└── LICENSE
Inspired by excellent community projects:
Key differences: the gateway runs as an isolated child process (no modification of DSH host config, no touching the trust fence), and mobile CSS targets stable aria-haspopup/slot selectors (more resilient to frontend upgrades).