@vtxmacro/cli 2026.8.38 → 2026.8.40
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 +30 -10
- package/bin/vtx.js +688 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ The VTX host verifies that native runtime's version and digest before use; it
|
|
|
15
15
|
does not borrow a Codex binary from PATH or an editor extension.
|
|
16
16
|
For Windows-login auto-start, install and configure the CLI from native Windows;
|
|
17
17
|
a CLI installed only inside WSL cannot start the WSL virtual machine at login.
|
|
18
|
+
An npm update does not replace code already loaded by a running durable service.
|
|
19
|
+
After updating, check `vtx inference-host service status --json`. If the service
|
|
20
|
+
was already desired-running, run `vtx inference-host service stop` followed by
|
|
21
|
+
`vtx inference-host service start`, then verify status and logs. Leave an
|
|
22
|
+
intentionally stopped service stopped until you want to run it.
|
|
18
23
|
|
|
19
24
|
## Configure
|
|
20
25
|
|
|
@@ -58,10 +63,10 @@ exposed. Before starting a Trader, confirm that the VTX AI page shows the
|
|
|
58
63
|
intended authenticated ChatGPT email and plan. The host reads the live Codex
|
|
59
64
|
account window: a reached limit pauses new dispatch until its reported reset,
|
|
60
65
|
while a transient throttle uses a short bounded cooldown.
|
|
61
|
-
The selected AI model row
|
|
62
|
-
|
|
63
|
-
Account-wide quota percentages remain in
|
|
64
|
-
profile-scoped selector.
|
|
66
|
+
The selected AI model row estimates the open VTX profile's attributable
|
|
67
|
+
membership burn rate for that subscription, expressed as equivalent percentage
|
|
68
|
+
points per hour, day, and seven days. Account-wide quota percentages remain in
|
|
69
|
+
safe host diagnostics instead of the profile-scoped selector.
|
|
65
70
|
|
|
66
71
|
```bash
|
|
67
72
|
vtx inference-host login
|
|
@@ -86,12 +91,27 @@ distinct. Use `service uninstall --instance <name>` to remove one worker;
|
|
|
86
91
|
unqualified `service uninstall` removes the whole supervisor. The legacy
|
|
87
92
|
unqualified host commands continue to target `default`.
|
|
88
93
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
On native Windows, if service status or logs show that guarded recovery is
|
|
95
|
+
blocking one installed subscription, recover that exact instance without rebooting:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
vtx inference-host service recover --instance <name> --force-recovery --json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Recovery is intentionally noninteractive and requires both `--instance` and
|
|
102
|
+
`--force-recovery`; `--json` changes only the output format. The shared
|
|
103
|
+
supervisor briefly quiesces so every installed worker can stop cooperatively,
|
|
104
|
+
then VTX restores its previous desired state and the same peer subscriptions.
|
|
105
|
+
The command never kills an arbitrary process, logs either account out, changes
|
|
106
|
+
VTX profile settings, or controls a Trader. If any package-owned VTX automation
|
|
107
|
+
is still live, recovery fails closed and preserves its evidence.
|
|
108
|
+
|
|
109
|
+
For each VTX profile and lane, **Subscription #1** is tried first and later
|
|
110
|
+
compatible subscriptions follow in the saved Account cascade order.
|
|
111
|
+
Quota/credits exhaustion, unusable authentication, and exhausted recoverable
|
|
112
|
+
provider failures can advance the same logical call. VTX never changes
|
|
113
|
+
provider, model, effort, or response mode, and never cascades a bad request,
|
|
114
|
+
policy rejection, or uncertain dispatch outcome.
|
|
95
115
|
|
|
96
116
|
Automated hosts do not impose a subscription-specific profile-count or
|
|
97
117
|
concurrency limit by default. Set an explicit positive integer with
|
package/bin/vtx.js
CHANGED
|
@@ -38,7 +38,7 @@ var init_agent_cli_release = __esm({
|
|
|
38
38
|
"agent-cli-release.json"() {
|
|
39
39
|
agent_cli_release_default = {
|
|
40
40
|
package_name: "@vtxmacro/cli",
|
|
41
|
-
package_version: "2026.8.
|
|
41
|
+
package_version: "2026.8.40",
|
|
42
42
|
codex_package_name: "@openai/codex",
|
|
43
43
|
codex_version: "0.147.0",
|
|
44
44
|
platforms: {
|
|
@@ -14375,7 +14375,7 @@ var init_zod = __esm({
|
|
|
14375
14375
|
});
|
|
14376
14376
|
|
|
14377
14377
|
// lib/external-inference-contract.ts
|
|
14378
|
-
var EXTERNAL_INFERENCE_CONTRACT_VERSION, EXTERNAL_INFERENCE_CONTROLLER_SCHEMA_VERSION, EXTERNAL_INFERENCE_ADVERTISEMENT_SCHEMA_VERSION, EXTERNAL_INFERENCE_ENVELOPE_SCHEMA_VERSION, EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, LEGACY_EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION, EXTERNAL_INFERENCE_INPUT_HASH_DOMAIN, MAX_EXTERNAL_INFERENCE_STATUS_HOSTS, MAX_GENERATION, MAX_SAFE_INTEGER, MAX_PROMPT_BYTES, MAX_IMMUTABLE_JOB_INPUT_BYTES, MAX_CANDIDATE_PLAINTEXT_BYTES, MAX_CIPHERTEXT_CHARS, SHA256_INITIAL_STATE, SHA256_ROUND_CONSTANTS, rotateRight, sha256BytesHex, sha256Utf8Hex, computeImmutableJobInputSha256, identifierSchema, operationIdentifierSchema, sha256HexSchema, generationSchema, zeroBasedGenerationSchema, nonNegativeSafeIntegerSchema, positiveSafeIntegerSchema, sqlIntegerTokenCountSchema, timestampSchema, safeCodeSchema, externalInferenceAdapterIdSchema, reasoningEffortSchema, modelNameSchema, displayNameSchema, authenticatedAccountEmailSchema, protocolVersionSchema, base64UrlAlphabet, isCanonicalBase64Url, base64Url32BytesSchema, base64UrlNonceSchema, base64UrlCiphertextSchema, claimHandleSchema, boundedJsonTextSchema, boundedPromptTextSchema, laneSchema, botModeSchema, executionModeSchema, responseModeSchema, dispatchOutcomeSchema, responseOutcomeSchema, nativeModelControllerSchema, externalAgentModelControllerSchema, externalAgentUnboundControllerSchema, liveModelControllerSchema, snapshotModelControllerSchema, modelIdentityMappingSchema, advertisedModelSchema, hostAdvertisementSchema, envelopeAadSchema, validateEnvelopeAad, commonEnvelopeShape, sealedKeyWrapEnvelopeSchema, sealedInputEnvelopeSchema, sealedCandidateEnvelopeSchema, sealedEnvelopeSchema, screenerCandidateRuntimeBindingSchema, screenerClientLeaseBindingSchema, screenerRuntimeBindingSchema, serverRuntimeBindingSchema, clientRuntimeBindingSchema, runtimeBindingSchema, immutableJobInputSchema, canonicalizeJsonValue, serializeImmutableJobInput, reportedTokenUsageSchema, unavailableTokenUsageSchema, tokenUsageSchema, agentConnectRequestSchema, agentNextRequestSchema, agentHeartbeatRequestSchema, emptyAgentNextResultSchema, claimedAgentNextResultSchema, agentNextResultSchema, agentCompleteRequestSchema, agentFailRequestSchema, requestedModelSelectionSchema, completedModelProvenanceSchema, attemptModelProvenanceSchema, providerWeeklyQuotaReadSchema, hostHeartbeatRequestSchema, jobClaimRequestSchema, emptyJobClaimSchema, claimedJobSchema, jobClaimResultSchema, attemptStartRequestSchema, jobHeartbeatRequestSchema, continueHeartbeatDirectiveSchema, cancelHeartbeatDirectiveSchema, jobHeartbeatResultSchema, adapterOutcomeEvidenceSchema, completionBaseShape, validateCompletion, providerResponseCompletionSchema, decisionCandidateCompletionSchema, jobCompleteRequestSchema, jobFailRequestSchema, jobCancelRequestSchema, jobCancellationReceiptSchema, jobDeliveryRequestSchema, deliveryBaseShape, validateDeliveryCandidate, providerResponseDeliverySchema, decisionCandidateDeliverySchema, jobDeliveryResultSchema, jobConsumptionRequestSchema, jobConsumptionReceiptSchema, jobStateSchema, jobStatusReadSchema, hostStatusReadSchema, controllerBindingStatusReadSchema, queueStatusReadSchema, outcomeStatusReadSchema, externalInferenceVolumeWindowReadSchema, externalInferenceFailureCountReadSchema, externalInferenceHostDiagnosticsReadSchema, externalInferenceProfileActivityReadSchema, externalInferenceStatusReadSchema, nativeResearchEvidenceSourceSchema, externalAgentResearchEvidenceSourceSchema, researchEvidenceSourceSchema, reportedResearchTokenUsageSchema, unavailableResearchTokenUsageSchema, researchTokenUsageSchema, externalInferenceProductLineageBaseShape, traderExternalInferenceProductLineageSchema, decisionChatExternalInferenceProductLineageSchema, screenerExternalInferenceProductLineageSchema, externalInferenceProductLineageSchema, normalProductLinkageSchema, evidenceMaterializationMarkerSchema, deliveredExternalAgentSourceIdentitySchema, deliveredRequestEvidenceSchema, researchEvidenceBaseShape, researchEvidenceBaseSchema, validateExactUtf8, validateResearchEvidence, providerResponseResearchEvidenceSchema, decisionCandidateResearchEvidenceSchema, researchEvidenceSchema;
|
|
14378
|
+
var EXTERNAL_INFERENCE_CONTRACT_VERSION, EXTERNAL_INFERENCE_CONTROLLER_SCHEMA_VERSION, EXTERNAL_INFERENCE_ADVERTISEMENT_SCHEMA_VERSION, EXTERNAL_INFERENCE_ENVELOPE_SCHEMA_VERSION, EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, LEGACY_EXTERNAL_INFERENCE_JOB_INPUT_SCHEMA_VERSION, EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION, EXTERNAL_INFERENCE_INPUT_HASH_DOMAIN, MAX_EXTERNAL_INFERENCE_STATUS_HOSTS, MAX_GENERATION, MAX_SAFE_INTEGER, MAX_PROMPT_BYTES, MAX_IMMUTABLE_JOB_INPUT_BYTES, MAX_CANDIDATE_PLAINTEXT_BYTES, MAX_CIPHERTEXT_CHARS, SHA256_INITIAL_STATE, SHA256_ROUND_CONSTANTS, rotateRight, sha256BytesHex, sha256Utf8Hex, computeImmutableJobInputSha256, identifierSchema, operationIdentifierSchema, sha256HexSchema, generationSchema, zeroBasedGenerationSchema, nonNegativeSafeIntegerSchema, positiveSafeIntegerSchema, sqlIntegerTokenCountSchema, timestampSchema, safeCodeSchema, externalInferenceAdapterIdSchema, reasoningEffortSchema, modelNameSchema, displayNameSchema, authenticatedAccountEmailSchema, protocolVersionSchema, base64UrlAlphabet, isCanonicalBase64Url, base64Url32BytesSchema, base64UrlNonceSchema, base64UrlCiphertextSchema, claimHandleSchema, boundedJsonTextSchema, boundedPromptTextSchema, laneSchema, botModeSchema, executionModeSchema, responseModeSchema, dispatchOutcomeSchema, responseOutcomeSchema, nativeModelControllerSchema, externalAgentModelControllerSchema, externalAgentUnboundControllerSchema, orderedFallbackHostIdsSchema, externalInferenceFallbackHostIdsSchema, liveModelControllerSchema, snapshotModelControllerSchema, modelIdentityMappingSchema, advertisedModelSchema, hostAdvertisementSchema, envelopeAadSchema, validateEnvelopeAad, commonEnvelopeShape, sealedKeyWrapEnvelopeSchema, sealedInputEnvelopeSchema, sealedCandidateEnvelopeSchema, sealedEnvelopeSchema, screenerCandidateRuntimeBindingSchema, screenerClientLeaseBindingSchema, screenerRuntimeBindingSchema, serverRuntimeBindingSchema, clientRuntimeBindingSchema, runtimeBindingSchema, immutableJobInputSchema, canonicalizeJsonValue, serializeImmutableJobInput, reportedTokenUsageSchema, unavailableTokenUsageSchema, tokenUsageSchema, agentConnectRequestSchema, agentNextRequestSchema, agentHeartbeatRequestSchema, emptyAgentNextResultSchema, claimedAgentNextResultSchema, agentNextResultSchema, agentCompleteRequestSchema, agentFailRequestSchema, requestedModelSelectionSchema, completedModelProvenanceSchema, attemptModelProvenanceSchema, providerWeeklyQuotaReadSchema, hostHeartbeatRequestSchema, jobClaimRequestSchema, emptyJobClaimSchema, claimedJobSchema, jobClaimResultSchema, attemptStartRequestSchema, jobHeartbeatRequestSchema, continueHeartbeatDirectiveSchema, cancelHeartbeatDirectiveSchema, jobHeartbeatResultSchema, adapterOutcomeEvidenceSchema, completionBaseShape, validateCompletion, providerResponseCompletionSchema, decisionCandidateCompletionSchema, jobCompleteRequestSchema, jobFailRequestSchema, jobCancelRequestSchema, jobCancellationReceiptSchema, jobDeliveryRequestSchema, deliveryBaseShape, validateDeliveryCandidate, providerResponseDeliverySchema, decisionCandidateDeliverySchema, jobDeliveryResultSchema, jobConsumptionRequestSchema, jobConsumptionReceiptSchema, jobStateSchema, jobStatusReadSchema, hostStatusReadSchema, controllerBindingStatusReadSchema, queueStatusReadSchema, outcomeStatusReadSchema, externalInferenceVolumeWindowReadSchema, externalInferenceFailureCountReadSchema, externalInferenceHostDiagnosticsReadSchema, externalInferenceProfileActivityReadSchema, externalInferenceStatusReadSchema, nativeResearchEvidenceSourceSchema, externalAgentResearchEvidenceSourceSchema, researchEvidenceSourceSchema, reportedResearchTokenUsageSchema, unavailableResearchTokenUsageSchema, researchTokenUsageSchema, externalInferenceProductLineageBaseShape, traderExternalInferenceProductLineageSchema, decisionChatExternalInferenceProductLineageSchema, screenerExternalInferenceProductLineageSchema, externalInferenceProductLineageSchema, normalProductLinkageSchema, evidenceMaterializationMarkerSchema, deliveredExternalAgentSourceIdentitySchema, deliveredRequestEvidenceSchema, researchEvidenceBaseShape, researchEvidenceBaseSchema, validateExactUtf8, validateResearchEvidence, providerResponseResearchEvidenceSchema, decisionCandidateResearchEvidenceSchema, researchEvidenceSchema;
|
|
14379
14379
|
var init_external_inference_contract = __esm({
|
|
14380
14380
|
"lib/external-inference-contract.ts"() {
|
|
14381
14381
|
"use strict";
|
|
@@ -14646,6 +14646,15 @@ var init_external_inference_contract = __esm({
|
|
|
14646
14646
|
response_mode_hint: responseModeSchema,
|
|
14647
14647
|
binding_state: external_exports.literal("rebind_required")
|
|
14648
14648
|
});
|
|
14649
|
+
orderedFallbackHostIdsSchema = external_exports.array(identifierSchema).max(127).refine(
|
|
14650
|
+
(values) => new Set(values).size === values.length,
|
|
14651
|
+
{ message: "fallback host IDs must be unique" }
|
|
14652
|
+
);
|
|
14653
|
+
externalInferenceFallbackHostIdsSchema = external_exports.strictObject({
|
|
14654
|
+
main: orderedFallbackHostIdsSchema,
|
|
14655
|
+
review: orderedFallbackHostIdsSchema,
|
|
14656
|
+
screener: orderedFallbackHostIdsSchema
|
|
14657
|
+
});
|
|
14649
14658
|
liveModelControllerSchema = external_exports.discriminatedUnion("kind", [
|
|
14650
14659
|
nativeModelControllerSchema,
|
|
14651
14660
|
externalAgentModelControllerSchema
|
|
@@ -19496,7 +19505,7 @@ async function logoutCodexSubscription(options) {
|
|
|
19496
19505
|
await session.close();
|
|
19497
19506
|
}
|
|
19498
19507
|
}
|
|
19499
|
-
var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_CONTENT_MAX_UTF8_BYTES, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
|
|
19508
|
+
var CODEX_INFERENCE_PERMISSION_PROFILE, CODEX_REASONING_CONTENT_MAX_UTF8_BYTES, CODEX_REASONING_SUMMARY_MAX_UTF8_BYTES, CODEX_ACCOUNT_PLAN_TYPES, CodexAppServerError, objectOrNull, finiteToken, tokenUsageFromBreakdown, usageFromNotification, validPlanType, nonnegativeSafeIntegerOrNull, nonnegativeFiniteNumberOrNull, parseRateLimitWindow, RATE_LIMIT_REACHED_TYPES, DEFAULT_CODEX_QUOTA_COOLDOWN_MS, MAX_CODEX_QUOTA_COOLDOWN_MS, CODEX_TRANSIENT_RATE_LIMIT_COOLDOWN_MS, parseRateLimitSnapshot, codexRateLimitRetryAtMs, codexAccountRateLimitReached, forbiddenMethod, forbiddenTerminalItem, classifyCodexTurnFailure, scrubbedCodexEnvironment, killWindowsProcessTree, appServerArgs, GUARDIAN_SCRIPT, WINDOWS_RECEIPT_REPLACE_ERROR_CODES, MAX_RETIRED_RESPONSE_IDS, LATE_RESPONSE_SAFE_METHODS, replaceCodexGuardianReceiptFile, writeCodexGuardianSpawnIntent, parseGuardianReceipt, readCodexGuardianReceipt, waitForCodexGuardianState, defaultSpawn, CodexAppServerSession;
|
|
19500
19509
|
var init_codex_app_server = __esm({
|
|
19501
19510
|
"lib/inference-host/codex-app-server.ts"() {
|
|
19502
19511
|
"use strict";
|
|
@@ -19959,6 +19968,8 @@ child.once('close', async () => {
|
|
|
19959
19968
|
"ENOTEMPTY",
|
|
19960
19969
|
"EPERM"
|
|
19961
19970
|
]);
|
|
19971
|
+
MAX_RETIRED_RESPONSE_IDS = 256;
|
|
19972
|
+
LATE_RESPONSE_SAFE_METHODS = /* @__PURE__ */ new Set(["account/rateLimits/read"]);
|
|
19962
19973
|
replaceCodexGuardianReceiptFile = async (temporaryPath, receiptPath, options = {}) => {
|
|
19963
19974
|
const platform = options.platform ?? process.platform;
|
|
19964
19975
|
const renameFile = options.renameFile ?? rename2;
|
|
@@ -20081,6 +20092,7 @@ child.once('close', async () => {
|
|
|
20081
20092
|
CodexAppServerSession = class _CodexAppServerSession {
|
|
20082
20093
|
constructor(options) {
|
|
20083
20094
|
this.pending = /* @__PURE__ */ new Map();
|
|
20095
|
+
this.retiredResponseIds = /* @__PURE__ */ new Map();
|
|
20084
20096
|
this.notificationListeners = /* @__PURE__ */ new Set();
|
|
20085
20097
|
this.fatalListeners = /* @__PURE__ */ new Set();
|
|
20086
20098
|
this.accountLoginCompletions = /* @__PURE__ */ new Map();
|
|
@@ -20219,6 +20231,9 @@ child.once('close', async () => {
|
|
|
20219
20231
|
if (typeof id2 === "number" && Number.isSafeInteger(id2) && !("method" in message)) {
|
|
20220
20232
|
const pending = this.pending.get(id2);
|
|
20221
20233
|
if (!pending) {
|
|
20234
|
+
const retiredMethod = this.retiredResponseIds.get(id2);
|
|
20235
|
+
this.retiredResponseIds.delete(id2);
|
|
20236
|
+
if (retiredMethod && LATE_RESPONSE_SAFE_METHODS.has(retiredMethod)) return;
|
|
20222
20237
|
this.failTransport("unexpected_response_id");
|
|
20223
20238
|
return;
|
|
20224
20239
|
}
|
|
@@ -20283,6 +20298,14 @@ child.once('close', async () => {
|
|
|
20283
20298
|
this.pending.clear();
|
|
20284
20299
|
for (const listener of this.fatalListeners) listener(this.fatalError);
|
|
20285
20300
|
}
|
|
20301
|
+
retireResponseId(id2, method) {
|
|
20302
|
+
this.retiredResponseIds.set(id2, method);
|
|
20303
|
+
while (this.retiredResponseIds.size > MAX_RETIRED_RESPONSE_IDS) {
|
|
20304
|
+
const oldest = this.retiredResponseIds.keys().next().value;
|
|
20305
|
+
if (typeof oldest !== "number") break;
|
|
20306
|
+
this.retiredResponseIds.delete(oldest);
|
|
20307
|
+
}
|
|
20308
|
+
}
|
|
20286
20309
|
write(message) {
|
|
20287
20310
|
if (this.closed || this.fatalError || this.process.exitCode !== null) {
|
|
20288
20311
|
throw this.fatalError ?? new CodexAppServerError({
|
|
@@ -20321,6 +20344,7 @@ child.once('close', async () => {
|
|
|
20321
20344
|
const pending = { resolve: resolve6, reject, timer: null, abortCleanup: null };
|
|
20322
20345
|
pending.timer = setTimeout(() => {
|
|
20323
20346
|
this.pending.delete(id2);
|
|
20347
|
+
this.retireResponseId(id2, method);
|
|
20324
20348
|
pending.abortCleanup?.();
|
|
20325
20349
|
reject(new CodexAppServerError({
|
|
20326
20350
|
message: "Codex app-server request timed out.",
|
|
@@ -20332,6 +20356,7 @@ child.once('close', async () => {
|
|
|
20332
20356
|
if (options.signal) {
|
|
20333
20357
|
const onAbort = () => {
|
|
20334
20358
|
this.pending.delete(id2);
|
|
20359
|
+
this.retireResponseId(id2, method);
|
|
20335
20360
|
if (pending.timer) clearTimeout(pending.timer);
|
|
20336
20361
|
pending.abortCleanup?.();
|
|
20337
20362
|
reject(new CodexAppServerError({
|
|
@@ -21448,10 +21473,10 @@ import {
|
|
|
21448
21473
|
realpath as realpath3,
|
|
21449
21474
|
rm as rm3
|
|
21450
21475
|
} from "node:fs/promises";
|
|
21451
|
-
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
21476
|
+
import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
|
|
21452
21477
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
21453
21478
|
import { isAbsolute as isAbsolute3, join as join4, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
21454
|
-
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;
|
|
21479
|
+
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;
|
|
21455
21480
|
var init_codex_adapter = __esm({
|
|
21456
21481
|
"lib/inference-host/codex-adapter.ts"() {
|
|
21457
21482
|
"use strict";
|
|
@@ -21462,6 +21487,34 @@ var init_codex_adapter = __esm({
|
|
|
21462
21487
|
MAX_PROMPT_BYTES2 = 3e5;
|
|
21463
21488
|
CODEX_MODEL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/u;
|
|
21464
21489
|
CODEX_REASONING_EFFORT_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/u;
|
|
21490
|
+
codexRecoveryCheckpointProcessFingerprint = (checkpoint) => createHash4("sha256").update(JSON.stringify({
|
|
21491
|
+
attemptId: checkpoint.attemptId,
|
|
21492
|
+
processToken: checkpoint.processToken,
|
|
21493
|
+
processReceiptPath: checkpoint.processReceiptPath,
|
|
21494
|
+
processState: checkpoint.processState,
|
|
21495
|
+
bootIdentity: checkpoint.bootIdentity ?? null,
|
|
21496
|
+
cleanupConfirmed: checkpoint.cleanupConfirmed
|
|
21497
|
+
})).digest("hex");
|
|
21498
|
+
proveCodexSameBootQuiescence = async (options) => {
|
|
21499
|
+
const readBootIdentity = options.readBootIdentity ?? readInferenceSystemBootIdentity;
|
|
21500
|
+
const bootIdentityBefore = await readBootIdentity();
|
|
21501
|
+
await options.confirmNoManagedCodexProcesses();
|
|
21502
|
+
const bootIdentityAfter = await readBootIdentity();
|
|
21503
|
+
if (bootIdentityAfter !== bootIdentityBefore) {
|
|
21504
|
+
throw new Error("System boot identity changed during Codex recovery process inspection.");
|
|
21505
|
+
}
|
|
21506
|
+
return {
|
|
21507
|
+
schemaVersion: "vtx_codex_same_boot_quiescence_v1",
|
|
21508
|
+
bootIdentity: bootIdentityAfter,
|
|
21509
|
+
observedAtMs: (options.now ?? Date.now)(),
|
|
21510
|
+
checkpointFingerprints: Object.fromEntries(
|
|
21511
|
+
Object.entries(options.attempts).map(([attemptId, checkpoint]) => [
|
|
21512
|
+
attemptId,
|
|
21513
|
+
codexRecoveryCheckpointProcessFingerprint(checkpoint)
|
|
21514
|
+
])
|
|
21515
|
+
)
|
|
21516
|
+
};
|
|
21517
|
+
};
|
|
21465
21518
|
tokenUsageReceiptSchema = external_exports.strictObject({
|
|
21466
21519
|
inputTokens: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
21467
21520
|
cachedInputTokens: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
@@ -21934,11 +21987,20 @@ var init_codex_adapter = __esm({
|
|
|
21934
21987
|
throw new Error("Codex cross-boot recovery receipt ownership is unconfirmed.");
|
|
21935
21988
|
}
|
|
21936
21989
|
} else {
|
|
21937
|
-
await
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
21990
|
+
const receipt = await readCodexGuardianReceipt(guardian.receiptPath);
|
|
21991
|
+
if (receipt === null && options.sameBootQuiescenceProof) {
|
|
21992
|
+
const proof = options.sameBootQuiescenceProof;
|
|
21993
|
+
const expectedFingerprint = proof.checkpointFingerprints[checkpoint.attemptId];
|
|
21994
|
+
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)) {
|
|
21995
|
+
throw new Error("Codex same-boot recovery process fence is invalid or stale.");
|
|
21996
|
+
}
|
|
21997
|
+
} else {
|
|
21998
|
+
await confirmGuardianTerminatedForRecovery(
|
|
21999
|
+
checkpoint,
|
|
22000
|
+
guardian,
|
|
22001
|
+
options.deadlineMs ?? 5e3
|
|
22002
|
+
);
|
|
22003
|
+
}
|
|
21942
22004
|
}
|
|
21943
22005
|
assertRecoveryResourceScope(checkpoint);
|
|
21944
22006
|
await removeRecoveredThread(checkpoint, options.codexHome);
|
|
@@ -22224,9 +22286,21 @@ var init_codex_adapter = __esm({
|
|
|
22224
22286
|
dispatchOutcome: checkpoint.dispatchOutcome
|
|
22225
22287
|
});
|
|
22226
22288
|
}
|
|
22227
|
-
|
|
22228
|
-
if (
|
|
22229
|
-
|
|
22289
|
+
let sharedReceiptStillRequired = true;
|
|
22290
|
+
if (recoveryHooks.loadAll) {
|
|
22291
|
+
try {
|
|
22292
|
+
const checkpoints = await recoveryHooks.loadAll();
|
|
22293
|
+
const receiptPath = resolve3(checkpoint.processReceiptPath);
|
|
22294
|
+
sharedReceiptStillRequired = Object.values(checkpoints).some((candidate) => candidate.attemptId !== attemptId && !candidate.cleanupConfirmed && candidate.processReceiptPath !== null && resolve3(candidate.processReceiptPath) === receiptPath);
|
|
22295
|
+
} catch {
|
|
22296
|
+
sharedReceiptStillRequired = true;
|
|
22297
|
+
}
|
|
22298
|
+
}
|
|
22299
|
+
if (!sharedReceiptStillRequired) {
|
|
22300
|
+
const receipt = await readCodexGuardianReceipt(checkpoint.processReceiptPath);
|
|
22301
|
+
if (checkpoint.processState === "terminated" || receipt === null || receipt.state === "terminated") {
|
|
22302
|
+
await rm3(checkpoint.processReceiptPath, { force: true });
|
|
22303
|
+
}
|
|
22230
22304
|
}
|
|
22231
22305
|
}
|
|
22232
22306
|
await recoveryHooks.clear(attemptId);
|
|
@@ -22570,11 +22644,147 @@ var init_codex_adapter = __esm({
|
|
|
22570
22644
|
}
|
|
22571
22645
|
});
|
|
22572
22646
|
|
|
22647
|
+
// lib/inference-host/codex-recovery-process.ts
|
|
22648
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
22649
|
+
var normalizedWindowsPath, classifyCodexRecoveryProcesses, WINDOWS_PROCESS_QUERY, queryWindowsProcesses, confirmNoVtxManagedCodexProcesses;
|
|
22650
|
+
var init_codex_recovery_process = __esm({
|
|
22651
|
+
"lib/inference-host/codex-recovery-process.ts"() {
|
|
22652
|
+
"use strict";
|
|
22653
|
+
normalizedWindowsPath = (value) => value.replaceAll("\\", "/").replaceAll(/\/+$/gu, "").toLowerCase();
|
|
22654
|
+
classifyCodexRecoveryProcesses = (processes, pinnedBinaryPath) => {
|
|
22655
|
+
const pinned = normalizedWindowsPath(pinnedBinaryPath);
|
|
22656
|
+
let managedProcessCount = 0;
|
|
22657
|
+
let ambiguousProcessCount = 0;
|
|
22658
|
+
for (const process3 of processes) {
|
|
22659
|
+
const name = process3.name.trim().toLowerCase();
|
|
22660
|
+
if (name === "codex.exe") {
|
|
22661
|
+
if (!process3.executable_path || !process3.command_line || !process3.creation_date) {
|
|
22662
|
+
ambiguousProcessCount += 1;
|
|
22663
|
+
continue;
|
|
22664
|
+
}
|
|
22665
|
+
const executable = normalizedWindowsPath(process3.executable_path);
|
|
22666
|
+
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/");
|
|
22667
|
+
if (packageOwned) managedProcessCount += 1;
|
|
22668
|
+
continue;
|
|
22669
|
+
}
|
|
22670
|
+
if (name !== "node.exe") continue;
|
|
22671
|
+
if (!process3.executable_path || !process3.command_line || !process3.creation_date) {
|
|
22672
|
+
ambiguousProcessCount += 1;
|
|
22673
|
+
continue;
|
|
22674
|
+
}
|
|
22675
|
+
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")) {
|
|
22676
|
+
managedProcessCount += 1;
|
|
22677
|
+
}
|
|
22678
|
+
}
|
|
22679
|
+
return {
|
|
22680
|
+
managed_process_count: managedProcessCount,
|
|
22681
|
+
ambiguous_process_count: ambiguousProcessCount
|
|
22682
|
+
};
|
|
22683
|
+
};
|
|
22684
|
+
WINDOWS_PROCESS_QUERY = String.raw`
|
|
22685
|
+
$ErrorActionPreference = 'Stop'
|
|
22686
|
+
$items = @(Get-CimInstance Win32_Process | Where-Object { $_.Name -in @('node.exe', 'codex.exe') } | ForEach-Object {
|
|
22687
|
+
[ordered]@{
|
|
22688
|
+
process_id = [int]$_.ProcessId
|
|
22689
|
+
name = [string]$_.Name
|
|
22690
|
+
executable_path = if ($null -eq $_.ExecutablePath) { $null } else { [string]$_.ExecutablePath }
|
|
22691
|
+
command_line = if ($null -eq $_.CommandLine) { $null } else { [string]$_.CommandLine }
|
|
22692
|
+
creation_date = if ($null -eq $_.CreationDate) { $null } else { [string]$_.CreationDate }
|
|
22693
|
+
}
|
|
22694
|
+
})
|
|
22695
|
+
[Console]::Out.Write(($items | ConvertTo-Json -Depth 3 -Compress))
|
|
22696
|
+
`;
|
|
22697
|
+
queryWindowsProcesses = async () => {
|
|
22698
|
+
const args = [
|
|
22699
|
+
"-NoLogo",
|
|
22700
|
+
"-NoProfile",
|
|
22701
|
+
"-NonInteractive",
|
|
22702
|
+
"-EncodedCommand",
|
|
22703
|
+
Buffer.from(WINDOWS_PROCESS_QUERY, "utf16le").toString("base64")
|
|
22704
|
+
];
|
|
22705
|
+
const result2 = await new Promise(
|
|
22706
|
+
(resolvePromise, reject) => {
|
|
22707
|
+
const child = spawn5("powershell.exe", args, {
|
|
22708
|
+
windowsHide: true,
|
|
22709
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22710
|
+
});
|
|
22711
|
+
let stdout = "";
|
|
22712
|
+
let stderr = "";
|
|
22713
|
+
child.stdout.on("data", (chunk) => {
|
|
22714
|
+
stdout = `${stdout}${chunk.toString("utf8")}`.slice(-4e6);
|
|
22715
|
+
});
|
|
22716
|
+
child.stderr.on("data", (chunk) => {
|
|
22717
|
+
stderr = `${stderr}${chunk.toString("utf8")}`.slice(-4096);
|
|
22718
|
+
});
|
|
22719
|
+
child.once("error", reject);
|
|
22720
|
+
child.once("close", (code) => {
|
|
22721
|
+
if (code !== 0) {
|
|
22722
|
+
reject(new Error("Windows process ownership inventory failed."));
|
|
22723
|
+
return;
|
|
22724
|
+
}
|
|
22725
|
+
resolvePromise({ exitCode: code ?? 1, stdout });
|
|
22726
|
+
});
|
|
22727
|
+
}
|
|
22728
|
+
);
|
|
22729
|
+
if (result2.exitCode !== 0) throw new Error("Windows process ownership inventory failed.");
|
|
22730
|
+
let parsed;
|
|
22731
|
+
try {
|
|
22732
|
+
parsed = result2.stdout.trim() ? JSON.parse(result2.stdout) : [];
|
|
22733
|
+
} catch {
|
|
22734
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22735
|
+
}
|
|
22736
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
22737
|
+
return entries.map((entry) => {
|
|
22738
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
22739
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22740
|
+
}
|
|
22741
|
+
const record2 = entry;
|
|
22742
|
+
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") {
|
|
22743
|
+
throw new Error("Windows process ownership inventory was invalid.");
|
|
22744
|
+
}
|
|
22745
|
+
return {
|
|
22746
|
+
process_id: Number(record2.process_id),
|
|
22747
|
+
name: record2.name,
|
|
22748
|
+
executable_path: record2.executable_path,
|
|
22749
|
+
command_line: record2.command_line,
|
|
22750
|
+
creation_date: record2.creation_date
|
|
22751
|
+
};
|
|
22752
|
+
});
|
|
22753
|
+
};
|
|
22754
|
+
confirmNoVtxManagedCodexProcesses = async (options) => {
|
|
22755
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
22756
|
+
throw new Error("Same-boot service recovery is currently supported only on Windows.");
|
|
22757
|
+
}
|
|
22758
|
+
const queryProcesses = options.queryProcesses ?? queryWindowsProcesses;
|
|
22759
|
+
const sleep4 = options.sleep ?? (async (milliseconds) => {
|
|
22760
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
22761
|
+
});
|
|
22762
|
+
for (let observation = 0; observation < 2; observation += 1) {
|
|
22763
|
+
const classification = classifyCodexRecoveryProcesses(
|
|
22764
|
+
await queryProcesses(),
|
|
22765
|
+
options.pinnedBinaryPath
|
|
22766
|
+
);
|
|
22767
|
+
if (classification.ambiguous_process_count > 0) {
|
|
22768
|
+
throw new Error(
|
|
22769
|
+
"Codex process ownership could not be inspected completely; recovery evidence was preserved."
|
|
22770
|
+
);
|
|
22771
|
+
}
|
|
22772
|
+
if (classification.managed_process_count > 0) {
|
|
22773
|
+
throw new Error(
|
|
22774
|
+
"VTX-managed Codex automation is still running; recovery evidence was preserved."
|
|
22775
|
+
);
|
|
22776
|
+
}
|
|
22777
|
+
if (observation === 0) await sleep4(250);
|
|
22778
|
+
}
|
|
22779
|
+
};
|
|
22780
|
+
}
|
|
22781
|
+
});
|
|
22782
|
+
|
|
22573
22783
|
// lib/inference-host/crypto.ts
|
|
22574
22784
|
import {
|
|
22575
22785
|
createCipheriv,
|
|
22576
22786
|
createDecipheriv,
|
|
22577
|
-
createHash as
|
|
22787
|
+
createHash as createHash5,
|
|
22578
22788
|
createPrivateKey,
|
|
22579
22789
|
createPublicKey,
|
|
22580
22790
|
diffieHellman,
|
|
@@ -22613,7 +22823,7 @@ var init_crypto = __esm({
|
|
|
22613
22823
|
const aad = envelopeAadSchema.parse(input);
|
|
22614
22824
|
return Buffer.from(JSON.stringify(canonicalize(aad)), "utf8");
|
|
22615
22825
|
};
|
|
22616
|
-
externalInferenceSha256 = (value) =>
|
|
22826
|
+
externalInferenceSha256 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
22617
22827
|
decodeCanonicalBase64Url = (value, fieldName, expectedBytes) => {
|
|
22618
22828
|
if (!/^[A-Za-z0-9_-]+$/u.test(value)) {
|
|
22619
22829
|
throw new ExternalInferenceEnvelopeError("invalid_encoding", `${fieldName} is invalid.`);
|
|
@@ -22712,7 +22922,7 @@ var init_crypto = __esm({
|
|
|
22712
22922
|
return Buffer.from(hkdfSync(
|
|
22713
22923
|
"sha256",
|
|
22714
22924
|
root,
|
|
22715
|
-
|
|
22925
|
+
createHash5("sha256").update(aadBytes).digest(),
|
|
22716
22926
|
info,
|
|
22717
22927
|
32
|
|
22718
22928
|
));
|
|
@@ -29126,7 +29336,7 @@ var require_ajv = __commonJS({
|
|
|
29126
29336
|
});
|
|
29127
29337
|
|
|
29128
29338
|
// lib/inference-host/runner.ts
|
|
29129
|
-
import { createHash as
|
|
29339
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
29130
29340
|
function createDefaultInferenceHostRunnerDependencies(options) {
|
|
29131
29341
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
29132
29342
|
return {
|
|
@@ -29434,7 +29644,7 @@ var init_runner = __esm({
|
|
|
29434
29644
|
attempts
|
|
29435
29645
|
};
|
|
29436
29646
|
};
|
|
29437
|
-
sha256 = (value) =>
|
|
29647
|
+
sha256 = (value) => createHash6("sha256").update(value, "utf8").digest("hex");
|
|
29438
29648
|
stableOperationId = (kind, parts) => `${kind}_${sha256(JSON.stringify(parts)).slice(0, 48)}`;
|
|
29439
29649
|
buildAttemptStartRequest = (claim, attemptId, startedAt) => attemptStartRequestSchema.parse({
|
|
29440
29650
|
schema_version: "external_inference_attempt_start_v1",
|
|
@@ -31073,7 +31283,7 @@ var init_runner = __esm({
|
|
|
31073
31283
|
});
|
|
31074
31284
|
|
|
31075
31285
|
// lib/inference-host/service.ts
|
|
31076
|
-
import { spawn as
|
|
31286
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
31077
31287
|
import { randomUUID } from "node:crypto";
|
|
31078
31288
|
import { createWriteStream, readFileSync } from "node:fs";
|
|
31079
31289
|
import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -31119,7 +31329,7 @@ var init_service = __esm({
|
|
|
31119
31329
|
plistEscape = xmlEscape;
|
|
31120
31330
|
systemdQuote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
|
|
31121
31331
|
defaultRunCommand = async (command, args) => await new Promise((resolvePromise) => {
|
|
31122
|
-
const child =
|
|
31332
|
+
const child = spawn6(command, [...args], {
|
|
31123
31333
|
windowsHide: true,
|
|
31124
31334
|
stdio: ["ignore", "pipe", "pipe"]
|
|
31125
31335
|
});
|
|
@@ -31592,8 +31802,10 @@ WantedBy=default.target
|
|
|
31592
31802
|
}
|
|
31593
31803
|
throw new Error("Background service did not reach an active state within 10 seconds.");
|
|
31594
31804
|
}
|
|
31595
|
-
async restoreRunningSupervisor(manifest) {
|
|
31596
|
-
const
|
|
31805
|
+
async restoreRunningSupervisor(manifest, preserveGeneration = false, targetInstanceName) {
|
|
31806
|
+
const previousRuntime = await readRuntimeAcrossAtomicReplacement(this.runtimePath()).catch(() => null);
|
|
31807
|
+
const previousRuntimeUpdatedAt = previousRuntime?.manifest_generation === manifest.generation ? Date.parse(previousRuntime.updated_at) : Number.NaN;
|
|
31808
|
+
const restoredManifest = preserveGeneration ? manifest : assertManifest({
|
|
31597
31809
|
...manifest,
|
|
31598
31810
|
generation: randomUUID(),
|
|
31599
31811
|
installed_at: this.now().toISOString()
|
|
@@ -31621,10 +31833,15 @@ ${result2.stderr}`)) {
|
|
|
31621
31833
|
}
|
|
31622
31834
|
await this.waitForManagerActive();
|
|
31623
31835
|
if (!this.confirmInitialReadiness) return await this.status();
|
|
31624
|
-
const runtime = await this.waitForManifestApplied(
|
|
31836
|
+
const runtime = await this.waitForManifestApplied(
|
|
31837
|
+
restoredManifest,
|
|
31838
|
+
targetInstanceName,
|
|
31839
|
+
this.reconcileWaitAttempts,
|
|
31840
|
+
Number.isFinite(previousRuntimeUpdatedAt) ? previousRuntimeUpdatedAt : void 0
|
|
31841
|
+
);
|
|
31625
31842
|
return await this.status(runtime);
|
|
31626
31843
|
}
|
|
31627
|
-
async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts) {
|
|
31844
|
+
async waitForManifestApplied(manifest, targetInstanceName, maxAttempts = this.reconcileWaitAttempts, minimumRuntimeUpdatedAtExclusive) {
|
|
31628
31845
|
let consecutiveReadyObservations = 0;
|
|
31629
31846
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
31630
31847
|
let runtime = null;
|
|
@@ -31635,7 +31852,9 @@ ${result2.stderr}`)) {
|
|
|
31635
31852
|
throw error48;
|
|
31636
31853
|
}
|
|
31637
31854
|
}
|
|
31638
|
-
|
|
31855
|
+
const runtimeUpdatedAt = runtime ? Date.parse(runtime.updated_at) : Number.NaN;
|
|
31856
|
+
const runtimeIsFresh = minimumRuntimeUpdatedAtExclusive === void 0 || Number.isFinite(runtimeUpdatedAt) && runtimeUpdatedAt > minimumRuntimeUpdatedAtExclusive;
|
|
31857
|
+
if (runtime?.manifest_generation === manifest.generation && runtimeIsFresh) {
|
|
31639
31858
|
const configuredNames = manifest.workers.map((worker) => worker.instance_name).sort();
|
|
31640
31859
|
const runtimeNames = runtime.workers.map((worker) => worker.instance_name).sort();
|
|
31641
31860
|
const exactWorkerSet = JSON.stringify(configuredNames) === JSON.stringify(runtimeNames);
|
|
@@ -31897,7 +32116,71 @@ ${result2.stderr}`)) {
|
|
|
31897
32116
|
async stop() {
|
|
31898
32117
|
return await this.withControlLock(async () => await this.stopUnlocked());
|
|
31899
32118
|
}
|
|
31900
|
-
|
|
32119
|
+
/**
|
|
32120
|
+
* Quiesce the complete installed worker boundary while one exact instance's
|
|
32121
|
+
* durable Codex recovery evidence is reconciled. The service process lock is
|
|
32122
|
+
* held for the callback, so neither the manager nor a manual run-internal can
|
|
32123
|
+
* recreate workers during the same-boot process inspection.
|
|
32124
|
+
*/
|
|
32125
|
+
async recoverInstance(instanceName, operation, options = {}) {
|
|
32126
|
+
return await this.withControlLock(async () => {
|
|
32127
|
+
const manifest = await readInferenceHostServiceManifest(this.manifestPath());
|
|
32128
|
+
if (!manifest) throw new Error("Inference-host service is not installed.");
|
|
32129
|
+
if (!manifest.workers.some((worker) => worker.instance_name === instanceName)) {
|
|
32130
|
+
throw new Error(`Inference-host instance ${instanceName} is not installed.`);
|
|
32131
|
+
}
|
|
32132
|
+
if (options.expectedManifestGeneration && options.expectedManifestGeneration !== manifest.generation) {
|
|
32133
|
+
throw new Error("Inference-host service manifest changed during recovery.");
|
|
32134
|
+
}
|
|
32135
|
+
const recordedDesiredRunning = await readInferenceHostServiceDesired(this.desiredPath());
|
|
32136
|
+
const desiredRunning = options.restoreDesiredRunning ?? recordedDesiredRunning;
|
|
32137
|
+
const context = { manifest, desiredRunning };
|
|
32138
|
+
await options.prepare?.(context);
|
|
32139
|
+
const before = await this.status();
|
|
32140
|
+
if (recordedDesiredRunning || before.manager_active) {
|
|
32141
|
+
await this.stopUnlocked(true);
|
|
32142
|
+
} else {
|
|
32143
|
+
await writeDesired(this.desiredPath(), false, this.now());
|
|
32144
|
+
}
|
|
32145
|
+
let serviceFence = null;
|
|
32146
|
+
let result2;
|
|
32147
|
+
let operationError = null;
|
|
32148
|
+
try {
|
|
32149
|
+
serviceFence = await this.acquireProcessLock(
|
|
32150
|
+
`${this.config.supervisorProcessLockPath}.service`
|
|
32151
|
+
);
|
|
32152
|
+
const stopped = await this.status();
|
|
32153
|
+
if (stopped.manager_active) {
|
|
32154
|
+
throw new Error("Background service manager became active during recovery quiescence.");
|
|
32155
|
+
}
|
|
32156
|
+
result2 = await operation(context);
|
|
32157
|
+
} catch (error48) {
|
|
32158
|
+
operationError = error48;
|
|
32159
|
+
} finally {
|
|
32160
|
+
await serviceFence?.release().catch((error48) => {
|
|
32161
|
+
operationError = operationError ?? error48;
|
|
32162
|
+
});
|
|
32163
|
+
}
|
|
32164
|
+
let restoredStatus;
|
|
32165
|
+
try {
|
|
32166
|
+
restoredStatus = desiredRunning ? await this.restoreRunningSupervisor(manifest, true, instanceName) : await this.status();
|
|
32167
|
+
} catch (restoreError) {
|
|
32168
|
+
if (operationError) {
|
|
32169
|
+
throw new Error(
|
|
32170
|
+
"Inference-host recovery failed and the previous supervisor could not be restored.",
|
|
32171
|
+
{ cause: new AggregateError([operationError, restoreError]) }
|
|
32172
|
+
);
|
|
32173
|
+
}
|
|
32174
|
+
throw new Error(
|
|
32175
|
+
"Inference-host recovery completed, but the previous supervisor could not be restored.",
|
|
32176
|
+
{ cause: restoreError }
|
|
32177
|
+
);
|
|
32178
|
+
}
|
|
32179
|
+
if (operationError) throw operationError;
|
|
32180
|
+
return { result: result2, status: restoredStatus };
|
|
32181
|
+
});
|
|
32182
|
+
}
|
|
32183
|
+
async stopUnlocked(preserveManifestGenerationOnFailure = false) {
|
|
31901
32184
|
const manifest = await readInferenceHostServiceManifest(this.manifestPath());
|
|
31902
32185
|
if (!manifest) {
|
|
31903
32186
|
throw new Error("Inference-host service is not installed.");
|
|
@@ -31939,7 +32222,7 @@ ${result2.stderr}`)) {
|
|
|
31939
32222
|
} catch (error48) {
|
|
31940
32223
|
if (!restoreOnFailure) throw error48;
|
|
31941
32224
|
try {
|
|
31942
|
-
await this.restoreRunningSupervisor(manifest);
|
|
32225
|
+
await this.restoreRunningSupervisor(manifest, preserveManifestGenerationOnFailure);
|
|
31943
32226
|
} catch (restoreError) {
|
|
31944
32227
|
throw new Error(
|
|
31945
32228
|
"Inference-host stop failed and the previous supervisor could not be restored.",
|
|
@@ -32106,7 +32389,7 @@ ${result2.stderr}`)) {
|
|
|
32106
32389
|
`);
|
|
32107
32390
|
}
|
|
32108
32391
|
};
|
|
32109
|
-
const child =
|
|
32392
|
+
const child = spawn6(manifest.executable, args, {
|
|
32110
32393
|
env: inferenceHostServiceChildEnvironment(worker.runtime_environment),
|
|
32111
32394
|
windowsHide: true,
|
|
32112
32395
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -32396,8 +32679,8 @@ __export(cli_exports, {
|
|
|
32396
32679
|
registerInferenceHostServiceControlInput: () => registerInferenceHostServiceControlInput,
|
|
32397
32680
|
runInferenceHostCli: () => runInferenceHostCli
|
|
32398
32681
|
});
|
|
32399
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
32400
|
-
import { spawn as
|
|
32682
|
+
import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
|
|
32683
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
32401
32684
|
import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
|
|
32402
32685
|
import { join as join6, resolve as resolve5 } from "node:path";
|
|
32403
32686
|
async function runInferenceHostCli(argv2, env = process.env, dependencies = {}) {
|
|
@@ -32407,6 +32690,21 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32407
32690
|
return { exitCode: 0, stdout: INFERENCE_HOST_HELP, stderr: "" };
|
|
32408
32691
|
}
|
|
32409
32692
|
const parsed = parseInferenceHostArgs(argv2, env);
|
|
32693
|
+
if (parsed.forceRecovery && !(parsed.command === "service" && parsed.serviceAction === "recover")) {
|
|
32694
|
+
throw new Error("--force-recovery is accepted only by service recover.");
|
|
32695
|
+
}
|
|
32696
|
+
if (parsed.command === "service" && parsed.serviceAction === "recover") {
|
|
32697
|
+
if (!parsed.instanceFlagExplicit) {
|
|
32698
|
+
throw new Error(
|
|
32699
|
+
"Service recovery requires an explicit --instance NAME; an environment or default instance is not accepted."
|
|
32700
|
+
);
|
|
32701
|
+
}
|
|
32702
|
+
if (!parsed.forceRecovery) {
|
|
32703
|
+
throw new Error(
|
|
32704
|
+
`Service recovery is disruptive. Rerun with --instance ${parsed.instanceName} --force-recovery after checking service status and logs.`
|
|
32705
|
+
);
|
|
32706
|
+
}
|
|
32707
|
+
}
|
|
32410
32708
|
const config2 = await resolveInferenceHostCommandConfig(parsed, env, dependencies);
|
|
32411
32709
|
if (parsed.command === "login") {
|
|
32412
32710
|
return await login(config2, parsed, env, dependencies, warnings);
|
|
@@ -32445,7 +32743,18 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32445
32743
|
);
|
|
32446
32744
|
}
|
|
32447
32745
|
if (parsed.command === "service") {
|
|
32448
|
-
|
|
32746
|
+
const mutationActions = /* @__PURE__ */ new Set(["install", "start", "stop", "recover", "uninstall"]);
|
|
32747
|
+
if (!parsed.serviceAction || !mutationActions.has(parsed.serviceAction)) {
|
|
32748
|
+
return await serviceCommand(config2, parsed, env, dependencies, warnings);
|
|
32749
|
+
}
|
|
32750
|
+
const recoveryCommandLock = await acquireInferenceHostProcessLock(
|
|
32751
|
+
serviceRecoveryCommandLockPath(config2)
|
|
32752
|
+
);
|
|
32753
|
+
try {
|
|
32754
|
+
return await serviceCommand(config2, parsed, env, dependencies, warnings);
|
|
32755
|
+
} finally {
|
|
32756
|
+
await recoveryCommandLock.release();
|
|
32757
|
+
}
|
|
32449
32758
|
}
|
|
32450
32759
|
if (parsed.command === "status") {
|
|
32451
32760
|
return await localStatus(config2, parsed, dependencies, warnings);
|
|
@@ -32474,7 +32783,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
|
|
|
32474
32783
|
};
|
|
32475
32784
|
}
|
|
32476
32785
|
}
|
|
32477
|
-
var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, restoreInferenceHostCredentialContext, recoverInferenceHostCredentialContextTransition, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, serviceCommand, hasExplicitCredentialStoreConfiguration, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
|
|
32786
|
+
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, 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;
|
|
32478
32787
|
var init_cli = __esm({
|
|
32479
32788
|
"lib/inference-host/cli.ts"() {
|
|
32480
32789
|
"use strict";
|
|
@@ -32483,6 +32792,7 @@ var init_cli = __esm({
|
|
|
32483
32792
|
init_agent_client();
|
|
32484
32793
|
init_agent_state();
|
|
32485
32794
|
init_codex_adapter();
|
|
32795
|
+
init_codex_recovery_process();
|
|
32486
32796
|
init_codex_app_server();
|
|
32487
32797
|
init_codex_binary();
|
|
32488
32798
|
init_config();
|
|
@@ -32499,6 +32809,7 @@ var init_cli = __esm({
|
|
|
32499
32809
|
revocationCheckpointPath = (config2) => `${config2.statePath}.revoke.json`;
|
|
32500
32810
|
foregroundHostLockPath = (config2) => `${config2.processLockPath}.foreground`;
|
|
32501
32811
|
credentialLifecycleLockPath = (config2) => `${inferenceHostCredentialContextPath(config2)}.lock`;
|
|
32812
|
+
serviceRecoveryCommandLockPath = (config2) => `${config2.supervisorProcessLockPath}.service-recovery-command`;
|
|
32502
32813
|
AGENT_HEARTBEAT_INTERVAL_MS = 3e3;
|
|
32503
32814
|
AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS = 9e3;
|
|
32504
32815
|
retryableAgentHeartbeatError = (error48) => {
|
|
@@ -32595,7 +32906,8 @@ Durable service:
|
|
|
32595
32906
|
vtx inference-host codex-login --instance codex-2
|
|
32596
32907
|
vtx inference-host service install --instance codex-2
|
|
32597
32908
|
vtx inference-host service uninstall --instance codex-2
|
|
32598
|
-
vtx inference-host service
|
|
32909
|
+
vtx inference-host service recover --instance codex-1 --force-recovery
|
|
32910
|
+
vtx inference-host service <start|stop|status|logs|recover|uninstall>
|
|
32599
32911
|
`;
|
|
32600
32912
|
parseHostConcurrency = (raw, label) => {
|
|
32601
32913
|
const value = Number(raw);
|
|
@@ -32611,6 +32923,7 @@ Durable service:
|
|
|
32611
32923
|
let maxConcurrency = configuredConcurrency ? parseHostConcurrency(configuredConcurrency, "VTX_INFERENCE_HOST_MAX_CONCURRENCY") : null;
|
|
32612
32924
|
let instanceName = String(env.VTX_INFERENCE_HOST_INSTANCE || "default").trim();
|
|
32613
32925
|
let instanceExplicit = Boolean(String(env.VTX_INFERENCE_HOST_INSTANCE || "").trim());
|
|
32926
|
+
let instanceFlagExplicit = false;
|
|
32614
32927
|
let displayName = String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim() || "Codex subscription host";
|
|
32615
32928
|
let displayNameExplicit = Boolean(String(env.VTX_INFERENCE_HOST_DISPLAY_NAME || "").trim());
|
|
32616
32929
|
let adapter = null;
|
|
@@ -32620,6 +32933,7 @@ Durable service:
|
|
|
32620
32933
|
let waitSeconds = 50;
|
|
32621
32934
|
let lines = 100;
|
|
32622
32935
|
let serviceManifestPath = null;
|
|
32936
|
+
let forceRecovery = false;
|
|
32623
32937
|
const positionals = [];
|
|
32624
32938
|
for (let index = 0; index < argv2.length; index += 1) {
|
|
32625
32939
|
const argument = argv2[index];
|
|
@@ -32631,6 +32945,10 @@ Durable service:
|
|
|
32631
32945
|
once = true;
|
|
32632
32946
|
continue;
|
|
32633
32947
|
}
|
|
32948
|
+
if (argument === "--force-recovery") {
|
|
32949
|
+
forceRecovery = true;
|
|
32950
|
+
continue;
|
|
32951
|
+
}
|
|
32634
32952
|
if (argument === "--max-concurrency") {
|
|
32635
32953
|
const raw = argv2[index + 1];
|
|
32636
32954
|
if (!raw) throw new Error("--max-concurrency requires a value.");
|
|
@@ -32669,6 +32987,7 @@ Durable service:
|
|
|
32669
32987
|
if (argument === "--instance") {
|
|
32670
32988
|
instanceName = raw.trim();
|
|
32671
32989
|
instanceExplicit = true;
|
|
32990
|
+
instanceFlagExplicit = true;
|
|
32672
32991
|
}
|
|
32673
32992
|
index += 1;
|
|
32674
32993
|
continue;
|
|
@@ -32699,6 +33018,7 @@ Durable service:
|
|
|
32699
33018
|
maxConcurrency,
|
|
32700
33019
|
instanceName,
|
|
32701
33020
|
instanceExplicit,
|
|
33021
|
+
instanceFlagExplicit,
|
|
32702
33022
|
displayName,
|
|
32703
33023
|
adapter,
|
|
32704
33024
|
modelId,
|
|
@@ -32707,13 +33027,14 @@ Durable service:
|
|
|
32707
33027
|
waitSeconds,
|
|
32708
33028
|
lines,
|
|
32709
33029
|
serviceAction,
|
|
32710
|
-
serviceManifestPath
|
|
33030
|
+
serviceManifestPath,
|
|
33031
|
+
forceRecovery
|
|
32711
33032
|
};
|
|
32712
33033
|
};
|
|
32713
33034
|
defaultOpenBrowser = (url2) => {
|
|
32714
33035
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
32715
33036
|
const args = process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
32716
|
-
const child =
|
|
33037
|
+
const child = spawn7(command, args, {
|
|
32717
33038
|
detached: true,
|
|
32718
33039
|
stdio: "ignore",
|
|
32719
33040
|
windowsHide: true
|
|
@@ -32877,10 +33198,14 @@ Durable service:
|
|
|
32877
33198
|
codexRecoveryPath(config2),
|
|
32878
33199
|
"Codex attempt recovery file"
|
|
32879
33200
|
);
|
|
33201
|
+
const serviceRecoveryPresent = await fileExistsPrivately(
|
|
33202
|
+
`${config2.supervisorStatePath}.service-recovery.json`,
|
|
33203
|
+
"Inference-host service recovery transaction"
|
|
33204
|
+
);
|
|
32880
33205
|
const revocationCheckpointPresent = await readRevocationCheckpoint(config2) !== null;
|
|
32881
33206
|
const agentAttemptPresent = await readInferenceAgentAttemptState(config2.statePath) !== null;
|
|
32882
33207
|
const agentNextRecoveryPresent = await readInferenceAgentNextState(config2.statePath) !== null;
|
|
32883
|
-
if (pendingAttempts > 0 || codexRecoveryPresent || revocationCheckpointPresent || agentAttemptPresent || agentNextRecoveryPresent) {
|
|
33208
|
+
if (pendingAttempts > 0 || codexRecoveryPresent || serviceRecoveryPresent || revocationCheckpointPresent || agentAttemptPresent || agentNextRecoveryPresent) {
|
|
32884
33209
|
throw new Error(
|
|
32885
33210
|
"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."
|
|
32886
33211
|
);
|
|
@@ -33195,6 +33520,10 @@ Waiting for approval...
|
|
|
33195
33520
|
codexRecoveryPath(config2),
|
|
33196
33521
|
"Codex attempt recovery file"
|
|
33197
33522
|
);
|
|
33523
|
+
const serviceRecoveryPresent = await fileExistsPrivately(
|
|
33524
|
+
`${config2.supervisorStatePath}.service-recovery.json`,
|
|
33525
|
+
"Inference-host service recovery transaction"
|
|
33526
|
+
);
|
|
33198
33527
|
const agentAttempt = await readInferenceAgentAttemptState(config2.statePath);
|
|
33199
33528
|
const agentNextRecoveryPresent = await readInferenceAgentNextState(config2.statePath) !== null;
|
|
33200
33529
|
return {
|
|
@@ -33223,6 +33552,7 @@ Waiting for approval...
|
|
|
33223
33552
|
} : null,
|
|
33224
33553
|
agent_next_recovery_present: agentNextRecoveryPresent,
|
|
33225
33554
|
codex_recovery_present: recoveryPresent,
|
|
33555
|
+
service_recovery_present: serviceRecoveryPresent,
|
|
33226
33556
|
codex_auth: codexAuthentication,
|
|
33227
33557
|
source: "local_only"
|
|
33228
33558
|
}, parsed.json),
|
|
@@ -33979,10 +34309,292 @@ Waiting for approval...
|
|
|
33979
34309
|
await lock2.release();
|
|
33980
34310
|
}
|
|
33981
34311
|
};
|
|
34312
|
+
recoveryBackupPath = (config2, transactionId) => `${codexRecoveryPath(config2)}.service-recovery-${transactionId}.backup`;
|
|
34313
|
+
serviceRecoveryTransactionPath = (config2) => `${config2.supervisorStatePath}.service-recovery.json`;
|
|
34314
|
+
readServiceRecoveryTransaction = async (config2) => {
|
|
34315
|
+
const raw = await readInferencePrivateFile(
|
|
34316
|
+
serviceRecoveryTransactionPath(config2),
|
|
34317
|
+
"Inference-host service recovery transaction"
|
|
34318
|
+
);
|
|
34319
|
+
if (raw === null) return null;
|
|
34320
|
+
let value;
|
|
34321
|
+
try {
|
|
34322
|
+
value = JSON.parse(raw);
|
|
34323
|
+
} catch {
|
|
34324
|
+
throw new Error("Inference-host service recovery transaction is not valid JSON.");
|
|
34325
|
+
}
|
|
34326
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34327
|
+
throw new Error("Inference-host service recovery transaction is invalid.");
|
|
34328
|
+
}
|
|
34329
|
+
const record2 = value;
|
|
34330
|
+
const exactKeys = [
|
|
34331
|
+
"schema_version",
|
|
34332
|
+
"phase",
|
|
34333
|
+
"transaction_id",
|
|
34334
|
+
"instance_name",
|
|
34335
|
+
"manifest_generation",
|
|
34336
|
+
"restore_desired_running",
|
|
34337
|
+
"recovery_backup_path",
|
|
34338
|
+
"recovery_backup_sha256",
|
|
34339
|
+
"cleanup_confirmed_attempt_ids",
|
|
34340
|
+
"started_at"
|
|
34341
|
+
];
|
|
34342
|
+
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(
|
|
34343
|
+
(attemptId) => typeof attemptId !== "string" || attemptId.length < 1 || attemptId.length > 256
|
|
34344
|
+
) || 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) {
|
|
34345
|
+
throw new Error("Inference-host service recovery transaction is invalid.");
|
|
34346
|
+
}
|
|
34347
|
+
if (resolve5(record2.recovery_backup_path) !== resolve5(recoveryBackupPath(config2, record2.transaction_id))) {
|
|
34348
|
+
throw new Error("Inference-host service recovery backup path is invalid.");
|
|
34349
|
+
}
|
|
34350
|
+
return record2;
|
|
34351
|
+
};
|
|
34352
|
+
assertResumableCodexRecoveryEvidence = (backupAttempts, currentAttempts, cleanupConfirmedAttemptIds, phase) => {
|
|
34353
|
+
const backupIds = Object.keys(backupAttempts).sort();
|
|
34354
|
+
const currentIds = Object.keys(currentAttempts).sort();
|
|
34355
|
+
const backupIdSet = new Set(backupIds);
|
|
34356
|
+
const cleanupConfirmedIdSet = new Set(cleanupConfirmedAttemptIds);
|
|
34357
|
+
if (backupIds.length === 0 || currentIds.some((attemptId) => !backupIdSet.has(attemptId)) || cleanupConfirmedAttemptIds.some((attemptId) => !backupIdSet.has(attemptId)) || phase === "reconciled" && backupIds.some((attemptId) => !cleanupConfirmedIdSet.has(attemptId))) {
|
|
34358
|
+
throw new Error("Codex recovery evidence does not match the retained service recovery backup.");
|
|
34359
|
+
}
|
|
34360
|
+
for (const attemptId of backupIds) {
|
|
34361
|
+
const original = backupAttempts[attemptId];
|
|
34362
|
+
const current = currentAttempts[attemptId];
|
|
34363
|
+
if (!current) {
|
|
34364
|
+
if (!cleanupConfirmedIdSet.has(attemptId)) {
|
|
34365
|
+
throw new Error("Codex recovery evidence does not match the retained service recovery backup.");
|
|
34366
|
+
}
|
|
34367
|
+
continue;
|
|
34368
|
+
}
|
|
34369
|
+
if (cleanupConfirmedIdSet.has(attemptId) && !original.cleanupConfirmed && !(current.processState === "terminated" && current.cleanupConfirmed)) {
|
|
34370
|
+
throw new Error("Codex recovery evidence regressed after confirmed cleanup.");
|
|
34371
|
+
}
|
|
34372
|
+
const {
|
|
34373
|
+
processState: _originalProcessState,
|
|
34374
|
+
cleanupConfirmed: _originalCleanupConfirmed,
|
|
34375
|
+
...originalStable
|
|
34376
|
+
} = original;
|
|
34377
|
+
const {
|
|
34378
|
+
processState: _currentProcessState,
|
|
34379
|
+
cleanupConfirmed: _currentCleanupConfirmed,
|
|
34380
|
+
...currentStable
|
|
34381
|
+
} = current;
|
|
34382
|
+
if (JSON.stringify(originalStable) !== JSON.stringify(currentStable) || !(current.processState === original.processState && current.cleanupConfirmed === original.cleanupConfirmed) && !(current.processState === "terminated" && current.cleanupConfirmed)) {
|
|
34383
|
+
throw new Error("Codex recovery evidence changed outside an exact cleanup transition.");
|
|
34384
|
+
}
|
|
34385
|
+
}
|
|
34386
|
+
};
|
|
34387
|
+
recoverInstalledCodexService = async (config2, parsed, env, dependencies, manager) => {
|
|
34388
|
+
if (!parsed.instanceFlagExplicit) {
|
|
34389
|
+
throw new Error(
|
|
34390
|
+
"Service recovery requires an explicit --instance NAME; an environment or default instance is not accepted."
|
|
34391
|
+
);
|
|
34392
|
+
}
|
|
34393
|
+
if (!parsed.forceRecovery) {
|
|
34394
|
+
throw new Error(
|
|
34395
|
+
`Service recovery is disruptive. Rerun with --instance ${config2.instanceName} --force-recovery after checking service status and logs.`
|
|
34396
|
+
);
|
|
34397
|
+
}
|
|
34398
|
+
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34399
|
+
credentialLifecycleLockPath(config2)
|
|
34400
|
+
);
|
|
34401
|
+
try {
|
|
34402
|
+
const recoveryStore = new FileCodexAttemptRecoveryStore(codexRecoveryPath(config2));
|
|
34403
|
+
const existingTransaction = await readServiceRecoveryTransaction(config2);
|
|
34404
|
+
if (existingTransaction && existingTransaction.instance_name !== config2.instanceName) {
|
|
34405
|
+
throw new Error(
|
|
34406
|
+
`Service recovery for instance ${existingTransaction.instance_name} must finish before another instance can recover.`
|
|
34407
|
+
);
|
|
34408
|
+
}
|
|
34409
|
+
const initialAttempts = await recoveryStore.loadAll();
|
|
34410
|
+
const pendingAttempts = Object.values(initialAttempts).filter(
|
|
34411
|
+
(checkpoint) => !checkpoint.cleanupConfirmed
|
|
34412
|
+
);
|
|
34413
|
+
if (pendingAttempts.length === 0 && !existingTransaction) {
|
|
34414
|
+
const status = await manager.status();
|
|
34415
|
+
return {
|
|
34416
|
+
exitCode: 0,
|
|
34417
|
+
stdout: render({
|
|
34418
|
+
status: "service_recovery_not_needed",
|
|
34419
|
+
instance_name: config2.instanceName,
|
|
34420
|
+
recovery_needed: false,
|
|
34421
|
+
recovered_attempt_count: 0,
|
|
34422
|
+
outcome_unknown_attempt_count: 0,
|
|
34423
|
+
desired_running: status.desired_running,
|
|
34424
|
+
manager_active: status.manager_active,
|
|
34425
|
+
worker_runtime_state: status.workers.find(
|
|
34426
|
+
(worker) => worker.instance_name === config2.instanceName
|
|
34427
|
+
)?.runtime_state ?? "unknown"
|
|
34428
|
+
}, parsed.json),
|
|
34429
|
+
stderr: ""
|
|
34430
|
+
};
|
|
34431
|
+
}
|
|
34432
|
+
const recoveryRaw = await readInferencePrivateFile(
|
|
34433
|
+
codexRecoveryPath(config2),
|
|
34434
|
+
"Codex attempt recovery file"
|
|
34435
|
+
);
|
|
34436
|
+
if (recoveryRaw === null && existingTransaction?.phase !== "reconciled") {
|
|
34437
|
+
throw new Error("Codex recovery evidence changed before service recovery began.");
|
|
34438
|
+
}
|
|
34439
|
+
const transactionId = existingTransaction?.transaction_id ?? randomUUID2();
|
|
34440
|
+
const backupPath = existingTransaction?.recovery_backup_path ?? recoveryBackupPath(config2, transactionId);
|
|
34441
|
+
if (!existingTransaction) {
|
|
34442
|
+
await writeAtomicInferencePrivateFile(backupPath, recoveryRaw);
|
|
34443
|
+
}
|
|
34444
|
+
const backupRaw = await readInferencePrivateFile(
|
|
34445
|
+
backupPath,
|
|
34446
|
+
"Codex service recovery backup"
|
|
34447
|
+
);
|
|
34448
|
+
const backupSha256 = backupRaw === null ? null : createHash7("sha256").update(backupRaw).digest("hex");
|
|
34449
|
+
if (backupSha256 === null || existingTransaction && backupSha256 !== existingTransaction.recovery_backup_sha256) {
|
|
34450
|
+
throw new Error("Codex service recovery backup is missing or changed.");
|
|
34451
|
+
}
|
|
34452
|
+
const backupAttempts = await new FileCodexAttemptRecoveryStore(backupPath).loadAll();
|
|
34453
|
+
assertResumableCodexRecoveryEvidence(
|
|
34454
|
+
backupAttempts,
|
|
34455
|
+
initialAttempts,
|
|
34456
|
+
existingTransaction?.cleanup_confirmed_attempt_ids ?? [],
|
|
34457
|
+
existingTransaction?.phase ?? "prepared"
|
|
34458
|
+
);
|
|
34459
|
+
const recoverySha256 = recoveryRaw === null ? null : createHash7("sha256").update(recoveryRaw).digest("hex");
|
|
34460
|
+
const unresolvedAttemptIds = Object.keys(backupAttempts).filter((attemptId) => !existingTransaction?.cleanup_confirmed_attempt_ids.includes(attemptId) && !initialAttempts[attemptId]?.cleanupConfirmed);
|
|
34461
|
+
const binary = unresolvedAttemptIds.length > 0 ? await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env) : null;
|
|
34462
|
+
let activeTransaction = existingTransaction;
|
|
34463
|
+
let completed = false;
|
|
34464
|
+
try {
|
|
34465
|
+
const recovered = await manager.recoverInstance(config2.instanceName, async ({ manifest }) => {
|
|
34466
|
+
const workerLocks = [];
|
|
34467
|
+
try {
|
|
34468
|
+
for (const worker of [...manifest.workers].sort(
|
|
34469
|
+
(left, right) => left.instance_name.localeCompare(right.instance_name)
|
|
34470
|
+
)) {
|
|
34471
|
+
const workerConfig = resolveInferenceHostConfig({
|
|
34472
|
+
...env,
|
|
34473
|
+
...worker.runtime_environment,
|
|
34474
|
+
VTX_INFERENCE_HOST_INSTANCE: worker.instance_name
|
|
34475
|
+
});
|
|
34476
|
+
workerLocks.push(await acquireInferenceHostProcessLock(
|
|
34477
|
+
foregroundHostLockPath(workerConfig)
|
|
34478
|
+
));
|
|
34479
|
+
workerLocks.push(await acquireInferenceHostProcessLock(workerConfig.processLockPath));
|
|
34480
|
+
}
|
|
34481
|
+
try {
|
|
34482
|
+
if (!activeTransaction) {
|
|
34483
|
+
throw new Error("Inference-host service recovery transaction was not prepared.");
|
|
34484
|
+
}
|
|
34485
|
+
const attempts = await recoveryStore.loadAll();
|
|
34486
|
+
assertResumableCodexRecoveryEvidence(
|
|
34487
|
+
backupAttempts,
|
|
34488
|
+
attempts,
|
|
34489
|
+
activeTransaction.cleanup_confirmed_attempt_ids,
|
|
34490
|
+
activeTransaction.phase
|
|
34491
|
+
);
|
|
34492
|
+
const unresolved = Object.keys(backupAttempts).filter((attemptId) => !activeTransaction.cleanup_confirmed_attempt_ids.includes(attemptId) && !attempts[attemptId]?.cleanupConfirmed);
|
|
34493
|
+
if (unresolved.length === 0) return 0;
|
|
34494
|
+
const currentRaw = await readInferencePrivateFile(
|
|
34495
|
+
codexRecoveryPath(config2),
|
|
34496
|
+
"Codex attempt recovery file"
|
|
34497
|
+
);
|
|
34498
|
+
if (currentRaw === null || recoverySha256 === null || createHash7("sha256").update(currentRaw).digest("hex") !== recoverySha256) {
|
|
34499
|
+
throw new Error("Codex recovery evidence changed while the service was stopping.");
|
|
34500
|
+
}
|
|
34501
|
+
const proof = await (dependencies.proveCodexQuiescence ?? proveCodexSameBootQuiescence)({
|
|
34502
|
+
attempts,
|
|
34503
|
+
confirmNoManagedCodexProcesses: async () => await (dependencies.confirmNoManagedCodexProcesses ?? confirmNoVtxManagedCodexProcesses)({ pinnedBinaryPath: binary.path })
|
|
34504
|
+
});
|
|
34505
|
+
return await (dependencies.reconcileCodexRecovery ?? reconcileCodexAttemptRecovery)({
|
|
34506
|
+
recoveryHooks: recoveryStore,
|
|
34507
|
+
codexHome: config2.codexHomePath,
|
|
34508
|
+
guardianReceiptRoot: codexGuardianReceiptRoot(config2),
|
|
34509
|
+
sameBootQuiescenceProof: proof
|
|
34510
|
+
});
|
|
34511
|
+
} finally {
|
|
34512
|
+
if (!activeTransaction) {
|
|
34513
|
+
throw new Error("Inference-host service recovery transaction was not prepared.");
|
|
34514
|
+
}
|
|
34515
|
+
const progressedAttempts = await recoveryStore.loadAll();
|
|
34516
|
+
assertResumableCodexRecoveryEvidence(
|
|
34517
|
+
backupAttempts,
|
|
34518
|
+
progressedAttempts,
|
|
34519
|
+
activeTransaction.cleanup_confirmed_attempt_ids,
|
|
34520
|
+
activeTransaction.phase
|
|
34521
|
+
);
|
|
34522
|
+
const priorConfirmedIds = new Set(activeTransaction.cleanup_confirmed_attempt_ids);
|
|
34523
|
+
const cleanupConfirmedAttemptIds = Object.keys(backupAttempts).filter((attemptId) => priorConfirmedIds.has(attemptId) || progressedAttempts[attemptId]?.cleanupConfirmed).sort();
|
|
34524
|
+
activeTransaction = {
|
|
34525
|
+
...activeTransaction,
|
|
34526
|
+
phase: cleanupConfirmedAttemptIds.length === Object.keys(backupAttempts).length ? "reconciled" : "prepared",
|
|
34527
|
+
cleanup_confirmed_attempt_ids: cleanupConfirmedAttemptIds
|
|
34528
|
+
};
|
|
34529
|
+
await writeAtomicInferencePrivateFile(
|
|
34530
|
+
serviceRecoveryTransactionPath(config2),
|
|
34531
|
+
`${JSON.stringify(activeTransaction, null, 2)}
|
|
34532
|
+
`
|
|
34533
|
+
);
|
|
34534
|
+
}
|
|
34535
|
+
} finally {
|
|
34536
|
+
for (const lock2 of workerLocks.reverse()) await lock2.release();
|
|
34537
|
+
}
|
|
34538
|
+
}, {
|
|
34539
|
+
restoreDesiredRunning: existingTransaction?.restore_desired_running,
|
|
34540
|
+
expectedManifestGeneration: existingTransaction?.manifest_generation,
|
|
34541
|
+
prepare: existingTransaction ? void 0 : async ({ manifest, desiredRunning }) => {
|
|
34542
|
+
activeTransaction = {
|
|
34543
|
+
schema_version: "vtx_inference_service_recovery_v1",
|
|
34544
|
+
phase: "prepared",
|
|
34545
|
+
transaction_id: transactionId,
|
|
34546
|
+
instance_name: config2.instanceName,
|
|
34547
|
+
manifest_generation: manifest.generation,
|
|
34548
|
+
restore_desired_running: desiredRunning,
|
|
34549
|
+
recovery_backup_path: backupPath,
|
|
34550
|
+
recovery_backup_sha256: backupSha256,
|
|
34551
|
+
cleanup_confirmed_attempt_ids: [],
|
|
34552
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
34553
|
+
};
|
|
34554
|
+
await writeAtomicInferencePrivateFile(
|
|
34555
|
+
serviceRecoveryTransactionPath(config2),
|
|
34556
|
+
`${JSON.stringify(activeTransaction, null, 2)}
|
|
34557
|
+
`
|
|
34558
|
+
);
|
|
34559
|
+
}
|
|
34560
|
+
});
|
|
34561
|
+
completed = true;
|
|
34562
|
+
const outcomeUnknownAttemptCount = Object.values(backupAttempts).filter(
|
|
34563
|
+
(checkpoint) => checkpoint.dispatchOutcome === "outcome_unknown"
|
|
34564
|
+
).length;
|
|
34565
|
+
return {
|
|
34566
|
+
exitCode: 0,
|
|
34567
|
+
stdout: render({
|
|
34568
|
+
status: "service_recovered",
|
|
34569
|
+
instance_name: config2.instanceName,
|
|
34570
|
+
recovery_needed: true,
|
|
34571
|
+
recovered_attempt_count: recovered.result,
|
|
34572
|
+
outcome_unknown_attempt_count: outcomeUnknownAttemptCount,
|
|
34573
|
+
desired_running: recovered.status.desired_running,
|
|
34574
|
+
manager_active: recovered.status.manager_active,
|
|
34575
|
+
worker_runtime_state: recovered.status.workers.find(
|
|
34576
|
+
(worker) => worker.instance_name === config2.instanceName
|
|
34577
|
+
)?.runtime_state ?? "unknown"
|
|
34578
|
+
}, parsed.json),
|
|
34579
|
+
stderr: ""
|
|
34580
|
+
};
|
|
34581
|
+
} finally {
|
|
34582
|
+
if (completed) {
|
|
34583
|
+
await clearInferencePrivateFile(
|
|
34584
|
+
serviceRecoveryTransactionPath(config2),
|
|
34585
|
+
"Inference-host service recovery transaction"
|
|
34586
|
+
);
|
|
34587
|
+
await rm5(backupPath, { force: true });
|
|
34588
|
+
}
|
|
34589
|
+
}
|
|
34590
|
+
} finally {
|
|
34591
|
+
await credentialLock.release();
|
|
34592
|
+
}
|
|
34593
|
+
};
|
|
33982
34594
|
serviceCommand = async (config2, parsed, env, dependencies, warnings) => {
|
|
33983
34595
|
const action = parsed.serviceAction;
|
|
33984
34596
|
if (!action) {
|
|
33985
|
-
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
|
|
34597
|
+
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|recover|uninstall>.");
|
|
33986
34598
|
}
|
|
33987
34599
|
if (action === "run-internal") {
|
|
33988
34600
|
if (!parsed.serviceManifestPath) throw new Error("Internal service manifest path is required.");
|
|
@@ -34009,15 +34621,32 @@ Waiting for approval...
|
|
|
34009
34621
|
await serviceLock.release();
|
|
34010
34622
|
}
|
|
34011
34623
|
}
|
|
34624
|
+
if (parsed.forceRecovery && action !== "recover") {
|
|
34625
|
+
throw new Error("--force-recovery is accepted only by service recover.");
|
|
34626
|
+
}
|
|
34012
34627
|
const manager = dependencies.createServiceManager?.(
|
|
34013
34628
|
config2,
|
|
34014
34629
|
dependencies.serviceDependencies
|
|
34015
34630
|
) ?? new InferenceHostServiceManager(config2, dependencies.serviceDependencies);
|
|
34631
|
+
if (action === "recover") {
|
|
34632
|
+
return await recoverInstalledCodexService(
|
|
34633
|
+
config2,
|
|
34634
|
+
parsed,
|
|
34635
|
+
env,
|
|
34636
|
+
dependencies,
|
|
34637
|
+
manager
|
|
34638
|
+
);
|
|
34639
|
+
}
|
|
34016
34640
|
if (action === "install") {
|
|
34017
34641
|
const credentialLock = await acquireInferenceHostProcessLock(
|
|
34018
34642
|
credentialLifecycleLockPath(config2)
|
|
34019
34643
|
);
|
|
34020
34644
|
try {
|
|
34645
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
34646
|
+
throw new Error(
|
|
34647
|
+
"A guarded service recovery transaction must finish before installing a subscription."
|
|
34648
|
+
);
|
|
34649
|
+
}
|
|
34021
34650
|
const adapter = parsed.adapter || "codex";
|
|
34022
34651
|
if (adapter !== "codex") {
|
|
34023
34652
|
throw new Error(
|
|
@@ -34075,9 +34704,15 @@ Waiting for approval...
|
|
|
34075
34704
|
};
|
|
34076
34705
|
}
|
|
34077
34706
|
if (action === "start") {
|
|
34707
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
34708
|
+
throw new Error("A guarded service recovery transaction must finish before starting the service.");
|
|
34709
|
+
}
|
|
34078
34710
|
return { exitCode: 0, stdout: render({ status: "service_started", ...await manager.start() }, parsed.json), stderr: "" };
|
|
34079
34711
|
}
|
|
34080
34712
|
if (action === "stop") {
|
|
34713
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
34714
|
+
throw new Error("A guarded service recovery transaction must finish before stopping the service.");
|
|
34715
|
+
}
|
|
34081
34716
|
return { exitCode: 0, stdout: render({ status: "service_stopped", ...await manager.stop() }, parsed.json), stderr: "" };
|
|
34082
34717
|
}
|
|
34083
34718
|
if (action === "status") {
|
|
@@ -34090,6 +34725,11 @@ Waiting for approval...
|
|
|
34090
34725
|
credentialLifecycleLockPath(config2)
|
|
34091
34726
|
);
|
|
34092
34727
|
try {
|
|
34728
|
+
if (await readServiceRecoveryTransaction(config2)) {
|
|
34729
|
+
throw new Error(
|
|
34730
|
+
"A guarded service recovery transaction must finish before uninstalling a subscription."
|
|
34731
|
+
);
|
|
34732
|
+
}
|
|
34093
34733
|
if (parsed.instanceExplicit) {
|
|
34094
34734
|
const retained = await readInferenceHostCredentialContext(config2);
|
|
34095
34735
|
const retainedIdentity = retained ? resolveInferenceHostConfig({
|
|
@@ -34108,7 +34748,7 @@ Waiting for approval...
|
|
|
34108
34748
|
await credentialLock.release();
|
|
34109
34749
|
}
|
|
34110
34750
|
}
|
|
34111
|
-
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|uninstall>.");
|
|
34751
|
+
throw new Error("Usage: vtx inference-host service <install|start|stop|status|logs|recover|uninstall>.");
|
|
34112
34752
|
};
|
|
34113
34753
|
hasExplicitCredentialStoreConfiguration = (env) => Boolean(
|
|
34114
34754
|
String(env.VTX_INFERENCE_HOST_CREDENTIAL_STORE || "").trim() || String(env.VTX_INFERENCE_HOST_CREDENTIAL_FILE || "").trim()
|
|
@@ -34130,8 +34770,8 @@ Waiting for approval...
|
|
|
34130
34770
|
}
|
|
34131
34771
|
throw new Error("Inference host credential store identity is invalid.");
|
|
34132
34772
|
};
|
|
34133
|
-
commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "uninstall");
|
|
34134
|
-
commandUsesInstalledServiceCredentials = (parsed) => parsed.command === "login" || parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "status" || parsed.command === "doctor" || parsed.command === "service" && parsed.serviceAction === "install" || parsed.command === "service" && parsed.serviceAction === "uninstall" && parsed.instanceExplicit;
|
|
34773
|
+
commandRequiresRetainedCredentialStore = (parsed) => parsed.command === "logout" || parsed.command === "revoke" || parsed.command === "service" && parsed.instanceExplicit && (parsed.serviceAction === "install" || parsed.serviceAction === "recover" || parsed.serviceAction === "uninstall");
|
|
34774
|
+
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;
|
|
34135
34775
|
resolveInferenceHostCommandConfig = async (parsed, env, dependencies) => {
|
|
34136
34776
|
const selectedEnv = {
|
|
34137
34777
|
...env,
|
|
@@ -34154,6 +34794,9 @@ Waiting for approval...
|
|
|
34154
34794
|
const installedEnvironment = manifest?.workers.find(
|
|
34155
34795
|
(worker) => worker.instance_name === baseConfig.instanceName
|
|
34156
34796
|
)?.runtime_environment;
|
|
34797
|
+
if (parsed.command === "service" && parsed.serviceAction === "recover" && !installedEnvironment) {
|
|
34798
|
+
throw new Error(`Inference-host instance ${baseConfig.instanceName} is not installed.`);
|
|
34799
|
+
}
|
|
34157
34800
|
const persistedContext = installedEnvironment ? null : await readInferenceHostCredentialContext(baseConfig);
|
|
34158
34801
|
const revocationCheckpoint = !installedEnvironment && !persistedContext && parsed.command === "revoke" ? await readRevocationCheckpoint(baseConfig) : null;
|
|
34159
34802
|
const retainedEnvironment = installedEnvironment ?? (persistedContext ? {
|
|
@@ -38681,7 +39324,7 @@ var init_utils = __esm({
|
|
|
38681
39324
|
});
|
|
38682
39325
|
|
|
38683
39326
|
// node_modules/ethers/lib.esm/crypto/crypto.js
|
|
38684
|
-
import { createHash as
|
|
39327
|
+
import { createHash as createHash8, createHmac, pbkdf2Sync, randomBytes as randomBytes5 } from "crypto";
|
|
38685
39328
|
var init_crypto2 = __esm({
|
|
38686
39329
|
"node_modules/ethers/lib.esm/crypto/crypto.js"() {
|
|
38687
39330
|
}
|
|
@@ -39895,10 +40538,10 @@ var init_sha22 = __esm({
|
|
|
39895
40538
|
init_crypto2();
|
|
39896
40539
|
init_utils();
|
|
39897
40540
|
_sha256 = function(data) {
|
|
39898
|
-
return
|
|
40541
|
+
return createHash8("sha256").update(data).digest();
|
|
39899
40542
|
};
|
|
39900
40543
|
_sha512 = function(data) {
|
|
39901
|
-
return
|
|
40544
|
+
return createHash8("sha512").update(data).digest();
|
|
39902
40545
|
};
|
|
39903
40546
|
__sha256 = _sha256;
|
|
39904
40547
|
__sha512 = _sha512;
|
|
@@ -52075,7 +52718,7 @@ __export(vtx_exports, {
|
|
|
52075
52718
|
runVtxCli: () => runVtxCli
|
|
52076
52719
|
});
|
|
52077
52720
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
52078
|
-
import { spawn as
|
|
52721
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
52079
52722
|
function render2(value, json2) {
|
|
52080
52723
|
if (json2) {
|
|
52081
52724
|
return `${JSON.stringify(value, null, 2)}
|
|
@@ -52105,7 +52748,7 @@ function openBrowser(url2) {
|
|
|
52105
52748
|
const platform = process.platform;
|
|
52106
52749
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
52107
52750
|
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
52108
|
-
const child =
|
|
52751
|
+
const child = spawn8(command, args, { detached: true, stdio: "ignore" });
|
|
52109
52752
|
child.unref();
|
|
52110
52753
|
}
|
|
52111
52754
|
function parseFlags(args) {
|