Skip to main content

deepseek-harness-desktop/packages/dsh-remote-web-ui

156Stars5Forks6Issues0Watchers

Adds QR code pairing for phone remote control and self-update capability to dsh web. After scanning, users can operate the workspace through an independent interface on the phone's small screen.

Evidence5/5methodologySourceInstallMaintenanceDSH versionSecurity scan
Machine-auditedInstall commandRepo verifieddsh-plugin topicLicenseREADMEAI wiki

ⓘ This plugin is a sub-package of the ningbainb/deepseek-harness-desktop monorepo — stars and activity count the whole repository.

Language
TypeScript
License
BSD-3-Clause
Branch
main
ai-agentai-coding-assistantcodexdeepseekdeepseek-harnessdesktop-appdshdsh-plugin

Install

cmdweb profile
$ dsh plugin --profile web add @linxin666/dsh-remote-web-ui

Run the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial

Install via your agent

Install the DeepSeek Harness plugin ningbainb/deepseek-harness-desktop/packages/dsh-remote-web-ui for me: review the repository at https://github.com/ningbainb/deepseek-harness-desktop first, then run the install command and verify the plugin loads successfully.

Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.

One-Sentence Pitch

Adds QR code-based mobile remote control to DeepSeek Harness (DSH) Web GUI — scan the QR code generated by the phone icon in the desktop sidebar, and the phone enters an independent, lightweight client interface designed for small screens, remotely using the current workspace; the download button in the sidebar can one-click self-update the entire dsh-web-ui suite.

Core Features

  • QR Pairing: The phone icon at the bottom of the sidebar opens a panel to generate a one-time, time-limited QR code link; scanning the QR code on the phone binds it and lands at the /m independent mobile interface.
  • Independent Mobile Interface: The phone loads a streamlined interface specifically designed for small screens (workspace, sessions, chat, model selection), not a desktop UI squeezed onto a phone.
  • Real-time Synchronization: The desktop panel and phone synchronize pairing status (waiting/connected/disconnected) via SSE in real-time; when messages arrive, the phone receives instant push notifications.
  • LAN Fence: Enabled by default, all non-local /api requests must carry a pairing cookie; clicking "Refresh QR Code" immediately invalidates the old link, clicking "Stop" clears all device tokens and sessions.
  • One-click Public Network Tunnel: After enabling, the plugin automatically launches Cloudflare quick tunnel (no account/domain required), and the QR code automatically becomes a public network link; a manually configured publicBaseUrl can also be used.
  • One-click Self-update: The sidebar's download button asynchronously checks npm for the latest versions of the @linxin666/dsh-* suite; when a new version is found, it automatically runs pnpm update and prompts to restart dsh web to take effect.

Technical Implementation

  • Language: TypeScript
  • Key Dependencies: @deepseek-ai/cordis, @deepseek-ai/dsh-host-webserver, qrcode.react (pure SVG QR code rendering without canvas), cloudflared (Cloudflare tunnel binary distributed with the package), schemastery (DSH standard config schema validation)
  • Architecture Pattern: Dual-half cordis plugin — host half (src/index.ts) holds pairing tokens, device sessions, /api/pair route family, /api/update endpoint, and api/gate listener; browser half (src/client/) renders sidebar entry, pairing panel, settings card, self-update panel. Each half is individually mounted via cordis apply(), and the gate listener intercepts unpaired requests before they enter ApiProxy via /api.
  • Entry Files: src/index.ts (host half) + src/client/index.ts (browser half)

Use Cases

For people who frequently leave their workstation but want to continue using a specific DSH workspace on their phone anytime — like on the couch, in bed, or while out, wanting to continue writing prompts, viewing logs, or following streaming output, without forcing the desktop UI into a phone browser. After installation, scan to use immediately; the phone uses a streamlined small-screen interface, expired QR codes can be refreshed with one click, compromised devices can be revoked instantly; the bundled one-click self-update spares multi-plugin users from manual pnpm update hassle.

Prerequisites & Compatibility

DependencyMin VersionDescription
DSH>= 0.1.0-rc.7Relies on api/gate watermark, sidebar.remote seat, host-apiproxy LAN fix; older versions missing these seams will lose partial functionality
Node>= 22.19.0 or >= 24.0.0package.json#engines declaration
PlatformmacOS / Windows / LinuxCross-platform; on Windows, updates go through cmd.exe to resolve npm's .cmd shim
Native ModulecloudflaredOne-click public network tunnel automatically downloads platform binaries with the package, no user installation required; not dependent if autoTunnel is disabled

Installation

dsh plugin --profile web add github:ningbainb/deepseek-harness-desktop/packages/dsh-remote-web-ui

Configuration Options

ConfigurationTypeDescriptionDefault Value
Enable Mobile Remote ControlToggleWhen disabled, removes sidebar entry and deactivates pairing routes and LAN fenceOn
Pairing Token Validity (ms)NumberQR code link expires after this duration, minimum 60000600000 (10 minutes)
Device Offline Detection (ms)NumberPaired device considered offline after this duration without heartbeat25000
Max Paired DevicesNumberWhen exceeded, oldest device session is evicted, range 1-644
Device Cookie NameTextCookie name carrying paired device identifierdsh_pair
LAN Access Requires PairingToggleOn: all non-local /api requests must carry valid pairing cookie; Off: LAN fence open, only token/status/revoke retainedOn
Public Address (Optional)TextPublic address for intranet penetration, e.g., https://xxx.trycloudflare.com; after filling, QR code becomes public linkEmpty
Auto Public TunnelToggleAfter enabling, plugin automatically starts Cloudflare quick tunnel (no tools installation required), and automatically updates public address and trust configurationOff
Mobile Enter to SendToggleOn: phone input Enter directly sends, Shift+Enter for newline; Off: Enter for newline, only click send button to sendOn

