- deploy/install-mac.sh: per-user Node 22 (checksum-verified), DSH + pnpm in ~/.local, dsh-remote from the repo archive, pairing, LaunchAgent dsh web - deploy/pair.mjs: pair without a running DSH (detects login-proxy redirects) - install-local.mjs: --accept secrets list, --restart-on-sync - sync.restartOnSync: slave restarts itself after synced plugins change (waits for idle sessions, at most once per 10 min)
64 lines
2.9 KiB
JavaScript
64 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// Register a dsh-remote checkout as a `link:` bundle in the current user's DSH web profile
|
|
// (no pnpm/network needed) and optionally set its config.
|
|
//
|
|
// node deploy/install-local.mjs [--role slave|master|auto] [--name "My PC"] [--profile web]
|
|
//
|
|
// Effects (idempotent):
|
|
// <profile>/package.json dependencies["@local/dsh-remote"] = "link:<this dir>"
|
|
// dsh.profile.bundles += "@local/dsh-remote"
|
|
// <profile>/node_modules/@local/dsh-remote -> <this dir>
|
|
// <profile>/cordis.patch.yml appends a `- id: dsh-remote` config block if missing
|
|
// Restart `dsh web` afterwards (host code is loaded at boot).
|
|
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const args = process.argv.slice(2)
|
|
const opt = (k, d) => { const i = args.indexOf(`--${k}`); return i >= 0 ? args[i + 1] : d }
|
|
const pluginDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
|
|
const prof = path.join(home, 'profiles', opt('profile', 'web'))
|
|
const NAME = '@local/dsh-remote'
|
|
|
|
const pkgFile = path.join(prof, 'package.json')
|
|
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf8'))
|
|
pkg.dependencies ||= {}
|
|
pkg.dependencies[NAME] = `link:${pluginDir}`
|
|
const bundles = (((pkg.dsh ||= {}).profile ||= {}).bundles ||= [])
|
|
if (!bundles.includes(NAME)) bundles.push(NAME)
|
|
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2) + '\n')
|
|
|
|
const linkDir = path.join(prof, 'node_modules', '@local')
|
|
fs.mkdirSync(linkDir, { recursive: true })
|
|
const link = path.join(linkDir, 'dsh-remote')
|
|
try { fs.rmSync(link, { recursive: true, force: true }) } catch { /* ignore */ }
|
|
fs.symlinkSync(pluginDir, link)
|
|
|
|
const patchFile = path.join(prof, 'cordis.patch.yml')
|
|
let patch = ''
|
|
try { patch = fs.readFileSync(patchFile, 'utf8') } catch { patch = '' }
|
|
if (!/^- id: dsh-remote\b/m.test(patch)) {
|
|
const role = opt('role', 'auto')
|
|
const name = opt('name', '')
|
|
const accept = String(opt('accept', '')).split(',').map((s) => s.trim()).filter((s) => /^[A-Z][A-Z0-9_]*$/.test(s))
|
|
const block = [
|
|
'# dsh-remote (installed by deploy/install-local.mjs)',
|
|
'- id: dsh-remote',
|
|
' config:',
|
|
` role: ${role}`,
|
|
` deviceName: ${JSON.stringify(name)}`,
|
|
' sync:',
|
|
' auto: true',
|
|
` restartOnSync: ${args.includes('--restart-on-sync') ? 'true' : 'false'}`,
|
|
' allow: []',
|
|
' deny: []',
|
|
accept.length ? ` secrets:\n${accept.map((s) => ` - ${s}`).join('\n')}` : ' secrets: []',
|
|
'',
|
|
].join('\n')
|
|
// An empty/comment-only patch file fails DSH boot, so only append real entries.
|
|
fs.writeFileSync(patchFile, (patch.trim() && !/^\s*\[\]\s*$/m.test(patch.trim()) ? patch.replace(/\n*$/, '\n') : '') + block)
|
|
}
|
|
console.log(`dsh-remote linked into ${prof}; restart dsh web to load it.`)
|