iteration-cli 0.4.2 → 0.4.3
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/dist/launch-agent.d.ts +7 -0
- package/dist/launch-agent.js +80 -10
- package/package.json +1 -1
package/dist/launch-agent.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
export type LaunchAgentRestart = {
|
|
2
|
+
isLoaded(timeoutMs: number): Promise<boolean>;
|
|
3
|
+
bootstrap(timeoutMs: number): Promise<void>;
|
|
4
|
+
wait(delayMs: number): Promise<void>;
|
|
5
|
+
now(): number;
|
|
6
|
+
};
|
|
1
7
|
export declare function launchAgentPlist(cliPath?: string, nodePath?: string, logDirectory?: string, dataDirectory?: string): string;
|
|
8
|
+
export declare function bootstrapAfterBootout(wasLoaded: boolean, control: LaunchAgentRestart): Promise<void>;
|
|
2
9
|
export declare function installLaunchAgent(dataDirectory?: string): Promise<string>;
|
|
3
10
|
export declare function uninstallLaunchAgent(): Promise<void>;
|
|
4
11
|
export declare function launchAgentStatus(): Promise<boolean>;
|
package/dist/launch-agent.js
CHANGED
|
@@ -3,9 +3,13 @@ import { execFile as execFileCallback } from "node:child_process";
|
|
|
3
3
|
import { promises as fs } from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
const execFile = promisify(execFileCallback);
|
|
8
9
|
const LABEL = "com.iteration.codex-collector";
|
|
10
|
+
const LAUNCHCTL_TIMEOUT_MS = 5_000;
|
|
11
|
+
const RESTART_TIMEOUT_MS = 10_000;
|
|
12
|
+
const RESTART_POLL_MS = 200;
|
|
9
13
|
function xml(value) {
|
|
10
14
|
return value
|
|
11
15
|
.replaceAll("&", "&")
|
|
@@ -52,18 +56,73 @@ function launchctlTarget() {
|
|
|
52
56
|
throw new Error("Could not determine the current macOS user id.");
|
|
53
57
|
return `gui/${userId}`;
|
|
54
58
|
}
|
|
55
|
-
|
|
59
|
+
function serviceMissing(error) {
|
|
60
|
+
const details = error;
|
|
61
|
+
return (String(details.code) === "113" ||
|
|
62
|
+
details.stderr?.includes("Could not find service") === true);
|
|
63
|
+
}
|
|
64
|
+
async function serviceLoaded(target, timeoutMs = LAUNCHCTL_TIMEOUT_MS) {
|
|
56
65
|
try {
|
|
57
|
-
await execFile("launchctl", ["print", `${target}/${LABEL}`]
|
|
66
|
+
await execFile("launchctl", ["print", `${target}/${LABEL}`], {
|
|
67
|
+
timeout: timeoutMs,
|
|
68
|
+
});
|
|
69
|
+
return true;
|
|
58
70
|
}
|
|
59
71
|
catch (error) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
details.stderr?.includes("Could not find service"))
|
|
63
|
-
return;
|
|
72
|
+
if (serviceMissing(error))
|
|
73
|
+
return false;
|
|
64
74
|
throw error;
|
|
65
75
|
}
|
|
66
|
-
|
|
76
|
+
}
|
|
77
|
+
async function bootoutIfLoaded(target) {
|
|
78
|
+
if (!(await serviceLoaded(target)))
|
|
79
|
+
return false;
|
|
80
|
+
await execFile("launchctl", ["bootout", `${target}/${LABEL}`], {
|
|
81
|
+
timeout: LAUNCHCTL_TIMEOUT_MS,
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function transientBootstrapFailure(error) {
|
|
86
|
+
const details = error;
|
|
87
|
+
return (String(details.code) === "5" ||
|
|
88
|
+
/^Bootstrap failed:\s*5\b/m.test(details.stderr ?? ""));
|
|
89
|
+
}
|
|
90
|
+
function restartTimeoutError(lastBootstrapFailure) {
|
|
91
|
+
return new Error(lastBootstrapFailure === undefined
|
|
92
|
+
? "The previous Codex collector did not stop within 10 seconds."
|
|
93
|
+
: "launchctl bootstrap still failed with error 5 after 10 seconds.", { cause: lastBootstrapFailure });
|
|
94
|
+
}
|
|
95
|
+
export async function bootstrapAfterBootout(wasLoaded, control) {
|
|
96
|
+
if (!wasLoaded) {
|
|
97
|
+
await control.bootstrap(LAUNCHCTL_TIMEOUT_MS);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const deadline = control.now() + RESTART_TIMEOUT_MS;
|
|
101
|
+
let lastBootstrapFailure;
|
|
102
|
+
while (true) {
|
|
103
|
+
let remaining = deadline - control.now();
|
|
104
|
+
if (remaining <= 0) {
|
|
105
|
+
throw restartTimeoutError(lastBootstrapFailure);
|
|
106
|
+
}
|
|
107
|
+
if (!(await control.isLoaded(Math.min(remaining, LAUNCHCTL_TIMEOUT_MS)))) {
|
|
108
|
+
remaining = deadline - control.now();
|
|
109
|
+
if (remaining <= 0)
|
|
110
|
+
continue;
|
|
111
|
+
try {
|
|
112
|
+
await control.bootstrap(Math.min(remaining, LAUNCHCTL_TIMEOUT_MS));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (!transientBootstrapFailure(error))
|
|
117
|
+
throw error;
|
|
118
|
+
lastBootstrapFailure = error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
remaining = deadline - control.now();
|
|
122
|
+
if (remaining > 0) {
|
|
123
|
+
await control.wait(Math.min(remaining, RESTART_POLL_MS));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
67
126
|
}
|
|
68
127
|
export async function installLaunchAgent(dataDirectory) {
|
|
69
128
|
if (process.platform !== "darwin") {
|
|
@@ -77,9 +136,20 @@ export async function installLaunchAgent(dataDirectory) {
|
|
|
77
136
|
await fs.writeFile(file, launchAgentPlist(undefined, undefined, undefined, dataDirectory ??
|
|
78
137
|
path.join(os.homedir(), ".config", "iteration", "codex-collector")), { encoding: "utf8", mode: 0o644 });
|
|
79
138
|
const target = launchctlTarget();
|
|
80
|
-
await bootoutIfLoaded(target);
|
|
81
|
-
await
|
|
82
|
-
|
|
139
|
+
const wasLoaded = await bootoutIfLoaded(target);
|
|
140
|
+
await bootstrapAfterBootout(wasLoaded, {
|
|
141
|
+
isLoaded: (timeoutMs) => serviceLoaded(target, timeoutMs),
|
|
142
|
+
bootstrap: async (timeoutMs) => {
|
|
143
|
+
await execFile("launchctl", ["bootstrap", target, file], {
|
|
144
|
+
timeout: timeoutMs,
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
wait: sleep,
|
|
148
|
+
now: Date.now,
|
|
149
|
+
});
|
|
150
|
+
await execFile("launchctl", ["kickstart", "-p", `${target}/${LABEL}`], {
|
|
151
|
+
timeout: LAUNCHCTL_TIMEOUT_MS,
|
|
152
|
+
});
|
|
83
153
|
return file;
|
|
84
154
|
}
|
|
85
155
|
export async function uninstallLaunchAgent() {
|