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