23 KiB
dsh-remote
Remote control for DeepSeek Harness, in the style of Claude Desktop: one master and any number of slaves.
- Every session on every device. Sessions run on the machine that created them. From any device you can browse all of them, open them, and chat in them; the work still executes on the owning machine.
- Continue a conversation on another device. "Continue here" on any remote chat (Devices page or sidebar), or "Continue on another device…" in a chat's "…" menu, copies the conversation to that device as a real native chat (its own history, tools and workspace), and the original is marked as moved. See Conversation sync.
- Works while the other device is offline. Each device keeps synced copies of the other devices' recent chats, so you can continue one even when its device is off. That device is told the moment it is reachable again, and if it got new messages in the meantime both sides are warned that the copies diverged.
- The full remote UI at
/r/<device>/. The whole DSH UI of another device is proxied over the link: HTTP, the Remote WebSocket, SSE, plugin bundles and terminals. Slave→slave goes through the master's relay. - Plugin and config sync, master → slaves. Slaves get the master's npm/registry bundles, its local
link:plugins (packed and shipped over the link) and itscordis.patch.yml. The config lands in a managed bundle layer, so each slave's owncordis.patch.ymlstill wins and slave-only plugins stay untouched. - Opt-in secrets. A credential (
OMNI_API_KEY, …) reaches a slave only if three places agree: the master offers it, the master enables it for that device, and the slave accepts it. - Devices UI. A sidebar panel lists every device and its sessions (live running state, filter, "New session on "). A "Remote devices" settings page handles pairing, revocation and per-device sync policy. You get a toast when a session on any device finishes.
No dependencies: the WebSocket, the multiplexer and the proxy are plain Node.
slave (laptop) ──outbound WebSocket /dsh-remote/link──► master ◄── slave (ai-pc)
own sessions + own local plugins plugins, config, relay hub
Install
On every machine, register the plugin in the DSH web profile and restart DSH:
# master (this repo as a link: plugin, e.g. in dsh-plugins/)
# profile package.json → "@local/dsh-remote": "link:/path/to/dsh-remote" + bundle entry
# or use the helper (also creates the node_modules link and a config block):
node deploy/install-local.mjs --role master --name "Pelican-Node"
node deploy/install-local.mjs --role auto --name "ai-pc" # on a slave
systemctl --user restart dsh # or restart `dsh web` however you run it
deploy/dsh-web.user.service is a ready-made systemd user unit for a rootless dsh web (127.0.0.1:3081).
macOS: one-line slave install
Create a pairing code on the master (Settings → Remote devices → Create pairing code). Then, in Terminal on the Mac:
curl -fsSL https://git.rizzy.dev/DSH-Plugins/dsh-remote/raw/branch/main/deploy/install-mac.sh \
| bash -s -- --master http://192.168.0.111:3080 --code XXXX-XXXX-XXXX-XXXX --name my-mac
No sudo and no Homebrew. The script:
- installs Node 22 to
~/.local/opt/node(checksum-verified), and DSH and pnpm to~/.local; - puts dsh-remote in
~/.dsh/plugins/dsh-remoteand registers it as a slave that acceptsOMNI_API_KEYandFORGE_CREDENTIALS(--acceptchanges the list); - pairs with the code (
deploy/pair.mjs, no running DSH needed); - installs the LaunchAgent
dev.rizzy.dsh-web:dsh webon 127.0.0.1:3081, started at login and restarted if it dies. Logs go to~/.dsh/logs/.
On first link-up the Mac pulls the master's plugins and config. With sync.restartOnSync: true, set by the script, DSH restarts itself once so new plugins load. It waits until no session is running and restarts at most once per 10 minutes.
Re-running the script updates everything and keeps the pairing. To remove it: launchctl bootout gui/$(id -u)/dev.rizzy.dsh-web, then delete ~/Library/LaunchAgents/dev.rizzy.dsh-web.plist, ~/.dsh and ~/.local/opt/node.
Away from home, point --master at the public URL. If an auth proxy guards it, add the --edge … flags (see Masters behind an auth proxy). Secrets are prompted for on the terminal and never passed on the command line:
curl -fsSL https://git.rizzy.dev/DSH-Plugins/dsh-remote/raw/branch/main/deploy/install-mac.sh \
| bash -s -- --master https://dsh.rizzy.dev --code XXXX-XXXX-XXXX-XXXX --name my-mac \
--edge basic --edge-user dsh-remote-mac
Pair a device
First choose what this installation is: Settings → Remote devices → Node.
| Setting | |
|---|---|
| Type: Master / Slave | Master holds plugins, config and sessions and accepts links. Slave connects to a master and syncs from it. The choice is stored in the plugin state and beats role in the config file. A change applies after a restart (a Restart DSH button appears when DSH runs under systemd / launchd). |
| Origin | Master: the address slaves use to reach it (read-only, taken from publicUrl / lanUrl or the page's own origin). Slave: the master's origin; read-only once paired, to change it re-pair. |
| Master has an auth middleware | On: a non-DSH answer on the link opens the sign-in window (see Masters behind an auth proxy), then the link retries. Off: work as usual. |
- Master: Settings → Remote devices → Create pairing code (single use, valid 15 min).
- Slave: Settings → Remote devices → enter the master URL (e.g.
http://192.168.0.111:3080orhttps://dsh.rizzy.dev) and the code → Pair. Then restart the slave's DSH once: withrole: autoit switches from master to slave.
The slave gets its own device key; the master stores only its hash. Revoke cuts the device off immediately: open links close with code 4401 and later connections are refused with 401.
Use it
- Workspaces list: the workspaces of every other machine appear in the stock Workspaces list, below your own, with a server icon instead of the folder icon and the device name on the right. Click one to fold its sessions open (newest first, "Show N more" for the rest); hover for + to start a session in that workspace on that device. Clicking a remote session opens it as the usual conversation view, served by that device at
/r/<device>/#/session/<id>. A "Remote: · back to local" pill marks the remote view. While you are viewing a device, its own workspaces are the native rows and are not repeated. - Sidebar → Devices: every device with its state (online / offline, last seen, platform, counts of workspaces, sessions and running ones), then its workspaces and sessions. The same server icon marks other machines' workspaces. Open DSH ↗ opens a device's whole UI, + New session creates a session in one of its workspaces.
- Toasts: ": '