@jonathanyee/devtunnel-ctl 0.0.0-stage → 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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +328 -2
  3. package/dist/auth.d.ts +24 -0
  4. package/dist/auth.js +151 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +246 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +94 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/core.d.ts +16 -0
  13. package/dist/core.js +114 -0
  14. package/dist/core.js.map +1 -0
  15. package/dist/daemon.d.ts +31 -0
  16. package/dist/daemon.js +153 -0
  17. package/dist/daemon.js.map +1 -0
  18. package/dist/forwarding.d.ts +15 -0
  19. package/dist/forwarding.js +87 -0
  20. package/dist/forwarding.js.map +1 -0
  21. package/dist/host-process.d.ts +4 -0
  22. package/dist/host-process.js +110 -0
  23. package/dist/host-process.js.map +1 -0
  24. package/dist/host-sdk.d.ts +38 -0
  25. package/dist/host-sdk.js +327 -0
  26. package/dist/host-sdk.js.map +1 -0
  27. package/dist/host-service.d.ts +21 -0
  28. package/dist/host-service.js +193 -0
  29. package/dist/host-service.js.map +1 -0
  30. package/dist/host-worker.d.ts +1 -0
  31. package/dist/host-worker.js +80 -0
  32. package/dist/host-worker.js.map +1 -0
  33. package/dist/host.d.ts +43 -0
  34. package/dist/host.js +158 -0
  35. package/dist/host.js.map +1 -0
  36. package/dist/launchd.d.ts +15 -0
  37. package/dist/launchd.js +135 -0
  38. package/dist/launchd.js.map +1 -0
  39. package/dist/lock.d.ts +10 -0
  40. package/dist/lock.js +110 -0
  41. package/dist/lock.js.map +1 -0
  42. package/dist/log.d.ts +26 -0
  43. package/dist/log.js +67 -0
  44. package/dist/log.js.map +1 -0
  45. package/dist/main.d.ts +2 -0
  46. package/dist/main.js +5 -0
  47. package/dist/main.js.map +1 -0
  48. package/dist/paths.d.ts +4 -0
  49. package/dist/paths.js +39 -0
  50. package/dist/paths.js.map +1 -0
  51. package/dist/probe.d.ts +1 -0
  52. package/dist/probe.js +45 -0
  53. package/dist/probe.js.map +1 -0
  54. package/dist/recovery.d.ts +77 -0
  55. package/dist/recovery.js +265 -0
  56. package/dist/recovery.js.map +1 -0
  57. package/dist/rpc.d.ts +30 -0
  58. package/dist/rpc.js +208 -0
  59. package/dist/rpc.js.map +1 -0
  60. package/dist/sdk-transport.d.ts +33 -0
  61. package/dist/sdk-transport.js +239 -0
  62. package/dist/sdk-transport.js.map +1 -0
  63. package/npm-shrinkwrap.json +810 -0
  64. package/package.json +31 -4
