dsh-remote 0.1.0: master/slave remote control for DSH

- authenticated WebSocket link (dependency-free RFC6455 + stream mux), pairing
  codes, per-device keys (hashed), revoke/remove
- /r/<device>/ proxy of the full DSH UI (HTTP, Remote WebSocket, SSE) with
  per-device cookie minting; slave->slave relay through the master
- Devices sidebar panel (all devices' sessions, new session on device),
  Remote devices settings page, finished-session toasts
- plugin + config sync master->slave: registry bundles via pluginManager,
  link: plugins as tarballs, cordis.patch.yml into a managed bundle layer
  (!!js preserved), allow/deny, opt-in per-secret credential sync
- deploy helpers + e2e probes, unit tests
This commit is contained in:
lyuksovannyy committed 2026-10-11 08:23:19 +00:00
commit 8872e73e76
21 files changed
+2914

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env node
// Tiny admin CLI for dsh-remote that talks to the LOCAL DSH over loopback, authenticating
// with DSH's own browser-session secret from $DSH_HOME/.credentials.yaml (owner-only file).
//
// node dshr-cli.mjs GET state
// node dshr-cli.mjs POST pairing '{}'
// node dshr-cli.mjs POST pair '{"masterUrl":"http://192.168.0.111:3080","code":"ABCD-..."}'
// node dshr-cli.mjs GET /r/<device>/ (raw path, prints status + first bytes)
//
// Env: DSH_PORT (default 3081), DSH_HOME (default ~/.dsh)
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { mintCookie } from '../lib/auth-cookie.mjs'
const [method = 'GET', target = 'state', body] = process.argv.slice(2)
const port = process.env.DSH_PORT || '3081'
const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
const creds = fs.readFileSync(path.join(home, '.credentials.yaml'), 'utf8')
const m = creds.match(/client-connection\/browser-session:[\s\S]*?secret:\s*([A-Za-z0-9_-]{43})/)
if (!m) { console.error('browser-session secret not found'); process.exit(2) }
const authority = `127.0.0.1:${port}`
const cookie = mintCookie(authority, m[1], 1)
const url = target.startsWith('/') ? `http://${authority}${target}` : `http://${authority}/dsh-remote/${target}`
const res = await fetch(url, {
method,
headers: { cookie, ...(body ? { 'content-type': 'application/json' } : {}) },
body: body || undefined,
redirect: 'manual',
})
const text = await res.text()
if (target.startsWith('/')) {
console.log(res.status, res.headers.get('content-type') || '', res.headers.get('location') || '')
console.log(text.slice(0, Number(process.env.BYTES || 400)))
} else {
console.log(res.status)
try { console.log(JSON.stringify(JSON.parse(text), null, 2)) } catch { console.log(text) }
}