@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 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
- failed candidate is rejected and the last working runtime resumes. The service
26
- keeps only the active release and one rollback release. If the update check is
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 readFile2,
8
+ readFile as readFile3,
9
9
  readdir,
10
10
  rename as rename2,
11
- rm as rm2
11
+ rm as rm3
12
12
  } from "node:fs/promises";
13
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2, win32 as windowsPath2 } from "node:path";
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.51",
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((resolve3) => setTimeout(resolve3, milliseconds)));
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 = 10 * 60 * 1e3;
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 resolve2(value);
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 readFile2(desiredPathFor(manifestPath), "utf8"));
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 = relative2(resolve2(parent), resolve2(child));
1235
- return childRelative !== "" && !childRelative.startsWith("..") && !isAbsolute2(childRelative);
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 readFile2(selectionPathFor(manifestPath), "utf8"));
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 access(script);
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 readFile2(rejectionPathFor(manifestPath), "utf8"));
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(join2(dirname2(executable), "npm"), args);
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(dirname2(path), { recursive: true, mode: 448 });
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 readFile2(tarballPath)).digest("base64");
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 = resolve2(rootResult.stdout.trim(), PACKAGE_NAME);
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 readFile2(join2(packagePath, "package.json"), "utf8"));
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 = resolve2(packagePath, binPath);
1441
+ const script = resolve3(packagePath, binPath);
1397
1442
  if (!isPathWithin(packagePath, script)) throw new Error("release_install_invalid");
1398
- await access(script);
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 rm2(resolve2(releasesPath, entry.name), { recursive: true, force: true });
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 = join2(releasesPath, target.version);
1416
- const stagingPath = join2(releasesPath, `.staging-${target.version}-${randomUUID()}`);
1417
- const downloadPath = join2(releasesPath, `.download-${target.version}-${randomUUID()}`);
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 access(finalPath);
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 = resolve2(downloadPath, filename);
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 = resolve2(finalPath, relative2(stagingPath, stagedScript));
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 rm2(stagingPath, { recursive: true, force: true }).catch(() => void 0);
1503
- await rm2(downloadPath, { recursive: true, force: true }).catch(() => void 0);
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 readFile2(manifestPath, "utf8")));
1550
- const value = JSON.parse(await readFile2(runtimePathFor(manifestPath), "utf8"));
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
- keep.filter((item) => item !== null).map((item) => resolve2(item.script)).filter((script) => isPathWithin(releasesPath, script)).map((script) => resolve2(releasesPath, relative2(releasesPath, script).split(/[\\/]/u)[0]))
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 = resolve2(releasesPath, entry.name);
1625
+ const releasePath = resolve3(releasesPath, entry.name);
1579
1626
  if (!keepPaths.has(releasePath)) {
1580
- await rm2(releasePath, { recursive: true, force: true });
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 = resolve2(manifestPathValue);
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 readFile2(manifestPath, "utf8")));
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 readFile2(manifestPath, "utf8")));
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 readFile2(manifestPath, "utf8")));
1710
+ manifest = parseManifest(JSON.parse(await readFile3(manifestPath, "utf8")));
1658
1711
  const script = selection?.script ?? manifest.script;
1659
- await access(manifest.executable);
1660
- await access(script);
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 rm2(runtimePathFor(manifestPath), { force: true });
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 rm2(rejectionPathFor(manifestPath), { force: true }).catch(() => void 0);
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] ? resolve2(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");