@vtxmacro/cli 2026.8.18 → 2026.8.20

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 +1535 -220
  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.18",
41
+ package_version: "2026.8.20",
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
  });
@@ -17825,7 +18258,7 @@ async function registerInferenceOAuthClient(metadata, redirectUri, fetchImpl = f
17825
18258
  grant_types: ["authorization_code", "refresh_token"],
17826
18259
  response_types: ["code"],
17827
18260
  application_type: "native",
17828
- client_name: "VTX Codex inference host",
18261
+ client_name: "VTX inference host",
17829
18262
  scope: INFERENCE_SCOPE
17830
18263
  })
17831
18264
  }, requestOptions);
@@ -18072,11 +18505,11 @@ async function revokeInferenceCredential(options) {
18072
18505
  }
18073
18506
  }
18074
18507
  async function listenLoopback(server) {
18075
- await new Promise((resolve5, reject) => {
18508
+ await new Promise((resolve6, reject) => {
18076
18509
  server.once("error", reject);
18077
18510
  server.listen(0, CALLBACK_HOST, () => {
18078
18511
  server.off("error", reject);
18079
- resolve5();
18512
+ resolve6();
18080
18513
  });
18081
18514
  });
18082
18515
  const address = server.address();
@@ -18087,7 +18520,7 @@ async function listenLoopback(server) {
18087
18520
  }
18088
18521
  async function closeServer(server) {
18089
18522
  if (!server.listening) return;
18090
- await new Promise((resolve5) => server.close(() => resolve5()));
18523
+ await new Promise((resolve6) => server.close(() => resolve6()));
18091
18524
  }
18092
18525
  async function beginInferenceOAuthLogin(options) {
18093
18526
  const loginTimeoutMs = options.timeoutMs ?? 6e5;
@@ -18143,7 +18576,7 @@ async function beginInferenceOAuthLogin(options) {
18143
18576
  });
18144
18577
  let settled = false;
18145
18578
  let rejectCompletion;
18146
- const completion = new Promise((resolve5, reject) => {
18579
+ const completion = new Promise((resolve6, reject) => {
18147
18580
  rejectCompletion = reject;
18148
18581
  handler = async (requestUrl, method, writeResponse) => {
18149
18582
  if (settled) {
@@ -18218,7 +18651,7 @@ async function beginInferenceOAuthLogin(options) {
18218
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>'
18219
18652
  );
18220
18653
  await closeServer(server);
18221
- resolve5({
18654
+ resolve6({
18222
18655
  accessToken: tokens.accessToken,
18223
18656
  expiresIn: tokens.expiresIn,
18224
18657
  credential,
@@ -18332,7 +18765,7 @@ async function createInferenceAgentMcpSession(options) {
18332
18765
  options.fetchImpl,
18333
18766
  { signal: options.signal, timeoutMs: options.requestTimeoutMs }
18334
18767
  );
18335
- let access3 = null;
18768
+ let access4 = null;
18336
18769
  let accessExpiresAt = 0;
18337
18770
  let refreshPromise = null;
18338
18771
  const refreshAccessToken = async (requestOptions = {}) => {
@@ -18349,7 +18782,7 @@ async function createInferenceAgentMcpSession(options) {
18349
18782
  });
18350
18783
  assertCredentialMatchesState(localState, refreshed.credential);
18351
18784
  credential = refreshed.credential;
18352
- access3 = refreshed;
18785
+ access4 = refreshed;
18353
18786
  accessExpiresAt = Date.now() + refreshed.expiresIn * 1e3;
18354
18787
  return refreshed.accessToken;
18355
18788
  })();
@@ -18362,10 +18795,10 @@ async function createInferenceAgentMcpSession(options) {
18362
18795
  await refreshAccessToken({ signal: options.signal });
18363
18796
  const tokenSource = {
18364
18797
  accessToken: async (requestOptions) => {
18365
- if (!access3 || accessExpiresAt - Date.now() <= 5e3) {
18798
+ if (!access4 || accessExpiresAt - Date.now() <= 5e3) {
18366
18799
  return refreshAccessToken(requestOptions);
18367
18800
  }
18368
- return access3.accessToken;
18801
+ return access4.accessToken;
18369
18802
  },
18370
18803
  refreshAccessToken
18371
18804
  };
@@ -18518,10 +18951,10 @@ var init_agent_state = __esm({
18518
18951
  });
18519
18952
 
18520
18953
  // lib/inference-host/codex-app-server.ts
18521
- import { spawn as spawn2 } from "node:child_process";
18522
- 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";
18523
18956
  import { tmpdir } from "node:os";
18524
- 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";
18525
18958
  import { createInterface } from "node:readline";
18526
18959
  async function loginCodexSubscription(options) {
18527
18960
  const session = await CodexAppServerSession.start(options);
@@ -18587,6 +19020,7 @@ var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServer
18587
19020
  var init_codex_app_server = __esm({
18588
19021
  "lib/inference-host/codex-app-server.ts"() {
18589
19022
  "use strict";
19023
+ init_config();
18590
19024
  CODEX_INFERENCE_PERMISSION_PROFILE = "vtx_inference_readonly";
18591
19025
  CODEX_ACCOUNT_PLAN_TYPES = [
18592
19026
  "free",
@@ -18820,8 +19254,8 @@ var init_codex_app_server = __esm({
18820
19254
  };
18821
19255
  };
18822
19256
  killWindowsProcessTree = async (pid) => {
18823
- await new Promise((resolve5, reject) => {
18824
- const child = spawn2(
19257
+ await new Promise((resolve6, reject) => {
19258
+ const child = spawn3(
18825
19259
  "taskkill.exe",
18826
19260
  ["/PID", String(pid), "/T", "/F"],
18827
19261
  {
@@ -18845,7 +19279,7 @@ var init_codex_app_server = __esm({
18845
19279
  });
18846
19280
  child.once("close", (code) => {
18847
19281
  clearTimeout(timer);
18848
- if (code === 0 || code === 128) resolve5();
19282
+ if (code === 0 || code === 128) resolve6();
18849
19283
  else reject(new Error("Windows Codex process-tree cleanup failed."));
18850
19284
  });
18851
19285
  });
@@ -18879,7 +19313,8 @@ var init_codex_app_server = __esm({
18879
19313
  };
18880
19314
  GUARDIAN_SCRIPT = String.raw`
18881
19315
  import { spawn } from 'node:child_process';
18882
- 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';
18883
19318
  const input = JSON.parse(Buffer.from(process.argv[1], 'base64url').toString('utf8'));
18884
19319
  // The guardian owns the process boundary for every attempt. Apply a private
18885
19320
  // umask before writing receipts or starting Codex so a lost thread/start reply
@@ -18916,6 +19351,7 @@ const writeReceipt = (state, childPid) => {
18916
19351
  state,
18917
19352
  guardian_pid: process.pid,
18918
19353
  child_pid: childPid ?? null,
19354
+ boot_identity: input.bootIdentity,
18919
19355
  updated_at: new Date().toISOString(),
18920
19356
  }) + '\n';
18921
19357
  receiptWriteSequence += 1;
@@ -18923,7 +19359,13 @@ const writeReceipt = (state, childPid) => {
18923
19359
  try {
18924
19360
  await writeFile(temporary, body, { mode: 0o600 });
18925
19361
  await chmod(temporary, 0o600).catch(() => undefined);
19362
+ const file = await open(temporary, 'r+');
19363
+ try { await file.sync(); } finally { await file.close(); }
18926
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
+ }
18927
19369
  } finally {
18928
19370
  await rm(temporary, { force: true }).catch(() => undefined);
18929
19371
  }
@@ -18960,6 +19402,7 @@ child = spawn(input.binary, input.args, {
18960
19402
  windowsHide: true,
18961
19403
  detached: false,
18962
19404
  });
19405
+ child.stdin.on('error', () => undefined);
18963
19406
  process.stdin.pipe(child.stdin, { end: false });
18964
19407
  child.stdout.pipe(process.stdout);
18965
19408
  child.stderr.pipe(process.stderr);
@@ -18973,9 +19416,14 @@ child.once('spawn', async () => {
18973
19416
  child.once('error', shutdown);
18974
19417
  child.once('close', async () => {
18975
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);
18976
19423
  try {
18977
19424
  await writeReceipt('terminated', child.pid ?? null);
18978
19425
  } finally {
19426
+ clearInterval(receiptKeepAlive);
18979
19427
  process.exit(0);
18980
19428
  }
18981
19429
  });
@@ -18991,7 +19439,7 @@ child.once('close', async () => {
18991
19439
  const platform = options.platform ?? process.platform;
18992
19440
  const renameFile = options.renameFile ?? rename2;
18993
19441
  const removeFile = options.removeFile ?? ((path) => rm2(path, { force: true }));
18994
- const sleep4 = options.sleep ?? ((milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds)));
19442
+ const sleep4 = options.sleep ?? ((milliseconds) => new Promise((resolve6) => setTimeout(resolve6, milliseconds)));
18995
19443
  const maxAttempts = Math.max(1, options.maxAttempts ?? 40);
18996
19444
  const retryDelayMs = Math.max(0, options.retryDelayMs ?? 25);
18997
19445
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
@@ -19022,20 +19470,38 @@ child.once('close', async () => {
19022
19470
  state: "spawn_intent",
19023
19471
  guardian_pid: null,
19024
19472
  child_pid: null,
19473
+ boot_identity: guardian.bootIdentity ?? null,
19025
19474
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
19026
19475
  })}
19027
19476
  `;
19028
19477
  const temporary = `${guardian.receiptPath}.intent-${process.pid}`;
19029
19478
  await writeFile(temporary, body, { mode: 384 });
19030
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
+ }
19031
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
+ }
19032
19495
  };
19033
19496
  parseGuardianReceipt = (raw) => {
19034
19497
  const value = JSON.parse(raw);
19035
- 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))) {
19036
19499
  throw new Error("Codex guardian receipt is invalid.");
19037
19500
  }
19038
- return value;
19501
+ return {
19502
+ ...value,
19503
+ boot_identity: typeof value.boot_identity === "string" ? value.boot_identity : null
19504
+ };
19039
19505
  };
19040
19506
  readCodexGuardianReceipt = async (path) => {
19041
19507
  try {
@@ -19061,14 +19527,14 @@ child.once('close', async () => {
19061
19527
  throw new Error("Codex guardian terminated before app-server initialization.");
19062
19528
  }
19063
19529
  }
19064
- await new Promise((resolve5) => setTimeout(resolve5, 25));
19530
+ await new Promise((resolve6) => setTimeout(resolve6, 25));
19065
19531
  }
19066
19532
  throw new Error(`Codex guardian ${expectedState} state was not confirmed.`);
19067
19533
  };
19068
19534
  defaultSpawn = (binary, args, options) => {
19069
19535
  if (!options.guardian) {
19070
19536
  const { guardian: _guardian, ...spawnOptions } = options;
19071
- return spawn2(binary, args, {
19537
+ return spawn3(binary, args, {
19072
19538
  ...spawnOptions,
19073
19539
  detached: options.detached
19074
19540
  });
@@ -19078,9 +19544,10 @@ child.once('close', async () => {
19078
19544
  args,
19079
19545
  processToken: options.guardian.processToken,
19080
19546
  receiptPath: options.guardian.receiptPath,
19081
- shutdownGraceMs: Math.max(25, options.guardian.shutdownGraceMs ?? 2e3)
19547
+ shutdownGraceMs: Math.max(25, options.guardian.shutdownGraceMs ?? 2e3),
19548
+ bootIdentity: options.guardian.bootIdentity
19082
19549
  }), "utf8").toString("base64url");
19083
- return spawn2(process.execPath, ["--input-type=module", "--eval", GUARDIAN_SCRIPT, payload], {
19550
+ return spawn3(process.execPath, ["--input-type=module", "--eval", GUARDIAN_SCRIPT, payload], {
19084
19551
  env: options.env,
19085
19552
  stdio: options.stdio,
19086
19553
  windowsHide: options.windowsHide,
@@ -19107,9 +19574,13 @@ child.once('close', async () => {
19107
19574
  }
19108
19575
  static async start(options) {
19109
19576
  const spawnProcess = options.spawnProcess ?? defaultSpawn;
19110
- if (options.guardian) {
19577
+ const guardian = options.guardian ? {
19578
+ ...options.guardian,
19579
+ bootIdentity: options.guardian.bootIdentity ?? await readInferenceSystemBootIdentity()
19580
+ } : void 0;
19581
+ if (guardian) {
19111
19582
  try {
19112
- await writeCodexGuardianSpawnIntent(options.guardian);
19583
+ await writeCodexGuardianSpawnIntent(guardian);
19113
19584
  } catch (error48) {
19114
19585
  throw new CodexAppServerError({
19115
19586
  message: "Codex guardian spawn intent could not be persisted.",
@@ -19125,17 +19596,32 @@ child.once('close', async () => {
19125
19596
  stdio: ["pipe", "pipe", "pipe"],
19126
19597
  windowsHide: true,
19127
19598
  detached: true,
19128
- guardian: options.guardian
19599
+ guardian
19129
19600
  });
19130
- if (options.guardian) {
19601
+ if (guardian) {
19131
19602
  try {
19132
19603
  await waitForCodexGuardianState(
19133
- options.guardian,
19604
+ guardian,
19134
19605
  "running",
19135
19606
  Math.min(options.deadlineAtMs, Date.now() + 1e4)
19136
19607
  );
19137
19608
  } catch (error48) {
19138
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
+ }
19139
19625
  throw new CodexAppServerError({
19140
19626
  message: "Codex guardian startup could not be confirmed.",
19141
19627
  category: "transport",
@@ -19170,6 +19656,23 @@ child.once('close', async () => {
19170
19656
  return session;
19171
19657
  } catch (error48) {
19172
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
+ }
19173
19676
  throw error48;
19174
19677
  }
19175
19678
  }
@@ -19287,8 +19790,8 @@ child.once('close', async () => {
19287
19790
  retryable: false
19288
19791
  });
19289
19792
  }
19290
- return await new Promise((resolve5, reject) => {
19291
- 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 };
19292
19795
  pending.timer = setTimeout(() => {
19293
19796
  this.pending.delete(id2);
19294
19797
  pending.abortCleanup?.();
@@ -19676,8 +20179,8 @@ child.once('close', async () => {
19676
20179
  let terminal = null;
19677
20180
  let terminalResolve;
19678
20181
  let terminalReject;
19679
- const terminalPromise = new Promise((resolve5, reject) => {
19680
- terminalResolve = resolve5;
20182
+ const terminalPromise = new Promise((resolve6, reject) => {
20183
+ terminalResolve = resolve6;
19681
20184
  terminalReject = reject;
19682
20185
  });
19683
20186
  void terminalPromise.catch(() => void 0);
@@ -20098,15 +20601,15 @@ child.once('close', async () => {
20098
20601
  if (this.closed) return;
20099
20602
  this.closed = true;
20100
20603
  this.process.stdin.end();
20101
- const exited = await new Promise((resolve5) => {
20604
+ const exited = await new Promise((resolve6) => {
20102
20605
  if (this.process.exitCode !== null) {
20103
- resolve5(true);
20606
+ resolve6(true);
20104
20607
  return;
20105
20608
  }
20106
- const timer = setTimeout(() => resolve5(false), 5e3);
20609
+ const timer = setTimeout(() => resolve6(false), 5e3);
20107
20610
  this.process.once("close", () => {
20108
20611
  clearTimeout(timer);
20109
- resolve5(true);
20612
+ resolve6(true);
20110
20613
  });
20111
20614
  });
20112
20615
  if (exited) return;
@@ -20117,11 +20620,11 @@ child.once('close', async () => {
20117
20620
  else this.process.kill("SIGTERM");
20118
20621
  } catch {
20119
20622
  }
20120
- const terminated = await new Promise((resolve5) => {
20121
- const timer = setTimeout(() => resolve5(false), 1e3);
20623
+ const terminated = await new Promise((resolve6) => {
20624
+ const timer = setTimeout(() => resolve6(false), 1e3);
20122
20625
  this.process.once("close", () => {
20123
20626
  clearTimeout(timer);
20124
- resolve5(true);
20627
+ resolve6(true);
20125
20628
  });
20126
20629
  });
20127
20630
  if (!terminated) {
@@ -20131,15 +20634,15 @@ child.once('close', async () => {
20131
20634
  else this.process.kill("SIGKILL");
20132
20635
  } catch {
20133
20636
  }
20134
- await new Promise((resolve5) => {
20637
+ await new Promise((resolve6) => {
20135
20638
  if (this.process.exitCode !== null) {
20136
- resolve5();
20639
+ resolve6();
20137
20640
  return;
20138
20641
  }
20139
- const timer = setTimeout(resolve5, 1e3);
20642
+ const timer = setTimeout(resolve6, 1e3);
20140
20643
  this.process.once("close", () => {
20141
20644
  clearTimeout(timer);
20142
- resolve5();
20645
+ resolve6();
20143
20646
  });
20144
20647
  });
20145
20648
  if (this.process.exitCode === null) {
@@ -20161,8 +20664,8 @@ import { createHash as createHash3 } from "node:crypto";
20161
20664
  import { constants as fsConstants } from "node:fs";
20162
20665
  import { access, readFile as readFile3, realpath as realpath2 } from "node:fs/promises";
20163
20666
  import { createRequire } from "node:module";
20164
- import { dirname as dirname2, join as join3 } from "node:path";
20165
- 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";
20166
20669
  var PINNED_CODEX_CLI_VERSION, PINNED_CODEX_LINUX_X64_SHA256, PINNED_CODEX_WINDOWS_X64_SHA256, pinnedShaForPlatform, pinnedPackageForPlatform, resolvePinnedCodexPackageBinaryPath, readCodexVersion, verifyPinnedCodexBinary, resolvePinnedCodexBinary;
20167
20670
  var init_codex_binary = __esm({
20168
20671
  "lib/inference-host/codex-binary.ts"() {
@@ -20199,15 +20702,15 @@ var init_codex_binary = __esm({
20199
20702
  const spec = pinnedPackageForPlatform(platform, architecture);
20200
20703
  const packageJsonPath = resolvePackage(`${spec.packageName}/package.json`);
20201
20704
  return join3(
20202
- dirname2(packageJsonPath),
20705
+ dirname3(packageJsonPath),
20203
20706
  "vendor",
20204
20707
  spec.target,
20205
20708
  "bin",
20206
20709
  spec.binaryName
20207
20710
  );
20208
20711
  };
20209
- readCodexVersion = async (binaryPath) => await new Promise((resolve5, reject) => {
20210
- const child = spawn3(binaryPath, ["--version"], {
20712
+ readCodexVersion = async (binaryPath) => await new Promise((resolve6, reject) => {
20713
+ const child = spawn4(binaryPath, ["--version"], {
20211
20714
  stdio: ["ignore", "pipe", "pipe"],
20212
20715
  windowsHide: true,
20213
20716
  env: {
@@ -20244,7 +20747,7 @@ var init_codex_binary = __esm({
20244
20747
  reject(new Error("Codex version probe failed."));
20245
20748
  return;
20246
20749
  }
20247
- resolve5(stdout.trim());
20750
+ resolve6(stdout.trim());
20248
20751
  });
20249
20752
  });
20250
20753
  verifyPinnedCodexBinary = async (candidatePath, dependencies = {}) => {
@@ -20304,7 +20807,7 @@ import {
20304
20807
  } from "node:fs/promises";
20305
20808
  import { randomBytes as randomBytes3 } from "node:crypto";
20306
20809
  import { tmpdir as tmpdir2 } from "node:os";
20307
- 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";
20308
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;
20309
20812
  var init_codex_adapter = __esm({
20310
20813
  "lib/inference-host/codex-adapter.ts"() {
@@ -20370,6 +20873,7 @@ var init_codex_adapter = __esm({
20370
20873
  processToken: external_exports.string().regex(/^[0-9a-f]{64}$/u).nullable(),
20371
20874
  processReceiptPath: external_exports.string().min(1).max(4096).nullable(),
20372
20875
  processState: external_exports.enum(["unmanaged", "spawn_intent", "running", "terminated"]),
20876
+ bootIdentity: external_exports.string().min(1).max(4096).nullable().optional(),
20373
20877
  cleanupConfirmed: external_exports.boolean(),
20374
20878
  adapterRequestId: external_exports.string().min(1).max(512).nullable(),
20375
20879
  adapterResponseId: external_exports.string().min(1).max(512).nullable(),
@@ -20613,7 +21117,7 @@ var init_codex_adapter = __esm({
20613
21117
  isStrictDescendant = (candidate, parent) => {
20614
21118
  const parentPath = resolve3(parent);
20615
21119
  const candidatePath = resolve3(candidate);
20616
- const scoped = relative(parentPath, candidatePath);
21120
+ const scoped = relative2(parentPath, candidatePath);
20617
21121
  return scoped.length > 0 && !scoped.startsWith(`..${sep2}`) && scoped !== ".." && !isAbsolute2(scoped);
20618
21122
  };
20619
21123
  assertRecoveryResourceScope = (checkpoint) => {
@@ -20623,7 +21127,7 @@ var init_codex_adapter = __esm({
20623
21127
  if (!isStrictDescendant(resourceRoot, temporaryRoot)) {
20624
21128
  throw new Error("Codex recovery resource root crossed temporary scope.");
20625
21129
  }
20626
- const parts = relative(temporaryRoot, resourceRoot).split(sep2);
21130
+ const parts = relative2(temporaryRoot, resourceRoot).split(sep2);
20627
21131
  const standalone = parts.length === 1 && parts[0].startsWith("vtx-codex-attempt-") && workspacePath === join4(resourceRoot, "workspace");
20628
21132
  const hosted = parts.length === 3 && parts[0].startsWith("vtx-codex-host-") && parts[1] === "workspaces" && parts[2].startsWith("attempt-") && workspacePath === resourceRoot;
20629
21133
  if (!standalone && !hosted) {
@@ -20747,6 +21251,7 @@ var init_codex_adapter = __esm({
20747
21251
  };
20748
21252
  reconcileCodexAttemptRecovery = async (options) => {
20749
21253
  const attempts = await options.recoveryHooks.loadAll?.() ?? {};
21254
+ const currentBootIdentity = options.bootIdentity ?? await readInferenceSystemBootIdentity();
20750
21255
  let reconciled = 0;
20751
21256
  for (const checkpoint of Object.values(attempts)) {
20752
21257
  if (checkpoint.cleanupConfirmed) continue;
@@ -20757,18 +21262,29 @@ var init_codex_adapter = __esm({
20757
21262
  processToken: checkpoint.processToken,
20758
21263
  receiptPath: checkpoint.processReceiptPath
20759
21264
  };
20760
- await confirmGuardianTerminatedForRecovery(
20761
- checkpoint,
20762
- guardian,
20763
- options.deadlineMs ?? 5e3
21265
+ const crossedBoot = Boolean(
21266
+ checkpoint.bootIdentity && checkpoint.bootIdentity !== currentBootIdentity
20764
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
+ }
20765
21280
  assertRecoveryResourceScope(checkpoint);
20766
21281
  await removeRecoveredThread(checkpoint, options.codexHome);
20767
21282
  await rm3(checkpoint.resourceRoot, { recursive: true, force: true });
20768
21283
  await options.recoveryHooks.save({
20769
21284
  ...checkpoint,
20770
21285
  processState: "terminated",
20771
- cleanupConfirmed: true
21286
+ cleanupConfirmed: true,
21287
+ dispatchOutcome: crossedBoot ? "outcome_unknown" : checkpoint.dispatchOutcome
20772
21288
  });
20773
21289
  reconciled += 1;
20774
21290
  }
@@ -20906,6 +21422,7 @@ var init_codex_adapter = __esm({
20906
21422
  await access2(resources.codexHome, fsConstants2.R_OK | fsConstants2.W_OK);
20907
21423
  await access2(resources.workspacePath, fsConstants2.R_OK);
20908
21424
  const guardianManaged = Boolean(recoveryHooks && !this.dependencies.spawnProcess);
21425
+ const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
20909
21426
  const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
20910
21427
  const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
20911
21428
  if (guardianReceiptRoot) {
@@ -20924,6 +21441,7 @@ var init_codex_adapter = __esm({
20924
21441
  processToken,
20925
21442
  processReceiptPath,
20926
21443
  processState: guardianManaged ? "spawn_intent" : "unmanaged",
21444
+ bootIdentity,
20927
21445
  cleanupConfirmed: false,
20928
21446
  adapterRequestId: null,
20929
21447
  adapterResponseId: null,
@@ -20938,7 +21456,7 @@ var init_codex_adapter = __esm({
20938
21456
  deadlineAtMs: input.deadlineAtMs,
20939
21457
  signal: input.signal,
20940
21458
  spawnProcess: this.dependencies.spawnProcess,
20941
- guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath } : void 0
21459
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
20942
21460
  });
20943
21461
  if (guardianManaged) {
20944
21462
  baseCheckpoint = { ...baseCheckpoint, processState: "running" };
@@ -22137,51 +22655,51 @@ var require_uri_all = __commonJS({
22137
22655
  }
22138
22656
  return uriTokens.join("");
22139
22657
  }
22140
- function resolveComponents(base2, relative2) {
22658
+ function resolveComponents(base2, relative3) {
22141
22659
  var options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
22142
22660
  var skipNormalization = arguments[3];
22143
22661
  var target = {};
22144
22662
  if (!skipNormalization) {
22145
22663
  base2 = parse3(serialize(base2, options), options);
22146
- relative2 = parse3(serialize(relative2, options), options);
22664
+ relative3 = parse3(serialize(relative3, options), options);
22147
22665
  }
22148
22666
  options = options || {};
22149
- if (!options.tolerant && relative2.scheme) {
22150
- target.scheme = relative2.scheme;
22151
- target.userinfo = relative2.userinfo;
22152
- target.host = relative2.host;
22153
- target.port = relative2.port;
22154
- target.path = removeDotSegments(relative2.path || "");
22155
- 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;
22156
22674
  } else {
22157
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
22158
- target.userinfo = relative2.userinfo;
22159
- target.host = relative2.host;
22160
- target.port = relative2.port;
22161
- target.path = removeDotSegments(relative2.path || "");
22162
- 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;
22163
22681
  } else {
22164
- if (!relative2.path) {
22682
+ if (!relative3.path) {
22165
22683
  target.path = base2.path;
22166
- if (relative2.query !== void 0) {
22167
- target.query = relative2.query;
22684
+ if (relative3.query !== void 0) {
22685
+ target.query = relative3.query;
22168
22686
  } else {
22169
22687
  target.query = base2.query;
22170
22688
  }
22171
22689
  } else {
22172
- if (relative2.path.charAt(0) === "/") {
22173
- target.path = removeDotSegments(relative2.path);
22690
+ if (relative3.path.charAt(0) === "/") {
22691
+ target.path = removeDotSegments(relative3.path);
22174
22692
  } else {
22175
22693
  if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) {
22176
- target.path = "/" + relative2.path;
22694
+ target.path = "/" + relative3.path;
22177
22695
  } else if (!base2.path) {
22178
- target.path = relative2.path;
22696
+ target.path = relative3.path;
22179
22697
  } else {
22180
- 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;
22181
22699
  }
22182
22700
  target.path = removeDotSegments(target.path);
22183
22701
  }
22184
- target.query = relative2.query;
22702
+ target.query = relative3.query;
22185
22703
  }
22186
22704
  target.userinfo = base2.userinfo;
22187
22705
  target.host = base2.host;
@@ -22189,10 +22707,10 @@ var require_uri_all = __commonJS({
22189
22707
  }
22190
22708
  target.scheme = base2.scheme;
22191
22709
  }
22192
- target.fragment = relative2.fragment;
22710
+ target.fragment = relative3.fragment;
22193
22711
  return target;
22194
22712
  }
22195
- function resolve5(baseURI, relativeURI, options) {
22713
+ function resolve6(baseURI, relativeURI, options) {
22196
22714
  var schemelessOptions = assign({ scheme: "null" }, options);
22197
22715
  return serialize(resolveComponents(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true), schemelessOptions);
22198
22716
  }
@@ -22457,7 +22975,7 @@ var require_uri_all = __commonJS({
22457
22975
  exports2.removeDotSegments = removeDotSegments;
22458
22976
  exports2.serialize = serialize;
22459
22977
  exports2.resolveComponents = resolveComponents;
22460
- exports2.resolve = resolve5;
22978
+ exports2.resolve = resolve6;
22461
22979
  exports2.normalize = normalize;
22462
22980
  exports2.equal = equal;
22463
22981
  exports2.escapeComponent = escapeComponent;
@@ -22810,18 +23328,18 @@ var require_resolve = __commonJS({
22810
23328
  var util = require_util();
22811
23329
  var SchemaObject = require_schema_obj();
22812
23330
  var traverse = require_json_schema_traverse();
22813
- module.exports = resolve5;
22814
- resolve5.normalizeId = normalizeId;
22815
- resolve5.fullPath = getFullPath;
22816
- resolve5.url = resolveUrl;
22817
- resolve5.ids = resolveIds;
22818
- resolve5.inlineRef = inlineRef;
22819
- resolve5.schema = resolveSchema;
22820
- 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) {
22821
23339
  var refVal = this._refs[ref];
22822
23340
  if (typeof refVal == "string") {
22823
23341
  if (this._refs[refVal]) refVal = this._refs[refVal];
22824
- else return resolve5.call(this, compile, root, refVal);
23342
+ else return resolve6.call(this, compile, root, refVal);
22825
23343
  }
22826
23344
  refVal = refVal || this._schemas[ref];
22827
23345
  if (refVal instanceof SchemaObject) {
@@ -23026,7 +23544,7 @@ var require_resolve = __commonJS({
23026
23544
  var require_error_classes = __commonJS({
23027
23545
  "node_modules/ajv/lib/compile/error_classes.js"(exports, module) {
23028
23546
  "use strict";
23029
- var resolve5 = require_resolve();
23547
+ var resolve6 = require_resolve();
23030
23548
  module.exports = {
23031
23549
  Validation: errorSubclass(ValidationError),
23032
23550
  MissingRef: errorSubclass(MissingRefError)
@@ -23041,8 +23559,8 @@ var require_error_classes = __commonJS({
23041
23559
  };
23042
23560
  function MissingRefError(baseId, ref, message) {
23043
23561
  this.message = message || MissingRefError.message(baseId, ref);
23044
- this.missingRef = resolve5.url(baseId, ref);
23045
- this.missingSchema = resolve5.normalizeId(resolve5.fullPath(this.missingRef));
23562
+ this.missingRef = resolve6.url(baseId, ref);
23563
+ this.missingSchema = resolve6.normalizeId(resolve6.fullPath(this.missingRef));
23046
23564
  }
23047
23565
  function errorSubclass(Subclass) {
23048
23566
  Subclass.prototype = Object.create(Error.prototype);
@@ -23570,7 +24088,7 @@ var require_validate = __commonJS({
23570
24088
  var require_compile = __commonJS({
23571
24089
  "node_modules/ajv/lib/compile/index.js"(exports, module) {
23572
24090
  "use strict";
23573
- var resolve5 = require_resolve();
24091
+ var resolve6 = require_resolve();
23574
24092
  var util = require_util();
23575
24093
  var errorClasses = require_error_classes();
23576
24094
  var stableStringify = require_fast_json_stable_stringify();
@@ -23632,7 +24150,7 @@ var require_compile = __commonJS({
23632
24150
  RULES,
23633
24151
  validate: validateGenerator,
23634
24152
  util,
23635
- resolve: resolve5,
24153
+ resolve: resolve6,
23636
24154
  resolveRef: resolveRef2,
23637
24155
  usePattern,
23638
24156
  useDefault,
@@ -23694,7 +24212,7 @@ var require_compile = __commonJS({
23694
24212
  return validate;
23695
24213
  }
23696
24214
  function resolveRef2(baseId2, ref, isRoot) {
23697
- ref = resolve5.url(baseId2, ref);
24215
+ ref = resolve6.url(baseId2, ref);
23698
24216
  var refIndex = refs[ref];
23699
24217
  var _refVal, refCode;
23700
24218
  if (refIndex !== void 0) {
@@ -23711,11 +24229,11 @@ var require_compile = __commonJS({
23711
24229
  }
23712
24230
  }
23713
24231
  refCode = addLocalRef(ref);
23714
- var v2 = resolve5.call(self2, localCompile, root, ref);
24232
+ var v2 = resolve6.call(self2, localCompile, root, ref);
23715
24233
  if (v2 === void 0) {
23716
24234
  var localSchema = localRefs && localRefs[ref];
23717
24235
  if (localSchema) {
23718
- 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);
23719
24237
  }
23720
24238
  }
23721
24239
  if (v2 === void 0) {
@@ -27332,7 +27850,7 @@ var require_ajv = __commonJS({
27332
27850
  "node_modules/ajv/lib/ajv.js"(exports, module) {
27333
27851
  "use strict";
27334
27852
  var compileSchema = require_compile();
27335
- var resolve5 = require_resolve();
27853
+ var resolve6 = require_resolve();
27336
27854
  var Cache = require_cache();
27337
27855
  var SchemaObject = require_schema_obj();
27338
27856
  var stableStringify = require_fast_json_stable_stringify();
@@ -27414,7 +27932,7 @@ var require_ajv = __commonJS({
27414
27932
  var id2 = this._getId(schema);
27415
27933
  if (id2 !== void 0 && typeof id2 != "string")
27416
27934
  throw new Error("schema id must be string");
27417
- key = resolve5.normalizeId(key || id2);
27935
+ key = resolve6.normalizeId(key || id2);
27418
27936
  checkUnique(this, key);
27419
27937
  this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
27420
27938
  return this;
@@ -27458,7 +27976,7 @@ var require_ajv = __commonJS({
27458
27976
  }
27459
27977
  }
27460
27978
  function _getSchemaFragment(self2, ref) {
27461
- var res = resolve5.schema.call(self2, { schema: {} }, ref);
27979
+ var res = resolve6.schema.call(self2, { schema: {} }, ref);
27462
27980
  if (res) {
27463
27981
  var schema = res.schema, root = res.root, baseId = res.baseId;
27464
27982
  var v = compileSchema.call(self2, schema, root, void 0, baseId);
@@ -27474,7 +27992,7 @@ var require_ajv = __commonJS({
27474
27992
  }
27475
27993
  }
27476
27994
  function _getSchemaObj(self2, keyRef) {
27477
- keyRef = resolve5.normalizeId(keyRef);
27995
+ keyRef = resolve6.normalizeId(keyRef);
27478
27996
  return self2._schemas[keyRef] || self2._refs[keyRef] || self2._fragments[keyRef];
27479
27997
  }
27480
27998
  function removeSchema(schemaKeyRef) {
@@ -27501,7 +28019,7 @@ var require_ajv = __commonJS({
27501
28019
  this._cache.del(cacheKey);
27502
28020
  var id2 = this._getId(schemaKeyRef);
27503
28021
  if (id2) {
27504
- id2 = resolve5.normalizeId(id2);
28022
+ id2 = resolve6.normalizeId(id2);
27505
28023
  delete this._schemas[id2];
27506
28024
  delete this._refs[id2];
27507
28025
  }
@@ -27525,13 +28043,13 @@ var require_ajv = __commonJS({
27525
28043
  var cached2 = this._cache.get(cacheKey);
27526
28044
  if (cached2) return cached2;
27527
28045
  shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false;
27528
- var id2 = resolve5.normalizeId(this._getId(schema));
28046
+ var id2 = resolve6.normalizeId(this._getId(schema));
27529
28047
  if (id2 && shouldAddSchema) checkUnique(this, id2);
27530
28048
  var willValidate = this._opts.validateSchema !== false && !skipValidation;
27531
28049
  var recursiveMeta;
27532
- if (willValidate && !(recursiveMeta = id2 && id2 == resolve5.normalizeId(schema.$schema)))
28050
+ if (willValidate && !(recursiveMeta = id2 && id2 == resolve6.normalizeId(schema.$schema)))
27533
28051
  this.validateSchema(schema, true);
27534
- var localRefs = resolve5.ids.call(this, schema);
28052
+ var localRefs = resolve6.ids.call(this, schema);
27535
28053
  var schemaObj = new SchemaObject({
27536
28054
  id: id2,
27537
28055
  schema,
@@ -27797,13 +28315,13 @@ var init_runner = __esm({
27797
28315
  };
27798
28316
  defaultSleep = async (milliseconds, signal) => {
27799
28317
  if (signal?.aborted) throw abortError();
27800
- await new Promise((resolve5, reject) => {
28318
+ await new Promise((resolve6, reject) => {
27801
28319
  const finish = (callback) => {
27802
28320
  signal?.removeEventListener("abort", onAbort);
27803
28321
  callback();
27804
28322
  };
27805
28323
  const timer = setTimeout(
27806
- () => finish(resolve5),
28324
+ () => finish(resolve6),
27807
28325
  Math.max(MIN_SLEEP_MS, milliseconds)
27808
28326
  );
27809
28327
  const onAbort = () => {
@@ -28286,8 +28804,8 @@ var init_runner = __esm({
28286
28804
  let failed = 0;
28287
28805
  let acceptAttemptUpdates = true;
28288
28806
  let wakeDrain;
28289
- const drainSignal = new Promise((resolve5) => {
28290
- wakeDrain = resolve5;
28807
+ const drainSignal = new Promise((resolve6) => {
28808
+ wakeDrain = resolve6;
28291
28809
  });
28292
28810
  const requestDrain = (reason) => {
28293
28811
  const priority = (value) => {
@@ -28336,7 +28854,7 @@ var init_runner = __esm({
28336
28854
  const metadata = await this.dependencies.discoverOAuth({
28337
28855
  signal: this.options.signal
28338
28856
  });
28339
- let access3 = null;
28857
+ let access4 = null;
28340
28858
  let accessExpiresAt = 0;
28341
28859
  let refreshPromise = null;
28342
28860
  const refreshAccessToken = async (requestOptions = {}) => {
@@ -28349,7 +28867,7 @@ var init_runner = __esm({
28349
28867
  );
28350
28868
  assertLocalCredentialIdentity(localState, refreshed.credential);
28351
28869
  credential = refreshed.credential;
28352
- access3 = refreshed;
28870
+ access4 = refreshed;
28353
28871
  accessExpiresAt = now() + refreshed.expiresIn * 1e3;
28354
28872
  return refreshed.accessToken;
28355
28873
  })();
@@ -28362,10 +28880,10 @@ var init_runner = __esm({
28362
28880
  await refreshAccessToken({ signal: this.options.signal });
28363
28881
  const tokenSource = {
28364
28882
  accessToken: async (requestOptions) => {
28365
- if (!access3 || accessExpiresAt - now() <= 5e3) {
28883
+ if (!access4 || accessExpiresAt - now() <= 5e3) {
28366
28884
  return refreshAccessToken(requestOptions);
28367
28885
  }
28368
- return access3.accessToken;
28886
+ return access4.accessToken;
28369
28887
  },
28370
28888
  refreshAccessToken
28371
28889
  };
@@ -29301,6 +29819,653 @@ var init_runner = __esm({
29301
29819
  }
29302
29820
  });
29303
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, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, 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
+ windowsOwnedCommandLine = (values) => {
29974
+ if (values.some((value) => value.includes('"') || value.includes("\0"))) {
29975
+ throw new Error("Windows inference-host service paths contain unsupported characters.");
29976
+ }
29977
+ return values.map((value) => `"${value}"`).join(" ");
29978
+ };
29979
+ vbScriptString = (value) => `"${value.replaceAll('"', '""')}"`;
29980
+ windowsServiceLauncher = (executable, args) => `Option Explicit\r
29981
+ Dim shell, exitCode\r
29982
+ Set shell = CreateObject("WScript.Shell")\r
29983
+ exitCode = shell.Run(${vbScriptString(windowsOwnedCommandLine([executable, ...args]))}, 0, True)\r
29984
+ WScript.Quit exitCode\r
29985
+ `;
29986
+ windowsTaskXml = (launcherPath, windowsDirectory, username) => `<?xml version="1.0" encoding="UTF-16"?>
29987
+ <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
29988
+ <RegistrationInfo><Description>${xmlEscape(SERVICE_NAME)}</Description></RegistrationInfo>
29989
+ <Triggers>
29990
+ <LogonTrigger><Enabled>true</Enabled><UserId>${xmlEscape(username)}</UserId></LogonTrigger>
29991
+ <TimeTrigger>
29992
+ <StartBoundary>2020-01-01T00:00:00</StartBoundary>
29993
+ <Enabled>true</Enabled>
29994
+ <Repetition><Interval>PT1M</Interval><StopAtDurationEnd>false</StopAtDurationEnd></Repetition>
29995
+ </TimeTrigger>
29996
+ </Triggers>
29997
+ <Principals><Principal id="Author"><UserId>${xmlEscape(username)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
29998
+ <Settings>
29999
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
30000
+ <Hidden>true</Hidden>
30001
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
30002
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
30003
+ <StartWhenAvailable>true</StartWhenAvailable>
30004
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
30005
+ <RestartOnFailure><Interval>PT1M</Interval><Count>255</Count></RestartOnFailure>
30006
+ </Settings>
30007
+ <Actions Context="Author"><Exec><Command>${xmlEscape(`${windowsDirectory}\\System32\\wscript.exe`)}</Command><Arguments>${xmlEscape(`//B //NoLogo "${launcherPath}"`)}</Arguments></Exec></Actions>
30008
+ </Task>
30009
+ `;
30010
+ systemdUnit = (executable, args) => `[Unit]
30011
+ Description=${SERVICE_NAME}
30012
+ After=network-online.target
30013
+ Wants=network-online.target
30014
+ StartLimitIntervalSec=0
30015
+
30016
+ [Service]
30017
+ Type=simple
30018
+ ExecStart=${[executable, ...args].map(systemdQuote).join(" ")}
30019
+ Restart=on-failure
30020
+ RestartSec=5s
30021
+ TimeoutStopSec=${SERVICE_COOPERATIVE_STOP_SECONDS}s
30022
+
30023
+ [Install]
30024
+ WantedBy=default.target
30025
+ `;
30026
+ launchAgentPlist = (executable, args, logPath) => `<?xml version="1.0" encoding="UTF-8"?>
30027
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
30028
+ <plist version="1.0"><dict>
30029
+ <key>Label</key><string>${LAUNCHD_LABEL}</string>
30030
+ <key>ProgramArguments</key><array>${[executable, ...args].map((arg) => `<string>${plistEscape(arg)}</string>`).join("")}</array>
30031
+ <key>RunAtLoad</key><true/>
30032
+ <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
30033
+ <key>ProcessType</key><string>Background</string>
30034
+ <key>ThrottleInterval</key><integer>5</integer>
30035
+ <key>ExitTimeOut</key><integer>${SERVICE_COOPERATIVE_STOP_SECONDS}</integer>
30036
+ <key>StandardOutPath</key><string>${plistEscape(logPath)}</string>
30037
+ <key>StandardErrorPath</key><string>${plistEscape(logPath)}</string>
30038
+ </dict></plist>
30039
+ `;
30040
+ InferenceHostServiceManager = class {
30041
+ constructor(config2, dependencies = {}) {
30042
+ this.config = config2;
30043
+ this.platform = dependencies.platform ?? process.platform;
30044
+ this.home = dependencies.homedir ?? homedir2();
30045
+ this.executable = resolve4(dependencies.executable ?? process.execPath);
30046
+ this.script = resolve4(dependencies.script ?? process.argv[1] ?? "");
30047
+ this.username = dependencies.username ?? ([process.env.USERDOMAIN, process.env.USERNAME].filter(Boolean).join("\\") || process.env.USER || "");
30048
+ this.windowsDirectory = (dependencies.windowsDirectory ?? process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows").replace(/[\\/]+$/u, "");
30049
+ this.runCommand = dependencies.runCommand ?? defaultRunCommand;
30050
+ this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
30051
+ this.sleep = dependencies.sleep ?? (async (milliseconds) => {
30052
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30053
+ });
30054
+ this.stopWaitAttempts = dependencies.stopWaitAttempts ?? SERVICE_COOPERATIVE_STOP_SECONDS * 4;
30055
+ this.startWaitAttempts = dependencies.startWaitAttempts ?? 40;
30056
+ this.acquireProcessLock = dependencies.acquireProcessLock ?? acquireInferenceHostProcessLock;
30057
+ managerName(this.platform);
30058
+ if (dependencies.platform === void 0 && this.platform === "linux" && isWindowsSubsystemForLinux()) {
30059
+ throw new Error(
30060
+ "Windows-login auto-start requires the native Windows VTX CLI. A WSL-only service cannot start the WSL virtual machine at login."
30061
+ );
30062
+ }
30063
+ }
30064
+ manifestPath() {
30065
+ return inferenceHostServiceManifestPath(this.config);
30066
+ }
30067
+ desiredPath() {
30068
+ return inferenceHostServiceDesiredPath(this.config);
30069
+ }
30070
+ logPath() {
30071
+ return inferenceHostServiceLogPath(this.config);
30072
+ }
30073
+ controlLockPath() {
30074
+ return `${this.config.processLockPath}.service-control`;
30075
+ }
30076
+ async withControlLock(operation) {
30077
+ let lock2 = null;
30078
+ for (let attempt = 0; attempt < 400; attempt += 1) {
30079
+ try {
30080
+ lock2 = await this.acquireProcessLock(this.controlLockPath());
30081
+ break;
30082
+ } catch (error48) {
30083
+ if (!/already owns/iu.test(error48 instanceof Error ? error48.message : "")) throw error48;
30084
+ await this.sleep(250);
30085
+ }
30086
+ }
30087
+ if (!lock2) {
30088
+ throw new Error("Another inference-host service control operation did not finish within 100 seconds.");
30089
+ }
30090
+ try {
30091
+ return await operation();
30092
+ } finally {
30093
+ await lock2.release();
30094
+ }
30095
+ }
30096
+ windowsTaskName() {
30097
+ const identity = this.username.replaceAll(/[^A-Za-z0-9_.@-]+/gu, "_").slice(-80) || "current-user";
30098
+ return `VTX Macro Inference Host (${identity})`;
30099
+ }
30100
+ windowsPowerShell(script) {
30101
+ return [
30102
+ "-NoLogo",
30103
+ "-NoProfile",
30104
+ "-NonInteractive",
30105
+ "-EncodedCommand",
30106
+ Buffer.from(script, "utf16le").toString("base64")
30107
+ ];
30108
+ }
30109
+ definitionPath() {
30110
+ if (this.platform === "win32") return `${this.config.statePath}.service-task.xml`;
30111
+ if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
30112
+ return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
30113
+ }
30114
+ windowsLauncherPath() {
30115
+ return `${this.config.statePath}.service-launcher.vbs`;
30116
+ }
30117
+ async managerCommand(action) {
30118
+ const definition = this.definitionPath();
30119
+ if (this.platform === "win32") {
30120
+ const taskName = this.windowsTaskName();
30121
+ if (action === "install") return await this.runCommand("schtasks.exe", ["/Create", "/F", "/TN", taskName, "/XML", definition]);
30122
+ const quotedName = taskName.replaceAll("'", "''");
30123
+ if (action === "start") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30124
+ `$ErrorActionPreference='Stop'; $task=Get-ScheduledTask -TaskName '${quotedName}' -ErrorAction Stop; Enable-ScheduledTask -InputObject $task -ErrorAction Stop | Out-Null; Start-ScheduledTask -TaskName '${quotedName}'`
30125
+ ));
30126
+ if (action === "stop") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30127
+ `$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 }`
30128
+ ));
30129
+ if (action === "status") return await this.runCommand("powershell.exe", this.windowsPowerShell(
30130
+ `$ErrorActionPreference='Stop'; [Console]::Out.Write([int](Get-ScheduledTask -TaskName '${quotedName}').State)`
30131
+ ));
30132
+ return await this.runCommand("powershell.exe", this.windowsPowerShell(
30133
+ `$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' }`
30134
+ ));
30135
+ }
30136
+ if (this.platform === "darwin") {
30137
+ const domain2 = `gui/${typeof process.getuid === "function" ? process.getuid() : 0}`;
30138
+ if (action === "install") return { exitCode: 0, stdout: "definition-written", stderr: "" };
30139
+ if (action === "start") return await this.runCommand("launchctl", ["bootstrap", domain2, definition]);
30140
+ if (action === "stop") return await this.runCommand("launchctl", ["bootout", domain2, definition]);
30141
+ if (action === "status") return await this.runCommand("launchctl", ["print", `${domain2}/${LAUNCHD_LABEL}`]);
30142
+ return await this.runCommand("launchctl", ["bootout", domain2, definition]);
30143
+ }
30144
+ if (action === "install") return await this.runCommand("systemctl", ["--user", "enable", SYSTEMD_UNIT]);
30145
+ if (action === "start") return await this.runCommand("systemctl", ["--user", "start", SYSTEMD_UNIT]);
30146
+ if (action === "stop") return await this.runCommand("systemctl", ["--user", "stop", SYSTEMD_UNIT]);
30147
+ if (action === "status") return await this.runCommand("systemctl", ["--user", "show", SYSTEMD_UNIT, "--property=ActiveState,SubState,UnitFileState", "--no-pager"]);
30148
+ return await this.runCommand("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
30149
+ }
30150
+ async waitForManagerActive() {
30151
+ for (let attempt = 0; attempt < this.startWaitAttempts; attempt += 1) {
30152
+ const status = await this.status();
30153
+ if (status.manager_active) return status;
30154
+ await this.sleep(250);
30155
+ }
30156
+ throw new Error("Background service did not reach an active state within 10 seconds.");
30157
+ }
30158
+ async install(options) {
30159
+ return await this.withControlLock(async () => await this.installUnlocked(options));
30160
+ }
30161
+ async installUnlocked(options) {
30162
+ if (await readInferenceHostServiceManifest(this.manifestPath())) {
30163
+ await this.uninstallUnlocked();
30164
+ }
30165
+ await access3(this.executable);
30166
+ await access3(this.script);
30167
+ const manifest = assertManifest({
30168
+ schema_version: "vtx_inference_service_v1",
30169
+ installed_at: this.now().toISOString(),
30170
+ adapter: options.adapter,
30171
+ executable: this.executable,
30172
+ script: this.script,
30173
+ display_name: options.displayName,
30174
+ log_path: this.logPath(),
30175
+ runtime_environment: runtimeEnvironment(this.config)
30176
+ });
30177
+ const args = serviceArguments(this.script, this.manifestPath());
30178
+ const definition = this.platform === "win32" ? windowsTaskXml(this.windowsLauncherPath(), this.windowsDirectory, this.username) : this.platform === "darwin" ? launchAgentPlist(this.executable, args, this.logPath()) : systemdUnit(this.executable, args);
30179
+ let managerInstallAttempted = false;
30180
+ try {
30181
+ await writeAtomicInferencePrivateFile(this.manifestPath(), `${JSON.stringify(manifest, null, 2)}
30182
+ `);
30183
+ await writeDesired(this.desiredPath(), options.startImmediately !== false, this.now());
30184
+ await mkdir3(dirname4(this.definitionPath()), { recursive: true, mode: 448 });
30185
+ if (this.platform === "win32") {
30186
+ await writeAtomicInferencePrivateFile(
30187
+ this.windowsLauncherPath(),
30188
+ windowsServiceLauncher(this.executable, args)
30189
+ );
30190
+ await writeFile2(this.definitionPath(), `\uFEFF${definition}`, {
30191
+ encoding: "utf16le",
30192
+ mode: 384
30193
+ });
30194
+ } else {
30195
+ await writeFile2(this.definitionPath(), definition, { encoding: "utf8", mode: 384 });
30196
+ await chmod3(this.definitionPath(), 384);
30197
+ }
30198
+ if (this.platform === "linux") {
30199
+ const reload = await this.runCommand("systemctl", ["--user", "daemon-reload"]);
30200
+ if (reload.exitCode !== 0) throw new Error(`systemd user daemon reload failed: ${reload.stderr.trim()}`);
30201
+ }
30202
+ managerInstallAttempted = true;
30203
+ const installed = await this.managerCommand("install");
30204
+ if (installed.exitCode !== 0) throw new Error(`Background service installation failed: ${installed.stderr.trim()}`);
30205
+ if (options.startImmediately !== false) {
30206
+ const started = await this.managerCommand("start");
30207
+ if (started.exitCode !== 0 && !/already running|in progress/iu.test(`${started.stdout}
30208
+ ${started.stderr}`)) {
30209
+ throw new Error(`Background service start failed: ${started.stderr.trim()}`);
30210
+ }
30211
+ await this.waitForManagerActive();
30212
+ }
30213
+ } catch (error48) {
30214
+ await writeDesired(this.desiredPath(), false, this.now()).catch(() => void 0);
30215
+ if (managerInstallAttempted) {
30216
+ const cleanup = await this.managerCommand("uninstall").catch((cleanupError) => ({
30217
+ exitCode: 1,
30218
+ stdout: "",
30219
+ stderr: cleanupError instanceof Error ? cleanupError.message : "unknown cleanup error"
30220
+ }));
30221
+ if (cleanup.exitCode !== 0 && !/not found|does not exist|not loaded|no such process|cannot find/iu.test(`${cleanup.stdout}
30222
+ ${cleanup.stderr}`)) {
30223
+ throw new Error(
30224
+ `Background service setup failed and manager cleanup was not confirmed: ${cleanup.stderr.trim()}`,
30225
+ { cause: error48 }
30226
+ );
30227
+ }
30228
+ }
30229
+ await rm4(this.definitionPath(), { force: true }).catch(() => void 0);
30230
+ if (this.platform === "win32") {
30231
+ await rm4(this.windowsLauncherPath(), { force: true }).catch(() => void 0);
30232
+ }
30233
+ await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
30234
+ await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
30235
+ if (this.platform === "linux") {
30236
+ await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
30237
+ }
30238
+ throw error48;
30239
+ }
30240
+ return await this.status();
30241
+ }
30242
+ async start() {
30243
+ return await this.withControlLock(async () => await this.startUnlocked());
30244
+ }
30245
+ async startUnlocked() {
30246
+ if (!await readInferenceHostServiceManifest(this.manifestPath())) {
30247
+ throw new Error("Inference-host service is not installed.");
30248
+ }
30249
+ await writeDesired(this.desiredPath(), true, this.now());
30250
+ const result2 = await this.managerCommand("start");
30251
+ if (result2.exitCode !== 0 && !/already running|in progress/iu.test(`${result2.stdout}
30252
+ ${result2.stderr}`)) {
30253
+ throw new Error(`Background service start failed: ${result2.stderr.trim()}`);
30254
+ }
30255
+ try {
30256
+ return await this.waitForManagerActive();
30257
+ } catch (error48) {
30258
+ await writeDesired(this.desiredPath(), false, this.now());
30259
+ await this.managerCommand("stop").catch(() => void 0);
30260
+ throw error48;
30261
+ }
30262
+ }
30263
+ async stop() {
30264
+ return await this.withControlLock(async () => await this.stopUnlocked());
30265
+ }
30266
+ async stopUnlocked() {
30267
+ if (!await readInferenceHostServiceManifest(this.manifestPath())) {
30268
+ throw new Error("Inference-host service is not installed.");
30269
+ }
30270
+ await writeDesired(this.desiredPath(), false, this.now());
30271
+ const serviceLockPath = `${this.config.processLockPath}.service`;
30272
+ let serviceReleased = false;
30273
+ for (let attempt = 0; attempt < this.stopWaitAttempts; attempt += 1) {
30274
+ try {
30275
+ const probe = await this.acquireProcessLock(serviceLockPath);
30276
+ await probe.release();
30277
+ serviceReleased = true;
30278
+ break;
30279
+ } catch (error48) {
30280
+ if (error48 instanceof Error && error48.message.includes("Another inference host process already owns")) {
30281
+ await this.sleep(250);
30282
+ continue;
30283
+ }
30284
+ throw error48;
30285
+ }
30286
+ }
30287
+ if (!serviceReleased) {
30288
+ throw new Error(
30289
+ `Inference-host worker did not stop cooperatively within ${SERVICE_COOPERATIVE_STOP_SECONDS} seconds; refusing forced termination while cleanup may be pending.`
30290
+ );
30291
+ }
30292
+ const result2 = await this.managerCommand("stop");
30293
+ if (result2.exitCode !== 0 && !/not running|not found|does not exist|not loaded|cannot find|no such process/iu.test(`${result2.stdout}
30294
+ ${result2.stderr}`)) {
30295
+ throw new Error(`Background service stop failed: ${result2.stderr.trim()}`);
30296
+ }
30297
+ const status = await this.status();
30298
+ if (status.manager_active) {
30299
+ throw new Error("Background service manager still reports the service active after stop.");
30300
+ }
30301
+ return status;
30302
+ }
30303
+ async status() {
30304
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30305
+ const desired = await readInferenceHostServiceDesired(this.desiredPath());
30306
+ const result2 = await this.managerCommand("status");
30307
+ const output3 = result2.stdout.trim();
30308
+ 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));
30309
+ return {
30310
+ installed: manifest !== null,
30311
+ desired_running: desired,
30312
+ manager_active: managerActive,
30313
+ manager: managerName(this.platform),
30314
+ manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
30315
+ adapter: manifest?.adapter ?? null,
30316
+ log_path: manifest?.log_path ?? this.logPath()
30317
+ };
30318
+ }
30319
+ async logs(lines = 100) {
30320
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30321
+ const path = manifest?.log_path ?? this.logPath();
30322
+ try {
30323
+ const contents = await readFile5(path, "utf8");
30324
+ return `${contents.trimEnd().split(/\r?\n/u).slice(-lines).join("\n")}
30325
+ `;
30326
+ } catch (error48) {
30327
+ if (error48.code === "ENOENT") return "";
30328
+ throw error48;
30329
+ }
30330
+ }
30331
+ async uninstall() {
30332
+ return await this.withControlLock(async () => await this.uninstallUnlocked());
30333
+ }
30334
+ async uninstallUnlocked() {
30335
+ const manifest = await readInferenceHostServiceManifest(this.manifestPath());
30336
+ if (!manifest) throw new Error("Inference-host service is not installed.");
30337
+ const current = await this.status();
30338
+ if (current.desired_running || current.manager_active) {
30339
+ await this.stopUnlocked();
30340
+ } else {
30341
+ await writeDesired(this.desiredPath(), false, this.now());
30342
+ }
30343
+ const result2 = await this.managerCommand("uninstall");
30344
+ if (result2.exitCode !== 0 && !/not found|does not exist|not loaded|no such process|cannot find/iu.test(`${result2.stdout}
30345
+ ${result2.stderr}`)) {
30346
+ throw new Error(`Background service uninstall failed: ${result2.stderr.trim()}`);
30347
+ }
30348
+ await rm4(this.definitionPath(), { force: true });
30349
+ if (this.platform === "win32") await rm4(this.windowsLauncherPath(), { force: true });
30350
+ if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
30351
+ await rm4(this.manifestPath(), { force: true });
30352
+ await rm4(this.desiredPath(), { force: true });
30353
+ return {
30354
+ installed: false,
30355
+ desired_running: false,
30356
+ manager_active: false,
30357
+ manager: managerName(this.platform),
30358
+ manager_state: "not-installed",
30359
+ adapter: null,
30360
+ log_path: manifest.log_path
30361
+ };
30362
+ }
30363
+ };
30364
+ appendServiceLog = async (path, event, fields = {}) => {
30365
+ await mkdir3(dirname4(path), { recursive: true, mode: 448 });
30366
+ const stream = createWriteStream(path, { flags: "a", mode: 384 });
30367
+ await new Promise((resolvePromise, reject) => {
30368
+ stream.once("error", reject);
30369
+ stream.end(`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), event, ...fields })}
30370
+ `, resolvePromise);
30371
+ });
30372
+ };
30373
+ spawnServiceChild = async (manifest, signal) => {
30374
+ const args = [
30375
+ manifest.script,
30376
+ "inference-host",
30377
+ "run",
30378
+ "--json",
30379
+ "--display-name",
30380
+ manifest.display_name
30381
+ ];
30382
+ const startedAt = Date.now();
30383
+ const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30384
+ return await new Promise((resolvePromise, reject) => {
30385
+ const child = spawn5(manifest.executable, args, {
30386
+ env: { ...process.env, ...manifest.runtime_environment },
30387
+ windowsHide: true,
30388
+ stdio: ["ignore", log, log]
30389
+ });
30390
+ const onAbort = () => child.kill("SIGTERM");
30391
+ signal.addEventListener("abort", onAbort, { once: true });
30392
+ child.once("error", (error48) => {
30393
+ signal.removeEventListener("abort", onAbort);
30394
+ log.end();
30395
+ reject(error48);
30396
+ });
30397
+ child.once("exit", (code) => {
30398
+ signal.removeEventListener("abort", onAbort);
30399
+ log.end();
30400
+ resolvePromise({ exitCode: code ?? 1, uptimeMs: Date.now() - startedAt });
30401
+ });
30402
+ });
30403
+ };
30404
+ runInferenceHostServiceSupervisor = async (manifestPath, options = {}) => {
30405
+ const manifest = await readInferenceHostServiceManifest(manifestPath);
30406
+ if (!manifest) throw new Error("Inference-host service manifest is missing.");
30407
+ const desiredPath = `${manifest.runtime_environment.VTX_INFERENCE_HOST_STATE_PATH}.service-desired.json`;
30408
+ const signal = options.signal ?? new AbortController().signal;
30409
+ const sleep4 = options.sleep ?? (async (milliseconds) => {
30410
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
30411
+ });
30412
+ const launch = options.runWorker ?? options.spawnChild ?? spawnServiceChild;
30413
+ let failures = 0;
30414
+ await appendServiceLog(manifest.log_path, "service_supervisor_started", { adapter: manifest.adapter });
30415
+ while (!signal.aborted && await readDesiredAcrossAtomicReplacement(desiredPath)) {
30416
+ try {
30417
+ const workerController = new AbortController();
30418
+ const forwardAbort = () => workerController.abort();
30419
+ signal.addEventListener("abort", forwardAbort, { once: true });
30420
+ let workerComplete = false;
30421
+ let monitorError = null;
30422
+ const desiredMonitor = (async () => {
30423
+ while (!workerComplete && !workerController.signal.aborted) {
30424
+ await sleep4(500);
30425
+ if (!await readDesiredAcrossAtomicReplacement(desiredPath)) {
30426
+ workerController.abort();
30427
+ break;
30428
+ }
30429
+ }
30430
+ })().catch((error48) => {
30431
+ monitorError = error48;
30432
+ workerController.abort();
30433
+ });
30434
+ let result2;
30435
+ try {
30436
+ result2 = await launch(manifest, workerController.signal);
30437
+ } finally {
30438
+ workerComplete = true;
30439
+ workerController.abort();
30440
+ signal.removeEventListener("abort", forwardAbort);
30441
+ await desiredMonitor;
30442
+ }
30443
+ if (monitorError) throw monitorError;
30444
+ if (signal.aborted || !await readDesiredAcrossAtomicReplacement(desiredPath)) break;
30445
+ failures = result2.uptimeMs >= 6e4 ? 0 : failures + 1;
30446
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30447
+ await appendServiceLog(manifest.log_path, "worker_exited", {
30448
+ exit_code: result2.exitCode,
30449
+ uptime_ms: result2.uptimeMs,
30450
+ retry_after_ms: retryAfterMs
30451
+ });
30452
+ await sleep4(retryAfterMs);
30453
+ } catch (error48) {
30454
+ if (signal.aborted) break;
30455
+ failures += 1;
30456
+ const retryAfterMs = Math.min(1e3 * 2 ** Math.min(failures, 5), 3e4);
30457
+ await appendServiceLog(manifest.log_path, "worker_launch_failed", {
30458
+ error: error48 instanceof Error ? error48.message : "unknown",
30459
+ retry_after_ms: retryAfterMs
30460
+ });
30461
+ await sleep4(retryAfterMs);
30462
+ }
30463
+ }
30464
+ await appendServiceLog(manifest.log_path, "service_supervisor_stopped");
30465
+ };
30466
+ }
30467
+ });
30468
+
29304
30469
  // lib/inference-host/cli.ts
29305
30470
  var cli_exports = {};
29306
30471
  __export(cli_exports, {
@@ -29308,9 +30473,9 @@ __export(cli_exports, {
29308
30473
  runInferenceHostCli: () => runInferenceHostCli
29309
30474
  });
29310
30475
  import { randomUUID } from "node:crypto";
29311
- import { spawn as spawn4 } from "node:child_process";
29312
- import { lstat as lstat4, realpath as realpath4, rm as rm4 } from "node:fs/promises";
29313
- import { join as join5, resolve as resolve4 } from "node:path";
30476
+ import { spawn as spawn6 } from "node:child_process";
30477
+ import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
30478
+ import { join as join6, resolve as resolve5 } from "node:path";
29314
30479
  async function runInferenceHostCli(argv2, env = process.env, dependencies = {}) {
29315
30480
  const warnings = [];
29316
30481
  try {
@@ -29355,6 +30520,9 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29355
30520
  async () => await agentFail(config2, parsed, dependencies, warnings)
29356
30521
  );
29357
30522
  }
30523
+ if (parsed.command === "service") {
30524
+ return await serviceCommand(config2, parsed, env, dependencies, warnings);
30525
+ }
29358
30526
  if (parsed.command === "status") {
29359
30527
  return await localStatus(config2, parsed, dependencies, warnings);
29360
30528
  }
@@ -29371,7 +30539,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29371
30539
  return await cleanupLogin(config2, parsed, dependencies, warnings, true);
29372
30540
  }
29373
30541
  throw new Error(
29374
- "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]"
30542
+ "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]"
29375
30543
  );
29376
30544
  } catch (error48) {
29377
30545
  return {
@@ -29382,7 +30550,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
29382
30550
  };
29383
30551
  }
29384
30552
  }
29385
- 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;
30553
+ 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;
29386
30554
  var init_cli = __esm({
29387
30555
  "lib/inference-host/cli.ts"() {
29388
30556
  "use strict";
@@ -29399,6 +30567,7 @@ var init_cli = __esm({
29399
30567
  init_oauth();
29400
30568
  init_runner();
29401
30569
  init_mcp_client();
30570
+ init_service();
29402
30571
  INFERENCE_HOST_CLI_VERSION = agent_cli_release_default.package_version;
29403
30572
  runtimeReceiptPath = (config2) => `${config2.statePath}.runtime.json`;
29404
30573
  codexRecoveryPath = (config2) => `${config2.statePath}.codex-recovery.json`;
@@ -29476,6 +30645,7 @@ Commands:
29476
30645
  agent-next Claim the next exact VTX inference request
29477
30646
  agent-complete Submit one completed agent result from stdin
29478
30647
  agent-fail Submit one truthful agent failure from stdin
30648
+ service Install and control the durable background host
29479
30649
  status Inspect local host and credential state
29480
30650
  doctor Verify credentials, Codex, and private runtime state
29481
30651
  logout Remove local VTX host state without revoking the grant
@@ -29489,6 +30659,10 @@ Common options:
29489
30659
  If the OS credential store cannot retain the VTX grant, set
29490
30660
  VTX_INFERENCE_HOST_CREDENTIAL_STORE=file before login to use the supported
29491
30661
  private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
30662
+
30663
+ Durable service:
30664
+ vtx inference-host service install
30665
+ vtx inference-host service <start|stop|status|logs|uninstall>
29492
30666
  `;
29493
30667
  parsePositiveInteger = (raw, label) => {
29494
30668
  const value = Number(raw);
@@ -29511,6 +30685,8 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29511
30685
  let modelLabel = null;
29512
30686
  let reasoningEffort = null;
29513
30687
  let waitSeconds = 50;
30688
+ let lines = 100;
30689
+ let serviceManifestPath = null;
29514
30690
  const positionals = [];
29515
30691
  for (let index = 0; index < argv2.length; index += 1) {
29516
30692
  const argument = argv2[index];
@@ -29537,7 +30713,7 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29537
30713
  index += 1;
29538
30714
  continue;
29539
30715
  }
29540
- if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds"].includes(argument)) {
30716
+ if (["--adapter", "--model", "--model-label", "--effort", "--wait-seconds", "--lines", "--service-manifest"].includes(argument)) {
29541
30717
  const raw = argv2[index + 1];
29542
30718
  if (!raw?.trim()) throw new Error(`${argument} requires a value.`);
29543
30719
  if (argument === "--adapter") adapter = raw.trim();
@@ -29550,6 +30726,13 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29550
30726
  throw new Error("--wait-seconds must be an integer from 0 through 300.");
29551
30727
  }
29552
30728
  }
30729
+ if (argument === "--lines") {
30730
+ lines = Number(raw);
30731
+ if (!Number.isInteger(lines) || lines < 1 || lines > 1e4) {
30732
+ throw new Error("--lines must be an integer from 1 through 10000.");
30733
+ }
30734
+ }
30735
+ if (argument === "--service-manifest") serviceManifestPath = resolve5(raw.trim());
29553
30736
  index += 1;
29554
30737
  continue;
29555
30738
  }
@@ -29561,10 +30744,14 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29561
30744
  if (positionals[0] !== "inference-host") {
29562
30745
  throw new Error("Inference-host command routing is invalid.");
29563
30746
  }
29564
- if (positionals.length > 2) {
30747
+ if (positionals.length > 3) {
29565
30748
  throw new Error("Inference-host commands do not accept positional arguments.");
29566
30749
  }
29567
30750
  const command = positionals[1] || null;
30751
+ const serviceAction = command === "service" ? positionals[2] ?? null : null;
30752
+ if (command !== "service" && positionals.length > 2) {
30753
+ throw new Error("Inference-host commands do not accept positional arguments.");
30754
+ }
29568
30755
  if (command === "agent-connect" && !displayNameExplicit) {
29569
30756
  displayName = "Agent-driven inference host";
29570
30757
  }
@@ -29578,13 +30765,16 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29578
30765
  modelId,
29579
30766
  modelLabel,
29580
30767
  reasoningEffort,
29581
- waitSeconds
30768
+ waitSeconds,
30769
+ lines,
30770
+ serviceAction,
30771
+ serviceManifestPath
29582
30772
  };
29583
30773
  };
29584
30774
  defaultOpenBrowser = (url2) => {
29585
30775
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
29586
30776
  const args = process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
29587
- const child = spawn4(command, args, {
30777
+ const child = spawn6(command, args, {
29588
30778
  detached: true,
29589
30779
  stdio: "ignore",
29590
30780
  windowsHide: true
@@ -29653,11 +30843,11 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29653
30843
  return raw !== null;
29654
30844
  };
29655
30845
  inspectCodexAuthentication = async (config2) => {
29656
- const path = join5(config2.codexHomePath, "auth.json");
30846
+ const path = join6(config2.codexHomePath, "auth.json");
29657
30847
  try {
29658
30848
  const before = await lstat4(path);
29659
30849
  const canonical = await realpath4(path);
29660
- 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()));
30850
+ 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()));
29661
30851
  return { present: true, private: isPrivate };
29662
30852
  } catch (error48) {
29663
30853
  if (error48.code === "ENOENT") {
@@ -29671,7 +30861,7 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29671
30861
  runtimeReceiptPath(config2),
29672
30862
  "Inference host runtime receipt file"
29673
30863
  );
29674
- await rm4(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
30864
+ await rm5(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
29675
30865
  await clearInferencePrivateFile(
29676
30866
  codexRecoveryPath(config2),
29677
30867
  "Codex attempt recovery file"
@@ -29680,6 +30870,13 @@ private-file fallback. See https://vtxmacro.com/insights#subscription-inference.
29680
30870
  await clearInferenceAgentNextState(config2.statePath);
29681
30871
  await clearInferenceHostLocalState(config2.statePath);
29682
30872
  };
30873
+ assertDurableServiceUninstalled = async (config2) => {
30874
+ if (await readInferenceHostServiceManifest(`${config2.statePath}.service.json`)) {
30875
+ throw new Error(
30876
+ "Uninstall the durable inference-host service before logout, revoke, or Codex logout."
30877
+ );
30878
+ }
30879
+ };
29683
30880
  assertLocalRuntimeArtifactsMayBeDiscarded = async (config2) => {
29684
30881
  const receipt = await new FileInferenceHostRuntimeReceiptStore(
29685
30882
  runtimeReceiptPath(config2)
@@ -29778,20 +30975,20 @@ Waiting for approval...
29778
30975
  } catch {
29779
30976
  }
29780
30977
  }
29781
- let access3;
30978
+ let access4;
29782
30979
  try {
29783
- access3 = await pending.completion;
30980
+ access4 = await pending.completion;
29784
30981
  } catch (error48) {
29785
30982
  await pending.cancel().catch(() => void 0);
29786
30983
  throw error48;
29787
30984
  }
29788
30985
  const state = {
29789
30986
  schema_version: 1,
29790
- issuer: access3.credential.issuer,
29791
- client_id: access3.credential.client_id,
29792
- host_id: access3.credential.host_id,
30987
+ issuer: access4.credential.issuer,
30988
+ client_id: access4.credential.client_id,
30989
+ host_id: access4.credential.host_id,
29793
30990
  host_generation: 1,
29794
- key_generation: access3.credential.key_generation
30991
+ key_generation: access4.credential.key_generation
29795
30992
  };
29796
30993
  try {
29797
30994
  await writeInferenceHostLocalState(config2.statePath, state);
@@ -29801,7 +30998,7 @@ Waiting for approval...
29801
30998
  const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
29802
30999
  await (dependencies.revokeCredential ?? revokeInferenceCredential)({
29803
31000
  metadata,
29804
- credential: access3.credential,
31001
+ credential: access4.credential,
29805
31002
  store
29806
31003
  });
29807
31004
  remoteCleanupConfirmed = true;
@@ -29812,8 +31009,8 @@ Waiting for approval...
29812
31009
  { cause: stateError }
29813
31010
  );
29814
31011
  }
29815
- await store.writeRecovery(access3.accountKey, access3.credential);
29816
- await store.remove(access3.accountKey).catch(() => void 0);
31012
+ await store.writeRecovery(access4.accountKey, access4.credential);
31013
+ await store.remove(access4.accountKey).catch(() => void 0);
29817
31014
  }
29818
31015
  if (!remoteCleanupConfirmed) {
29819
31016
  throw new Error(
@@ -29892,6 +31089,7 @@ Waiting for approval...
29892
31089
  }
29893
31090
  };
29894
31091
  codexLogout = async (config2, parsed, env, dependencies) => {
31092
+ await assertDurableServiceUninstalled(config2);
29895
31093
  const cancellation = lifecycleCancellation(dependencies);
29896
31094
  let lock2 = null;
29897
31095
  try {
@@ -30068,6 +31266,7 @@ Waiting for approval...
30068
31266
  };
30069
31267
  };
30070
31268
  cleanupLogin = async (config2, parsed, dependencies, warnings, revoke) => {
31269
+ await assertDurableServiceUninstalled(config2);
30071
31270
  const lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
30072
31271
  let keeperLock = null;
30073
31272
  try {
@@ -30436,7 +31635,7 @@ Waiting for approval...
30436
31635
  }
30437
31636
  if (parsed.once && !commandLock) break;
30438
31637
  await (dependencies.sleep ?? (async (milliseconds) => {
30439
- await new Promise((resolve5) => setTimeout(resolve5, milliseconds));
31638
+ await new Promise((resolve6) => setTimeout(resolve6, milliseconds));
30440
31639
  }))(retryDelayMs);
30441
31640
  }
30442
31641
  return {
@@ -30461,7 +31660,7 @@ Waiting for approval...
30461
31660
  const session = await agentSession(config2, dependencies, warnings);
30462
31661
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
30463
31662
  const sleep4 = dependencies.sleep ?? (async (milliseconds) => {
30464
- await new Promise((resolve5) => setTimeout(resolve5, milliseconds));
31663
+ await new Promise((resolve6) => setTimeout(resolve6, milliseconds));
30465
31664
  });
30466
31665
  const stopAt = now().getTime() + parsed.waitSeconds * 1e3;
30467
31666
  while (true) {
@@ -30645,6 +31844,8 @@ Waiting for approval...
30645
31844
  throw new Error("agent-fail requires a boolean retryable field.");
30646
31845
  }
30647
31846
  const dispatched = dispatchOutcome !== "not_dispatched";
31847
+ const effectiveModel = dispatched && Object.hasOwn(input, "effective_model") ? input.effective_model : null;
31848
+ const effectiveEffort = dispatched && Object.hasOwn(input, "effective_reasoning_effort") ? input.effective_reasoning_effort : null;
30648
31849
  request = agentFailRequestSchema.parse({
30649
31850
  operation_id: state.failure_operation_id,
30650
31851
  host_id: state.host_id,
@@ -30652,8 +31853,8 @@ Waiting for approval...
30652
31853
  attempt_id: state.attempt_id,
30653
31854
  claim_handle: state.claim_handle,
30654
31855
  dispatch_outcome: dispatchOutcome,
30655
- effective_model: dispatched ? String(input.effective_model ?? state.requested_model) : null,
30656
- effective_reasoning_effort: dispatched ? String(input.effective_reasoning_effort ?? state.requested_reasoning_effort) : null,
31856
+ effective_model: effectiveModel == null ? null : String(effectiveModel),
31857
+ effective_reasoning_effort: effectiveEffort == null ? null : String(effectiveEffort),
30657
31858
  adapter_request_id: input.adapter_request_id == null ? null : String(input.adapter_request_id),
30658
31859
  adapter_response_id: input.adapter_response_id == null ? null : String(input.adapter_response_id),
30659
31860
  usage: input.usage ?? (dispatched ? {
@@ -30689,15 +31890,127 @@ Waiting for approval...
30689
31890
  await lock2.release();
30690
31891
  }
30691
31892
  };
31893
+ serviceCommand = async (config2, parsed, env, dependencies, warnings) => {
31894
+ const action = parsed.serviceAction;
31895
+ if (!action) {
31896
+ throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
31897
+ }
31898
+ if (action === "run-internal") {
31899
+ if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
31900
+ const serviceManifest = await readInferenceHostServiceManifest(parsed.serviceManifestPath);
31901
+ if (!serviceManifest) throw new Error("Inference-host service manifest is missing.");
31902
+ const serviceConfig = resolveInferenceHostConfig({
31903
+ ...env,
31904
+ ...serviceManifest.runtime_environment
31905
+ });
31906
+ const serviceLock = await acquireInferenceHostProcessLock(
31907
+ `${serviceConfig.processLockPath}.service`
31908
+ );
31909
+ const cancellation = lifecycleCancellation(dependencies);
31910
+ try {
31911
+ await (dependencies.runServiceSupervisor ?? runInferenceHostServiceSupervisor)(
31912
+ parsed.serviceManifestPath,
31913
+ {
31914
+ signal: cancellation.signal,
31915
+ runWorker: async (manifest, signal) => {
31916
+ const startedAt = Date.now();
31917
+ const serviceEnv = { ...env, ...manifest.runtime_environment };
31918
+ const serviceConfig2 = resolveInferenceHostConfig(serviceEnv);
31919
+ const unregister = (abort) => {
31920
+ const onAbort = () => abort("SIGTERM");
31921
+ signal.addEventListener("abort", onAbort, { once: true });
31922
+ return () => signal.removeEventListener("abort", onAbort);
31923
+ };
31924
+ const result2 = await runHost(serviceConfig2, {
31925
+ ...parsed,
31926
+ command: "run",
31927
+ serviceAction: null,
31928
+ serviceManifestPath: null,
31929
+ displayName: manifest.display_name,
31930
+ once: false
31931
+ }, serviceEnv, {
31932
+ ...dependencies,
31933
+ registerLifecycleSignalHandlers: unregister
31934
+ }, warnings);
31935
+ return { exitCode: result2.exitCode, uptimeMs: Date.now() - startedAt };
31936
+ }
31937
+ }
31938
+ );
31939
+ return { exitCode: 0, stdout: "", stderr: "" };
31940
+ } finally {
31941
+ cancellation.unregister();
31942
+ await serviceLock.release();
31943
+ }
31944
+ }
31945
+ const manager = dependencies.createServiceManager?.(
31946
+ config2,
31947
+ dependencies.serviceDependencies
31948
+ ) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
31949
+ if (action === "install") {
31950
+ const adapter = parsed.adapter || "codex";
31951
+ if (adapter !== "codex") {
31952
+ throw new Error(
31953
+ `Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
31954
+ );
31955
+ }
31956
+ const state = await readInferenceHostLocalState(config2.statePath);
31957
+ if (!state) throw new Error("Run vtx inference-host login before installing the service.");
31958
+ const store = configuredCredentialStore(config2, dependencies, (message) => warnings.push(message));
31959
+ const credential = await store.read(accountKeyForState(state));
31960
+ if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
31961
+ assertCredentialMatchesState2(state, credential);
31962
+ if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
31963
+ throw new Error("Automated Codex service does not accept agent model or effort options.");
31964
+ }
31965
+ const auth = await inspectCodexAuthentication(config2);
31966
+ if (!auth.present || !auth.private) {
31967
+ throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
31968
+ }
31969
+ const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
31970
+ await (dependencies.preflightCodex ?? preflightCodexSubscription)({
31971
+ binary,
31972
+ codexHome: config2.codexHomePath,
31973
+ deadlineAtMs: Date.now() + 3e4
31974
+ });
31975
+ const status = await manager.install({ adapter: "codex", displayName: parsed.displayName });
31976
+ return {
31977
+ exitCode: 0,
31978
+ stdout: render({ status: "service_installed", ...status }, parsed.json),
31979
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
31980
+ ` : ""
31981
+ };
31982
+ }
31983
+ if (parsed.adapter || parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
31984
+ throw new Error("Service adapter and model options are accepted only by service install.");
31985
+ }
31986
+ if (action === "logs") {
31987
+ return { exitCode: 0, stdout: await manager.logs(parsed.lines), stderr: "" };
31988
+ }
31989
+ if (action === "start") {
31990
+ return { exitCode: 0, stdout: render({ status: "service_started", ...await manager.start() }, parsed.json), stderr: "" };
31991
+ }
31992
+ if (action === "stop") {
31993
+ return { exitCode: 0, stdout: render({ status: "service_stopped", ...await manager.stop() }, parsed.json), stderr: "" };
31994
+ }
31995
+ if (action === "status") {
31996
+ const status = await manager.status();
31997
+ const lifecycleMatches = status.installed && (status.desired_running ? status.manager_active : !status.manager_active);
31998
+ return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
31999
+ }
32000
+ if (action === "uninstall") {
32001
+ return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...await manager.uninstall() }, parsed.json), stderr: "" };
32002
+ }
32003
+ throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
32004
+ };
30692
32005
  }
30693
32006
  });
30694
32007
 
30695
32008
  // lib/agent-core/config.ts
30696
- import { mkdir as mkdir3, readFile as readFile5, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
30697
- import { dirname as dirname3, join as join6 } from "node:path";
30698
- import { homedir as homedir2 } from "node:os";
32009
+ import { mkdir as mkdir5, readFile as readFile6, rm as rm6, writeFile as writeFile3 } from "node:fs/promises";
32010
+ import { dirname as dirname5, join as join7 } from "node:path";
32011
+ import { homedir as homedir3 } from "node:os";
30699
32012
  function defaultBaseDir() {
30700
- return join6(homedir2(), ".vtx");
32013
+ return join7(homedir3(), ".vtx");
30701
32014
  }
30702
32015
  function resolveAgentCliConfig(env = process.env) {
30703
32016
  const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
@@ -30705,8 +32018,8 @@ function resolveAgentCliConfig(env = process.env) {
30705
32018
  const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
30706
32019
  return {
30707
32020
  apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
30708
- tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join6(baseDir, "token.json"),
30709
- statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join6(baseDir, "runtime-state.json"),
32021
+ tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join7(baseDir, "token.json"),
32022
+ statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join7(baseDir, "runtime-state.json"),
30710
32023
  runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
30711
32024
  activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
30712
32025
  outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
@@ -30714,7 +32027,7 @@ function resolveAgentCliConfig(env = process.env) {
30714
32027
  }
30715
32028
  async function readStoredAgentAuth(path) {
30716
32029
  try {
30717
- const raw = await readFile5(path, "utf8");
32030
+ const raw = await readFile6(path, "utf8");
30718
32031
  if (!raw.trim()) {
30719
32032
  throw new Error(`VTX token file is empty at ${path}. Run "vtx auth login" or remove the file and retry.`);
30720
32033
  }
@@ -30732,16 +32045,16 @@ async function readStoredAgentAuth(path) {
30732
32045
  }
30733
32046
  }
30734
32047
  async function writeStoredAgentAuth(path, auth) {
30735
- await mkdir3(dirname3(path), { recursive: true });
30736
- await writeFile2(path, `${JSON.stringify(auth, null, 2)}
32048
+ await mkdir5(dirname5(path), { recursive: true });
32049
+ await writeFile3(path, `${JSON.stringify(auth, null, 2)}
30737
32050
  `, { encoding: "utf8", mode: 384 });
30738
32051
  }
30739
32052
  async function clearStoredAgentAuth(path) {
30740
- await rm5(path, { force: true });
32053
+ await rm6(path, { force: true });
30741
32054
  }
30742
32055
  async function readRuntimeState(path) {
30743
32056
  try {
30744
- const raw = await readFile5(path, "utf8");
32057
+ const raw = await readFile6(path, "utf8");
30745
32058
  const parsed = JSON.parse(raw);
30746
32059
  const profileId = Number(parsed.profileId);
30747
32060
  const runtimeSessionId = String(parsed.runtimeSessionId || "").trim();
@@ -30765,12 +32078,12 @@ async function readRuntimeState(path) {
30765
32078
  }
30766
32079
  }
30767
32080
  async function writeRuntimeState(path, state) {
30768
- await mkdir3(dirname3(path), { recursive: true });
30769
- await writeFile2(path, `${JSON.stringify(state, null, 2)}
32081
+ await mkdir5(dirname5(path), { recursive: true });
32082
+ await writeFile3(path, `${JSON.stringify(state, null, 2)}
30770
32083
  `, { encoding: "utf8", mode: 384 });
30771
32084
  }
30772
32085
  async function clearRuntimeState(path) {
30773
- await rm5(path, { force: true });
32086
+ await rm6(path, { force: true });
30774
32087
  }
30775
32088
  var init_config2 = __esm({
30776
32089
  "lib/agent-core/config.ts"() {
@@ -32595,12 +33908,12 @@ var init_hyperliquid_account_state_adapter = __esm({
32595
33908
  if (signal.aborted) {
32596
33909
  throw createAbortError(abortMessage);
32597
33910
  }
32598
- return new Promise((resolve5, reject) => {
33911
+ return new Promise((resolve6, reject) => {
32599
33912
  const onAbort = () => {
32600
33913
  reject(createAbortError(abortMessage));
32601
33914
  };
32602
33915
  signal.addEventListener("abort", onAbort, { once: true });
32603
- promise2.then(resolve5, reject).finally(() => {
33916
+ promise2.then(resolve6, reject).finally(() => {
32604
33917
  signal.removeEventListener("abort", onAbort);
32605
33918
  });
32606
33919
  });
@@ -34011,7 +35324,7 @@ var init_api2 = __esm({
34011
35324
  }
34012
35325
  };
34013
35326
  API_URL = getApiUrl();
34014
- sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
35327
+ sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
34015
35328
  isTransientNetworkFetchError = (error48) => {
34016
35329
  const message = error48 instanceof Error ? error48.message : String(error48 || "");
34017
35330
  const normalized = message.toLowerCase();
@@ -42246,9 +43559,9 @@ function decryptKeystoreJsonSync(json2, _password) {
42246
43559
  return getAccount(data, key);
42247
43560
  }
42248
43561
  function stall(duration3) {
42249
- return new Promise((resolve5) => {
43562
+ return new Promise((resolve6) => {
42250
43563
  setTimeout(() => {
42251
- resolve5();
43564
+ resolve6();
42252
43565
  }, duration3);
42253
43566
  });
42254
43567
  }
@@ -42858,9 +44171,9 @@ var init_json_crowdsale = __esm({
42858
44171
 
42859
44172
  // node_modules/ethers/lib.esm/wallet/wallet.js
42860
44173
  function stall2(duration3) {
42861
- return new Promise((resolve5) => {
44174
+ return new Promise((resolve6) => {
42862
44175
  setTimeout(() => {
42863
- resolve5();
44176
+ resolve6();
42864
44177
  }, duration3);
42865
44178
  });
42866
44179
  }
@@ -43955,8 +45268,8 @@ var init_exchange_mutation_fence = __esm({
43955
45268
  return operation();
43956
45269
  }
43957
45270
  let markSettled;
43958
- const settlement = new Promise((resolve5) => {
43959
- markSettled = resolve5;
45271
+ const settlement = new Promise((resolve6) => {
45272
+ markSettled = resolve6;
43960
45273
  });
43961
45274
  const activeSettlements = activeSettlementsByScope.get(normalizedScope) ?? /* @__PURE__ */ new Set();
43962
45275
  activeSettlements.add(settlement);
@@ -45213,12 +46526,12 @@ var init_hyperliquid_client = __esm({
45213
46526
  },
45214
46527
  captureController.signal
45215
46528
  ).then(() => void 0).catch(() => void 0);
45216
- const captureDeadline = new Promise((resolve5) => {
46529
+ const captureDeadline = new Promise((resolve6) => {
45217
46530
  captureTimeout = globalThis.setTimeout(() => {
45218
46531
  captureController.abort(
45219
46532
  new DOMException("Execution attempt capture timed out.", "TimeoutError")
45220
46533
  );
45221
- resolve5();
46534
+ resolve6();
45222
46535
  }, remainingBudgetMs);
45223
46536
  });
45224
46537
  await Promise.race([captureRequest, captureDeadline]);
@@ -45652,7 +46965,7 @@ var init_vault = __esm({
45652
46965
  }
45653
46966
  return output3.buffer.slice(output3.byteOffset, output3.byteOffset + output3.byteLength);
45654
46967
  };
45655
- openVaultDb = async () => new Promise((resolve5, reject) => {
46968
+ openVaultDb = async () => new Promise((resolve6, reject) => {
45656
46969
  const request = indexedDB.open(VAULT_DB_NAME, VAULT_DB_VERSION);
45657
46970
  request.onerror = () => reject(request.error ?? new Error("Failed to open client vault database."));
45658
46971
  request.onupgradeneeded = () => {
@@ -45661,7 +46974,7 @@ var init_vault = __esm({
45661
46974
  database.createObjectStore(VAULT_KEY_STORE);
45662
46975
  }
45663
46976
  };
45664
- request.onsuccess = () => resolve5(request.result);
46977
+ request.onsuccess = () => resolve6(request.result);
45665
46978
  });
45666
46979
  withVaultStore = async (mode, fn) => {
45667
46980
  const database = await openVaultDb();
@@ -45669,8 +46982,8 @@ var init_vault = __esm({
45669
46982
  const transaction = database.transaction(VAULT_KEY_STORE, mode);
45670
46983
  const store = transaction.objectStore(VAULT_KEY_STORE);
45671
46984
  const result2 = await fn(store);
45672
- await new Promise((resolve5, reject) => {
45673
- transaction.oncomplete = () => resolve5();
46985
+ await new Promise((resolve6, reject) => {
46986
+ transaction.oncomplete = () => resolve6();
45674
46987
  transaction.onerror = () => reject(transaction.error ?? new Error("Client vault transaction failed."));
45675
46988
  transaction.onabort = () => reject(transaction.error ?? new Error("Client vault transaction aborted."));
45676
46989
  });
@@ -45685,9 +46998,9 @@ var init_vault = __esm({
45685
46998
  }
45686
46999
  return withVaultStore("readonly", async (store) => {
45687
47000
  const request = store.get(profileId);
45688
- return await new Promise((resolve5, reject) => {
47001
+ return await new Promise((resolve6, reject) => {
45689
47002
  request.onerror = () => reject(request.error ?? new Error("Failed to read client vault key."));
45690
- request.onsuccess = () => resolve5(request.result ?? null);
47003
+ request.onsuccess = () => resolve6(request.result ?? null);
45691
47004
  });
45692
47005
  });
45693
47006
  };
@@ -45709,9 +47022,9 @@ var init_vault = __esm({
45709
47022
  );
45710
47023
  await withVaultStore("readwrite", async (store) => {
45711
47024
  const request = store.put(createdKey, profileId);
45712
- await new Promise((resolve5, reject) => {
47025
+ await new Promise((resolve6, reject) => {
45713
47026
  request.onerror = () => reject(request.error ?? new Error("Failed to persist client vault key."));
45714
- request.onsuccess = () => resolve5();
47027
+ request.onsuccess = () => resolve6();
45715
47028
  });
45716
47029
  });
45717
47030
  return createdKey;
@@ -45722,9 +47035,9 @@ var init_vault = __esm({
45722
47035
  }
45723
47036
  await withVaultStore("readwrite", async (store) => {
45724
47037
  const request = store.delete(profileId);
45725
- await new Promise((resolve5, reject) => {
47038
+ await new Promise((resolve6, reject) => {
45726
47039
  request.onerror = () => reject(request.error ?? new Error("Failed to remove client vault key."));
45727
- request.onsuccess = () => resolve5();
47040
+ request.onsuccess = () => resolve6();
45728
47041
  });
45729
47042
  });
45730
47043
  };
@@ -46939,10 +48252,10 @@ var init_runtime_execution = __esm({
46939
48252
  }
46940
48253
  };
46941
48254
  sleep3 = async (ms, signal) => {
46942
- await new Promise((resolve5, reject) => {
48255
+ await new Promise((resolve6, reject) => {
46943
48256
  const timer = setTimeout(() => {
46944
48257
  cleanup();
46945
- resolve5(void 0);
48258
+ resolve6(void 0);
46946
48259
  }, ms);
46947
48260
  const cleanup = () => {
46948
48261
  clearTimeout(timer);
@@ -48580,7 +49893,7 @@ __export(vtx_exports, {
48580
49893
  runVtxCli: () => runVtxCli
48581
49894
  });
48582
49895
  import { randomUUID as randomUUID5 } from "node:crypto";
48583
- import { spawn as spawn5 } from "node:child_process";
49896
+ import { spawn as spawn7 } from "node:child_process";
48584
49897
  function render2(value, json2) {
48585
49898
  if (json2) {
48586
49899
  return `${JSON.stringify(value, null, 2)}
@@ -48610,7 +49923,7 @@ function openBrowser(url2) {
48610
49923
  const platform = process.platform;
48611
49924
  const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
48612
49925
  const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
48613
- const child = spawn5(command, args, { detached: true, stdio: "ignore" });
49926
+ const child = spawn7(command, args, { detached: true, stdio: "ignore" });
48614
49927
  child.unref();
48615
49928
  }
48616
49929
  function parseFlags(args) {
@@ -49140,7 +50453,7 @@ Global options:
49140
50453
  "alibaba_cloud_model_studio_api_key",
49141
50454
  "lightning_api_key"
49142
50455
  ]);
49143
- delay = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
50456
+ delay = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
49144
50457
  }
49145
50458
  });
49146
50459
 
@@ -49152,7 +50465,9 @@ var INFERENCE_HOST_VALUE_OPTIONS = /* @__PURE__ */ new Set([
49152
50465
  "--model",
49153
50466
  "--model-label",
49154
50467
  "--effort",
49155
- "--wait-seconds"
50468
+ "--wait-seconds",
50469
+ "--lines",
50470
+ "--service-manifest"
49156
50471
  ]);
49157
50472
  var isInferenceHostCliInvocation = (argv2) => {
49158
50473
  for (let index = 0; index < argv2.length; index += 1) {