@vtxmacro/cli 2026.9.51 → 2026.9.52
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 +5 -3
- package/bin/vtx-service-bootstrap.js +118 -44
- package/bin/vtx-service-launcher.js +1389 -0
- package/bin/vtx.js +436 -293
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,9 +21,11 @@ service start, and periodically while running, it checks the official npm regist
|
|
|
21
21
|
and verifies the exact release integrity and installed CLI version in a new
|
|
22
22
|
versioned directory. Before switching, it stops new admission and lets active
|
|
23
23
|
Provider calls and Agent turns finish while keeping heartbeats alive. It selects
|
|
24
|
-
the candidate only after every configured worker reports ready. A
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
the candidate only after every configured worker reports ready. A stable recovery
|
|
25
|
+
launcher starts the versioned updater and keeps the previous working release.
|
|
26
|
+
Routine updates do not replace this recovery launcher. If activation fails or
|
|
27
|
+
the updated process exits unexpectedly, it rejects that release and restores the
|
|
28
|
+
previous runtime. The service keeps the active release and its recovery release. If the update check is
|
|
27
29
|
unavailable, it keeps the last verified runtime. An intentionally stopped
|
|
28
30
|
service stays stopped and makes no update request. Hosts installed before this
|
|
29
31
|
private launcher need one final normal npm update and service reinstall to
|
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
import { spawn as spawn2 } from "node:child_process";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import {
|
|
5
|
-
access,
|
|
5
|
+
access as access2,
|
|
6
6
|
appendFile,
|
|
7
7
|
mkdir as mkdir2,
|
|
8
|
-
readFile as
|
|
8
|
+
readFile as readFile3,
|
|
9
9
|
readdir,
|
|
10
10
|
rename as rename2,
|
|
11
|
-
rm as
|
|
11
|
+
rm as rm3
|
|
12
12
|
} from "node:fs/promises";
|
|
13
|
-
import { dirname as
|
|
13
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3, relative as relative3, resolve as resolve3, win32 as windowsPath2 } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
|
|
16
16
|
// agent-cli-release.json
|
|
17
17
|
var agent_cli_release_default = {
|
|
18
18
|
package_name: "@vtxmacro/cli",
|
|
19
|
-
package_version: "2026.9.
|
|
19
|
+
package_version: "2026.9.52",
|
|
20
20
|
codex_package_name: "@openai/codex",
|
|
21
21
|
codex_version: "0.153.3",
|
|
22
22
|
copilot_sdk_package_name: "@github/copilot-sdk",
|
|
@@ -207,6 +207,8 @@ var runtime_policy_generated_default = {
|
|
|
207
207
|
service: {
|
|
208
208
|
update_check_interval_ms: 3e5,
|
|
209
209
|
update_registry_timeout_ms: 15e3,
|
|
210
|
+
bootstrap_readiness_timeout_ms: 6e5,
|
|
211
|
+
bootstrap_stop_timeout_ms: 1e4,
|
|
210
212
|
startup_max_attempts: 40,
|
|
211
213
|
startup_poll_interval_ms: 250,
|
|
212
214
|
restart_stable_uptime_ms: 6e4,
|
|
@@ -289,6 +291,10 @@ var runtime_policy_generated_default = {
|
|
|
289
291
|
}
|
|
290
292
|
};
|
|
291
293
|
|
|
294
|
+
// lib/inference-host/service-launcher-state.ts
|
|
295
|
+
import { access, readFile as readFile2, rm as rm2 } from "node:fs/promises";
|
|
296
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
297
|
+
|
|
292
298
|
// lib/inference-host/config.ts
|
|
293
299
|
import { randomBytes } from "node:crypto";
|
|
294
300
|
import { spawn } from "node:child_process";
|
|
@@ -899,7 +905,7 @@ var WINDOWS_PRIVATE_FILE_REPLACE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
899
905
|
async function replaceAtomicInferencePrivateFile(temporaryPath, destinationPath, options = {}) {
|
|
900
906
|
const platform = options.platform ?? process.platform;
|
|
901
907
|
const renameFile = options.renameFile ?? rename;
|
|
902
|
-
const sleep = options.sleep ?? ((milliseconds) => new Promise((
|
|
908
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve4) => setTimeout(resolve4, milliseconds)));
|
|
903
909
|
const maxAttempts = Math.max(1, options.maxAttempts ?? 40);
|
|
904
910
|
const retryDelayMs = Math.max(0, options.retryDelayMs ?? 25);
|
|
905
911
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
@@ -1150,19 +1156,58 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
|
|
|
1150
1156
|
};
|
|
1151
1157
|
}
|
|
1152
1158
|
|
|
1159
|
+
// lib/inference-host/service-launcher-state.ts
|
|
1160
|
+
var launcherSibling = (manifest, suffix) => {
|
|
1161
|
+
if (!manifest.endsWith(".service.json")) throw new Error("Invalid service manifest path.");
|
|
1162
|
+
return `${manifest.slice(0, -".service.json".length)}${suffix}`;
|
|
1163
|
+
};
|
|
1164
|
+
var within = (parent, child) => {
|
|
1165
|
+
const part = relative2(resolve2(parent), resolve2(child));
|
|
1166
|
+
return Boolean(part) && !part.startsWith("..") && !isAbsolute2(part);
|
|
1167
|
+
};
|
|
1168
|
+
var launcherSelection = (manifest, value) => {
|
|
1169
|
+
if (!value || typeof value !== "object") return null;
|
|
1170
|
+
const r = value;
|
|
1171
|
+
if (r.schema_version !== "vtx_inference_service_release_v1" || r.package_name !== "@vtxmacro/cli" || typeof r.package_version !== "string" || !/^\d{4}\.\d{1,2}\.\d{1,3}$/u.test(r.package_version) || typeof r.integrity !== "string" || typeof r.selected_at !== "string" || !Number.isFinite(Date.parse(r.selected_at)) || typeof r.script !== "string" || !isAbsolute2(r.script) || /[\r\n\0]/u.test(r.script) || !within(launcherSibling(manifest, ".service-releases"), r.script)) return null;
|
|
1172
|
+
return r;
|
|
1173
|
+
};
|
|
1174
|
+
var readLauncherState = async (manifest) => {
|
|
1175
|
+
let value;
|
|
1176
|
+
try {
|
|
1177
|
+
value = JSON.parse(await readFile2(launcherSibling(manifest, ".service-launcher.json"), "utf8"));
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
if (error.code === "ENOENT") return null;
|
|
1180
|
+
throw error;
|
|
1181
|
+
}
|
|
1182
|
+
if (!value || typeof value !== "object") throw new Error("Invalid service launcher state.");
|
|
1183
|
+
const r = value;
|
|
1184
|
+
const target = (v) => {
|
|
1185
|
+
if (!v || typeof v !== "object") return false;
|
|
1186
|
+
const t = v;
|
|
1187
|
+
return typeof t.package_version === "string" && /^\d{4}\.\d{1,2}\.\d{1,3}$/u.test(t.package_version) && typeof t.script === "string" && isAbsolute2(t.script) && !/[\r\n\0]/u.test(t.script) && (within(launcherSibling(manifest, ".service-releases"), t.script) || resolve2(t.script) === resolve2(launcherSibling(manifest, ".service-bootstrap.previous.js"))) && (t.selection === null || (() => {
|
|
1188
|
+
const selected = launcherSelection(manifest, t.selection);
|
|
1189
|
+
return Boolean(selected && selected.package_version === t.package_version && (resolve2(t.script) === resolve2(join2(dirname2(selected.script), "vtx-service-bootstrap.js")) || resolve2(t.script) === resolve2(launcherSibling(manifest, ".service-bootstrap.previous.js"))));
|
|
1190
|
+
})());
|
|
1191
|
+
};
|
|
1192
|
+
if (r.schema_version !== "vtx_inference_service_launcher_v1" || !target(r.active) || r.previous !== null && !target(r.previous) || typeof r.pending !== "boolean") {
|
|
1193
|
+
throw new Error("Invalid service launcher recovery targets.");
|
|
1194
|
+
}
|
|
1195
|
+
return r;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1153
1198
|
// lib/inference-host/service-bootstrap.ts
|
|
1154
1199
|
var OFFICIAL_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
1155
1200
|
var PACKAGE_NAME = "@vtxmacro/cli";
|
|
1156
1201
|
var UPDATE_ATTEMPTS = 3;
|
|
1157
1202
|
var UPDATE_RETRY_MS = 2e3;
|
|
1158
|
-
var CANDIDATE_READINESS_TIMEOUT_MS =
|
|
1203
|
+
var CANDIDATE_READINESS_TIMEOUT_MS = runtime_policy_generated_default.external_inference.service.bootstrap_readiness_timeout_ms;
|
|
1159
1204
|
var SERVICE_DRAIN_COMMAND = "vtx-inference-host-service-drain-v1";
|
|
1160
1205
|
var SERVICE_QUIESCE_COMMAND = "vtx-inference-host-service-quiesce-v1";
|
|
1161
1206
|
var safePath = (value, label) => {
|
|
1162
1207
|
if (typeof value !== "string" || !value || /[\r\n\0]/u.test(value)) {
|
|
1163
1208
|
throw new Error(`Inference-host bootstrap ${label} is invalid.`);
|
|
1164
1209
|
}
|
|
1165
|
-
return
|
|
1210
|
+
return resolve3(value);
|
|
1166
1211
|
};
|
|
1167
1212
|
var parseManifest = (value) => {
|
|
1168
1213
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -1201,7 +1246,7 @@ var releasesPathFor = (manifestPath) => serviceSiblingPath(manifestPath, ".servi
|
|
|
1201
1246
|
var rejectionPathFor = (manifestPath) => serviceSiblingPath(manifestPath, ".service-release-rejection.json");
|
|
1202
1247
|
var runtimePathFor = (manifestPath) => serviceSiblingPath(manifestPath, ".service-runtime.json");
|
|
1203
1248
|
var readDesired = async (manifestPath) => {
|
|
1204
|
-
const value = JSON.parse(await
|
|
1249
|
+
const value = JSON.parse(await readFile3(desiredPathFor(manifestPath), "utf8"));
|
|
1205
1250
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1206
1251
|
throw new Error("Inference-host bootstrap desired state is invalid.");
|
|
1207
1252
|
}
|
|
@@ -1231,17 +1276,17 @@ var compareInferenceHostReleaseVersions = (left, right) => {
|
|
|
1231
1276
|
return 0;
|
|
1232
1277
|
};
|
|
1233
1278
|
var isPathWithin = (parent, child) => {
|
|
1234
|
-
const childRelative =
|
|
1235
|
-
return childRelative !== "" && !childRelative.startsWith("..") && !
|
|
1279
|
+
const childRelative = relative3(resolve3(parent), resolve3(child));
|
|
1280
|
+
return childRelative !== "" && !childRelative.startsWith("..") && !isAbsolute3(childRelative);
|
|
1236
1281
|
};
|
|
1237
1282
|
var parseSelection = async (manifestPath) => {
|
|
1238
1283
|
try {
|
|
1239
|
-
const value = JSON.parse(await
|
|
1284
|
+
const value = JSON.parse(await readFile3(selectionPathFor(manifestPath), "utf8"));
|
|
1240
1285
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1241
1286
|
const record = value;
|
|
1242
1287
|
const script = safePath(record.script, "selected script");
|
|
1243
1288
|
if (record.schema_version !== "vtx_inference_service_release_v1" || record.package_name !== PACKAGE_NAME || typeof record.package_version !== "string" || !parseVersion(record.package_version) || typeof record.integrity !== "string" || typeof record.selected_at !== "string" || !Number.isFinite(Date.parse(record.selected_at)) || !isPathWithin(releasesPathFor(manifestPath), script)) return null;
|
|
1244
|
-
await
|
|
1289
|
+
await access2(script);
|
|
1245
1290
|
return record;
|
|
1246
1291
|
} catch (error) {
|
|
1247
1292
|
if (error.code === "ENOENT") return null;
|
|
@@ -1250,7 +1295,7 @@ var parseSelection = async (manifestPath) => {
|
|
|
1250
1295
|
};
|
|
1251
1296
|
var parseRejection = async (manifestPath) => {
|
|
1252
1297
|
try {
|
|
1253
|
-
const value = JSON.parse(await
|
|
1298
|
+
const value = JSON.parse(await readFile3(rejectionPathFor(manifestPath), "utf8"));
|
|
1254
1299
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1255
1300
|
const record = value;
|
|
1256
1301
|
if (record.schema_version !== "vtx_inference_service_release_rejection_v1" || typeof record.package_version !== "string" || !parseVersion(record.package_version) || typeof record.rejected_at !== "string" || !Number.isFinite(Date.parse(record.rejected_at)) || typeof record.reason !== "string" || !/^[a-z0-9_]{1,96}$/u.test(record.reason)) return null;
|
|
@@ -1289,7 +1334,7 @@ var defaultRunCommand = async (command, args) => await new Promise((resolvePromi
|
|
|
1289
1334
|
}));
|
|
1290
1335
|
});
|
|
1291
1336
|
var runInferenceHostBootstrapNpmCommand = async (executable, args, platform = process.platform, runCommand = defaultRunCommand) => {
|
|
1292
|
-
if (platform !== "win32") return await runCommand(
|
|
1337
|
+
if (platform !== "win32") return await runCommand(join3(dirname3(executable), "npm"), args);
|
|
1293
1338
|
const npmScript = windowsPath2.join(
|
|
1294
1339
|
windowsPath2.dirname(executable),
|
|
1295
1340
|
"node_modules",
|
|
@@ -1328,7 +1373,7 @@ var defaultRunSupervisor = (executable, args) => {
|
|
|
1328
1373
|
};
|
|
1329
1374
|
};
|
|
1330
1375
|
var appendBootstrapLog = async (path, event, fields = {}) => {
|
|
1331
|
-
await mkdir2(
|
|
1376
|
+
await mkdir2(dirname3(path), { recursive: true, mode: 448 });
|
|
1332
1377
|
await appendFile(
|
|
1333
1378
|
path,
|
|
1334
1379
|
`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), event, ...fields })}
|
|
@@ -1363,7 +1408,7 @@ var verifyIntegrity = async (tarballPath, integrity) => {
|
|
|
1363
1408
|
}).filter((entry) => entry !== null);
|
|
1364
1409
|
const supported = ["sha512", "sha384", "sha256"].map((algorithm) => candidates.find((candidate) => candidate.algorithm === algorithm)).find(Boolean);
|
|
1365
1410
|
if (!supported) throw new Error("release_integrity_unsupported");
|
|
1366
|
-
const actual = createHash(supported.algorithm).update(await
|
|
1411
|
+
const actual = createHash(supported.algorithm).update(await readFile3(tarballPath)).digest("base64");
|
|
1367
1412
|
if (actual !== supported.digest) throw new Error("release_integrity_mismatch");
|
|
1368
1413
|
};
|
|
1369
1414
|
var atomicWriteSelection = async (manifestPath, selection) => {
|
|
@@ -1383,9 +1428,9 @@ var verifyInstalledRelease = async (prefix, targetVersion, executable, platform,
|
|
|
1383
1428
|
if (rootResult.exitCode !== 0 || !rootResult.stdout.trim()) {
|
|
1384
1429
|
throw new Error("release_install_invalid");
|
|
1385
1430
|
}
|
|
1386
|
-
const packagePath =
|
|
1431
|
+
const packagePath = resolve3(rootResult.stdout.trim(), PACKAGE_NAME);
|
|
1387
1432
|
if (!isPathWithin(prefix, packagePath)) throw new Error("release_install_invalid");
|
|
1388
|
-
const packageJson = JSON.parse(await
|
|
1433
|
+
const packageJson = JSON.parse(await readFile3(join3(packagePath, "package.json"), "utf8"));
|
|
1389
1434
|
if (!packageJson || typeof packageJson !== "object" || Array.isArray(packageJson)) {
|
|
1390
1435
|
throw new Error("release_install_invalid");
|
|
1391
1436
|
}
|
|
@@ -1393,9 +1438,9 @@ var verifyInstalledRelease = async (prefix, targetVersion, executable, platform,
|
|
|
1393
1438
|
const bin = packageRecord.bin;
|
|
1394
1439
|
const binPath = bin && typeof bin === "object" && !Array.isArray(bin) ? bin.vtx : null;
|
|
1395
1440
|
if (packageRecord.name !== PACKAGE_NAME || packageRecord.version !== targetVersion || typeof binPath !== "string") throw new Error("release_install_invalid");
|
|
1396
|
-
const script =
|
|
1441
|
+
const script = resolve3(packagePath, binPath);
|
|
1397
1442
|
if (!isPathWithin(packagePath, script)) throw new Error("release_install_invalid");
|
|
1398
|
-
await
|
|
1443
|
+
await access2(script);
|
|
1399
1444
|
const verified = await runCommand(executable, [script, "--version"]);
|
|
1400
1445
|
if (verified.exitCode !== 0 || verified.stdout.trim() !== targetVersion) {
|
|
1401
1446
|
throw new Error("release_version_verification_failed");
|
|
@@ -1406,19 +1451,19 @@ var cleanupInterruptedReleaseStaging = async (releasesPath) => {
|
|
|
1406
1451
|
const entries = await readdir(releasesPath, { withFileTypes: true });
|
|
1407
1452
|
for (const entry of entries) {
|
|
1408
1453
|
if (entry.isDirectory() && /^\.(?:staging|download)-\d{4}\.\d{1,2}\.\d{1,3}-[0-9a-f-]{36}$/u.test(entry.name)) {
|
|
1409
|
-
await
|
|
1454
|
+
await rm3(resolve3(releasesPath, entry.name), { recursive: true, force: true });
|
|
1410
1455
|
}
|
|
1411
1456
|
}
|
|
1412
1457
|
};
|
|
1413
1458
|
var stageRelease = async (manifestPath, target, executable, platform, runCommand, now) => {
|
|
1414
1459
|
const releasesPath = releasesPathFor(manifestPath);
|
|
1415
|
-
const finalPath =
|
|
1416
|
-
const stagingPath =
|
|
1417
|
-
const downloadPath =
|
|
1460
|
+
const finalPath = join3(releasesPath, target.version);
|
|
1461
|
+
const stagingPath = join3(releasesPath, `.staging-${target.version}-${randomUUID()}`);
|
|
1462
|
+
const downloadPath = join3(releasesPath, `.download-${target.version}-${randomUUID()}`);
|
|
1418
1463
|
await mkdir2(releasesPath, { recursive: true, mode: 448 });
|
|
1419
1464
|
await cleanupInterruptedReleaseStaging(releasesPath);
|
|
1420
1465
|
try {
|
|
1421
|
-
await
|
|
1466
|
+
await access2(finalPath);
|
|
1422
1467
|
const existingScript = await verifyInstalledRelease(
|
|
1423
1468
|
finalPath,
|
|
1424
1469
|
target.version,
|
|
@@ -1458,7 +1503,7 @@ var stageRelease = async (manifestPath, target, executable, platform, runCommand
|
|
|
1458
1503
|
} catch {
|
|
1459
1504
|
throw new Error("release_download_invalid");
|
|
1460
1505
|
}
|
|
1461
|
-
const tarballPath =
|
|
1506
|
+
const tarballPath = resolve3(downloadPath, filename);
|
|
1462
1507
|
if (!filename || !isPathWithin(downloadPath, tarballPath)) {
|
|
1463
1508
|
throw new Error("release_download_invalid");
|
|
1464
1509
|
}
|
|
@@ -1488,7 +1533,7 @@ var stageRelease = async (manifestPath, target, executable, platform, runCommand
|
|
|
1488
1533
|
if (error.code !== "EEXIST") throw error;
|
|
1489
1534
|
throw new Error("release_path_already_exists");
|
|
1490
1535
|
}
|
|
1491
|
-
const finalScript =
|
|
1536
|
+
const finalScript = resolve3(finalPath, relative3(stagingPath, stagedScript));
|
|
1492
1537
|
const selection = {
|
|
1493
1538
|
schema_version: "vtx_inference_service_release_v1",
|
|
1494
1539
|
package_name: PACKAGE_NAME,
|
|
@@ -1499,8 +1544,8 @@ var stageRelease = async (manifestPath, target, executable, platform, runCommand
|
|
|
1499
1544
|
};
|
|
1500
1545
|
return selection;
|
|
1501
1546
|
} finally {
|
|
1502
|
-
await
|
|
1503
|
-
await
|
|
1547
|
+
await rm3(stagingPath, { recursive: true, force: true }).catch(() => void 0);
|
|
1548
|
+
await rm3(downloadPath, { recursive: true, force: true }).catch(() => void 0);
|
|
1504
1549
|
}
|
|
1505
1550
|
};
|
|
1506
1551
|
var updateSelection = async (manifestPath, manifest, dependencies) => {
|
|
@@ -1546,8 +1591,8 @@ var waitForCandidateReadiness = async (manifestPath, candidate, signal, timeoutM
|
|
|
1546
1591
|
while (!signal.aborted && Date.now() < deadline) {
|
|
1547
1592
|
if (!await readDesired(manifestPath)) return "aborted";
|
|
1548
1593
|
try {
|
|
1549
|
-
const currentManifest = parseManifest(JSON.parse(await
|
|
1550
|
-
const value = JSON.parse(await
|
|
1594
|
+
const currentManifest = parseManifest(JSON.parse(await readFile3(manifestPath, "utf8")));
|
|
1595
|
+
const value = JSON.parse(await readFile3(runtimePathFor(manifestPath), "utf8"));
|
|
1551
1596
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1552
1597
|
const runtime = value;
|
|
1553
1598
|
const workers = Array.isArray(runtime.workers) ? runtime.workers : [];
|
|
@@ -1563,8 +1608,10 @@ var waitForCandidateReadiness = async (manifestPath, candidate, signal, timeoutM
|
|
|
1563
1608
|
};
|
|
1564
1609
|
var prunePrivateReleases = async (manifestPath, keep) => {
|
|
1565
1610
|
const releasesPath = releasesPathFor(manifestPath);
|
|
1611
|
+
const recovery = await readLauncherState(manifestPath);
|
|
1612
|
+
const retained = [...keep, recovery?.active.selection ?? null, recovery?.previous?.selection ?? null];
|
|
1566
1613
|
const keepPaths = new Set(
|
|
1567
|
-
|
|
1614
|
+
retained.filter((item) => item !== null).map((item) => resolve3(item.script)).filter((script) => isPathWithin(releasesPath, script)).map((script) => resolve3(releasesPath, relative3(releasesPath, script).split(/[\\/]/u)[0]))
|
|
1568
1615
|
);
|
|
1569
1616
|
let entries;
|
|
1570
1617
|
try {
|
|
@@ -1575,9 +1622,9 @@ var prunePrivateReleases = async (manifestPath, keep) => {
|
|
|
1575
1622
|
}
|
|
1576
1623
|
for (const entry of entries) {
|
|
1577
1624
|
if (!entry.isDirectory() || !parseVersion(entry.name)) continue;
|
|
1578
|
-
const releasePath =
|
|
1625
|
+
const releasePath = resolve3(releasesPath, entry.name);
|
|
1579
1626
|
if (!keepPaths.has(releasePath)) {
|
|
1580
|
-
await
|
|
1627
|
+
await rm3(releasePath, { recursive: true, force: true });
|
|
1581
1628
|
}
|
|
1582
1629
|
}
|
|
1583
1630
|
};
|
|
@@ -1602,7 +1649,7 @@ var waitForUpdateCheck = async (milliseconds, signal) => {
|
|
|
1602
1649
|
});
|
|
1603
1650
|
};
|
|
1604
1651
|
var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies = {}, lockPathValue) => {
|
|
1605
|
-
const manifestPath =
|
|
1652
|
+
const manifestPath = resolve3(manifestPathValue);
|
|
1606
1653
|
const lockPath = safePath(
|
|
1607
1654
|
lockPathValue ?? serviceSiblingPath(manifestPath, ".service-bootstrap.lock"),
|
|
1608
1655
|
"lock path"
|
|
@@ -1618,11 +1665,16 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1618
1665
|
}
|
|
1619
1666
|
};
|
|
1620
1667
|
dependencies.signal?.addEventListener("abort", stop, { once: true });
|
|
1668
|
+
const launcherStop = (message) => {
|
|
1669
|
+
if (message && typeof message === "object" && message.type === "vtx-inference-host-launcher-stop-v1") stop();
|
|
1670
|
+
};
|
|
1671
|
+
process.on("message", launcherStop);
|
|
1672
|
+
if (process.env.VTX_INFERENCE_HOST_LAUNCHER_PROTOCOL === "1") process.on("disconnect", stop);
|
|
1621
1673
|
process.on("SIGTERM", stop);
|
|
1622
1674
|
process.on("SIGINT", stop);
|
|
1623
1675
|
if (dependencies.signal?.aborted) stop();
|
|
1624
1676
|
try {
|
|
1625
|
-
let manifest = parseManifest(JSON.parse(await
|
|
1677
|
+
let manifest = parseManifest(JSON.parse(await readFile3(manifestPath, "utf8")));
|
|
1626
1678
|
const shouldRun = async () => !lifetime.signal.aborted && await readDesired(manifestPath);
|
|
1627
1679
|
if (!await shouldRun()) {
|
|
1628
1680
|
await appendBootstrapLog(manifest.log_path, "service_bootstrap_stopped");
|
|
@@ -1642,7 +1694,7 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1642
1694
|
signal: lifetime.signal
|
|
1643
1695
|
};
|
|
1644
1696
|
const checkUpdate = async () => {
|
|
1645
|
-
manifest = parseManifest(JSON.parse(await
|
|
1697
|
+
manifest = parseManifest(JSON.parse(await readFile3(manifestPath, "utf8")));
|
|
1646
1698
|
const active = await parseSelection(manifestPath);
|
|
1647
1699
|
try {
|
|
1648
1700
|
return await updateSelection(manifestPath, manifest, updateDependencies);
|
|
@@ -1653,16 +1705,18 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1653
1705
|
return { active, candidate: null };
|
|
1654
1706
|
}
|
|
1655
1707
|
};
|
|
1708
|
+
let launchedAt = now().toISOString();
|
|
1656
1709
|
const launch = async (selection) => {
|
|
1657
|
-
manifest = parseManifest(JSON.parse(await
|
|
1710
|
+
manifest = parseManifest(JSON.parse(await readFile3(manifestPath, "utf8")));
|
|
1658
1711
|
const script = selection?.script ?? manifest.script;
|
|
1659
|
-
await
|
|
1660
|
-
await
|
|
1712
|
+
await access2(manifest.executable);
|
|
1713
|
+
await access2(script);
|
|
1661
1714
|
if (!await shouldRun()) return null;
|
|
1662
1715
|
await appendBootstrapLog(manifest.log_path, "service_bootstrap_launch", {
|
|
1663
1716
|
package_version: selection?.package_version ?? agent_cli_release_default.package_version
|
|
1664
1717
|
});
|
|
1665
1718
|
if (!await shouldRun()) return null;
|
|
1719
|
+
launchedAt = now().toISOString();
|
|
1666
1720
|
live.supervisor = runSupervisor(manifest.executable, supervisorArguments(script, manifestPath));
|
|
1667
1721
|
return live.supervisor;
|
|
1668
1722
|
};
|
|
@@ -1670,7 +1724,7 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1670
1724
|
while (await shouldRun()) {
|
|
1671
1725
|
if (updatePlan.candidate) {
|
|
1672
1726
|
const candidate = { ...updatePlan.candidate, selected_at: now().toISOString() };
|
|
1673
|
-
await
|
|
1727
|
+
await rm3(runtimePathFor(manifestPath), { force: true });
|
|
1674
1728
|
await appendBootstrapLog(manifest.log_path, "service_release_candidate_started", {
|
|
1675
1729
|
package_version: candidate.package_version
|
|
1676
1730
|
});
|
|
@@ -1696,7 +1750,7 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1696
1750
|
]);
|
|
1697
1751
|
if (first.kind === "ready" && await shouldRun()) {
|
|
1698
1752
|
await atomicWriteSelection(manifestPath, candidate);
|
|
1699
|
-
await
|
|
1753
|
+
await rm3(rejectionPathFor(manifestPath), { force: true }).catch(() => void 0);
|
|
1700
1754
|
await prunePrivateReleases(manifestPath, [candidate, updatePlan.active]).catch(
|
|
1701
1755
|
async () => await appendBootstrapLog(
|
|
1702
1756
|
manifest.log_path,
|
|
@@ -1750,6 +1804,24 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1750
1804
|
}
|
|
1751
1805
|
const supervisor = live.supervisor ?? await launch(updatePlan.active);
|
|
1752
1806
|
if (!supervisor) return 0;
|
|
1807
|
+
if (process.env.VTX_INFERENCE_HOST_LAUNCHER_PROTOCOL === "1" && process.send) {
|
|
1808
|
+
const readiness = new AbortController();
|
|
1809
|
+
const cancel = () => readiness.abort();
|
|
1810
|
+
lifetime.signal.addEventListener("abort", cancel, { once: true });
|
|
1811
|
+
void waitForCandidateReadiness(manifestPath, {
|
|
1812
|
+
...updatePlan.active ?? {
|
|
1813
|
+
schema_version: "vtx_inference_service_release_v1",
|
|
1814
|
+
package_name: PACKAGE_NAME,
|
|
1815
|
+
package_version: agent_cli_release_default.package_version,
|
|
1816
|
+
integrity: "",
|
|
1817
|
+
script: manifest.script
|
|
1818
|
+
},
|
|
1819
|
+
selected_at: launchedAt
|
|
1820
|
+
}, readiness.signal, dependencies.readinessTimeoutMs ?? CANDIDATE_READINESS_TIMEOUT_MS).then((status) => {
|
|
1821
|
+
if (status === "ready" && process.connected) process.send?.({ type: "vtx-inference-host-bootstrap-ready-v1", package_version: agent_cli_release_default.package_version }, () => void 0);
|
|
1822
|
+
}).catch(() => void 0).finally(() => lifetime.signal.removeEventListener("abort", cancel));
|
|
1823
|
+
void supervisor.completion.finally(cancel).catch(() => void 0);
|
|
1824
|
+
}
|
|
1753
1825
|
let exited = false;
|
|
1754
1826
|
const completion = supervisor.completion.then(
|
|
1755
1827
|
(exitCode) => {
|
|
@@ -1817,12 +1889,14 @@ var runInferenceHostServiceBootstrap = async (manifestPathValue, dependencies =
|
|
|
1817
1889
|
await live.supervisor.completion.catch(() => 1);
|
|
1818
1890
|
}
|
|
1819
1891
|
dependencies.signal?.removeEventListener("abort", stop);
|
|
1892
|
+
process.removeListener("message", launcherStop);
|
|
1893
|
+
process.removeListener("disconnect", stop);
|
|
1820
1894
|
process.removeListener("SIGTERM", stop);
|
|
1821
1895
|
process.removeListener("SIGINT", stop);
|
|
1822
1896
|
await lock.release();
|
|
1823
1897
|
}
|
|
1824
1898
|
};
|
|
1825
|
-
var invokedPath = process.argv[1] ?
|
|
1899
|
+
var invokedPath = process.argv[1] ? resolve3(process.argv[1]) : "";
|
|
1826
1900
|
if (invokedPath === fileURLToPath(import.meta.url)) {
|
|
1827
1901
|
const manifestIndex = process.argv.indexOf("--service-manifest");
|
|
1828
1902
|
const lockIndex = process.argv.indexOf("--service-lock");
|