- 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
39 lines
1.8 KiB
JavaScript
39 lines
1.8 KiB
JavaScript
#!/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) }
|
|
}
|