@vtxmacro/cli 2026.8.39 → 2026.8.41
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 +49 -13
- package/bin/vtx.js +1215 -108
- package/package.json +4 -2
package/bin/vtx.js
CHANGED
|
@@ -38,9 +38,13 @@ 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.41",
|
|
42
42
|
codex_package_name: "@openai/codex",
|
|
43
43
|
codex_version: "0.147.0",
|
|
44
|
+
copilot_sdk_package_name: "@github/copilot-sdk",
|
|
45
|
+
copilot_sdk_version: "1.0.0-beta.8",
|
|
46
|
+
copilot_cli_package_name: "@github/copilot",
|
|
47
|
+
copilot_cli_version: "1.0.80",
|
|
44
48
|
platforms: {
|
|
45
49
|
"linux-x64": {
|
|
46
50
|
package_name: "@openai/codex-linux-x64",
|
|
@@ -14725,6 +14729,7 @@ var init_external_inference_contract = __esm({
|
|
|
14725
14729
|
key_generation: generationSchema,
|
|
14726
14730
|
adapter: externalInferenceAdapterIdSchema,
|
|
14727
14731
|
display_name: displayNameSchema,
|
|
14732
|
+
authenticated_account_identity: safeCodeSchema.nullable().optional(),
|
|
14728
14733
|
authenticated_account_email: authenticatedAccountEmailSchema.nullable().optional(),
|
|
14729
14734
|
authenticated_account_plan: safeCodeSchema.nullable().optional(),
|
|
14730
14735
|
protocol_version: protocolVersionSchema,
|
|
@@ -15644,6 +15649,7 @@ var init_external_inference_contract = __esm({
|
|
|
15644
15649
|
hostStatusReadSchema = external_exports.strictObject({
|
|
15645
15650
|
host_id: identifierSchema,
|
|
15646
15651
|
display_name: displayNameSchema,
|
|
15652
|
+
authenticated_account_identity: safeCodeSchema.nullable().optional(),
|
|
15647
15653
|
authenticated_account_email: authenticatedAccountEmailSchema.nullable().optional(),
|
|
15648
15654
|
authenticated_account_plan: safeCodeSchema.nullable().optional(),
|
|
15649
15655
|
adapter: externalInferenceAdapterIdSchema,
|
|
@@ -18179,7 +18185,7 @@ var init_mcp_client = __esm({
|
|
|
18179
18185
|
if (!host) return invalidBoundResult(name);
|
|
18180
18186
|
if (host.host_id !== request.host_id || host.host_generation !== request.host_generation || host.advertisement_generation !== request.advertisement_generation || host.key_generation !== request.key_generation || request.adapter !== void 0 && host.adapter !== request.adapter) invalidBoundResult(name);
|
|
18181
18187
|
if (name !== "inference.host.heartbeat") {
|
|
18182
|
-
if (host.display_name !== request.display_name || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== request.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (request.authenticated_account_plan ?? null) || !exactJson(host.models, request.models) || host.status === "revoked") invalidBoundResult(name);
|
|
18188
|
+
if (host.display_name !== request.display_name || host.authenticated_account_identity !== void 0 && host.authenticated_account_identity !== null && host.authenticated_account_identity !== request.authenticated_account_identity || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== request.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (request.authenticated_account_plan ?? null) || !exactJson(host.models, request.models) || host.status === "revoked") invalidBoundResult(name);
|
|
18183
18189
|
return;
|
|
18184
18190
|
}
|
|
18185
18191
|
const expectedStatus = request.status === "healthy" ? "online" : request.status;
|
|
@@ -19478,8 +19484,8 @@ async function preflightCodexSubscription(options) {
|
|
|
19478
19484
|
});
|
|
19479
19485
|
}
|
|
19480
19486
|
const rateLimits = await session.readAccountRateLimits(options.deadlineAtMs, options.signal);
|
|
19481
|
-
const
|
|
19482
|
-
if (
|
|
19487
|
+
const modelCapabilities2 = await session.readModelCapabilities(options.deadlineAtMs, options.signal);
|
|
19488
|
+
if (modelCapabilities2.length === 0) {
|
|
19483
19489
|
throw new CodexAppServerError({
|
|
19484
19490
|
message: "Codex did not return any visible models with supported reasoning efforts.",
|
|
19485
19491
|
category: "model",
|
|
@@ -19491,7 +19497,7 @@ async function preflightCodexSubscription(options) {
|
|
|
19491
19497
|
authenticated_account_email: account.authenticatedChatGptAccount.email,
|
|
19492
19498
|
authenticated_account_plan: account.authenticatedChatGptAccount.planType,
|
|
19493
19499
|
rate_limits: rateLimits,
|
|
19494
|
-
model_capabilities:
|
|
19500
|
+
model_capabilities: modelCapabilities2
|
|
19495
19501
|
};
|
|
19496
19502
|
} finally {
|
|
19497
19503
|
await session.close();
|
|
@@ -21473,10 +21479,10 @@ import {
|
|
|
21473
21479
|
realpath as realpath3,
|
|
21474
21480
|
rm as rm3
|
|
21475
21481
|
} from "node:fs/promises";
|
|
21476
|
-
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
21482
|
+
import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
|
|
21477
21483
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
21478
21484
|
import { isAbsolute as isAbsolute3, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
21479
|
-
var MAX_PROMPT_BYTES2, CODEX_MODEL_NAME_PATTERN, CODEX_REASONING_EFFORT_PATTERN, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, assertAttemptActive, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
|
|
21485
|
+
var MAX_PROMPT_BYTES2, CODEX_MODEL_NAME_PATTERN, CODEX_REASONING_EFFORT_PATTERN, codexRecoveryCheckpointProcessFingerprint, proveCodexSameBootQuiescence, tokenUsageReceiptSchema, turnResultReceiptSchema, terminalReceiptSchema, recoveryCheckpointSchema, recoveryFileSchema, FileCodexAttemptRecoveryStore, utf8Bytes, assertAttemptActive, tomlString, permissionConfig, ensureDedicatedCodexHome, createIsolatedCodexAttemptResources, createIsolatedCodexHostResources, validateAttemptInput, isStrictDescendant, assertRecoveryResourceScope, removeRecoveredThread, confirmGuardianTerminatedForRecovery, reconcileCodexAttemptRecovery, CodexSubscriptionAdapter;
|
|
21480
21486
|
var init_codex_adapter = __esm({
|
|
21481
21487
|
"lib/inference-host/codex-adapter.ts"() {
|
|
21482
21488
|
"use strict";
|
|
@@ -21487,6 +21493,34 @@ var init_codex_adapter = __esm({
|
|
|
21487
21493
|
MAX_PROMPT_BYTES2 = 3e5;
|
|
21488
21494
|
CODEX_MODEL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/u;
|
|
21489
21495
|
CODEX_REASONING_EFFORT_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/u;
|
|
21496
|
+
codexRecoveryCheckpointProcessFingerprint = (checkpoint) => createHash4("sha256").update(JSON.stringify({
|
|
21497
|
+
attemptId: checkpoint.attemptId,
|
|
21498
|
+
processToken: checkpoint.processToken,
|
|
21499
|
+
processReceiptPath: checkpoint.processReceiptPath,
|
|
21500
|
+
processState: checkpoint.processState,
|
|
21501
|
+
bootIdentity: checkpoint.bootIdentity ?? null,
|
|
21502
|
+
cleanupConfirmed: checkpoint.cleanupConfirmed
|
|
21503
|
+
})).digest("hex");
|
|
21504
|
+
proveCodexSameBootQuiescence = async (options) => {
|
|
21505
|
+
const readBootIdentity = options.readBootIdentity ?? readInferenceSystemBootIdentity;
|
|
21506
|
+
const bootIdentityBefore = await readBootIdentity();
|
|
21507
|
+
await options.confirmNoManagedCodexProcesses();
|
|
21508
|
+
const bootIdentityAfter = await readBootIdentity();
|
|
21509
|
+
if (bootIdentityAfter !== bootIdentityBefore) {
|
|
21510
|
+
throw new Error("System boot identity changed during Codex recovery process inspection.");
|
|
21511
|
+
}
|
|
21512
|
+
return {
|
|
21513
|
+
schemaVersion: "vtx_codex_same_boot_quiescence_v1",
|
|
21514
|
+
bootIdentity: bootIdentityAfter,
|
|
21515
|
+
observedAtMs: (options.now ?? Date.now)(),
|
|
21516
|
+
checkpointFingerprints: Object.fromEntries(
|
|
21517
|
+
Object.entries(options.attempts).map(([attemptId, checkpoint]) => [
|
|
21518
|
+
attemptId,
|
|
21519
|
+
codexRecoveryCheckpointProcessFingerprint(checkpoint)
|
|
21520
|
+
])
|
|
21521
|
+
)
|
|
21522
|
+
};
|
|
21523
|
+
};
|
|
21490
21524
|
tokenUsageReceiptSchema = external_exports.strictObject({
|
|
21491
21525
|
inputTokens: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
21492
21526
|
cachedInputTokens: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
@@ -21959,11 +21993,20 @@ var init_codex_adapter = __esm({
|
|
|
21959
21993
|
throw new Error("Codex cross-boot recovery receipt ownership is unconfirmed.");
|
|
21960
21994
|
}
|
|
21961
21995
|
} else {
|
|
21962
|
-
await
|
|
21963
|
-
|
|
21964
|
-
|
|
21965
|
-
|
|
21966
|
-
|
|
21996
|
+
const receipt = await readCodexGuardianReceipt(guardian.receiptPath);
|
|
21997
|
+
if (receipt === null && options.sameBootQuiescenceProof) {
|
|
21998
|
+
const proof = options.sameBootQuiescenceProof;
|
|
21999
|
+
const expectedFingerprint = proof.checkpointFingerprints[checkpoint.attemptId];
|
|
22000
|
+
if (proof.schemaVersion !== "vtx_codex_same_boot_quiescence_v1" || proof.bootIdentity !== currentBootIdentity || checkpoint.bootIdentity !== currentBootIdentity || !Number.isFinite(proof.observedAtMs) || proof.observedAtMs > Date.now() + 1e3 || Date.now() - proof.observedAtMs > 3e4 || expectedFingerprint !== codexRecoveryCheckpointProcessFingerprint(checkpoint)) {
|
|
22001
|
+
throw new Error("Codex same-boot recovery process fence is invalid or stale.");
|
|
22002
|
+
}
|
|
22003
|
+
} else {
|
|
22004
|
+
await confirmGuardianTerminatedForRecovery(
|
|
22005
|
+
checkpoint,
|
|
22006
|
+
guardian,
|
|
22007
|
+
options.deadlineMs ?? 5e3
|
|
22008
|
+
);
|
|
22009
|
+
}
|
|
21967
22010
|
}
|
|
21968
22011
|
assertRecoveryResourceScope(checkpoint);
|
|
21969
22012
|
await removeRecoveredThread(checkpoint, options.codexHome);
|
|
@@ -22607,11 +22650,458 @@ var init_codex_adapter = __esm({
|
|
|
22607
22650
|
}
|
|
22608
22651
|
});
|
|
22609
22652
|
|
|
22653
|
+
// lib/inference-host/codex-recovery-process.ts
|
|
22654
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
22655
|
+
var normalizedWindowsPath, classifyCodexRecoveryProcesses, WINDOWS_PROCESS_QUERY, queryWindowsProcesses, confirmNoVtxManagedCodexProcesses;
|
|
22656
|
+
var init_codex_recovery_process = __esm({
|
|
22657
|
+
"lib/inference-host/codex-recovery-process.ts"() {
|
|
22658
|
+
"use strict";
|
|
22659
|
+
normalizedWindowsPath = (value) => value.replaceAll("\\", "/").replaceAll(/\/+$/gu, "").toLowerCase();
|
|
22660
|
+
classifyCodexRecoveryProcesses = (processes, pinnedBinaryPath) => {
|
|
22661
|
+
const pinned = normalizedWindowsPath(pinnedBinaryPath);
|
|
22662
|
+
let managedProcessCount = 0;
|
|
22663
|
+
let ambiguousProcessCount = 0;
|
|
22664
|
+
for (const process3 of processes) {
|
|
22665
|
+
const name = process3.name.trim().toLowerCase();
|
|
22666
|
+
if (name === "codex.exe") {
|
|
22667
|
+
if (!process3.executable_path || !process3.command_line || !process3.creation_date) {
|
|
22668
|
+
ambiguousProcessCount += 1;
|
|
22669
|
+
continue;
|
|
22670
|
+
}
|
|
22671
|
+
const executable = normalizedWindowsPath(process3.executable_path);
|
|
22672
|
+
const packageOwned = executable === pinned || executable.includes("/node_modules/@vtxmacro/") && executable.includes("/node_modules/@openai/codex-win32-x64/") || executable.includes("/node_modules/@vtxmacro/.cli-") && executable.includes("/node_modules/@openai/codex-win32-x64/");
|
|
22673
|
+
if (packageOwned) managedProcessCount += 1;
|
|
22674
|
+
continue;
|
|
22675
|
+
}
|
|
22676
|
+
if (name !== "node.exe") continue;
|
|
22677
|
+
if (!process3.executable_path || !process3.command_line || !process3.creation_date) {
|
|
22678
|
+
ambiguousProcessCount += 1;
|
|
22679
|
+
continue;
|
|
22680
|
+
}
|
|
22681
|
+
if (process3.command_line.includes("--input-type=module") && process3.command_line.includes("vtx_codex_guardian_v1") && process3.command_line.includes("processToken") && process3.command_line.includes("receiptPath")) {
|
|
22682
|
+
managedProcessCount += 1;
|
|
22683
|
+
}
|
|
22684
|
+
}
|
|
22685
|
+
return {
|
|
22686
|
+
managed_process_count: managedProcessCount,
|
|
22687
|
+
ambiguous_process_count: ambiguousProcessCount
|
|
22688
|
+
};
|
|
22689
|
+
};
|
|
22690
|
+
WINDOWS_PROCESS_QUERY = String.raw`
|
|
22691
|
+
$ErrorActionPreference = 'Stop'
|
|
22692
|
+
$items = @(Get-CimInstance Win32_Process | Where-Object { $_.Name -in @('node.exe', 'codex.exe') } | ForEach-Object {
|
|
22693
|
+
[ordered]@{
|
|
22694
|
+
process_id = [int]$_.ProcessId
|
|
22695
|
+
name = [string]$_.Name
|
|
22696
|
+
executable_path = if ($null -eq $_.ExecutablePath) { $null } else { [string]$_.ExecutablePath }
|
|
22697
|
+
command_line = if ($null -eq $_.CommandLine) { $null } else { [string]$_.CommandLine }
|
|
22698
|
+
creation_date = if ($null -eq $_.CreationDate) { $null } else { [string]$_.CreationDate }
|
|
22699
|
+
}
|
|
22700
|
+
})
|
|
22701
|
+
[Console]::Out.Write(($items | ConvertTo-Json -Depth 3 -Compress))
|
|
22702
|
+
`;
|
|
22703
|
+
queryWindowsProcesses = async () => {
|
|
22704
|
+
const args = [
|
|
22705
|
+
"-NoLogo",
|
|
22706
|
+
"-NoProfile",
|
|
22707
|
+
"-NonInteractive",
|
|
22708
|
+
"-EncodedCommand",
|
|
22709
|
+
Buffer.from(WINDOWS_PROCESS_QUERY, "utf16le").toString("base64")
|
|
22710
|
+
];
|
|
22711
|
+
const result2 = await new Promise(
|
|
22712
|
+
(resolvePromise, reject) => {
|
|
22713
|
+
const child = spawn5("powershell.exe", args, {
|
|
22714
|
+
windowsHide: true,
|
|
22715
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22716
|
+
});
|
|
22717
|
+
let stdout = "";
|
|
22718
|
+
let stderr = "";
|
|
22719
|
+
child.stdout.on("data", (chunk) => {
|
|
22720
|
+
stdout = `${stdout}${chunk.toString("utf8")}`.slice(-4e6);
|
|
22721
|
+
});
|
|
22722
|
+
child.stderr.on("data", (chunk) => {
|
|
22723
|
+
stderr = `${stderr}${chunk.toString("utf8")}`.slice(-4096);
|
|
22724
|
+
});
|
|
22725
|
+
child.once("error", reject);
|
|
22726
|
+
child.once("close", (code) => {
|
|
22727
|
+
if (code !== 0) {
|
|
22728
|
+
reject(new Error("Windows process ownership inventory failed."));
|
|
22729
|
+
return;
|
|
22730
|
+
}
|
|
22731
|
+
resolvePromise({ exitCode: code ?? 1, stdout });
|
|
22732
|
+
});
|
|
22733
|
+
}
|
|
22734
|
+
);
|
|
22735
|
+
if (result2.exitCode !== 0) throw new Error("Windows process ownership inventory failed.");
|
|
22736
|
+
let parsed;
|
|
22737
|
+
try {
|
|
22738
|
+
parsed = result2.stdout.trim() ? JSON.parse(result2.stdout) : [];
|
|
22739
|
+
} catch {
|
|
22740
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22741
|
+
}
|
|
22742
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
22743
|
+
return entries.map((entry) => {
|
|
22744
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
22745
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22746
|
+
}
|
|
22747
|
+
const record2 = entry;
|
|
22748
|
+
if (!Number.isSafeInteger(record2.process_id) || Number(record2.process_id) < 1 || typeof record2.name !== "string" || record2.executable_path !== null && typeof record2.executable_path !== "string" || record2.command_line !== null && typeof record2.command_line !== "string" || record2.creation_date !== null && typeof record2.creation_date !== "string") {
|
|
22749
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22750
|
+
}
|
|
22751
|
+
return {
|
|
22752
|
+
process_id: Number(record2.process_id),
|
|
22753
|
+
name: record2.name,
|
|
22754
|
+
executable_path: record2.executable_path,
|
|
22755
|
+
command_line: record2.command_line,
|
|
22756
|
+
creation_date: record2.creation_date
|
|
22757
|
+
};
|
|
22758
|
+
});
|
|
22759
|
+
};
|
|
22760
|
+
confirmNoVtxManagedCodexProcesses = async (options) => {
|
|
22761
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
22762
|
+
throw new Error("Same-boot service recovery is currently supported only on Windows.");
|
|
22763
|
+
}
|
|
22764
|
+
const queryProcesses = options.queryProcesses ?? queryWindowsProcesses;
|
|
22765
|
+
const sleep4 = options.sleep ?? (async (milliseconds) => {
|
|
22766
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
22767
|
+
});
|
|
22768
|
+
for (let observation = 0; observation < 2; observation += 1) {
|
|
22769
|
+
const classification = classifyCodexRecoveryProcesses(
|
|
22770
|
+
await queryProcesses(),
|
|
22771
|
+
options.pinnedBinaryPath
|
|
22772
|
+
);
|
|
22773
|
+
if (classification.ambiguous_process_count > 0) {
|
|
22774
|
+
throw new Error(
|
|
22775
|
+
"Codex process ownership could not be inspected completely; recovery evidence was preserved."
|
|
22776
|
+
);
|
|
22777
|
+
}
|
|
22778
|
+
if (classification.managed_process_count > 0) {
|
|
22779
|
+
throw new Error(
|
|
22780
|
+
"VTX-managed Codex automation is still running; recovery evidence was preserved."
|
|
22781
|
+
);
|
|
22782
|
+
}
|
|
22783
|
+
if (observation === 0) await sleep4(250);
|
|
22784
|
+
}
|
|
22785
|
+
};
|
|
22786
|
+
}
|
|
22787
|
+
});
|
|
22788
|
+
|
|
22789
|
+
// lib/inference-host/copilot-adapter.ts
|
|
22790
|
+
import { mkdtemp as mkdtemp2, rm as rm4 } from "node:fs/promises";
|
|
22791
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
22792
|
+
import { join as join5 } from "node:path";
|
|
22793
|
+
import { CopilotClient } from "@github/copilot-sdk";
|
|
22794
|
+
var COPILOT_SDK_VERSION, MAX_RESULT_BYTES, createPrivateWorkspace, cleanCopilotEnvironment, defaultClient, modelCapabilities, requiredUsageInteger, optionalUsageInteger, CopilotSubscriptionAdapter;
|
|
22795
|
+
var init_copilot_adapter = __esm({
|
|
22796
|
+
"lib/inference-host/copilot-adapter.ts"() {
|
|
22797
|
+
"use strict";
|
|
22798
|
+
init_codex_app_server();
|
|
22799
|
+
COPILOT_SDK_VERSION = "1.0.0-beta.8";
|
|
22800
|
+
MAX_RESULT_BYTES = 3e5;
|
|
22801
|
+
createPrivateWorkspace = async () => {
|
|
22802
|
+
const path = await mkdtemp2(join5(tmpdir3(), "vtx-copilot-"));
|
|
22803
|
+
return {
|
|
22804
|
+
path,
|
|
22805
|
+
cleanup: async () => {
|
|
22806
|
+
await rm4(path, { recursive: true, force: true });
|
|
22807
|
+
}
|
|
22808
|
+
};
|
|
22809
|
+
};
|
|
22810
|
+
cleanCopilotEnvironment = () => {
|
|
22811
|
+
const environment = { ...process.env };
|
|
22812
|
+
for (const name of [
|
|
22813
|
+
"COPILOT_GITHUB_TOKEN",
|
|
22814
|
+
"GH_TOKEN",
|
|
22815
|
+
"GITHUB_TOKEN",
|
|
22816
|
+
"COPILOT_PROVIDER_API_KEY",
|
|
22817
|
+
"COPILOT_PROVIDER_BASE_URL",
|
|
22818
|
+
"COPILOT_PROVIDER_TYPE",
|
|
22819
|
+
"COPILOT_CLI_PATH",
|
|
22820
|
+
"COPILOT_SDK_AUTH_TOKEN",
|
|
22821
|
+
"COPILOT_CONNECTION_TOKEN",
|
|
22822
|
+
"COPILOT_HOME"
|
|
22823
|
+
]) delete environment[name];
|
|
22824
|
+
return environment;
|
|
22825
|
+
};
|
|
22826
|
+
defaultClient = (workingDirectory) => new CopilotClient({
|
|
22827
|
+
workingDirectory,
|
|
22828
|
+
env: cleanCopilotEnvironment(),
|
|
22829
|
+
useLoggedInUser: true,
|
|
22830
|
+
logLevel: "error"
|
|
22831
|
+
});
|
|
22832
|
+
modelCapabilities = (models) => {
|
|
22833
|
+
const capabilities = models.filter((model) => model.policy?.state !== "disabled" && model.policy?.state !== "unconfigured").flatMap((model) => {
|
|
22834
|
+
const efforts = model.capabilities.supports.reasoningEffort ? [...model.supportedReasoningEfforts ?? []] : ["none"];
|
|
22835
|
+
if (efforts.length === 0) return [];
|
|
22836
|
+
const defaultEffort = model.defaultReasoningEffort && efforts.includes(model.defaultReasoningEffort) ? model.defaultReasoningEffort : efforts[0];
|
|
22837
|
+
return [{
|
|
22838
|
+
id: model.id,
|
|
22839
|
+
model: model.id,
|
|
22840
|
+
displayName: model.name,
|
|
22841
|
+
hidden: false,
|
|
22842
|
+
supportedReasoningEfforts: efforts,
|
|
22843
|
+
defaultReasoningEffort: defaultEffort,
|
|
22844
|
+
isDefault: false
|
|
22845
|
+
}];
|
|
22846
|
+
});
|
|
22847
|
+
return capabilities.map((capability, index) => ({
|
|
22848
|
+
...capability,
|
|
22849
|
+
isDefault: index === 0
|
|
22850
|
+
}));
|
|
22851
|
+
};
|
|
22852
|
+
requiredUsageInteger = (value, field) => {
|
|
22853
|
+
if (!Number.isSafeInteger(value) || Number(value) < 0) {
|
|
22854
|
+
throw new Error(`copilot_usage_${field}_missing`);
|
|
22855
|
+
}
|
|
22856
|
+
return Number(value);
|
|
22857
|
+
};
|
|
22858
|
+
optionalUsageInteger = (value, field) => {
|
|
22859
|
+
if (value === void 0) return 0;
|
|
22860
|
+
if (!Number.isSafeInteger(value) || Number(value) < 0) {
|
|
22861
|
+
throw new Error(`copilot_usage_${field}_invalid`);
|
|
22862
|
+
}
|
|
22863
|
+
return Number(value);
|
|
22864
|
+
};
|
|
22865
|
+
CopilotSubscriptionAdapter = class {
|
|
22866
|
+
constructor(dependencies = {}) {
|
|
22867
|
+
this.inFlight = /* @__PURE__ */ new Map();
|
|
22868
|
+
this.dependencies = dependencies;
|
|
22869
|
+
}
|
|
22870
|
+
async preflight(signal) {
|
|
22871
|
+
const workspace = await (this.dependencies.createWorkspace ?? createPrivateWorkspace)();
|
|
22872
|
+
const client = (this.dependencies.createClient ?? defaultClient)(workspace.path);
|
|
22873
|
+
try {
|
|
22874
|
+
signal?.throwIfAborted();
|
|
22875
|
+
await client.start();
|
|
22876
|
+
const [status, auth, models] = await Promise.all([
|
|
22877
|
+
client.getStatus(),
|
|
22878
|
+
client.getAuthStatus(),
|
|
22879
|
+
client.listModels()
|
|
22880
|
+
]);
|
|
22881
|
+
if (!auth.isAuthenticated || auth.authType !== "user" && auth.authType !== "gh-cli") {
|
|
22882
|
+
throw new CodexAppServerError({
|
|
22883
|
+
message: "GitHub Copilot must use a logged-in Copilot subscription, not BYOK.",
|
|
22884
|
+
category: "auth",
|
|
22885
|
+
code: "copilot_subscription_auth_required",
|
|
22886
|
+
retryable: false
|
|
22887
|
+
});
|
|
22888
|
+
}
|
|
22889
|
+
const accountIdentity = auth.login?.trim().toLowerCase() ?? "";
|
|
22890
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,95}$/u.test(accountIdentity)) {
|
|
22891
|
+
throw new CodexAppServerError({
|
|
22892
|
+
message: "GitHub Copilot did not return a stable authenticated login identity.",
|
|
22893
|
+
category: "auth",
|
|
22894
|
+
code: "copilot_account_identity_required",
|
|
22895
|
+
retryable: false
|
|
22896
|
+
});
|
|
22897
|
+
}
|
|
22898
|
+
const capabilities = modelCapabilities(models);
|
|
22899
|
+
if (capabilities.length === 0) {
|
|
22900
|
+
throw new CodexAppServerError({
|
|
22901
|
+
message: "GitHub Copilot did not return an enabled model catalog.",
|
|
22902
|
+
category: "model",
|
|
22903
|
+
code: "copilot_model_catalog_empty",
|
|
22904
|
+
retryable: false
|
|
22905
|
+
});
|
|
22906
|
+
}
|
|
22907
|
+
return {
|
|
22908
|
+
adapterId: "copilot",
|
|
22909
|
+
runtimeVersion: `${COPILOT_SDK_VERSION}+cli.${status.version}.rpc${status.protocolVersion}`,
|
|
22910
|
+
authenticatedAccountIdentity: accountIdentity,
|
|
22911
|
+
authenticatedAccountEmail: null,
|
|
22912
|
+
authenticatedAccountPlan: null,
|
|
22913
|
+
modelCapabilities: capabilities,
|
|
22914
|
+
rateLimits: null,
|
|
22915
|
+
sameAttemptRecovery: false
|
|
22916
|
+
};
|
|
22917
|
+
} finally {
|
|
22918
|
+
await client.stop().catch(async () => {
|
|
22919
|
+
await client.forceStop();
|
|
22920
|
+
});
|
|
22921
|
+
await workspace.cleanup();
|
|
22922
|
+
}
|
|
22923
|
+
}
|
|
22924
|
+
runAttempt(input) {
|
|
22925
|
+
const active = this.inFlight.get(input.attemptId);
|
|
22926
|
+
if (active) return active;
|
|
22927
|
+
const promise2 = this.executeAttempt(input).finally(() => {
|
|
22928
|
+
if (this.inFlight.get(input.attemptId) === promise2) this.inFlight.delete(input.attemptId);
|
|
22929
|
+
});
|
|
22930
|
+
this.inFlight.set(input.attemptId, promise2);
|
|
22931
|
+
return promise2;
|
|
22932
|
+
}
|
|
22933
|
+
async executeAttempt(input) {
|
|
22934
|
+
const startedAt = (this.dependencies.now ?? Date.now)();
|
|
22935
|
+
const workspace = await (this.dependencies.createWorkspace ?? createPrivateWorkspace)();
|
|
22936
|
+
const client = (this.dependencies.createClient ?? defaultClient)(workspace.path);
|
|
22937
|
+
let session = null;
|
|
22938
|
+
let dispatchEntered = false;
|
|
22939
|
+
let providerReceiptObserved = false;
|
|
22940
|
+
try {
|
|
22941
|
+
input.signal?.throwIfAborted();
|
|
22942
|
+
if (Date.now() >= input.deadlineAtMs) throw new Error("deadline_exceeded");
|
|
22943
|
+
await client.start();
|
|
22944
|
+
session = await client.createSession({
|
|
22945
|
+
clientName: "@vtxmacro/cli durable Copilot inference host",
|
|
22946
|
+
model: input.requestedModel,
|
|
22947
|
+
...input.requestedReasoningEffort === "none" ? {} : { reasoningEffort: input.requestedReasoningEffort },
|
|
22948
|
+
systemMessage: {
|
|
22949
|
+
mode: "replace",
|
|
22950
|
+
content: `${input.systemPrompt}
|
|
22951
|
+
|
|
22952
|
+
Return only one JSON value matching this JSON Schema exactly:
|
|
22953
|
+
${input.outputSchemaJson}`
|
|
22954
|
+
},
|
|
22955
|
+
tools: [],
|
|
22956
|
+
availableTools: [],
|
|
22957
|
+
enableConfigDiscovery: false,
|
|
22958
|
+
enableSessionTelemetry: true,
|
|
22959
|
+
workingDirectory: workspace.path,
|
|
22960
|
+
streaming: true
|
|
22961
|
+
});
|
|
22962
|
+
const usageEvents = [];
|
|
22963
|
+
session.on((event) => {
|
|
22964
|
+
if (event.type === "assistant.usage" && !event.agentId) {
|
|
22965
|
+
providerReceiptObserved = true;
|
|
22966
|
+
usageEvents.push(event);
|
|
22967
|
+
}
|
|
22968
|
+
});
|
|
22969
|
+
const onAbort = () => {
|
|
22970
|
+
void session?.abort().catch(() => void 0);
|
|
22971
|
+
};
|
|
22972
|
+
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
22973
|
+
dispatchEntered = true;
|
|
22974
|
+
let response;
|
|
22975
|
+
try {
|
|
22976
|
+
response = await session.sendAndWait(
|
|
22977
|
+
{ prompt: input.userPrompt },
|
|
22978
|
+
Math.max(1, input.deadlineAtMs - Date.now())
|
|
22979
|
+
);
|
|
22980
|
+
providerReceiptObserved = true;
|
|
22981
|
+
} finally {
|
|
22982
|
+
input.signal?.removeEventListener("abort", onAbort);
|
|
22983
|
+
}
|
|
22984
|
+
if (!response?.data.content || Buffer.byteLength(response.data.content, "utf8") > MAX_RESULT_BYTES) {
|
|
22985
|
+
throw new Error("copilot_invalid_result");
|
|
22986
|
+
}
|
|
22987
|
+
const observedModels = /* @__PURE__ */ new Set();
|
|
22988
|
+
const observedEfforts = /* @__PURE__ */ new Set();
|
|
22989
|
+
let inputTokens = 0;
|
|
22990
|
+
let cachedInputTokens = 0;
|
|
22991
|
+
let outputTokens = 0;
|
|
22992
|
+
let reasoningOutputTokens = 0;
|
|
22993
|
+
let cacheWriteInputTokens = 0;
|
|
22994
|
+
let cacheWriteSupported = false;
|
|
22995
|
+
let timeToFirstTokenMs = null;
|
|
22996
|
+
let providerCallId = null;
|
|
22997
|
+
for (const event of usageEvents) {
|
|
22998
|
+
if (event.type !== "assistant.usage") continue;
|
|
22999
|
+
observedModels.add(event.data.model);
|
|
23000
|
+
if (event.data.reasoningEffort) observedEfforts.add(event.data.reasoningEffort);
|
|
23001
|
+
inputTokens += requiredUsageInteger(event.data.inputTokens, "input_tokens");
|
|
23002
|
+
cachedInputTokens += optionalUsageInteger(event.data.cacheReadTokens, "cache_read_tokens");
|
|
23003
|
+
outputTokens += requiredUsageInteger(event.data.outputTokens, "output_tokens");
|
|
23004
|
+
reasoningOutputTokens += optionalUsageInteger(event.data.reasoningTokens, "reasoning_tokens");
|
|
23005
|
+
if (event.data.cacheWriteTokens !== void 0) {
|
|
23006
|
+
cacheWriteSupported = true;
|
|
23007
|
+
cacheWriteInputTokens += optionalUsageInteger(
|
|
23008
|
+
event.data.cacheWriteTokens,
|
|
23009
|
+
"cache_write_tokens"
|
|
23010
|
+
);
|
|
23011
|
+
}
|
|
23012
|
+
if (timeToFirstTokenMs === null && Number.isSafeInteger(event.data.timeToFirstTokenMs)) {
|
|
23013
|
+
timeToFirstTokenMs = Number(event.data.timeToFirstTokenMs);
|
|
23014
|
+
}
|
|
23015
|
+
providerCallId = event.data.providerCallId ?? event.data.apiCallId ?? providerCallId;
|
|
23016
|
+
}
|
|
23017
|
+
if (usageEvents.length === 0) {
|
|
23018
|
+
throw new Error("copilot_usage_receipt_missing");
|
|
23019
|
+
}
|
|
23020
|
+
if (response.data.model) observedModels.add(response.data.model);
|
|
23021
|
+
if (observedModels.size !== 1 || !observedModels.has(input.requestedModel)) {
|
|
23022
|
+
throw new Error("copilot_effective_model_mismatch");
|
|
23023
|
+
}
|
|
23024
|
+
const effectiveEffort = input.requestedReasoningEffort === "none" ? observedEfforts.size === 0 ? "none" : observedEfforts.size === 1 ? [...observedEfforts][0] : null : observedEfforts.size === 1 ? [...observedEfforts][0] : null;
|
|
23025
|
+
if (effectiveEffort !== input.requestedReasoningEffort) {
|
|
23026
|
+
throw new Error("copilot_effective_effort_mismatch");
|
|
23027
|
+
}
|
|
23028
|
+
const usage = {
|
|
23029
|
+
inputTokens,
|
|
23030
|
+
cachedInputTokens: Math.min(cachedInputTokens, inputTokens),
|
|
23031
|
+
outputTokens,
|
|
23032
|
+
reasoningOutputTokens: Math.min(reasoningOutputTokens, outputTokens),
|
|
23033
|
+
totalTokens: inputTokens + outputTokens,
|
|
23034
|
+
cacheWriteInputTokens: cacheWriteSupported ? cacheWriteInputTokens : null,
|
|
23035
|
+
cacheWriteSupported
|
|
23036
|
+
};
|
|
23037
|
+
return {
|
|
23038
|
+
text: response.data.content,
|
|
23039
|
+
reasoningContent: response.data.reasoningText ?? null,
|
|
23040
|
+
reasoningSummary: null,
|
|
23041
|
+
requestedModel: input.requestedModel,
|
|
23042
|
+
effectiveModel: input.requestedModel,
|
|
23043
|
+
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
23044
|
+
effectiveReasoningEffort: effectiveEffort,
|
|
23045
|
+
adapterRequestId: providerCallId ?? response.data.requestId ?? response.data.messageId,
|
|
23046
|
+
adapterResponseId: response.data.serviceRequestId ?? response.data.messageId,
|
|
23047
|
+
usage,
|
|
23048
|
+
latencyMs: Math.max(0, (this.dependencies.now ?? Date.now)() - startedAt),
|
|
23049
|
+
timeToFirstTokenMs,
|
|
23050
|
+
terminalStatus: "completed"
|
|
23051
|
+
};
|
|
23052
|
+
} catch (error48) {
|
|
23053
|
+
if (error48 instanceof CodexAppServerError) throw error48;
|
|
23054
|
+
const cancelled = input.signal?.aborted === true;
|
|
23055
|
+
const deadline = Date.now() >= input.deadlineAtMs;
|
|
23056
|
+
if (dispatchEntered && session) {
|
|
23057
|
+
await session.abort().catch(() => void 0);
|
|
23058
|
+
}
|
|
23059
|
+
throw new CodexAppServerError({
|
|
23060
|
+
message: cancelled ? "GitHub Copilot attempt was cancelled." : deadline ? "GitHub Copilot attempt exceeded its immutable deadline." : error48 instanceof Error ? error48.message : "GitHub Copilot adapter failed.",
|
|
23061
|
+
category: cancelled ? "cancelled" : deadline ? "timeout" : "adapter",
|
|
23062
|
+
code: cancelled ? "cancelled" : deadline ? "deadline_exceeded" : "copilot_adapter_failure",
|
|
23063
|
+
retryable: false,
|
|
23064
|
+
dispatchOutcome: providerReceiptObserved ? "confirmed_dispatched" : dispatchEntered ? "outcome_unknown" : "not_dispatched",
|
|
23065
|
+
cause: error48
|
|
23066
|
+
});
|
|
23067
|
+
} finally {
|
|
23068
|
+
if (session) {
|
|
23069
|
+
const sessionId = session.sessionId;
|
|
23070
|
+
await session.disconnect().catch(() => void 0);
|
|
23071
|
+
await client.deleteSession(sessionId).catch(() => void 0);
|
|
23072
|
+
}
|
|
23073
|
+
await client.stop().catch(async () => {
|
|
23074
|
+
await client.forceStop();
|
|
23075
|
+
});
|
|
23076
|
+
await workspace.cleanup();
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
};
|
|
23080
|
+
}
|
|
23081
|
+
});
|
|
23082
|
+
|
|
23083
|
+
// lib/inference-host/durable-adapter.ts
|
|
23084
|
+
var DURABLE_INFERENCE_ADAPTER_IDS, isDurableInferenceAdapterId, durableAdapterDisplayName;
|
|
23085
|
+
var init_durable_adapter = __esm({
|
|
23086
|
+
"lib/inference-host/durable-adapter.ts"() {
|
|
23087
|
+
"use strict";
|
|
23088
|
+
DURABLE_INFERENCE_ADAPTER_IDS = [
|
|
23089
|
+
"codex",
|
|
23090
|
+
"copilot"
|
|
23091
|
+
];
|
|
23092
|
+
isDurableInferenceAdapterId = (value) => DURABLE_INFERENCE_ADAPTER_IDS.includes(value);
|
|
23093
|
+
durableAdapterDisplayName = (adapter) => ({
|
|
23094
|
+
codex: "Codex",
|
|
23095
|
+
copilot: "GitHub Copilot"
|
|
23096
|
+
})[adapter];
|
|
23097
|
+
}
|
|
23098
|
+
});
|
|
23099
|
+
|
|
22610
23100
|
// lib/inference-host/crypto.ts
|
|
22611
23101
|
import {
|
|
22612
23102
|
createCipheriv,
|
|
22613
23103
|
createDecipheriv,
|
|
22614
|
-
createHash as
|
|
23104
|
+
createHash as createHash5,
|
|
22615
23105
|
createPrivateKey,
|
|
22616
23106
|
createPublicKey,
|
|
22617
23107
|
diffieHellman,
|
|
@@ -22650,7 +23140,7 @@ var init_crypto = __esm({
|
|
|
22650
23140
|
const aad = envelopeAadSchema.parse(input);
|
|
22651
23141
|
return Buffer.from(JSON.stringify(canonicalize(aad)), "utf8");
|
|
22652
23142
|
};
|
|
22653
|
-
externalInferenceSha256 = (value) =>
|
|
23143
|
+
externalInferenceSha256 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
22654
23144
|
decodeCanonicalBase64Url = (value, fieldName, expectedBytes) => {
|
|
22655
23145
|
if (!/^[A-Za-z0-9_-]+$/u.test(value)) {
|
|
22656
23146
|
throw new ExternalInferenceEnvelopeError("invalid_encoding", `${fieldName} is invalid.`);
|
|
@@ -22749,7 +23239,7 @@ var init_crypto = __esm({
|
|
|
22749
23239
|
return Buffer.from(hkdfSync(
|
|
22750
23240
|
"sha256",
|
|
22751
23241
|
root,
|
|
22752
|
-
|
|
23242
|
+
createHash5("sha256").update(aadBytes).digest(),
|
|
22753
23243
|
info,
|
|
22754
23244
|
32
|
|
22755
23245
|
));
|
|
@@ -29163,7 +29653,7 @@ var require_ajv = __commonJS({
|
|
|
29163
29653
|
});
|
|
29164
29654
|
|
|
29165
29655
|
// lib/inference-host/runner.ts
|
|
29166
|
-
import { createHash as
|
|
29656
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
29167
29657
|
function createDefaultInferenceHostRunnerDependencies(options) {
|
|
29168
29658
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
29169
29659
|
return {
|
|
@@ -29198,7 +29688,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
|
|
|
29198
29688
|
envelopePublicKey: options.envelopePublicKey
|
|
29199
29689
|
};
|
|
29200
29690
|
}
|
|
29201
|
-
var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS,
|
|
29691
|
+
var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildInferenceAdvertisedModels, summarizeInferenceHostRuntimeRecovery, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, membershipFailureDisposition, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
|
|
29202
29692
|
var init_runner = __esm({
|
|
29203
29693
|
"lib/inference-host/runner.ts"() {
|
|
29204
29694
|
"use strict";
|
|
@@ -29223,18 +29713,18 @@ var init_runner = __esm({
|
|
|
29223
29713
|
DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
29224
29714
|
MIN_CLAIM_START_WINDOW_MS = 5e3;
|
|
29225
29715
|
UNBOUNDED_AVAILABLE_SLOTS = Number.MAX_SAFE_INTEGER;
|
|
29226
|
-
|
|
29716
|
+
buildInferenceAdvertisedModels = (capabilities, adapterRuntimeVersion, adapter = "codex", structuredOutput = true, sameAttemptRecovery = adapter === "codex") => {
|
|
29227
29717
|
const visible = capabilities.filter((capability) => !capability.hidden);
|
|
29228
29718
|
if (visible.length === 0 || visible.length > 64) {
|
|
29229
29719
|
throw new InferenceHostRunnerError(
|
|
29230
29720
|
"invalid_configuration",
|
|
29231
|
-
visible.length === 0 ?
|
|
29721
|
+
visible.length === 0 ? `${adapter} did not return any visible models that VTX can advertise.` : `${adapter} returned more models than the VTX advertisement contract can represent.`
|
|
29232
29722
|
);
|
|
29233
29723
|
}
|
|
29234
29724
|
const models = visible.map((capability) => ({
|
|
29235
29725
|
model_id: capability.model,
|
|
29236
29726
|
label: capability.displayName,
|
|
29237
|
-
adapter
|
|
29727
|
+
adapter,
|
|
29238
29728
|
supported_lanes: ["main", "review", "screener"],
|
|
29239
29729
|
supported_reasoning_efforts: [...capability.supportedReasoningEfforts],
|
|
29240
29730
|
default_reasoning_effort: capability.defaultReasoningEffort,
|
|
@@ -29243,14 +29733,14 @@ var init_runner = __esm({
|
|
|
29243
29733
|
requested_model: capability.model,
|
|
29244
29734
|
effective_models: [capability.model]
|
|
29245
29735
|
}],
|
|
29246
|
-
structured_output:
|
|
29247
|
-
same_attempt_recovery:
|
|
29736
|
+
structured_output: structuredOutput,
|
|
29737
|
+
same_attempt_recovery: sameAttemptRecovery,
|
|
29248
29738
|
adapter_runtime_version: adapterRuntimeVersion
|
|
29249
29739
|
}));
|
|
29250
29740
|
if (new Set(models.map((model) => model.model_id)).size !== models.length) {
|
|
29251
29741
|
throw new InferenceHostRunnerError(
|
|
29252
29742
|
"invalid_configuration",
|
|
29253
|
-
|
|
29743
|
+
`${adapter} returned duplicate runtime model identities.`
|
|
29254
29744
|
);
|
|
29255
29745
|
}
|
|
29256
29746
|
return models;
|
|
@@ -29471,7 +29961,7 @@ var init_runner = __esm({
|
|
|
29471
29961
|
attempts
|
|
29472
29962
|
};
|
|
29473
29963
|
};
|
|
29474
|
-
sha256 = (value) =>
|
|
29964
|
+
sha256 = (value) => createHash6("sha256").update(value, "utf8").digest("hex");
|
|
29475
29965
|
stableOperationId = (kind, parts) => `${kind}_${sha256(JSON.stringify(parts)).slice(0, 48)}`;
|
|
29476
29966
|
buildAttemptStartRequest = (claim, attemptId, startedAt) => attemptStartRequestSchema.parse({
|
|
29477
29967
|
schema_version: "external_inference_attempt_start_v1",
|
|
@@ -29526,6 +30016,10 @@ var init_runner = __esm({
|
|
|
29526
30016
|
return result2;
|
|
29527
30017
|
};
|
|
29528
30018
|
validateRunnerOptions = (options) => {
|
|
30019
|
+
const adapterId = options.adapterId ?? "codex";
|
|
30020
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,63}$/u.test(adapterId)) {
|
|
30021
|
+
throw new InferenceHostRunnerError("invalid_configuration", "Inference adapter ID is invalid.");
|
|
30022
|
+
}
|
|
29529
30023
|
if (!options.displayName.trim() || options.displayName.length > 128) {
|
|
29530
30024
|
throw new InferenceHostRunnerError("invalid_configuration", "Inference host display name is invalid.");
|
|
29531
30025
|
}
|
|
@@ -29549,9 +30043,15 @@ var init_runner = __esm({
|
|
|
29549
30043
|
);
|
|
29550
30044
|
}
|
|
29551
30045
|
return {
|
|
29552
|
-
|
|
30046
|
+
adapterId,
|
|
30047
|
+
usageSource: options.usageSource ?? (adapterId === "codex" ? "codex_app_server" : adapterId),
|
|
30048
|
+
sameAttemptRecovery: options.sameAttemptRecovery ?? adapterId === "codex",
|
|
30049
|
+
advertisedModels: buildInferenceAdvertisedModels(
|
|
29553
30050
|
options.codexModelCapabilities,
|
|
29554
|
-
options.adapterRuntimeVersion
|
|
30051
|
+
options.adapterRuntimeVersion,
|
|
30052
|
+
adapterId,
|
|
30053
|
+
options.structuredOutput ?? adapterId === "codex",
|
|
30054
|
+
options.sameAttemptRecovery ?? adapterId === "codex"
|
|
29555
30055
|
),
|
|
29556
30056
|
maxConcurrency: options.maxConcurrency === null || options.maxConcurrency === void 0 ? null : finitePositiveOption(options.maxConcurrency, 1, "Maximum concurrency"),
|
|
29557
30057
|
advertisementTtlMs,
|
|
@@ -29756,12 +30256,12 @@ var init_runner = __esm({
|
|
|
29756
30256
|
};
|
|
29757
30257
|
assertAdvertisementResult = (advertisement, result2) => {
|
|
29758
30258
|
const host = result2.host;
|
|
29759
|
-
if (host.host_id !== advertisement.host_id || host.host_generation !== advertisement.host_generation || host.advertisement_generation !== advertisement.advertisement_generation || host.key_generation !== advertisement.key_generation || host.display_name !== advertisement.display_name || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== advertisement.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (advertisement.authenticated_account_plan ?? null) || host.adapter !== advertisement.adapter || host.status === "revoked" || !exactJson2(host.models, advertisement.models)) runnerIdentityMismatch("Host advertisement");
|
|
30259
|
+
if (host.host_id !== advertisement.host_id || host.host_generation !== advertisement.host_generation || host.advertisement_generation !== advertisement.advertisement_generation || host.key_generation !== advertisement.key_generation || host.display_name !== advertisement.display_name || host.authenticated_account_identity !== void 0 && host.authenticated_account_identity !== null && host.authenticated_account_identity !== advertisement.authenticated_account_identity || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== advertisement.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (advertisement.authenticated_account_plan ?? null) || host.adapter !== advertisement.adapter || host.status === "revoked" || !exactJson2(host.models, advertisement.models)) runnerIdentityMismatch("Host advertisement");
|
|
29760
30260
|
};
|
|
29761
|
-
assertHostHeartbeatResult = (request, result2, allowOfflineProjection = false) => {
|
|
30261
|
+
assertHostHeartbeatResult = (request, result2, expectedAdapter, allowOfflineProjection = false) => {
|
|
29762
30262
|
const expectedStatus = request.status === "healthy" ? "online" : request.status;
|
|
29763
30263
|
const expectedTemporaryOffline = allowOfflineProjection && request.status === "healthy" && result2.host.status === "offline";
|
|
29764
|
-
if (result2.host.host_id !== request.host_id || result2.host.host_generation !== request.host_generation || result2.host.advertisement_generation !== request.advertisement_generation || result2.host.key_generation !== request.key_generation || result2.host.adapter !==
|
|
30264
|
+
if (result2.host.host_id !== request.host_id || result2.host.host_generation !== request.host_generation || result2.host.advertisement_generation !== request.advertisement_generation || result2.host.key_generation !== request.key_generation || result2.host.adapter !== expectedAdapter || result2.host.status !== expectedStatus && !expectedTemporaryOffline) runnerIdentityMismatch("Host heartbeat");
|
|
29765
30265
|
};
|
|
29766
30266
|
assertClaimResult = (request, result2) => {
|
|
29767
30267
|
if (result2.claim_request_id !== request.claim_request_id) {
|
|
@@ -29837,10 +30337,10 @@ var init_runner = __esm({
|
|
|
29837
30337
|
"authentication_timeout"
|
|
29838
30338
|
].includes(code);
|
|
29839
30339
|
};
|
|
29840
|
-
reportedTokenUsage = (usage) => ({
|
|
30340
|
+
reportedTokenUsage = (usage, source = "codex_app_server") => ({
|
|
29841
30341
|
schema_version: EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION,
|
|
29842
30342
|
availability: "reported",
|
|
29843
|
-
source
|
|
30343
|
+
source,
|
|
29844
30344
|
input_tokens: usage.inputTokens,
|
|
29845
30345
|
cached_input_tokens: usage.cachedInputTokens,
|
|
29846
30346
|
output_tokens: usage.outputTokens,
|
|
@@ -29849,7 +30349,7 @@ var init_runner = __esm({
|
|
|
29849
30349
|
cache_write_input_tokens: usage.cacheWriteInputTokens,
|
|
29850
30350
|
cache_write_support: usage.cacheWriteSupported ? "reported" : "unsupported"
|
|
29851
30351
|
});
|
|
29852
|
-
reportedUsage = (result2) => reportedTokenUsage(result2.usage);
|
|
30352
|
+
reportedUsage = (result2, source) => reportedTokenUsage(result2.usage, source);
|
|
29853
30353
|
InferenceHostRunner = class {
|
|
29854
30354
|
constructor(dependencies, options) {
|
|
29855
30355
|
this.receiptWriteChain = Promise.resolve();
|
|
@@ -30079,8 +30579,9 @@ var init_runner = __esm({
|
|
|
30079
30579
|
host_generation: localState.host_generation,
|
|
30080
30580
|
advertisement_generation: generation,
|
|
30081
30581
|
key_generation: localState.key_generation,
|
|
30082
|
-
adapter:
|
|
30582
|
+
adapter: settings.adapterId,
|
|
30083
30583
|
display_name: this.options.displayName.trim(),
|
|
30584
|
+
authenticated_account_identity: this.options.authenticatedAccountIdentity ?? null,
|
|
30084
30585
|
authenticated_account_email: this.options.authenticatedAccountEmail ?? null,
|
|
30085
30586
|
authenticated_account_plan: this.options.authenticatedAccountPlan ?? null,
|
|
30086
30587
|
protocol_version: this.options.protocolVersion,
|
|
@@ -30189,7 +30690,7 @@ var init_runner = __esm({
|
|
|
30189
30690
|
}
|
|
30190
30691
|
throw error48;
|
|
30191
30692
|
}
|
|
30192
|
-
assertHostHeartbeatResult(request, result2, allowOfflineProjection);
|
|
30693
|
+
assertHostHeartbeatResult(request, result2, settings.adapterId, allowOfflineProjection);
|
|
30193
30694
|
if (result2.host.status === "revoked" || result2.host.status === "offline" && !allowOfflineProjection) {
|
|
30194
30695
|
const error48 = new InferenceHostRunnerError(
|
|
30195
30696
|
result2.host.status === "revoked" ? "host_revoked" : "host_offline",
|
|
@@ -30633,6 +31134,9 @@ var init_runner = __esm({
|
|
|
30633
31134
|
signal,
|
|
30634
31135
|
now
|
|
30635
31136
|
} = options;
|
|
31137
|
+
const adapterId = this.options.adapterId ?? "codex";
|
|
31138
|
+
const usageSource = this.options.usageSource ?? (adapterId === "codex" ? "codex_app_server" : adapterId);
|
|
31139
|
+
const sameAttemptRecovery = this.options.sameAttemptRecovery ?? adapterId === "codex";
|
|
30636
31140
|
const decryptClaim = this.dependencies.decryptClaim ?? decryptExternalInferenceClaim;
|
|
30637
31141
|
const sealCandidate = this.dependencies.sealCandidate ?? sealExternalInferenceCandidate;
|
|
30638
31142
|
const validateOutput = this.dependencies.validateOutput ?? defaultValidateOutput;
|
|
@@ -30809,6 +31313,15 @@ var init_runner = __esm({
|
|
|
30809
31313
|
phase: "dispatched",
|
|
30810
31314
|
dispatch_outcome: "outcome_unknown"
|
|
30811
31315
|
});
|
|
31316
|
+
if (options.resumeReceipt?.phase === "dispatched" && !sameAttemptRecovery) {
|
|
31317
|
+
throw new CodexAppServerError({
|
|
31318
|
+
message: `${adapterId} cannot prove the exact result of the interrupted attempt.`,
|
|
31319
|
+
category: "adapter",
|
|
31320
|
+
code: "same_attempt_recovery_unavailable",
|
|
31321
|
+
retryable: false,
|
|
31322
|
+
dispatchOutcome: "outcome_unknown"
|
|
31323
|
+
});
|
|
31324
|
+
}
|
|
30812
31325
|
adapterResult = await this.dependencies.codexAdapter.runAttempt({
|
|
30813
31326
|
attemptId,
|
|
30814
31327
|
inputSha256: jobInput.input_sha256,
|
|
@@ -30834,7 +31347,7 @@ var init_runner = __esm({
|
|
|
30834
31347
|
} catch (error48) {
|
|
30835
31348
|
throw new InferenceHostRunnerError(
|
|
30836
31349
|
"invalid_structured_output",
|
|
30837
|
-
|
|
31350
|
+
`${adapterId} did not return valid structured JSON.`,
|
|
30838
31351
|
{ cause: error48 }
|
|
30839
31352
|
);
|
|
30840
31353
|
}
|
|
@@ -30846,7 +31359,7 @@ var init_runner = __esm({
|
|
|
30846
31359
|
if (Buffer.byteLength(adapterResult.text, "utf8") > outputContract.providerResponseTextMaxUtf8Bytes) {
|
|
30847
31360
|
throw new InferenceHostRunnerError(
|
|
30848
31361
|
"output_schema_response_text_too_large",
|
|
30849
|
-
|
|
31362
|
+
`${adapterId} response text exceeds the immutable provider-response UTF-8 byte limit.`
|
|
30850
31363
|
);
|
|
30851
31364
|
}
|
|
30852
31365
|
if (outputContract.providerReasoningContentSupported && outputContract.providerReasoningContentMaxUtf8Bytes !== null && adapterResult.reasoningContent !== null && Buffer.byteLength(adapterResult.reasoningContent, "utf8") > outputContract.providerReasoningContentMaxUtf8Bytes) {
|
|
@@ -30919,7 +31432,7 @@ var init_runner = __esm({
|
|
|
30919
31432
|
adapter_request_id: adapterResult.adapterRequestId,
|
|
30920
31433
|
adapter_response_id: adapterResult.adapterResponseId
|
|
30921
31434
|
},
|
|
30922
|
-
usage: reportedUsage(adapterResult),
|
|
31435
|
+
usage: reportedUsage(adapterResult, usageSource),
|
|
30923
31436
|
outcome: {
|
|
30924
31437
|
dispatch_outcome: "confirmed_dispatched",
|
|
30925
31438
|
response_outcome: "completed"
|
|
@@ -31034,7 +31547,7 @@ var init_runner = __esm({
|
|
|
31034
31547
|
adapter_request_id: canReportEffective ? adapterResult?.adapterRequestId ?? null : null,
|
|
31035
31548
|
adapter_response_id: canReportEffective ? adapterResult?.adapterResponseId ?? null : null
|
|
31036
31549
|
},
|
|
31037
|
-
usage: adapterResult ? reportedUsage(adapterResult) : error48 instanceof CodexAppServerError && error48.usage ? reportedTokenUsage(error48.usage) : null,
|
|
31550
|
+
usage: adapterResult ? reportedUsage(adapterResult, usageSource) : error48 instanceof CodexAppServerError && error48.usage ? reportedTokenUsage(error48.usage, usageSource) : null,
|
|
31038
31551
|
failure_category: failure.category,
|
|
31039
31552
|
failure_code: failure.code,
|
|
31040
31553
|
retryable: failure.retryable,
|
|
@@ -31101,7 +31614,7 @@ var init_runner = __esm({
|
|
|
31101
31614
|
if (result2.requestedModel !== jobInput.requested_model || result2.requestedReasoningEffort !== jobInput.requested_reasoning_effort || !mapping?.effective_models.includes(result2.effectiveModel) || !jobInput.allowed_effective_reasoning_efforts.includes(result2.effectiveReasoningEffort)) {
|
|
31102
31615
|
throw new InferenceHostRunnerError(
|
|
31103
31616
|
"effective_model_mismatch",
|
|
31104
|
-
"
|
|
31617
|
+
"The effective adapter model provenance did not match the immutable allowance."
|
|
31105
31618
|
);
|
|
31106
31619
|
}
|
|
31107
31620
|
}
|
|
@@ -31110,16 +31623,17 @@ var init_runner = __esm({
|
|
|
31110
31623
|
});
|
|
31111
31624
|
|
|
31112
31625
|
// lib/inference-host/service.ts
|
|
31113
|
-
import { spawn as
|
|
31626
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
31114
31627
|
import { randomUUID } from "node:crypto";
|
|
31115
31628
|
import { createWriteStream, readFileSync } from "node:fs";
|
|
31116
|
-
import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as
|
|
31629
|
+
import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
|
|
31117
31630
|
import { homedir as homedir2 } from "node:os";
|
|
31118
|
-
import { dirname as dirname4, join as
|
|
31631
|
+
import { dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
|
|
31119
31632
|
var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, INFERENCE_HOST_SERVICE_DRAIN_COMMAND, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, inferenceHostServiceRuntimePath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, manifestGeneration, assertWorker, assertManifest, assertServiceRuntimeState, assertDesiredState, readInferenceHostServiceManifest, readManifestAcrossAtomicReplacement, readInferenceHostServiceRuntime, readRuntimeAcrossAtomicReplacement, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, sameWorker, sameServiceDefinition, sameWorkerSet, InferenceHostServiceManager, appendServiceLog, spawnInferenceHostServiceChild, runInferenceHostServiceSupervisor;
|
|
31120
31633
|
var init_service = __esm({
|
|
31121
31634
|
"lib/inference-host/service.ts"() {
|
|
31122
31635
|
"use strict";
|
|
31636
|
+
init_durable_adapter();
|
|
31123
31637
|
init_config();
|
|
31124
31638
|
SERVICE_NAME = "VTX Macro Inference Host";
|
|
31125
31639
|
SYSTEMD_UNIT = "vtx-inference-host.service";
|
|
@@ -31156,7 +31670,7 @@ var init_service = __esm({
|
|
|
31156
31670
|
plistEscape = xmlEscape;
|
|
31157
31671
|
systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
|
|
31158
31672
|
defaultRunCommand = async (command, args) => await new Promise((resolvePromise) => {
|
|
31159
|
-
const child =
|
|
31673
|
+
const child = spawn6(command, [...args], {
|
|
31160
31674
|
windowsHide: true,
|
|
31161
31675
|
stdio: ["ignore", "pipe", "pipe"]
|
|
31162
31676
|
});
|
|
@@ -31208,14 +31722,15 @@ var init_service = __esm({
|
|
|
31208
31722
|
throw new Error("Inference-host service worker is invalid.");
|
|
31209
31723
|
}
|
|
31210
31724
|
const record2 = value;
|
|
31211
|
-
if (typeof record2.instance_name !== "string" || !/^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u.test(record2.instance_name) || record2.adapter !== "
|
|
31725
|
+
if (typeof record2.instance_name !== "string" || !/^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u.test(record2.instance_name) || typeof record2.adapter !== "string" || !isDurableInferenceAdapterId(record2.adapter) || typeof record2.display_name !== "string" || !record2.display_name || record2.max_concurrency !== null && (!Number.isSafeInteger(record2.max_concurrency) || Number(record2.max_concurrency) < 1) || record2.authenticated_account_email !== null && typeof record2.authenticated_account_email !== "string" || record2.authenticated_account_identity !== void 0 && record2.authenticated_account_identity !== null && typeof record2.authenticated_account_identity !== "string" || record2.authenticated_account_plan !== null && typeof record2.authenticated_account_plan !== "string") {
|
|
31212
31726
|
throw new Error("Inference-host service worker is invalid.");
|
|
31213
31727
|
}
|
|
31214
31728
|
return {
|
|
31215
31729
|
instance_name: record2.instance_name,
|
|
31216
|
-
adapter:
|
|
31730
|
+
adapter: record2.adapter,
|
|
31217
31731
|
display_name: record2.display_name,
|
|
31218
31732
|
max_concurrency: record2.max_concurrency === null ? null : Number(record2.max_concurrency),
|
|
31733
|
+
authenticated_account_identity: record2.authenticated_account_identity,
|
|
31219
31734
|
authenticated_account_email: record2.authenticated_account_email,
|
|
31220
31735
|
authenticated_account_plan: record2.authenticated_account_plan,
|
|
31221
31736
|
runtime_environment: assertRuntimeEnvironment(record2.runtime_environment)
|
|
@@ -31582,8 +32097,8 @@ WantedBy=default.target
|
|
|
31582
32097
|
}
|
|
31583
32098
|
definitionPath() {
|
|
31584
32099
|
if (this.platform === "win32") return `${this.config.supervisorStatePath}.service-task.xml`;
|
|
31585
|
-
if (this.platform === "darwin") return
|
|
31586
|
-
return
|
|
32100
|
+
if (this.platform === "darwin") return join6(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
32101
|
+
return join6(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
31587
32102
|
}
|
|
31588
32103
|
windowsLauncherPath() {
|
|
31589
32104
|
return `${this.config.supervisorStatePath}.service-launcher.vbs`;
|
|
@@ -31629,8 +32144,10 @@ WantedBy=default.target
|
|
|
31629
32144
|
}
|
|
31630
32145
|
throw new Error("Background service did not reach an active state within 10 seconds.");
|
|
31631
32146
|
}
|
|
31632
|
-
async restoreRunningSupervisor(manifest) {
|
|
31633
|
-
const
|
|
32147
|
+
async restoreRunningSupervisor(manifest, preserveGeneration = false, targetInstanceName) {
|
|
32148
|
+
const previousRuntime = await readRuntimeAcrossAtomicReplacement(this.runtimePath()).catch(() => null);
|
|
32149
|
+
const previousRuntimeUpdatedAt = previousRuntime?.manifest_generation === manifest.generation ? Date.parse(previousRuntime.updated_at) : Number.NaN;
|
|
32150
|
+
const restoredManifest = preserveGeneration ? manifest : assertManifest({
|
|
31634
32151
|
...manifest,
|
|
31635
32152
|
generation: randomUUID(),
|
|
31636
32153
|
installed_at: this.now().toISOString()
|
|
@@ -31658,10 +32175,15 @@ ${result2.stderr}`)) {
|
|
|
31658
32175
|
}
|
|
31659
32176
|
await this.waitForManagerActive();
|
|
31660
32177
|
if (!this.confirmInitialReadiness) return await this.status();
|
|
31661
|
-
const runtime = await this.waitForManifestApplied(
|
|
32178
|
+
const runtime = await this.waitForManifestApplied(
|
|
32179
|
+
restoredManifest,
|
|
32180
|
+
targetInstanceName,
|
|
32181
|
+
this.reconcileWaitAttempts,
|
|
32182
|
+
Number.isFinite(previousRuntimeUpdatedAt) ? previousRuntimeUpdatedAt : void 0
|
|
32183
|
+
);
|
|
31662
32184
|
return await this.status(runtime);
|
|
31663
32185
|
}
|
|
31664
|
-
async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts) {
|
|
32186
|
+
async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts, minimumRuntimeUpdatedAtExclusive) {
|
|
31665
32187
|
let consecutiveReadyObservations = 0;
|
|
31666
32188
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
31667
32189
|
let runtime = null;
|
|
@@ -31672,7 +32194,9 @@ ${result2.stderr}`)) {
|
|
|
31672
32194
|
throw error48;
|
|
31673
32195
|
}
|
|
31674
32196
|
}
|
|
31675
|
-
|
|
32197
|
+
const runtimeUpdatedAt = runtime ? Date.parse(runtime.updated_at) : Number.NaN;
|
|
32198
|
+
const runtimeIsFresh = minimumRuntimeUpdatedAtExclusive === void 0 || Number.isFinite(runtimeUpdatedAt) && runtimeUpdatedAt > minimumRuntimeUpdatedAtExclusive;
|
|
32199
|
+
if (runtime?.manifest_generation === manifest.generation && runtimeIsFresh) {
|
|
31676
32200
|
const configuredNames = manifest.workers.map((worker) => worker.instance_name).sort();
|
|
31677
32201
|
const runtimeNames = runtime.workers.map((worker) => worker.instance_name).sort();
|
|
31678
32202
|
const exactWorkerSet = JSON.stringify(configuredNames) === JSON.stringify(runtimeNames);
|
|
@@ -31756,13 +32280,13 @@ ${cleanup.stderr}`)) {
|
|
|
31756
32280
|
);
|
|
31757
32281
|
}
|
|
31758
32282
|
}
|
|
31759
|
-
await
|
|
32283
|
+
await rm5(this.definitionPath(), { force: true }).catch(() => void 0);
|
|
31760
32284
|
if (this.platform === "win32") {
|
|
31761
|
-
await
|
|
32285
|
+
await rm5(this.windowsLauncherPath(), { force: true }).catch(() => void 0);
|
|
31762
32286
|
}
|
|
31763
|
-
await
|
|
31764
|
-
await
|
|
31765
|
-
await
|
|
32287
|
+
await rm5(this.manifestPath(), { force: true }).catch(() => void 0);
|
|
32288
|
+
await rm5(this.desiredPath(), { force: true }).catch(() => void 0);
|
|
32289
|
+
await rm5(this.runtimePath(), { force: true }).catch(() => void 0);
|
|
31766
32290
|
if (this.platform === "linux") {
|
|
31767
32291
|
await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
|
|
31768
32292
|
}
|
|
@@ -31858,18 +32382,31 @@ ${cleanup.stderr}`)) {
|
|
|
31858
32382
|
}
|
|
31859
32383
|
async installUnlocked(options) {
|
|
31860
32384
|
const existing = await readInferenceHostServiceManifest(this.manifestPath());
|
|
32385
|
+
const currentWorker = existing?.workers.find(
|
|
32386
|
+
(worker) => worker.instance_name === this.config.instanceName
|
|
32387
|
+
);
|
|
32388
|
+
if (currentWorker && currentWorker.adapter !== options.adapter) {
|
|
32389
|
+
throw new Error(
|
|
32390
|
+
`Inference-host instance ${this.config.instanceName} is already bound to ${currentWorker.adapter}; install ${options.adapter} under a distinct --instance and VTX grant.`
|
|
32391
|
+
);
|
|
32392
|
+
}
|
|
31861
32393
|
const normalizedEmail = options.authenticatedAccountEmail?.trim().toLowerCase() || null;
|
|
32394
|
+
const normalizedAccountIdentity = options.authenticatedAccountIdentity?.trim().toLowerCase() || null;
|
|
32395
|
+
const normalizedIdentity = normalizedAccountIdentity || normalizedEmail;
|
|
31862
32396
|
const otherWorkers = existing?.workers.filter(
|
|
31863
32397
|
(worker) => worker.instance_name !== this.config.instanceName
|
|
31864
32398
|
) ?? [];
|
|
31865
|
-
|
|
32399
|
+
const peerAdapterWorkers = otherWorkers.filter(
|
|
32400
|
+
(worker) => worker.adapter === options.adapter
|
|
32401
|
+
);
|
|
32402
|
+
if (peerAdapterWorkers.length > 0 && (!normalizedIdentity || peerAdapterWorkers.some((worker) => !(worker.authenticated_account_identity || worker.authenticated_account_email)))) {
|
|
31866
32403
|
throw new Error(
|
|
31867
|
-
|
|
32404
|
+
`Cannot prove that every installed ${options.adapter} instance uses a distinct subscription identity.`
|
|
31868
32405
|
);
|
|
31869
32406
|
}
|
|
31870
|
-
if (
|
|
32407
|
+
if (normalizedIdentity && peerAdapterWorkers.some((worker) => (worker.authenticated_account_identity || worker.authenticated_account_email)?.trim().toLowerCase() === normalizedIdentity)) {
|
|
31871
32408
|
throw new Error(
|
|
31872
|
-
|
|
32409
|
+
`This ${options.adapter} subscription is already installed under another inference-host instance.`
|
|
31873
32410
|
);
|
|
31874
32411
|
}
|
|
31875
32412
|
if (this.config.credentialFilePath && otherWorkers.some((worker) => {
|
|
@@ -31890,6 +32427,7 @@ ${cleanup.stderr}`)) {
|
|
|
31890
32427
|
adapter: options.adapter,
|
|
31891
32428
|
display_name: options.displayName,
|
|
31892
32429
|
max_concurrency: maxConcurrency,
|
|
32430
|
+
...normalizedAccountIdentity ? { authenticated_account_identity: normalizedAccountIdentity } : {},
|
|
31893
32431
|
authenticated_account_email: normalizedEmail,
|
|
31894
32432
|
authenticated_account_plan: options.authenticatedAccountPlan ?? null,
|
|
31895
32433
|
runtime_environment: runtimeEnvironment(this.config, maxConcurrency)
|
|
@@ -31934,7 +32472,71 @@ ${result2.stderr}`)) {
|
|
|
31934
32472
|
async stop() {
|
|
31935
32473
|
return await this.withControlLock(async () => await this.stopUnlocked());
|
|
31936
32474
|
}
|
|
31937
|
-
|
|
32475
|
+
/**
|
|
32476
|
+
* Quiesce the complete installed worker boundary while one exact instance's
|
|
32477
|
+
* durable Codex recovery evidence is reconciled. The service process lock is
|
|
32478
|
+
* held for the callback, so neither the manager nor a manual run-internal can
|
|
32479
|
+
* recreate workers during the same-boot process inspection.
|
|
32480
|
+
*/
|
|
32481
|
+
async recoverInstance(instanceName, operation, options = {}) {
|
|
32482
|
+
return await this.withControlLock(async () => {
|
|
32483
|
+
const manifest = await readInferenceHostServiceManifest(this.manifestPath());
|
|
32484
|
+
if (!manifest) throw new Error("Inference-host service is not installed.");
|
|
32485
|
+
if (!manifest.workers.some((worker) => worker.instance_name === instanceName)) {
|
|
32486
|
+
throw new Error(`Inference-host instance ${instanceName} is not installed.`);
|
|
32487
|
+
}
|
|
32488
|
+
if (options.expectedManifestGeneration && options.expectedManifestGeneration !== manifest.generation) {
|
|
32489
|
+
throw new Error("Inference-host service manifest changed during recovery.");
|
|
32490
|
+
}
|
|
32491
|
+
const recordedDesiredRunning = await readInferenceHostServiceDesired(this.desiredPath());
|
|
32492
|
+
const desiredRunning = options.restoreDesiredRunning ?? recordedDesiredRunning;
|
|
32493
|
+
const context = { manifest, desiredRunning };
|
|
32494
|
+
await options.prepare?.(context);
|
|
32495
|
+
const before = await this.status();
|
|
32496
|
+
if (recordedDesiredRunning || before.manager_active) {
|
|
32497
|
+
await this.stopUnlocked(true);
|
|
32498
|
+
} else {
|
|
32499
|
+
await writeDesired(this.desiredPath(), false, this.now());
|
|
32500
|
+
}
|
|
32501
|
+
let serviceFence = null;
|
|
32502
|
+
let result2;
|
|
32503
|
+
let operationError = null;
|
|
32504
|
+
try {
|
|
32505
|
+
serviceFence = await this.acquireProcessLock(
|
|
32506
|
+
`${this.config.supervisorProcessLockPath}.service`
|
|
32507
|
+
);
|
|
32508
|
+
const stopped = await this.status();
|
|
32509
|
+
if (stopped.manager_active) {
|
|
32510
|
+
throw new Error("Background service manager became active during recovery quiescence.");
|
|
32511
|
+
}
|
|
32512
|
+
result2 = await operation(context);
|
|
32513
|
+
} catch (error48) {
|
|
32514
|
+
operationError = error48;
|
|
32515
|
+
} finally {
|
|
32516
|
+
await serviceFence?.release().catch((error48) => {
|
|
32517
|
+
operationError = operationError ?? error48;
|
|
32518
|
+
});
|
|
32519
|
+
}
|
|
32520
|
+
let restoredStatus;
|
|
32521
|
+
try {
|
|
32522
|
+
restoredStatus = desiredRunning ? await this.restoreRunningSupervisor(manifest, true, instanceName) : await this.status();
|
|
32523
|
+
} catch (restoreError) {
|
|
32524
|
+
if (operationError) {
|
|
32525
|
+
throw new Error(
|
|
32526
|
+
"Inference-host recovery failed and the previous supervisor could not be restored.",
|
|
32527
|
+
{ cause: new AggregateError([operationError, restoreError]) }
|
|
32528
|
+
);
|
|
32529
|
+
}
|
|
32530
|
+
throw new Error(
|
|
32531
|
+
"Inference-host recovery completed, but the previous supervisor could not be restored.",
|
|
32532
|
+
{ cause: restoreError }
|
|
32533
|
+
);
|
|
32534
|
+
}
|
|
32535
|
+
if (operationError) throw operationError;
|
|
32536
|
+
return { result: result2, status: restoredStatus };
|
|
32537
|
+
});
|
|
32538
|
+
}
|
|
32539
|
+
async stopUnlocked(preserveManifestGenerationOnFailure = false) {
|
|
31938
32540
|
const manifest = await readInferenceHostServiceManifest(this.manifestPath());
|
|
31939
32541
|
if (!manifest) {
|
|
31940
32542
|
throw new Error("Inference-host service is not installed.");
|
|
@@ -31976,7 +32578,7 @@ ${result2.stderr}`)) {
|
|
|
31976
32578
|
} catch (error48) {
|
|
31977
32579
|
if (!restoreOnFailure) throw error48;
|
|
31978
32580
|
try {
|
|
31979
|
-
await this.restoreRunningSupervisor(manifest);
|
|
32581
|
+
await this.restoreRunningSupervisor(manifest, preserveManifestGenerationOnFailure);
|
|
31980
32582
|
} catch (restoreError) {
|
|
31981
32583
|
throw new Error(
|
|
31982
32584
|
"Inference-host stop failed and the previous supervisor could not be restored.",
|
|
@@ -31999,12 +32601,14 @@ ${result2.stderr}`)) {
|
|
|
31999
32601
|
manager_active: managerActive,
|
|
32000
32602
|
manager: managerName(this.platform),
|
|
32001
32603
|
manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
|
|
32002
|
-
adapter: manifest ?
|
|
32604
|
+
adapter: manifest && new Set(manifest.workers.map((worker) => worker.adapter)).size === 1 ? manifest.workers[0]?.adapter ?? null : null,
|
|
32003
32605
|
log_path: manifest?.log_path ?? this.logPath(),
|
|
32004
32606
|
workers: manifest?.workers.map((worker) => ({
|
|
32005
32607
|
instance_name: worker.instance_name,
|
|
32608
|
+
adapter: worker.adapter,
|
|
32006
32609
|
display_name: worker.display_name,
|
|
32007
32610
|
max_concurrency: worker.max_concurrency,
|
|
32611
|
+
authenticated_account_identity: worker.authenticated_account_identity ?? null,
|
|
32008
32612
|
authenticated_account_email: worker.authenticated_account_email,
|
|
32009
32613
|
authenticated_account_plan: worker.authenticated_account_plan,
|
|
32010
32614
|
runtime_state: runtime?.manifest_generation === manifest.generation && managerActive ? runtime.workers.find((item) => item.instance_name === worker.instance_name)?.state ?? "unknown" : "unknown"
|
|
@@ -32068,12 +32672,12 @@ ${result2.stderr}`)) {
|
|
|
32068
32672
|
${result2.stderr}`)) {
|
|
32069
32673
|
throw new Error(`Background service uninstall failed: ${result2.stderr.trim()}`);
|
|
32070
32674
|
}
|
|
32071
|
-
await
|
|
32072
|
-
if (this.platform === "win32") await
|
|
32675
|
+
await rm5(this.definitionPath(), { force: true });
|
|
32676
|
+
if (this.platform === "win32") await rm5(this.windowsLauncherPath(), { force: true });
|
|
32073
32677
|
if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
|
|
32074
|
-
await
|
|
32075
|
-
await
|
|
32076
|
-
await
|
|
32678
|
+
await rm5(this.manifestPath(), { force: true });
|
|
32679
|
+
await rm5(this.desiredPath(), { force: true });
|
|
32680
|
+
await rm5(this.runtimePath(), { force: true });
|
|
32077
32681
|
return {
|
|
32078
32682
|
installed: false,
|
|
32079
32683
|
desired_running: false,
|
|
@@ -32102,6 +32706,8 @@ ${result2.stderr}`)) {
|
|
|
32102
32706
|
"inference-host",
|
|
32103
32707
|
"run",
|
|
32104
32708
|
"--json",
|
|
32709
|
+
"--adapter",
|
|
32710
|
+
worker.adapter,
|
|
32105
32711
|
"--display-name",
|
|
32106
32712
|
worker.display_name
|
|
32107
32713
|
];
|
|
@@ -32143,7 +32749,7 @@ ${result2.stderr}`)) {
|
|
|
32143
32749
|
`);
|
|
32144
32750
|
}
|
|
32145
32751
|
};
|
|
32146
|
-
const child =
|
|
32752
|
+
const child = spawn6(manifest.executable, args, {
|
|
32147
32753
|
env: inferenceHostServiceChildEnvironment(worker.runtime_environment),
|
|
32148
32754
|
windowsHide: true,
|
|
32149
32755
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -32214,7 +32820,7 @@ ${result2.stderr}`)) {
|
|
|
32214
32820
|
});
|
|
32215
32821
|
const launch = options.runWorker ?? options.spawnChild ?? spawnInferenceHostServiceChild;
|
|
32216
32822
|
await appendServiceLog(initialManifest.log_path, "service_supervisor_started", {
|
|
32217
|
-
|
|
32823
|
+
adapters: [...new Set(initialManifest.workers.map((worker) => worker.adapter))].sort(),
|
|
32218
32824
|
instances: initialManifest.workers.map((worker) => worker.instance_name)
|
|
32219
32825
|
});
|
|
32220
32826
|
const workers = /* @__PURE__ */ new Map();
|
|
@@ -32433,10 +33039,10 @@ __export(cli_exports, {
|
|
|
32433
33039
|
registerInferenceHostServiceControlInput: () => registerInferenceHostServiceControlInput,
|
|
32434
33040
|
runInferenceHostCli: () => runInferenceHostCli
|
|
32435
33041
|
});
|
|
32436
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
32437
|
-
import { spawn as
|
|
32438
|
-
import { lstat as lstat4, realpath as realpath4, rm as
|
|
32439
|
-
import { join as
|
|
33042
|
+
import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
|
|
33043
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
33044
|
+
import { lstat as lstat4, realpath as realpath4, rm as rm6 } from "node:fs/promises";
|
|
33045
|
+
import { join as join7, resolve as resolve5 } from "node:path";
|
|
32440
33046
|
async function runInferenceHostCli(argv2, env = process.env, dependencies = {}) {
|
|
32441
33047
|
const warnings = [];
|
|
32442
33048
|
try {
|
|
@@ -32444,6 +33050,21 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32444
33050
|
return { exitCode: 0, stdout: INFERENCE_HOST_HELP, stderr: "" };
|
|
32445
33051
|
}
|
|
32446
33052
|
const parsed = parseInferenceHostArgs(argv2, env);
|
|
33053
|
+
if (parsed.forceRecovery && !(parsed.command === "service" && parsed.serviceAction === "recover")) {
|
|
33054
|
+
throw new Error("--force-recovery is accepted only by service recover.");
|
|
33055
|
+
}
|
|
33056
|
+
if (parsed.command === "service" && parsed.serviceAction === "recover") {
|
|
33057
|
+
if (!parsed.instanceFlagExplicit) {
|
|
33058
|
+
throw new Error(
|
|
33059
|
+
"Service recovery requires an explicit --instance NAME; an environment or default instance is not accepted."
|
|
33060
|
+
);
|
|
33061
|
+
}
|
|
33062
|
+
if (!parsed.forceRecovery) {
|
|
33063
|
+
throw new Error(
|
|
33064
|
+
`Service recovery is disruptive. Rerun with --instance ${parsed.instanceName} --force-recovery after checking service status and logs.`
|
|
33065
|
+
);
|
|
33066
|
+
}
|
|
33067
|
+
}
|
|
32447
33068
|
const config2 = await resolveInferenceHostCommandConfig(parsed, env, dependencies);
|
|
32448
33069
|
if (parsed.command === "login") {
|
|
32449
33070
|
return await login(config2, parsed, env, dependencies, warnings);
|
|
@@ -32482,7 +33103,18 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32482
33103
|
);
|
|
32483
33104
|
}
|
|
32484
33105
|
if (parsed.command === "service") {
|
|
32485
|
-
|
|
33106
|
+
const mutationActions = /* @__PURE__ */ new Set(["install", "start", "stop", "recover", "uninstall"]);
|
|
33107
|
+
if (!parsed.serviceAction || !mutationActions.has(parsed.serviceAction)) {
|
|
33108
|
+
return await serviceCommand(config2, parsed, env, dependencies, warnings);
|
|
33109
|
+
}
|
|
33110
|
+
const recoveryCommandLock = await acquireInferenceHostProcessLock(
|
|
33111
|
+
serviceRecoveryCommandLockPath(config2)
|
|
33112
|
+
);
|
|
33113
|
+
try {
|
|
33114
|
+
return await serviceCommand(config2, parsed, env, dependencies, warnings);
|
|
33115
|
+
} finally {
|
|
33116
|
+
await recoveryCommandLock.release();
|
|
33117
|
+
}
|
|
32486
33118
|
}
|
|
32487
33119
|
if (parsed.command === "status") {
|
|
32488
33120
|
return await localStatus(config2, parsed, dependencies, warnings);
|
|
@@ -32511,7 +33143,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32511
33143
|
};
|
|
32512
33144
|
}
|
|
32513
33145
|
}
|
|
32514
|
-
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;
|
|
33146
|
+
var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, serviceRecoveryCommandLockPath, 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, preparePortableDurableAdapter, defaultRunPortableDurableAdapter, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, recoveryBackupPath, serviceRecoveryTransactionPath, readServiceRecoveryTransaction, assertResumableCodexRecoveryEvidence, recoverInstalledCodexService, serviceCommand, hasExplicitCredentialStoreConfiguration, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
|
|
32515
33147
|
var init_cli = __esm({
|
|
32516
33148
|
"lib/inference-host/cli.ts"() {
|
|
32517
33149
|
"use strict";
|
|
@@ -32520,6 +33152,9 @@ var init_cli = __esm({
|
|
|
32520
33152
|
init_agent_client();
|
|
32521
33153
|
init_agent_state();
|
|
32522
33154
|
init_codex_adapter();
|
|
33155
|
+
init_codex_recovery_process();
|
|
33156
|
+
init_copilot_adapter();
|
|
33157
|
+
init_durable_adapter();
|
|
32523
33158
|
init_codex_app_server();
|
|
32524
33159
|
init_codex_binary();
|
|
32525
33160
|
init_config();
|
|
@@ -32536,6 +33171,7 @@ var init_cli = __esm({
|
|
|
32536
33171
|
revocationCheckpointPath = (config2) => `${config2.statePath}.revoke.json`;
|
|
32537
33172
|
foregroundHostLockPath = (config2) => `${config2.processLockPath}.foreground`;
|
|
32538
33173
|
credentialLifecycleLockPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.lock`;
|
|
33174
|
+
serviceRecoveryCommandLockPath = (config2) => `${config2.supervisorProcessLockPath}.service-recovery-command`;
|
|
32539
33175
|
AGENT_HEARTBEAT_INTERVAL_MS = 3e3;
|
|
32540
33176
|
AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS = 9e3;
|
|
32541
33177
|
retryableAgentHeartbeatError = (error48) => {
|
|
@@ -32601,7 +33237,7 @@ var init_cli = __esm({
|
|
|
32601
33237
|
Commands:
|
|
32602
33238
|
login Authorize the isolated VTX insights:inference grant
|
|
32603
33239
|
codex-login Sign the automated Codex host into a ChatGPT subscription
|
|
32604
|
-
run Run
|
|
33240
|
+
run Run a supported durable inference adapter in the foreground
|
|
32605
33241
|
agent-connect Advertise a model from a compatible agent harness
|
|
32606
33242
|
agent-run Keep an agent-driven host online in the foreground
|
|
32607
33243
|
agent-next Claim the next exact VTX inference request
|
|
@@ -32609,7 +33245,7 @@ Commands:
|
|
|
32609
33245
|
agent-fail Submit one truthful agent failure from stdin
|
|
32610
33246
|
service Install and control the durable background host
|
|
32611
33247
|
status Inspect local host and credential state
|
|
32612
|
-
doctor Verify credentials,
|
|
33248
|
+
doctor Verify credentials, provider runtime, and private state
|
|
32613
33249
|
logout Remove local VTX host state without revoking the grant
|
|
32614
33250
|
revoke Revoke the VTX grant and remove local host state
|
|
32615
33251
|
codex-logout Remove the automated host's dedicated Codex login
|
|
@@ -32617,7 +33253,8 @@ Commands:
|
|
|
32617
33253
|
Common options:
|
|
32618
33254
|
--json Emit machine-readable JSON
|
|
32619
33255
|
--instance NAME Target an isolated local subscription instance (default: default)
|
|
32620
|
-
--
|
|
33256
|
+
--adapter ID Durable adapter: codex or copilot
|
|
33257
|
+
--max-concurrency N Optional positive-integer local slot limit (default: unlimited)
|
|
32621
33258
|
--help, -h Show this help
|
|
32622
33259
|
|
|
32623
33260
|
If the OS credential store cannot retain the VTX grant, set
|
|
@@ -32631,8 +33268,10 @@ Durable service:
|
|
|
32631
33268
|
vtx inference-host login --instance codex-2
|
|
32632
33269
|
vtx inference-host codex-login --instance codex-2
|
|
32633
33270
|
vtx inference-host service install --instance codex-2
|
|
33271
|
+
vtx inference-host service install --adapter copilot --instance copilot-1
|
|
32634
33272
|
vtx inference-host service uninstall --instance codex-2
|
|
32635
|
-
vtx inference-host service
|
|
33273
|
+
vtx inference-host service recover --instance codex-1 --force-recovery
|
|
33274
|
+
vtx inference-host service <start|stop|status|logs|recover|uninstall>
|
|
32636
33275
|
`;
|
|
32637
33276
|
parseHostConcurrency = (raw, label) => {
|
|
32638
33277
|
const value = Number(raw);
|
|
@@ -32648,6 +33287,7 @@ Durable service:
|
|
|
32648
33287
|
let maxConcurrency = configuredConcurrency ? parseHostConcurrency(configuredConcurrency, "VTX_INFERENCE_HOST_MAX_CONCURRENCY") : null;
|
|
32649
33288
|
let instanceName = String(env.VTX_INFERENCE_HOST_INSTANCE || "default").trim();
|
|
32650
33289
|
let instanceExplicit = Boolean(String(env.VTX_INFERENCE_HOST_INSTANCE || "").trim());
|
|
33290
|
+
let instanceFlagExplicit = false;
|
|
32651
33291
|
let displayName = String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim() || "Codex subscription host";
|
|
32652
33292
|
let displayNameExplicit = Boolean(String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim());
|
|
32653
33293
|
let adapter = null;
|
|
@@ -32657,6 +33297,7 @@ Durable service:
|
|
|
32657
33297
|
let waitSeconds = 50;
|
|
32658
33298
|
let lines = 100;
|
|
32659
33299
|
let serviceManifestPath = null;
|
|
33300
|
+
let forceRecovery = false;
|
|
32660
33301
|
const positionals = [];
|
|
32661
33302
|
for (let index = 0; index < argv2.length; index += 1) {
|
|
32662
33303
|
const argument = argv2[index];
|
|
@@ -32668,6 +33309,10 @@ Durable service:
|
|
|
32668
33309
|
once = true;
|
|
32669
33310
|
continue;
|
|
32670
33311
|
}
|
|
33312
|
+
if (argument === "--force-recovery") {
|
|
33313
|
+
forceRecovery = true;
|
|
33314
|
+
continue;
|
|
33315
|
+
}
|
|
32671
33316
|
if (argument === "--max-concurrency") {
|
|
32672
33317
|
const raw = argv2[index + 1];
|
|
32673
33318
|
if (!raw) throw new Error("--max-concurrency requires a value.");
|
|
@@ -32706,6 +33351,7 @@ Durable service:
|
|
|
32706
33351
|
if (argument === "--instance") {
|
|
32707
33352
|
instanceName = raw.trim();
|
|
32708
33353
|
instanceExplicit = true;
|
|
33354
|
+
instanceFlagExplicit = true;
|
|
32709
33355
|
}
|
|
32710
33356
|
index += 1;
|
|
32711
33357
|
continue;
|
|
@@ -32728,6 +33374,8 @@ Durable service:
|
|
|
32728
33374
|
}
|
|
32729
33375
|
if (command === "agent-connect" && !displayNameExplicit) {
|
|
32730
33376
|
displayName = "Agent-driven inference host";
|
|
33377
|
+
} else if (adapter && isDurableInferenceAdapterId(adapter) && !displayNameExplicit) {
|
|
33378
|
+
displayName = `${durableAdapterDisplayName(adapter)} subscription host`;
|
|
32731
33379
|
}
|
|
32732
33380
|
return {
|
|
32733
33381
|
command,
|
|
@@ -32736,6 +33384,7 @@ Durable service:
|
|
|
32736
33384
|
maxConcurrency,
|
|
32737
33385
|
instanceName,
|
|
32738
33386
|
instanceExplicit,
|
|
33387
|
+
instanceFlagExplicit,
|
|
32739
33388
|
displayName,
|
|
32740
33389
|
adapter,
|
|
32741
33390
|
modelId,
|
|
@@ -32744,13 +33393,14 @@ Durable service:
|
|
|
32744
33393
|
waitSeconds,
|
|
32745
33394
|
lines,
|
|
32746
33395
|
serviceAction,
|
|
32747
|
-
serviceManifestPath
|
|
33396
|
+
serviceManifestPath,
|
|
33397
|
+
forceRecovery
|
|
32748
33398
|
};
|
|
32749
33399
|
};
|
|
32750
33400
|
defaultOpenBrowser = (url2) => {
|
|
32751
33401
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
32752
33402
|
const args = process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
32753
|
-
const child =
|
|
33403
|
+
const child = spawn7(command, args, {
|
|
32754
33404
|
detached: true,
|
|
32755
33405
|
stdio: "ignore",
|
|
32756
33406
|
windowsHide: true
|
|
@@ -32870,7 +33520,7 @@ Durable service:
|
|
|
32870
33520
|
return raw !== null;
|
|
32871
33521
|
};
|
|
32872
33522
|
inspectCodexAuthentication = async (config2) => {
|
|
32873
|
-
const path =
|
|
33523
|
+
const path = join7(config2.codexHomePath, "auth.json");
|
|
32874
33524
|
try {
|
|
32875
33525
|
const before = await lstat4(path);
|
|
32876
33526
|
const canonical = await realpath4(path);
|
|
@@ -32888,7 +33538,7 @@ Durable service:
|
|
|
32888
33538
|
runtimeReceiptPath(config2),
|
|
32889
33539
|
"Inference host runtime receipt file"
|
|
32890
33540
|
);
|
|
32891
|
-
await
|
|
33541
|
+
await rm6(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
|
|
32892
33542
|
await clearInferencePrivateFile(
|
|
32893
33543
|
codexRecoveryPath(config2),
|
|
32894
33544
|
"Codex attempt recovery file"
|
|
@@ -32914,10 +33564,14 @@ Durable service:
|
|
|
32914
33564
|
codexRecoveryPath(config2),
|
|
32915
33565
|
"Codex attempt recovery file"
|
|
32916
33566
|
);
|
|
33567
|
+
const serviceRecoveryPresent = await fileExistsPrivately(
|
|
33568
|
+
`${config2.supervisorStatePath}.service-recovery.json`,
|
|
33569
|
+
"Inference-host service recovery transaction"
|
|
33570
|
+
);
|
|
32917
33571
|
const revocationCheckpointPresent = await readRevocationCheckpoint(config2) !== null;
|
|
32918
33572
|
const agentAttemptPresent = await readInferenceAgentAttemptState(config2.statePath) !== null;
|
|
32919
33573
|
const agentNextRecoveryPresent = await readInferenceAgentNextState(config2.statePath) !== null;
|
|
32920
|
-
if (pendingAttempts > 0 || codexRecoveryPresent || revocationCheckpointPresent || agentAttemptPresent || agentNextRecoveryPresent) {
|
|
33574
|
+
if (pendingAttempts > 0 || codexRecoveryPresent || serviceRecoveryPresent || revocationCheckpointPresent || agentAttemptPresent || agentNextRecoveryPresent) {
|
|
32921
33575
|
throw new Error(
|
|
32922
33576
|
"Inference host logout refused because attempt recovery is still pending (Codex, agent-driven, or revocation recovery). Rerun agent-next, complete or fail the active attempt, run the automated host to reconcile it, or resume revoke."
|
|
32923
33577
|
);
|
|
@@ -32966,6 +33620,49 @@ Durable service:
|
|
|
32966
33620
|
signal: options.signal
|
|
32967
33621
|
}).run();
|
|
32968
33622
|
};
|
|
33623
|
+
preparePortableDurableAdapter = async (adapterId, signal) => {
|
|
33624
|
+
const adapter = new CopilotSubscriptionAdapter();
|
|
33625
|
+
return { adapter, preflight: await adapter.preflight(signal) };
|
|
33626
|
+
};
|
|
33627
|
+
defaultRunPortableDurableAdapter = async (options) => {
|
|
33628
|
+
const dependencies = createDefaultInferenceHostRunnerDependencies({
|
|
33629
|
+
apiUrl: options.config.apiUrl,
|
|
33630
|
+
statePath: options.config.statePath,
|
|
33631
|
+
processLockPath: options.config.processLockPath,
|
|
33632
|
+
credentialStore: options.credentialStore,
|
|
33633
|
+
codexAdapter: options.adapter,
|
|
33634
|
+
envelopePublicKey: (credential) => externalInferenceEnvelopePublicKey(credential.x25519_private_key),
|
|
33635
|
+
clientVersion: INFERENCE_HOST_CLI_VERSION,
|
|
33636
|
+
runtimeReceiptPath: runtimeReceiptPath(options.config)
|
|
33637
|
+
});
|
|
33638
|
+
const borrowedProcessLock = {
|
|
33639
|
+
path: options.processLock.path,
|
|
33640
|
+
ownerToken: options.processLock.ownerToken,
|
|
33641
|
+
release: async () => void 0
|
|
33642
|
+
};
|
|
33643
|
+
return await new InferenceHostRunner({
|
|
33644
|
+
...dependencies,
|
|
33645
|
+
acquireProcessLock: async () => borrowedProcessLock
|
|
33646
|
+
}, {
|
|
33647
|
+
adapterId: options.preflight.adapterId,
|
|
33648
|
+
usageSource: options.preflight.adapterId,
|
|
33649
|
+
structuredOutput: false,
|
|
33650
|
+
sameAttemptRecovery: options.preflight.sameAttemptRecovery,
|
|
33651
|
+
instanceName: options.config.instanceName,
|
|
33652
|
+
displayName: options.displayName,
|
|
33653
|
+
authenticatedAccountIdentity: options.preflight.authenticatedAccountIdentity,
|
|
33654
|
+
authenticatedAccountEmail: options.preflight.authenticatedAccountEmail,
|
|
33655
|
+
authenticatedAccountPlan: options.preflight.authenticatedAccountPlan,
|
|
33656
|
+
codexRateLimits: options.preflight.rateLimits,
|
|
33657
|
+
codexModelCapabilities: options.preflight.modelCapabilities,
|
|
33658
|
+
protocolVersion: EXTERNAL_INFERENCE_CONTRACT_VERSION,
|
|
33659
|
+
adapterRuntimeVersion: options.preflight.runtimeVersion,
|
|
33660
|
+
maxConcurrency: options.maxConcurrency,
|
|
33661
|
+
once: options.once,
|
|
33662
|
+
emitDiagnosticEvent: options.emitDiagnosticEvent,
|
|
33663
|
+
signal: options.signal
|
|
33664
|
+
}).run();
|
|
33665
|
+
};
|
|
32969
33666
|
login = async (config2, parsed, env, dependencies, warnings) => {
|
|
32970
33667
|
const credentialLock = await acquireInferenceHostProcessLock(
|
|
32971
33668
|
credentialLifecycleLockPath(config2)
|
|
@@ -33232,6 +33929,10 @@ Waiting for approval...
|
|
|
33232
33929
|
codexRecoveryPath(config2),
|
|
33233
33930
|
"Codex attempt recovery file"
|
|
33234
33931
|
);
|
|
33932
|
+
const serviceRecoveryPresent = await fileExistsPrivately(
|
|
33933
|
+
`${config2.supervisorStatePath}.service-recovery.json`,
|
|
33934
|
+
"Inference-host service recovery transaction"
|
|
33935
|
+
);
|
|
33235
33936
|
const agentAttempt = await readInferenceAgentAttemptState(config2.statePath);
|
|
33236
33937
|
const agentNextRecoveryPresent = await readInferenceAgentNextState(config2.statePath) !== null;
|
|
33237
33938
|
return {
|
|
@@ -33260,6 +33961,7 @@ Waiting for approval...
|
|
|
33260
33961
|
} : null,
|
|
33261
33962
|
agent_next_recovery_present: agentNextRecoveryPresent,
|
|
33262
33963
|
codex_recovery_present: recoveryPresent,
|
|
33964
|
+
service_recovery_present: serviceRecoveryPresent,
|
|
33263
33965
|
codex_auth: codexAuthentication,
|
|
33264
33966
|
source: "local_only"
|
|
33265
33967
|
}, parsed.json),
|
|
@@ -33308,6 +34010,35 @@ Waiting for approval...
|
|
|
33308
34010
|
detail: error48 instanceof Error ? error48.message : "unavailable"
|
|
33309
34011
|
});
|
|
33310
34012
|
}
|
|
34013
|
+
const adapterId = parsed.adapter ?? "codex";
|
|
34014
|
+
if (!isDurableInferenceAdapterId(adapterId)) {
|
|
34015
|
+
checks.push({ name: "provider_runtime", ok: false, detail: "unsupported-adapter" });
|
|
34016
|
+
} else if (adapterId !== "codex") {
|
|
34017
|
+
try {
|
|
34018
|
+
const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
|
|
34019
|
+
adapterId
|
|
34020
|
+
);
|
|
34021
|
+
checks.push({
|
|
34022
|
+
name: "provider_runtime",
|
|
34023
|
+
ok: true,
|
|
34024
|
+
detail: `${adapterId}:${prepared.preflight.runtimeVersion}:${prepared.preflight.modelCapabilities.length}-models`
|
|
34025
|
+
});
|
|
34026
|
+
await prepared.adapter.close?.();
|
|
34027
|
+
} catch (error48) {
|
|
34028
|
+
checks.push({
|
|
34029
|
+
name: "provider_runtime",
|
|
34030
|
+
ok: false,
|
|
34031
|
+
detail: error48 instanceof Error ? error48.message : "unavailable"
|
|
34032
|
+
});
|
|
34033
|
+
}
|
|
34034
|
+
const healthy2 = checks.every((check2) => check2.ok);
|
|
34035
|
+
return {
|
|
34036
|
+
exitCode: healthy2 ? 0 : 1,
|
|
34037
|
+
stdout: render({ healthy: healthy2, adapter: adapterId, checks, source: "local_only" }, parsed.json),
|
|
34038
|
+
stderr: warnings.length > 0 ? `${warnings.join("\n")}
|
|
34039
|
+
` : ""
|
|
34040
|
+
};
|
|
34041
|
+
}
|
|
33311
34042
|
try {
|
|
33312
34043
|
const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
|
|
33313
34044
|
checks.push({
|
|
@@ -33524,6 +34255,37 @@ Waiting for approval...
|
|
|
33524
34255
|
if (await store.readRecovery?.()) {
|
|
33525
34256
|
throw new Error("Inference host credential recovery is required before the runner can start.");
|
|
33526
34257
|
}
|
|
34258
|
+
const adapterId = parsed.adapter ?? "codex";
|
|
34259
|
+
if (!isDurableInferenceAdapterId(adapterId)) {
|
|
34260
|
+
throw new Error(`Adapter ${adapterId} does not have a supported durable integration.`);
|
|
34261
|
+
}
|
|
34262
|
+
const emitDiagnosticEvent = parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
|
|
34263
|
+
process.stdout.write(text);
|
|
34264
|
+
}))(`${JSON.stringify(event)}
|
|
34265
|
+
`) : void 0;
|
|
34266
|
+
if (adapterId !== "codex") {
|
|
34267
|
+
const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
|
|
34268
|
+
adapterId,
|
|
34269
|
+
cancellation.signal
|
|
34270
|
+
);
|
|
34271
|
+
const summary = await (dependencies.runPortableDurableAdapter ?? defaultRunPortableDurableAdapter)({
|
|
34272
|
+
config: config2,
|
|
34273
|
+
credentialStore: store,
|
|
34274
|
+
...prepared,
|
|
34275
|
+
displayName: parsed.displayName,
|
|
34276
|
+
maxConcurrency: parsed.maxConcurrency,
|
|
34277
|
+
once: parsed.once,
|
|
34278
|
+
processLock,
|
|
34279
|
+
emitDiagnosticEvent,
|
|
34280
|
+
signal: cancellation.signal
|
|
34281
|
+
});
|
|
34282
|
+
return {
|
|
34283
|
+
exitCode: 0,
|
|
34284
|
+
stdout: render({ status: "stopped", ...summary }, parsed.json),
|
|
34285
|
+
stderr: warnings.length > 0 ? `${warnings.join("\n")}
|
|
34286
|
+
` : ""
|
|
34287
|
+
};
|
|
34288
|
+
}
|
|
33527
34289
|
const verifiedBinary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
|
|
33528
34290
|
cancellation.signal.throwIfAborted();
|
|
33529
34291
|
await reconcileCodexAttemptRecovery({
|
|
@@ -33554,10 +34316,7 @@ Waiting for approval...
|
|
|
33554
34316
|
once: parsed.once,
|
|
33555
34317
|
env,
|
|
33556
34318
|
processLock,
|
|
33557
|
-
emitDiagnosticEvent
|
|
33558
|
-
process.stdout.write(text);
|
|
33559
|
-
}))(`${JSON.stringify(event)}
|
|
33560
|
-
`) : void 0,
|
|
34319
|
+
emitDiagnosticEvent,
|
|
33561
34320
|
signal: cancellation.signal
|
|
33562
34321
|
});
|
|
33563
34322
|
return {
|
|
@@ -34016,10 +34775,303 @@ Waiting for approval...
|
|
|
34016
34775
|
await lock2.release();
|
|
34017
34776
|
}
|
|
34018
34777
|
};
|
|
34778
|
+
recoveryBackupPath = (config2, transactionId) => `${codexRecoveryPath(config2)}.service-recovery-${transactionId}.backup`;
|
|
34779
|
+
serviceRecoveryTransactionPath = (config2) => `${config2.supervisorStatePath}.service-recovery.json`;
|
|
34780
|
+
readServiceRecoveryTransaction = async (config2) => {
|
|
34781
|
+
const raw = await readInferencePrivateFile(
|
|
34782
|
+
serviceRecoveryTransactionPath(config2),
|
|
34783
|
+
"Inference-host service recovery transaction"
|
|
34784
|
+
);
|
|
34785
|
+
if (raw === null) return null;
|
|
34786
|
+
let value;
|
|
34787
|
+
try {
|
|
34788
|
+
value = JSON.parse(raw);
|
|
34789
|
+
} catch {
|
|
34790
|
+
throw new Error("Inference-host service recovery transaction is not valid JSON.");
|
|
34791
|
+
}
|
|
34792
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34793
|
+
throw new Error("Inference-host service recovery transaction is invalid.");
|
|
34794
|
+
}
|
|
34795
|
+
const record2 = value;
|
|
34796
|
+
const exactKeys = [
|
|
34797
|
+
"schema_version",
|
|
34798
|
+
"phase",
|
|
34799
|
+
"transaction_id",
|
|
34800
|
+
"instance_name",
|
|
34801
|
+
"manifest_generation",
|
|
34802
|
+
"restore_desired_running",
|
|
34803
|
+
"recovery_backup_path",
|
|
34804
|
+
"recovery_backup_sha256",
|
|
34805
|
+
"cleanup_confirmed_attempt_ids",
|
|
34806
|
+
"started_at"
|
|
34807
|
+
];
|
|
34808
|
+
if (Object.keys(record2).sort().join("\0") !== exactKeys.sort().join("\0") || record2.schema_version !== "vtx_inference_service_recovery_v1" || record2.phase !== "prepared" && record2.phase !== "reconciled" || typeof record2.transaction_id !== "string" || !/^[0-9a-f-]{36}$/u.test(record2.transaction_id) || typeof record2.instance_name !== "string" || typeof record2.manifest_generation !== "string" || typeof record2.restore_desired_running !== "boolean" || typeof record2.recovery_backup_path !== "string" || typeof record2.recovery_backup_sha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record2.recovery_backup_sha256) || !Array.isArray(record2.cleanup_confirmed_attempt_ids) || record2.cleanup_confirmed_attempt_ids.some(
|
|
34809
|
+
(attemptId) => typeof attemptId !== "string" || attemptId.length < 1 || attemptId.length > 256
|
|
34810
|
+
) || new Set(record2.cleanup_confirmed_attempt_ids).size !== record2.cleanup_confirmed_attempt_ids.length || [...record2.cleanup_confirmed_attempt_ids].sort().join("\0") !== record2.cleanup_confirmed_attempt_ids.join("\0") || typeof record2.started_at !== "string" || !Number.isFinite(Date.parse(record2.started_at)) || new Date(record2.started_at).toISOString() !== record2.started_at) {
|
|
34811
|
+
throw new Error("Inference-host service recovery transaction is invalid.");
|
|
34812
|
+
}
|
|
34813
|
+
if (resolve5(record2.recovery_backup_path) !== resolve5(recoveryBackupPath(config2, record2.transaction_id))) {
|
|
34814
|
+
throw new Error("Inference-host service recovery backup path is invalid.");
|
|
34815
|
+
}
|
|
34816
|
+
return record2;
|
|
34817
|
+
};
|
|
34818
|
+
assertResumableCodexRecoveryEvidence = (backupAttempts, currentAttempts, cleanupConfirmedAttemptIds, phase) => {
|
|
34819
|
+
const backupIds = Object.keys(backupAttempts).sort();
|
|
34820
|
+
const currentIds = Object.keys(currentAttempts).sort();
|
|
34821
|
+
const backupIdSet = new Set(backupIds);
|
|
34822
|
+
const cleanupConfirmedIdSet = new Set(cleanupConfirmedAttemptIds);
|
|
34823
|
+
if (backupIds.length === 0 || currentIds.some((attemptId) => !backupIdSet.has(attemptId)) || cleanupConfirmedAttemptIds.some((attemptId) => !backupIdSet.has(attemptId)) || phase === "reconciled" && backupIds.some((attemptId) => !cleanupConfirmedIdSet.has(attemptId))) {
|
|
34824
|
+
throw new Error("Codex recovery evidence does not match the retained service recovery backup.");
|
|
34825
|
+
}
|
|
34826
|
+
for (const attemptId of backupIds) {
|
|
34827
|
+
const original = backupAttempts[attemptId];
|
|
34828
|
+
const current = currentAttempts[attemptId];
|
|
34829
|
+
if (!current) {
|
|
34830
|
+
if (!cleanupConfirmedIdSet.has(attemptId)) {
|
|
34831
|
+
throw new Error("Codex recovery evidence does not match the retained service recovery backup.");
|
|
34832
|
+
}
|
|
34833
|
+
continue;
|
|
34834
|
+
}
|
|
34835
|
+
if (cleanupConfirmedIdSet.has(attemptId) && !original.cleanupConfirmed && !(current.processState === "terminated" && current.cleanupConfirmed)) {
|
|
34836
|
+
throw new Error("Codex recovery evidence regressed after confirmed cleanup.");
|
|
34837
|
+
}
|
|
34838
|
+
const {
|
|
34839
|
+
processState: _originalProcessState,
|
|
34840
|
+
cleanupConfirmed: _originalCleanupConfirmed,
|
|
34841
|
+
...originalStable
|
|
34842
|
+
} = original;
|
|
34843
|
+
const {
|
|
34844
|
+
processState: _currentProcessState,
|
|
34845
|
+
cleanupConfirmed: _currentCleanupConfirmed,
|
|
34846
|
+
...currentStable
|
|
34847
|
+
} = current;
|
|
34848
|
+
if (JSON.stringify(originalStable) !== JSON.stringify(currentStable) || !(current.processState === original.processState && current.cleanupConfirmed === original.cleanupConfirmed) && !(current.processState === "terminated" && current.cleanupConfirmed)) {
|
|
34849
|
+
throw new Error("Codex recovery evidence changed outside an exact cleanup transition.");
|
|
34850
|
+
}
|
|
34851
|
+
}
|
|
34852
|
+
};
|
|
34853
|
+
recoverInstalledCodexService = async (config2, parsed, env, dependencies, manager) => {
|
|
34854
|
+
if (!parsed.instanceFlagExplicit) {
|
|
34855
|
+
throw new Error(
|
|
34856
|
+
"Service recovery requires an explicit --instance NAME; an environment or default instance is not accepted."
|
|
34857
|
+
);
|
|
34858
|
+
}
|
|
34859
|
+
if (!parsed.forceRecovery) {
|
|
34860
|
+
throw new Error(
|
|
34861
|
+
`Service recovery is disruptive. Rerun with --instance ${config2.instanceName} --force-recovery after checking service status and logs.`
|
|
34862
|
+
);
|
|
34863
|
+
}
|
|
34864
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34865
|
+
credentialLifecycleLockPath(config2)
|
|
34866
|
+
);
|
|
34867
|
+
try {
|
|
34868
|
+
const installedManifest = await readInferenceHostServiceManifest(
|
|
34869
|
+
`${config2.supervisorStatePath}.service.json`
|
|
34870
|
+
);
|
|
34871
|
+
const installedWorker = installedManifest?.workers.find(
|
|
34872
|
+
(worker) => worker.instance_name === config2.instanceName
|
|
34873
|
+
);
|
|
34874
|
+
if (installedWorker && installedWorker.adapter !== "codex") {
|
|
34875
|
+
throw new Error(
|
|
34876
|
+
`Guarded same-attempt recovery is not supported for ${installedWorker.adapter}; interrupted attempts remain quarantined as outcome_unknown.`
|
|
34877
|
+
);
|
|
34878
|
+
}
|
|
34879
|
+
const recoveryStore = new FileCodexAttemptRecoveryStore(codexRecoveryPath(config2));
|
|
34880
|
+
const existingTransaction = await readServiceRecoveryTransaction(config2);
|
|
34881
|
+
if (existingTransaction && existingTransaction.instance_name !== config2.instanceName) {
|
|
34882
|
+
throw new Error(
|
|
34883
|
+
`Service recovery for instance ${existingTransaction.instance_name} must finish before another instance can recover.`
|
|
34884
|
+
);
|
|
34885
|
+
}
|
|
34886
|
+
const initialAttempts = await recoveryStore.loadAll();
|
|
34887
|
+
const pendingAttempts = Object.values(initialAttempts).filter(
|
|
34888
|
+
(checkpoint) => !checkpoint.cleanupConfirmed
|
|
34889
|
+
);
|
|
34890
|
+
if (pendingAttempts.length === 0 && !existingTransaction) {
|
|
34891
|
+
const status = await manager.status();
|
|
34892
|
+
return {
|
|
34893
|
+
exitCode: 0,
|
|
34894
|
+
stdout: render({
|
|
34895
|
+
status: "service_recovery_not_needed",
|
|
34896
|
+
instance_name: config2.instanceName,
|
|
34897
|
+
recovery_needed: false,
|
|
34898
|
+
recovered_attempt_count: 0,
|
|
34899
|
+
outcome_unknown_attempt_count: 0,
|
|
34900
|
+
desired_running: status.desired_running,
|
|
34901
|
+
manager_active: status.manager_active,
|
|
34902
|
+
worker_runtime_state: status.workers.find(
|
|
34903
|
+
(worker) => worker.instance_name === config2.instanceName
|
|
34904
|
+
)?.runtime_state ?? "unknown"
|
|
34905
|
+
}, parsed.json),
|
|
34906
|
+
stderr: ""
|
|
34907
|
+
};
|
|
34908
|
+
}
|
|
34909
|
+
const recoveryRaw = await readInferencePrivateFile(
|
|
34910
|
+
codexRecoveryPath(config2),
|
|
34911
|
+
"Codex attempt recovery file"
|
|
34912
|
+
);
|
|
34913
|
+
if (recoveryRaw === null && existingTransaction?.phase !== "reconciled") {
|
|
34914
|
+
throw new Error("Codex recovery evidence changed before service recovery began.");
|
|
34915
|
+
}
|
|
34916
|
+
const transactionId = existingTransaction?.transaction_id ?? randomUUID2();
|
|
34917
|
+
const backupPath = existingTransaction?.recovery_backup_path ?? recoveryBackupPath(config2, transactionId);
|
|
34918
|
+
if (!existingTransaction) {
|
|
34919
|
+
await writeAtomicInferencePrivateFile(backupPath, recoveryRaw);
|
|
34920
|
+
}
|
|
34921
|
+
const backupRaw = await readInferencePrivateFile(
|
|
34922
|
+
backupPath,
|
|
34923
|
+
"Codex service recovery backup"
|
|
34924
|
+
);
|
|
34925
|
+
const backupSha256 = backupRaw === null ? null : createHash7("sha256").update(backupRaw).digest("hex");
|
|
34926
|
+
if (backupSha256 === null || existingTransaction && backupSha256 !== existingTransaction.recovery_backup_sha256) {
|
|
34927
|
+
throw new Error("Codex service recovery backup is missing or changed.");
|
|
34928
|
+
}
|
|
34929
|
+
const backupAttempts = await new FileCodexAttemptRecoveryStore(backupPath).loadAll();
|
|
34930
|
+
assertResumableCodexRecoveryEvidence(
|
|
34931
|
+
backupAttempts,
|
|
34932
|
+
initialAttempts,
|
|
34933
|
+
existingTransaction?.cleanup_confirmed_attempt_ids ?? [],
|
|
34934
|
+
existingTransaction?.phase ?? "prepared"
|
|
34935
|
+
);
|
|
34936
|
+
const recoverySha256 = recoveryRaw === null ? null : createHash7("sha256").update(recoveryRaw).digest("hex");
|
|
34937
|
+
const unresolvedAttemptIds = Object.keys(backupAttempts).filter((attemptId) => !existingTransaction?.cleanup_confirmed_attempt_ids.includes(attemptId) && !initialAttempts[attemptId]?.cleanupConfirmed);
|
|
34938
|
+
const binary = unresolvedAttemptIds.length > 0 ? await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env) : null;
|
|
34939
|
+
let activeTransaction = existingTransaction;
|
|
34940
|
+
let completed = false;
|
|
34941
|
+
try {
|
|
34942
|
+
const recovered = await manager.recoverInstance(config2.instanceName, async ({ manifest }) => {
|
|
34943
|
+
const workerLocks = [];
|
|
34944
|
+
try {
|
|
34945
|
+
for (const worker of [...manifest.workers].sort(
|
|
34946
|
+
(left, right) => left.instance_name.localeCompare(right.instance_name)
|
|
34947
|
+
)) {
|
|
34948
|
+
const workerConfig = resolveInferenceHostConfig({
|
|
34949
|
+
...env,
|
|
34950
|
+
...worker.runtime_environment,
|
|
34951
|
+
VTX_INFERENCE_HOST_INSTANCE: worker.instance_name
|
|
34952
|
+
});
|
|
34953
|
+
workerLocks.push(await acquireInferenceHostProcessLock(
|
|
34954
|
+
foregroundHostLockPath(workerConfig)
|
|
34955
|
+
));
|
|
34956
|
+
workerLocks.push(await acquireInferenceHostProcessLock(workerConfig.processLockPath));
|
|
34957
|
+
}
|
|
34958
|
+
try {
|
|
34959
|
+
if (!activeTransaction) {
|
|
34960
|
+
throw new Error("Inference-host service recovery transaction was not prepared.");
|
|
34961
|
+
}
|
|
34962
|
+
const attempts = await recoveryStore.loadAll();
|
|
34963
|
+
assertResumableCodexRecoveryEvidence(
|
|
34964
|
+
backupAttempts,
|
|
34965
|
+
attempts,
|
|
34966
|
+
activeTransaction.cleanup_confirmed_attempt_ids,
|
|
34967
|
+
activeTransaction.phase
|
|
34968
|
+
);
|
|
34969
|
+
const unresolved = Object.keys(backupAttempts).filter((attemptId) => !activeTransaction.cleanup_confirmed_attempt_ids.includes(attemptId) && !attempts[attemptId]?.cleanupConfirmed);
|
|
34970
|
+
if (unresolved.length === 0) return 0;
|
|
34971
|
+
const currentRaw = await readInferencePrivateFile(
|
|
34972
|
+
codexRecoveryPath(config2),
|
|
34973
|
+
"Codex attempt recovery file"
|
|
34974
|
+
);
|
|
34975
|
+
if (currentRaw === null || recoverySha256 === null || createHash7("sha256").update(currentRaw).digest("hex") !== recoverySha256) {
|
|
34976
|
+
throw new Error("Codex recovery evidence changed while the service was stopping.");
|
|
34977
|
+
}
|
|
34978
|
+
const proof = await (dependencies.proveCodexQuiescence ?? proveCodexSameBootQuiescence)({
|
|
34979
|
+
attempts,
|
|
34980
|
+
confirmNoManagedCodexProcesses: async () => await (dependencies.confirmNoManagedCodexProcesses ?? confirmNoVtxManagedCodexProcesses)({ pinnedBinaryPath: binary.path })
|
|
34981
|
+
});
|
|
34982
|
+
return await (dependencies.reconcileCodexRecovery ?? reconcileCodexAttemptRecovery)({
|
|
34983
|
+
recoveryHooks: recoveryStore,
|
|
34984
|
+
codexHome: config2.codexHomePath,
|
|
34985
|
+
guardianReceiptRoot: codexGuardianReceiptRoot(config2),
|
|
34986
|
+
sameBootQuiescenceProof: proof
|
|
34987
|
+
});
|
|
34988
|
+
} finally {
|
|
34989
|
+
if (!activeTransaction) {
|
|
34990
|
+
throw new Error("Inference-host service recovery transaction was not prepared.");
|
|
34991
|
+
}
|
|
34992
|
+
const progressedAttempts = await recoveryStore.loadAll();
|
|
34993
|
+
assertResumableCodexRecoveryEvidence(
|
|
34994
|
+
backupAttempts,
|
|
34995
|
+
progressedAttempts,
|
|
34996
|
+
activeTransaction.cleanup_confirmed_attempt_ids,
|
|
34997
|
+
activeTransaction.phase
|
|
34998
|
+
);
|
|
34999
|
+
const priorConfirmedIds = new Set(activeTransaction.cleanup_confirmed_attempt_ids);
|
|
35000
|
+
const cleanupConfirmedAttemptIds = Object.keys(backupAttempts).filter((attemptId) => priorConfirmedIds.has(attemptId) || progressedAttempts[attemptId]?.cleanupConfirmed).sort();
|
|
35001
|
+
activeTransaction = {
|
|
35002
|
+
...activeTransaction,
|
|
35003
|
+
phase: cleanupConfirmedAttemptIds.length === Object.keys(backupAttempts).length ? "reconciled" : "prepared",
|
|
35004
|
+
cleanup_confirmed_attempt_ids: cleanupConfirmedAttemptIds
|
|
35005
|
+
};
|
|
35006
|
+
await writeAtomicInferencePrivateFile(
|
|
35007
|
+
serviceRecoveryTransactionPath(config2),
|
|
35008
|
+
`${JSON.stringify(activeTransaction, null, 2)}
|
|
35009
|
+
`
|
|
35010
|
+
);
|
|
35011
|
+
}
|
|
35012
|
+
} finally {
|
|
35013
|
+
for (const lock2 of workerLocks.reverse()) await lock2.release();
|
|
35014
|
+
}
|
|
35015
|
+
}, {
|
|
35016
|
+
restoreDesiredRunning: existingTransaction?.restore_desired_running,
|
|
35017
|
+
expectedManifestGeneration: existingTransaction?.manifest_generation,
|
|
35018
|
+
prepare: existingTransaction ? void 0 : async ({ manifest, desiredRunning }) => {
|
|
35019
|
+
activeTransaction = {
|
|
35020
|
+
schema_version: "vtx_inference_service_recovery_v1",
|
|
35021
|
+
phase: "prepared",
|
|
35022
|
+
transaction_id: transactionId,
|
|
35023
|
+
instance_name: config2.instanceName,
|
|
35024
|
+
manifest_generation: manifest.generation,
|
|
35025
|
+
restore_desired_running: desiredRunning,
|
|
35026
|
+
recovery_backup_path: backupPath,
|
|
35027
|
+
recovery_backup_sha256: backupSha256,
|
|
35028
|
+
cleanup_confirmed_attempt_ids: [],
|
|
35029
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
35030
|
+
};
|
|
35031
|
+
await writeAtomicInferencePrivateFile(
|
|
35032
|
+
serviceRecoveryTransactionPath(config2),
|
|
35033
|
+
`${JSON.stringify(activeTransaction, null, 2)}
|
|
35034
|
+
`
|
|
35035
|
+
);
|
|
35036
|
+
}
|
|
35037
|
+
});
|
|
35038
|
+
completed = true;
|
|
35039
|
+
const outcomeUnknownAttemptCount = Object.values(backupAttempts).filter(
|
|
35040
|
+
(checkpoint) => checkpoint.dispatchOutcome === "outcome_unknown"
|
|
35041
|
+
).length;
|
|
35042
|
+
return {
|
|
35043
|
+
exitCode: 0,
|
|
35044
|
+
stdout: render({
|
|
35045
|
+
status: "service_recovered",
|
|
35046
|
+
instance_name: config2.instanceName,
|
|
35047
|
+
recovery_needed: true,
|
|
35048
|
+
recovered_attempt_count: recovered.result,
|
|
35049
|
+
outcome_unknown_attempt_count: outcomeUnknownAttemptCount,
|
|
35050
|
+
desired_running: recovered.status.desired_running,
|
|
35051
|
+
manager_active: recovered.status.manager_active,
|
|
35052
|
+
worker_runtime_state: recovered.status.workers.find(
|
|
35053
|
+
(worker) => worker.instance_name === config2.instanceName
|
|
35054
|
+
)?.runtime_state ?? "unknown"
|
|
35055
|
+
}, parsed.json),
|
|
35056
|
+
stderr: ""
|
|
35057
|
+
};
|
|
35058
|
+
} finally {
|
|
35059
|
+
if (completed) {
|
|
35060
|
+
await clearInferencePrivateFile(
|
|
35061
|
+
serviceRecoveryTransactionPath(config2),
|
|
35062
|
+
"Inference-host service recovery transaction"
|
|
35063
|
+
);
|
|
35064
|
+
await rm6(backupPath, { force: true });
|
|
35065
|
+
}
|
|
35066
|
+
}
|
|
35067
|
+
} finally {
|
|
35068
|
+
await credentialLock.release();
|
|
35069
|
+
}
|
|
35070
|
+
};
|
|
34019
35071
|
serviceCommand = async (config2, parsed, env, dependencies, warnings) => {
|
|
34020
35072
|
const action = parsed.serviceAction;
|
|
34021
35073
|
if (!action) {
|
|
34022
|
-
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
|
|
35074
|
+
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|recover|uninstall>.");
|
|
34023
35075
|
}
|
|
34024
35076
|
if (action === "run-internal") {
|
|
34025
35077
|
if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
|
|
@@ -34046,17 +35098,34 @@ Waiting for approval...
|
|
|
34046
35098
|
await serviceLock.release();
|
|
34047
35099
|
}
|
|
34048
35100
|
}
|
|
35101
|
+
if (parsed.forceRecovery && action !== "recover") {
|
|
35102
|
+
throw new Error("--force-recovery is accepted only by service recover.");
|
|
35103
|
+
}
|
|
34049
35104
|
const manager = dependencies.createServiceManager?.(
|
|
34050
35105
|
config2,
|
|
34051
35106
|
dependencies.serviceDependencies
|
|
34052
35107
|
) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
|
|
35108
|
+
if (action === "recover") {
|
|
35109
|
+
return await recoverInstalledCodexService(
|
|
35110
|
+
config2,
|
|
35111
|
+
parsed,
|
|
35112
|
+
env,
|
|
35113
|
+
dependencies,
|
|
35114
|
+
manager
|
|
35115
|
+
);
|
|
35116
|
+
}
|
|
34053
35117
|
if (action === "install") {
|
|
34054
35118
|
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34055
35119
|
credentialLifecycleLockPath(config2)
|
|
34056
35120
|
);
|
|
34057
35121
|
try {
|
|
35122
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
35123
|
+
throw new Error(
|
|
35124
|
+
"A guarded service recovery transaction must finish before installing a subscription."
|
|
35125
|
+
);
|
|
35126
|
+
}
|
|
34058
35127
|
const adapter = parsed.adapter || "codex";
|
|
34059
|
-
if (adapter
|
|
35128
|
+
if (!isDurableInferenceAdapterId(adapter)) {
|
|
34060
35129
|
throw new Error(
|
|
34061
35130
|
`Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
|
|
34062
35131
|
);
|
|
@@ -34068,7 +35137,31 @@ Waiting for approval...
|
|
|
34068
35137
|
if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
|
|
34069
35138
|
assertCredentialMatchesState2(state, credential);
|
|
34070
35139
|
if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
|
|
34071
|
-
throw new Error("
|
|
35140
|
+
throw new Error("Durable services discover their exact model and effort catalog from the authenticated runtime.");
|
|
35141
|
+
}
|
|
35142
|
+
if (adapter !== "codex") {
|
|
35143
|
+
const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
|
|
35144
|
+
adapter
|
|
35145
|
+
);
|
|
35146
|
+
try {
|
|
35147
|
+
const status2 = await manager.install({
|
|
35148
|
+
adapter,
|
|
35149
|
+
displayName: parsed.displayName,
|
|
35150
|
+
maxConcurrency: parsed.maxConcurrency,
|
|
35151
|
+
authenticatedAccountIdentity: prepared.preflight.authenticatedAccountIdentity,
|
|
35152
|
+
authenticatedAccountEmail: prepared.preflight.authenticatedAccountEmail,
|
|
35153
|
+
authenticatedAccountPlan: prepared.preflight.authenticatedAccountPlan
|
|
35154
|
+
});
|
|
35155
|
+
await writeInferenceHostCredentialContext(config2);
|
|
35156
|
+
return {
|
|
35157
|
+
exitCode: 0,
|
|
35158
|
+
stdout: render({ status: "service_installed", ...status2 }, parsed.json),
|
|
35159
|
+
stderr: warnings.length > 0 ? `${warnings.join("\n")}
|
|
35160
|
+
` : ""
|
|
35161
|
+
};
|
|
35162
|
+
} finally {
|
|
35163
|
+
await prepared.adapter.close?.();
|
|
35164
|
+
}
|
|
34072
35165
|
}
|
|
34073
35166
|
const auth = await inspectCodexAuthentication(config2);
|
|
34074
35167
|
if (!auth.present || !auth.private) {
|
|
@@ -34112,9 +35205,15 @@ Waiting for approval...
|
|
|
34112
35205
|
};
|
|
34113
35206
|
}
|
|
34114
35207
|
if (action === "start") {
|
|
35208
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
35209
|
+
throw new Error("A guarded service recovery transaction must finish before starting the service.");
|
|
35210
|
+
}
|
|
34115
35211
|
return { exitCode: 0, stdout: render({ status: "service_started", ...await manager.start() }, parsed.json), stderr: "" };
|
|
34116
35212
|
}
|
|
34117
35213
|
if (action === "stop") {
|
|
35214
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
35215
|
+
throw new Error("A guarded service recovery transaction must finish before stopping the service.");
|
|
35216
|
+
}
|
|
34118
35217
|
return { exitCode: 0, stdout: render({ status: "service_stopped", ...await manager.stop() }, parsed.json), stderr: "" };
|
|
34119
35218
|
}
|
|
34120
35219
|
if (action === "status") {
|
|
@@ -34127,6 +35226,11 @@ Waiting for approval...
|
|
|
34127
35226
|
credentialLifecycleLockPath(config2)
|
|
34128
35227
|
);
|
|
34129
35228
|
try {
|
|
35229
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
35230
|
+
throw new Error(
|
|
35231
|
+
"A guarded service recovery transaction must finish before uninstalling a subscription."
|
|
35232
|
+
);
|
|
35233
|
+
}
|
|
34130
35234
|
if (parsed.instanceExplicit) {
|
|
34131
35235
|
const retained = await readInferenceHostCredentialContext(config2);
|
|
34132
35236
|
const retainedIdentity = retained ? resolveInferenceHostConfig({
|
|
@@ -34145,7 +35249,7 @@ Waiting for approval...
|
|
|
34145
35249
|
await credentialLock.release();
|
|
34146
35250
|
}
|
|
34147
35251
|
}
|
|
34148
|
-
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
|
|
35252
|
+
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|recover|uninstall>.");
|
|
34149
35253
|
};
|
|
34150
35254
|
hasExplicitCredentialStoreConfiguration = (env) => Boolean(
|
|
34151
35255
|
String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "").trim() || String(env.VTX_INFERENCE_HOST_CREDENTIAL_FILE || "").trim()
|
|
@@ -34167,8 +35271,8 @@ Waiting for approval...
|
|
|
34167
35271
|
}
|
|
34168
35272
|
throw new Error("Inference host credential store identity is invalid.");
|
|
34169
35273
|
};
|
|
34170
|
-
commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "uninstall");
|
|
34171
|
-
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;
|
|
35274
|
+
commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "recover" || parsed.serviceAction === "uninstall");
|
|
35275
|
+
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 === "recover" || parsed.command === "service" && parsed.serviceAction === "uninstall" && parsed.instanceExplicit;
|
|
34172
35276
|
resolveInferenceHostCommandConfig = async (parsed, env, dependencies) => {
|
|
34173
35277
|
const selectedEnv = {
|
|
34174
35278
|
...env,
|
|
@@ -34191,6 +35295,9 @@ Waiting for approval...
|
|
|
34191
35295
|
const installedEnvironment = manifest?.workers.find(
|
|
34192
35296
|
(worker) => worker.instance_name === baseConfig.instanceName
|
|
34193
35297
|
)?.runtime_environment;
|
|
35298
|
+
if (parsed.command === "service" && parsed.serviceAction === "recover" && !installedEnvironment) {
|
|
35299
|
+
throw new Error(`Inference-host instance ${baseConfig.instanceName} is not installed.`);
|
|
35300
|
+
}
|
|
34194
35301
|
const persistedContext = installedEnvironment ? null : await readInferenceHostCredentialContext(baseConfig);
|
|
34195
35302
|
const revocationCheckpoint = !installedEnvironment && !persistedContext && parsed.command === "revoke" ? await readRevocationCheckpoint(baseConfig) : null;
|
|
34196
35303
|
const retainedEnvironment = installedEnvironment ?? (persistedContext ? {
|
|
@@ -34221,11 +35328,11 @@ Waiting for approval...
|
|
|
34221
35328
|
});
|
|
34222
35329
|
|
|
34223
35330
|
// lib/agent-core/config.ts
|
|
34224
|
-
import { mkdir as mkdir5, readFile as readFile6, rm as
|
|
34225
|
-
import { dirname as dirname5, join as
|
|
35331
|
+
import { mkdir as mkdir5, readFile as readFile6, rm as rm7, writeFile as writeFile3 } from "node:fs/promises";
|
|
35332
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
34226
35333
|
import { homedir as homedir3 } from "node:os";
|
|
34227
35334
|
function defaultBaseDir() {
|
|
34228
|
-
return
|
|
35335
|
+
return join8(homedir3(), ".vtx");
|
|
34229
35336
|
}
|
|
34230
35337
|
function resolveAgentCliConfig(env = process.env) {
|
|
34231
35338
|
const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
|
|
@@ -34233,8 +35340,8 @@ function resolveAgentCliConfig(env = process.env) {
|
|
|
34233
35340
|
const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
|
|
34234
35341
|
return {
|
|
34235
35342
|
apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
|
|
34236
|
-
tokenPath: String(env.VTX_TOKEN_PATH || "").trim() ||
|
|
34237
|
-
statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() ||
|
|
35343
|
+
tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join8(baseDir, "token.json"),
|
|
35344
|
+
statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join8(baseDir, "runtime-state.json"),
|
|
34238
35345
|
runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
|
|
34239
35346
|
activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
|
|
34240
35347
|
outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
|
|
@@ -34265,7 +35372,7 @@ async function writeStoredAgentAuth(path, auth) {
|
|
|
34265
35372
|
`, { encoding: "utf8", mode: 384 });
|
|
34266
35373
|
}
|
|
34267
35374
|
async function clearStoredAgentAuth(path) {
|
|
34268
|
-
await
|
|
35375
|
+
await rm7(path, { force: true });
|
|
34269
35376
|
}
|
|
34270
35377
|
async function readRuntimeState(path) {
|
|
34271
35378
|
try {
|
|
@@ -34298,7 +35405,7 @@ async function writeRuntimeState(path, state) {
|
|
|
34298
35405
|
`, { encoding: "utf8", mode: 384 });
|
|
34299
35406
|
}
|
|
34300
35407
|
async function clearRuntimeState(path) {
|
|
34301
|
-
await
|
|
35408
|
+
await rm7(path, { force: true });
|
|
34302
35409
|
}
|
|
34303
35410
|
var init_config2 = __esm({
|
|
34304
35411
|
"lib/agent-core/config.ts"() {
|
|
@@ -38718,7 +39825,7 @@ var init_utils = __esm({
|
|
|
38718
39825
|
});
|
|
38719
39826
|
|
|
38720
39827
|
// node_modules/ethers/lib.esm/crypto/crypto.js
|
|
38721
|
-
import { createHash as
|
|
39828
|
+
import { createHash as createHash8, createHmac, pbkdf2Sync, randomBytes as randomBytes5 } from "crypto";
|
|
38722
39829
|
var init_crypto2 = __esm({
|
|
38723
39830
|
"node_modules/ethers/lib.esm/crypto/crypto.js"() {
|
|
38724
39831
|
}
|
|
@@ -39932,10 +41039,10 @@ var init_sha22 = __esm({
|
|
|
39932
41039
|
init_crypto2();
|
|
39933
41040
|
init_utils();
|
|
39934
41041
|
_sha256 = function(data) {
|
|
39935
|
-
return
|
|
41042
|
+
return createHash8("sha256").update(data).digest();
|
|
39936
41043
|
};
|
|
39937
41044
|
_sha512 = function(data) {
|
|
39938
|
-
return
|
|
41045
|
+
return createHash8("sha512").update(data).digest();
|
|
39939
41046
|
};
|
|
39940
41047
|
__sha256 = _sha256;
|
|
39941
41048
|
__sha512 = _sha512;
|
|
@@ -52112,7 +53219,7 @@ __export(vtx_exports, {
|
|
|
52112
53219
|
runVtxCli: () => runVtxCli
|
|
52113
53220
|
});
|
|
52114
53221
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
52115
|
-
import { spawn as
|
|
53222
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
52116
53223
|
function render2(value, json2) {
|
|
52117
53224
|
if (json2) {
|
|
52118
53225
|
return `${JSON.stringify(value, null, 2)}
|
|
@@ -52142,7 +53249,7 @@ function openBrowser(url2) {
|
|
|
52142
53249
|
const platform = process.platform;
|
|
52143
53250
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
52144
53251
|
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
52145
|
-
const child =
|
|
53252
|
+
const child = spawn8(command, args, { detached: true, stdio: "ignore" });
|
|
52146
53253
|
child.unref();
|
|
52147
53254
|
}
|
|
52148
53255
|
function parseFlags(args) {
|