remote-pi 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -40
- package/dist/bin/supervisord.d.ts +2 -0
- package/dist/bin/supervisord.js +44 -0
- package/dist/bin/supervisord.js.map +1 -0
- package/dist/config.d.ts +44 -13
- package/dist/config.js +61 -22
- package/dist/config.js.map +1 -1
- package/dist/daemon/client.d.ts +20 -0
- package/dist/daemon/client.js +128 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/control_protocol.d.ts +100 -0
- package/dist/daemon/control_protocol.js +63 -0
- package/dist/daemon/control_protocol.js.map +1 -0
- package/dist/daemon/id.d.ts +18 -0
- package/dist/daemon/id.js +30 -0
- package/dist/daemon/id.js.map +1 -0
- package/dist/daemon/install.d.ts +132 -0
- package/dist/daemon/install.js +312 -0
- package/dist/daemon/install.js.map +1 -0
- package/dist/daemon/registry.d.ts +47 -0
- package/dist/daemon/registry.js +123 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/rpc_child.d.ts +76 -0
- package/dist/daemon/rpc_child.js +130 -0
- package/dist/daemon/rpc_child.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +38 -0
- package/dist/daemon/supervisor.js +301 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/index.d.ts +62 -8
- package/dist/index.js +1226 -301
- package/dist/index.js.map +1 -1
- package/dist/mesh/canonical.d.ts +30 -0
- package/dist/mesh/canonical.js +61 -0
- package/dist/mesh/canonical.js.map +1 -0
- package/dist/mesh/client.d.ts +31 -0
- package/dist/mesh/client.js +56 -0
- package/dist/mesh/client.js.map +1 -0
- package/dist/mesh/encoding.d.ts +36 -0
- package/dist/mesh/encoding.js +53 -0
- package/dist/mesh/encoding.js.map +1 -0
- package/dist/mesh/self_revoke.d.ts +111 -0
- package/dist/mesh/self_revoke.js +182 -0
- package/dist/mesh/self_revoke.js.map +1 -0
- package/dist/mesh/siblings.d.ts +62 -0
- package/dist/mesh/siblings.js +95 -0
- package/dist/mesh/siblings.js.map +1 -0
- package/dist/mesh/types.d.ts +34 -0
- package/dist/mesh/types.js +11 -0
- package/dist/mesh/types.js.map +1 -0
- package/dist/mesh/verify.d.ts +17 -0
- package/dist/mesh/verify.js +77 -0
- package/dist/mesh/verify.js.map +1 -0
- package/dist/pairing/qr.d.ts +16 -5
- package/dist/pairing/qr.js +27 -8
- package/dist/pairing/qr.js.map +1 -1
- package/dist/pairing/storage.d.ts +41 -0
- package/dist/pairing/storage.js +158 -21
- package/dist/pairing/storage.js.map +1 -1
- package/dist/protocol/types.d.ts +23 -0
- package/dist/session/broker.d.ts +74 -0
- package/dist/session/broker.js +142 -4
- package/dist/session/broker.js.map +1 -1
- package/dist/session/broker_remote.d.ts +110 -0
- package/dist/session/broker_remote.js +397 -0
- package/dist/session/broker_remote.js.map +1 -0
- package/dist/session/cwd_lock.d.ts +28 -0
- package/dist/session/cwd_lock.js +89 -0
- package/dist/session/cwd_lock.js.map +1 -0
- package/dist/session/global_config.d.ts +9 -0
- package/dist/session/global_config.js +9 -0
- package/dist/session/global_config.js.map +1 -1
- package/dist/session/leader_election.d.ts +16 -0
- package/dist/session/leader_election.js +22 -0
- package/dist/session/leader_election.js.map +1 -1
- package/dist/session/local_config.d.ts +12 -5
- package/dist/session/local_config.js +24 -3
- package/dist/session/local_config.js.map +1 -1
- package/dist/session/peer.d.ts +28 -1
- package/dist/session/peer.js +69 -2
- package/dist/session/peer.js.map +1 -1
- package/dist/session/peer_inventory.d.ts +13 -0
- package/dist/session/peer_inventory.js +48 -0
- package/dist/session/peer_inventory.js.map +1 -0
- package/dist/session/setup_wizard.d.ts +32 -8
- package/dist/session/setup_wizard.js +45 -33
- package/dist/session/setup_wizard.js.map +1 -1
- package/dist/session/tools.d.ts +15 -7
- package/dist/session/tools.js +145 -31
- package/dist/session/tools.js.map +1 -1
- package/dist/transport/pi_forward_client.d.ts +29 -0
- package/dist/transport/pi_forward_client.js +62 -0
- package/dist/transport/pi_forward_client.js.map +1 -0
- package/dist/ui/footer.js +8 -6
- package/dist/ui/footer.js.map +1 -1
- package/docs/daemon.md +289 -0
- package/package.json +8 -2
- package/service-templates/launchd.plist.template +35 -0
- package/service-templates/systemd.service.template +19 -0
- package/skills/agent-network/SKILL.md +273 -294
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { delimiter } from "node:path";
|
|
4
|
+
import { homedir, platform, userInfo } from "node:os";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
export function detectPlatform() {
|
|
8
|
+
switch (platform()) {
|
|
9
|
+
case "darwin": return "macos";
|
|
10
|
+
case "linux": return "linux";
|
|
11
|
+
default: return "unsupported";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// ── Path resolution ────────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Absolute path to the supervisor's compiled entry. We resolve from
|
|
17
|
+
* `import.meta.url` (this file's location) since wherever the daemon
|
|
18
|
+
* module lives, `bin/supervisord.js` is a sibling of `daemon/` under
|
|
19
|
+
* `dist/`.
|
|
20
|
+
*
|
|
21
|
+
* After build: `dist/daemon/install.js` → `dist/bin/supervisord.js`.
|
|
22
|
+
* In dev (`tsx`): same path resolution still lands inside `src/`, which
|
|
23
|
+
* isn't directly runnable by `node` — dev install isn't expected.
|
|
24
|
+
*/
|
|
25
|
+
export function findSupervisorScript() {
|
|
26
|
+
const here = fileURLToPath(import.meta.url); // dist/daemon/install.js
|
|
27
|
+
const daemonDir = dirname(here); // dist/daemon
|
|
28
|
+
const distRoot = dirname(daemonDir); // dist
|
|
29
|
+
return resolve(distRoot, "bin/supervisord.js");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Absolute path to the extension's CLI entry (`dist/index.js`). This is
|
|
33
|
+
* the file we symlink to `~/.local/bin/remote-pi` so the user can run
|
|
34
|
+
* `remote-pi <subcommand>` from any shell after installing the extension
|
|
35
|
+
* through Pi (`pi install npm:remote-pi`).
|
|
36
|
+
*
|
|
37
|
+
* Same resolution strategy as `findSupervisorScript`: from
|
|
38
|
+
* `dist/daemon/install.js` → `dist/index.js`.
|
|
39
|
+
*/
|
|
40
|
+
export function findRemotePiScript() {
|
|
41
|
+
const here = fileURLToPath(import.meta.url); // dist/daemon/install.js
|
|
42
|
+
const daemonDir = dirname(here); // dist/daemon
|
|
43
|
+
const distRoot = dirname(daemonDir); // dist
|
|
44
|
+
return resolve(distRoot, "index.js");
|
|
45
|
+
}
|
|
46
|
+
export function findNodeBinary() {
|
|
47
|
+
// `process.execPath` is always absolute and points at the current Node
|
|
48
|
+
// binary. Embedding it in the service unit means the user gets the
|
|
49
|
+
// exact same Node version they invoked `remote-pi install` with — no
|
|
50
|
+
// PATH ambiguity at boot time.
|
|
51
|
+
return process.execPath;
|
|
52
|
+
}
|
|
53
|
+
export function findTemplate(name) {
|
|
54
|
+
// Templates ship next to the compiled `dist/` (via `files` in package.json).
|
|
55
|
+
// From `dist/daemon/install.js` go up two levels and into
|
|
56
|
+
// `service-templates/`. In the published npm tarball the layout is the
|
|
57
|
+
// same — `service-templates/` is sibling to `dist/`.
|
|
58
|
+
const here = fileURLToPath(import.meta.url); // dist/daemon/install.js
|
|
59
|
+
const pkgRoot = resolve(dirname(dirname(dirname(here)))); // package root
|
|
60
|
+
const file = name === "systemd"
|
|
61
|
+
? "systemd.service.template"
|
|
62
|
+
: "launchd.plist.template";
|
|
63
|
+
return resolve(pkgRoot, "service-templates", file);
|
|
64
|
+
}
|
|
65
|
+
// ── Service paths ──────────────────────────────────────────────────────────
|
|
66
|
+
export function systemdUnitPath() {
|
|
67
|
+
return join(homedir(), ".config", "systemd", "user", "remote-pi-supervisord.service");
|
|
68
|
+
}
|
|
69
|
+
export function launchdPlistPath() {
|
|
70
|
+
return join(homedir(), "Library", "LaunchAgents", "dev.remotepi.supervisord.plist");
|
|
71
|
+
}
|
|
72
|
+
const LAUNCHD_LABEL = "dev.remotepi.supervisord";
|
|
73
|
+
export function defaultRenderVars() {
|
|
74
|
+
return {
|
|
75
|
+
node: findNodeBinary(),
|
|
76
|
+
supervisor: findSupervisorScript(),
|
|
77
|
+
home: homedir(),
|
|
78
|
+
user: userInfo().username,
|
|
79
|
+
path: process.env["PATH"] ?? "/usr/local/bin:/usr/bin:/bin",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Replace `{NODE}` / `{SUPERVISOR}` / `{USER}` / `{HOME}` / `{PATH}`. */
|
|
83
|
+
export function renderTemplate(template, vars) {
|
|
84
|
+
return template
|
|
85
|
+
.replace(/\{NODE\}/g, vars.node)
|
|
86
|
+
.replace(/\{SUPERVISOR\}/g, vars.supervisor)
|
|
87
|
+
.replace(/\{USER\}/g, vars.user)
|
|
88
|
+
.replace(/\{HOME\}/g, vars.home)
|
|
89
|
+
.replace(/\{PATH\}/g, vars.path);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Writes the unit/plist, runs the platform's activation command. Throws
|
|
93
|
+
* on unsupported OS or when the supervisor script isn't found.
|
|
94
|
+
*
|
|
95
|
+
* Idempotent: re-running re-writes the unit (paths could have changed)
|
|
96
|
+
* and re-activates via the platform tool's idempotent flag.
|
|
97
|
+
*/
|
|
98
|
+
export function installService(vars = defaultRenderVars()) {
|
|
99
|
+
const plat = detectPlatform();
|
|
100
|
+
const log = [];
|
|
101
|
+
if (plat === "unsupported") {
|
|
102
|
+
throw new Error(`unsupported platform: ${platform()}. Only macOS and Linux.`);
|
|
103
|
+
}
|
|
104
|
+
// Sanity: supervisor script must exist on disk.
|
|
105
|
+
if (!existsSync(vars.supervisor)) {
|
|
106
|
+
throw new Error(`supervisor script not found at ${vars.supervisor}. ` +
|
|
107
|
+
"Run `pnpm build` (dev) or `npm install -g remote-pi` (prod) first.");
|
|
108
|
+
}
|
|
109
|
+
const templatePath = findTemplate(plat === "macos" ? "launchd" : "systemd");
|
|
110
|
+
if (!existsSync(templatePath)) {
|
|
111
|
+
throw new Error(`service template missing: ${templatePath}`);
|
|
112
|
+
}
|
|
113
|
+
const tpl = readFileSync(templatePath, "utf8");
|
|
114
|
+
const rendered = renderTemplate(tpl, vars);
|
|
115
|
+
const unitPath = plat === "macos" ? launchdPlistPath() : systemdUnitPath();
|
|
116
|
+
mkdirSync(dirname(unitPath), { recursive: true });
|
|
117
|
+
writeFileSync(unitPath, rendered);
|
|
118
|
+
log.push(`wrote ${unitPath}`);
|
|
119
|
+
if (plat === "macos") {
|
|
120
|
+
// Unload first in case a stale entry exists from a prior install —
|
|
121
|
+
// `launchctl bootstrap` errors out otherwise. `bootout` is the modern
|
|
122
|
+
// API; `unload` is the legacy fallback. Either may fail silently.
|
|
123
|
+
const uid = userInfo().uid;
|
|
124
|
+
_tryExec("launchctl", ["bootout", `gui/${uid}`, unitPath], log);
|
|
125
|
+
_tryExec("launchctl", ["unload", unitPath], log);
|
|
126
|
+
_exec("launchctl", ["bootstrap", `gui/${uid}`, unitPath], log);
|
|
127
|
+
log.push(`activated via launchctl bootstrap gui/${uid}`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
_exec("systemctl", ["--user", "daemon-reload"], log);
|
|
131
|
+
_exec("systemctl", ["--user", "enable", "--now", "remote-pi-supervisord.service"], log);
|
|
132
|
+
log.push("activated via systemctl --user enable --now");
|
|
133
|
+
}
|
|
134
|
+
return { platform: plat, unitPath, log };
|
|
135
|
+
}
|
|
136
|
+
export function uninstallService() {
|
|
137
|
+
const plat = detectPlatform();
|
|
138
|
+
const log = [];
|
|
139
|
+
if (plat === "unsupported") {
|
|
140
|
+
throw new Error(`unsupported platform: ${platform()}. Only macOS and Linux.`);
|
|
141
|
+
}
|
|
142
|
+
const unitPath = plat === "macos" ? launchdPlistPath() : systemdUnitPath();
|
|
143
|
+
if (plat === "macos") {
|
|
144
|
+
const uid = userInfo().uid;
|
|
145
|
+
_tryExec("launchctl", ["bootout", `gui/${uid}`, unitPath], log);
|
|
146
|
+
_tryExec("launchctl", ["unload", unitPath], log);
|
|
147
|
+
log.push("deactivated via launchctl bootout");
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
_tryExec("systemctl", ["--user", "disable", "--now", "remote-pi-supervisord.service"], log);
|
|
151
|
+
log.push("deactivated via systemctl --user disable --now");
|
|
152
|
+
}
|
|
153
|
+
let removed = false;
|
|
154
|
+
if (existsSync(unitPath)) {
|
|
155
|
+
try {
|
|
156
|
+
unlinkSync(unitPath);
|
|
157
|
+
removed = true;
|
|
158
|
+
log.push(`removed ${unitPath}`);
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
log.push(`failed to remove ${unitPath}: ${String(e)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (plat === "linux") {
|
|
165
|
+
_tryExec("systemctl", ["--user", "daemon-reload"], log);
|
|
166
|
+
}
|
|
167
|
+
// Hint about the label for users that want to verify manually.
|
|
168
|
+
if (plat === "macos")
|
|
169
|
+
log.push(`(label: ${LAUNCHD_LABEL})`);
|
|
170
|
+
return { platform: plat, unitPath, removed, log };
|
|
171
|
+
}
|
|
172
|
+
// ── Internals ──────────────────────────────────────────────────────────────
|
|
173
|
+
function _exec(cmd, args, log) {
|
|
174
|
+
try {
|
|
175
|
+
const out = execFileSync(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
176
|
+
if (out.trim())
|
|
177
|
+
log.push(`$ ${cmd} ${args.join(" ")}\n${out.trim()}`);
|
|
178
|
+
else
|
|
179
|
+
log.push(`$ ${cmd} ${args.join(" ")}`);
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
const err = e;
|
|
183
|
+
const stderr = typeof err.stderr === "string" ? err.stderr : err.stderr?.toString() ?? "";
|
|
184
|
+
throw new Error(`\`${cmd} ${args.join(" ")}\` exited ${err.status ?? "?"}\n${stderr.trim() || err.message}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Like _exec but swallows errors — used for cleanup steps where failure
|
|
188
|
+
* is expected (e.g., "unload" before "load" when nothing was loaded). */
|
|
189
|
+
function _tryExec(cmd, args, log) {
|
|
190
|
+
try {
|
|
191
|
+
_exec(cmd, args, log);
|
|
192
|
+
}
|
|
193
|
+
catch { /* expected, suppress */ }
|
|
194
|
+
}
|
|
195
|
+
export function userLocalBinDir(home = homedir()) {
|
|
196
|
+
return join(home, ".local", "bin");
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check whether `dir` is on `process.env.PATH`. Tolerates trailing
|
|
200
|
+
* slashes and relative entries (which we treat as not matching — `~/.local/bin`
|
|
201
|
+
* is always absolute on our end).
|
|
202
|
+
*/
|
|
203
|
+
export function isOnPath(dir, envPath = process.env["PATH"] ?? "") {
|
|
204
|
+
const target = dir.replace(/\/+$/, "");
|
|
205
|
+
return envPath.split(delimiter).some((entry) => entry.replace(/\/+$/, "") === target);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create (or refresh) the `remote-pi` + `pi-supervisord` symlinks in
|
|
209
|
+
* `~/.local/bin/`. Idempotent — replaces stale links pointing at old
|
|
210
|
+
* extension paths (Pi can reinstall the extension to a different hash dir
|
|
211
|
+
* on upgrades, so this MUST overwrite).
|
|
212
|
+
*
|
|
213
|
+
* Returns `onPath: false` when `~/.local/bin` isn't in the user's `$PATH`.
|
|
214
|
+
* The caller is responsible for surfacing the shell-rc instruction —
|
|
215
|
+
* we don't edit the user's shell config files automatically.
|
|
216
|
+
*/
|
|
217
|
+
export function linkCliBinaries(home = homedir(), paths = {}) {
|
|
218
|
+
const binDir = userLocalBinDir(home);
|
|
219
|
+
const log = [];
|
|
220
|
+
mkdirSync(binDir, { recursive: true });
|
|
221
|
+
log.push(`ensured ${binDir}`);
|
|
222
|
+
const remotePi = paths.remotePi ?? findRemotePiScript();
|
|
223
|
+
const supervisord = paths.supervisord ?? findSupervisorScript();
|
|
224
|
+
if (!existsSync(remotePi)) {
|
|
225
|
+
throw new Error(`remote-pi script not found at ${remotePi}. ` +
|
|
226
|
+
"Run `pnpm build` (dev) or reinstall the extension.");
|
|
227
|
+
}
|
|
228
|
+
if (!existsSync(supervisord)) {
|
|
229
|
+
throw new Error(`supervisor script not found at ${supervisord}. ` +
|
|
230
|
+
"Run `pnpm build` (dev) or reinstall the extension.");
|
|
231
|
+
}
|
|
232
|
+
// tsc strips the executable bit on its outputs; the shebang at the top
|
|
233
|
+
// of dist/index.js means the file IS a valid interpreter target once
|
|
234
|
+
// chmod +x is applied. Same for supervisord.js (no shebang — we rely
|
|
235
|
+
// on `node` resolving via the symlink at exec time).
|
|
236
|
+
try {
|
|
237
|
+
chmodSync(remotePi, 0o755);
|
|
238
|
+
}
|
|
239
|
+
catch { /* best-effort */ }
|
|
240
|
+
try {
|
|
241
|
+
chmodSync(supervisord, 0o755);
|
|
242
|
+
}
|
|
243
|
+
catch { /* best-effort */ }
|
|
244
|
+
const links = [
|
|
245
|
+
{ name: "remote-pi", path: join(binDir, "remote-pi"), target: remotePi },
|
|
246
|
+
{ name: "pi-supervisord", path: join(binDir, "pi-supervisord"), target: supervisord },
|
|
247
|
+
];
|
|
248
|
+
for (const link of links) {
|
|
249
|
+
_replaceSymlink(link.path, link.target, log);
|
|
250
|
+
}
|
|
251
|
+
const onPath = isOnPath(binDir);
|
|
252
|
+
if (!onPath) {
|
|
253
|
+
log.push(`WARNING: ${binDir} is not on $PATH. ` +
|
|
254
|
+
`Add this line to your shell rc (~/.zshrc, ~/.bashrc, etc.): ` +
|
|
255
|
+
`export PATH="$HOME/.local/bin:$PATH"`);
|
|
256
|
+
}
|
|
257
|
+
return { binDir, links, onPath, log };
|
|
258
|
+
}
|
|
259
|
+
export function unlinkCliBinaries(home = homedir()) {
|
|
260
|
+
const binDir = userLocalBinDir(home);
|
|
261
|
+
const log = [];
|
|
262
|
+
const names = ["remote-pi", "pi-supervisord"];
|
|
263
|
+
const removed = [];
|
|
264
|
+
for (const name of names) {
|
|
265
|
+
const path = join(binDir, name);
|
|
266
|
+
let existed = false;
|
|
267
|
+
try {
|
|
268
|
+
// lstatSync (not stat) so a symlink targeting a deleted file still
|
|
269
|
+
// resolves — we want to remove the LINK itself, not chase it.
|
|
270
|
+
lstatSync(path);
|
|
271
|
+
existed = true;
|
|
272
|
+
}
|
|
273
|
+
catch { /* not present */ }
|
|
274
|
+
if (existed) {
|
|
275
|
+
try {
|
|
276
|
+
unlinkSync(path);
|
|
277
|
+
log.push(`removed ${path}`);
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
log.push(`failed to remove ${path}: ${String(e)}`);
|
|
281
|
+
existed = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
removed.push({ name, path, existed });
|
|
285
|
+
}
|
|
286
|
+
return { binDir, removed, log };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Atomic-ish symlink replace. Idiomatic recipe — `symlinkSync` errors
|
|
290
|
+
* with `EEXIST` if the path is already a symlink/file, so we remove
|
|
291
|
+
* first. Race window between unlink and symlink is irrelevant for a
|
|
292
|
+
* single-user install command (no concurrent writers).
|
|
293
|
+
*/
|
|
294
|
+
function _replaceSymlink(linkPath, target, log) {
|
|
295
|
+
let existing = null;
|
|
296
|
+
try {
|
|
297
|
+
existing = readlinkSync(linkPath);
|
|
298
|
+
}
|
|
299
|
+
catch { /* not a symlink, or doesn't exist */ }
|
|
300
|
+
if (existing === target) {
|
|
301
|
+
log.push(`symlink ${linkPath} → ${target} (unchanged)`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Either it doesn't exist, or it points elsewhere. Remove + recreate.
|
|
305
|
+
try {
|
|
306
|
+
unlinkSync(linkPath);
|
|
307
|
+
}
|
|
308
|
+
catch { /* fine if absent */ }
|
|
309
|
+
symlinkSync(target, linkPath);
|
|
310
|
+
log.push(`symlink ${linkPath} → ${target}`);
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/daemon/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1I,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AA4BzC,MAAM,UAAU,cAAc;IAC5B,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC;QAC9B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,OAAO,CAAC,CAAC,OAAO,aAAa,CAAC;IAChC,CAAC;AACH,CAAC;AAED,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAU,yBAAyB;IAC/E,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAuB,cAAc;IACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAmB,OAAO;IAC9D,OAAO,OAAO,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAU,yBAAyB;IAC/E,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAuB,cAAc;IACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAmB,OAAO;IAC9D,OAAO,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,uEAAuE;IACvE,mEAAmE;IACnE,qEAAqE;IACrE,+BAA+B;IAC/B,OAAO,OAAO,CAAC,QAAQ,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAA2B;IACtD,6EAA6E;IAC7E,0DAA0D;IAC1D,uEAAuE;IACvE,qDAAqD;IACrD,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAU,yBAAyB;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,eAAe;IAC1E,MAAM,IAAI,GAAG,IAAI,KAAK,SAAS;QAC7B,CAAC,CAAC,0BAA0B;QAC5B,CAAC,CAAC,wBAAwB,CAAC;IAC7B,OAAO,OAAO,CAAC,OAAO,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,8EAA8E;AAE9E,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,+BAA+B,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,gCAAgC,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAcjD,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,IAAI,EAAE,cAAc,EAAE;QACtB,UAAU,EAAE,oBAAoB,EAAE;QAClC,IAAI,EAAE,OAAO,EAAE;QACf,IAAI,EAAE,QAAQ,EAAE,CAAC,QAAQ;QACzB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,8BAA8B;KAC5D,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,IAAgB;IAC/D,OAAO,QAAQ;SACZ,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC;SAC/B,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,UAAU,CAAC;SAC3C,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC;SAC/B,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC;SAC/B,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAWD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAmB,iBAAiB,EAAE;IACnE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,yBAAyB,CAAC,CAAC;IAChF,CAAC;IAED,gDAAgD;IAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,kCAAkC,IAAI,CAAC,UAAU,IAAI;YACrD,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5E,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,6BAA6B,YAAY,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;IAC3E,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClC,GAAG,CAAC,IAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IAE9B,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,mEAAmE;QACnE,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC;QAC3B,QAAQ,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;QAChE,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;QACjD,KAAK,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/D,GAAG,CAAC,IAAI,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,+BAA+B,CAAC,EAAE,GAAG,CAAC,CAAC;QACxF,GAAG,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;AAC3C,CAAC;AASD,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,yBAAyB,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;IAE3E,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC;QAC3B,QAAQ,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;QAChE,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,+BAA+B,CAAC,EAAE,GAAG,CAAC,CAAC;QAC5F,GAAG,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,OAAO,GAAG,IAAI,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,WAAW,QAAQ,EAAE,CAAC,CAAC;QAAC,CAAC;QAC9E,OAAO,CAAC,EAAE,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC;IACvE,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,+DAA+D;IAC/D,IAAI,IAAI,KAAK,OAAO;QAAE,GAAG,CAAC,IAAI,CAAC,WAAW,aAAa,GAAG,CAAC,CAAC;IAE5D,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACpD,CAAC;AAED,8EAA8E;AAE9E,SAAS,KAAK,CAAC,GAAW,EAAE,IAAc,EAAE,GAAa;IACvD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7F,IAAI,GAAG,CAAC,IAAI,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;;YACjE,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAmE,CAAC;QAChF,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC1F,MAAM,IAAI,KAAK,CACb,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,MAAM,IAAI,GAAG,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,CAC5F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;0EAC0E;AAC1E,SAAS,QAAQ,CAAC,GAAW,EAAE,IAAc,EAAE,GAAa;IAC1D,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;AACnE,CAAC;AAoCD,MAAM,UAAU,eAAe,CAAC,OAAe,OAAO,EAAE;IACtD,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,UAAkB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;IAC/E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;AACxF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,OAAO,EAAE,EACxB,QAAqD,EAAE;IAEvD,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IAE9B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,kBAAkB,EAAE,CAAC;IACxD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,oBAAoB,EAAE,CAAC;IAChE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,iCAAiC,QAAQ,IAAI;YAC7C,oDAAoD,CACrD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,kCAAkC,WAAW,IAAI;YACjD,oDAAoD,CACrD,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,qEAAqE;IACrE,qEAAqE;IACrE,qDAAqD;IACrD,IAAI,CAAC;QAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC/D,IAAI,CAAC;QAAC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAElE,MAAM,KAAK,GAAgC;QACzC,EAAE,IAAI,EAAE,WAAW,EAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAAO,MAAM,EAAE,QAAQ,EAAE;QACjF,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE;KACtF,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,IAAI,CACN,YAAY,MAAM,oBAAoB;YACtC,8DAA8D;YAC9D,sCAAsC,CACvC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACxC,CAAC;AAcD,MAAM,UAAU,iBAAiB,CAAC,OAAe,OAAO,EAAE;IACxD,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAoC,EAAE,CAAC;IAEpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,mEAAmE;YACnE,8DAA8D;YAC9D,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAC7B,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,UAAU,CAAC,IAAI,CAAC,CAAC;gBACjB,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YAC9B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,GAAG,CAAC,IAAI,CAAC,oBAAoB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACnD,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;QACH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAClC,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,MAAc,EAAE,GAAa;IACtE,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC,CAAC,qCAAqC,CAAC,CAAC;IAEjD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,WAAW,QAAQ,MAAM,MAAM,cAAc,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC;QAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC5D,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9B,GAAG,CAAC,IAAI,CAAC,WAAW,QAAQ,MAAM,MAAM,EAAE,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface DaemonEntry {
|
|
2
|
+
/** Absolute realpath of the cwd this daemon manages. */
|
|
3
|
+
cwd: string;
|
|
4
|
+
}
|
|
5
|
+
export interface DaemonRegistry {
|
|
6
|
+
daemons: DaemonEntry[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes a user-provided path: expands `~`/`~/...`, resolves relative
|
|
10
|
+
* components against `process.cwd()`, and runs `realpath` to canonicalize
|
|
11
|
+
* symlinks. Throws if the resulting path doesn't exist on disk.
|
|
12
|
+
*
|
|
13
|
+
* `/remote-pi create` always stores normalized paths so two registrations
|
|
14
|
+
* of the same logical folder via different aliases produce a single entry.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeCwd(input: string): string;
|
|
17
|
+
/** Reads the registry, returning an empty one when the file is absent. */
|
|
18
|
+
export declare function loadRegistry(): DaemonRegistry;
|
|
19
|
+
export declare function saveRegistry(reg: DaemonRegistry): void;
|
|
20
|
+
/**
|
|
21
|
+
* Adds a daemon entry. Refuses duplicates (same normalized cwd already
|
|
22
|
+
* present). Returns the derived id + normalized cwd so the caller can
|
|
23
|
+
* report it back to the user.
|
|
24
|
+
*/
|
|
25
|
+
export declare function addDaemon(rawCwd: string): {
|
|
26
|
+
id: string;
|
|
27
|
+
cwd: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Removes the daemon entry whose derived id matches `id`. Returns the
|
|
31
|
+
* removed cwd (if any). Does NOT touch the cwd's local config — `create`
|
|
32
|
+
* the same cwd later restores the registration idempotently.
|
|
33
|
+
*/
|
|
34
|
+
export declare function removeDaemon(id: string): {
|
|
35
|
+
removed: boolean;
|
|
36
|
+
cwd?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Snapshot of all registered daemons with derived ids. Order matches the
|
|
39
|
+
* file's insertion order — first-registered first. */
|
|
40
|
+
export declare function listDaemons(): Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
cwd: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** Test/diag-only: returns the on-disk path. Exported so tests can poke
|
|
45
|
+
* at it (e.g. tmpdir override is done via env, but for now the path is
|
|
46
|
+
* hardcoded to ~/.pi/remote/daemons.json). */
|
|
47
|
+
export declare function registryPath(): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve as resolvePath } from "node:path";
|
|
4
|
+
import { daemonIdForCwd } from "./id.js";
|
|
5
|
+
/**
|
|
6
|
+
* The global daemon registry: which working directories are promoted to
|
|
7
|
+
* always-on daemons under the supervisor.
|
|
8
|
+
*
|
|
9
|
+
* Schema rationale (decision Q in plan/26): the registry stores **only**
|
|
10
|
+
* `cwd` per entry. Everything else (agent_name, auto_start_relay, etc.)
|
|
11
|
+
* is the cwd's local config at `<cwd>/.pi/remote-pi/config.json` — single
|
|
12
|
+
* source of truth, no duplication. The daemon `id` is *derived* from cwd
|
|
13
|
+
* via `daemonIdForCwd`, never persisted.
|
|
14
|
+
*
|
|
15
|
+
* Cwds are always normalized to an **absolute realpath** before storage.
|
|
16
|
+
* A user typing `/remote-pi create ~/Movies` or `/remote-pi create .`
|
|
17
|
+
* results in the same entry as `/remote-pi create /Users/x/Movies` — no
|
|
18
|
+
* surprise duplicates, symlinks collapse correctly.
|
|
19
|
+
*/
|
|
20
|
+
/** Resolved at call time so tests can override via `REMOTE_PI_HOME`. The
|
|
21
|
+
* prod path is always `~/.pi/remote/daemons.json`. */
|
|
22
|
+
function registryPathInternal() {
|
|
23
|
+
const root = process.env["REMOTE_PI_HOME"] || homedir();
|
|
24
|
+
return join(root, ".pi", "remote", "daemons.json");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Normalizes a user-provided path: expands `~`/`~/...`, resolves relative
|
|
28
|
+
* components against `process.cwd()`, and runs `realpath` to canonicalize
|
|
29
|
+
* symlinks. Throws if the resulting path doesn't exist on disk.
|
|
30
|
+
*
|
|
31
|
+
* `/remote-pi create` always stores normalized paths so two registrations
|
|
32
|
+
* of the same logical folder via different aliases produce a single entry.
|
|
33
|
+
*/
|
|
34
|
+
export function normalizeCwd(input) {
|
|
35
|
+
if (!input || !input.trim()) {
|
|
36
|
+
throw new Error("cwd is required");
|
|
37
|
+
}
|
|
38
|
+
let p = input.trim();
|
|
39
|
+
// Expand `~` / `~/relative`. Shell wouldn't expand inside slash command args.
|
|
40
|
+
if (p === "~")
|
|
41
|
+
p = homedir();
|
|
42
|
+
else if (p.startsWith("~/"))
|
|
43
|
+
p = join(homedir(), p.slice(2));
|
|
44
|
+
if (!isAbsolute(p))
|
|
45
|
+
p = resolvePath(process.cwd(), p);
|
|
46
|
+
// realpath canonicalizes symlinks + throws if path doesn't exist.
|
|
47
|
+
return realpathSync(p);
|
|
48
|
+
}
|
|
49
|
+
/** Reads the registry, returning an empty one when the file is absent. */
|
|
50
|
+
export function loadRegistry() {
|
|
51
|
+
if (!existsSync(registryPathInternal()))
|
|
52
|
+
return { daemons: [] };
|
|
53
|
+
try {
|
|
54
|
+
const raw = readFileSync(registryPathInternal(), "utf8");
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
if (!parsed || typeof parsed !== "object")
|
|
57
|
+
return { daemons: [] };
|
|
58
|
+
const arr = parsed.daemons;
|
|
59
|
+
if (!Array.isArray(arr))
|
|
60
|
+
return { daemons: [] };
|
|
61
|
+
const daemons = [];
|
|
62
|
+
for (const item of arr) {
|
|
63
|
+
if (!item || typeof item !== "object")
|
|
64
|
+
continue;
|
|
65
|
+
const cwd = item.cwd;
|
|
66
|
+
if (typeof cwd === "string" && cwd.length > 0) {
|
|
67
|
+
daemons.push({ cwd });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { daemons };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { daemons: [] };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function saveRegistry(reg) {
|
|
77
|
+
mkdirSync(dirname(registryPathInternal()), { recursive: true });
|
|
78
|
+
writeFileSync(registryPathInternal(), JSON.stringify(reg, null, 2) + "\n");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Adds a daemon entry. Refuses duplicates (same normalized cwd already
|
|
82
|
+
* present). Returns the derived id + normalized cwd so the caller can
|
|
83
|
+
* report it back to the user.
|
|
84
|
+
*/
|
|
85
|
+
export function addDaemon(rawCwd) {
|
|
86
|
+
const cwd = normalizeCwd(rawCwd);
|
|
87
|
+
const reg = loadRegistry();
|
|
88
|
+
if (reg.daemons.some((d) => d.cwd === cwd)) {
|
|
89
|
+
throw new Error(`Daemon already registered for cwd: ${cwd}`);
|
|
90
|
+
}
|
|
91
|
+
reg.daemons.push({ cwd });
|
|
92
|
+
saveRegistry(reg);
|
|
93
|
+
return { id: daemonIdForCwd(cwd), cwd };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Removes the daemon entry whose derived id matches `id`. Returns the
|
|
97
|
+
* removed cwd (if any). Does NOT touch the cwd's local config — `create`
|
|
98
|
+
* the same cwd later restores the registration idempotently.
|
|
99
|
+
*/
|
|
100
|
+
export function removeDaemon(id) {
|
|
101
|
+
const reg = loadRegistry();
|
|
102
|
+
const idx = reg.daemons.findIndex((d) => daemonIdForCwd(d.cwd) === id);
|
|
103
|
+
if (idx === -1)
|
|
104
|
+
return { removed: false };
|
|
105
|
+
const [removed] = reg.daemons.splice(idx, 1);
|
|
106
|
+
saveRegistry(reg);
|
|
107
|
+
return { removed: true, cwd: removed.cwd };
|
|
108
|
+
}
|
|
109
|
+
/** Snapshot of all registered daemons with derived ids. Order matches the
|
|
110
|
+
* file's insertion order — first-registered first. */
|
|
111
|
+
export function listDaemons() {
|
|
112
|
+
return loadRegistry().daemons.map((d) => ({
|
|
113
|
+
id: daemonIdForCwd(d.cwd),
|
|
114
|
+
cwd: d.cwd,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
/** Test/diag-only: returns the on-disk path. Exported so tests can poke
|
|
118
|
+
* at it (e.g. tmpdir override is done via env, but for now the path is
|
|
119
|
+
* hardcoded to ~/.pi/remote/daemons.json). */
|
|
120
|
+
export function registryPath() {
|
|
121
|
+
return registryPathInternal();
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/daemon/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { DaemonState } from "./control_protocol.js";
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper around a `pi --mode rpc -e <extension>` child process for the
|
|
5
|
+
* supervisor.
|
|
6
|
+
*
|
|
7
|
+
* Lifecycle:
|
|
8
|
+
* - `spawn()` boots the child with the daemon's cwd + `REMOTE_PI_DAEMON=1`
|
|
9
|
+
* env so the extension knows to skip the interactive wizard.
|
|
10
|
+
* - `sendPrompt(text)` writes a Pi RPC `prompt` command to stdin.
|
|
11
|
+
* - The child's stdout (Pi RPC events) is currently consumed line-by-line
|
|
12
|
+
* and ignored — wave 2 only needs fire-and-forget. Later waves can
|
|
13
|
+
* surface the events back through the supervisor's status op.
|
|
14
|
+
* - Exit/crash fires `exit` event with `{code, signal, isCrash}` so the
|
|
15
|
+
* supervisor can decide whether to auto-restart.
|
|
16
|
+
*
|
|
17
|
+
* Each `RpcChild` instance maps 1:1 to a registry entry (single cwd).
|
|
18
|
+
* The supervisor owns the map of these and addresses them by `id`.
|
|
19
|
+
*/
|
|
20
|
+
export interface RpcChildOptions {
|
|
21
|
+
/** Path to the `pi` binary. Defaults to "pi" (must be on PATH). */
|
|
22
|
+
piBin?: string;
|
|
23
|
+
/** Absolute path to the remote-pi `dist/index.js` to load as -e. */
|
|
24
|
+
extensionPath: string;
|
|
25
|
+
/** Working directory for the spawned process. Determines which local
|
|
26
|
+
* config the extension reads. */
|
|
27
|
+
cwd: string;
|
|
28
|
+
/** Additional env vars merged on top of `process.env` + the mandatory
|
|
29
|
+
* `REMOTE_PI_DAEMON=1`. */
|
|
30
|
+
env?: NodeJS.ProcessEnv;
|
|
31
|
+
}
|
|
32
|
+
export interface RpcChildExitEvent {
|
|
33
|
+
code: number | null;
|
|
34
|
+
signal: NodeJS.Signals | null;
|
|
35
|
+
/** True when exit was not clean (non-zero or signal). */
|
|
36
|
+
isCrash: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare class RpcChild extends EventEmitter {
|
|
39
|
+
private readonly opts;
|
|
40
|
+
private child;
|
|
41
|
+
private _state;
|
|
42
|
+
private _startedAt;
|
|
43
|
+
private _restartCount;
|
|
44
|
+
/** Accumulates partial stdout lines while waiting for `\n`. */
|
|
45
|
+
private stdoutBuf;
|
|
46
|
+
constructor(opts: RpcChildOptions);
|
|
47
|
+
get state(): DaemonState;
|
|
48
|
+
get pid(): number | undefined;
|
|
49
|
+
get restartCount(): number;
|
|
50
|
+
get uptimeMs(): number | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Spawn the child process. Idempotent for the same instance: a second
|
|
53
|
+
* call while already running is a no-op.
|
|
54
|
+
*/
|
|
55
|
+
spawn(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Sends a Pi RPC `prompt` command to the child's stdin. Fire-and-forget
|
|
58
|
+
* — we don't wait for the response (response would be the success ack;
|
|
59
|
+
* the actual agent output streams via the relay/UDS, not back through
|
|
60
|
+
* stdout).
|
|
61
|
+
*
|
|
62
|
+
* Returns false if the child isn't running (caller decides how to report).
|
|
63
|
+
*/
|
|
64
|
+
sendPrompt(text: string, requestId?: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Asks the child to exit gracefully. Sends SIGTERM; if the child doesn't
|
|
67
|
+
* exit within `timeoutMs`, escalates to SIGKILL. Resolves when the
|
|
68
|
+
* `exit` event fires.
|
|
69
|
+
*/
|
|
70
|
+
stop(timeoutMs?: number): Promise<void>;
|
|
71
|
+
private _onStdout;
|
|
72
|
+
private _onExit;
|
|
73
|
+
/** Bumps the restart counter — called by the supervisor when it
|
|
74
|
+
* decides to re-spawn after a crash. Exposed so tests can drive it. */
|
|
75
|
+
noteRestart(): void;
|
|
76
|
+
}
|