@vtxmacro/cli 2026.8.19 → 2026.8.21

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 (2) hide show
  1. package/bin/vtx.js +270 -67
  2. package/package.json +1 -1
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.19",
41
+ package_version: "2026.8.21",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
44
  platforms: {
@@ -19837,6 +19837,9 @@ child.once('close', async () => {
19837
19837
  this.fatalListeners.add(listener);
19838
19838
  return () => this.fatalListeners.delete(listener);
19839
19839
  }
19840
+ isUsable() {
19841
+ return !this.closed && this.fatalError === null && this.process.exitCode === null;
19842
+ }
19840
19843
  async readAccountSnapshot(deadlineAtMs, signal, refreshToken = false) {
19841
19844
  const result2 = await this.request("account/read", { refreshToken }, {
19842
19845
  timeoutMs: Math.max(1, deadlineAtMs - Date.now()),
@@ -20196,6 +20199,30 @@ child.once('close', async () => {
20196
20199
  });
20197
20200
  const startedAt = Date.now();
20198
20201
  const unsubscribe = this.subscribe(({ method, params }) => {
20202
+ const eventThreadId = String(params.threadId || "");
20203
+ const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20204
+ if (scopedMethod && !eventThreadId) {
20205
+ terminalReject(new CodexAppServerError({
20206
+ message: "Codex notification omitted its thread scope.",
20207
+ category: "adapter",
20208
+ code: "missing_thread_notification_scope",
20209
+ retryable: false,
20210
+ dispatchOutcome: requestWritten ? turnId ? "confirmed_dispatched" : "outcome_unknown" : "not_dispatched"
20211
+ }));
20212
+ return;
20213
+ }
20214
+ if (scopedMethod && eventThreadId !== request.thread.threadId) {
20215
+ if (!this.threads.has(eventThreadId)) {
20216
+ terminalReject(new CodexAppServerError({
20217
+ message: "Codex notification crossed thread scope.",
20218
+ category: "adapter",
20219
+ code: "cross_thread_notification",
20220
+ retryable: false,
20221
+ dispatchOutcome: turnId ? "confirmed_dispatched" : "outcome_unknown"
20222
+ }));
20223
+ }
20224
+ return;
20225
+ }
20199
20226
  if (forbiddenMethod(method)) {
20200
20227
  terminalReject(new CodexAppServerError({
20201
20228
  message: "Codex attempted to use forbidden child authority.",
@@ -20231,18 +20258,6 @@ child.once('close', async () => {
20231
20258
  return;
20232
20259
  }
20233
20260
  }
20234
- const eventThreadId = String(params.threadId || "");
20235
- const scopedMethod = method.startsWith("thread/") || method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
20236
- if (scopedMethod && eventThreadId !== request.thread.threadId) {
20237
- terminalReject(new CodexAppServerError({
20238
- message: "Codex notification crossed thread scope.",
20239
- category: "adapter",
20240
- code: "cross_thread_notification",
20241
- retryable: false,
20242
- dispatchOutcome: turnId ? "confirmed_dispatched" : "outcome_unknown"
20243
- }));
20244
- return;
20245
- }
20246
20261
  const eventTurn = objectOrNull(params.turn);
20247
20262
  const eventTurnId = String(params.turnId || eventTurn?.id || "");
20248
20263
  const turnScopedMethod = method.startsWith("turn/") || method.startsWith("item/") || method.startsWith("rawResponse") || method === "model/rerouted";
@@ -20884,7 +20899,7 @@ var init_codex_adapter = __esm({
20884
20899
  context.addIssue({ code: "custom", message: "Codex recovery thread identity is incomplete." });
20885
20900
  }
20886
20901
  const guardianManaged = value.processToken !== null || value.processReceiptPath !== null;
20887
- if (value.processToken === null !== (value.processReceiptPath === null) || value.processState === "unmanaged" === guardianManaged || value.cleanupConfirmed && !["unmanaged", "terminated"].includes(value.processState)) {
20902
+ if (value.processToken === null !== (value.processReceiptPath === null) || value.processState === "unmanaged" === guardianManaged || value.cleanupConfirmed && value.processState === "spawn_intent") {
20888
20903
  context.addIssue({ code: "custom", message: "Codex recovery process identity is invalid." });
20889
20904
  }
20890
20905
  });
@@ -21293,8 +21308,114 @@ var init_codex_adapter = __esm({
21293
21308
  CodexSubscriptionAdapter = class {
21294
21309
  constructor(dependencies = {}) {
21295
21310
  this.inFlightAttempts = /* @__PURE__ */ new Map();
21311
+ this.durableSession = null;
21312
+ this.durableSessionStart = null;
21313
+ this.closing = false;
21314
+ this.adapterClosePromise = null;
21296
21315
  this.dependencies = dependencies;
21297
21316
  }
21317
+ async startDurableSession(codexHome, deadlineAtMs) {
21318
+ const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21319
+ const guardianManaged = Boolean(
21320
+ this.dependencies.recoveryHooks && !this.dependencies.spawnProcess
21321
+ );
21322
+ const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
21323
+ const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
21324
+ const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
21325
+ if (guardianReceiptRoot) await ensureInferencePrivateDirectory(guardianReceiptRoot);
21326
+ const processReceiptPath = guardianReceiptRoot && processToken ? join4(guardianReceiptRoot, `${processToken}.json`) : null;
21327
+ const session = await CodexAppServerSession.start({
21328
+ binary,
21329
+ codexHome,
21330
+ deadlineAtMs,
21331
+ spawnProcess: this.dependencies.spawnProcess,
21332
+ guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
21333
+ });
21334
+ return {
21335
+ session,
21336
+ codexHome,
21337
+ processToken,
21338
+ processReceiptPath,
21339
+ bootIdentity,
21340
+ terminated: false,
21341
+ closePromise: null
21342
+ };
21343
+ }
21344
+ async acquireDurableSession(codexHome, deadlineAtMs, signal) {
21345
+ if (this.closing) {
21346
+ throw new CodexAppServerError({
21347
+ message: "Codex adapter is closing.",
21348
+ category: "transport",
21349
+ code: "transport_closed",
21350
+ retryable: true
21351
+ });
21352
+ }
21353
+ if (this.durableSession && this.durableSession.codexHome === codexHome && this.durableSession.session.isUsable()) return this.durableSession;
21354
+ if (this.durableSession && this.durableSession.codexHome !== codexHome) {
21355
+ await this.invalidateDurableSession(this.durableSession);
21356
+ }
21357
+ if (this.durableSession && !this.durableSession.session.isUsable()) {
21358
+ await this.invalidateDurableSession(this.durableSession);
21359
+ }
21360
+ if (!this.durableSessionStart) {
21361
+ const start2 = this.startDurableSession(codexHome, deadlineAtMs);
21362
+ this.durableSessionStart = start2;
21363
+ void start2.then((session) => {
21364
+ if (this.durableSessionStart === start2) this.durableSession = session;
21365
+ }).finally(() => {
21366
+ if (this.durableSessionStart === start2) this.durableSessionStart = null;
21367
+ }).catch(() => void 0);
21368
+ }
21369
+ const start = this.durableSessionStart;
21370
+ return await new Promise((resolveSession, reject) => {
21371
+ let settled = false;
21372
+ const finish = (callback) => {
21373
+ if (settled) return;
21374
+ settled = true;
21375
+ clearTimeout(timer);
21376
+ signal?.removeEventListener("abort", onAbort);
21377
+ callback();
21378
+ };
21379
+ const onAbort = () => finish(() => reject(new CodexAppServerError({
21380
+ message: "Codex session acquisition was cancelled.",
21381
+ category: "cancelled",
21382
+ code: "cancelled",
21383
+ retryable: false
21384
+ })));
21385
+ const timer = setTimeout(() => finish(() => reject(new CodexAppServerError({
21386
+ message: "Codex session acquisition exceeded the immutable deadline.",
21387
+ category: "timeout",
21388
+ code: "deadline_exceeded",
21389
+ retryable: false
21390
+ }))), Math.max(1, deadlineAtMs - Date.now()));
21391
+ signal?.addEventListener("abort", onAbort, { once: true });
21392
+ if (signal?.aborted) {
21393
+ onAbort();
21394
+ return;
21395
+ }
21396
+ void start.then(
21397
+ (session) => finish(() => resolveSession(session)),
21398
+ (error48) => finish(() => reject(error48))
21399
+ );
21400
+ });
21401
+ }
21402
+ async invalidateDurableSession(session) {
21403
+ if (this.durableSession === session) this.durableSession = null;
21404
+ if (!session.closePromise) {
21405
+ session.closePromise = (async () => {
21406
+ await session.session.close();
21407
+ if (session.processToken && session.processReceiptPath) {
21408
+ await waitForCodexGuardianState(
21409
+ { processToken: session.processToken, receiptPath: session.processReceiptPath },
21410
+ "terminated",
21411
+ Date.now() + 5e3
21412
+ );
21413
+ }
21414
+ session.terminated = true;
21415
+ })();
21416
+ }
21417
+ await session.closePromise;
21418
+ }
21298
21419
  runAttempt(input) {
21299
21420
  const active = this.inFlightAttempts.get(input.attemptId);
21300
21421
  if (active) {
@@ -21352,10 +21473,37 @@ var init_codex_adapter = __esm({
21352
21473
  dispatchOutcome: checkpoint.dispatchOutcome
21353
21474
  });
21354
21475
  }
21355
- await rm3(checkpoint.processReceiptPath, { force: true });
21476
+ const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
21477
+ if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
21478
+ await rm3(checkpoint.processReceiptPath, { force: true });
21479
+ }
21356
21480
  }
21357
21481
  await recoveryHooks.clear(attemptId);
21358
21482
  }
21483
+ async close() {
21484
+ if (!this.adapterClosePromise) {
21485
+ this.closing = true;
21486
+ this.adapterClosePromise = (async () => {
21487
+ const sessions = /* @__PURE__ */ new Set();
21488
+ if (this.durableSession) sessions.add(this.durableSession);
21489
+ const starting = this.durableSessionStart;
21490
+ if (starting) {
21491
+ const started = await starting.catch(() => null);
21492
+ if (started) sessions.add(started);
21493
+ }
21494
+ await Promise.all(
21495
+ [...sessions].map((session) => this.invalidateDurableSession(session))
21496
+ );
21497
+ await Promise.allSettled(
21498
+ [...this.inFlightAttempts.values()].map((attempt) => attempt.promise)
21499
+ );
21500
+ for (const session of sessions) {
21501
+ if (session.processReceiptPath) await rm3(session.processReceiptPath, { force: true });
21502
+ }
21503
+ })();
21504
+ }
21505
+ await this.adapterClosePromise;
21506
+ }
21359
21507
  async executeAttempt(input) {
21360
21508
  const validatedInput = validateAttemptInput(input);
21361
21509
  const recoveryHooks = this.dependencies.recoveryHooks;
@@ -21406,6 +21554,7 @@ var init_codex_adapter = __esm({
21406
21554
  });
21407
21555
  }
21408
21556
  let resources = null;
21557
+ let sessionLease = null;
21409
21558
  let session = null;
21410
21559
  let threadId = null;
21411
21560
  let baseCheckpoint = null;
@@ -21413,22 +21562,20 @@ var init_codex_adapter = __esm({
21413
21562
  let currentDispatchOutcome = "not_dispatched";
21414
21563
  let completedResult = null;
21415
21564
  let caughtError = null;
21565
+ let sessionInvalidationError = null;
21416
21566
  try {
21417
- const binary = await (this.dependencies.resolveBinary ?? resolvePinnedCodexBinary)();
21418
21567
  resources = await (this.dependencies.createAttemptResources ?? ((attemptId) => createIsolatedCodexAttemptResources(
21419
21568
  attemptId,
21420
21569
  this.dependencies.codexHome
21421
21570
  )))(input.attemptId);
21422
21571
  await access2(resources.codexHome, fsConstants2.R_OK | fsConstants2.W_OK);
21423
21572
  await access2(resources.workspacePath, fsConstants2.R_OK);
21424
- const guardianManaged = Boolean(recoveryHooks && !this.dependencies.spawnProcess);
21425
- const bootIdentity = guardianManaged ? await (this.dependencies.readBootIdentity ?? readInferenceSystemBootIdentity)() : null;
21426
- const processToken = guardianManaged ? randomBytes3(32).toString("hex") : null;
21427
- const guardianReceiptRoot = guardianManaged ? this.dependencies.guardianReceiptRoot ?? join4(tmpdir2(), "vtx-codex-guardian-receipts") : null;
21428
- if (guardianReceiptRoot) {
21429
- await ensureInferencePrivateDirectory(guardianReceiptRoot);
21430
- }
21431
- const processReceiptPath = guardianReceiptRoot && processToken ? join4(guardianReceiptRoot, `${processToken}.json`) : null;
21573
+ sessionLease = await this.acquireDurableSession(
21574
+ resources.codexHome,
21575
+ input.deadlineAtMs,
21576
+ input.signal
21577
+ );
21578
+ session = sessionLease.session;
21432
21579
  baseCheckpoint = {
21433
21580
  schemaVersion: "vtx_codex_attempt_recovery_v1",
21434
21581
  attemptId: input.attemptId,
@@ -21438,10 +21585,10 @@ var init_codex_adapter = __esm({
21438
21585
  workspacePath: resources.workspacePath,
21439
21586
  threadId: null,
21440
21587
  threadPath: null,
21441
- processToken,
21442
- processReceiptPath,
21443
- processState: guardianManaged ? "spawn_intent" : "unmanaged",
21444
- bootIdentity,
21588
+ processToken: sessionLease.processToken,
21589
+ processReceiptPath: sessionLease.processReceiptPath,
21590
+ processState: sessionLease.processToken ? "running" : "unmanaged",
21591
+ bootIdentity: sessionLease.bootIdentity,
21445
21592
  cleanupConfirmed: false,
21446
21593
  adapterRequestId: null,
21447
21594
  adapterResponseId: null,
@@ -21450,19 +21597,6 @@ var init_codex_adapter = __esm({
21450
21597
  };
21451
21598
  latestCheckpoint = baseCheckpoint;
21452
21599
  await recoveryHooks?.save(baseCheckpoint);
21453
- session = await CodexAppServerSession.start({
21454
- binary,
21455
- codexHome: resources.codexHome,
21456
- deadlineAtMs: input.deadlineAtMs,
21457
- signal: input.signal,
21458
- spawnProcess: this.dependencies.spawnProcess,
21459
- guardian: processToken && processReceiptPath ? { processToken, receiptPath: processReceiptPath, bootIdentity: bootIdentity ?? void 0 } : void 0
21460
- });
21461
- if (guardianManaged) {
21462
- baseCheckpoint = { ...baseCheckpoint, processState: "running" };
21463
- latestCheckpoint = baseCheckpoint;
21464
- await recoveryHooks?.save(baseCheckpoint);
21465
- }
21466
21600
  await session.requireManagedChatGptAuth(input.deadlineAtMs, input.signal);
21467
21601
  await session.requireModel(
21468
21602
  input.requestedModel,
@@ -21522,6 +21656,20 @@ var init_codex_adapter = __esm({
21522
21656
  return result2;
21523
21657
  } catch (error48) {
21524
21658
  caughtError = error48;
21659
+ if (sessionLease && (!sessionLease.session.isUsable() || error48 instanceof CodexAppServerError && (error48.category === "transport" || [
21660
+ "rpc_rejected",
21661
+ "invalid_rpc_result",
21662
+ "rpc_timeout",
21663
+ "invalid_thread_provenance",
21664
+ "thread_cleanup_identity_missing",
21665
+ "thread_delete_unconfirmed"
21666
+ ].includes(error48.code)))) {
21667
+ try {
21668
+ await this.invalidateDurableSession(sessionLease);
21669
+ } catch (invalidationError) {
21670
+ sessionInvalidationError = invalidationError;
21671
+ }
21672
+ }
21525
21673
  if (error48 instanceof CodexAppServerError) {
21526
21674
  const dispatchOutcome = error48.dispatchOutcome === "not_dispatched" ? currentDispatchOutcome : error48.dispatchOutcome;
21527
21675
  if (baseCheckpoint && !completedResult) {
@@ -21584,7 +21732,7 @@ var init_codex_adapter = __esm({
21584
21732
  }
21585
21733
  throw classified;
21586
21734
  } finally {
21587
- let cleanupError = null;
21735
+ let cleanupError = sessionInvalidationError;
21588
21736
  if (session && threadId) {
21589
21737
  try {
21590
21738
  await session.deleteThread(threadId);
@@ -21592,23 +21740,9 @@ var init_codex_adapter = __esm({
21592
21740
  cleanupError = error48;
21593
21741
  }
21594
21742
  }
21595
- try {
21596
- await session?.close();
21597
- } catch (error48) {
21598
- cleanupError ??= error48;
21599
- }
21600
- if (latestCheckpoint && recoveryHooks) {
21743
+ if (sessionLease && cleanupError) {
21601
21744
  try {
21602
- if (latestCheckpoint.processToken && latestCheckpoint.processReceiptPath) {
21603
- await waitForCodexGuardianState(
21604
- {
21605
- processToken: latestCheckpoint.processToken,
21606
- receiptPath: latestCheckpoint.processReceiptPath
21607
- },
21608
- "terminated",
21609
- Date.now() + 5e3
21610
- );
21611
- }
21745
+ await this.invalidateDurableSession(sessionLease);
21612
21746
  } catch (error48) {
21613
21747
  cleanupError ??= error48;
21614
21748
  }
@@ -21623,7 +21757,7 @@ var init_codex_adapter = __esm({
21623
21757
  if (latestCheckpoint && recoveryHooks && cleanupError === null) {
21624
21758
  latestCheckpoint = {
21625
21759
  ...latestCheckpoint,
21626
- processState: latestCheckpoint.processToken ? "terminated" : "unmanaged",
21760
+ processState: latestCheckpoint.processToken ? sessionLease?.terminated ? "terminated" : "running" : "unmanaged",
21627
21761
  cleanupConfirmed: true
21628
21762
  };
21629
21763
  try {
@@ -29377,7 +29511,11 @@ var init_runner = __esm({
29377
29511
  } finally {
29378
29512
  removeAbortListener();
29379
29513
  unregisterSignals();
29380
- await lock2.release();
29514
+ try {
29515
+ await this.dependencies.codexAdapter.close?.();
29516
+ } finally {
29517
+ await lock2.release();
29518
+ }
29381
29519
  }
29382
29520
  }
29383
29521
  async persistReceipt(receipt, now) {
@@ -29825,7 +29963,7 @@ import { createWriteStream, readFileSync } from "node:fs";
29825
29963
  import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
29826
29964
  import { homedir as homedir2 } from "node:os";
29827
29965
  import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
29828
- var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertManifest, assertDesiredState, readInferenceHostServiceManifest, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsTaskXml, systemdUnit, launchAgentPlist, InferenceHostServiceManager, appendServiceLog, spawnServiceChild, runInferenceHostServiceSupervisor;
29966
+ 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
29967
  var init_service = __esm({
29830
29968
  "lib/inference-host/service.ts"() {
29831
29969
  "use strict";
@@ -29970,7 +30108,20 @@ var init_service = __esm({
29970
30108
  "--service-manifest",
29971
30109
  manifestPath
29972
30110
  ];
29973
- windowsTaskXml = (executable, args, username) => `<?xml version="1.0" encoding="UTF-16"?>
30111
+ windowsOwnedCommandLine = (values) => {
30112
+ if (values.some((value) => value.includes('"') || value.includes("\0"))) {
30113
+ throw new Error("Windows inference-host service paths contain unsupported characters.");
30114
+ }
30115
+ return values.map((value) => `"${value}"`).join(" ");
30116
+ };
30117
+ vbScriptString = (value) => `"${value.replaceAll('"', '""')}"`;
30118
+ windowsServiceLauncher = (executable, args) => `Option Explicit\r
30119
+ Dim shell, exitCode\r
30120
+ Set shell = CreateObject("WScript.Shell")\r
30121
+ exitCode = shell.Run(${vbScriptString(windowsOwnedCommandLine([executable, ...args]))}, 0, True)\r
30122
+ WScript.Quit exitCode\r
30123
+ `;
30124
+ windowsTaskXml = (launcherPath, windowsDirectory, username) => `<?xml version="1.0" encoding="UTF-16"?>
29974
30125
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
29975
30126
  <RegistrationInfo><Description>${xmlEscape(SERVICE_NAME)}</Description></RegistrationInfo>
29976
30127
  <Triggers>
@@ -29984,13 +30135,14 @@ var init_service = __esm({
29984
30135
  <Principals><Principal id="Author"><UserId>${xmlEscape(username)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
29985
30136
  <Settings>
29986
30137
  <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
30138
+ <Hidden>true</Hidden>
29987
30139
  <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
29988
30140
  <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
29989
30141
  <StartWhenAvailable>true</StartWhenAvailable>
29990
30142
  <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
29991
30143
  <RestartOnFailure><Interval>PT1M</Interval><Count>255</Count></RestartOnFailure>
29992
30144
  </Settings>
29993
- <Actions Context="Author"><Exec><Command>${xmlEscape(executable)}</Command><Arguments>${xmlEscape(args.map((arg) => `"${arg.replaceAll('"', '\\"')}"`).join(" "))}</Arguments></Exec></Actions>
30145
+ <Actions Context="Author"><Exec><Command>${xmlEscape(`${windowsDirectory}\\System32\\wscript.exe`)}</Command><Arguments>${xmlEscape(`//B //NoLogo "${launcherPath}"`)}</Arguments></Exec></Actions>
29994
30146
  </Task>
29995
30147
  `;
29996
30148
  systemdUnit = (executable, args) => `[Unit]
@@ -30031,6 +30183,7 @@ WantedBy=default.target
30031
30183
  this.executable = resolve4(dependencies.executable ?? process.execPath);
30032
30184
  this.script = resolve4(dependencies.script ?? process.argv[1] ?? "");
30033
30185
  this.username = dependencies.username ?? ([process.env.USERDOMAIN, process.env.USERNAME].filter(Boolean).join("\\") || process.env.USER || "");
30186
+ this.windowsDirectory = (dependencies.windowsDirectory ?? process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows").replace(/[\\/]+$/u, "");
30034
30187
  this.runCommand = dependencies.runCommand ?? defaultRunCommand;
30035
30188
  this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
30036
30189
  this.sleep = dependencies.sleep ?? (async (milliseconds) => {
@@ -30096,6 +30249,9 @@ WantedBy=default.target
30096
30249
  if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
30097
30250
  return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
30098
30251
  }
30252
+ windowsLauncherPath() {
30253
+ return `${this.config.statePath}.service-launcher.vbs`;
30254
+ }
30099
30255
  async managerCommand(action) {
30100
30256
  const definition = this.definitionPath();
30101
30257
  if (this.platform === "win32") {
@@ -30157,7 +30313,7 @@ WantedBy=default.target
30157
30313
  runtime_environment: runtimeEnvironment(this.config)
30158
30314
  });
30159
30315
  const args = serviceArguments(this.script, this.manifestPath());
30160
- const definition = this.platform === "win32" ? windowsTaskXml(this.executable, args, this.username) : this.platform === "darwin" ? launchAgentPlist(this.executable, args, this.logPath()) : systemdUnit(this.executable, args);
30316
+ 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);
30161
30317
  let managerInstallAttempted = false;
30162
30318
  try {
30163
30319
  await writeAtomicInferencePrivateFile(this.manifestPath(), `${JSON.stringify(manifest, null, 2)}
@@ -30165,6 +30321,10 @@ WantedBy=default.target
30165
30321
  await writeDesired(this.desiredPath(), options.startImmediately !== false, this.now());
30166
30322
  await mkdir3(dirname4(this.definitionPath()), { recursive: true, mode: 448 });
30167
30323
  if (this.platform === "win32") {
30324
+ await writeAtomicInferencePrivateFile(
30325
+ this.windowsLauncherPath(),
30326
+ windowsServiceLauncher(this.executable, args)
30327
+ );
30168
30328
  await writeFile2(this.definitionPath(), `\uFEFF${definition}`, {
30169
30329
  encoding: "utf16le",
30170
30330
  mode: 384
@@ -30205,6 +30365,9 @@ ${cleanup.stderr}`)) {
30205
30365
  }
30206
30366
  }
30207
30367
  await rm4(this.definitionPath(), { force: true }).catch(() => void 0);
30368
+ if (this.platform === "win32") {
30369
+ await rm4(this.windowsLauncherPath(), { force: true }).catch(() => void 0);
30370
+ }
30208
30371
  await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
30209
30372
  await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
30210
30373
  if (this.platform === "linux") {
@@ -30321,6 +30484,7 @@ ${result2.stderr}`)) {
30321
30484
  throw new Error(`Background service uninstall failed: ${result2.stderr.trim()}`);
30322
30485
  }
30323
30486
  await rm4(this.definitionPath(), { force: true });
30487
+ if (this.platform === "win32") await rm4(this.windowsLauncherPath(), { force: true });
30324
30488
  if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
30325
30489
  await rm4(this.manifestPath(), { force: true });
30326
30490
  await rm4(this.desiredPath(), { force: true });
@@ -30356,11 +30520,18 @@ ${result2.stderr}`)) {
30356
30520
  const startedAt = Date.now();
30357
30521
  const log = createWriteStream(manifest.log_path, { flags: "a", mode: 384 });
30358
30522
  return await new Promise((resolvePromise, reject) => {
30523
+ let stdout = "";
30359
30524
  const child = spawn5(manifest.executable, args, {
30360
30525
  env: { ...process.env, ...manifest.runtime_environment },
30361
30526
  windowsHide: true,
30362
- stdio: ["ignore", log, log]
30527
+ stdio: ["ignore", "pipe", "pipe"]
30528
+ });
30529
+ child.stdout.on("data", (chunk) => {
30530
+ const text = chunk.toString("utf8");
30531
+ log.write(text);
30532
+ stdout = `${stdout}${text}`.slice(-65536);
30363
30533
  });
30534
+ child.stderr.on("data", (chunk) => log.write(chunk));
30364
30535
  const onAbort = () => child.kill("SIGTERM");
30365
30536
  signal.addEventListener("abort", onAbort, { once: true });
30366
30537
  child.once("error", (error48) => {
@@ -30371,7 +30542,24 @@ ${result2.stderr}`)) {
30371
30542
  child.once("exit", (code) => {
30372
30543
  signal.removeEventListener("abort", onAbort);
30373
30544
  log.end();
30374
- resolvePromise({ exitCode: code ?? 1, uptimeMs: Date.now() - startedAt });
30545
+ let drainReason = null;
30546
+ for (const line of stdout.trim().split("\n").reverse()) {
30547
+ try {
30548
+ const parsed = JSON.parse(line);
30549
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
30550
+ parsed.drain_reason
30551
+ )) {
30552
+ drainReason = parsed.drain_reason;
30553
+ break;
30554
+ }
30555
+ } catch {
30556
+ }
30557
+ }
30558
+ resolvePromise({
30559
+ exitCode: code ?? 1,
30560
+ uptimeMs: Date.now() - startedAt,
30561
+ drainReason
30562
+ });
30375
30563
  });
30376
30564
  });
30377
30565
  };
@@ -30421,6 +30609,7 @@ ${result2.stderr}`)) {
30421
30609
  await appendServiceLog(manifest.log_path, "worker_exited", {
30422
30610
  exit_code: result2.exitCode,
30423
30611
  uptime_ms: result2.uptimeMs,
30612
+ drain_reason: result2.drainReason ?? null,
30424
30613
  retry_after_ms: retryAfterMs
30425
30614
  });
30426
30615
  await sleep4(retryAfterMs);
@@ -31906,7 +32095,21 @@ Waiting for approval...
31906
32095
  ...dependencies,
31907
32096
  registerLifecycleSignalHandlers: unregister
31908
32097
  }, warnings);
31909
- return { exitCode: result2.exitCode, uptimeMs: Date.now() - startedAt };
32098
+ let drainReason = null;
32099
+ try {
32100
+ const summary = JSON.parse(result2.stdout);
32101
+ if (summary && typeof summary === "object" && !Array.isArray(summary) && typeof summary.drain_reason === "string" && /^[a-z0-9_]{1,96}$/u.test(
32102
+ summary.drain_reason
32103
+ )) {
32104
+ drainReason = summary.drain_reason;
32105
+ }
32106
+ } catch {
32107
+ }
32108
+ return {
32109
+ exitCode: result2.exitCode,
32110
+ uptimeMs: Date.now() - startedAt,
32111
+ drainReason
32112
+ };
31910
32113
  }
31911
32114
  }
31912
32115
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.19",
3
+ "version": "2026.8.21",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",