@allocator-one/harness-cli 1.16.0 → 1.17.1
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 +123 -0
- package/dist/{chunk-FQWV5MIC.js → chunk-5THYIKLW.js} +26 -4
- package/dist/chunk-67NX7MJ2.js +563 -0
- package/dist/chunk-BHBEIT5I.js +1348 -0
- package/dist/{chunk-GVTZKAXC.js → chunk-CEEQW6ZV.js} +299 -1592
- package/dist/chunk-WSDFLMO6.js +717 -0
- package/dist/chunk-XA5UDNJI.js +1448 -0
- package/dist/cli.js +1827 -1146
- package/dist/infraone-7TAHY4RN.js +593 -0
- package/dist/infraone-SLZXBJCV.js +22133 -0
- package/dist/infraone.js +372 -0
- package/dist/{login-page-KGEJFFXR.js → login-page-MFHNVTUV.js} +1 -1
- package/dist/{server-F6YRKNC2.js → server-TRXBTJSA.js} +11 -9
- package/package.json +4 -3
- package/skills/harness-code/SKILL.md +37 -0
- package/spec/openapi.json +3411 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveCacheHome
|
|
4
|
+
} from "./chunk-BHBEIT5I.js";
|
|
5
|
+
import {
|
|
6
|
+
CLI_VERSION,
|
|
7
|
+
OPERATOR_PACKAGE,
|
|
8
|
+
mountOperatorUnderInfraone,
|
|
9
|
+
named,
|
|
10
|
+
operatorMounted
|
|
11
|
+
} from "./chunk-5THYIKLW.js";
|
|
12
|
+
|
|
13
|
+
// src/core/stdio.ts
|
|
14
|
+
var BROKEN_PIPE_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED"]);
|
|
15
|
+
function guardBrokenPipes(streams = [process.stdout, process.stderr]) {
|
|
16
|
+
for (const stream of streams) {
|
|
17
|
+
stream.on("error", (error) => {
|
|
18
|
+
if (!BROKEN_PIPE_CODES.has(error.code ?? "")) throw error;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/core/upgrade.ts
|
|
24
|
+
import { spawn, spawnSync } from "child_process";
|
|
25
|
+
import {
|
|
26
|
+
accessSync,
|
|
27
|
+
constants,
|
|
28
|
+
existsSync,
|
|
29
|
+
linkSync,
|
|
30
|
+
mkdirSync,
|
|
31
|
+
readFileSync,
|
|
32
|
+
realpathSync,
|
|
33
|
+
renameSync,
|
|
34
|
+
rmSync,
|
|
35
|
+
statSync,
|
|
36
|
+
writeFileSync
|
|
37
|
+
} from "fs";
|
|
38
|
+
import { basename, dirname, join, parse, sep } from "path";
|
|
39
|
+
import { fileURLToPath } from "url";
|
|
40
|
+
var PACKAGE_NAME = "@allocator-one/harness-cli";
|
|
41
|
+
var PACKAGE_NAMES = [PACKAGE_NAME, OPERATOR_PACKAGE];
|
|
42
|
+
var UPGRADE_INSTALL_SPEC = `${PACKAGE_NAME}@latest`;
|
|
43
|
+
function upgradeInstallSpec(install) {
|
|
44
|
+
return `${install.packageName ?? PACKAGE_NAME}@latest`;
|
|
45
|
+
}
|
|
46
|
+
var UPDATE_CHECK_COMMAND = "__update-check";
|
|
47
|
+
var REEXEC_SENTINEL = "HARNESS_UPGRADED";
|
|
48
|
+
function latestUrl(packageName = PACKAGE_NAME) {
|
|
49
|
+
return `https://registry.npmjs.org/${packageName}/latest`;
|
|
50
|
+
}
|
|
51
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
52
|
+
var REGISTRY_TIMEOUT_MS = 5e3;
|
|
53
|
+
var NPM_ROOT_TIMEOUT_MS = 15e3;
|
|
54
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
55
|
+
var LOCK_STALE_MS = NPM_ROOT_TIMEOUT_MS + INSTALL_TIMEOUT_MS + 6e4;
|
|
56
|
+
function detectInstall(from = fileURLToPath(import.meta.url)) {
|
|
57
|
+
const unknown = {
|
|
58
|
+
kind: "unknown",
|
|
59
|
+
packageRoot: null,
|
|
60
|
+
nodeModules: null
|
|
61
|
+
};
|
|
62
|
+
if (from.split(sep).includes("_npx")) return { ...unknown, kind: "npx" };
|
|
63
|
+
const found = findPackageRoot(from);
|
|
64
|
+
if (found === null) return unknown;
|
|
65
|
+
const { packageRoot, packageName } = found;
|
|
66
|
+
if (existsSync(join(packageRoot, ".git"))) {
|
|
67
|
+
return { kind: "checkout", packageRoot, packageName, nodeModules: null };
|
|
68
|
+
}
|
|
69
|
+
const nodeModules = enclosingNodeModules(packageRoot);
|
|
70
|
+
if (nodeModules === null) return { ...unknown, packageRoot, packageName };
|
|
71
|
+
const kind = existsSync(join(nodeModules, ".bin")) ? "local" : "global";
|
|
72
|
+
return { kind, packageRoot, packageName, nodeModules };
|
|
73
|
+
}
|
|
74
|
+
function findPackageRoot(from) {
|
|
75
|
+
let dir = dirname(from);
|
|
76
|
+
const { root } = parse(dir);
|
|
77
|
+
for (; ; ) {
|
|
78
|
+
const name = packageNameAt(join(dir, "package.json"));
|
|
79
|
+
if (name !== null && PACKAGE_NAMES.includes(name))
|
|
80
|
+
return { packageRoot: dir, packageName: name };
|
|
81
|
+
if (dir === root) return null;
|
|
82
|
+
dir = dirname(dir);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function packageNameAt(path) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
88
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
89
|
+
const name = parsed["name"];
|
|
90
|
+
return typeof name === "string" ? name : null;
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function versionAt(packageRoot) {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(
|
|
98
|
+
readFileSync(join(packageRoot, "package.json"), "utf8")
|
|
99
|
+
);
|
|
100
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
101
|
+
const version = parsed["version"];
|
|
102
|
+
return typeof version === "string" ? version : null;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function enclosingNodeModules(packageRoot) {
|
|
108
|
+
let dir = dirname(packageRoot);
|
|
109
|
+
const { root } = parse(dir);
|
|
110
|
+
for (; ; ) {
|
|
111
|
+
if (basename(dir) === "node_modules") return dir;
|
|
112
|
+
if (dir === root) return null;
|
|
113
|
+
dir = dirname(dir);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
var SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
117
|
+
function parseVersion(value) {
|
|
118
|
+
const match = SEMVER.exec(value.trim());
|
|
119
|
+
if (match === null) return null;
|
|
120
|
+
return {
|
|
121
|
+
parts: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
122
|
+
pre: match[4] ?? null
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function isNewer(candidate, current) {
|
|
126
|
+
const a = parseVersion(candidate);
|
|
127
|
+
const b = parseVersion(current);
|
|
128
|
+
if (a === null || b === null) return false;
|
|
129
|
+
for (let index = 0; index < 3; index++) {
|
|
130
|
+
if (a.parts[index] !== b.parts[index])
|
|
131
|
+
return a.parts[index] > b.parts[index];
|
|
132
|
+
}
|
|
133
|
+
if (a.pre === b.pre) return false;
|
|
134
|
+
if (a.pre === null) return true;
|
|
135
|
+
if (b.pre === null) return false;
|
|
136
|
+
return comparePrerelease(a.pre, b.pre) > 0;
|
|
137
|
+
}
|
|
138
|
+
function comparePrerelease(left, right) {
|
|
139
|
+
const a = left.split(".");
|
|
140
|
+
const b = right.split(".");
|
|
141
|
+
for (let index = 0; index < Math.max(a.length, b.length); index++) {
|
|
142
|
+
const x = a[index];
|
|
143
|
+
const y = b[index];
|
|
144
|
+
if (x === void 0) return -1;
|
|
145
|
+
if (y === void 0) return 1;
|
|
146
|
+
const xNumeric = /^\d+$/.test(x);
|
|
147
|
+
const yNumeric = /^\d+$/.test(y);
|
|
148
|
+
if (xNumeric && yNumeric) {
|
|
149
|
+
if (Number(x) !== Number(y)) return Number(x) < Number(y) ? -1 : 1;
|
|
150
|
+
} else if (xNumeric !== yNumeric) {
|
|
151
|
+
return xNumeric ? -1 : 1;
|
|
152
|
+
} else if (x !== y) {
|
|
153
|
+
return x < y ? -1 : 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
function stampPath(env = process.env, packageName = PACKAGE_NAME) {
|
|
159
|
+
const file = packageName === PACKAGE_NAME ? "update-check.json" : `update-check-${packageName.replace(/^@[^/]+\//, "")}.json`;
|
|
160
|
+
return join(resolveCacheHome(env), "harness", file);
|
|
161
|
+
}
|
|
162
|
+
function readStamp(env = process.env, packageName = PACKAGE_NAME) {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(
|
|
165
|
+
readFileSync(stampPath(env, packageName), "utf8")
|
|
166
|
+
);
|
|
167
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
168
|
+
const record = parsed;
|
|
169
|
+
const checkedAt = record["checkedAt"];
|
|
170
|
+
if (typeof checkedAt !== "number" || !Number.isFinite(checkedAt))
|
|
171
|
+
return null;
|
|
172
|
+
const latest = record["latest"];
|
|
173
|
+
return { checkedAt, latest: typeof latest === "string" ? latest : null };
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function writeStamp(stamp, env = process.env, packageName = PACKAGE_NAME) {
|
|
179
|
+
const path = stampPath(env, packageName);
|
|
180
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
181
|
+
const latest = stamp.latest ?? readStamp(env, packageName)?.latest ?? null;
|
|
182
|
+
try {
|
|
183
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
184
|
+
writeFileSync(temporary, JSON.stringify({ ...stamp, latest }) + "\n");
|
|
185
|
+
renameSync(temporary, path);
|
|
186
|
+
} catch {
|
|
187
|
+
try {
|
|
188
|
+
rmSync(temporary, { force: true });
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function updateCheck(options = {}) {
|
|
194
|
+
const env = options.env ?? process.env;
|
|
195
|
+
const now = options.now ?? Date.now();
|
|
196
|
+
if (!checksEnabled(env)) return null;
|
|
197
|
+
const install = options.install ?? detectInstall();
|
|
198
|
+
if (install.kind !== "global" && install.kind !== "local") return null;
|
|
199
|
+
const packageName = install.packageName ?? PACKAGE_NAME;
|
|
200
|
+
const stamp = readStamp(env, packageName);
|
|
201
|
+
if (stamp === null || now - stamp.checkedAt >= CHECK_INTERVAL_MS) {
|
|
202
|
+
writeStamp({ checkedAt: now, latest: stamp?.latest ?? null }, env, packageName);
|
|
203
|
+
(options.refresh ?? spawnRefresh)();
|
|
204
|
+
}
|
|
205
|
+
if (stamp !== null && stamp.latest !== null && isNewer(stamp.latest, CLI_VERSION)) {
|
|
206
|
+
return `harness: ${stamp.latest} is available (you have ${CLI_VERSION}) \u2014 ${upgradeHint(install)}
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function upgradeHint(install) {
|
|
212
|
+
return install.kind === "local" ? `npm i ${upgradeInstallSpec(install)}` : `npm i -g ${upgradeInstallSpec(install)}`;
|
|
213
|
+
}
|
|
214
|
+
function spawnRefresh() {
|
|
215
|
+
const self = process.argv[1];
|
|
216
|
+
if (self === void 0 || self === "") return;
|
|
217
|
+
try {
|
|
218
|
+
const child = spawn(process.execPath, [self, UPDATE_CHECK_COMMAND], {
|
|
219
|
+
detached: true,
|
|
220
|
+
stdio: "ignore"
|
|
221
|
+
});
|
|
222
|
+
child.on("error", () => {
|
|
223
|
+
});
|
|
224
|
+
child.unref();
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function performUpdateCheck(env = process.env, now = Date.now(), packageName = detectInstall().packageName ?? PACKAGE_NAME) {
|
|
229
|
+
if (!checksEnabled(env)) return;
|
|
230
|
+
writeStamp(
|
|
231
|
+
{ checkedAt: now, latest: await fetchLatestVersion(env, packageName) },
|
|
232
|
+
env,
|
|
233
|
+
packageName
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
async function fetchLatestVersion(env, packageName) {
|
|
237
|
+
const configured = env["HARNESS_REGISTRY_URL"];
|
|
238
|
+
const url = configured !== void 0 && configured.trim() !== "" ? configured.trim() : latestUrl(packageName);
|
|
239
|
+
try {
|
|
240
|
+
const response = await fetch(url, {
|
|
241
|
+
headers: { accept: "application/json" },
|
|
242
|
+
signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS)
|
|
243
|
+
});
|
|
244
|
+
if (!response.ok) return null;
|
|
245
|
+
const parsed = await response.json();
|
|
246
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
247
|
+
const version = parsed["version"];
|
|
248
|
+
if (typeof version !== "string" || parseVersion(version) === null)
|
|
249
|
+
return null;
|
|
250
|
+
return version;
|
|
251
|
+
} catch {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function selfHeal(options = {}) {
|
|
256
|
+
const env = options.env ?? process.env;
|
|
257
|
+
const argv = options.argv ?? process.argv;
|
|
258
|
+
if (truthy(env[REEXEC_SENTINEL])) {
|
|
259
|
+
return {
|
|
260
|
+
kind: "skipped",
|
|
261
|
+
reason: "already upgraded once for this command"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (!autoUpgradeEnabled(env))
|
|
265
|
+
return { kind: "skipped", reason: "automatic upgrades are off" };
|
|
266
|
+
if (argv[2] === "mcp")
|
|
267
|
+
return {
|
|
268
|
+
kind: "skipped",
|
|
269
|
+
reason: "an MCP session must not be replaced mid-stream"
|
|
270
|
+
};
|
|
271
|
+
const install = options.install ?? detectInstall();
|
|
272
|
+
if (install.kind !== "global" || install.packageRoot === null) {
|
|
273
|
+
return { kind: "skipped", reason: describeInstall(install.kind) };
|
|
274
|
+
}
|
|
275
|
+
const packageRoot = install.packageRoot;
|
|
276
|
+
const installed = versionAt(packageRoot);
|
|
277
|
+
if (installed !== null && isNewer(installed, CLI_VERSION)) {
|
|
278
|
+
return { kind: "upgraded", code: reexec(options, env, argv) };
|
|
279
|
+
}
|
|
280
|
+
const lock = takeLock(env);
|
|
281
|
+
if (lock === null)
|
|
282
|
+
return {
|
|
283
|
+
kind: "skipped",
|
|
284
|
+
reason: "another harness process is already upgrading"
|
|
285
|
+
};
|
|
286
|
+
try {
|
|
287
|
+
const stillHeld = () => readHolder(lock.path)?.pid === lock.pid;
|
|
288
|
+
const result = (options.runInstall ?? npmInstallGlobal)(install, stillHeld);
|
|
289
|
+
if (!result.ok) return { kind: "failed", reason: result.detail };
|
|
290
|
+
const after = versionAt(packageRoot);
|
|
291
|
+
if (after !== null && after === installed) {
|
|
292
|
+
return {
|
|
293
|
+
kind: "failed",
|
|
294
|
+
reason: `still ${after} after upgrading \u2014 no newer release is published`
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (after !== null && installed !== null && !isNewer(after, installed)) {
|
|
298
|
+
return {
|
|
299
|
+
kind: "failed",
|
|
300
|
+
reason: `npm left ${after} installed, which is not newer than ${installed}`
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
} finally {
|
|
304
|
+
releaseLock(lock);
|
|
305
|
+
}
|
|
306
|
+
return { kind: "upgraded", code: reexec(options, env, argv) };
|
|
307
|
+
}
|
|
308
|
+
function reexec(options, env, argv) {
|
|
309
|
+
return (options.reexec ?? reexecSelf)(
|
|
310
|
+
{ ...env, [REEXEC_SENTINEL]: "1" },
|
|
311
|
+
argv
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
function describeInstall(kind) {
|
|
315
|
+
switch (kind) {
|
|
316
|
+
case "checkout":
|
|
317
|
+
return "this is a git checkout, not an npm install";
|
|
318
|
+
case "local":
|
|
319
|
+
return "this copy is a project dependency, not a global install";
|
|
320
|
+
case "npx":
|
|
321
|
+
return "npx runs a throwaway copy";
|
|
322
|
+
default:
|
|
323
|
+
return "cannot tell how this CLI was installed";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function npmInstallGlobal(install, stillHeld) {
|
|
327
|
+
const globalRoot = npmGlobalRoot();
|
|
328
|
+
if (globalRoot === null)
|
|
329
|
+
return { ok: false, detail: "npm did not say where its global root is" };
|
|
330
|
+
if (globalRoot.includes("***")) {
|
|
331
|
+
return {
|
|
332
|
+
ok: false,
|
|
333
|
+
detail: "npm redacted its own global root, so this copy's location cannot be confirmed"
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (install.nodeModules === null || !samePath(globalRoot, install.nodeModules)) {
|
|
337
|
+
return {
|
|
338
|
+
ok: false,
|
|
339
|
+
detail: `this copy lives in ${install.nodeModules ?? "an unrecognized place"}, not npm's global root (${globalRoot})`
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
accessSync(globalRoot, constants.W_OK);
|
|
344
|
+
} catch {
|
|
345
|
+
return {
|
|
346
|
+
ok: false,
|
|
347
|
+
detail: `${globalRoot} is not writable \u2014 upgrade it yourself`
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (!stillHeld()) {
|
|
351
|
+
return {
|
|
352
|
+
ok: false,
|
|
353
|
+
detail: "lost the upgrade lock while preparing \u2014 another process took over"
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
process.stderr.write(
|
|
357
|
+
named(`harness: upgrading ${install.packageName ?? PACKAGE_NAME} \u2026
|
|
358
|
+
`)
|
|
359
|
+
);
|
|
360
|
+
const args = [
|
|
361
|
+
"install",
|
|
362
|
+
"--global",
|
|
363
|
+
"--ignore-scripts",
|
|
364
|
+
"--no-audit",
|
|
365
|
+
"--no-fund",
|
|
366
|
+
"--no-update-notifier",
|
|
367
|
+
"--loglevel=error",
|
|
368
|
+
upgradeInstallSpec(install)
|
|
369
|
+
];
|
|
370
|
+
const result = spawnSync(npmCommand(), args, {
|
|
371
|
+
// npm's diagnostics belong on stderr with ours; stdout stays the
|
|
372
|
+
// command's own, so its summary is dropped rather than piped into jq.
|
|
373
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
374
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
375
|
+
...npmSpawnOptions()
|
|
376
|
+
});
|
|
377
|
+
if (result.error !== void 0)
|
|
378
|
+
return { ok: false, detail: result.error.message };
|
|
379
|
+
if (result.status !== 0) {
|
|
380
|
+
return {
|
|
381
|
+
ok: false,
|
|
382
|
+
detail: `npm install exited ${result.status ?? `on ${String(result.signal)}`}`
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return { ok: true, detail: "" };
|
|
386
|
+
}
|
|
387
|
+
function npmGlobalRoot() {
|
|
388
|
+
try {
|
|
389
|
+
const result = spawnSync(npmCommand(), ["root", "--global"], {
|
|
390
|
+
encoding: "utf8",
|
|
391
|
+
timeout: NPM_ROOT_TIMEOUT_MS,
|
|
392
|
+
...npmSpawnOptions()
|
|
393
|
+
});
|
|
394
|
+
if (result.status !== 0 || typeof result.stdout !== "string") return null;
|
|
395
|
+
const path = result.stdout.trim();
|
|
396
|
+
return path === "" ? null : path;
|
|
397
|
+
} catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function npmCommand() {
|
|
402
|
+
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
403
|
+
}
|
|
404
|
+
function npmSpawnOptions() {
|
|
405
|
+
return process.platform === "win32" ? { shell: true } : {};
|
|
406
|
+
}
|
|
407
|
+
function samePath(left, right) {
|
|
408
|
+
const resolve = (path) => {
|
|
409
|
+
try {
|
|
410
|
+
return realpathSync(path);
|
|
411
|
+
} catch {
|
|
412
|
+
return path;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
return resolve(left) === resolve(right);
|
|
416
|
+
}
|
|
417
|
+
function reexecSelf(env, argv) {
|
|
418
|
+
const self = argv[1];
|
|
419
|
+
if (self === void 0) return 1;
|
|
420
|
+
const result = spawnSync(process.execPath, [self, ...argv.slice(2)], {
|
|
421
|
+
stdio: "inherit",
|
|
422
|
+
env
|
|
423
|
+
});
|
|
424
|
+
if (result.error !== void 0) {
|
|
425
|
+
process.stderr.write(
|
|
426
|
+
named(`harness: upgraded, but re-running failed: ${result.error.message}
|
|
427
|
+
`)
|
|
428
|
+
);
|
|
429
|
+
return 1;
|
|
430
|
+
}
|
|
431
|
+
return result.status ?? 1;
|
|
432
|
+
}
|
|
433
|
+
function takeLock(env) {
|
|
434
|
+
const path = join(resolveCacheHome(env), "harness", "upgrade.lock");
|
|
435
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
436
|
+
try {
|
|
437
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
438
|
+
writeFileSync(temporary, `${process.pid}
|
|
439
|
+
`);
|
|
440
|
+
} catch {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
445
|
+
try {
|
|
446
|
+
linkSync(temporary, path);
|
|
447
|
+
return { path, pid: process.pid };
|
|
448
|
+
} catch {
|
|
449
|
+
const holder = readHolder(path);
|
|
450
|
+
if (holder === null) continue;
|
|
451
|
+
const aged = Date.now() - holder.mtimeMs > LOCK_STALE_MS;
|
|
452
|
+
const abandoned = holder.pid === null ? aged : !running(holder.pid) || aged;
|
|
453
|
+
if (!abandoned) return null;
|
|
454
|
+
try {
|
|
455
|
+
rmSync(path, { force: true });
|
|
456
|
+
} catch {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return null;
|
|
462
|
+
} finally {
|
|
463
|
+
try {
|
|
464
|
+
rmSync(temporary, { force: true });
|
|
465
|
+
} catch {
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function releaseLock(lock) {
|
|
470
|
+
try {
|
|
471
|
+
if (readHolder(lock.path)?.pid !== lock.pid) return;
|
|
472
|
+
rmSync(lock.path, { force: true });
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function readHolder(path) {
|
|
477
|
+
try {
|
|
478
|
+
const mtimeMs = statSync(path).mtimeMs;
|
|
479
|
+
const pid = Number.parseInt(readFileSync(path, "utf8").trim(), 10);
|
|
480
|
+
return { pid: Number.isInteger(pid) && pid > 0 ? pid : null, mtimeMs };
|
|
481
|
+
} catch {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function running(pid) {
|
|
486
|
+
if (pid === null) return false;
|
|
487
|
+
try {
|
|
488
|
+
process.kill(pid, 0);
|
|
489
|
+
return true;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
return error.code === "EPERM";
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function truthy(value) {
|
|
495
|
+
if (value === void 0) return false;
|
|
496
|
+
const normalized = value.trim().toLowerCase();
|
|
497
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
498
|
+
}
|
|
499
|
+
function checksEnabled(env) {
|
|
500
|
+
return !truthy(env["CI"]) && !truthy(env["HARNESS_NO_UPDATE_CHECK"]);
|
|
501
|
+
}
|
|
502
|
+
function autoUpgradeEnabled(env) {
|
|
503
|
+
if (!checksEnabled(env)) return false;
|
|
504
|
+
const explicit = env["HARNESS_AUTO_UPGRADE"];
|
|
505
|
+
if (explicit !== void 0 && explicit.trim() !== "") return truthy(explicit);
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/commands/operator-mount.ts
|
|
510
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
511
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
512
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
513
|
+
async function runOperatorMount(args) {
|
|
514
|
+
mountOperatorUnderInfraone();
|
|
515
|
+
process.argv = [process.execPath, fileURLToPath2(new URL("./cli.js", import.meta.url)), ...args];
|
|
516
|
+
await import("./cli.js");
|
|
517
|
+
return process.exit(0);
|
|
518
|
+
}
|
|
519
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
520
|
+
function deprecationStampPath(env = process.env) {
|
|
521
|
+
return join2(resolveCacheHome(env), "harness", "operator-deprecation.json");
|
|
522
|
+
}
|
|
523
|
+
function operatorDeprecationNotice(options = {}) {
|
|
524
|
+
const env = options.env ?? process.env;
|
|
525
|
+
const now = options.now ?? Date.now();
|
|
526
|
+
if (operatorMounted() || !checksEnabled(env)) return null;
|
|
527
|
+
const path = deprecationStampPath(env);
|
|
528
|
+
const shownAt = readShownAt(path);
|
|
529
|
+
if (shownAt !== null && now - shownAt < DAY_MS && now >= shownAt) return null;
|
|
530
|
+
writeShownAt(path, now);
|
|
531
|
+
return "operator: the operator command is now `infraone operator`, with the same commands and login (npm i -g @allocator-one/infraone); operator keeps working for now\n";
|
|
532
|
+
}
|
|
533
|
+
function readShownAt(path) {
|
|
534
|
+
try {
|
|
535
|
+
const shownAt = JSON.parse(readFileSync2(path, "utf8"))["shownAt"];
|
|
536
|
+
return typeof shownAt === "number" && Number.isFinite(shownAt) ? shownAt : null;
|
|
537
|
+
} catch {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function writeShownAt(path, now) {
|
|
542
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
543
|
+
try {
|
|
544
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
545
|
+
writeFileSync2(temporary, JSON.stringify({ shownAt: now }) + "\n");
|
|
546
|
+
renameSync2(temporary, path);
|
|
547
|
+
} catch {
|
|
548
|
+
try {
|
|
549
|
+
rmSync2(temporary, { force: true });
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export {
|
|
556
|
+
guardBrokenPipes,
|
|
557
|
+
UPDATE_CHECK_COMMAND,
|
|
558
|
+
updateCheck,
|
|
559
|
+
performUpdateCheck,
|
|
560
|
+
selfHeal,
|
|
561
|
+
runOperatorMount,
|
|
562
|
+
operatorDeprecationNotice
|
|
563
|
+
};
|