dsh-remote 0.3.0: one-line macOS slave install

- 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)
This commit is contained in:
lyuksovannyy committed 2026-10-11 12:00:08 +00:00
1 parent d1e134196d
commit 08202cad67
6 files changed
+243 -2

No files matched your search

+19
View File
@@ -555,11 +555,30 @@ export function apply(ctx, config = {}) {
lastSync = result
result.restartRequired = profileChanged
notify({ type: 'sync', result })
// Unattended slaves (LaunchAgent KeepAlive / systemd Restart=always): restart DSH so
// newly synced plugins load. Only when no session is running, and at most once per
// 10 minutes so a broken plugin can't cause a restart loop.
if (profileChanged && config.sync?.restartOnSync) scheduleSelfRestart('plugins changed by sync')
return result
})()
try { return await syncing } finally { syncing = null }
}
function scheduleSelfRestart(reason) {
const last = Number(store.data.lastSelfRestart || 0)
if (Date.now() - last < 10 * 60 * 1000) { log.warn(`dsh-remote: restart needed (${reason}) but skipped: restarted <10 min ago`); return }
const attempt = async (tries) => {
let busy = false
try { busy = (await listLocalSessions()).items.some((s) => s.running) } catch { busy = false }
if (busy && tries < 60) { setTimeout(() => attempt(tries + 1), 30000).unref?.(); return }
store.data.lastSelfRestart = Date.now()
store.save()
log.warn(`dsh-remote: restarting DSH (${reason}); the service manager brings it back`)
setTimeout(() => process.kill(process.pid, 'SIGTERM'), 1500).unref?.()
}
setTimeout(() => attempt(0), 3000).unref?.()
}
// ------------------------------------------------------------------ HTTP API
const route = (path, methods, handler) => ctx.effect(() => ctx.webServer.register({
kind: 'exact',