@vtxmacro/cli 2026.8.17 → 2026.8.19

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 (3) hide show
  1. package/README.md +21 -7
  2. package/bin/vtx.js +1531 -225
  3. package/package.json +2 -2
package/bin/vtx.js CHANGED
@@ -38,7 +38,7 @@ var init_agent_cli_release = __esm({
38
38
  "agent-cli-release.json"() {
39
39
  agent_cli_release_default = {
40
40
  package_name: "@vtxmacro/cli",
41
- package_version: "2026.8.17",
41
+ package_version: "2026.8.19",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -16080,10 +16080,11 @@ var init_external_inference_contract = __esm({
16080
16080
 
16081
16081
  // lib/inference-host/config.ts
16082
16082
  import { randomBytes } from "node:crypto";
16083
+ import { spawn } from "node:child_process";
16083
16084
  import { constants } from "node:fs";
16084
16085
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
16085
16086
  import { homedir } from "node:os";
16086
- import { dirname, join, resolve } from "node:path";
16087
+ import { dirname, join, relative, resolve } from "node:path";
16087
16088
  function inferenceBaseDir(env) {
16088
16089
  const configured = String(env.VTX_INFERENCE_HOST_HOME || "").trim();
16089
16090
  return configured || join(homedir(), ".vtx", "inference-host");
@@ -16132,6 +16133,7 @@ function assertLocalState(value) {
16132
16133
  return record2;
16133
16134
  }
16134
16135
  async function ensureInferencePrivateDirectory(path) {
16136
+ let created = false;
16135
16137
  try {
16136
16138
  const before = await lstat(path);
16137
16139
  if (before.isSymbolicLink() || !before.isDirectory()) {
@@ -16140,22 +16142,26 @@ async function ensureInferencePrivateDirectory(path) {
16140
16142
  } catch (error48) {
16141
16143
  if (error48.code !== "ENOENT") throw error48;
16142
16144
  await mkdir(path, { recursive: true, mode: 448 });
16145
+ created = true;
16143
16146
  }
16144
16147
  const after = await lstat(path);
16145
16148
  if (after.isSymbolicLink() || !after.isDirectory()) {
16146
16149
  throw new Error("Inference host state directory is unsafe.");
16147
16150
  }
16148
- if (process.platform !== "win32") {
16149
- if ((after.mode & 63) !== 0) {
16150
- throw new Error("Inference host state directory must have mode 0700.");
16151
- }
16152
- if (typeof process.getuid === "function" && after.uid !== process.getuid()) {
16153
- throw new Error("Inference host state directory must be owned by the current user.");
16154
- }
16155
- const canonical = await realpath(path);
16156
- if (canonical !== resolve(path)) {
16157
- throw new Error("Inference host state directory may not traverse symbolic links.");
16158
- }
16151
+ if (process.platform === "win32") {
16152
+ if (created) await secureWindowsPrivatePath(path, "directory", "harden");
16153
+ else await secureExistingWindowsPath(path, "directory");
16154
+ return;
16155
+ }
16156
+ if ((after.mode & 63) !== 0) {
16157
+ throw new Error("Inference host state directory must have mode 0700.");
16158
+ }
16159
+ if (typeof process.getuid === "function" && after.uid !== process.getuid()) {
16160
+ throw new Error("Inference host state directory must be owned by the current user.");
16161
+ }
16162
+ const canonical = await realpath(path);
16163
+ if (canonical !== resolve(path)) {
16164
+ throw new Error("Inference host state directory may not traverse symbolic links.");
16159
16165
  }
16160
16166
  }
16161
16167
  async function syncInferenceDirectory(path, platform = process.platform) {
@@ -16188,8 +16194,12 @@ async function writeAtomicInferencePrivateFile(path, contents) {
16188
16194
  throw error48;
16189
16195
  }
16190
16196
  await handle.close();
16197
+ await secureWindowsPrivatePath(temporary, "file", "harden");
16191
16198
  try {
16199
+ invalidateWindowsAclCache(path);
16192
16200
  await rename(temporary, path);
16201
+ invalidateWindowsAclCache(temporary);
16202
+ await secureWindowsPrivatePath(path, "file", "verify");
16193
16203
  await syncInferenceDirectory(directory);
16194
16204
  } catch (error48) {
16195
16205
  await rm(temporary, { force: true }).catch(() => void 0);
@@ -16207,6 +16217,8 @@ async function readInferencePrivateFile(path, label) {
16207
16217
  if (before.isSymbolicLink() || !before.isFile()) {
16208
16218
  throw new Error(`${label} is unsafe.`);
16209
16219
  }
16220
+ await secureExistingWindowsPath(dirname(path), "directory");
16221
+ await secureExistingWindowsPath(path, "file");
16210
16222
  const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
16211
16223
  try {
16212
16224
  const opened = await handle.stat();
@@ -16227,7 +16239,10 @@ async function clearInferencePrivateFile(path, label) {
16227
16239
  if (metadata.isSymbolicLink() || !metadata.isFile()) {
16228
16240
  throw new Error(`${label} is unsafe.`);
16229
16241
  }
16242
+ await secureExistingWindowsPath(dirname(path), "directory");
16243
+ await secureExistingWindowsPath(path, "file");
16230
16244
  await rm(path);
16245
+ invalidateWindowsAclCache(path);
16231
16246
  await syncInferenceDirectory(dirname(path));
16232
16247
  } catch (error48) {
16233
16248
  if (error48.code !== "ENOENT") throw error48;
@@ -16257,10 +16272,21 @@ async function writeInferenceHostLocalState(path, state) {
16257
16272
  async function clearInferenceHostLocalState(path) {
16258
16273
  await clearInferencePrivateFile(path, "Inference host local state file");
16259
16274
  }
16260
- async function acquireInferenceHostProcessLock(path) {
16275
+ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
16261
16276
  const directory = dirname(path);
16262
16277
  await ensureInferencePrivateDirectory(directory);
16263
16278
  const ownerToken = randomBytes(24).toString("base64url");
16279
+ const observeIdentity = dependencies.processIdentity ?? defaultProcessIdentity;
16280
+ const currentBootIdentity = dependencies.currentBootIdentity ?? (() => readInferenceSystemBootIdentity());
16281
+ const isProcessAlive = dependencies.processAlive ?? ((pid) => {
16282
+ try {
16283
+ process.kill(pid, 0);
16284
+ return true;
16285
+ } catch (error48) {
16286
+ if (error48.code === "ESRCH") return false;
16287
+ throw error48;
16288
+ }
16289
+ });
16264
16290
  let handle;
16265
16291
  for (let acquisition = 0; acquisition < 2; acquisition += 1) {
16266
16292
  try {
@@ -16275,41 +16301,123 @@ async function acquireInferenceHostProcessLock(path) {
16275
16301
  if (acquisition > 0) {
16276
16302
  throw new Error("Another inference host process already owns this local host.");
16277
16303
  }
16278
- const before = await lstat(path);
16304
+ let before;
16305
+ try {
16306
+ before = await lstat(path);
16307
+ } catch (error49) {
16308
+ if (error49.code === "ENOENT") continue;
16309
+ throw error49;
16310
+ }
16279
16311
  if (before.isSymbolicLink() || !before.isFile() || process.platform !== "win32" && (before.mode & 63) !== 0 || typeof process.getuid === "function" && before.uid !== process.getuid()) {
16280
16312
  throw new Error("Inference host process lock is unsafe.");
16281
16313
  }
16314
+ const reclaimPublicationResidue = async () => {
16315
+ let current2;
16316
+ try {
16317
+ current2 = await lstat(path);
16318
+ } catch (error49) {
16319
+ if (error49.code === "ENOENT") return false;
16320
+ throw error49;
16321
+ }
16322
+ if (current2.isSymbolicLink() || !current2.isFile() || current2.dev !== before.dev || current2.ino !== before.ino || process.platform !== "win32" && (current2.mode & 63) !== 0 || typeof process.getuid === "function" && current2.uid !== process.getuid()) {
16323
+ throw new Error("Inference host process lock publication residue changed or is unsafe.");
16324
+ }
16325
+ try {
16326
+ await rm(path);
16327
+ } catch (error49) {
16328
+ if (error49.code === "ENOENT") return false;
16329
+ throw error49;
16330
+ }
16331
+ processLockObservationCache.delete(resolve(path));
16332
+ await syncInferenceDirectory(directory);
16333
+ return true;
16334
+ };
16335
+ try {
16336
+ await secureExistingWindowsPath(path, "file");
16337
+ } catch (error49) {
16338
+ if (error49.code === "ENOENT") continue;
16339
+ throw error49;
16340
+ }
16282
16341
  let staleOwner;
16283
16342
  try {
16284
16343
  staleOwner = JSON.parse(await readFile(path, "utf8"));
16285
- } catch {
16286
- throw new Error("Inference host process lock is invalid.");
16344
+ } catch (error49) {
16345
+ if (error49.code === "ENOENT") continue;
16346
+ if (Date.now() - before.mtimeMs < 1e4) {
16347
+ throw new Error("Another inference host process already owns this local host.");
16348
+ }
16349
+ await reclaimPublicationResidue();
16350
+ continue;
16287
16351
  }
16288
- if (staleOwner.schema_version !== 1 || !Number.isSafeInteger(staleOwner.pid) || Number(staleOwner.pid) < 1 || typeof staleOwner.owner_token !== "string" || !staleOwner.owner_token) {
16289
- throw new Error("Inference host process lock is invalid.");
16352
+ if (staleOwner.schema_version !== 2 || !Number.isSafeInteger(staleOwner.pid) || Number(staleOwner.pid) < 1 || typeof staleOwner.owner_token !== "string" || !staleOwner.owner_token || typeof staleOwner.process_identity !== "string" || !staleOwner.process_identity || typeof staleOwner.boot_identity !== "string" || !staleOwner.boot_identity) {
16353
+ if (Date.now() - before.mtimeMs < 1e4) {
16354
+ throw new Error("Another inference host process already owns this local host.");
16355
+ }
16356
+ await reclaimPublicationResidue();
16357
+ continue;
16290
16358
  }
16359
+ const stalePid = Number(staleOwner.pid);
16360
+ const bootIdentity2 = await currentBootIdentity();
16361
+ if (bootIdentity2 === staleOwner.boot_identity && isProcessAlive(stalePid)) {
16362
+ const cached2 = processLockObservationCache.get(resolve(path));
16363
+ const observedIdentity = cached2 && cached2.dev === before.dev && cached2.ino === before.ino && cached2.pid === stalePid && cached2.storedIdentity === staleOwner.process_identity && Date.now() - cached2.observedAtMs < 5e3 ? cached2.observedIdentity : await observeIdentity(stalePid);
16364
+ if (!observedIdentity) {
16365
+ throw new Error("Inference host could not verify the existing lock owner identity.");
16366
+ }
16367
+ processLockObservationCache.set(resolve(path), {
16368
+ dev: before.dev,
16369
+ ino: before.ino,
16370
+ pid: stalePid,
16371
+ storedIdentity: staleOwner.process_identity,
16372
+ observedIdentity,
16373
+ observedAtMs: Date.now()
16374
+ });
16375
+ if (inferenceProcessIdentitiesMatch(staleOwner.process_identity, observedIdentity)) {
16376
+ throw new Error("Another inference host process already owns this local host.");
16377
+ }
16378
+ }
16379
+ let current;
16291
16380
  try {
16292
- process.kill(Number(staleOwner.pid), 0);
16293
- throw new Error("Another inference host process already owns this local host.");
16294
- } catch (probeError) {
16295
- if (probeError.code !== "ESRCH") throw probeError;
16381
+ current = await lstat(path);
16382
+ } catch (error49) {
16383
+ if (error49.code === "ENOENT") continue;
16384
+ throw error49;
16296
16385
  }
16297
- const current = await lstat(path);
16298
16386
  if (current.dev !== before.dev || current.ino !== before.ino) {
16299
16387
  throw new Error("Inference host process lock ownership changed.");
16300
16388
  }
16301
16389
  await rm(path);
16390
+ processLockObservationCache.delete(resolve(path));
16302
16391
  await syncInferenceDirectory(directory);
16303
16392
  }
16304
16393
  }
16305
16394
  if (!handle) throw new Error("Inference host process lock could not be acquired.");
16395
+ let ownerIdentity;
16396
+ let bootIdentity;
16397
+ try {
16398
+ [ownerIdentity, bootIdentity] = await Promise.all([
16399
+ (dependencies.currentProcessIdentity ?? defaultCurrentProcessIdentity)(),
16400
+ currentBootIdentity()
16401
+ ]);
16402
+ } catch (error48) {
16403
+ await handle.close().catch(() => void 0);
16404
+ await rm(path, { force: true }).catch(() => void 0);
16405
+ throw error48;
16406
+ }
16306
16407
  await handle.writeFile(
16307
- `${JSON.stringify({ schema_version: 1, pid: process.pid, owner_token: ownerToken })}
16408
+ `${JSON.stringify({
16409
+ schema_version: 2,
16410
+ pid: process.pid,
16411
+ owner_token: ownerToken,
16412
+ process_identity: ownerIdentity,
16413
+ boot_identity: bootIdentity
16414
+ })}
16308
16415
  `,
16309
16416
  "utf8"
16310
16417
  );
16311
16418
  await handle.sync();
16312
16419
  await handle.close();
16420
+ await secureWindowsPrivatePath(path, "file", "harden");
16313
16421
  await syncInferenceDirectory(directory);
16314
16422
  let released = false;
16315
16423
  return {
@@ -16326,17 +16434,342 @@ async function acquireInferenceHostProcessLock(path) {
16326
16434
  throw new Error("Inference host process lock ownership changed.");
16327
16435
  }
16328
16436
  await rm(path);
16437
+ processLockObservationCache.delete(resolve(path));
16329
16438
  await syncInferenceDirectory(directory);
16330
16439
  released = true;
16331
16440
  }
16332
16441
  };
16333
16442
  }
16334
- var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, credentialStoreIdentity;
16443
+ var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_SCRIPT, windowsPrivateAclInvocation, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, credentialStoreIdentity;
16335
16444
  var init_config = __esm({
16336
16445
  "lib/inference-host/config.ts"() {
16337
16446
  "use strict";
16338
16447
  INFERENCE_CREDENTIAL_NAMESPACE = "vtxmacro-insights-inference";
16339
16448
  MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
16449
+ WINDOWS_PRIVATE_ACL_SCRIPT = `$ErrorActionPreference='Stop'
16450
+ $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:VTX_PRIVATE_PATH_B64))
16451
+ $kind=$env:VTX_PRIVATE_PATH_KIND
16452
+ $mode=$env:VTX_PRIVATE_ACL_MODE
16453
+ if(-not (Test-Path -LiteralPath $path)) { [Console]::Out.Write('missing'); exit 3 }
16454
+ $current=[Security.Principal.WindowsIdentity]::GetCurrent().User
16455
+ $system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
16456
+ $admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
16457
+ $allowed=@($current.Value,$system.Value,$admins.Value)
16458
+ if($mode -eq 'harden') {
16459
+ $acl=Get-Acl -LiteralPath $path
16460
+ $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16461
+ if($existingOwner -ne $current.Value) { throw 'private ACL owner is invalid' }
16462
+ $acl.SetAccessRuleProtection($true,$false)
16463
+ foreach($existingRule in @($acl.Access)) {
16464
+ [void]$acl.RemoveAccessRuleSpecific($existingRule)
16465
+ }
16466
+ $inherit=if($kind -eq 'directory') {
16467
+ [Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
16468
+ } else { [Security.AccessControl.InheritanceFlags]::None }
16469
+ foreach($sid in @($current,$system,$admins)) {
16470
+ $rule=New-Object Security.AccessControl.FileSystemAccessRule(
16471
+ $sid,
16472
+ [Security.AccessControl.FileSystemRights]::FullControl,
16473
+ $inherit,
16474
+ [Security.AccessControl.PropagationFlags]::None,
16475
+ [Security.AccessControl.AccessControlType]::Allow
16476
+ )
16477
+ [void]$acl.AddAccessRule($rule)
16478
+ }
16479
+ (Get-Item -LiteralPath $path).SetAccessControl($acl)
16480
+ }
16481
+ $actual=Get-Acl -LiteralPath $path
16482
+ $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
16483
+ if($owner -ne $current.Value) { throw 'private ACL owner is invalid' }
16484
+ if($mode -eq 'verify-owner') { [Console]::Out.Write('ok'); exit 0 }
16485
+ if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
16486
+ $userFull=$false
16487
+ foreach($rule in $actual.Access) {
16488
+ $sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
16489
+ if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
16490
+ throw 'private ACL contains an unauthorized access rule'
16491
+ }
16492
+ if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
16493
+ }
16494
+ if(-not $userFull) { throw 'private ACL does not grant the current user full control' }
16495
+ [Console]::Out.Write('ok')`;
16496
+ windowsPrivateAclInvocation = (path, kind, mode) => ({
16497
+ args: [
16498
+ "-NoLogo",
16499
+ "-NoProfile",
16500
+ "-NonInteractive",
16501
+ "-EncodedCommand",
16502
+ Buffer.from(WINDOWS_PRIVATE_ACL_SCRIPT, "utf16le").toString("base64")
16503
+ ],
16504
+ env: {
16505
+ NODE_ENV: process.env.NODE_ENV,
16506
+ SystemRoot: process.env.SystemRoot,
16507
+ WINDIR: process.env.WINDIR,
16508
+ ComSpec: process.env.ComSpec,
16509
+ PATHEXT: process.env.PATHEXT,
16510
+ PSModulePath: process.env.PSModulePath,
16511
+ TEMP: process.env.TEMP,
16512
+ TMP: process.env.TMP,
16513
+ VTX_PRIVATE_PATH_B64: Buffer.from(path, "utf8").toString("base64"),
16514
+ VTX_PRIVATE_PATH_KIND: kind,
16515
+ VTX_PRIVATE_ACL_MODE: mode
16516
+ }
16517
+ });
16518
+ runWindowsPrivateAcl = async (path, kind, mode) => await new Promise((resolvePromise, reject) => {
16519
+ const invocation = windowsPrivateAclInvocation(path, kind, mode);
16520
+ const child = spawn("powershell.exe", [...invocation.args], {
16521
+ env: invocation.env,
16522
+ windowsHide: true,
16523
+ stdio: ["ignore", "pipe", "pipe"]
16524
+ });
16525
+ const stdout = [];
16526
+ const stderr = [];
16527
+ let settled = false;
16528
+ const timer = setTimeout(() => child.kill("SIGKILL"), 15e3);
16529
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
16530
+ child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
16531
+ const settle = (error48) => {
16532
+ if (settled) return;
16533
+ settled = true;
16534
+ clearTimeout(timer);
16535
+ if (error48) reject(error48);
16536
+ else resolvePromise();
16537
+ };
16538
+ child.once("error", (error48) => settle(error48));
16539
+ child.once("exit", (code) => {
16540
+ const output3 = Buffer.concat(stdout).toString("utf8").trim();
16541
+ if (code === 0 && output3 === "ok") {
16542
+ settle();
16543
+ return;
16544
+ }
16545
+ const detail = Buffer.concat(stderr).toString("utf8").trim();
16546
+ if (code === 3 && output3 === "missing" || /GetAcl_PathNotFound|Cannot find path .* because it does not exist/iu.test(detail)) {
16547
+ const error48 = new Error("Windows private path no longer exists.");
16548
+ error48.code = "ENOENT";
16549
+ settle(error48);
16550
+ return;
16551
+ }
16552
+ settle(new Error(`Windows private ACL ${mode} failed${detail ? `: ${detail}` : "."}`));
16553
+ });
16554
+ });
16555
+ windowsAclCache = /* @__PURE__ */ new Map();
16556
+ windowsAclIdentity = async (path) => {
16557
+ const metadata = await lstat(path, { bigint: true });
16558
+ return {
16559
+ dev: metadata.dev,
16560
+ ino: metadata.ino,
16561
+ ctimeNs: metadata.ctimeNs,
16562
+ mtimeNs: metadata.mtimeNs,
16563
+ mode: metadata.mode,
16564
+ size: metadata.size
16565
+ };
16566
+ };
16567
+ aclCacheKey = (path, kind, mode) => `${kind}:${mode}:${resolve(path)}`;
16568
+ secureWindowsPrivatePath = async (path, kind, mode, platform = process.platform, runner = runWindowsPrivateAcl) => {
16569
+ if (platform !== "win32") return;
16570
+ const key = aclCacheKey(path, kind, mode);
16571
+ const before = await windowsAclIdentity(path);
16572
+ const cached2 = windowsAclCache.get(key);
16573
+ if (cached2 && cached2.dev === before.dev && cached2.ino === before.ino && cached2.ctimeNs === before.ctimeNs && cached2.mtimeNs === before.mtimeNs && cached2.mode === before.mode && cached2.size === before.size) return;
16574
+ await runner(path, kind, mode);
16575
+ windowsAclCache.set(key, await windowsAclIdentity(path));
16576
+ };
16577
+ invalidateWindowsAclCache = (path) => {
16578
+ for (const kind of ["file", "directory"]) {
16579
+ for (const mode of ["harden", "verify", "verify-owner"]) {
16580
+ windowsAclCache.delete(aclCacheKey(path, kind, mode));
16581
+ }
16582
+ }
16583
+ };
16584
+ isDefaultWindowsPrivatePath = (path, root = resolve(homedir(), ".vtx", "inference-host")) => {
16585
+ const child = relative(root, resolve(path));
16586
+ return child === "" || !child.startsWith("..") && !child.includes(":");
16587
+ };
16588
+ secureExistingWindowsPath = async (path, kind, platform = process.platform, runner = runWindowsPrivateAcl, defaultRoot = resolve(homedir(), ".vtx", "inference-host")) => {
16589
+ if (platform !== "win32") return;
16590
+ if (!isDefaultWindowsPrivatePath(path, defaultRoot)) {
16591
+ await secureWindowsPrivatePath(path, kind, "verify", platform, runner);
16592
+ return;
16593
+ }
16594
+ await secureWindowsPrivatePath(path, kind, "verify-owner", platform, runner);
16595
+ invalidateWindowsAclCache(path);
16596
+ await secureWindowsPrivatePath(path, kind, "harden", platform, runner);
16597
+ };
16598
+ linuxProcessIdentity = async (pid) => {
16599
+ try {
16600
+ const [bootId, statLine] = await Promise.all([
16601
+ readFile("/proc/sys/kernel/random/boot_id", "utf8"),
16602
+ readFile(`/proc/${pid}/stat`, "utf8")
16603
+ ]);
16604
+ const fields = statLine.slice(statLine.lastIndexOf(")") + 2).trim().split(/\s+/u);
16605
+ const startTicks = fields[19];
16606
+ return startTicks ? `linux:${bootId.trim()}:${startTicks}` : null;
16607
+ } catch {
16608
+ return null;
16609
+ }
16610
+ };
16611
+ windowsProcessIdentity = async (pid) => await new Promise((resolvePromise) => {
16612
+ const script = `$ErrorActionPreference='Stop';$p=Get-Process -Id ([int]$env:VTX_PROCESS_PID);[Console]::Out.Write($p.StartTime.ToUniversalTime().ToFileTimeUtc())`;
16613
+ const child = spawn("powershell.exe", [
16614
+ "-NoLogo",
16615
+ "-NoProfile",
16616
+ "-NonInteractive",
16617
+ "-EncodedCommand",
16618
+ Buffer.from(script, "utf16le").toString("base64")
16619
+ ], {
16620
+ windowsHide: true,
16621
+ stdio: ["ignore", "pipe", "ignore"],
16622
+ env: {
16623
+ NODE_ENV: process.env.NODE_ENV,
16624
+ SystemRoot: process.env.SystemRoot,
16625
+ WINDIR: process.env.WINDIR,
16626
+ ComSpec: process.env.ComSpec,
16627
+ PATHEXT: process.env.PATHEXT,
16628
+ PSModulePath: process.env.PSModulePath,
16629
+ VTX_PROCESS_PID: String(pid)
16630
+ }
16631
+ });
16632
+ const output3 = [];
16633
+ let settled = false;
16634
+ const settle = (value) => {
16635
+ if (settled) return;
16636
+ settled = true;
16637
+ clearTimeout(timer);
16638
+ resolvePromise(value);
16639
+ };
16640
+ const timer = setTimeout(() => {
16641
+ child.kill("SIGKILL");
16642
+ settle(null);
16643
+ }, 5e3);
16644
+ child.stdout.on("data", (chunk) => {
16645
+ if (output3.reduce((total, item) => total + item.byteLength, 0) + Buffer.byteLength(chunk) > 1024) {
16646
+ child.kill("SIGKILL");
16647
+ settle(null);
16648
+ return;
16649
+ }
16650
+ output3.push(Buffer.from(chunk));
16651
+ });
16652
+ child.once("error", () => settle(null));
16653
+ child.once("exit", (code) => {
16654
+ const started = Buffer.concat(output3).toString("utf8").trim();
16655
+ settle(code === 0 && /^\d+$/u.test(started) ? `win32:${started}` : null);
16656
+ });
16657
+ });
16658
+ darwinProcessIdentity = async (pid) => await new Promise((resolvePromise) => {
16659
+ const child = spawn("ps", ["-p", String(pid), "-o", "lstart="], {
16660
+ stdio: ["ignore", "pipe", "ignore"]
16661
+ });
16662
+ const output3 = [];
16663
+ let settled = false;
16664
+ const settle = (value) => {
16665
+ if (settled) return;
16666
+ settled = true;
16667
+ clearTimeout(timer);
16668
+ resolvePromise(value);
16669
+ };
16670
+ const timer = setTimeout(() => {
16671
+ child.kill("SIGKILL");
16672
+ settle(null);
16673
+ }, 5e3);
16674
+ child.stdout.on("data", (chunk) => output3.push(Buffer.from(chunk)));
16675
+ child.once("error", () => settle(null));
16676
+ child.once("exit", (code) => {
16677
+ const started = Date.parse(Buffer.concat(output3).toString("utf8").trim());
16678
+ settle(code === 0 && Number.isFinite(started) ? `darwin:${Math.floor(started / 1e3)}` : null);
16679
+ });
16680
+ });
16681
+ runSmallIdentityCommand = async (command, args, env) => await new Promise((resolvePromise) => {
16682
+ const child = spawn(command, [...args], {
16683
+ windowsHide: true,
16684
+ stdio: ["ignore", "pipe", "ignore"],
16685
+ env
16686
+ });
16687
+ const output3 = [];
16688
+ let bytes3 = 0;
16689
+ let settled = false;
16690
+ const settle = (value) => {
16691
+ if (settled) return;
16692
+ settled = true;
16693
+ clearTimeout(timer);
16694
+ resolvePromise(value);
16695
+ };
16696
+ const timer = setTimeout(() => {
16697
+ child.kill("SIGKILL");
16698
+ settle(null);
16699
+ }, 5e3);
16700
+ child.stdout.on("data", (chunk) => {
16701
+ bytes3 += Buffer.byteLength(chunk);
16702
+ if (bytes3 > 4096) {
16703
+ child.kill("SIGKILL");
16704
+ settle(null);
16705
+ return;
16706
+ }
16707
+ output3.push(Buffer.from(chunk));
16708
+ });
16709
+ child.once("error", () => settle(null));
16710
+ child.once("exit", (code) => settle(
16711
+ code === 0 ? Buffer.concat(output3).toString("utf8").trim() || null : null
16712
+ ));
16713
+ });
16714
+ cachedSystemBootIdentity = null;
16715
+ readInferenceSystemBootIdentity = async (platform = process.platform) => {
16716
+ if (platform === process.platform && cachedSystemBootIdentity) {
16717
+ return await cachedSystemBootIdentity;
16718
+ }
16719
+ const read = async () => {
16720
+ let identity = null;
16721
+ if (platform === "linux") {
16722
+ try {
16723
+ identity = (await readFile("/proc/sys/kernel/random/boot_id", "utf8")).trim();
16724
+ } catch {
16725
+ identity = null;
16726
+ }
16727
+ } else if (platform === "win32") {
16728
+ const script = `$ErrorActionPreference='Stop';$v=(Get-CimInstance Win32_OperatingSystem).LastBootUpTime.ToUniversalTime().ToFileTimeUtc();[Console]::Out.Write($v)`;
16729
+ identity = await runSmallIdentityCommand("powershell.exe", [
16730
+ "-NoLogo",
16731
+ "-NoProfile",
16732
+ "-NonInteractive",
16733
+ "-EncodedCommand",
16734
+ Buffer.from(script, "utf16le").toString("base64")
16735
+ ], {
16736
+ NODE_ENV: process.env.NODE_ENV,
16737
+ SystemRoot: process.env.SystemRoot,
16738
+ WINDIR: process.env.WINDIR,
16739
+ ComSpec: process.env.ComSpec,
16740
+ PATHEXT: process.env.PATHEXT,
16741
+ PSModulePath: process.env.PSModulePath
16742
+ });
16743
+ } else if (platform === "darwin") {
16744
+ identity = await runSmallIdentityCommand("sysctl", ["-n", "kern.boottime"], void 0);
16745
+ }
16746
+ if (!identity || /[\r\n\0]/u.test(identity)) {
16747
+ throw new Error("Inference host could not establish the current system boot identity.");
16748
+ }
16749
+ return `${platform}:${identity}`;
16750
+ };
16751
+ const pending = read();
16752
+ if (platform === process.platform) cachedSystemBootIdentity = pending;
16753
+ try {
16754
+ return await pending;
16755
+ } catch (error48) {
16756
+ if (platform === process.platform) cachedSystemBootIdentity = null;
16757
+ throw error48;
16758
+ }
16759
+ };
16760
+ defaultProcessIdentity = async (pid) => {
16761
+ if (process.platform === "linux") return await linuxProcessIdentity(pid);
16762
+ if (process.platform === "win32") return await windowsProcessIdentity(pid);
16763
+ if (process.platform === "darwin") return await darwinProcessIdentity(pid);
16764
+ return null;
16765
+ };
16766
+ defaultCurrentProcessIdentity = async () => {
16767
+ const identity = await defaultProcessIdentity(process.pid);
16768
+ if (!identity) throw new Error("Inference host could not establish its process-start identity.");
16769
+ return identity;
16770
+ };
16771
+ processLockObservationCache = /* @__PURE__ */ new Map();
16772
+ inferenceProcessIdentitiesMatch = (stored, observed) => stored === observed;
16340
16773
  credentialStoreIdentity = (mode, filePath, env) => {
16341
16774
  if (mode === "file") {
16342
16775
  if (!filePath) throw new Error("Inference credential fallback path is unavailable.");
@@ -16354,7 +16787,7 @@ var init_config = __esm({
16354
16787
 
16355
16788
  // lib/inference-host/credential-store.ts
16356
16789
  import { createHash } from "node:crypto";
16357
- import { spawn } from "node:child_process";
16790
+ import { spawn as spawn2 } from "node:child_process";
16358
16791
  function canonicalUrl(raw, label) {
16359
16792
  let parsed;
16360
16793
  try {
@@ -16520,8 +16953,8 @@ var init_credential_store = __esm({
16520
16953
  }
16521
16954
  return;
16522
16955
  }
16523
- await new Promise((resolve5) => {
16524
- const terminator = spawn(
16956
+ await new Promise((resolve6) => {
16957
+ const terminator = spawn2(
16525
16958
  "taskkill.exe",
16526
16959
  ["/PID", String(pid), "/T", "/F"],
16527
16960
  {
@@ -16538,21 +16971,21 @@ var init_credential_store = __esm({
16538
16971
  const timer = setTimeout(() => {
16539
16972
  terminator.kill("SIGKILL");
16540
16973
  child.kill("SIGKILL");
16541
- resolve5();
16974
+ resolve6();
16542
16975
  }, CREDENTIAL_COMMAND_TERMINATION_TIMEOUT_MS);
16543
16976
  terminator.once("error", () => {
16544
16977
  clearTimeout(timer);
16545
16978
  child.kill("SIGKILL");
16546
- resolve5();
16979
+ resolve6();
16547
16980
  });
16548
16981
  terminator.once("close", () => {
16549
16982
  clearTimeout(timer);
16550
16983
  if (child.exitCode === null) child.kill("SIGKILL");
16551
- resolve5();
16984
+ resolve6();
16552
16985
  });
16553
16986
  });
16554
16987
  };
16555
- runCredentialCommand = async (command, options) => await new Promise((resolve5, reject) => {
16988
+ runCredentialCommand = async (command, options) => await new Promise((resolve6, reject) => {
16556
16989
  const timeoutMs = options.timeoutMs ?? DEFAULT_CREDENTIAL_COMMAND_TIMEOUT_MS;
16557
16990
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1) {
16558
16991
  reject(new Error("OS credential command timeout must be a positive integer."));
@@ -16562,7 +16995,7 @@ var init_credential_store = __esm({
16562
16995
  reject(new Error("OS credential command was aborted."));
16563
16996
  return;
16564
16997
  }
16565
- const child = spawn(command.command, command.args, {
16998
+ const child = spawn2(command.command, command.args, {
16566
16999
  detached: true,
16567
17000
  shell: false,
16568
17001
  stdio: ["pipe", "pipe", "pipe"],
@@ -16611,7 +17044,7 @@ var init_credential_store = __esm({
16611
17044
  const transformedStdout = normalizedExitCode === 0 ? command.transformStdout?.(stdout) ?? stdout : stdout;
16612
17045
  settled = true;
16613
17046
  cleanup();
16614
- resolve5({
17047
+ resolve6({
16615
17048
  exitCode: normalizedExitCode,
16616
17049
  stdout: transformedStdout
16617
17050
  });
@@ -17403,11 +17836,13 @@ var init_mcp_client = __esm({
17403
17836
  }).passthrough().parse(rawResult);
17404
17837
  if (callResult.isError === true) {
17405
17838
  const errorText = (callResult.content ?? []).filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n");
17406
- const definitivelyNotApplied = (name === "inference.host.register" || name === "inference.host.advertise") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.")) || (name === "inference.agent.next" || name === "inference.agent.heartbeat") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.") || errorText.includes("Heartbeat time is outside the allowed clock skew.")) || name === "inference.job.claim" && (errorText.includes(
17839
+ const definitivelyNotApplied = (name === "inference.host.register" || name === "inference.host.advertise") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.")) || (name === "inference.agent.next" || name === "inference.agent.heartbeat") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.") || errorText.includes("Heartbeat time is outside the allowed clock skew.")) || name === "inference.host.heartbeat" && errorText.includes("Heartbeat time is outside the allowed clock skew.") || name === "inference.job.claim" && (errorText.includes(
17407
17840
  "External inference claim request was not applied before its generation became stale"
17408
17841
  ) || errorText.includes("External inference host advertisement is expired or superseded"));
17409
17842
  const retryableInfrastructureRejection = EXTERNAL_INFERENCE_AUTOMATED_HOST_TOOLS.includes(name) && (errorText.includes("External inference polling is at its configured concurrency limit") || errorText.includes("The Insights action reached the response timeout") || errorText.includes("Insights capability failed without exposing private runtime details"));
17410
17843
  const retryableClaimRejection = name === "inference.job.claim" && !definitivelyNotApplied && (retryableInfrastructureRejection || errorText.includes("External inference host is not live enough to claim work") || errorText.includes("External inference host request generation is stale"));
17844
+ const retryableHeartbeatClockSkew = name === "inference.host.heartbeat" && definitivelyNotApplied;
17845
+ const retryableAdvertisementClockSkew = (name === "inference.host.register" || name === "inference.host.advertise") && definitivelyNotApplied;
17411
17846
  throw new ExternalInferenceMcpError(
17412
17847
  "tool_rejected",
17413
17848
  `Insights MCP rejected ${name}.`,
@@ -17417,7 +17852,7 @@ var init_mcp_client = __esm({
17417
17852
  // API rotation the MCP server can return a tool error after the
17418
17853
  // request reached the application boundary, so retain and replay
17419
17854
  // the exact request unless the server proves it was not applied.
17420
- retryable: retryableInfrastructureRejection || retryableClaimRejection
17855
+ retryable: retryableInfrastructureRejection || retryableClaimRejection || retryableHeartbeatClockSkew || retryableAdvertisementClockSkew
17421
17856
  }
17422
17857
  );
17423
17858
  }
@@ -17823,7 +18258,7 @@ async function registerInferenceOAuthClient(metadata, redirectUri, fetchImpl = f
17823
18258
  grant_types: ["authorization_code", "refresh_token"],
17824
18259
  response_types: ["code"],
17825
18260
  application_type: "native",
17826
- client_name: "VTX Codex inference host",
18261
+ client_name: "VTX inference host",
17827
18262
  scope: INFERENCE_SCOPE
17828
18263
  })
17829
18264
  }, requestOptions);
@@ -18070,11 +18505,11 @@ async function revokeInferenceCredential(options) {
18070
18505
  }
18071
18506
  }
18072
18507
  async function listenLoopback(server) {
18073
- await new Promise((resolve5, reject) => {
18508
+ await new Promise((resolve6, reject) => {
18074
18509
  server.once("error", reject);
18075
18510
  server.listen(0, CALLBACK_HOST, () => {
18076
18511
  server.off("error", reject);
18077
- resolve5();
18512
+ resolve6();
18078
18513
  });
18079
18514
  });
18080
18515
  const address = server.address();
@@ -18085,7 +18520,7 @@ async function listenLoopback(server) {
18085
18520
  }
18086
18521
  async function closeServer(server) {
18087
18522
  if (!server.listening) return;
18088
- await new Promise((resolve5) => server.close(() => resolve5()));
18523
+ await new Promise((resolve6) => server.close(() => resolve6()));
18089
18524
  }
18090
18525
  async function beginInferenceOAuthLogin(options) {
18091
18526
  const loginTimeoutMs = options.timeoutMs ?? 6e5;
@@ -18141,7 +18576,7 @@ async function beginInferenceOAuthLogin(options) {
18141
18576
  });
18142
18577
  let settled = false;
18143
18578
  let rejectCompletion;
18144
- const completion = new Promise((resolve5, reject) => {
18579
+ const completion = new Promise((resolve6, reject) => {
18145
18580
  rejectCompletion = reject;
18146
18581
  handler = async (requestUrl, method, writeResponse) => {
18147
18582
  if (settled) {
@@ -18216,7 +18651,7 @@ async function beginInferenceOAuthLogin(options) {
18216
18651
  '<!doctype html><html><head><meta charset="utf-8"><title>VTX authorization complete</title></head><body><main><h1>VTX authorization complete</h1><p>The VTX CLI received your approval. You can close this tab and return to the terminal.</p></main></body></html>'
18217
18652
  );
18218
18653
  await closeServer(server);
18219
- resolve5({
18654
+ resolve6({
18220
18655
  accessToken: tokens.accessToken,
18221
18656
  expiresIn: tokens.expiresIn,
18222
18657
  credential,
@@ -18330,7 +18765,7 @@ async function createInferenceAgentMcpSession(options) {
18330
18765
  options.fetchImpl,
18331
18766
  { signal: options.signal, timeoutMs: options.requestTimeoutMs }
18332
18767
  );
18333
- let access3 = null;
18768
+ let access4 = null;
18334
18769
  let accessExpiresAt = 0;
18335
18770
  let refreshPromise = null;
18336
18771
  const refreshAccessToken = async (requestOptions = {}) => {
@@ -18347,7 +18782,7 @@ async function createInferenceAgentMcpSession(options) {
18347
18782
  });
18348
18783
  assertCredentialMatchesState(localState, refreshed.credential);
18349
18784
  credential = refreshed.credential;
18350
- access3 = refreshed;
18785
+ access4 = refreshed;
18351
18786
  accessExpiresAt = Date.now() + refreshed.expiresIn * 1e3;
18352
18787
  return refreshed.accessToken;
18353
18788
  })();
@@ -18360,10 +18795,10 @@ async function createInferenceAgentMcpSession(options) {
18360
18795
  await refreshAccessToken({ signal: options.signal });
18361
18796
  const tokenSource = {
18362
18797
  accessToken: async (requestOptions) => {
18363
- if (!access3 || accessExpiresAt - Date.now() <= 5e3) {
18798
+ if (!access4 || accessExpiresAt - Date.now() <= 5e3) {
18364
18799
  return refreshAccessToken(requestOptions);
18365
18800
  }
18366
- return access3.accessToken;
18801
+ return access4.accessToken;
18367
18802
  },
18368
18803
  refreshAccessToken
18369
18804
  };
@@ -18516,10 +18951,10 @@ var init_agent_state = __esm({
18516
18951
  });
18517
18952
 
18518
18953
  // lib/inference-host/codex-app-server.ts
18519
- import { spawn as spawn2 } from "node:child_process";
18520
- import { chmod, lstat as lstat2, readFile as readFile2, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
18954
+ import { spawn as spawn3 } from "node:child_process";
18955
+ import { chmod, lstat as lstat2, open as open2, readFile as readFile2, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
18521
18956
  import { tmpdir } from "node:os";
18522
- import { isAbsolute, join as join2, resolve as resolve2, sep } from "node:path";
18957
+ import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2, sep } from "node:path";
18523
18958
  import { createInterface } from "node:readline";
18524
18959
  async function loginCodexSubscription(options) {
18525
18960
  const session = await CodexAppServerSession.start(options);
@@ -18585,6 +19020,7 @@ var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServer
18585
19020
  var init_codex_app_server = __esm({
18586
19021
  "lib/inference-host/codex-app-server.ts"() {
18587
19022
  "use strict";
19023
+ init_config();
18588
19024
  CODEX_INFERENCE_PERMISSION_PROFILE = "vtx_inference_readonly";
18589
19025
  CODEX_ACCOUNT_PLAN_TYPES = [
18590
19026
  "free",
@@ -18818,8 +19254,8 @@ var init_codex_app_server = __esm({
18818
19254
  };
18819
19255
  };
18820
19256
  killWindowsProcessTree = async (pid) => {
18821
- await new Promise((resolve5, reject) => {
18822
- const child = spawn2(
19257
+ await new Promise((resolve6, reject) => {
19258
+ const child = spawn3(
18823
19259
  "taskkill.exe",
18824
19260
  ["/PID", String(pid), "/T", "/F"],
18825
19261
  {
@@ -18843,7 +19279,7 @@ var init_codex_app_server = __esm({
18843
19279
  });
18844
19280
  child.once("close", (code) => {
18845
19281
  clearTimeout(timer);
18846
- if (code === 0 || code === 128) resolve5();
19282
+ if (code === 0 || code === 128) resolve6();
18847
19283
  else reject(new Error("Windows Codex process-tree cleanup failed."));
18848
19284
  });
18849
19285
  });
@@ -18877,7 +19313,8 @@ var init_codex_app_server = __esm({
18877
19313
  };
18878
19314
  GUARDIAN_SCRIPT = String.raw`
18879
19315
  import { spawn } from 'node:child_process';
18880
- import { chmod, rename, rm, writeFile } from 'node:fs/promises';
19316
+ import { chmod, open, rename, rm, writeFile } from 'node:fs/promises';
19317
+ import { dirname } from 'node:path';
18881
19318
  const input = JSON.parse(Buffer.from(process.argv[1], 'base64url').toString('utf8'));
18882
19319
  // The guardian owns the process boundary for every attempt. Apply a private
18883
19320
  // umask before writing receipts or starting Codex so a lost thread/start reply
@@ -18914,6 +19351,7 @@ const writeReceipt = (state, childPid) => {
18914
19351
  state,
18915
19352
  guardian_pid: process.pid,
18916
19353
  child_pid: childPid ?? null,
19354
+ boot_identity: input.bootIdentity,
18917
19355
  updated_at: new Date().toISOString(),
18918
19356
  }) + '\n';
18919
19357
  receiptWriteSequence += 1;
@@ -18921,7 +19359,13 @@ const writeReceipt = (state, childPid) => {
18921
19359
  try {
18922
19360
  await writeFile(temporary, body, { mode: 0o600 });
18923
19361
  await chmod(temporary, 0o600).catch(() => undefined);
19362
+ const file = await open(temporary, 'r+');
19363
+ try { await file.sync(); } finally { await file.close(); }
18924
19364
  await replaceReceipt(temporary);
19365
+ if (process.platform !== 'win32') {
19366
+ const directory = await open(dirname(input.receiptPath), 'r');
19367
+ try { await directory.sync(); } finally { await directory.close(); }
19368
+ }
18925
19369
  } finally {
18926
19370
  await rm(temporary, { force: true }).catch(() => undefined);
18927
19371
  }
@@ -18958,6 +19402,7 @@ child = spawn(input.binary, input.args, {
18958
19402
  windowsHide: true,
18959
19403
  detached: false,
18960
19404
  });
19405
+ child.stdin.on('error', () => undefined);
18961
19406
  process.stdin.pipe(child.stdin, { end: false });
18962
19407
  child.stdout.pipe(process.stdout);
18963
19408
  child.stderr.pipe(process.stderr);
@@ -18971,9 +19416,14 @@ child.once('spawn', async () => {
18971
19416
  child.once('error', shutdown);
18972
19417
  child.once('close', async () => {
18973
19418
  if (forceTimer) clearTimeout(forceTimer);
19419
+ // A pending promise alone does not keep Node's event loop alive after the
19420
+ // child and parent stdin have both closed. Hold one local handle until the
19421
+ // serialized terminal tombstone is durably replaced.
19422
+ const receiptKeepAlive = setInterval(() => undefined, 1_000);
18974
19423
  try {
18975
19424
  await writeReceipt('terminated', child.pid ?? null);
18976
19425
  } finally {
19426
+ clearInterval(receiptKeepAlive);
18977
19427
  process.exit(0);
18978
19428
  }
18979
19429
  });
@@ -18989,7 +19439,7 @@ child.once('close', async () => {
18989
19439
  const platform = options.platform ?? process.platform;
18990
19440
  const renameFile = options.renameFile ?? rename2;
18991
19441
  const removeFile = options.removeFile ?? ((path) => rm2(path, { force: true }));
18992
- const sleep4 = options.sleep ?? ((milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds)));
19442
+ const sleep4 = options.sleep ?? ((milliseconds) => new Promise((resolve6) => setTimeout(resolve6, milliseconds)));
18993
19443
  const maxAttempts = Math.max(1, options.maxAttempts ?? 40);
18994
19444
  const retryDelayMs = Math.max(0, options.retryDelayMs ?? 25);
18995
19445
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
@@ -19020,20 +19470,38 @@ child.once('close', async () => {
19020
19470
  state: "spawn_intent",
19021
19471
  guardian_pid: null,
19022
19472
  child_pid: null,
19473
+ boot_identity: guardian.bootIdentity ?? null,
19023
19474
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
19024
19475
  })}
19025
19476
  `;
19026
19477
  const temporary = `${guardian.receiptPath}.intent-${process.pid}`;
19027
19478
  await writeFile(temporary, body, { mode: 384 });
19028
19479
  await chmod(temporary, 384).catch(() => void 0);
19480
+ const handle = await open2(temporary, "r+");
19481
+ try {
19482
+ await handle.sync();
19483
+ } finally {
19484
+ await handle.close();
19485
+ }
19029
19486
  await replaceCodexGuardianReceiptFile(temporary, guardian.receiptPath);
19487
+ if (process.platform !== "win32") {
19488
+ const directory = await open2(dirname2(guardian.receiptPath), "r");
19489
+ try {
19490
+ await directory.sync();
19491
+ } finally {
19492
+ await directory.close();
19493
+ }
19494
+ }
19030
19495
  };
19031
19496
  parseGuardianReceipt = (raw) => {
19032
19497
  const value = JSON.parse(raw);
19033
- if (value.schema_version !== "vtx_codex_guardian_v1" || typeof value.process_token !== "string" || !/^[0-9a-f]{64}$/u.test(value.process_token) || !["spawn_intent", "starting", "running", "terminated"].includes(String(value.state)) || value.guardian_pid !== null && !Number.isSafeInteger(value.guardian_pid) || value.state === "spawn_intent" && value.guardian_pid !== null || value.state !== "spawn_intent" && !Number.isSafeInteger(value.guardian_pid) || value.child_pid !== null && !Number.isSafeInteger(value.child_pid) || typeof value.updated_at !== "string" || !Number.isFinite(Date.parse(value.updated_at))) {
19498
+ if (value.schema_version !== "vtx_codex_guardian_v1" || typeof value.process_token !== "string" || !/^[0-9a-f]{64}$/u.test(value.process_token) || !["spawn_intent", "starting", "running", "terminated"].includes(String(value.state)) || value.guardian_pid !== null && !Number.isSafeInteger(value.guardian_pid) || value.state === "spawn_intent" && value.guardian_pid !== null || value.state !== "spawn_intent" && !Number.isSafeInteger(value.guardian_pid) || value.child_pid !== null && !Number.isSafeInteger(value.child_pid) || value.boot_identity !== null && value.boot_identity !== void 0 && typeof value.boot_identity !== "string" || typeof value.updated_at !== "string" || !Number.isFinite(Date.parse(value.updated_at))) {
19034
19499
  throw new Error("Codex guardian receipt is invalid.");
19035
19500
  }
19036
- return value;
19501
+ return {
19502
+ ...value,
19503
+ boot_identity: typeof value.boot_identity === "string" ? value.boot_identity : null
19504
+ };
19037
19505
  };
19038
19506
  readCodexGuardianReceipt = async (path) => {
19039
19507
  try {
@@ -19059,14 +19527,14 @@ child.once('close', async () => {
19059
19527
  throw new Error("Codex guardian terminated before app-server initialization.");
19060
19528
  }
19061
19529
  }
19062
- await new Promise((resolve5) => setTimeout(resolve5, 25));
19530
+ await new Promise((resolve6) => setTimeout(resolve6, 25));
19063
19531
  }
19064
19532
  throw new Error(`Codex guardian ${expectedState} state was not confirmed.`);
19065
19533
  };
19066
19534
  defaultSpawn = (binary, args, options) => {
19067
19535
  if (!options.guardian) {
19068
19536
  const { guardian: _guardian, ...spawnOptions } = options;
19069
- return spawn2(binary, args, {
19537
+ return spawn3(binary, args, {
19070
19538
  ...spawnOptions,
19071
19539
  detached: options.detached
19072
19540
  });
@@ -19076,9 +19544,10 @@ child.once('close', async () => {
19076
19544
  args,
19077
19545
  processToken: options.guardian.processToken,
19078
19546
  receiptPath: options.guardian.receiptPath,
19079
- shutdownGraceMs: Math.max(25, options.guardian.shutdownGraceMs ?? 2e3)
19547
+ shutdownGraceMs: Math.max(25, options.guardian.shutdownGraceMs ?? 2e3),
19548
+ bootIdentity: options.guardian.bootIdentity
19080
19549
  }), "utf8").toString("base64url");
19081
- return spawn2(process.execPath, ["--input-type=module", "--eval", GUARDIAN_SCRIPT, payload], {
19550
+ return spawn3(process.execPath, ["--input-type=module", "--eval", GUARDIAN_SCRIPT, payload], {
19082
19551
  env: options.env,
19083
19552
  stdio: options.stdio,
19084
19553
  windowsHide: options.windowsHide,
@@ -19105,9 +19574,13 @@ child.once('close', async () => {
19105
19574
  }
19106
19575
  static async start(options) {
19107
19576
  const spawnProcess = options.spawnProcess ?? defaultSpawn;
19108
- if (options.guardian) {
19577
+ const guardian = options.guardian ? {
19578
+ ...options.guardian,
19579
+ bootIdentity: options.guardian.bootIdentity ?? await readInferenceSystemBootIdentity()
19580
+ } : void 0;
19581
+ if (guardian) {
19109
19582
  try {
19110
- await writeCodexGuardianSpawnIntent(options.guardian);
19583
+ await writeCodexGuardianSpawnIntent(guardian);
19111
19584
  } catch (error48) {
19112
19585
  throw new CodexAppServerError({
19113
19586
  message: "Codex guardian spawn intent could not be persisted.",
@@ -19123,17 +19596,32 @@ child.once('close', async () => {
19123
19596
  stdio: ["pipe", "pipe", "pipe"],
19124
19597
  windowsHide: true,
19125
19598
  detached: true,
19126
- guardian: options.guardian
19599
+ guardian
19127
19600
  });
19128
- if (options.guardian) {
19601
+ if (guardian) {
19129
19602
  try {
19130
19603
  await waitForCodexGuardianState(
19131
- options.guardian,
19604
+ guardian,
19132
19605
  "running",
19133
19606
  Math.min(options.deadlineAtMs, Date.now() + 1e4)
19134
19607
  );
19135
19608
  } catch (error48) {
19136
19609
  child.stdin.end();
19610
+ try {
19611
+ await waitForCodexGuardianState(
19612
+ guardian,
19613
+ "terminated",
19614
+ Date.now() + 5e3
19615
+ );
19616
+ } catch (terminationError) {
19617
+ throw new CodexAppServerError({
19618
+ message: "Codex guardian startup failed and termination was not confirmed.",
19619
+ category: "transport",
19620
+ code: "codex_guardian_termination_unconfirmed",
19621
+ retryable: false,
19622
+ cause: terminationError
19623
+ });
19624
+ }
19137
19625
  throw new CodexAppServerError({
19138
19626
  message: "Codex guardian startup could not be confirmed.",
19139
19627
  category: "transport",
@@ -19168,6 +19656,23 @@ child.once('close', async () => {
19168
19656
  return session;
19169
19657
  } catch (error48) {
19170
19658
  await session.close().catch(() => void 0);
19659
+ if (guardian) {
19660
+ try {
19661
+ await waitForCodexGuardianState(
19662
+ guardian,
19663
+ "terminated",
19664
+ Date.now() + 5e3
19665
+ );
19666
+ } catch (terminationError) {
19667
+ throw new CodexAppServerError({
19668
+ message: "Codex app-server failed and guardian termination was not confirmed.",
19669
+ category: "transport",
19670
+ code: "codex_guardian_termination_unconfirmed",
19671
+ retryable: false,
19672
+ cause: terminationError
19673
+ });
19674
+ }
19675
+ }
19171
19676
  throw error48;
19172
19677
  }
19173
19678
  }
@@ -19285,8 +19790,8 @@ child.once('close', async () => {
19285
19790
  retryable: false
19286
19791
  });
19287
19792
  }
19288
- return await new Promise((resolve5, reject) => {
19289
- const pending = { resolve: resolve5, reject, timer: null, abortCleanup: null };
19793
+ return await new Promise((resolve6, reject) => {
19794
+ const pending = { resolve: resolve6, reject, timer: null, abortCleanup: null };
19290
19795
  pending.timer = setTimeout(() => {
19291
19796
  this.pending.delete(id2);
19292
19797
  pending.abortCleanup?.();
@@ -19674,8 +20179,8 @@ child.once('close', async () => {
19674
20179
  let terminal = null;
19675
20180
  let terminalResolve;
19676
20181
  let terminalReject;
19677
- const terminalPromise = new Promise((resolve5, reject) => {
19678
- terminalResolve = resolve5;
20182
+ const terminalPromise = new Promise((resolve6, reject) => {
20183
+ terminalResolve = resolve6;
19679
20184
  terminalReject = reject;
19680
20185
  });
19681
20186
  void terminalPromise.catch(() => void 0);
@@ -20096,15 +20601,15 @@ child.once('close', async () => {
20096
20601
  if (this.closed) return;
20097
20602
  this.closed = true;
20098
20603
  this.process.stdin.end();
20099
- const exited = await new Promise((resolve5) => {
20604
+ const exited = await new Promise((resolve6) => {
20100
20605
  if (this.process.exitCode !== null) {
20101
- resolve5(true);
20606
+ resolve6(true);
20102
20607
  return;
20103
20608
  }
20104
- const timer = setTimeout(() => resolve5(false), 5e3);
20609
+ const timer = setTimeout(() => resolve6(false), 5e3);
20105
20610
  this.process.once("close", () => {
20106
20611
  clearTimeout(timer);
20107
- resolve5(true);
20612
+ resolve6(true);
20108
20613
  });
20109
20614
  });
20110
20615
  if (exited) return;
@@ -20115,11 +20620,11 @@ child.once('close', async () => {
20115
20620
  else this.process.kill("SIGTERM");
20116
20621
  } catch {
20117
20622
  }
20118
- const terminated = await new Promise((resolve5) => {
20119
- const timer = setTimeout(() => resolve5(false), 1e3);
20623
+ const terminated = await new Promise((resolve6) => {
20624
+ const timer = setTimeout(() => resolve6(false), 1e3);
20120
20625
  this.process.once("close", () => {
20121
20626
  clearTimeout(timer);
20122
- resolve5(true);
20627
+ resolve6(true);
20123
20628
  });
20124
20629
  });
20125
20630
  if (!terminated) {
@@ -20129,15 +20634,15 @@ child.once('close', async () => {
20129
20634
  else this.process.kill("SIGKILL");
20130
20635
  } catch {
20131
20636
  }
20132
- await new Promise((resolve5) => {
20637
+ await new Promise((resolve6) => {
20133
20638
  if (this.process.exitCode !== null) {
20134
- resolve5();
20639
+ resolve6();
20135
20640
  return;
20136
20641
  }
20137
- const timer = setTimeout(resolve5, 1e3);
20642
+ const timer = setTimeout(resolve6, 1e3);
20138
20643
  this.process.once("close", () => {
20139
20644
  clearTimeout(timer);
20140
- resolve5();
20645
+ resolve6();
20141
20646
  });
20142
20647
  });
20143
20648
  if (this.process.exitCode === null) {
@@ -20159,8 +20664,8 @@ import { createHash as createHash3 } from "node:crypto";
20159
20664
  import { constants as fsConstants } from "node:fs";
20160
20665
  import { access, readFile as readFile3, realpath as realpath2 } from "node:fs/promises";
20161
20666
  import { createRequire } from "node:module";
20162
- import { dirname as dirname2, join as join3 } from "node:path";
20163
- import { spawn as spawn3 } from "node:child_process";
20667
+ import { dirname as dirname3, join as join3 } from "node:path";
20668
+ import { spawn as spawn4 } from "node:child_process";
20164
20669
  var PINNED_CODEX_CLI_VERSION, PINNED_CODEX_LINUX_X64_SHA256, PINNED_CODEX_WINDOWS_X64_SHA256, pinnedShaForPlatform, pinnedPackageForPlatform, resolvePinnedCodexPackageBinaryPath, readCodexVersion, verifyPinnedCodexBinary, resolvePinnedCodexBinary;
20165
20670
  var init_codex_binary = __esm({
20166
20671
  "lib/inference-host/codex-binary.ts"() {
@@ -20197,15 +20702,15 @@ var init_codex_binary = __esm({
20197
20702
  const spec = pinnedPackageForPlatform(platform, architecture);
20198
20703
  const packageJsonPath = resolvePackage(`${spec.packageName}/package.json`);
20199
20704
  return join3(
20200
- dirname2(packageJsonPath),
20705
+ dirname3(packageJsonPath),
20201
20706
  "vendor",
20202
20707
  spec.target,
20203
20708
  "bin",
20204
20709
  spec.binaryName
20205
20710
  );
20206
20711
  };
20207
- readCodexVersion = async (binaryPath) => await new Promise((resolve5, reject) => {
20208
- const child = spawn3(binaryPath, ["--version"], {
20712
+ readCodexVersion = async (binaryPath) => await new Promise((resolve6, reject) => {
20713
+ const child = spawn4(binaryPath, ["--version"], {
20209
20714
  stdio: ["ignore", "pipe", "pipe"],
20210
20715
  windowsHide: true,
20211
20716
  env: {
@@ -20242,7 +20747,7 @@ var init_codex_binary = __esm({
20242
20747
  reject(new Error("Codex version probe failed."));
20243
20748
  return;
20244
20749
  }
20245
- resolve5(stdout.trim());
20750
+ resolve6(stdout.trim());
20246
20751
  });
20247
20752
  });
20248
20753
  verifyPinnedCodexBinary = async (candidatePath, dependencies = {}) => {
@@ -20302,7 +20807,7 @@ import {
20302
20807
  } from "node:fs/promises";
20303
20808
  import { randomBytes as randomBytes3 } from "node:crypto";
20304
20809
  import { tmpdir as tmpdir2 } from "node:os";
20305
- import { isAbsolute as isAbsolute2, join as join4, relative, resolve as resolve3, sep as sep2 } from "node:path";
20810
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
20306
20811
  var INITIAL_CODEX_INFERENCE_MODEL, SUPPORTED_CODEX_REASONING_EFFORTS, MAX_PROMPT_BYTES2, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
20307
20812
  var init_codex_adapter = __esm({
20308
20813
  "lib/inference-host/codex-adapter.ts"() {
@@ -20368,6 +20873,7 @@ var init_codex_adapter = __esm({
20368
20873
  processToken: external_exports.string().regex(/^[0-9a-f]{64}$/u).nullable(),
20369
20874
  processReceiptPath: external_exports.string().min(1).max(4096).nullable(),
20370
20875
  processState: external_exports.enum(["unmanaged", "spawn_intent", "running", "terminated"]),
20876
+ bootIdentity: external_exports.string().min(1).max(4096).nullable().optional(),
20371
20877
  cleanupConfirmed: external_exports.boolean(),
20372
20878
  adapterRequestId: external_exports.string().min(1).max(512).nullable(),
20373
20879
  adapterResponseId: external_exports.string().min(1).max(512).nullable(),
@@ -20611,7 +21117,7 @@ var init_codex_adapter = __esm({
20611
21117
  isStrictDescendant = (candidate, parent) => {
20612
21118
  const parentPath = resolve3(parent);
20613
21119
  const candidatePath = resolve3(candidate);
20614
- const scoped = relative(parentPath, candidatePath);
21120
+ const scoped = relative2(parentPath, candidatePath);
20615
21121
  return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute2(scoped);
20616
21122
  };
20617
21123
  assertRecoveryResourceScope = (checkpoint) => {
@@ -20621,7 +21127,7 @@ var init_codex_adapter = __esm({
20621
21127
  if (!isStrictDescendant(resourceRoot, temporaryRoot)) {
20622
21128
  throw new Error("Codex recovery resource root crossed temporary scope.");
20623
21129
  }
20624
- const parts = relative(temporaryRoot, resourceRoot).split(sep2);
21130
+ const parts = relative2(temporaryRoot, resourceRoot).split(sep2);
20625
21131
  const standalone = parts.length === 1 && parts[0].startsWith("vtx-codex-attempt-") && workspacePath === join4(resourceRoot, "workspace");
20626
21132
  const hosted = parts.length === 3 && parts[0].startsWith("vtx-codex-host-") && parts[1] === "workspaces" && parts[2].startsWith("attempt-") && workspacePath === resourceRoot;
20627
21133
  if (!standalone && !hosted) {
@@ -20745,6 +21251,7 @@ var init_codex_adapter = __esm({
20745
21251
  };
20746
21252
  reconcileCodexAttemptRecovery = async (options) => {
20747
21253
  const attempts = await options.recoveryHooks.loadAll?.() ?? {};
21254
+ const currentBootIdentity = options.bootIdentity ?? await readInferenceSystemBootIdentity();
20748
21255
  let reconciled = 0;
20749
21256
  for (const checkpoint of Object.values(attempts)) {
20750
21257
  if (checkpoint.cleanupConfirmed) continue;
@@ -20755,18 +21262,29 @@ var init_codex_adapter = __esm({
20755
21262
  processToken: checkpoint.processToken,
20756
21263
  receiptPath: checkpoint.processReceiptPath
20757
21264
  };
20758
- await confirmGuardianTerminatedForRecovery(
20759
- checkpoint,
20760
- guardian,
20761
- options.deadlineMs ?? 5e3
21265
+ const crossedBoot = Boolean(
21266
+ checkpoint.bootIdentity && checkpoint.bootIdentity !== currentBootIdentity
20762
21267
  );
21268
+ if (crossedBoot) {
21269
+ const receipt = await readCodexGuardianReceipt(guardian.receiptPath);
21270
+ if (receipt && (receipt.process_token !== checkpoint.processToken || receipt.boot_identity !== checkpoint.bootIdentity)) {
21271
+ throw new Error("Codex cross-boot recovery receipt ownership is unconfirmed.");
21272
+ }
21273
+ } else {
21274
+ await confirmGuardianTerminatedForRecovery(
21275
+ checkpoint,
21276
+ guardian,
21277
+ options.deadlineMs ?? 5e3
21278
+ );
21279
+ }
20763
21280
  assertRecoveryResourceScope(checkpoint);
20764
21281
  await removeRecoveredThread(checkpoint, options.codexHome);
20765
21282
  await rm3(checkpoint.resourceRoot, { recursive: true, force: true });
20766
21283
  await options.recoveryHooks.save({
20767
21284
  ...checkpoint,
20768
21285
  processState: "terminated",
20769
- cleanupConfirmed: true
21286
+ cleanupConfirmed: true,
21287
+ dispatchOutcome: crossedBoot ? "outcome_unknown" : checkpoint.dispatchOutcome
20770
21288
  });
20771
21289
  reconciled += 1;
20772
21290
  }
@@ -20904,6 +21422,7 @@ var init_codex_adapter = __esm({
20904
21422
  await access2(resources.codexHome, fsConstants2.R_OK | fsConstants2.W_OK);
20905
21423
  await access2(resources.workspacePath, fsConstants2.R_OK);
20906
21424
  const guardianManaged = Boolean(recoveryHooks && !this.dependencies.spawnProcess);
21425
+ const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
20907
21426
  const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
20908
21427
  const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
20909
21428
  if (guardianReceiptRoot) {
@@ -20922,6 +21441,7 @@ var init_codex_adapter = __esm({
20922
21441
  processToken,
20923
21442
  processReceiptPath,
20924
21443
  processState: guardianManaged ? "spawn_intent" : "unmanaged",
21444
+ bootIdentity,
20925
21445
  cleanupConfirmed: false,
20926
21446
  adapterRequestId: null,
20927
21447
  adapterResponseId: null,
@@ -20936,7 +21456,7 @@ var init_codex_adapter = __esm({
20936
21456
  deadlineAtMs: input.deadlineAtMs,
20937
21457
  signal: input.signal,
20938
21458
  spawnProcess: this.dependencies.spawnProcess,
20939
- guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath } : void 0
21459
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
20940
21460
  });
20941
21461
  if (guardianManaged) {
20942
21462
  baseCheckpoint = { ...baseCheckpoint, processState: "running" };
@@ -22135,51 +22655,51 @@ var require_uri_all = __commonJS({
22135
22655
  }
22136
22656
  return uriTokens.join("");
22137
22657
  }
22138
- function resolveComponents(base2, relative2) {
22658
+ function resolveComponents(base2, relative3) {
22139
22659
  var options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
22140
22660
  var skipNormalization = arguments[3];
22141
22661
  var target = {};
22142
22662
  if (!skipNormalization) {
22143
22663
  base2 = parse3(serialize(base2, options), options);
22144
- relative2 = parse3(serialize(relative2, options), options);
22664
+ relative3 = parse3(serialize(relative3, options), options);
22145
22665
  }
22146
22666
  options = options || {};
22147
- if (!options.tolerant && relative2.scheme) {
22148
- target.scheme = relative2.scheme;
22149
- target.userinfo = relative2.userinfo;
22150
- target.host = relative2.host;
22151
- target.port = relative2.port;
22152
- target.path = removeDotSegments(relative2.path || "");
22153
- target.query = relative2.query;
22667
+ if (!options.tolerant && relative3.scheme) {
22668
+ target.scheme = relative3.scheme;
22669
+ target.userinfo = relative3.userinfo;
22670
+ target.host = relative3.host;
22671
+ target.port = relative3.port;
22672
+ target.path = removeDotSegments(relative3.path || "");
22673
+ target.query = relative3.query;
22154
22674
  } else {
22155
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
22156
- target.userinfo = relative2.userinfo;
22157
- target.host = relative2.host;
22158
- target.port = relative2.port;
22159
- target.path = removeDotSegments(relative2.path || "");
22160
- target.query = relative2.query;
22675
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
22676
+ target.userinfo = relative3.userinfo;
22677
+ target.host = relative3.host;
22678
+ target.port = relative3.port;
22679
+ target.path = removeDotSegments(relative3.path || "");
22680
+ target.query = relative3.query;
22161
22681
  } else {
22162
- if (!relative2.path) {
22682
+ if (!relative3.path) {
22163
22683
  target.path = base2.path;
22164
- if (relative2.query !== void 0) {
22165
- target.query = relative2.query;
22684
+ if (relative3.query !== void 0) {
22685
+ target.query = relative3.query;
22166
22686
  } else {
22167
22687
  target.query = base2.query;
22168
22688
  }
22169
22689
  } else {
22170
- if (relative2.path.charAt(0) === "/") {
22171
- target.path = removeDotSegments(relative2.path);
22690
+ if (relative3.path.charAt(0) === "/") {
22691
+ target.path = removeDotSegments(relative3.path);
22172
22692
  } else {
22173
22693
  if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) {
22174
- target.path = "/" + relative2.path;
22694
+ target.path = "/" + relative3.path;
22175
22695
  } else if (!base2.path) {
22176
- target.path = relative2.path;
22696
+ target.path = relative3.path;
22177
22697
  } else {
22178
- target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative2.path;
22698
+ target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative3.path;
22179
22699
  }
22180
22700
  target.path = removeDotSegments(target.path);
22181
22701
  }
22182
- target.query = relative2.query;
22702
+ target.query = relative3.query;
22183
22703
  }
22184
22704
  target.userinfo = base2.userinfo;
22185
22705
  target.host = base2.host;
@@ -22187,10 +22707,10 @@ var require_uri_all = __commonJS({
22187
22707
  }
22188
22708
  target.scheme = base2.scheme;
22189
22709
  }
22190
- target.fragment = relative2.fragment;
22710
+ target.fragment = relative3.fragment;
22191
22711
  return target;
22192
22712
  }
22193
- function resolve5(baseURI, relativeURI, options) {
22713
+ function resolve6(baseURI, relativeURI, options) {
22194
22714
  var schemelessOptions = assign({ scheme: "null" }, options);
22195
22715
  return serialize(resolveComponents(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true), schemelessOptions);
22196
22716
  }
@@ -22455,7 +22975,7 @@ var require_uri_all = __commonJS({
22455
22975
  exports2.removeDotSegments = removeDotSegments;
22456
22976
  exports2.serialize = serialize;
22457
22977
  exports2.resolveComponents = resolveComponents;
22458
- exports2.resolve = resolve5;
22978
+ exports2.resolve = resolve6;
22459
22979
  exports2.normalize = normalize;
22460
22980
  exports2.equal = equal;
22461
22981
  exports2.escapeComponent = escapeComponent;
@@ -22808,18 +23328,18 @@ var require_resolve = __commonJS({
22808
23328
  var util = require_util();
22809
23329
  var SchemaObject = require_schema_obj();
22810
23330
  var traverse = require_json_schema_traverse();
22811
- module.exports = resolve5;
22812
- resolve5.normalizeId = normalizeId;
22813
- resolve5.fullPath = getFullPath;
22814
- resolve5.url = resolveUrl;
22815
- resolve5.ids = resolveIds;
22816
- resolve5.inlineRef = inlineRef;
22817
- resolve5.schema = resolveSchema;
22818
- function resolve5(compile, root, ref) {
23331
+ module.exports = resolve6;
23332
+ resolve6.normalizeId = normalizeId;
23333
+ resolve6.fullPath = getFullPath;
23334
+ resolve6.url = resolveUrl;
23335
+ resolve6.ids = resolveIds;
23336
+ resolve6.inlineRef = inlineRef;
23337
+ resolve6.schema = resolveSchema;
23338
+ function resolve6(compile, root, ref) {
22819
23339
  var refVal = this._refs[ref];
22820
23340
  if (typeof refVal == "string") {
22821
23341
  if (this._refs[refVal]) refVal = this._refs[refVal];
22822
- else return resolve5.call(this, compile, root, refVal);
23342
+ else return resolve6.call(this, compile, root, refVal);
22823
23343
  }
22824
23344
  refVal = refVal || this._schemas[ref];
22825
23345
  if (refVal instanceof SchemaObject) {
@@ -23024,7 +23544,7 @@ var require_resolve = __commonJS({
23024
23544
  var require_error_classes = __commonJS({
23025
23545
  "node_modules/ajv/lib/compile/error_classes.js"(exports, module) {
23026
23546
  "use strict";
23027
- var resolve5 = require_resolve();
23547
+ var resolve6 = require_resolve();
23028
23548
  module.exports = {
23029
23549
  Validation: errorSubclass(ValidationError),
23030
23550
  MissingRef: errorSubclass(MissingRefError)
@@ -23039,8 +23559,8 @@ var require_error_classes = __commonJS({
23039
23559
  };
23040
23560
  function MissingRefError(baseId, ref, message) {
23041
23561
  this.message = message || MissingRefError.message(baseId, ref);
23042
- this.missingRef = resolve5.url(baseId, ref);
23043
- this.missingSchema = resolve5.normalizeId(resolve5.fullPath(this.missingRef));
23562
+ this.missingRef = resolve6.url(baseId, ref);
23563
+ this.missingSchema = resolve6.normalizeId(resolve6.fullPath(this.missingRef));
23044
23564
  }
23045
23565
  function errorSubclass(Subclass) {
23046
23566
  Subclass.prototype = Object.create(Error.prototype);
@@ -23568,7 +24088,7 @@ var require_validate = __commonJS({
23568
24088
  var require_compile = __commonJS({
23569
24089
  "node_modules/ajv/lib/compile/index.js"(exports, module) {
23570
24090
  "use strict";
23571
- var resolve5 = require_resolve();
24091
+ var resolve6 = require_resolve();
23572
24092
  var util = require_util();
23573
24093
  var errorClasses = require_error_classes();
23574
24094
  var stableStringify = require_fast_json_stable_stringify();
@@ -23630,7 +24150,7 @@ var require_compile = __commonJS({
23630
24150
  RULES,
23631
24151
  validate: validateGenerator,
23632
24152
  util,
23633
- resolve: resolve5,
24153
+ resolve: resolve6,
23634
24154
  resolveRef: resolveRef2,
23635
24155
  usePattern,
23636
24156
  useDefault,
@@ -23692,7 +24212,7 @@ var require_compile = __commonJS({
23692
24212
  return validate;
23693
24213
  }
23694
24214
  function resolveRef2(baseId2, ref, isRoot) {
23695
- ref = resolve5.url(baseId2, ref);
24215
+ ref = resolve6.url(baseId2, ref);
23696
24216
  var refIndex = refs[ref];
23697
24217
  var _refVal, refCode;
23698
24218
  if (refIndex !== void 0) {
@@ -23709,11 +24229,11 @@ var require_compile = __commonJS({
23709
24229
  }
23710
24230
  }
23711
24231
  refCode = addLocalRef(ref);
23712
- var v2 = resolve5.call(self2, localCompile, root, ref);
24232
+ var v2 = resolve6.call(self2, localCompile, root, ref);
23713
24233
  if (v2 === void 0) {
23714
24234
  var localSchema = localRefs && localRefs[ref];
23715
24235
  if (localSchema) {
23716
- v2 = resolve5.inlineRef(localSchema, opts.inlineRefs) ? localSchema : compile.call(self2, localSchema, root, localRefs, baseId2);
24236
+ v2 = resolve6.inlineRef(localSchema, opts.inlineRefs) ? localSchema : compile.call(self2, localSchema, root, localRefs, baseId2);
23717
24237
  }
23718
24238
  }
23719
24239
  if (v2 === void 0) {
@@ -27330,7 +27850,7 @@ var require_ajv = __commonJS({
27330
27850
  "node_modules/ajv/lib/ajv.js"(exports, module) {
27331
27851
  "use strict";
27332
27852
  var compileSchema = require_compile();
27333
- var resolve5 = require_resolve();
27853
+ var resolve6 = require_resolve();
27334
27854
  var Cache = require_cache();
27335
27855
  var SchemaObject = require_schema_obj();
27336
27856
  var stableStringify = require_fast_json_stable_stringify();
@@ -27412,7 +27932,7 @@ var require_ajv = __commonJS({
27412
27932
  var id2 = this._getId(schema);
27413
27933
  if (id2 !== void 0 && typeof id2 != "string")
27414
27934
  throw new Error("schema id must be string");
27415
- key = resolve5.normalizeId(key || id2);
27935
+ key = resolve6.normalizeId(key || id2);
27416
27936
  checkUnique(this, key);
27417
27937
  this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
27418
27938
  return this;
@@ -27456,7 +27976,7 @@ var require_ajv = __commonJS({
27456
27976
  }
27457
27977
  }
27458
27978
  function _getSchemaFragment(self2, ref) {
27459
- var res = resolve5.schema.call(self2, { schema: {} }, ref);
27979
+ var res = resolve6.schema.call(self2, { schema: {} }, ref);
27460
27980
  if (res) {
27461
27981
  var schema = res.schema, root = res.root, baseId = res.baseId;
27462
27982
  var v = compileSchema.call(self2, schema, root, void 0, baseId);
@@ -27472,7 +27992,7 @@ var require_ajv = __commonJS({
27472
27992
  }
27473
27993
  }
27474
27994
  function _getSchemaObj(self2, keyRef) {
27475
- keyRef = resolve5.normalizeId(keyRef);
27995
+ keyRef = resolve6.normalizeId(keyRef);
27476
27996
  return self2._schemas[keyRef] || self2._refs[keyRef] || self2._fragments[keyRef];
27477
27997
  }
27478
27998
  function removeSchema(schemaKeyRef) {
@@ -27499,7 +28019,7 @@ var require_ajv = __commonJS({
27499
28019
  this._cache.del(cacheKey);
27500
28020
  var id2 = this._getId(schemaKeyRef);
27501
28021
  if (id2) {
27502
- id2 = resolve5.normalizeId(id2);
28022
+ id2 = resolve6.normalizeId(id2);
27503
28023
  delete this._schemas[id2];
27504
28024
  delete this._refs[id2];
27505
28025
  }
@@ -27523,13 +28043,13 @@ var require_ajv = __commonJS({
27523
28043
  var cached2 = this._cache.get(cacheKey);
27524
28044
  if (cached2) return cached2;
27525
28045
  shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false;
27526
- var id2 = resolve5.normalizeId(this._getId(schema));
28046
+ var id2 = resolve6.normalizeId(this._getId(schema));
27527
28047
  if (id2 && shouldAddSchema) checkUnique(this, id2);
27528
28048
  var willValidate = this._opts.validateSchema !== false && !skipValidation;
27529
28049
  var recursiveMeta;
27530
- if (willValidate && !(recursiveMeta = id2 && id2 == resolve5.normalizeId(schema.$schema)))
28050
+ if (willValidate && !(recursiveMeta = id2 && id2 == resolve6.normalizeId(schema.$schema)))
27531
28051
  this.validateSchema(schema, true);
27532
- var localRefs = resolve5.ids.call(this, schema);
28052
+ var localRefs = resolve6.ids.call(this, schema);
27533
28053
  var schemaObj = new SchemaObject({
27534
28054
  id: id2,
27535
28055
  schema,
@@ -27795,13 +28315,13 @@ var init_runner = __esm({
27795
28315
  };
27796
28316
  defaultSleep = async (milliseconds, signal) => {
27797
28317
  if (signal?.aborted) throw abortError();
27798
- await new Promise((resolve5, reject) => {
28318
+ await new Promise((resolve6, reject) => {
27799
28319
  const finish = (callback) => {
27800
28320
  signal?.removeEventListener("abort", onAbort);
27801
28321
  callback();
27802
28322
  };
27803
28323
  const timer = setTimeout(
27804
- () => finish(resolve5),
28324
+ () => finish(resolve6),
27805
28325
  Math.max(MIN_SLEEP_MS, milliseconds)
27806
28326
  );
27807
28327
  const onAbort = () => {
@@ -28284,8 +28804,8 @@ var init_runner = __esm({
28284
28804
  let failed = 0;
28285
28805
  let acceptAttemptUpdates = true;
28286
28806
  let wakeDrain;
28287
- const drainSignal = new Promise((resolve5) => {
28288
- wakeDrain = resolve5;
28807
+ const drainSignal = new Promise((resolve6) => {
28808
+ wakeDrain = resolve6;
28289
28809
  });
28290
28810
  const requestDrain = (reason) => {
28291
28811
  const priority = (value) => {
@@ -28334,7 +28854,7 @@ var init_runner = __esm({
28334
28854
  const metadata = await this.dependencies.discoverOAuth({
28335
28855
  signal: this.options.signal
28336
28856
  });
28337
- let access3 = null;
28857
+ let access4 = null;
28338
28858
  let accessExpiresAt = 0;
28339
28859
  let refreshPromise = null;
28340
28860
  const refreshAccessToken = async (requestOptions = {}) => {
@@ -28347,7 +28867,7 @@ var init_runner = __esm({
28347
28867
  );
28348
28868
  assertLocalCredentialIdentity(localState, refreshed.credential);
28349
28869
  credential = refreshed.credential;
28350
- access3 = refreshed;
28870
+ access4 = refreshed;
28351
28871
  accessExpiresAt = now() + refreshed.expiresIn * 1e3;
28352
28872
  return refreshed.accessToken;
28353
28873
  })();
@@ -28360,10 +28880,10 @@ var init_runner = __esm({
28360
28880
  await refreshAccessToken({ signal: this.options.signal });
28361
28881
  const tokenSource = {
28362
28882
  accessToken: async (requestOptions) => {
28363
- if (!access3 || accessExpiresAt - now() <= 5e3) {
28883
+ if (!access4 || accessExpiresAt - now() <= 5e3) {
28364
28884
  return refreshAccessToken(requestOptions);
28365
28885
  }
28366
- return access3.accessToken;
28886
+ return access4.accessToken;
28367
28887
  },
28368
28888
  refreshAccessToken
28369
28889
  };
@@ -28420,7 +28940,7 @@ var init_runner = __esm({
28420
28940
  return await operation();
28421
28941
  } catch (error48) {
28422
28942
  lastError = error48;
28423
- if (!retryableRemoteError(error48) || attempt === settings.remoteRetryLimit) throw error48;
28943
+ if (!retryableRemoteError(error48) || error48 instanceof ExternalInferenceMcpError && error48.definitivelyNotApplied || attempt === settings.remoteRetryLimit) throw error48;
28424
28944
  const requestedRetryDelay = remoteRetryAfterMs(
28425
28945
  error48,
28426
28946
  Math.min(250 * attempt, 1e3)
@@ -28521,7 +29041,7 @@ var init_runner = __esm({
28521
29041
  }
28522
29042
  const hostHeartbeat = async (status, requestOptions = {
28523
29043
  signal: this.options.signal
28524
- }, retryRemote = true, allowOfflineProjection = false) => {
29044
+ }, retryRemote = true, allowOfflineProjection = false, definitiveReseedAttempt = 0) => {
28525
29045
  const sequence = receipt.host_heartbeat_sequence + 1;
28526
29046
  const request = {
28527
29047
  schema_version: "external_inference_host_heartbeat_v1",
@@ -28547,7 +29067,22 @@ var init_runner = __esm({
28547
29067
  request,
28548
29068
  requestOptions
28549
29069
  );
28550
- const result2 = status === "draining" || !retryRemote ? await heartbeatOperation() : await retryExact(heartbeatOperation, requestOptions);
29070
+ let result2;
29071
+ try {
29072
+ result2 = status === "draining" || !retryRemote ? await heartbeatOperation() : await retryExact(heartbeatOperation, requestOptions);
29073
+ } catch (error48) {
29074
+ if (status !== "draining" && retryRemote && error48 instanceof ExternalInferenceMcpError && error48.definitivelyNotApplied && error48.retryable && definitiveReseedAttempt < settings.remoteRetryLimit - 1) {
29075
+ await sleep4(remoteRetryAfterMs(error48, MIN_SLEEP_MS), requestOptions.signal);
29076
+ return await hostHeartbeat(
29077
+ status,
29078
+ requestOptions,
29079
+ retryRemote,
29080
+ allowOfflineProjection,
29081
+ definitiveReseedAttempt + 1
29082
+ );
29083
+ }
29084
+ throw error48;
29085
+ }
28551
29086
  assertHostHeartbeatResult(request, result2, allowOfflineProjection);
28552
29087
  if (result2.host.status === "revoked" || result2.host.status === "offline" && !allowOfflineProjection) {
28553
29088
  const error48 = new InferenceHostRunnerError(
@@ -29284,6 +29819,627 @@ var init_runner = __esm({
29284
29819
  }
29285
29820
  });
29286
29821
 
29822
+ // lib/inference-host/service.ts
29823
+ import { spawn as spawn5 } from "node:child_process";
29824
+ import { createWriteStream, readFileSync } from "node:fs";
29825
+ import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
29826
+ import { homedir as homedir2 } from "node:os";
29827
+ import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
29828
+ var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
29829
+ var init_service = __esm({
29830
+ "lib/inference-host/service.ts"() {
29831
+ "use strict";
29832
+ init_config();
29833
+ SERVICE_NAME = "VTX Macro Inference Host";
29834
+ SYSTEMD_UNIT = "vtx-inference-host.service";
29835
+ LAUNCHD_LABEL = "com.vtxmacro.inference-host";
29836
+ SERVICE_COOPERATIVE_STOP_SECONDS = 75;
29837
+ isWindowsSubsystemForLinux = (env = process.env, kernelRelease) => Boolean(
29838
+ String(env.WSL_INTEROP || "").trim() || String(env.WSL_DISTRO_NAME || "").trim() || /microsoft/iu.test(kernelRelease ?? (() => {
29839
+ try {
29840
+ return readFileSync("/proc/sys/kernel/osrelease", "utf8");
29841
+ } catch {
29842
+ return "";
29843
+ }
29844
+ })())
29845
+ );
29846
+ inferenceHostServiceManifestPath = (config2) => `${config2.statePath}.service.json`;
29847
+ inferenceHostServiceDesiredPath = (config2) => `${config2.statePath}.service-desired.json`;
29848
+ inferenceHostServiceLogPath = (config2) => `${config2.statePath}.service.log`;
29849
+ xmlEscape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
29850
+ plistEscape = xmlEscape;
29851
+ systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
29852
+ defaultRunCommand = async (command, args) => await new Promise((resolvePromise) => {
29853
+ const child = spawn5(command, [...args], {
29854
+ windowsHide: true,
29855
+ stdio: ["ignore", "pipe", "pipe"]
29856
+ });
29857
+ const stdout = [];
29858
+ const stderr = [];
29859
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
29860
+ child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
29861
+ child.once("error", (error48) => resolvePromise({
29862
+ exitCode: 1,
29863
+ stdout: "",
29864
+ stderr: error48.message
29865
+ }));
29866
+ child.once("exit", (code) => resolvePromise({
29867
+ exitCode: code ?? 1,
29868
+ stdout: Buffer.concat(stdout).toString("utf8"),
29869
+ stderr: Buffer.concat(stderr).toString("utf8")
29870
+ }));
29871
+ });
29872
+ managerName = (platform) => {
29873
+ if (platform === "win32") return "windows-task-scheduler";
29874
+ if (platform === "darwin") return "launch-agent";
29875
+ if (platform === "linux") return "systemd-user";
29876
+ throw new Error(`Inference-host background service is unsupported on ${platform}.`);
29877
+ };
29878
+ assertManifest = (value) => {
29879
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
29880
+ throw new Error("Inference-host service manifest is invalid.");
29881
+ }
29882
+ const record2 = value;
29883
+ if (record2.schema_version !== "vtx_inference_service_v1" || record2.adapter !== "codex" || typeof record2.installed_at !== "string" || !Number.isFinite(Date.parse(record2.installed_at)) || typeof record2.executable !== "string" || !record2.executable || typeof record2.script !== "string" || !record2.script || typeof record2.display_name !== "string" || !record2.display_name || typeof record2.log_path !== "string" || !record2.log_path || !record2.runtime_environment || typeof record2.runtime_environment !== "object" || Array.isArray(record2.runtime_environment)) {
29884
+ throw new Error("Inference-host service manifest is invalid.");
29885
+ }
29886
+ for (const value2 of [
29887
+ record2.executable,
29888
+ record2.script,
29889
+ record2.log_path
29890
+ ]) {
29891
+ if (typeof value2 === "string" && /[\r\n\0]/u.test(value2)) {
29892
+ throw new Error("Inference-host service manifest paths contain control characters.");
29893
+ }
29894
+ }
29895
+ for (const [key, entry] of Object.entries(record2.runtime_environment)) {
29896
+ if (!key.startsWith("VTX_") || typeof entry !== "string") {
29897
+ throw new Error("Inference-host service runtime environment is invalid.");
29898
+ }
29899
+ }
29900
+ return record2;
29901
+ };
29902
+ assertDesiredState = (value) => {
29903
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
29904
+ throw new Error("Inference-host service desired state is invalid.");
29905
+ }
29906
+ const record2 = value;
29907
+ if (record2.schema_version !== "vtx_inference_service_desired_v1" || typeof record2.desired_running !== "boolean" || typeof record2.updated_at !== "string" || !Number.isFinite(Date.parse(record2.updated_at))) {
29908
+ throw new Error("Inference-host service desired state is invalid.");
29909
+ }
29910
+ return record2;
29911
+ };
29912
+ readInferenceHostServiceManifest = async (path) => {
29913
+ const raw = await readInferencePrivateFile(path, "Inference-host service manifest");
29914
+ if (raw === null) return null;
29915
+ try {
29916
+ return assertManifest(JSON.parse(raw));
29917
+ } catch (error48) {
29918
+ if (error48 instanceof SyntaxError) throw new Error("Inference-host service manifest is not valid JSON.");
29919
+ throw error48;
29920
+ }
29921
+ };
29922
+ readInferenceHostServiceDesired = async (path) => {
29923
+ const raw = await readInferencePrivateFile(path, "Inference-host service desired state");
29924
+ if (raw === null) return false;
29925
+ try {
29926
+ return assertDesiredState(JSON.parse(raw)).desired_running;
29927
+ } catch (error48) {
29928
+ if (error48 instanceof SyntaxError) throw new Error("Inference-host service desired state is not valid JSON.");
29929
+ throw error48;
29930
+ }
29931
+ };
29932
+ readDesiredAcrossAtomicReplacement = async (path) => {
29933
+ for (let attempt = 0; attempt < 3; attempt += 1) {
29934
+ try {
29935
+ return await readInferenceHostServiceDesired(path);
29936
+ } catch (error48) {
29937
+ if (!(error48 instanceof Error) || !error48.message.includes("changed while it was opened")) {
29938
+ throw error48;
29939
+ }
29940
+ }
29941
+ }
29942
+ return await readInferenceHostServiceDesired(path);
29943
+ };
29944
+ writeDesired = async (path, desired, now) => {
29945
+ await writeAtomicInferencePrivateFile(path, `${JSON.stringify({
29946
+ schema_version: "vtx_inference_service_desired_v1",
29947
+ desired_running: desired,
29948
+ updated_at: now.toISOString()
29949
+ }, null, 2)}
29950
+ `);
29951
+ };
29952
+ runtimeEnvironment = (config2) => {
29953
+ const result2 = {
29954
+ VTX_API_URL: config2.apiUrl,
29955
+ VTX_INFERENCE_HOST_HOME: dirname4(config2.codexHomePath),
29956
+ VTX_INFERENCE_HOST_CREDENTIAL_STORE: config2.credentialStoreMode,
29957
+ VTX_INFERENCE_HOST_STATE_PATH: config2.statePath,
29958
+ VTX_INFERENCE_HOST_LOCK_PATH: config2.processLockPath
29959
+ };
29960
+ if (config2.credentialFilePath) {
29961
+ result2.VTX_INFERENCE_HOST_CREDENTIAL_FILE = config2.credentialFilePath;
29962
+ }
29963
+ return result2;
29964
+ };
29965
+ serviceArguments = (script, manifestPath) => [
29966
+ script,
29967
+ "inference-host",
29968
+ "service",
29969
+ "run-internal",
29970
+ "--service-manifest",
29971
+ manifestPath
29972
+ ];
29973
+ windowsTaskXml = (executable, args, username) => `<?xml version="1.0" encoding="UTF-16"?>
29974
+ <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
29975
+ <RegistrationInfo><Description>${xmlEscape(SERVICE_NAME)}</Description></RegistrationInfo>
29976
+ <Triggers>
29977
+ <LogonTrigger><Enabled>true</Enabled><UserId>${xmlEscape(username)}</UserId></LogonTrigger>
29978
+ <TimeTrigger>
29979
+ <StartBoundary>2020-01-01T00:00:00</StartBoundary>
29980
+ <Enabled>true</Enabled>
29981
+ <Repetition><Interval>PT1M</Interval><StopAtDurationEnd>false</StopAtDurationEnd></Repetition>
29982
+ </TimeTrigger>
29983
+ </Triggers>
29984
+ <Principals><Principal id="Author"><UserId>${xmlEscape(username)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
29985
+ <Settings>
29986
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
29987
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
29988
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
29989
+ <StartWhenAvailable>true</StartWhenAvailable>
29990
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
29991
+ <RestartOnFailure><Interval>PT1M</Interval><Count>255</Count></RestartOnFailure>
29992
+ </Settings>
29993
+ <Actions Context="Author"><Exec><Command>${xmlEscape(executable)}</Command><Arguments>${xmlEscape(args.map((arg) => `"${arg.replaceAll('"', '\\"')}"`).join(" "))}</Arguments></Exec></Actions>
29994
+ </Task>
29995
+ `;
29996
+ systemdUnit = (executable, args) => `[Unit]
29997
+ Description=${SERVICE_NAME}
29998
+ After=network-online.target
29999
+ Wants=network-online.target
30000
+ StartLimitIntervalSec=0
30001
+
30002
+ [Service]
30003
+ Type=simple
30004
+ ExecStart=${[executable, ...args].map(systemdQuote).join(" ")}
30005
+ Restart=on-failure
30006
+ RestartSec=5s
30007
+ TimeoutStopSec=${SERVICE_COOPERATIVE_STOP_SECONDS}s
30008
+
30009
+ [Install]
30010
+ WantedBy=default.target
30011
+ `;
30012
+ launchAgentPlist = (executable, args, logPath) => `<?xml version="1.0" encoding="UTF-8"?>
30013
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
30014
+ <plist version="1.0"><dict>
30015
+ <key>Label</key><string>${LAUNCHD_LABEL}</string>
30016
+ <key>ProgramArguments</key><array>${[executable, ...args].map((arg) => `<string>${plistEscape(arg)}</string>`).join("")}</array>
30017
+ <key>RunAtLoad</key><true/>
30018
+ <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
30019
+ <key>ProcessType</key><string>Background</string>
30020
+ <key>ThrottleInterval</key><integer>5</integer>
30021
+ <key>ExitTimeOut</key><integer>${SERVICE_COOPERATIVE_STOP_SECONDS}</integer>
30022
+ <key>StandardOutPath</key><string>${plistEscape(logPath)}</string>
30023
+ <key>StandardErrorPath</key><string>${plistEscape(logPath)}</string>
30024
+ </dict></plist>
30025
+ `;
30026
+ InferenceHostServiceManager = class {
30027
+ constructor(config2, dependencies = {}) {
30028
+ this.config = config2;
30029
+ this.platform = dependencies.platform ?? process.platform;
30030
+ this.home = dependencies.homedir ?? homedir2();
30031
+ this.executable = resolve4(dependencies.executable ?? process.execPath);
30032
+ this.script = resolve4(dependencies.script ?? process.argv[1] ?? "");
30033
+ this.username = dependencies.username ?? ([process.env.USERDOMAIN, process.env.USERNAME].filter(Boolean).join("\\") || process.env.USER || "");
30034
+ this.runCommand = dependencies.runCommand ?? defaultRunCommand;
30035
+ this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
30036
+ this.sleep = dependencies.sleep ?? (async (milliseconds) => {
30037
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30038
+ });
30039
+ this.stopWaitAttempts = dependencies.stopWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
30040
+ this.startWaitAttempts = dependencies.startWaitAttempts ?? 40;
30041
+ this.acquireProcessLock = dependencies.acquireProcessLock ?? acquireInferenceHostProcessLock;
30042
+ managerName(this.platform);
30043
+ if (dependencies.platform === void 0 && this.platform === "linux" && isWindowsSubsystemForLinux()) {
30044
+ throw new Error(
30045
+ "Windows-login auto-start requires the native Windows VTX CLI. A WSL-only service cannot start the WSL virtual machine at login."
30046
+ );
30047
+ }
30048
+ }
30049
+ manifestPath() {
30050
+ return inferenceHostServiceManifestPath(this.config);
30051
+ }
30052
+ desiredPath() {
30053
+ return inferenceHostServiceDesiredPath(this.config);
30054
+ }
30055
+ logPath() {
30056
+ return inferenceHostServiceLogPath(this.config);
30057
+ }
30058
+ controlLockPath() {
30059
+ return `${this.config.processLockPath}.service-control`;
30060
+ }
30061
+ async withControlLock(operation) {
30062
+ let lock2 = null;
30063
+ for (let attempt = 0; attempt < 400; attempt += 1) {
30064
+ try {
30065
+ lock2 = await this.acquireProcessLock(this.controlLockPath());
30066
+ break;
30067
+ } catch (error48) {
30068
+ if (!/already owns/iu.test(error48 instanceof Error ? error48.message : "")) throw error48;
30069
+ await this.sleep(250);
30070
+ }
30071
+ }
30072
+ if (!lock2) {
30073
+ throw new Error("Another inference-host service control operation did not finish within 100 seconds.");
30074
+ }
30075
+ try {
30076
+ return await operation();
30077
+ } finally {
30078
+ await lock2.release();
30079
+ }
30080
+ }
30081
+ windowsTaskName() {
30082
+ const identity = this.username.replaceAll(/[^A-Za-z0-9_.@-]+/gu, "_").slice(-80) || "current-user";
30083
+ return `VTX Macro Inference Host (${identity})`;
30084
+ }
30085
+ windowsPowerShell(script) {
30086
+ return [
30087
+ "-NoLogo",
30088
+ "-NoProfile",
30089
+ "-NonInteractive",
30090
+ "-EncodedCommand",
30091
+ Buffer.from(script, "utf16le").toString("base64")
30092
+ ];
30093
+ }
30094
+ definitionPath() {
30095
+ if (this.platform === "win32") return `${this.config.statePath}.service-task.xml`;
30096
+ if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
30097
+ return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
30098
+ }
30099
+ async managerCommand(action) {
30100
+ const definition = this.definitionPath();
30101
+ if (this.platform === "win32") {
30102
+ const taskName = this.windowsTaskName();
30103
+ if (action === "install") return await this.runCommand("schtasks.exe", ["/Create", "/F", "/TN", taskName, "/XML", definition]);
30104
+ const quotedName = taskName.replaceAll("'", "''");
30105
+ if (action === "start") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30106
+ `$ErrorActionPreference='Stop'; $task=Get-ScheduledTask -TaskName '${quotedName}' -ErrorAction Stop; Enable-ScheduledTask -InputObject $task -ErrorAction Stop | Out-Null; Start-ScheduledTask -TaskName '${quotedName}'`
30107
+ ));
30108
+ if (action === "stop") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30109
+ `$ErrorActionPreference='Stop'; $task=Get-ScheduledTask -TaskName '${quotedName}' -ErrorAction SilentlyContinue; if($null -ne $task) { Stop-ScheduledTask -InputObject $task -ErrorAction Stop; Disable-ScheduledTask -InputObject $task -ErrorAction Stop | Out-Null }`
30110
+ ));
30111
+ if (action === "status") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30112
+ `$ErrorActionPreference='Stop'; [Console]::Out.Write([int](Get-ScheduledTask -TaskName '${quotedName}').State)`
30113
+ ));
30114
+ return await this.runCommand("powershell.exe", this.windowsPowerShell(
30115
+ `$ErrorActionPreference='Stop'; $task=Get-ScheduledTask -TaskName '${quotedName}' -ErrorAction SilentlyContinue; if($null -ne $task) { Unregister-ScheduledTask -InputObject $task -Confirm:$false -ErrorAction Stop }; if($null -ne (Get-ScheduledTask -TaskName '${quotedName}' -ErrorAction SilentlyContinue)) { throw 'scheduled task still exists after unregister' }`
30116
+ ));
30117
+ }
30118
+ if (this.platform === "darwin") {
30119
+ const domain2 = `gui/${typeof process.getuid === "function" ? process.getuid() : 0}`;
30120
+ if (action === "install") return { exitCode: 0, stdout: "definition-written", stderr: "" };
30121
+ if (action === "start") return await this.runCommand("launchctl", ["bootstrap", domain2, definition]);
30122
+ if (action === "stop") return await this.runCommand("launchctl", ["bootout", domain2, definition]);
30123
+ if (action === "status") return await this.runCommand("launchctl", ["print", `${domain2}/${LAUNCHD_LABEL}`]);
30124
+ return await this.runCommand("launchctl", ["bootout", domain2, definition]);
30125
+ }
30126
+ if (action === "install") return await this.runCommand("systemctl", ["--user", "enable", SYSTEMD_UNIT]);
30127
+ if (action === "start") return await this.runCommand("systemctl", ["--user", "start", SYSTEMD_UNIT]);
30128
+ if (action === "stop") return await this.runCommand("systemctl", ["--user", "stop", SYSTEMD_UNIT]);
30129
+ if (action === "status") return await this.runCommand("systemctl", ["--user", "show", SYSTEMD_UNIT, "--property=ActiveState,SubState,UnitFileState", "--no-pager"]);
30130
+ return await this.runCommand("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
30131
+ }
30132
+ async waitForManagerActive() {
30133
+ for (let attempt = 0; attempt < this.startWaitAttempts; attempt += 1) {
30134
+ const status = await this.status();
30135
+ if (status.manager_active) return status;
30136
+ await this.sleep(250);
30137
+ }
30138
+ throw new Error("Background service did not reach an active state within 10 seconds.");
30139
+ }
30140
+ async install(options) {
30141
+ return await this.withControlLock(async () => await this.installUnlocked(options));
30142
+ }
30143
+ async installUnlocked(options) {
30144
+ if (await readInferenceHostServiceManifest(this.manifestPath())) {
30145
+ await this.uninstallUnlocked();
30146
+ }
30147
+ await access3(this.executable);
30148
+ await access3(this.script);
30149
+ const manifest = assertManifest({
30150
+ schema_version: "vtx_inference_service_v1",
30151
+ installed_at: this.now().toISOString(),
30152
+ adapter: options.adapter,
30153
+ executable: this.executable,
30154
+ script: this.script,
30155
+ display_name: options.displayName,
30156
+ log_path: this.logPath(),
30157
+ runtime_environment: runtimeEnvironment(this.config)
30158
+ });
30159
+ const args = serviceArguments(this.script, this.manifestPath());
30160
+ const definition = this.platform === "win32" ? windowsTaskXml(this.executable, args, this.username) : this.platform === "darwin" ? launchAgentPlist(this.executable, args, this.logPath()) : systemdUnit(this.executable, args);
30161
+ let managerInstallAttempted = false;
30162
+ try {
30163
+ await writeAtomicInferencePrivateFile(this.manifestPath(), `${JSON.stringify(manifest, null, 2)}
30164
+ `);
30165
+ await writeDesired(this.desiredPath(), options.startImmediately !== false, this.now());
30166
+ await mkdir3(dirname4(this.definitionPath()), { recursive: true, mode: 448 });
30167
+ if (this.platform === "win32") {
30168
+ await writeFile2(this.definitionPath(), `\uFEFF${definition}`, {
30169
+ encoding: "utf16le",
30170
+ mode: 384
30171
+ });
30172
+ } else {
30173
+ await writeFile2(this.definitionPath(), definition, { encoding: "utf8", mode: 384 });
30174
+ await chmod3(this.definitionPath(), 384);
30175
+ }
30176
+ if (this.platform === "linux") {
30177
+ const reload = await this.runCommand("systemctl", ["--user", "daemon-reload"]);
30178
+ if (reload.exitCode !== 0) throw new Error(`systemd user daemon reload failed: ${reload.stderr.trim()}`);
30179
+ }
30180
+ managerInstallAttempted = true;
30181
+ const installed = await this.managerCommand("install");
30182
+ if (installed.exitCode !== 0) throw new Error(`Background service installation failed: ${installed.stderr.trim()}`);
30183
+ if (options.startImmediately !== false) {
30184
+ const started = await this.managerCommand("start");
30185
+ if (started.exitCode !== 0 && !/already running|in progress/iu.test(`${started.stdout}
30186
+ ${started.stderr}`)) {
30187
+ throw new Error(`Background service start failed: ${started.stderr.trim()}`);
30188
+ }
30189
+ await this.waitForManagerActive();
30190
+ }
30191
+ } catch (error48) {
30192
+ await writeDesired(this.desiredPath(), false, this.now()).catch(() => void 0);
30193
+ if (managerInstallAttempted) {
30194
+ const cleanup = await this.managerCommand("uninstall").catch((cleanupError) => ({
30195
+ exitCode: 1,
30196
+ stdout: "",
30197
+ stderr: cleanupError instanceof Error ? cleanupError.message : "unknown cleanup error"
30198
+ }));
30199
+ if (cleanup.exitCode !== 0 && !/not found|does not exist|not loaded|no such process|cannot find/iu.test(`${cleanup.stdout}
30200
+ ${cleanup.stderr}`)) {
30201
+ throw new Error(
30202
+ `Background service setup failed and manager cleanup was not confirmed: ${cleanup.stderr.trim()}`,
30203
+ { cause: error48 }
30204
+ );
30205
+ }
30206
+ }
30207
+ await rm4(this.definitionPath(), { force: true }).catch(() => void 0);
30208
+ await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
30209
+ await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
30210
+ if (this.platform === "linux") {
30211
+ await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
30212
+ }
30213
+ throw error48;
30214
+ }
30215
+ return await this.status();
30216
+ }
30217
+ async start() {
30218
+ return await this.withControlLock(async () => await this.startUnlocked());
30219
+ }
30220
+ async startUnlocked() {
30221
+ if (!await readInferenceHostServiceManifest(this.manifestPath())) {
30222
+ throw new Error("Inference-host service is not installed.");
30223
+ }
30224
+ await writeDesired(this.desiredPath(), true, this.now());
30225
+ const result2 = await this.managerCommand("start");
30226
+ if (result2.exitCode !== 0 && !/already running|in progress/iu.test(`${result2.stdout}
30227
+ ${result2.stderr}`)) {
30228
+ throw new Error(`Background service start failed: ${result2.stderr.trim()}`);
30229
+ }
30230
+ try {
30231
+ return await this.waitForManagerActive();
30232
+ } catch (error48) {
30233
+ await writeDesired(this.desiredPath(), false, this.now());
30234
+ await this.managerCommand("stop").catch(() => void 0);
30235
+ throw error48;
30236
+ }
30237
+ }
30238
+ async stop() {
30239
+ return await this.withControlLock(async () => await this.stopUnlocked());
30240
+ }
30241
+ async stopUnlocked() {
30242
+ if (!await readInferenceHostServiceManifest(this.manifestPath())) {
30243
+ throw new Error("Inference-host service is not installed.");
30244
+ }
30245
+ await writeDesired(this.desiredPath(), false, this.now());
30246
+ const serviceLockPath = `${this.config.processLockPath}.service`;
30247
+ let serviceReleased = false;
30248
+ for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
30249
+ try {
30250
+ const probe = await this.acquireProcessLock(serviceLockPath);
30251
+ await probe.release();
30252
+ serviceReleased = true;
30253
+ break;
30254
+ } catch (error48) {
30255
+ if (error48 instanceof Error && error48.message.includes("Another inference host process already owns")) {
30256
+ await this.sleep(250);
30257
+ continue;
30258
+ }
30259
+ throw error48;
30260
+ }
30261
+ }
30262
+ if (!serviceReleased) {
30263
+ throw new Error(
30264
+ `Inference-host worker did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
30265
+ );
30266
+ }
30267
+ const result2 = await this.managerCommand("stop");
30268
+ if (result2.exitCode !== 0 && !/not running|not found|does not exist|not loaded|cannot find|no such process/iu.test(`${result2.stdout}
30269
+ ${result2.stderr}`)) {
30270
+ throw new Error(`Background service stop failed: ${result2.stderr.trim()}`);
30271
+ }
30272
+ const status = await this.status();
30273
+ if (status.manager_active) {
30274
+ throw new Error("Background service manager still reports the service active after stop.");
30275
+ }
30276
+ return status;
30277
+ }
30278
+ async status() {
30279
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30280
+ const desired = await readInferenceHostServiceDesired(this.desiredPath());
30281
+ const result2 = await this.managerCommand("status");
30282
+ const output3 = result2.stdout.trim();
30283
+ const managerActive = result2.exitCode === 0 && (this.platform === "linux" ? /(?:^|\n)ActiveState=active(?:\n|$)/u.test(output3) : this.platform === "win32" ? output3.trim() === "4" : /\bstate\s*=\s*running\b/iu.test(output3));
30284
+ return {
30285
+ installed: manifest !== null,
30286
+ desired_running: desired,
30287
+ manager_active: managerActive,
30288
+ manager: managerName(this.platform),
30289
+ manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
30290
+ adapter: manifest?.adapter ?? null,
30291
+ log_path: manifest?.log_path ?? this.logPath()
30292
+ };
30293
+ }
30294
+ async logs(lines = 100) {
30295
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30296
+ const path = manifest?.log_path ?? this.logPath();
30297
+ try {
30298
+ const contents = await readFile5(path, "utf8");
30299
+ return `${contents.trimEnd().split(/\r?\n/u).slice(-lines).join("\n")}
30300
+ `;
30301
+ } catch (error48) {
30302
+ if (error48.code === "ENOENT") return "";
30303
+ throw error48;
30304
+ }
30305
+ }
30306
+ async uninstall() {
30307
+ return await this.withControlLock(async () => await this.uninstallUnlocked());
30308
+ }
30309
+ async uninstallUnlocked() {
30310
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30311
+ if (!manifest) throw new Error("Inference-host service is not installed.");
30312
+ const current = await this.status();
30313
+ if (current.desired_running || current.manager_active) {
30314
+ await this.stopUnlocked();
30315
+ } else {
30316
+ await writeDesired(this.desiredPath(), false, this.now());
30317
+ }
30318
+ const result2 = await this.managerCommand("uninstall");
30319
+ if (result2.exitCode !== 0 && !/not found|does not exist|not loaded|no such process|cannot find/iu.test(`${result2.stdout}
30320
+ ${result2.stderr}`)) {
30321
+ throw new Error(`Background service uninstall failed: ${result2.stderr.trim()}`);
30322
+ }
30323
+ await rm4(this.definitionPath(), { force: true });
30324
+ if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
30325
+ await rm4(this.manifestPath(), { force: true });
30326
+ await rm4(this.desiredPath(), { force: true });
30327
+ return {
30328
+ installed: false,
30329
+ desired_running: false,
30330
+ manager_active: false,
30331
+ manager: managerName(this.platform),
30332
+ manager_state: "not-installed",
30333
+ adapter: null,
30334
+ log_path: manifest.log_path
30335
+ };
30336
+ }
30337
+ };
30338
+ appendServiceLog = async (path, event, fields = {}) => {
30339
+ await mkdir3(dirname4(path), { recursive: true, mode: 448 });
30340
+ const stream = createWriteStream(path, { flags: "a", mode: 384 });
30341
+ await new Promise((resolvePromise, reject) => {
30342
+ stream.once("error", reject);
30343
+ stream.end(`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), event, ...fields })}
30344
+ `, resolvePromise);
30345
+ });
30346
+ };
30347
+ spawnServiceChild = async (manifest, signal) => {
30348
+ const args = [
30349
+ manifest.script,
30350
+ "inference-host",
30351
+ "run",
30352
+ "--json",
30353
+ "--display-name",
30354
+ manifest.display_name
30355
+ ];
30356
+ const startedAt = Date.now();
30357
+ const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30358
+ return await new Promise((resolvePromise, reject) => {
30359
+ const child = spawn5(manifest.executable, args, {
30360
+ env: { ...process.env, ...manifest.runtime_environment },
30361
+ windowsHide: true,
30362
+ stdio: ["ignore", log, log]
30363
+ });
30364
+ const onAbort = () => child.kill("SIGTERM");
30365
+ signal.addEventListener("abort", onAbort, { once: true });
30366
+ child.once("error", (error48) => {
30367
+ signal.removeEventListener("abort", onAbort);
30368
+ log.end();
30369
+ reject(error48);
30370
+ });
30371
+ child.once("exit", (code) => {
30372
+ signal.removeEventListener("abort", onAbort);
30373
+ log.end();
30374
+ resolvePromise({ exitCode: code ?? 1, uptimeMs: Date.now() - startedAt });
30375
+ });
30376
+ });
30377
+ };
30378
+ runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
30379
+ const manifest = await readInferenceHostServiceManifest(manifestPath);
30380
+ if (!manifest) throw new Error("Inference-host service manifest is missing.");
30381
+ const desiredPath = `${manifest.runtime_environment.VTX_INFERENCE_HOST_STATE_PATH}.service-desired.json`;
30382
+ const signal = options.signal ?? new AbortController().signal;
30383
+ const sleep4 = options.sleep ?? (async (milliseconds) => {
30384
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30385
+ });
30386
+ const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
30387
+ let failures = 0;
30388
+ await appendServiceLog(manifest.log_path, "service_supervisor_started", { adapter: manifest.adapter });
30389
+ while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30390
+ try {
30391
+ const workerController = new AbortController();
30392
+ const forwardAbort = () => workerController.abort();
30393
+ signal.addEventListener("abort", forwardAbort, { once: true });
30394
+ let workerComplete = false;
30395
+ let monitorError = null;
30396
+ const desiredMonitor = (async () => {
30397
+ while (!workerComplete && !workerController.signal.aborted) {
30398
+ await sleep4(500);
30399
+ if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
30400
+ workerController.abort();
30401
+ break;
30402
+ }
30403
+ }
30404
+ })().catch((error48) => {
30405
+ monitorError = error48;
30406
+ workerController.abort();
30407
+ });
30408
+ let result2;
30409
+ try {
30410
+ result2 = await launch(manifest, workerController.signal);
30411
+ } finally {
30412
+ workerComplete = true;
30413
+ workerController.abort();
30414
+ signal.removeEventListener("abort", forwardAbort);
30415
+ await desiredMonitor;
30416
+ }
30417
+ if (monitorError) throw monitorError;
30418
+ if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
30419
+ failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
30420
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30421
+ await appendServiceLog(manifest.log_path, "worker_exited", {
30422
+ exit_code: result2.exitCode,
30423
+ uptime_ms: result2.uptimeMs,
30424
+ retry_after_ms: retryAfterMs
30425
+ });
30426
+ await sleep4(retryAfterMs);
30427
+ } catch (error48) {
30428
+ if (signal.aborted) break;
30429
+ failures += 1;
30430
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30431
+ await appendServiceLog(manifest.log_path, "worker_launch_failed", {
30432
+ error: error48 instanceof Error ? error48.message : "unknown",
30433
+ retry_after_ms: retryAfterMs
30434
+ });
30435
+ await sleep4(retryAfterMs);
30436
+ }
30437
+ }
30438
+ await appendServiceLog(manifest.log_path, "service_supervisor_stopped");
30439
+ };
30440
+ }
30441
+ });
30442
+
29287
30443
  // lib/inference-host/cli.ts
29288
30444
  var cli_exports = {};
29289
30445
  __export(cli_exports, {
@@ -29291,9 +30447,9 @@ __export(cli_exports, {
29291
30447
  runInferenceHostCli: () => runInferenceHostCli
29292
30448
  });
29293
30449
  import { randomUUID } from "node:crypto";
29294
- import { spawn as spawn4 } from "node:child_process";
29295
- import { lstat as lstat4, realpath as realpath4, rm as rm4 } from "node:fs/promises";
29296
- import { join as join5, resolve as resolve4 } from "node:path";
30450
+ import { spawn as spawn6 } from "node:child_process";
30451
+ import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
30452
+ import { join as join6, resolve as resolve5 } from "node:path";
29297
30453
  async function runInferenceHostCli(argv2, env = process.env, dependencies = {}) {
29298
30454
  const warnings = [];
29299
30455
  try {
@@ -29338,6 +30494,9 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29338
30494
  async () => await agentFail(config2, parsed, dependencies, warnings)
29339
30495
  );
29340
30496
  }
30497
+ if (parsed.command === "service") {
30498
+ return await serviceCommand(config2, parsed, env, dependencies, warnings);
30499
+ }
29341
30500
  if (parsed.command === "status") {
29342
30501
  return await localStatus(config2, parsed, dependencies, warnings);
29343
30502
  }
@@ -29354,7 +30513,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29354
30513
  return await cleanupLogin(config2, parsed, dependencies, warnings, true);
29355
30514
  }
29356
30515
  throw new Error(
29357
- "Usage: vtx inference-host <login|codex-login|run|agent-connect|agent-run|agent-next|agent-complete|agent-fail|status|doctor|logout|codex-logout|revoke> [--json]"
30516
+ "Usage: vtx inference-host <login|codex-login|run|agent-connect|agent-run|agent-next|agent-complete|agent-fail|service|status|doctor|logout|codex-logout|revoke> [--json]"
29358
30517
  );
29359
30518
  } catch (error48) {
29360
30519
  return {
@@ -29365,7 +30524,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29365
30524
  };
29366
30525
  }
29367
30526
  }
29368
- var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parsePositiveInteger, parseInferenceHostArgs, defaultOpenBrowser, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock;
30527
+ var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parsePositiveInteger, parseInferenceHostArgs, defaultOpenBrowser, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand;
29369
30528
  var init_cli = __esm({
29370
30529
  "lib/inference-host/cli.ts"() {
29371
30530
  "use strict";
@@ -29382,6 +30541,7 @@ var init_cli = __esm({
29382
30541
  init_oauth();
29383
30542
  init_runner();
29384
30543
  init_mcp_client();
30544
+ init_service();
29385
30545
  INFERENCE_HOST_CLI_VERSION = agent_cli_release_default.package_version;
29386
30546
  runtimeReceiptPath = (config2) => `${config2.statePath}.runtime.json`;
29387
30547
  codexRecoveryPath = (config2) => `${config2.statePath}.codex-recovery.json`;
@@ -29459,6 +30619,7 @@ Commands:
29459
30619
  agent-next Claim the next exact VTX inference request
29460
30620
  agent-complete Submit one completed agent result from stdin
29461
30621
  agent-fail Submit one truthful agent failure from stdin
30622
+ service Install and control the durable background host
29462
30623
  status Inspect local host and credential state
29463
30624
  doctor Verify credentials, Codex, and private runtime state
29464
30625
  logout Remove local VTX host state without revoking the grant
@@ -29472,6 +30633,10 @@ Common options:
29472
30633
  If the OS credential store cannot retain the VTX grant, set
29473
30634
  VTX_INFERENCE_HOST_CREDENTIAL_STORE=file before login to use the supported
29474
30635
  private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
30636
+
30637
+ Durable service:
30638
+ vtx inference-host service install
30639
+ vtx inference-host service <start|stop|status|logs|uninstall>
29475
30640
  `;
29476
30641
  parsePositiveInteger = (raw, label) => {
29477
30642
  const value = Number(raw);
@@ -29494,6 +30659,8 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29494
30659
  let modelLabel = null;
29495
30660
  let reasoningEffort = null;
29496
30661
  let waitSeconds = 50;
30662
+ let lines = 100;
30663
+ let serviceManifestPath = null;
29497
30664
  const positionals = [];
29498
30665
  for (let index = 0; index < argv2.length; index += 1) {
29499
30666
  const argument = argv2[index];
@@ -29520,7 +30687,7 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29520
30687
  index += 1;
29521
30688
  continue;
29522
30689
  }
29523
- if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds"].includes(argument)) {
30690
+ if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest"].includes(argument)) {
29524
30691
  const raw = argv2[index + 1];
29525
30692
  if (!raw?.trim()) throw new Error(`${argument} requires a value.`);
29526
30693
  if (argument === "--adapter") adapter = raw.trim();
@@ -29533,6 +30700,13 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29533
30700
  throw new Error("--wait-seconds must be an integer from 0 through 300.");
29534
30701
  }
29535
30702
  }
30703
+ if (argument === "--lines") {
30704
+ lines = Number(raw);
30705
+ if (!Number.isInteger(lines) || lines < 1 || lines > 1e4) {
30706
+ throw new Error("--lines must be an integer from 1 through 10000.");
30707
+ }
30708
+ }
30709
+ if (argument === "--service-manifest") serviceManifestPath = resolve5(raw.trim());
29536
30710
  index += 1;
29537
30711
  continue;
29538
30712
  }
@@ -29544,10 +30718,14 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29544
30718
  if (positionals[0] !== "inference-host") {
29545
30719
  throw new Error("Inference-host command routing is invalid.");
29546
30720
  }
29547
- if (positionals.length > 2) {
30721
+ if (positionals.length > 3) {
29548
30722
  throw new Error("Inference-host commands do not accept positional arguments.");
29549
30723
  }
29550
30724
  const command = positionals[1] || null;
30725
+ const serviceAction = command === "service" ? positionals[2] ?? null : null;
30726
+ if (command !== "service" && positionals.length > 2) {
30727
+ throw new Error("Inference-host commands do not accept positional arguments.");
30728
+ }
29551
30729
  if (command === "agent-connect" && !displayNameExplicit) {
29552
30730
  displayName = "Agent-driven inference host";
29553
30731
  }
@@ -29561,13 +30739,16 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29561
30739
  modelId,
29562
30740
  modelLabel,
29563
30741
  reasoningEffort,
29564
- waitSeconds
30742
+ waitSeconds,
30743
+ lines,
30744
+ serviceAction,
30745
+ serviceManifestPath
29565
30746
  };
29566
30747
  };
29567
30748
  defaultOpenBrowser = (url2) => {
29568
30749
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
29569
30750
  const args = process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
29570
- const child = spawn4(command, args, {
30751
+ const child = spawn6(command, args, {
29571
30752
  detached: true,
29572
30753
  stdio: "ignore",
29573
30754
  windowsHide: true
@@ -29636,11 +30817,11 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29636
30817
  return raw !== null;
29637
30818
  };
29638
30819
  inspectCodexAuthentication = async (config2) => {
29639
- const path = join5(config2.codexHomePath, "auth.json");
30820
+ const path = join6(config2.codexHomePath, "auth.json");
29640
30821
  try {
29641
30822
  const before = await lstat4(path);
29642
30823
  const canonical = await realpath4(path);
29643
- const isPrivate = before.isFile() && !before.isSymbolicLink() && canonical === resolve4(path) && before.size <= 8 * 1024 * 1024 && (process.platform === "win32" || (before.mode & 63) === 0 && (typeof process.getuid !== "function" || before.uid === process.getuid()));
30824
+ const isPrivate = before.isFile() && !before.isSymbolicLink() && canonical === resolve5(path) && before.size <= 8 * 1024 * 1024 && (process.platform === "win32" || (before.mode & 63) === 0 && (typeof process.getuid !== "function" || before.uid === process.getuid()));
29644
30825
  return { present: true, private: isPrivate };
29645
30826
  } catch (error48) {
29646
30827
  if (error48.code === "ENOENT") {
@@ -29654,7 +30835,7 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29654
30835
  runtimeReceiptPath(config2),
29655
30836
  "Inference host runtime receipt file"
29656
30837
  );
29657
- await rm4(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
30838
+ await rm5(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
29658
30839
  await clearInferencePrivateFile(
29659
30840
  codexRecoveryPath(config2),
29660
30841
  "Codex attempt recovery file"
@@ -29663,6 +30844,13 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29663
30844
  await clearInferenceAgentNextState(config2.statePath);
29664
30845
  await clearInferenceHostLocalState(config2.statePath);
29665
30846
  };
30847
+ assertDurableServiceUninstalled = async (config2) => {
30848
+ if (await readInferenceHostServiceManifest(`${config2.statePath}.service.json`)) {
30849
+ throw new Error(
30850
+ "Uninstall the durable inference-host service before logout, revoke, or Codex logout."
30851
+ );
30852
+ }
30853
+ };
29666
30854
  assertLocalRuntimeArtifactsMayBeDiscarded = async (config2) => {
29667
30855
  const receipt = await new FileInferenceHostRuntimeReceiptStore(
29668
30856
  runtimeReceiptPath(config2)
@@ -29761,20 +30949,20 @@ Waiting for approval...
29761
30949
  } catch {
29762
30950
  }
29763
30951
  }
29764
- let access3;
30952
+ let access4;
29765
30953
  try {
29766
- access3 = await pending.completion;
30954
+ access4 = await pending.completion;
29767
30955
  } catch (error48) {
29768
30956
  await pending.cancel().catch(() => void 0);
29769
30957
  throw error48;
29770
30958
  }
29771
30959
  const state = {
29772
30960
  schema_version: 1,
29773
- issuer: access3.credential.issuer,
29774
- client_id: access3.credential.client_id,
29775
- host_id: access3.credential.host_id,
30961
+ issuer: access4.credential.issuer,
30962
+ client_id: access4.credential.client_id,
30963
+ host_id: access4.credential.host_id,
29776
30964
  host_generation: 1,
29777
- key_generation: access3.credential.key_generation
30965
+ key_generation: access4.credential.key_generation
29778
30966
  };
29779
30967
  try {
29780
30968
  await writeInferenceHostLocalState(config2.statePath, state);
@@ -29784,7 +30972,7 @@ Waiting for approval...
29784
30972
  const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
29785
30973
  await (dependencies.revokeCredential ?? revokeInferenceCredential)({
29786
30974
  metadata,
29787
- credential: access3.credential,
30975
+ credential: access4.credential,
29788
30976
  store
29789
30977
  });
29790
30978
  remoteCleanupConfirmed = true;
@@ -29795,8 +30983,8 @@ Waiting for approval...
29795
30983
  { cause: stateError }
29796
30984
  );
29797
30985
  }
29798
- await store.writeRecovery(access3.accountKey, access3.credential);
29799
- await store.remove(access3.accountKey).catch(() => void 0);
30986
+ await store.writeRecovery(access4.accountKey, access4.credential);
30987
+ await store.remove(access4.accountKey).catch(() => void 0);
29800
30988
  }
29801
30989
  if (!remoteCleanupConfirmed) {
29802
30990
  throw new Error(
@@ -29875,6 +31063,7 @@ Waiting for approval...
29875
31063
  }
29876
31064
  };
29877
31065
  codexLogout = async (config2, parsed, env, dependencies) => {
31066
+ await assertDurableServiceUninstalled(config2);
29878
31067
  const cancellation = lifecycleCancellation(dependencies);
29879
31068
  let lock2 = null;
29880
31069
  try {
@@ -30051,6 +31240,7 @@ Waiting for approval...
30051
31240
  };
30052
31241
  };
30053
31242
  cleanupLogin = async (config2, parsed, dependencies, warnings, revoke) => {
31243
+ await assertDurableServiceUninstalled(config2);
30054
31244
  const lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
30055
31245
  let keeperLock = null;
30056
31246
  try {
@@ -30419,7 +31609,7 @@ Waiting for approval...
30419
31609
  }
30420
31610
  if (parsed.once && !commandLock) break;
30421
31611
  await (dependencies.sleep ?? (async (milliseconds) => {
30422
- await new Promise((resolve5) => setTimeout(resolve5, milliseconds));
31612
+ await new Promise((resolve6) => setTimeout(resolve6, milliseconds));
30423
31613
  }))(retryDelayMs);
30424
31614
  }
30425
31615
  return {
@@ -30444,7 +31634,7 @@ Waiting for approval...
30444
31634
  const session = await agentSession(config2, dependencies, warnings);
30445
31635
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
30446
31636
  const sleep4 = dependencies.sleep ?? (async (milliseconds) => {
30447
- await new Promise((resolve5) => setTimeout(resolve5, milliseconds));
31637
+ await new Promise((resolve6) => setTimeout(resolve6, milliseconds));
30448
31638
  });
30449
31639
  const stopAt = now().getTime() + parsed.waitSeconds * 1e3;
30450
31640
  while (true) {
@@ -30628,6 +31818,8 @@ Waiting for approval...
30628
31818
  throw new Error("agent-fail requires a boolean retryable field.");
30629
31819
  }
30630
31820
  const dispatched = dispatchOutcome !== "not_dispatched";
31821
+ const effectiveModel = dispatched && Object.hasOwn(input, "effective_model") ? input.effective_model : null;
31822
+ const effectiveEffort = dispatched && Object.hasOwn(input, "effective_reasoning_effort") ? input.effective_reasoning_effort : null;
30631
31823
  request = agentFailRequestSchema.parse({
30632
31824
  operation_id: state.failure_operation_id,
30633
31825
  host_id: state.host_id,
@@ -30635,8 +31827,8 @@ Waiting for approval...
30635
31827
  attempt_id: state.attempt_id,
30636
31828
  claim_handle: state.claim_handle,
30637
31829
  dispatch_outcome: dispatchOutcome,
30638
- effective_model: dispatched ? String(input.effective_model ?? state.requested_model) : null,
30639
- effective_reasoning_effort: dispatched ? String(input.effective_reasoning_effort ?? state.requested_reasoning_effort) : null,
31830
+ effective_model: effectiveModel == null ? null : String(effectiveModel),
31831
+ effective_reasoning_effort: effectiveEffort == null ? null : String(effectiveEffort),
30640
31832
  adapter_request_id: input.adapter_request_id == null ? null : String(input.adapter_request_id),
30641
31833
  adapter_response_id: input.adapter_response_id == null ? null : String(input.adapter_response_id),
30642
31834
  usage: input.usage ?? (dispatched ? {
@@ -30672,15 +31864,127 @@ Waiting for approval...
30672
31864
  await lock2.release();
30673
31865
  }
30674
31866
  };
31867
+ serviceCommand = async (config2, parsed, env, dependencies, warnings) => {
31868
+ const action = parsed.serviceAction;
31869
+ if (!action) {
31870
+ throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
31871
+ }
31872
+ if (action === "run-internal") {
31873
+ if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
31874
+ const serviceManifest = await readInferenceHostServiceManifest(parsed.serviceManifestPath);
31875
+ if (!serviceManifest) throw new Error("Inference-host service manifest is missing.");
31876
+ const serviceConfig = resolveInferenceHostConfig({
31877
+ ...env,
31878
+ ...serviceManifest.runtime_environment
31879
+ });
31880
+ const serviceLock = await acquireInferenceHostProcessLock(
31881
+ `${serviceConfig.processLockPath}.service`
31882
+ );
31883
+ const cancellation = lifecycleCancellation(dependencies);
31884
+ try {
31885
+ await (dependencies.runServiceSupervisor ?? runInferenceHostServiceSupervisor)(
31886
+ parsed.serviceManifestPath,
31887
+ {
31888
+ signal: cancellation.signal,
31889
+ runWorker: async (manifest, signal) => {
31890
+ const startedAt = Date.now();
31891
+ const serviceEnv = { ...env, ...manifest.runtime_environment };
31892
+ const serviceConfig2 = resolveInferenceHostConfig(serviceEnv);
31893
+ const unregister = (abort) => {
31894
+ const onAbort = () => abort("SIGTERM");
31895
+ signal.addEventListener("abort", onAbort, { once: true });
31896
+ return () => signal.removeEventListener("abort", onAbort);
31897
+ };
31898
+ const result2 = await runHost(serviceConfig2, {
31899
+ ...parsed,
31900
+ command: "run",
31901
+ serviceAction: null,
31902
+ serviceManifestPath: null,
31903
+ displayName: manifest.display_name,
31904
+ once: false
31905
+ }, serviceEnv, {
31906
+ ...dependencies,
31907
+ registerLifecycleSignalHandlers: unregister
31908
+ }, warnings);
31909
+ return { exitCode: result2.exitCode, uptimeMs: Date.now() - startedAt };
31910
+ }
31911
+ }
31912
+ );
31913
+ return { exitCode: 0, stdout: "", stderr: "" };
31914
+ } finally {
31915
+ cancellation.unregister();
31916
+ await serviceLock.release();
31917
+ }
31918
+ }
31919
+ const manager = dependencies.createServiceManager?.(
31920
+ config2,
31921
+ dependencies.serviceDependencies
31922
+ ) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
31923
+ if (action === "install") {
31924
+ const adapter = parsed.adapter || "codex";
31925
+ if (adapter !== "codex") {
31926
+ throw new Error(
31927
+ `Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
31928
+ );
31929
+ }
31930
+ const state = await readInferenceHostLocalState(config2.statePath);
31931
+ if (!state) throw new Error("Run vtx inference-host login before installing the service.");
31932
+ const store = configuredCredentialStore(config2, dependencies, (message) => warnings.push(message));
31933
+ const credential = await store.read(accountKeyForState(state));
31934
+ if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
31935
+ assertCredentialMatchesState2(state, credential);
31936
+ if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
31937
+ throw new Error("Automated Codex service does not accept agent model or effort options.");
31938
+ }
31939
+ const auth = await inspectCodexAuthentication(config2);
31940
+ if (!auth.present || !auth.private) {
31941
+ throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
31942
+ }
31943
+ const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
31944
+ await (dependencies.preflightCodex ?? preflightCodexSubscription)({
31945
+ binary,
31946
+ codexHome: config2.codexHomePath,
31947
+ deadlineAtMs: Date.now() + 3e4
31948
+ });
31949
+ const status = await manager.install({ adapter: "codex", displayName: parsed.displayName });
31950
+ return {
31951
+ exitCode: 0,
31952
+ stdout: render({ status: "service_installed", ...status }, parsed.json),
31953
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
31954
+ ` : ""
31955
+ };
31956
+ }
31957
+ if (parsed.adapter || parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
31958
+ throw new Error("Service adapter and model options are accepted only by service install.");
31959
+ }
31960
+ if (action === "logs") {
31961
+ return { exitCode: 0, stdout: await manager.logs(parsed.lines), stderr: "" };
31962
+ }
31963
+ if (action === "start") {
31964
+ return { exitCode: 0, stdout: render({ status: "service_started", ...await manager.start() }, parsed.json), stderr: "" };
31965
+ }
31966
+ if (action === "stop") {
31967
+ return { exitCode: 0, stdout: render({ status: "service_stopped", ...await manager.stop() }, parsed.json), stderr: "" };
31968
+ }
31969
+ if (action === "status") {
31970
+ const status = await manager.status();
31971
+ const lifecycleMatches = status.installed && (status.desired_running ? status.manager_active : !status.manager_active);
31972
+ return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
31973
+ }
31974
+ if (action === "uninstall") {
31975
+ return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...await manager.uninstall() }, parsed.json), stderr: "" };
31976
+ }
31977
+ throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
31978
+ };
30675
31979
  }
30676
31980
  });
30677
31981
 
30678
31982
  // lib/agent-core/config.ts
30679
- import { mkdir as mkdir3, readFile as readFile5, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
30680
- import { dirname as dirname3, join as join6 } from "node:path";
30681
- import { homedir as homedir2 } from "node:os";
31983
+ import { mkdir as mkdir5, readFile as readFile6, rm as rm6, writeFile as writeFile3 } from "node:fs/promises";
31984
+ import { dirname as dirname5, join as join7 } from "node:path";
31985
+ import { homedir as homedir3 } from "node:os";
30682
31986
  function defaultBaseDir() {
30683
- return join6(homedir2(), ".vtx");
31987
+ return join7(homedir3(), ".vtx");
30684
31988
  }
30685
31989
  function resolveAgentCliConfig(env = process.env) {
30686
31990
  const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
@@ -30688,8 +31992,8 @@ function resolveAgentCliConfig(env = process.env) {
30688
31992
  const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
30689
31993
  return {
30690
31994
  apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
30691
- tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join6(baseDir, "token.json"),
30692
- statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join6(baseDir, "runtime-state.json"),
31995
+ tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join7(baseDir, "token.json"),
31996
+ statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join7(baseDir, "runtime-state.json"),
30693
31997
  runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
30694
31998
  activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
30695
31999
  outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
@@ -30697,7 +32001,7 @@ function resolveAgentCliConfig(env = process.env) {
30697
32001
  }
30698
32002
  async function readStoredAgentAuth(path) {
30699
32003
  try {
30700
- const raw = await readFile5(path, "utf8");
32004
+ const raw = await readFile6(path, "utf8");
30701
32005
  if (!raw.trim()) {
30702
32006
  throw new Error(`VTX token file is empty at ${path}. Run "vtx auth login" or remove the file and retry.`);
30703
32007
  }
@@ -30715,16 +32019,16 @@ async function readStoredAgentAuth(path) {
30715
32019
  }
30716
32020
  }
30717
32021
  async function writeStoredAgentAuth(path, auth) {
30718
- await mkdir3(dirname3(path), { recursive: true });
30719
- await writeFile2(path, `${JSON.stringify(auth, null, 2)}
32022
+ await mkdir5(dirname5(path), { recursive: true });
32023
+ await writeFile3(path, `${JSON.stringify(auth, null, 2)}
30720
32024
  `, { encoding: "utf8", mode: 384 });
30721
32025
  }
30722
32026
  async function clearStoredAgentAuth(path) {
30723
- await rm5(path, { force: true });
32027
+ await rm6(path, { force: true });
30724
32028
  }
30725
32029
  async function readRuntimeState(path) {
30726
32030
  try {
30727
- const raw = await readFile5(path, "utf8");
32031
+ const raw = await readFile6(path, "utf8");
30728
32032
  const parsed = JSON.parse(raw);
30729
32033
  const profileId = Number(parsed.profileId);
30730
32034
  const runtimeSessionId = String(parsed.runtimeSessionId || "").trim();
@@ -30748,12 +32052,12 @@ async function readRuntimeState(path) {
30748
32052
  }
30749
32053
  }
30750
32054
  async function writeRuntimeState(path, state) {
30751
- await mkdir3(dirname3(path), { recursive: true });
30752
- await writeFile2(path, `${JSON.stringify(state, null, 2)}
32055
+ await mkdir5(dirname5(path), { recursive: true });
32056
+ await writeFile3(path, `${JSON.stringify(state, null, 2)}
30753
32057
  `, { encoding: "utf8", mode: 384 });
30754
32058
  }
30755
32059
  async function clearRuntimeState(path) {
30756
- await rm5(path, { force: true });
32060
+ await rm6(path, { force: true });
30757
32061
  }
30758
32062
  var init_config2 = __esm({
30759
32063
  "lib/agent-core/config.ts"() {
@@ -32578,12 +33882,12 @@ var init_hyperliquid_account_state_adapter = __esm({
32578
33882
  if (signal.aborted) {
32579
33883
  throw createAbortError(abortMessage);
32580
33884
  }
32581
- return new Promise((resolve5, reject) => {
33885
+ return new Promise((resolve6, reject) => {
32582
33886
  const onAbort = () => {
32583
33887
  reject(createAbortError(abortMessage));
32584
33888
  };
32585
33889
  signal.addEventListener("abort", onAbort, { once: true });
32586
- promise2.then(resolve5, reject).finally(() => {
33890
+ promise2.then(resolve6, reject).finally(() => {
32587
33891
  signal.removeEventListener("abort", onAbort);
32588
33892
  });
32589
33893
  });
@@ -33994,7 +35298,7 @@ var init_api2 = __esm({
33994
35298
  }
33995
35299
  };
33996
35300
  API_URL = getApiUrl();
33997
- sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
35301
+ sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
33998
35302
  isTransientNetworkFetchError = (error48) => {
33999
35303
  const message = error48 instanceof Error ? error48.message : String(error48 || "");
34000
35304
  const normalized = message.toLowerCase();
@@ -42229,9 +43533,9 @@ function decryptKeystoreJsonSync(json2, _password) {
42229
43533
  return getAccount(data, key);
42230
43534
  }
42231
43535
  function stall(duration3) {
42232
- return new Promise((resolve5) => {
43536
+ return new Promise((resolve6) => {
42233
43537
  setTimeout(() => {
42234
- resolve5();
43538
+ resolve6();
42235
43539
  }, duration3);
42236
43540
  });
42237
43541
  }
@@ -42841,9 +44145,9 @@ var init_json_crowdsale = __esm({
42841
44145
 
42842
44146
  // node_modules/ethers/lib.esm/wallet/wallet.js
42843
44147
  function stall2(duration3) {
42844
- return new Promise((resolve5) => {
44148
+ return new Promise((resolve6) => {
42845
44149
  setTimeout(() => {
42846
- resolve5();
44150
+ resolve6();
42847
44151
  }, duration3);
42848
44152
  });
42849
44153
  }
@@ -43938,8 +45242,8 @@ var init_exchange_mutation_fence = __esm({
43938
45242
  return operation();
43939
45243
  }
43940
45244
  let markSettled;
43941
- const settlement = new Promise((resolve5) => {
43942
- markSettled = resolve5;
45245
+ const settlement = new Promise((resolve6) => {
45246
+ markSettled = resolve6;
43943
45247
  });
43944
45248
  const activeSettlements = activeSettlementsByScope.get(normalizedScope) ?? /* @__PURE__ */ new Set();
43945
45249
  activeSettlements.add(settlement);
@@ -45196,12 +46500,12 @@ var init_hyperliquid_client = __esm({
45196
46500
  },
45197
46501
  captureController.signal
45198
46502
  ).then(() => void 0).catch(() => void 0);
45199
- const captureDeadline = new Promise((resolve5) => {
46503
+ const captureDeadline = new Promise((resolve6) => {
45200
46504
  captureTimeout = globalThis.setTimeout(() => {
45201
46505
  captureController.abort(
45202
46506
  new DOMException("Execution attempt capture timed out.", "TimeoutError")
45203
46507
  );
45204
- resolve5();
46508
+ resolve6();
45205
46509
  }, remainingBudgetMs);
45206
46510
  });
45207
46511
  await Promise.race([captureRequest, captureDeadline]);
@@ -45635,7 +46939,7 @@ var init_vault = __esm({
45635
46939
  }
45636
46940
  return output3.buffer.slice(output3.byteOffset, output3.byteOffset + output3.byteLength);
45637
46941
  };
45638
- openVaultDb = async () => new Promise((resolve5, reject) => {
46942
+ openVaultDb = async () => new Promise((resolve6, reject) => {
45639
46943
  const request = indexedDB.open(VAULT_DB_NAME, VAULT_DB_VERSION);
45640
46944
  request.onerror = () => reject(request.error ?? new Error("Failed to open client vault database."));
45641
46945
  request.onupgradeneeded = () => {
@@ -45644,7 +46948,7 @@ var init_vault = __esm({
45644
46948
  database.createObjectStore(VAULT_KEY_STORE);
45645
46949
  }
45646
46950
  };
45647
- request.onsuccess = () => resolve5(request.result);
46951
+ request.onsuccess = () => resolve6(request.result);
45648
46952
  });
45649
46953
  withVaultStore = async (mode, fn) => {
45650
46954
  const database = await openVaultDb();
@@ -45652,8 +46956,8 @@ var init_vault = __esm({
45652
46956
  const transaction = database.transaction(VAULT_KEY_STORE, mode);
45653
46957
  const store = transaction.objectStore(VAULT_KEY_STORE);
45654
46958
  const result2 = await fn(store);
45655
- await new Promise((resolve5, reject) => {
45656
- transaction.oncomplete = () => resolve5();
46959
+ await new Promise((resolve6, reject) => {
46960
+ transaction.oncomplete = () => resolve6();
45657
46961
  transaction.onerror = () => reject(transaction.error ?? new Error("Client vault transaction failed."));
45658
46962
  transaction.onabort = () => reject(transaction.error ?? new Error("Client vault transaction aborted."));
45659
46963
  });
@@ -45668,9 +46972,9 @@ var init_vault = __esm({
45668
46972
  }
45669
46973
  return withVaultStore("readonly", async (store) => {
45670
46974
  const request = store.get(profileId);
45671
- return await new Promise((resolve5, reject) => {
46975
+ return await new Promise((resolve6, reject) => {
45672
46976
  request.onerror = () => reject(request.error ?? new Error("Failed to read client vault key."));
45673
- request.onsuccess = () => resolve5(request.result ?? null);
46977
+ request.onsuccess = () => resolve6(request.result ?? null);
45674
46978
  });
45675
46979
  });
45676
46980
  };
@@ -45692,9 +46996,9 @@ var init_vault = __esm({
45692
46996
  );
45693
46997
  await withVaultStore("readwrite", async (store) => {
45694
46998
  const request = store.put(createdKey, profileId);
45695
- await new Promise((resolve5, reject) => {
46999
+ await new Promise((resolve6, reject) => {
45696
47000
  request.onerror = () => reject(request.error ?? new Error("Failed to persist client vault key."));
45697
- request.onsuccess = () => resolve5();
47001
+ request.onsuccess = () => resolve6();
45698
47002
  });
45699
47003
  });
45700
47004
  return createdKey;
@@ -45705,9 +47009,9 @@ var init_vault = __esm({
45705
47009
  }
45706
47010
  await withVaultStore("readwrite", async (store) => {
45707
47011
  const request = store.delete(profileId);
45708
- await new Promise((resolve5, reject) => {
47012
+ await new Promise((resolve6, reject) => {
45709
47013
  request.onerror = () => reject(request.error ?? new Error("Failed to remove client vault key."));
45710
- request.onsuccess = () => resolve5();
47014
+ request.onsuccess = () => resolve6();
45711
47015
  });
45712
47016
  });
45713
47017
  };
@@ -46922,10 +48226,10 @@ var init_runtime_execution = __esm({
46922
48226
  }
46923
48227
  };
46924
48228
  sleep3 = async (ms, signal) => {
46925
- await new Promise((resolve5, reject) => {
48229
+ await new Promise((resolve6, reject) => {
46926
48230
  const timer = setTimeout(() => {
46927
48231
  cleanup();
46928
- resolve5(void 0);
48232
+ resolve6(void 0);
46929
48233
  }, ms);
46930
48234
  const cleanup = () => {
46931
48235
  clearTimeout(timer);
@@ -48563,7 +49867,7 @@ __export(vtx_exports, {
48563
49867
  runVtxCli: () => runVtxCli
48564
49868
  });
48565
49869
  import { randomUUID as randomUUID5 } from "node:crypto";
48566
- import { spawn as spawn5 } from "node:child_process";
49870
+ import { spawn as spawn7 } from "node:child_process";
48567
49871
  function render2(value, json2) {
48568
49872
  if (json2) {
48569
49873
  return `${JSON.stringify(value, null, 2)}
@@ -48593,7 +49897,7 @@ function openBrowser(url2) {
48593
49897
  const platform = process.platform;
48594
49898
  const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
48595
49899
  const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
48596
- const child = spawn5(command, args, { detached: true, stdio: "ignore" });
49900
+ const child = spawn7(command, args, { detached: true, stdio: "ignore" });
48597
49901
  child.unref();
48598
49902
  }
48599
49903
  function parseFlags(args) {
@@ -49123,7 +50427,7 @@ Global options:
49123
50427
  "alibaba_cloud_model_studio_api_key",
49124
50428
  "lightning_api_key"
49125
50429
  ]);
49126
- delay = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
50430
+ delay = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
49127
50431
  }
49128
50432
  });
49129
50433
 
@@ -49135,7 +50439,9 @@ var INFERENCE_HOST_VALUE_OPTIONS = /* @__PURE__ */ new Set([
49135
50439
  "--model",
49136
50440
  "--model-label",
49137
50441
  "--effort",
49138
- "--wait-seconds"
50442
+ "--wait-seconds",
50443
+ "--lines",
50444
+ "--service-manifest"
49139
50445
  ]);
49140
50446
  var isInferenceHostCliInvocation = (argv2) => {
49141
50447
  for (let index = 0; index < argv2.length; index += 1) {