FAQ

Q: After installation, no phone icon appears on startup?

A: Three common causes: ① Started with 127.0.0.1 binding but no publicBaseUrl configured / autoTunnel enabled, panel shows "This feature requires...". Please start with dsh web --host 0.0.0.0, or configure public address in settings / open auto tunnel; ② DSH version too old (< 0.1.0-rc.7) missing sidebar.remote seat — upgrade host; ③ DSH settings allowlist doesn't open this plugin namespace to config page — go to ~/.dsh/settings.yaml and directly configure remote-web-ui namespace.

Q: After disabling "LAN Access Requires Pairing", will it still get disconnected?

A: No. This toggle only determines whether non-local /api requests must carry pairing cookie. After disabling, token/status/revoke/heartbeat still work; just LAN fence is bypassed, anyone on LAN can directly access /api, phone pairing is like an extra double insurance.

Q: After Quick Tunnel starts, why does phone side new messages have a few seconds delay?

A: This is expected behavior. cloudflared quick tunnel and Tailscale Serve don't forward Server-Sent Events (SSE) by default, and SSE is the channel for real-time message receipt on phone. This plugin will automatically fall back to polling session.history at short intervals, messages still arrive but not instant; once SSE channel recovers, streaming resumes immediately. For truly real-time push, use Cloudflare Named Tunnel (domain hosted on Cloudflare) or TCP port forwarding (LAN address, Tailscale virtual interface address, ssh -L, cloudflared TCP tunnel all work).

Q: What can one-click self-update update, what can't it?

A: Panel only updates @linxin666/dsh-web-ui suite normally installed via npm (executes pnpm update, falls back to corepack pnpm / npx --yes pnpm on failure, resolves .cmd shim via cmd.exe on Windows). If plugin is installed via link: (dev mode), panel detects this and only shows version number display, prompts you to git pull in repo, won't execute update (because link mode has no corresponding version on npm).

Q: How to revoke a paired phone?

A: Click "Stop" in desktop panel. Current active pairing token and all bound device sessions are immediately cleared, next click "Refresh QR Code" can restart scanning. Connected phone will be blocked by LAN fence on next request (403), real-time stream also cut off.

Q: Can mobile chat send images or attachments?

A: Rich interactions like images/attachments/tool call confirmation not covered in this plugin scope; mobile provides text input, model selection, permission switch; mobile code only does chat history/streaming send/receive, model/permission switch RPC rich capability; attachment/tool call confirmation goes through host native session, phone side only handles text chat and setting switches.

Q: With multiple network cards / Tailscale virtual interface, which network segment does QR code point to?

A: Panel lists all non-internal IPv4 literals on machine for selection (including virtual adapters), Tailscale's 100.x virtual interface also automatically appears. When phone and computer on same network, choose LAN segment most stable; when not, choose public address.

Q: Must use Cloudflare tunnel to access from external network?

A: No. Any tunnel that forwards public address back to local 127.0.0.1:port works — just fill in publicBaseUrl in plugin settings (e.g., https://xxx.trycloudflare.com), and add --trusted-host <corresponding public host> when starting dsh web to let host ApiProxy trust that host; cloud network doesn't automatically trust that host, public /api requests will be 403 rejected before reaching pairing layer.

Learning Curve

Beginner — install plugin, scan QR, immediately usable; adjusting security policies or going public network only requires toggling corresponding switches in settings card, no code writing needed.

Known Issues & Limitations

  • Revoke takes effect per-request: requests in flight when clicking "Stop" will complete, next request gets 403; this is a concurrency side effect by design, not a bug.
  • Device sessions are in-memory: pairing state (token + devices) resets with dsh web process, after restart all phones need to re-scan.
  • No per-device management UI: panel only shows aggregated status (waiting/connected N/offline), single device revoke deferred, need to "Stop" all then next auto-refresh.
  • Quick-tunnel hostname changes each time: trycloudflare.com URL gets random new hostname each time cloudflared starts, host --trusted-host and plugin publicBaseUrl need to update together; using Named Tunnel (fixed hostname) avoids this jitter.
  • LAN fence enabled by default after installing this plugin: desktop browser opened via LAN URL must pair like phone, otherwise all /api get 403; to keep original open LAN behavior, just turn off "LAN Access Requires Pairing".
  • Auto public tunnel reachability not guaranteed in mainland China: Cloudflare decides locally, self-test required.
  • When plugin is installed in link: form (dev mode), one-click self-update button only shows current version number and "Please git pull in repo" prompt, won't actually initiate update.
  • Dev HMR: dsh web --dev polls each roster bundle by path, so rebuilding this package (its own tsdown --watch) will hot-reload client bundle; this is unrelated to normal use, only needed to know during development.

Read the usage guide →

Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.

Listing badge

Listed on deepseek-plugin.org
[![Listed on deepseek-plugin.org](https://img.shields.io/badge/listed_on-deepseek--plugin.org-007EC6)](https://deepseek-plugin.org/plugins/ningbainb/deepseek-harness-desktop/packages/dsh-remote-web-ui)

Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.

← Back to plugin directory