@vtxmacro/cli 2026.8.37 → 2026.8.38
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/bin/vtx.js +369 -60
- package/package.json +1 -1
package/bin/vtx.js
CHANGED
|
@@ -38,7 +38,7 @@ var init_agent_cli_release = __esm({
|
|
|
38
38
|
"agent-cli-release.json"() {
|
|
39
39
|
agent_cli_release_default = {
|
|
40
40
|
package_name: "@vtxmacro/cli",
|
|
41
|
-
package_version: "2026.8.
|
|
41
|
+
package_version: "2026.8.38",
|
|
42
42
|
codex_package_name: "@openai/codex",
|
|
43
43
|
codex_version: "0.147.0",
|
|
44
44
|
platforms: {
|
|
@@ -16245,6 +16245,109 @@ function resolveInferenceHostConfig(env = process.env) {
|
|
|
16245
16245
|
supervisorProcessLockPath: join(baseDir, "host.lock")
|
|
16246
16246
|
};
|
|
16247
16247
|
}
|
|
16248
|
+
async function writeInferenceHostCredentialContext(config2) {
|
|
16249
|
+
const context = credentialContextForConfig(config2);
|
|
16250
|
+
await writeAtomicInferencePrivateFile(
|
|
16251
|
+
inferenceHostCredentialContextPath(config2),
|
|
16252
|
+
`${JSON.stringify(context, null, 2)}
|
|
16253
|
+
`
|
|
16254
|
+
);
|
|
16255
|
+
}
|
|
16256
|
+
async function writeInferenceHostCredentialContextTransition(config2, previous, candidate = credentialContextForConfig(config2), operation = "selection", phase = "prepared") {
|
|
16257
|
+
const transition = {
|
|
16258
|
+
schema_version: "vtx_inference_credential_context_transition_v1",
|
|
16259
|
+
operation,
|
|
16260
|
+
phase,
|
|
16261
|
+
previous,
|
|
16262
|
+
candidate
|
|
16263
|
+
};
|
|
16264
|
+
await writeAtomicInferencePrivateFile(
|
|
16265
|
+
inferenceHostCredentialContextTransitionPath(config2),
|
|
16266
|
+
`${JSON.stringify(transition, null, 2)}
|
|
16267
|
+
`
|
|
16268
|
+
);
|
|
16269
|
+
}
|
|
16270
|
+
async function readInferenceHostCredentialContextTransition(config2) {
|
|
16271
|
+
const raw = await readInferencePrivateFile(
|
|
16272
|
+
inferenceHostCredentialContextTransitionPath(config2),
|
|
16273
|
+
"Inference host credential context transition"
|
|
16274
|
+
);
|
|
16275
|
+
if (raw === null) return null;
|
|
16276
|
+
let value;
|
|
16277
|
+
try {
|
|
16278
|
+
value = JSON.parse(raw);
|
|
16279
|
+
} catch {
|
|
16280
|
+
throw new Error("Inference host credential context transition is not valid JSON.");
|
|
16281
|
+
}
|
|
16282
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
16283
|
+
throw new Error("Inference host credential context transition is invalid.");
|
|
16284
|
+
}
|
|
16285
|
+
const record2 = value;
|
|
16286
|
+
const expectedKeys = ["schema_version", "operation", "phase", "previous", "candidate"];
|
|
16287
|
+
if (Object.keys(record2).sort().join("\0") !== expectedKeys.sort().join("\0") || record2.schema_version !== "vtx_inference_credential_context_transition_v1" || record2.operation !== "selection" && record2.operation !== "cleanup" || record2.phase !== "prepared" && record2.phase !== "committed") {
|
|
16288
|
+
throw new Error("Inference host credential context transition is invalid.");
|
|
16289
|
+
}
|
|
16290
|
+
return {
|
|
16291
|
+
schema_version: "vtx_inference_credential_context_transition_v1",
|
|
16292
|
+
operation: record2.operation,
|
|
16293
|
+
phase: record2.phase,
|
|
16294
|
+
previous: record2.previous === null ? null : assertInferenceHostCredentialContext(record2.previous, config2),
|
|
16295
|
+
candidate: record2.candidate === null ? null : assertInferenceHostCredentialContext(record2.candidate, config2)
|
|
16296
|
+
};
|
|
16297
|
+
}
|
|
16298
|
+
async function clearInferenceHostCredentialContextTransition(config2) {
|
|
16299
|
+
await clearInferencePrivateFile(
|
|
16300
|
+
inferenceHostCredentialContextTransitionPath(config2),
|
|
16301
|
+
"Inference host credential context transition"
|
|
16302
|
+
);
|
|
16303
|
+
}
|
|
16304
|
+
async function clearInferenceHostCredentialContext(config2) {
|
|
16305
|
+
await clearInferencePrivateFile(
|
|
16306
|
+
inferenceHostCredentialContextPath(config2),
|
|
16307
|
+
"Inference host credential context"
|
|
16308
|
+
);
|
|
16309
|
+
}
|
|
16310
|
+
async function readInferenceHostCredentialContext(config2) {
|
|
16311
|
+
const raw = await readInferencePrivateFile(
|
|
16312
|
+
inferenceHostCredentialContextPath(config2),
|
|
16313
|
+
"Inference host credential context"
|
|
16314
|
+
);
|
|
16315
|
+
if (raw === null) return null;
|
|
16316
|
+
let value;
|
|
16317
|
+
try {
|
|
16318
|
+
value = JSON.parse(raw);
|
|
16319
|
+
} catch {
|
|
16320
|
+
throw new Error("Inference host credential context is not valid JSON.");
|
|
16321
|
+
}
|
|
16322
|
+
return assertInferenceHostCredentialContext(value, config2);
|
|
16323
|
+
}
|
|
16324
|
+
function assertInferenceHostCredentialContext(value, config2) {
|
|
16325
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
16326
|
+
throw new Error("Inference host credential context is invalid.");
|
|
16327
|
+
}
|
|
16328
|
+
const record2 = value;
|
|
16329
|
+
const expectedKeys = [
|
|
16330
|
+
"schema_version",
|
|
16331
|
+
"instance_name",
|
|
16332
|
+
"credential_store_mode",
|
|
16333
|
+
"credential_file_path"
|
|
16334
|
+
];
|
|
16335
|
+
if (Object.keys(record2).sort().join("\0") !== expectedKeys.sort().join("\0") || record2.schema_version !== "vtx_inference_credential_context_v1" || record2.instance_name !== config2.instanceName || record2.credential_store_mode !== "os" && record2.credential_store_mode !== "file" || (record2.credential_store_mode === "os" ? record2.credential_file_path !== null : typeof record2.credential_file_path !== "string" || !record2.credential_file_path)) {
|
|
16336
|
+
throw new Error("Inference host credential context is invalid.");
|
|
16337
|
+
}
|
|
16338
|
+
const credentialFilePath = record2.credential_store_mode === "file" ? requireNamedInstancePath(
|
|
16339
|
+
config2.instanceName,
|
|
16340
|
+
config2.instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? config2.baseDir : join(config2.baseDir, "instances", config2.instanceName),
|
|
16341
|
+
record2.credential_file_path,
|
|
16342
|
+
"Stored inference credential path"
|
|
16343
|
+
) : null;
|
|
16344
|
+
return {
|
|
16345
|
+
schema_version: "vtx_inference_credential_context_v1",
|
|
16346
|
+
instance_name: config2.instanceName,
|
|
16347
|
+
credential_store_mode: record2.credential_store_mode,
|
|
16348
|
+
credential_file_path: credentialFilePath
|
|
16349
|
+
};
|
|
16350
|
+
}
|
|
16248
16351
|
function assertLocalState(value) {
|
|
16249
16352
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
16250
16353
|
throw new Error("Inference host local state is invalid.");
|
|
@@ -16619,7 +16722,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
|
|
|
16619
16722
|
}
|
|
16620
16723
|
};
|
|
16621
16724
|
}
|
|
16622
|
-
var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity;
|
|
16725
|
+
var INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, defaultCurrentProcessIdentity, processLockObservationCache, inferenceProcessIdentitiesMatch, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity, inferenceHostCredentialContextPath, inferenceHostCredentialContextTransitionPath, credentialContextForConfig;
|
|
16623
16726
|
var init_config = __esm({
|
|
16624
16727
|
"lib/inference-host/config.ts"() {
|
|
16625
16728
|
"use strict";
|
|
@@ -17138,6 +17241,17 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
17138
17241
|
if (process.platform === "darwin") return "os:macos-keychain";
|
|
17139
17242
|
return `os:${process.platform}:unsupported`;
|
|
17140
17243
|
};
|
|
17244
|
+
inferenceHostCredentialContextPath = (config2) => join(
|
|
17245
|
+
config2.instanceName === DEFAULT_INFERENCE_HOST_INSTANCE ? config2.baseDir : join(config2.baseDir, "instances", config2.instanceName),
|
|
17246
|
+
"credential-context.json"
|
|
17247
|
+
);
|
|
17248
|
+
inferenceHostCredentialContextTransitionPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.pending`;
|
|
17249
|
+
credentialContextForConfig = (config2) => ({
|
|
17250
|
+
schema_version: "vtx_inference_credential_context_v1",
|
|
17251
|
+
instance_name: config2.instanceName,
|
|
17252
|
+
credential_store_mode: config2.credentialStoreMode,
|
|
17253
|
+
credential_file_path: config2.credentialStoreMode === "file" ? resolve(config2.credentialFilePath) : null
|
|
17254
|
+
});
|
|
17141
17255
|
}
|
|
17142
17256
|
});
|
|
17143
17257
|
|
|
@@ -31510,9 +31624,9 @@ ${result2.stderr}`)) {
|
|
|
31510
31624
|
const runtime = await this.waitForManifestApplied(restoredManifest);
|
|
31511
31625
|
return await this.status(runtime);
|
|
31512
31626
|
}
|
|
31513
|
-
async waitForManifestApplied(manifest, targetInstanceName) {
|
|
31627
|
+
async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts) {
|
|
31514
31628
|
let consecutiveReadyObservations = 0;
|
|
31515
|
-
for (let attempt = 0; attempt <
|
|
31629
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
31516
31630
|
let runtime = null;
|
|
31517
31631
|
try {
|
|
31518
31632
|
runtime = await readRuntimeAcrossAtomicReplacement(this.runtimePath());
|
|
@@ -31648,7 +31762,12 @@ ${cleanup.stderr}`)) {
|
|
|
31648
31762
|
previous,
|
|
31649
31763
|
targetInstanceName && previous.workers.some(
|
|
31650
31764
|
(worker) => worker.instance_name === targetInstanceName
|
|
31651
|
-
) ? targetInstanceName : void 0
|
|
31765
|
+
) ? targetInstanceName : void 0,
|
|
31766
|
+
// A failed generation may still be draining while the manager
|
|
31767
|
+
// restores the previous manifest. Never give rollback less than
|
|
31768
|
+
// a bounded 100-observation confirmation window, even when a
|
|
31769
|
+
// caller intentionally shortens forward-readiness checks.
|
|
31770
|
+
Math.max(this.reconcileWaitAttempts, 100)
|
|
31652
31771
|
);
|
|
31653
31772
|
} catch (rollbackError) {
|
|
31654
31773
|
throw new Error(
|
|
@@ -32288,7 +32407,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32288
32407
|
return { exitCode: 0, stdout: INFERENCE_HOST_HELP, stderr: "" };
|
|
32289
32408
|
}
|
|
32290
32409
|
const parsed = parseInferenceHostArgs(argv2, env);
|
|
32291
|
-
const config2 = await resolveInferenceHostCommandConfig(parsed, env);
|
|
32410
|
+
const config2 = await resolveInferenceHostCommandConfig(parsed, env, dependencies);
|
|
32292
32411
|
if (parsed.command === "login") {
|
|
32293
32412
|
return await login(config2, parsed, env, dependencies, warnings);
|
|
32294
32413
|
}
|
|
@@ -32355,7 +32474,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32355
32474
|
};
|
|
32356
32475
|
}
|
|
32357
32476
|
}
|
|
32358
|
-
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, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, 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, hasExplicitCredentialStoreConfiguration, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
|
|
32477
|
+
var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, restoreInferenceHostCredentialContext, recoverInferenceHostCredentialContextTransition, 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, hasExplicitCredentialStoreConfiguration, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
|
|
32359
32478
|
var init_cli = __esm({
|
|
32360
32479
|
"lib/inference-host/cli.ts"() {
|
|
32361
32480
|
"use strict";
|
|
@@ -32379,6 +32498,7 @@ var init_cli = __esm({
|
|
|
32379
32498
|
codexGuardianReceiptRoot = (config2) => `${config2.statePath}.codex-processes`;
|
|
32380
32499
|
revocationCheckpointPath = (config2) => `${config2.statePath}.revoke.json`;
|
|
32381
32500
|
foregroundHostLockPath = (config2) => `${config2.processLockPath}.foreground`;
|
|
32501
|
+
credentialLifecycleLockPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.lock`;
|
|
32382
32502
|
AGENT_HEARTBEAT_INTERVAL_MS = 3e3;
|
|
32383
32503
|
AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS = 9e3;
|
|
32384
32504
|
retryableAgentHeartbeatError = (error48) => {
|
|
@@ -32647,6 +32767,36 @@ Durable service:
|
|
|
32647
32767
|
};
|
|
32648
32768
|
};
|
|
32649
32769
|
configuredCredentialStore = (config2, dependencies, warn) => dependencies.createCredentialStore?.(config2, warn) ?? createConfiguredInferenceCredentialStore(config2, { warn });
|
|
32770
|
+
restoreInferenceHostCredentialContext = async (config2, previous) => {
|
|
32771
|
+
if (!previous) {
|
|
32772
|
+
await clearInferenceHostCredentialContext(config2);
|
|
32773
|
+
return;
|
|
32774
|
+
}
|
|
32775
|
+
await writeInferenceHostCredentialContext({
|
|
32776
|
+
...config2,
|
|
32777
|
+
credentialStoreMode: previous.credential_store_mode,
|
|
32778
|
+
credentialFilePath: previous.credential_file_path
|
|
32779
|
+
});
|
|
32780
|
+
};
|
|
32781
|
+
recoverInferenceHostCredentialContextTransition = async (config2, selectedEnv, dependencies) => {
|
|
32782
|
+
const transition = await readInferenceHostCredentialContextTransition(config2);
|
|
32783
|
+
if (!transition) return;
|
|
32784
|
+
const localState = await readInferenceHostLocalState(config2.statePath);
|
|
32785
|
+
const candidateRecovery = transition.candidate ? await configuredCredentialStore(
|
|
32786
|
+
resolveInferenceHostConfig({
|
|
32787
|
+
...selectedEnv,
|
|
32788
|
+
VTX_INFERENCE_HOST_CREDENTIAL_STORE: transition.candidate.credential_store_mode,
|
|
32789
|
+
VTX_INFERENCE_HOST_CREDENTIAL_FILE: transition.candidate.credential_file_path ?? void 0
|
|
32790
|
+
}),
|
|
32791
|
+
dependencies,
|
|
32792
|
+
() => void 0
|
|
32793
|
+
).readRecovery?.() ?? null : null;
|
|
32794
|
+
await restoreInferenceHostCredentialContext(
|
|
32795
|
+
config2,
|
|
32796
|
+
transition.operation === "selection" ? localState || candidateRecovery ? transition.candidate : transition.previous : transition.phase === "committed" && !localState ? null : transition.previous
|
|
32797
|
+
);
|
|
32798
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
32799
|
+
};
|
|
32650
32800
|
accountKeyForState = (state) => inferenceCredentialAccountKey({
|
|
32651
32801
|
issuer: state.issuer,
|
|
32652
32802
|
clientId: state.client_id,
|
|
@@ -32780,8 +32930,15 @@ Durable service:
|
|
|
32780
32930
|
}).run();
|
|
32781
32931
|
};
|
|
32782
32932
|
login = async (config2, parsed, env, dependencies, warnings) => {
|
|
32783
|
-
const
|
|
32933
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
32934
|
+
credentialLifecycleLockPath(config2)
|
|
32935
|
+
);
|
|
32936
|
+
let lock2 = null;
|
|
32937
|
+
let previousCredentialContext;
|
|
32938
|
+
let selectedStore = null;
|
|
32939
|
+
let localStateCommitted = false;
|
|
32784
32940
|
try {
|
|
32941
|
+
lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
|
|
32785
32942
|
if (await readRevocationCheckpoint(config2)) {
|
|
32786
32943
|
throw new Error("Inference host revocation recovery must finish before login.");
|
|
32787
32944
|
}
|
|
@@ -32791,18 +32948,41 @@ Durable service:
|
|
|
32791
32948
|
const store = configuredCredentialStore(config2, dependencies, (message) => {
|
|
32792
32949
|
warnings.push(message);
|
|
32793
32950
|
});
|
|
32951
|
+
selectedStore = store;
|
|
32952
|
+
if (!store.writeRecovery || !store.removeRecovery || !store.readRecovery) {
|
|
32953
|
+
throw new Error("Inference host credential store does not support durable login recovery.");
|
|
32954
|
+
}
|
|
32794
32955
|
if (await store.readRecovery?.()) {
|
|
32795
32956
|
throw new Error("Inference host credential recovery is required before login.");
|
|
32796
32957
|
}
|
|
32958
|
+
previousCredentialContext = await readInferenceHostCredentialContext(config2);
|
|
32959
|
+
await writeInferenceHostCredentialContextTransition(config2, previousCredentialContext);
|
|
32960
|
+
await writeInferenceHostCredentialContext(config2);
|
|
32797
32961
|
const keyPair = generateExternalInferenceEnvelopeKeyPair();
|
|
32798
32962
|
const hostId = randomUUID2();
|
|
32799
32963
|
const beginLogin = dependencies.beginLogin ?? beginInferenceOAuthLogin;
|
|
32964
|
+
const loginStore = {
|
|
32965
|
+
kind: store.kind,
|
|
32966
|
+
read: store.read.bind(store),
|
|
32967
|
+
remove: store.remove.bind(store),
|
|
32968
|
+
readRecovery: store.readRecovery.bind(store),
|
|
32969
|
+
writeRecovery: store.writeRecovery.bind(store),
|
|
32970
|
+
removeRecovery: store.removeRecovery.bind(store),
|
|
32971
|
+
write: async (accountKey, credential, options) => {
|
|
32972
|
+
await store.writeRecovery(accountKey, credential);
|
|
32973
|
+
try {
|
|
32974
|
+
await store.write(accountKey, credential, options);
|
|
32975
|
+
} catch (error48) {
|
|
32976
|
+
throw error48;
|
|
32977
|
+
}
|
|
32978
|
+
}
|
|
32979
|
+
};
|
|
32800
32980
|
const pending = await beginLogin({
|
|
32801
32981
|
apiUrl: config2.apiUrl,
|
|
32802
32982
|
hostId,
|
|
32803
32983
|
x25519PrivateKey: keyPair.private_key,
|
|
32804
32984
|
keyGeneration: 1,
|
|
32805
|
-
store
|
|
32985
|
+
store: loginStore
|
|
32806
32986
|
});
|
|
32807
32987
|
const emitStdout = dependencies.emitStdout ?? ((text) => {
|
|
32808
32988
|
process.stdout.write(text);
|
|
@@ -32846,6 +33026,7 @@ Waiting for approval...
|
|
|
32846
33026
|
store
|
|
32847
33027
|
});
|
|
32848
33028
|
remoteCleanupConfirmed = true;
|
|
33029
|
+
await store.removeRecovery?.();
|
|
32849
33030
|
} catch {
|
|
32850
33031
|
if (!store.writeRecovery) {
|
|
32851
33032
|
throw new Error(
|
|
@@ -32864,6 +33045,9 @@ Waiting for approval...
|
|
|
32864
33045
|
}
|
|
32865
33046
|
throw stateError;
|
|
32866
33047
|
}
|
|
33048
|
+
localStateCommitted = true;
|
|
33049
|
+
await store.removeRecovery?.();
|
|
33050
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
32867
33051
|
return {
|
|
32868
33052
|
exitCode: 0,
|
|
32869
33053
|
stdout: render({
|
|
@@ -32876,8 +33060,18 @@ Waiting for approval...
|
|
|
32876
33060
|
stderr: warnings.length > 0 ? `${warnings.join("\n")}
|
|
32877
33061
|
` : ""
|
|
32878
33062
|
};
|
|
33063
|
+
} catch (error48) {
|
|
33064
|
+
const recovery = await selectedStore?.readRecovery?.() ?? null;
|
|
33065
|
+
if (!recovery && !localStateCommitted && previousCredentialContext !== void 0) {
|
|
33066
|
+
await restoreInferenceHostCredentialContext(config2, previousCredentialContext);
|
|
33067
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
33068
|
+
} else if (recovery) {
|
|
33069
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
33070
|
+
}
|
|
33071
|
+
throw error48;
|
|
32879
33072
|
} finally {
|
|
32880
|
-
await lock2
|
|
33073
|
+
await lock2?.release();
|
|
33074
|
+
await credentialLock.release();
|
|
32881
33075
|
}
|
|
32882
33076
|
};
|
|
32883
33077
|
codexLogin = async (config2, parsed, env, dependencies) => {
|
|
@@ -33111,10 +33305,14 @@ Waiting for approval...
|
|
|
33111
33305
|
};
|
|
33112
33306
|
};
|
|
33113
33307
|
cleanupLogin = async (config2, parsed, dependencies, warnings, revoke) => {
|
|
33114
|
-
await
|
|
33115
|
-
|
|
33308
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
33309
|
+
credentialLifecycleLockPath(config2)
|
|
33310
|
+
);
|
|
33311
|
+
let lock2 = null;
|
|
33116
33312
|
let keeperLock = null;
|
|
33117
33313
|
try {
|
|
33314
|
+
await assertDurableServiceUninstalled(config2);
|
|
33315
|
+
lock2 = await acquireInferenceHostProcessLock(config2.processLockPath);
|
|
33118
33316
|
try {
|
|
33119
33317
|
keeperLock = await acquireInferenceHostProcessLock(foregroundHostLockPath(config2));
|
|
33120
33318
|
} catch (error48) {
|
|
@@ -33141,8 +33339,35 @@ Waiting for approval...
|
|
|
33141
33339
|
state,
|
|
33142
33340
|
oauthRecovery
|
|
33143
33341
|
);
|
|
33342
|
+
}
|
|
33343
|
+
let cleanupTransitionStaged = false;
|
|
33344
|
+
const stageCleanupTransition = async () => {
|
|
33345
|
+
if (cleanupTransitionStaged) return;
|
|
33346
|
+
const selectedContext = await readInferenceHostCredentialContext(config2);
|
|
33347
|
+
await writeInferenceHostCredentialContextTransition(
|
|
33348
|
+
config2,
|
|
33349
|
+
selectedContext,
|
|
33350
|
+
null,
|
|
33351
|
+
"cleanup",
|
|
33352
|
+
"prepared"
|
|
33353
|
+
);
|
|
33354
|
+
cleanupTransitionStaged = true;
|
|
33355
|
+
};
|
|
33356
|
+
const commitCleanupTransition = async () => {
|
|
33357
|
+
await writeInferenceHostCredentialContextTransition(
|
|
33358
|
+
config2,
|
|
33359
|
+
await readInferenceHostCredentialContext(config2),
|
|
33360
|
+
null,
|
|
33361
|
+
"cleanup",
|
|
33362
|
+
"committed"
|
|
33363
|
+
);
|
|
33364
|
+
};
|
|
33365
|
+
if (revocationCheckpoint) {
|
|
33366
|
+
await stageCleanupTransition();
|
|
33367
|
+
await commitCleanupTransition();
|
|
33144
33368
|
} else if (oauthRecovery) {
|
|
33145
33369
|
if (revoke) {
|
|
33370
|
+
await stageCleanupTransition();
|
|
33146
33371
|
const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
|
|
33147
33372
|
await (dependencies.revokeCredential ?? revokeInferenceCredential)({
|
|
33148
33373
|
metadata,
|
|
@@ -33157,9 +33382,12 @@ Waiting for approval...
|
|
|
33157
33382
|
config2.credentialStoreIdentity
|
|
33158
33383
|
);
|
|
33159
33384
|
await writeRevocationCheckpoint(config2, revocationCheckpoint);
|
|
33385
|
+
await commitCleanupTransition();
|
|
33160
33386
|
} else {
|
|
33387
|
+
await stageCleanupTransition();
|
|
33161
33388
|
await store.remove(oauthRecovery.accountKey);
|
|
33162
33389
|
await store.removeRecovery?.();
|
|
33390
|
+
await commitCleanupTransition();
|
|
33163
33391
|
}
|
|
33164
33392
|
} else if (state) {
|
|
33165
33393
|
const accountKey = accountKeyForState(state);
|
|
@@ -33169,6 +33397,7 @@ Waiting for approval...
|
|
|
33169
33397
|
if (!credential) {
|
|
33170
33398
|
throw new Error("Inference host credential is unavailable; remote revoke cannot be confirmed.");
|
|
33171
33399
|
}
|
|
33400
|
+
await stageCleanupTransition();
|
|
33172
33401
|
const metadata = await (dependencies.discoverOAuth ?? discoverInferenceOAuth)(config2.apiUrl);
|
|
33173
33402
|
await (dependencies.revokeCredential ?? revokeInferenceCredential)({
|
|
33174
33403
|
metadata,
|
|
@@ -33183,11 +33412,16 @@ Waiting for approval...
|
|
|
33183
33412
|
config2.credentialStoreIdentity
|
|
33184
33413
|
);
|
|
33185
33414
|
await writeRevocationCheckpoint(config2, revocationCheckpoint);
|
|
33415
|
+
await commitCleanupTransition();
|
|
33186
33416
|
} else {
|
|
33417
|
+
await stageCleanupTransition();
|
|
33187
33418
|
await store.remove(accountKey);
|
|
33419
|
+
await commitCleanupTransition();
|
|
33188
33420
|
}
|
|
33189
33421
|
} else if (!revoke && store.kind === "file") {
|
|
33422
|
+
await stageCleanupTransition();
|
|
33190
33423
|
await store.remove("");
|
|
33424
|
+
await commitCleanupTransition();
|
|
33191
33425
|
} else if (revoke) {
|
|
33192
33426
|
throw new Error("Inference host is not logged in; there is no exact grant to revoke.");
|
|
33193
33427
|
}
|
|
@@ -33207,14 +33441,18 @@ Waiting for approval...
|
|
|
33207
33441
|
{ cause: error48 }
|
|
33208
33442
|
);
|
|
33209
33443
|
}
|
|
33210
|
-
await clearLocalRuntimeArtifacts(config2);
|
|
33211
33444
|
await store.remove(revocationCheckpoint.account_key);
|
|
33212
33445
|
if (revocationCheckpoint.remove_credential_recovery) {
|
|
33213
33446
|
await store.removeRecovery?.();
|
|
33214
33447
|
}
|
|
33448
|
+
await (dependencies.clearLocalRuntimeArtifacts ?? clearLocalRuntimeArtifacts)(config2);
|
|
33449
|
+
await clearInferenceHostCredentialContext(config2);
|
|
33215
33450
|
await clearRevocationCheckpoint(config2);
|
|
33451
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
33216
33452
|
} else {
|
|
33217
|
-
await clearLocalRuntimeArtifacts(config2);
|
|
33453
|
+
await (dependencies.clearLocalRuntimeArtifacts ?? clearLocalRuntimeArtifacts)(config2);
|
|
33454
|
+
await clearInferenceHostCredentialContext(config2);
|
|
33455
|
+
await clearInferenceHostCredentialContextTransition(config2);
|
|
33218
33456
|
}
|
|
33219
33457
|
return {
|
|
33220
33458
|
exitCode: 0,
|
|
@@ -33228,7 +33466,8 @@ Waiting for approval...
|
|
|
33228
33466
|
};
|
|
33229
33467
|
} finally {
|
|
33230
33468
|
await keeperLock?.release();
|
|
33231
|
-
await lock2
|
|
33469
|
+
await lock2?.release();
|
|
33470
|
+
await credentialLock.release();
|
|
33232
33471
|
}
|
|
33233
33472
|
};
|
|
33234
33473
|
runHost = async (config2, parsed, env, dependencies, warnings) => {
|
|
@@ -33775,44 +34014,52 @@ Waiting for approval...
|
|
|
33775
34014
|
dependencies.serviceDependencies
|
|
33776
34015
|
) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
|
|
33777
34016
|
if (action === "install") {
|
|
33778
|
-
const
|
|
33779
|
-
|
|
33780
|
-
|
|
33781
|
-
|
|
33782
|
-
|
|
33783
|
-
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
|
|
33787
|
-
|
|
33788
|
-
|
|
33789
|
-
|
|
33790
|
-
|
|
33791
|
-
|
|
33792
|
-
|
|
33793
|
-
|
|
33794
|
-
|
|
33795
|
-
|
|
33796
|
-
|
|
33797
|
-
|
|
33798
|
-
|
|
33799
|
-
|
|
33800
|
-
|
|
33801
|
-
|
|
33802
|
-
|
|
33803
|
-
|
|
33804
|
-
|
|
33805
|
-
|
|
33806
|
-
|
|
33807
|
-
|
|
33808
|
-
|
|
33809
|
-
|
|
33810
|
-
|
|
33811
|
-
|
|
33812
|
-
|
|
33813
|
-
|
|
34017
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34018
|
+
credentialLifecycleLockPath(config2)
|
|
34019
|
+
);
|
|
34020
|
+
try {
|
|
34021
|
+
const adapter = parsed.adapter || "codex";
|
|
34022
|
+
if (adapter !== "codex") {
|
|
34023
|
+
throw new Error(
|
|
34024
|
+
`Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
|
|
34025
|
+
);
|
|
34026
|
+
}
|
|
34027
|
+
const state = await readInferenceHostLocalState(config2.statePath);
|
|
34028
|
+
if (!state) throw new Error("Run vtx inference-host login before installing the service.");
|
|
34029
|
+
const store = configuredCredentialStore(config2, dependencies, (message) => warnings.push(message));
|
|
34030
|
+
const credential = await store.read(accountKeyForState(state));
|
|
34031
|
+
if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
|
|
34032
|
+
assertCredentialMatchesState2(state, credential);
|
|
34033
|
+
if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
|
|
34034
|
+
throw new Error("Automated Codex service does not accept agent model or effort options.");
|
|
34035
|
+
}
|
|
34036
|
+
const auth = await inspectCodexAuthentication(config2);
|
|
34037
|
+
if (!auth.present || !auth.private) {
|
|
34038
|
+
throw new Error("Run vtx inference-host codex-login before installing the automated Codex service.");
|
|
34039
|
+
}
|
|
34040
|
+
const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
|
|
34041
|
+
const preflight = await (dependencies.preflightCodex ?? preflightCodexSubscription)({
|
|
34042
|
+
binary,
|
|
34043
|
+
codexHome: config2.codexHomePath,
|
|
34044
|
+
deadlineAtMs: Date.now() + 3e4
|
|
34045
|
+
});
|
|
34046
|
+
const status = await manager.install({
|
|
34047
|
+
adapter: "codex",
|
|
34048
|
+
displayName: parsed.displayName,
|
|
34049
|
+
maxConcurrency: parsed.maxConcurrency,
|
|
34050
|
+
authenticatedAccountEmail: preflight?.authenticated_account_email ?? null,
|
|
34051
|
+
authenticatedAccountPlan: preflight?.authenticated_account_plan ?? null
|
|
34052
|
+
});
|
|
34053
|
+
await writeInferenceHostCredentialContext(config2);
|
|
34054
|
+
return {
|
|
34055
|
+
exitCode: 0,
|
|
34056
|
+
stdout: render({ status: "service_installed", ...status }, parsed.json),
|
|
34057
|
+
stderr: warnings.length > 0 ? `${warnings.join("\n")}
|
|
33814
34058
|
` : ""
|
|
33815
|
-
|
|
34059
|
+
};
|
|
34060
|
+
} finally {
|
|
34061
|
+
await credentialLock.release();
|
|
34062
|
+
}
|
|
33816
34063
|
}
|
|
33817
34064
|
if (parsed.adapter || parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
|
|
33818
34065
|
throw new Error("Service adapter and model options are accepted only by service install.");
|
|
@@ -33839,36 +34086,98 @@ Waiting for approval...
|
|
|
33839
34086
|
return { exitCode: lifecycleMatches ? 0 : 1, stdout: render(status, parsed.json), stderr: "" };
|
|
33840
34087
|
}
|
|
33841
34088
|
if (action === "uninstall") {
|
|
33842
|
-
const
|
|
33843
|
-
|
|
34089
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34090
|
+
credentialLifecycleLockPath(config2)
|
|
34091
|
+
);
|
|
34092
|
+
try {
|
|
34093
|
+
if (parsed.instanceExplicit) {
|
|
34094
|
+
const retained = await readInferenceHostCredentialContext(config2);
|
|
34095
|
+
const retainedIdentity = retained ? resolveInferenceHostConfig({
|
|
34096
|
+
...env,
|
|
34097
|
+
VTX_INFERENCE_HOST_INSTANCE: config2.instanceName,
|
|
34098
|
+
VTX_INFERENCE_HOST_CREDENTIAL_STORE: retained.credential_store_mode,
|
|
34099
|
+
VTX_INFERENCE_HOST_CREDENTIAL_FILE: retained.credential_file_path ?? void 0
|
|
34100
|
+
}).credentialStoreIdentity : null;
|
|
34101
|
+
if (retainedIdentity !== config2.credentialStoreIdentity) {
|
|
34102
|
+
await writeInferenceHostCredentialContext(config2);
|
|
34103
|
+
}
|
|
34104
|
+
}
|
|
34105
|
+
const status = parsed.instanceExplicit ? await manager.uninstallInstance(config2.instanceName) : await manager.uninstall();
|
|
34106
|
+
return { exitCode: 0, stdout: render({ status: "service_uninstalled", ...status }, parsed.json), stderr: "" };
|
|
34107
|
+
} finally {
|
|
34108
|
+
await credentialLock.release();
|
|
34109
|
+
}
|
|
33844
34110
|
}
|
|
33845
34111
|
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
|
|
33846
34112
|
};
|
|
33847
34113
|
hasExplicitCredentialStoreConfiguration = (env) => Boolean(
|
|
33848
34114
|
String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "").trim() || String(env.VTX_INFERENCE_HOST_CREDENTIAL_FILE || "").trim()
|
|
33849
34115
|
);
|
|
33850
|
-
|
|
33851
|
-
|
|
34116
|
+
credentialEnvironmentForIdentity = (identity) => {
|
|
34117
|
+
if (identity.startsWith("file:")) {
|
|
34118
|
+
const path = identity.slice("file:".length);
|
|
34119
|
+
if (!path) throw new Error("Inference host credential store identity is invalid.");
|
|
34120
|
+
return {
|
|
34121
|
+
VTX_INFERENCE_HOST_CREDENTIAL_STORE: "file",
|
|
34122
|
+
VTX_INFERENCE_HOST_CREDENTIAL_FILE: path
|
|
34123
|
+
};
|
|
34124
|
+
}
|
|
34125
|
+
if (identity.startsWith("os:")) {
|
|
34126
|
+
return {
|
|
34127
|
+
VTX_INFERENCE_HOST_CREDENTIAL_STORE: "os",
|
|
34128
|
+
VTX_INFERENCE_HOST_CREDENTIAL_FILE: void 0
|
|
34129
|
+
};
|
|
34130
|
+
}
|
|
34131
|
+
throw new Error("Inference host credential store identity is invalid.");
|
|
34132
|
+
};
|
|
34133
|
+
commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "uninstall");
|
|
34134
|
+
commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "login" || parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install" || parsed.command === "service" && parsed.serviceAction === "uninstall" && parsed.instanceExplicit;
|
|
34135
|
+
resolveInferenceHostCommandConfig = async (parsed, env, dependencies) => {
|
|
33852
34136
|
const selectedEnv = {
|
|
33853
34137
|
...env,
|
|
33854
34138
|
VTX_INFERENCE_HOST_INSTANCE: parsed.instanceName
|
|
33855
34139
|
};
|
|
33856
34140
|
const baseConfig = resolveInferenceHostConfig(selectedEnv);
|
|
33857
|
-
if (!commandUsesInstalledServiceCredentials(parsed)
|
|
33858
|
-
|
|
34141
|
+
if (!commandUsesInstalledServiceCredentials(parsed)) return baseConfig;
|
|
34142
|
+
if (await readInferenceHostCredentialContextTransition(baseConfig)) {
|
|
34143
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34144
|
+
credentialLifecycleLockPath(baseConfig)
|
|
34145
|
+
);
|
|
34146
|
+
try {
|
|
34147
|
+
await recoverInferenceHostCredentialContextTransition(baseConfig, selectedEnv, dependencies);
|
|
34148
|
+
} finally {
|
|
34149
|
+
await credentialLock.release();
|
|
34150
|
+
}
|
|
33859
34151
|
}
|
|
33860
34152
|
const manifestPath = `${baseConfig.supervisorStatePath}.service.json`;
|
|
33861
34153
|
const manifest = await readInferenceHostServiceManifest(manifestPath);
|
|
33862
|
-
|
|
34154
|
+
const installedEnvironment = manifest?.workers.find(
|
|
34155
|
+
(worker) => worker.instance_name === baseConfig.instanceName
|
|
34156
|
+
)?.runtime_environment;
|
|
34157
|
+
const persistedContext = installedEnvironment ? null : await readInferenceHostCredentialContext(baseConfig);
|
|
34158
|
+
const revocationCheckpoint = !installedEnvironment && !persistedContext && parsed.command === "revoke" ? await readRevocationCheckpoint(baseConfig) : null;
|
|
34159
|
+
const retainedEnvironment = installedEnvironment ?? (persistedContext ? {
|
|
34160
|
+
VTX_INFERENCE_HOST_CREDENTIAL_STORE: persistedContext.credential_store_mode,
|
|
34161
|
+
VTX_INFERENCE_HOST_CREDENTIAL_FILE: persistedContext.credential_file_path ?? void 0
|
|
34162
|
+
} : revocationCheckpoint ? credentialEnvironmentForIdentity(revocationCheckpoint.credential_store_identity) : null);
|
|
34163
|
+
if (!retainedEnvironment) return baseConfig;
|
|
33863
34164
|
const installedConfig = resolveInferenceHostConfig({
|
|
33864
34165
|
...selectedEnv,
|
|
33865
|
-
...
|
|
34166
|
+
...retainedEnvironment
|
|
33866
34167
|
});
|
|
33867
34168
|
if (resolve5(installedConfig.statePath) !== resolve5(baseConfig.statePath)) {
|
|
33868
34169
|
throw new Error(
|
|
33869
34170
|
"Installed inference-host service manifest does not match the requested local state path."
|
|
33870
34171
|
);
|
|
33871
34172
|
}
|
|
34173
|
+
if (hasExplicitCredentialStoreConfiguration(env)) {
|
|
34174
|
+
if (commandRequiresRetainedCredentialStore(parsed) && baseConfig.credentialStoreIdentity !== installedConfig.credentialStoreIdentity) {
|
|
34175
|
+
throw new Error(
|
|
34176
|
+
"Explicit inference credential store does not match the retained store for this host. Log out or revoke with the retained store before switching."
|
|
34177
|
+
);
|
|
34178
|
+
}
|
|
34179
|
+
return baseConfig;
|
|
34180
|
+
}
|
|
33872
34181
|
return installedConfig;
|
|
33873
34182
|
};
|
|
33874
34183
|
}
|