#!/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): // /package.json dependencies["@local/dsh-remote"] = "link:" // dsh.profile.bundles += "@local/dsh-remote" // /node_modules/@local/dsh-remote -> // /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.`)