@@ -0,0 +1,80 @@
1
+ import { record, safeFault, bounded, Fault } from "./core.js";
2
+ import { validateHost } from "./host.js";
3
+ import { runHostSdk } from "./host-sdk.js";
4
+ const life = new AbortController();
5
+ let started = false, pending = 0;
6
+ let endpointAck;
7
+ const send = (message) => {
8
+ if (!process.connected || (pending > 0 && message.kind === 'heartbeat'))
9
+ return;
10
+ if (pending >= 32) {
11
+ life.abort();
12
+ throw new Fault('protocol', 'Host worker IPC backlog exceeded its limit.');
13
+ }
14
+ pending++;
15
+ process.send?.(message, error => {
16
+ pending--;
17
+ if (error)
18
+ life.abort();
19
+ });
20
+ };
21
+ const report = (message) => {
22
+ if (message.kind !== 'endpoint') {
23
+ send(message);
24
+ return;
25
+ }
26
+ return bounded(() => new Promise((resolve, reject) => {
27
+ if (endpointAck) {
28
+ reject(new Fault('protocol'));
29
+ return;
30
+ }
31
+ endpointAck = { id: message.id, resolve };
32
+ send(message);
33
+ }), 2000, life.signal).finally(() => { endpointAck = undefined; });
34
+ };
35
+ const disconnected = () => life.abort();
36
+ process.once('disconnect', disconnected);
37
+ process.once('SIGTERM', disconnected);
38
+ process.once('SIGINT', disconnected);
39
+ const initial = setTimeout(() => { life.abort(); process.disconnect?.(); }, 5000);
40
+ process.on('message', value => {
41
+ if (!record(value)) {
42
+ life.abort();
43
+ return;
44
+ }
45
+ if (value.kind === 'stop') {
46
+ life.abort();
47
+ return;
48
+ }
49
+ if (value.kind === 'endpoint-recorded' && value.id === endpointAck?.id) {
50
+ endpointAck?.resolve();
51
+ return;
52
+ }
53
+ if (value.kind !== 'start' || started) {
54
+ life.abort();
55
+ return;
56
+ }
57
+ started = true;
58
+ clearTimeout(initial);
59
+ let options;
60
+ try {
61
+ options = validateHost(value.options);
62
+ }
63
+ catch {
64
+ send({ kind: 'fault', code: 'invalid-config', retryAfterMs: 0 });
65
+ process.disconnect?.();
66
+ return;
67
+ }
68
+ const heartbeat = setInterval(() => send({ kind: 'heartbeat' }), 1000);
69
+ void runHostSdk(options, report, life.signal).catch(error => {
70
+ const fault = safeFault(error);
71
+ send({ kind: 'fault', code: fault.code, retryAfterMs: fault.retryAfterMs });
72
+ }).finally(() => {
73
+ clearInterval(heartbeat);
74
+ if (process.connected)
75
+ process.disconnect();
76
+ });
77
+ });
78
+ if (!process.send)
79
+ throw new Fault('invalid-config', 'The host worker requires a supervisor IPC channel.');
80
+ //# sourceMappingURL=host-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-worker.js","sourceRoot":"","sources":["../src/host-worker.ts"],"names":[],"mappings":"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"}
package/dist/host.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { Clock, FaultCode } from './core.ts';
2
+ export interface HostOptions {
3
+ tunnelId: string;
4
+ clusterId?: string;
5
+ helperPath: string;
6
+ probePorts: number[];
7
+ }
8
+ export interface HostStatus {
9
+ kind: 'status';
10
+ state: 'connecting' | 'connected' | 'verified' | 'degraded';
11
+ relayConnected: boolean;
12
+ ports: number[];
13
+ localUnavailable: number[];
14
+ verifiedPorts: number[];
15
+ probeFault?: FaultCode;
16
+ }
17
+ export type HostMessage = HostStatus | {
18
+ kind: 'heartbeat';
19
+ } | {
20
+ kind: 'endpoint';
21
+ id: string;
22
+ tunnelId: string;
23
+ clusterId: string;
24
+ } | {
25
+ kind: 'fault';
26
+ code: FaultCode;
27
+ retryAfterMs: number;
28
+ } | {
29
+ kind: 'retry';
30
+ attempt: number;
31
+ nextRetryAt: number;
32
+ };
33
+ export declare function validateHost(value: unknown): HostOptions;
34
+ export declare function parseHostMessage(value: unknown): HostMessage | undefined;
35
+ export interface HostDependencies {
36
+ worker(options: HostOptions, emit: (message: HostMessage) => void, signal: AbortSignal): Promise<void>;
37
+ report(message: HostMessage): void;
38
+ clock?: Clock;
39
+ random?: () => number;
40
+ sleep?(ms: number, signal: AbortSignal): Promise<void>;
41
+ }
42
+ export declare function sleep(ms: number, signal: AbortSignal): Promise<void>;
43
+ export declare function superviseHost(options: HostOptions, deps: HostDependencies, signal: AbortSignal): Promise<void>;
package/dist/host.js ADDED
@@ -0,0 +1,158 @@
1
+ import { isAbsolute } from 'node:path';
2
+ import { bounded, clock, Fault, isFaultCode, record, safeFault } from "./core.js";
3
+ const port = (v) => Number.isInteger(v) && Number(v) > 0 && Number(v) <= 65535;
4
+ const id = /^[a-z0-9][a-z0-9-]{0,62}$/i;
5
+ const terminal = (code) => ['login-required', 'access-denied', 'invalid-config', 'host-superseded', 'host-cleanup-failed'].includes(code);
6
+ export function validateHost(value) {
7
+ if (!record(value) || typeof value.tunnelId !== 'string' ||
8
+ typeof value.helperPath !== 'string' || !isAbsolute(value.helperPath) ||
9
+ /[\0\r\n]/.test(value.helperPath) || !Array.isArray(value.probePorts) ||
10
+ value.probePorts.length > 16 || !value.probePorts.every(port) ||
11
+ new Set(value.probePorts).size !== value.probePorts.length)
12
+ throw new Fault('invalid-config', 'Invalid host options or probe ports.');
13
+ const [tunnelId, suffix, extra] = value.tunnelId.split('.');
14
+ const clusterId = value.clusterId ?? suffix;
15
+ if (!tunnelId || !id.test(tunnelId) || extra !== undefined ||
16
+ (clusterId !== undefined && (typeof clusterId !== 'string' || !id.test(clusterId))) ||
17
+ (suffix !== undefined && value.clusterId !== undefined && suffix !== value.clusterId)) {
18
+ throw new Fault('invalid-config', 'Invalid host tunnel or cluster ID.');
19
+ }
20
+ return { tunnelId, ...(clusterId ? { clusterId: String(clusterId) } : {}),
21
+ helperPath: value.helperPath, probePorts: [...value.probePorts] };
22
+ }
23
+ export function parseHostMessage(value) {
24
+ if (!record(value))
25
+ return;
26
+ if (value.kind === 'heartbeat')
27
+ return { kind: 'heartbeat' };
28
+ if (value.kind === 'retry' && Number.isSafeInteger(value.attempt) && Number(value.attempt) > 0 &&
29
+ typeof value.nextRetryAt === 'number' && Number.isFinite(value.nextRetryAt) && value.nextRetryAt >= 0) {
30
+ return { kind: 'retry', attempt: Number(value.attempt), nextRetryAt: value.nextRetryAt };
31
+ }
32
+ if (value.kind === 'endpoint' && typeof value.id === 'string' && /^[a-f0-9-]{36}-relay$/.test(value.id) &&
33
+ typeof value.tunnelId === 'string' && id.test(value.tunnelId) && typeof value.clusterId === 'string' && id.test(value.clusterId)) {
34
+ return { kind: 'endpoint', id: value.id, tunnelId: value.tunnelId, clusterId: value.clusterId };
35
+ }
36
+ if (value.kind === 'fault' && isFaultCode(value.code) && typeof value.retryAfterMs === 'number' &&
37
+ Number.isFinite(value.retryAfterMs) && value.retryAfterMs >= 0 && value.retryAfterMs <= 86400000) {
38
+ return { kind: 'fault', code: value.code, retryAfterMs: value.retryAfterMs };
39
+ }
40
+ if (value.kind !== 'status' || !['connecting', 'connected', 'verified', 'degraded'].includes(String(value.state)) ||
41
+ typeof value.relayConnected !== 'boolean' || (value.probeFault !== undefined && !isFaultCode(value.probeFault)))
42
+ return;
43
+ const arrays = [value.ports, value.localUnavailable, value.verifiedPorts];
44
+ if (!arrays.every(a => Array.isArray(a) && a.length <= 1024 && a.every(port) && new Set(a).size === a.length))
45
+ return;
46
+ const ports = value.ports, localUnavailable = value.localUnavailable, verifiedPorts = value.verifiedPorts;
47
+ if (localUnavailable.some(p => !ports.includes(p)) || verifiedPorts.some(p => !ports.includes(p) || localUnavailable.includes(p)) ||
48
+ (verifiedPorts.length > 0 && !value.relayConnected) || (value.state === 'verified' && (!value.relayConnected || !verifiedPorts.length)))
49
+ return;
50
+ return { kind: 'status', state: value.state, relayConnected: value.relayConnected,
51
+ ports: [...ports], localUnavailable: [...localUnavailable], verifiedPorts: [...verifiedPorts],
52
+ ...(isFaultCode(value.probeFault) ? { probeFault: value.probeFault } : {}) };
53
+ }
54
+ export function sleep(ms, signal) {
55
+ return new Promise((resolve, reject) => {
56
+ if (signal.aborted) {
57
+ reject(new Fault('aborted'));
58
+ return;
59
+ }
60
+ const aborted = () => { clearTimeout(timer); reject(new Fault('aborted')); };
61
+ const timer = setTimeout(() => { signal.removeEventListener('abort', aborted); resolve(); }, ms);
62
+ signal.addEventListener('abort', aborted, { once: true });
63
+ });
64
+ }
65
+ export async function superviseHost(options, deps, signal) {
66
+ const timer = deps.clock ?? clock;
67
+ let attempt = 0;
68
+ while (!signal.aborted) {
69
+ const life = new AbortController();
70
+ const parentAbort = () => life.abort();
71
+ signal.addEventListener('abort', parentAbort, { once: true });
72
+ const started = timer.now();
73
+ let lastHeartbeat = started, established = false, failure;
74
+ let cancelWatch = () => { };
75
+ const watch = () => {
76
+ if (timer.now() - lastHeartbeat > 15000 || (!established && timer.now() - started > 45000)) {
77
+ failure ??= new Fault('timeout');
78
+ life.abort();
79
+ return;
80
+ }
81
+ cancelWatch = timer.later(1000, watch);
82
+ };
83
+ watch();
84
+ let cancelAbort = () => { };
85
+ const aborted = new Promise(resolve => {
86
+ const abort = () => resolve();
87
+ life.signal.addEventListener('abort', abort, { once: true });
88
+ cancelAbort = () => life.signal.removeEventListener('abort', abort);
89
+ });
90
+ const work = Promise.resolve().then(() => {
91
+ if (signal.aborted)
92
+ life.abort();
93
+ return deps.worker(options, message => {
94
+ if (life.signal.aborted)
95
+ return;
96
+ if (message.kind === 'heartbeat') {
97
+ lastHeartbeat = timer.now();
98
+ return;
99
+ }
100
+ if (message.kind === 'fault') {
101
+ failure = new Fault(message.code, undefined, message.retryAfterMs);
102
+ life.abort();
103
+ }
104
+ else if (message.kind === 'status') {
105
+ established ||= message.relayConnected;
106
+ if ((message.state === 'verified' && options.probePorts.every(p => message.verifiedPorts.includes(p))) ||
107
+ (!options.probePorts.length && message.relayConnected))
108
+ attempt = 0;
109
+ deps.report(message);
110
+ }
111
+ }, life.signal);
112
+ });
113
+ try {
114
+ await Promise.race([work, aborted]);
115
+ }
116
+ catch (error) {
117
+ failure ??= safeFault(error);
118
+ }
119
+ finally {
120
+ life.abort();
121
+ cancelWatch();
122
+ cancelAbort();
123
+ signal.removeEventListener('abort', parentAbort);
124
+ }
125
+ // A replacement cannot overlap a worker that has not confirmed its exit.
126
+ try {
127
+ await bounded(() => work.catch(error => {
128
+ const fault = safeFault(error);
129
+ if (!failure || terminal(fault.code))
130
+ failure = fault;
131
+ }), 25000, undefined, undefined, timer);
132
+ }
133
+ catch {
134
+ throw new Fault('internal', 'Host worker exit could not be confirmed; refusing overlapping hosts.');
135
+ }
136
+ if (signal.aborted) {
137
+ if (failure?.code === 'host-cleanup-failed') {
138
+ deps.report({ kind: 'fault', code: failure.code, retryAfterMs: failure.retryAfterMs });
139
+ }
140
+ return;
141
+ }
142
+ failure ??= new Fault('retrying');
143
+ deps.report({ kind: 'fault', code: failure.code, retryAfterMs: failure.retryAfterMs });
144
+ if (terminal(failure.code))
145
+ return;
146
+ const backoff = Math.min(15000, 1000 * 2 ** Math.min(attempt++, 5));
147
+ const delay = Math.max(failure.retryAfterMs, Math.round(backoff * (0.5 + (deps.random ?? Math.random)() * 0.5)));
148
+ deps.report({ kind: 'retry', attempt, nextRetryAt: timer.now() + delay });
149
+ try {
150
+ await (deps.sleep ?? sleep)(delay, signal);
151
+ }
152
+ catch (error) {
153
+ if (!signal.aborted)
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Store } from './config.ts';
2
+ interface AgentPaths {
3
+ label: string;
4
+ node: string;
5
+ entry: string;
6
+ state: string;
7
+ }
8
+ export declare const xml: (value: string) => string;
9
+ export declare function launchAgent(paths: AgentPaths): string;
10
+ export declare function launchctl(args: string[], allowAbsent?: boolean): Promise<{
11
+ loaded: boolean;
12
+ pid?: number;
13
+ }>;
14
+ export declare function service(action: string, store: Store, entry: string): Promise<unknown>;
15
+ export {};
@@ -0,0 +1,135 @@
1
+ import { isAbsolute, join } from 'node:path';
2
+ import { homedir } from 'node:os';
3
+ import { realpath, mkdir, writeFile, lstat, unlink } from 'node:fs/promises';
4
+ import { createHash } from 'node:crypto';
5
+ import { spawn } from 'node:child_process';
6
+ import { bounded, Fault } from "./core.js";
7
+ import { DaemonLock } from "./lock.js";
8
+ import { request } from "./rpc.js";
9
+ import { executable } from "./paths.js";
10
+ export const xml = (value) => value.replace(/[<>&"']/g, ch => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&apos;' })[ch]);
11
+ export function launchAgent(paths) {
12
+ if (![paths.node, paths.entry, paths.state].every(p => isAbsolute(p) && !/[\0\r\n]/.test(p))) {
13
+ throw new Fault('invalid-config', 'launchd requires absolute runtime, entrypoint and state paths.');
14
+ }
15
+ return `<?xml version="1.0" encoding="UTF-8"?>
16
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
17
+ <plist version="1.0"><dict>
18
+ <key>Label</key><string>${xml(paths.label)}</string>
19
+ <key>ProgramArguments</key><array>${[paths.node, paths.entry, '--state-dir', paths.state, 'daemon'].map(arg => `<string>${xml(arg)}</string>`).join('')}</array>
20
+ <key>RunAtLoad</key><true/>
21
+ <key>KeepAlive</key><true/>
22
+ <key>ThrottleInterval</key><integer>15</integer>
23
+ <key>ProcessType</key><string>Background</string>
24
+ <key>StandardOutPath</key><string>/dev/null</string>
25
+ <key>StandardErrorPath</key><string>/dev/null</string>
26
+ </dict></plist>
27
+ `;
28
+ }
29
+ export async function launchctl(args, allowAbsent = false) {
30
+ return bounded(signal => new Promise((resolve, reject) => {
31
+ const child = spawn('/bin/launchctl', args, { shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
32
+ let bytes = 0, stdout = '';
33
+ const abort = () => { child.kill('SIGKILL'); };
34
+ signal.addEventListener('abort', abort, { once: true });
35
+ child.stdout.on('data', b => { bytes += b.length; if (bytes > 65536)
36
+ abort();
37
+ else
38
+ stdout += b.toString(); });
39
+ child.stderr.on('data', b => { bytes += b.length; if (bytes > 65536)
40
+ abort(); });
41
+ child.on('error', () => { signal.removeEventListener('abort', abort); reject(new Fault('internal', 'Cannot execute launchctl.')); });
42
+ child.on('close', code => {
43
+ signal.removeEventListener('abort', abort);
44
+ if (code === 0) {
45
+ const pid = /^\s*pid = ([1-9][0-9]*)\s*$/m.exec(stdout)?.[1];
46
+ resolve({ loaded: true, ...(pid ? { pid: Number(pid) } : {}) });
47
+ }
48
+ else if (allowAbsent && code === 113)
49
+ resolve({ loaded: false });
50
+ else
51
+ reject(new Fault('internal', 'launchctl failed. Check the service registration with launchctl print gui/$(id -u)/LABEL; no persistent action is assumed successful.'));
52
+ });
53
+ }), 5000);
54
+ }
55
+ export async function service(action, store, entry) {
56
+ if (process.platform !== 'darwin' || process.getuid?.() === 0)
57
+ throw new Fault('invalid-config', 'Service management requires a non-root macOS user.');
58
+ const label = `local.devtunnel-ctl.${createHash('sha256').update(store.dir).digest('hex').slice(0, 12)}`;
59
+ const directory = join(homedir(), 'Library', 'LaunchAgents');
60
+ const path = join(directory, label + '.plist');
61
+ const target = `gui/${process.getuid()}`;
62
+ if (action === 'install') {
63
+ const lock = new DaemonLock(store.dir);
64
+ await lock.acquire();
65
+ try {
66
+ const config = await store.load();
67
+ const node = await executable(process.execPath), main = await realpath(entry), helper = await executable(config.helperPath);
68
+ await mkdir(directory, { recursive: true, mode: 0o700 });
69
+ try {
70
+ await writeFile(path, launchAgent({ label, node, entry: main, state: store.dir }), { flag: 'wx', mode: 0o600 });
71
+ }
72
+ catch (error) {
73
+ if (error instanceof Error && 'code' in error && error.code === 'EEXIST') {
74
+ throw new Fault('invalid-config', 'Service is already installed. Stop and uninstall it before reinstalling paths.');
75
+ }
76
+ throw error;
77
+ }
78
+ try {
79
+ await store.save({ ...config, helperPath: helper });
80
+ }
81
+ catch (error) {
82
+ await unlink(path);
83
+ throw error;
84
+ }
85
+ return { installed: true, loaded: false, label, path, node, entry: main, helper };
86
+ }
87
+ finally {
88
+ await lock.release();
89
+ }
90
+ }
91
+ if (action === 'status') {
92
+ let installed = true;
93
+ try {
94
+ await lstat(path);
95
+ }
96
+ catch (error) {
97
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
98
+ installed = false;
99
+ else
100
+ throw error;
101
+ }
102
+ const result = await launchctl(['print', `${target}/${label}`], true);
103
+ if (!result.loaded)
104
+ return { label, installed, loaded: false, responsive: false };
105
+ try {
106
+ const status = await request(join(store.dir, 'control.sock'), { v: 1, command: 'status' });
107
+ return { label, installed, loaded: true, responsive: true, status };
108
+ }
109
+ catch {
110
+ return { label, installed, loaded: true, responsive: false,
111
+ detail: 'Registered service is unresponsive. Stop it, then run daemon in a terminal to see its sanitized startup error.' };
112
+ }
113
+ }
114
+ const info = await lstat(path);
115
+ if (!info.isFile() || info.isSymbolicLink() || info.uid !== process.getuid?.() || (info.mode & 0o077)) {
116
+ throw new Fault('invalid-config', 'Service plist is not a private owned file.');
117
+ }
118
+ if (action === 'start') {
119
+ await launchctl(['bootstrap', target, path]);
120
+ return { loaded: true, label };
121
+ }
122
+ if (action === 'stop') {
123
+ await launchctl(['bootout', `${target}/${label}`]);
124
+ return { loaded: false, label };
125
+ }
126
+ if (action === 'uninstall') {
127
+ const result = await launchctl(['print', `${target}/${label}`], true);
128
+ if (result.loaded)
129
+ await launchctl(['bootout', `${target}/${label}`]);
130
+ await unlink(path);
131
+ return { installed: false, loaded: false, label };
132
+ }
133
+ throw new Fault('invalid-config', 'Use service install, start, stop, uninstall or status.');
134
+ }
135
+ //# sourceMappingURL=launchd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launchd.js","sourceRoot":"","sources":["../src/launchd.ts"],"names":[],"mappings":"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"}
package/dist/lock.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export declare class DaemonLock {
2
+ private readonly path;
3
+ private readonly candidate;
4
+ private readonly nonce;
5
+ private owned;
6
+ constructor(dir: string);
7
+ acquire(): Promise<void>;
8
+ private reclaim;
9
+ release(): Promise<void>;
10
+ }
package/dist/lock.js ADDED
@@ -0,0 +1,110 @@
1
+ import { mkdir, writeFile, readFile, lstat, unlink, rmdir, rename, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { Fault, record } from "./core.js";
5
+ import { privatePath } from "./paths.js";
6
+ export class DaemonLock {
7
+ path;
8
+ candidate;
9
+ nonce = randomUUID();
10
+ owned = false;
11
+ constructor(dir) {
12
+ this.path = join(dir, 'daemon.lock');
13
+ this.candidate = join(dir, `.daemon-lock-${this.nonce}`);
14
+ }
15
+ async acquire() {
16
+ await mkdir(this.candidate, { mode: 0o700 });
17
+ const marker = `owner-${this.nonce}.json`;
18
+ try {
19
+ await writeFile(join(this.candidate, marker), JSON.stringify({ pid: process.pid, nonce: this.nonce }), { flag: 'wx', mode: 0o600 });
20
+ for (let attempt = 0; attempt < 5; attempt++) {
21
+ try {
22
+ // A populated candidate atomically replaces only an absent or empty directory.
23
+ await rename(this.candidate, this.path);
24
+ this.owned = true;
25
+ return;
26
+ }
27
+ catch (error) {
28
+ if (!(error instanceof Error && 'code' in error &&
29
+ (error.code === 'ENOTEMPTY' || error.code === 'EEXIST')))
30
+ throw error;
31
+ }
32
+ if (!await this.reclaim())
33
+ throw new Fault('daemon-unavailable', 'A live daemon owns the state lock.');
34
+ }
35
+ throw new Fault('daemon-unavailable', 'Daemon lock is changing; retry after the other starter finishes.');
36
+ }
37
+ finally {
38
+ if (!this.owned) {
39
+ await unlink(join(this.candidate, marker));
40
+ await rmdir(this.candidate);
41
+ }
42
+ }
43
+ }
44
+ async reclaim() {
45
+ try {
46
+ await privatePath(this.path, true);
47
+ const names = await readdir(this.path);
48
+ if (!names.length)
49
+ return true;
50
+ if (names.length !== 1 || !/^owner-[a-z0-9-]{1,64}\.json$/.test(names[0])) {
51
+ throw new Fault('daemon-unavailable', 'Unrecognized daemon lock contents; refusing automatic cleanup.');
52
+ }
53
+ const marker = join(this.path, names[0]);
54
+ await privatePath(marker);
55
+ if ((await lstat(marker)).size > 1024)
56
+ throw new Fault('daemon-unavailable', 'Invalid daemon lock record.');
57
+ const owner = JSON.parse(await readFile(marker, 'utf8'));
58
+ if (!record(owner) || !Number.isSafeInteger(owner.pid) || Number(owner.pid) <= 0 ||
59
+ typeof owner.nonce !== 'string' || names[0] !== `owner-${owner.nonce}.json`) {
60
+ throw new Fault('daemon-unavailable', 'Invalid daemon lock record.');
61
+ }
62
+ try {
63
+ process.kill(Number(owner.pid), 0);
64
+ return false;
65
+ }
66
+ catch (error) {
67
+ if (!(error instanceof Error && 'code' in error && error.code === 'ESRCH'))
68
+ return false;
69
+ }
70
+ // Unique marker names keep a racing reclaimer from deleting a new owner's record.
71
+ await unlink(marker);
72
+ try {
73
+ await rmdir(this.path);
74
+ }
75
+ catch (error) {
76
+ if (!(error instanceof Error && 'code' in error &&
77
+ (error.code === 'ENOENT' || error.code === 'ENOTEMPTY' || error.code === 'EEXIST')))
78
+ throw error;
79
+ }
80
+ return true;
81
+ }
82
+ catch (error) {
83
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
84
+ return true;
85
+ throw error;
86
+ }
87
+ }
88
+ async release() {
89
+ if (!this.owned)
90
+ return;
91
+ const owner = join(this.path, `owner-${this.nonce}.json`);
92
+ const info = await lstat(owner);
93
+ if (info.isSymbolicLink() || !info.isFile())
94
+ throw new Fault('internal', 'Daemon lock ownership changed.');
95
+ const value = JSON.parse(await readFile(owner, 'utf8'));
96
+ if (value.nonce !== this.nonce)
97
+ throw new Fault('internal', 'Daemon lock ownership changed.');
98
+ await unlink(owner);
99
+ try {
100
+ await rmdir(this.path);
101
+ }
102
+ catch (error) {
103
+ if (!(error instanceof Error && 'code' in error &&
104
+ (error.code === 'ENOENT' || error.code === 'ENOTEMPTY' || error.code === 'EEXIST')))
105
+ throw error;
106
+ }
107
+ this.owned = false;
108
+ }
109
+ }
110
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export interface LogEntry {
2
+ event: string;
3
+ profile?: string;
4
+ state?: string;
5
+ message?: string;
6
+ attempt?: number;
7
+ relayConnected?: boolean;
8
+ lastHealthyAt?: number;
9
+ nextRetryAt?: number;
10
+ }
11
+ export declare class Log {
12
+ private readonly dir;
13
+ private readonly maxBytes;
14
+ private pending;
15
+ private tail;
16
+ private sequence;
17
+ private readonly recent;
18
+ constructor(dir: string, maxBytes?: number);
19
+ init(): Promise<void>;
20
+ write(entry: LogEntry): Promise<void>;
21
+ flush(): Promise<void>;
22
+ snapshot(): {
23
+ sequence: number;
24
+ line: string;
25
+ }[];
26
+ }