@corca-org/ceal-host-linux-arm64 1.0.19 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ceal-host +0 -0
- package/libexec/ceal-host/claude-hook.mjs +200 -75
- package/libexec/ceal-host/claude-invocation-authentication.mjs +174 -81
- package/libexec/ceal-host/claude-producer.mjs +212 -119
- package/libexec/ceal-host/codex-hook.mjs +216 -25
- package/libexec/ceal-host/codex-invocation-authentication.mjs +207 -46
- package/libexec/ceal-host/codex-producer.mjs +259 -90
- package/libexec/ceal-host/control.mjs +764 -212
- package/libexec/ceal-host/relay.mjs +92 -14
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -1016,6 +1016,7 @@ var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_f
|
|
|
1016
1016
|
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
1017
1017
|
|
|
1018
1018
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
1019
|
+
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
1019
1020
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
1020
1021
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
1021
1022
|
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
|
|
@@ -1099,6 +1100,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1099
1100
|
next: { required: true, timestamp: false },
|
|
1100
1101
|
kind: { required: true, timestamp: false },
|
|
1101
1102
|
matched_kinds: { required: false, timestamp: false },
|
|
1103
|
+
title_match: { required: false, timestamp: false },
|
|
1102
1104
|
title: { required: true, timestamp: false },
|
|
1103
1105
|
container_label: { required: true, timestamp: false },
|
|
1104
1106
|
container_ref: { required: false, timestamp: false },
|
|
@@ -1584,7 +1586,7 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
1584
1586
|
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
1585
1587
|
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
1586
1588
|
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1587
|
-
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn;
|
|
1589
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; send a new message to start a fresh Host turn, then retry this command on that turn (run `ceal-host install` only if the Host hook registration is missing or drifted)" }),
|
|
1588
1590
|
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1589
1591
|
no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
|
|
1590
1592
|
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
@@ -1605,6 +1607,7 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
1605
1607
|
});
|
|
1606
1608
|
|
|
1607
1609
|
// packages/ceal-client-protocol/src/host-service-diagnostic.ts
|
|
1610
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
|
|
1608
1611
|
var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
|
|
1609
1612
|
var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
1610
1613
|
"hook_invoked",
|
|
@@ -1613,6 +1616,7 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
|
1613
1616
|
"watcher_turn_observed",
|
|
1614
1617
|
"current_prompt_registered",
|
|
1615
1618
|
"current_prompt_registration_failed",
|
|
1619
|
+
"current_prompt_capacity_exhausted",
|
|
1616
1620
|
"activation_recorded",
|
|
1617
1621
|
"activation_failed",
|
|
1618
1622
|
"watcher_scan_failed",
|
|
@@ -2085,7 +2089,7 @@ function exactOptional(value, required, optional) {
|
|
|
2085
2089
|
// host/relay/src/host-producer-core.ts
|
|
2086
2090
|
import { createHash as createHash3 } from "node:crypto";
|
|
2087
2091
|
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2088
|
-
import { mkdir as mkdir2, open as
|
|
2092
|
+
import { mkdir as mkdir2, open as open4, readFile, rename as rename2, writeFile } from "node:fs/promises";
|
|
2089
2093
|
import { join as join2 } from "node:path";
|
|
2090
2094
|
|
|
2091
2095
|
// host/relay/src/relay-ipc.ts
|
|
@@ -2111,6 +2115,64 @@ var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
|
2111
2115
|
// host/relay/src/current-prompt-custody.ts
|
|
2112
2116
|
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2113
2117
|
|
|
2118
|
+
// host/service-log.ts
|
|
2119
|
+
import { copyFile, open as open3, stat, truncate } from "node:fs/promises";
|
|
2120
|
+
import { isAbsolute } from "node:path";
|
|
2121
|
+
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
2122
|
+
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
2123
|
+
const line = `${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {}, ...event === "watcher_scan_failed" && cause !== void 0 ? { cause } : {} })}
|
|
2124
|
+
`;
|
|
2125
|
+
try {
|
|
2126
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
2127
|
+
} catch {
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
2131
|
+
process.stderr.write(line);
|
|
2132
|
+
}
|
|
2133
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
2134
|
+
var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
|
|
2135
|
+
function hostServiceLogPreviousPath(path) {
|
|
2136
|
+
return `${path}.1`;
|
|
2137
|
+
}
|
|
2138
|
+
async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
|
|
2139
|
+
let size;
|
|
2140
|
+
try {
|
|
2141
|
+
size = (await stat(path)).size;
|
|
2142
|
+
} catch (error) {
|
|
2143
|
+
if (isNotFound(error)) return false;
|
|
2144
|
+
throw error;
|
|
2145
|
+
}
|
|
2146
|
+
if (size <= maxBytes) return false;
|
|
2147
|
+
await copyFile(path, hostServiceLogPreviousPath(path));
|
|
2148
|
+
await truncate(path, 0);
|
|
2149
|
+
return true;
|
|
2150
|
+
}
|
|
2151
|
+
function boundHostServiceLog(environment = process.env) {
|
|
2152
|
+
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
2153
|
+
if (path === void 0 || !isAbsolute(path)) return () => void 0;
|
|
2154
|
+
let reported = false;
|
|
2155
|
+
const check = () => {
|
|
2156
|
+
rotateHostServiceLog(path).catch((error) => {
|
|
2157
|
+
if (reported) return;
|
|
2158
|
+
reported = true;
|
|
2159
|
+
process.stderr.write(`[host-service-log] rotation_failed code=${errorCode(error)}
|
|
2160
|
+
`);
|
|
2161
|
+
});
|
|
2162
|
+
};
|
|
2163
|
+
check();
|
|
2164
|
+
const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
|
|
2165
|
+
timer.unref();
|
|
2166
|
+
return () => clearInterval(timer);
|
|
2167
|
+
}
|
|
2168
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
2169
|
+
function isNotFound(error) {
|
|
2170
|
+
return errorCode(error) === "ENOENT";
|
|
2171
|
+
}
|
|
2172
|
+
function errorCode(error) {
|
|
2173
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2114
2176
|
// host/relay/src/relay-ipc.ts
|
|
2115
2177
|
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
2116
2178
|
"schema_version",
|
|
@@ -2129,6 +2191,7 @@ var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
|
2129
2191
|
]);
|
|
2130
2192
|
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
2131
2193
|
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2194
|
+
var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
|
|
2132
2195
|
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2133
2196
|
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2134
2197
|
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
@@ -2187,11 +2250,17 @@ var HostRelayIpcError = class extends Error {
|
|
|
2187
2250
|
this.failure = failure;
|
|
2188
2251
|
}
|
|
2189
2252
|
};
|
|
2253
|
+
var HostRelayCurrentPromptCapacityRefusal = class extends Error {
|
|
2254
|
+
constructor() {
|
|
2255
|
+
super("current_prompt_capacity_exhausted");
|
|
2256
|
+
this.name = "HostRelayCurrentPromptCapacityRefusal";
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2190
2259
|
function hostRelayExchangeFailure(error) {
|
|
2191
2260
|
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
2192
2261
|
}
|
|
2193
2262
|
function socketErrorFailure(error) {
|
|
2194
|
-
switch (
|
|
2263
|
+
switch (errorCode2(error)) {
|
|
2195
2264
|
case "EACCES":
|
|
2196
2265
|
case "EPERM":
|
|
2197
2266
|
return "permission_denied";
|
|
@@ -2226,8 +2295,14 @@ var HostRelayCurrentPromptPort = class {
|
|
|
2226
2295
|
async register(input) {
|
|
2227
2296
|
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
2228
2297
|
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
2298
|
+
if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
|
|
2229
2299
|
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
2230
2300
|
}
|
|
2301
|
+
async release(identity) {
|
|
2302
|
+
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
|
|
2303
|
+
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
2304
|
+
if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
|
|
2305
|
+
}
|
|
2231
2306
|
};
|
|
2232
2307
|
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
2233
2308
|
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
@@ -2310,7 +2385,7 @@ function decodeResponseBuffer(buffer) {
|
|
|
2310
2385
|
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
2311
2386
|
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
2312
2387
|
if (value === null) return null;
|
|
2313
|
-
if (!record2(value) || !exactKeys2(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
|
|
2388
|
+
if (!record2(value) || !exactKeys2(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
|
|
2314
2389
|
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
2315
2390
|
}
|
|
2316
2391
|
function decodeLengthPrefixedJson(input, maximum) {
|
|
@@ -2336,7 +2411,7 @@ function exactKeys2(value, required, optional = []) {
|
|
|
2336
2411
|
const keys = Object.keys(value);
|
|
2337
2412
|
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2338
2413
|
}
|
|
2339
|
-
function
|
|
2414
|
+
function errorCode2(error) {
|
|
2340
2415
|
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
2341
2416
|
}
|
|
2342
2417
|
|
|
@@ -2346,6 +2421,29 @@ function isHostTranscriptCursorReason(value) {
|
|
|
2346
2421
|
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
2347
2422
|
}
|
|
2348
2423
|
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
2424
|
+
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
2425
|
+
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
2426
|
+
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
2427
|
+
const adapter = adapterFailureKind(error);
|
|
2428
|
+
if (adapter !== void 0) return adapter;
|
|
2429
|
+
const code = transcriptErrorCode(error);
|
|
2430
|
+
if (code === "EFBIG") return "size_budget";
|
|
2431
|
+
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
2432
|
+
return "other";
|
|
2433
|
+
}
|
|
2434
|
+
async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
|
|
2435
|
+
try {
|
|
2436
|
+
await register();
|
|
2437
|
+
diagnostic("current_prompt_registered");
|
|
2438
|
+
return true;
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
|
|
2441
|
+
throw error;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
function transcriptErrorCode(error) {
|
|
2445
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
2446
|
+
}
|
|
2349
2447
|
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
2350
2448
|
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
2351
2449
|
const available = Math.max(0, size - offset);
|
|
@@ -2454,19 +2552,24 @@ var HostTranscriptWatcher = class {
|
|
|
2454
2552
|
return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
|
|
2455
2553
|
}
|
|
2456
2554
|
async scanRegistration(registration) {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2555
|
+
try {
|
|
2556
|
+
return await this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
|
|
2557
|
+
missing: () => this.retireMissing(registration),
|
|
2558
|
+
failed: (handle, error) => this.scanFailed(handle, registration, error)
|
|
2559
|
+
});
|
|
2560
|
+
} catch (error) {
|
|
2561
|
+
if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
|
|
2562
|
+
throw error;
|
|
2563
|
+
}
|
|
2461
2564
|
}
|
|
2462
2565
|
async openTranscript(registration, scan, handlers) {
|
|
2463
2566
|
let handle;
|
|
2464
2567
|
try {
|
|
2465
|
-
handle = await
|
|
2568
|
+
handle = await open4(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
|
|
2466
2569
|
return await scan(handle);
|
|
2467
2570
|
} catch (error) {
|
|
2468
2571
|
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
2469
|
-
if (hostRelayExchangeFailure(error) !== void 0) throw error;
|
|
2572
|
+
if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
|
|
2470
2573
|
return handlers.failed(handle, error);
|
|
2471
2574
|
} finally {
|
|
2472
2575
|
await handle?.close();
|
|
@@ -2607,7 +2710,7 @@ var HostProducerDrainCore = class {
|
|
|
2607
2710
|
const value = JSON.parse(await readFile(this.statePath, "utf8"));
|
|
2608
2711
|
return this.decodeState(value);
|
|
2609
2712
|
} catch (error) {
|
|
2610
|
-
if (
|
|
2713
|
+
if (errorCode3(error) !== "ENOENT") throw error;
|
|
2611
2714
|
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
2612
2715
|
}
|
|
2613
2716
|
}
|
|
@@ -2653,7 +2756,7 @@ var HostProducerDrainCore = class {
|
|
|
2653
2756
|
function isDrainStateHeader(value, schema, installationRef) {
|
|
2654
2757
|
return isTranscriptRecord(value) && value.schema_version === schema && value.producer_installation_ref === installationRef && Number.isSafeInteger(value.producer_epoch) && Number(value.producer_epoch) > 0 && Number.isSafeInteger(value.next_sequence) && Number(value.next_sequence) > 0 && isTranscriptRecord(value.pending);
|
|
2655
2758
|
}
|
|
2656
|
-
function
|
|
2759
|
+
function errorCode3(error) {
|
|
2657
2760
|
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
2658
2761
|
}
|
|
2659
2762
|
|
|
@@ -2672,8 +2775,8 @@ import { createHash as createHash4 } from "node:crypto";
|
|
|
2672
2775
|
|
|
2673
2776
|
// host/codex/src/codex-outbox.ts
|
|
2674
2777
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
2675
|
-
import { lstat as lstat3, mkdir as mkdir3, open as
|
|
2676
|
-
import { dirname, isAbsolute, join as join3 } from "node:path";
|
|
2778
|
+
import { lstat as lstat3, mkdir as mkdir3, open as open5, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
|
|
2779
|
+
import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
2677
2780
|
|
|
2678
2781
|
// host/invocation-context.ts
|
|
2679
2782
|
var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
|
|
@@ -2859,7 +2962,7 @@ var CodexOutboxStore = class {
|
|
|
2859
2962
|
try {
|
|
2860
2963
|
await unlink2(this.entryPath(entry.submission_ref));
|
|
2861
2964
|
} catch (error) {
|
|
2862
|
-
if (!
|
|
2965
|
+
if (!isNotFound2(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
2863
2966
|
}
|
|
2864
2967
|
}
|
|
2865
2968
|
async writeBinding(binding) {
|
|
@@ -2882,7 +2985,7 @@ var CodexOutboxStore = class {
|
|
|
2882
2985
|
try {
|
|
2883
2986
|
await unlink2(path);
|
|
2884
2987
|
} catch (unlinkError) {
|
|
2885
|
-
if (!
|
|
2988
|
+
if (!isNotFound2(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
|
|
2886
2989
|
}
|
|
2887
2990
|
throw error;
|
|
2888
2991
|
}
|
|
@@ -2943,7 +3046,7 @@ var CodexOutboxStore = class {
|
|
|
2943
3046
|
try {
|
|
2944
3047
|
await unlink2(this.activeContextPath(identity));
|
|
2945
3048
|
} catch (error) {
|
|
2946
|
-
if (!
|
|
3049
|
+
if (!isNotFound2(error)) throw storageError("Unable to clear the Codex Host invocation context.");
|
|
2947
3050
|
}
|
|
2948
3051
|
return true;
|
|
2949
3052
|
}
|
|
@@ -3059,7 +3162,7 @@ async function removeOwnerState(path, what) {
|
|
|
3059
3162
|
try {
|
|
3060
3163
|
await unlink2(path);
|
|
3061
3164
|
} catch (error) {
|
|
3062
|
-
if (!
|
|
3165
|
+
if (!isNotFound2(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
|
|
3063
3166
|
}
|
|
3064
3167
|
}
|
|
3065
3168
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
@@ -3109,7 +3212,7 @@ async function writeAtomicJson(path, value) {
|
|
|
3109
3212
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
3110
3213
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
3111
3214
|
try {
|
|
3112
|
-
const handle = await
|
|
3215
|
+
const handle = await open5(temp, "wx", 384);
|
|
3113
3216
|
try {
|
|
3114
3217
|
await handle.writeFile(text, "utf8");
|
|
3115
3218
|
await handle.sync();
|
|
@@ -3129,7 +3232,7 @@ async function writeAtomicJson(path, value) {
|
|
|
3129
3232
|
}
|
|
3130
3233
|
async function syncDirectory(path) {
|
|
3131
3234
|
try {
|
|
3132
|
-
const handle = await
|
|
3235
|
+
const handle = await open5(path, "r");
|
|
3133
3236
|
try {
|
|
3134
3237
|
await handle.sync();
|
|
3135
3238
|
} finally {
|
|
@@ -3144,7 +3247,7 @@ async function readOptionalJson(path, validate) {
|
|
|
3144
3247
|
validate(value);
|
|
3145
3248
|
return value;
|
|
3146
3249
|
} catch (error) {
|
|
3147
|
-
if (
|
|
3250
|
+
if (isNotFound2(error)) return null;
|
|
3148
3251
|
if (error instanceof CodexHostAdapterError) throw error;
|
|
3149
3252
|
throw storageError("Codex owner state is malformed.");
|
|
3150
3253
|
}
|
|
@@ -3153,7 +3256,7 @@ async function ensurePrivateOwnerDirectory(path) {
|
|
|
3153
3256
|
try {
|
|
3154
3257
|
await lstat3(path);
|
|
3155
3258
|
} catch (error) {
|
|
3156
|
-
if (!
|
|
3259
|
+
if (!isNotFound2(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
3157
3260
|
await mkdir3(path, { recursive: true, mode: 448 });
|
|
3158
3261
|
}
|
|
3159
3262
|
const stats = await lstat3(path);
|
|
@@ -3165,7 +3268,7 @@ async function ensurePrivateOwnerDirectory(path) {
|
|
|
3165
3268
|
async function readPrivateFile(path) {
|
|
3166
3269
|
let handle;
|
|
3167
3270
|
try {
|
|
3168
|
-
handle = await
|
|
3271
|
+
handle = await open5(path, O_RDONLY3 | O_NOFOLLOW2);
|
|
3169
3272
|
const stats = await handle.stat();
|
|
3170
3273
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
3171
3274
|
if (!stats.isFile()) throw storageError(`Codex owner state path is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
|
|
@@ -3208,7 +3311,7 @@ function validateRegistrationFields(value) {
|
|
|
3208
3311
|
if (!isTimestamp(value.registered_at) || !isTimestamp(value.expires_at)) throw storageError(`Codex transcript registration timestamps are malformed (${refusalFacts([{ name: "registered_at", value: value.registered_at }, { name: "expires_at", value: value.expires_at }])}).`);
|
|
3209
3312
|
}
|
|
3210
3313
|
function validRegistrationCwd(value) {
|
|
3211
|
-
return value === void 0 || typeof value === "string" &&
|
|
3314
|
+
return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
|
|
3212
3315
|
}
|
|
3213
3316
|
function validateCursor(value) {
|
|
3214
3317
|
if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
@@ -3303,7 +3406,7 @@ function sameKeys(value, required, optional = []) {
|
|
|
3303
3406
|
function isRecord4(value) {
|
|
3304
3407
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3305
3408
|
}
|
|
3306
|
-
function
|
|
3409
|
+
function isNotFound2(error) {
|
|
3307
3410
|
return isRecord4(error) && error.code === "ENOENT";
|
|
3308
3411
|
}
|
|
3309
3412
|
function storageError(message) {
|
|
@@ -3693,6 +3796,8 @@ function codexSubmissionReference(hostInputRef, event, ordinal) {
|
|
|
3693
3796
|
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
3694
3797
|
store;
|
|
3695
3798
|
registerCurrentPrompt;
|
|
3799
|
+
releaseCurrentPrompt;
|
|
3800
|
+
diagnostic;
|
|
3696
3801
|
constructor(options) {
|
|
3697
3802
|
super(options, {
|
|
3698
3803
|
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
@@ -3701,6 +3806,8 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
3701
3806
|
});
|
|
3702
3807
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
3703
3808
|
this.registerCurrentPrompt = options.register_current_prompt;
|
|
3809
|
+
this.releaseCurrentPrompt = options.release_current_prompt;
|
|
3810
|
+
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
3704
3811
|
}
|
|
3705
3812
|
transcriptPathOf(registration) {
|
|
3706
3813
|
return registration.transcript_path;
|
|
@@ -3716,17 +3823,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
3716
3823
|
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
3717
3824
|
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
3718
3825
|
}
|
|
3719
|
-
async scanFailed(handle, registration,
|
|
3826
|
+
async scanFailed(handle, registration, error) {
|
|
3827
|
+
this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
3720
3828
|
await this.quarantineFailure(registration, handle);
|
|
3721
3829
|
return { records: 0, enqueued: 0, quarantined: true };
|
|
3722
3830
|
}
|
|
3723
3831
|
async retireMissing(registration) {
|
|
3832
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
|
|
3724
3833
|
return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
|
|
3725
3834
|
}
|
|
3726
3835
|
async preflight(registration, handle, size, device, inode) {
|
|
3727
3836
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
3728
3837
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
3729
|
-
if (cursor.status === "quarantined")
|
|
3838
|
+
if (cursor.status === "quarantined") {
|
|
3839
|
+
if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
3840
|
+
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
3841
|
+
}
|
|
3730
3842
|
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
3731
3843
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
3732
3844
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
@@ -3765,25 +3877,41 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
3765
3877
|
const stats = await handle.stat().catch(() => null);
|
|
3766
3878
|
if (stats === null) return;
|
|
3767
3879
|
const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
|
|
3768
|
-
|
|
3880
|
+
const quarantined = {
|
|
3881
|
+
...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
|
|
3882
|
+
transcript_path: registration.transcript_path,
|
|
3883
|
+
device: String(stats.dev),
|
|
3884
|
+
inode: String(stats.ino),
|
|
3885
|
+
status: "quarantined",
|
|
3886
|
+
reason: "transcript_malformed",
|
|
3887
|
+
updated_at: this.stamp()
|
|
3888
|
+
};
|
|
3889
|
+
await this.store.writeTranscriptCursor(quarantined);
|
|
3769
3890
|
}
|
|
3770
3891
|
async processRecord(registration, activeTask, record3) {
|
|
3771
|
-
if (record3.kind === "task_started")
|
|
3772
|
-
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
3773
|
-
}
|
|
3892
|
+
if (record3.kind === "task_started") return this.processTaskStarted(registration, activeTask, record3);
|
|
3774
3893
|
if (record3.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record3.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record3.model } : activeTask }, enqueued: 0 };
|
|
3775
3894
|
if (record3.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record3) }, enqueued: 0 };
|
|
3776
3895
|
if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
|
|
3777
3896
|
if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
|
|
3778
3897
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
3779
3898
|
}
|
|
3899
|
+
async processTaskStarted(registration, activeTask, record3) {
|
|
3900
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
3901
|
+
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
3902
|
+
}
|
|
3780
3903
|
async processUserInput(registration, activeTask, record3) {
|
|
3781
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id)
|
|
3904
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) {
|
|
3905
|
+
const adopted = await this.adoptHookCorrelatedTurn(registration, activeTask, record3);
|
|
3906
|
+
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
3907
|
+
activeTask = adopted;
|
|
3908
|
+
}
|
|
3782
3909
|
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id, record3.message_id, record3.ordinal, record3.prompt);
|
|
3783
3910
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
3784
3911
|
const promptValue = promptObservationValue(record3.prompt);
|
|
3785
3912
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
3786
3913
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
3914
|
+
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
3787
3915
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
3788
3916
|
const observations = [{
|
|
3789
3917
|
observation_ref: observationRef("transcript_user_text", hostInputRef, record3.prompt),
|
|
@@ -3809,14 +3937,88 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
3809
3937
|
created_at: record3.timestamp,
|
|
3810
3938
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
3811
3939
|
};
|
|
3812
|
-
await this.
|
|
3813
|
-
await this.
|
|
3814
|
-
|
|
3940
|
+
const persisted = await this.persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
3941
|
+
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
3942
|
+
return persisted;
|
|
3943
|
+
}
|
|
3944
|
+
/**
|
|
3945
|
+
* The steered prompt to re-activate under the running task, or null when
|
|
3946
|
+
* there is nothing to repair. A steered message joins the running turn
|
|
3947
|
+
* with new prompt bytes but no task_started. When the steer hook already
|
|
3948
|
+
* replaced the turn selection (a turn id the hook did not recognize as
|
|
3949
|
+
* the running one, or a hook that ran before any context was live), the
|
|
3950
|
+
* running task's context no longer matches and the next invocation would
|
|
3951
|
+
* wait for a boundary that never comes. When the hook kept the live
|
|
3952
|
+
* selection, the context still matches and this stays null.
|
|
3953
|
+
*/
|
|
3954
|
+
async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
|
|
3955
|
+
if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
|
|
3956
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
|
|
3957
|
+
return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Adopt the live hooked turn when its transcript message arrives before
|
|
3961
|
+
* (or without) its task_started, as an interrupted turn's follow-up can.
|
|
3962
|
+
* The UserPromptSubmit hook names the exact hooked prompt, so only a
|
|
3963
|
+
* message carrying those bytes adopts; inherited history and other turns
|
|
3964
|
+
* stay ignored. A stale active task for another turn releases first, the
|
|
3965
|
+
* same as a task_started supersession.
|
|
3966
|
+
*/
|
|
3967
|
+
async adoptHookCorrelatedTurn(registration, activeTask, record3) {
|
|
3968
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record3.prompt).body_sha256);
|
|
3969
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
3970
|
+
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
3971
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) {
|
|
3972
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
3973
|
+
}
|
|
3974
|
+
return {
|
|
3975
|
+
codex_turn_id: record3.turn_id,
|
|
3976
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id),
|
|
3977
|
+
started_at: record3.timestamp
|
|
3978
|
+
};
|
|
3979
|
+
}
|
|
3980
|
+
async persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
3981
|
+
const custodyRegistered = await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
|
|
3982
|
+
if (custodyRegistered) this.diagnostic("watcher_turn_observed");
|
|
3983
|
+
try {
|
|
3984
|
+
await this.store.put(envelope);
|
|
3985
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
3986
|
+
} catch (error) {
|
|
3987
|
+
if (custodyRegistered) await this.releasePrompt(registration.host_session_ref, activeTask, currentPromptRef).catch(() => void 0);
|
|
3988
|
+
throw error;
|
|
3989
|
+
}
|
|
3815
3990
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
3816
3991
|
}
|
|
3817
3992
|
async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
|
|
3818
|
-
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return;
|
|
3819
|
-
|
|
3993
|
+
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
|
|
3994
|
+
const register = this.registerCurrentPrompt;
|
|
3995
|
+
const installationRef = this.producerInstallationRef;
|
|
3996
|
+
return registerHostCurrentPromptDiagnosed(
|
|
3997
|
+
() => register({ producer_id: "codex", producer_installation_ref: installationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt }),
|
|
3998
|
+
(event) => this.diagnostic(event)
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
4002
|
+
try {
|
|
4003
|
+
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
4004
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
4005
|
+
await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
4006
|
+
this.diagnostic("activation_recorded");
|
|
4007
|
+
} catch (error) {
|
|
4008
|
+
this.diagnostic("activation_failed");
|
|
4009
|
+
throw error;
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
async releasePrompt(sessionRef, activeTask, promptRef) {
|
|
4013
|
+
if (this.producerInstallationRef === void 0 || this.releaseCurrentPrompt === void 0) return;
|
|
4014
|
+
await this.releaseCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef });
|
|
4015
|
+
}
|
|
4016
|
+
async releaseStoredPromptBestEffort(sessionRef, cursor) {
|
|
4017
|
+
await this.releaseActiveTaskBestEffort(sessionRef, cursor?.active_task);
|
|
4018
|
+
}
|
|
4019
|
+
async releaseActiveTaskBestEffort(sessionRef, activeTask) {
|
|
4020
|
+
if (activeTask?.prompt_ref === void 0) return;
|
|
4021
|
+
await this.releasePrompt(sessionRef, activeTask, activeTask.prompt_ref).catch(() => void 0);
|
|
3820
4022
|
}
|
|
3821
4023
|
async processTerminal(registration, activeTask, record3) {
|
|
3822
4024
|
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
@@ -3836,18 +4038,23 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
3836
4038
|
created_at: record3.timestamp,
|
|
3837
4039
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
3838
4040
|
});
|
|
4041
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
3839
4042
|
await this.store.clearInvocationContext(identity, this.now());
|
|
3840
4043
|
return { enqueued: 1 };
|
|
3841
4044
|
}
|
|
3842
4045
|
async quarantine(registration, cursor, device, inode, reason) {
|
|
4046
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
3843
4047
|
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
|
|
3844
4048
|
await this.store.writeTranscriptCursor(quarantined);
|
|
3845
4049
|
}
|
|
3846
4050
|
};
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
4051
|
+
function codexTranscriptScanFailureKind(error) {
|
|
4052
|
+
return transcriptScanFailureKind(error, (candidate) => {
|
|
4053
|
+
if (!(candidate instanceof CodexHostAdapterError)) return void 0;
|
|
4054
|
+
if (candidate.code === "payload_rejected") return "payload_rejected";
|
|
4055
|
+
if (candidate.code === "storage") return "read_error";
|
|
4056
|
+
return void 0;
|
|
4057
|
+
});
|
|
3851
4058
|
}
|
|
3852
4059
|
function accumulateTokenCount(activeTask, record3) {
|
|
3853
4060
|
if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
|
|
@@ -3935,7 +4142,16 @@ function assertRecordSequence(registration, cursor, records, lastOrdinal, record
|
|
|
3935
4142
|
// host/codex/src/codex-producer-runtime.ts
|
|
3936
4143
|
async function drainCodexProducerOnce(options) {
|
|
3937
4144
|
const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
|
|
3938
|
-
const scan = await new CodexTranscriptWatcher({
|
|
4145
|
+
const scan = await new CodexTranscriptWatcher({
|
|
4146
|
+
outbox_dir: options.outbox_dir,
|
|
4147
|
+
producer_installation_ref: options.producer_installation_ref,
|
|
4148
|
+
register_current_prompt: (input) => promptPort.register(input),
|
|
4149
|
+
release_current_prompt: (identity) => promptPort.release(identity),
|
|
4150
|
+
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
4151
|
+
}).scanOnce().catch((error) => {
|
|
4152
|
+
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
4153
|
+
throw error;
|
|
4154
|
+
});
|
|
3939
4155
|
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, producer_installation_ref: options.producer_installation_ref, relay: new HostRelayIpcAdmissionPort(options.relay_endpoint, options.timeout_ms), ...options.producer_epoch === void 0 ? {} : { producer_epoch: options.producer_epoch }, ...options.now === void 0 ? {} : { now: options.now } });
|
|
3940
4156
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
3941
4157
|
}
|
|
@@ -3961,53 +4177,6 @@ function runCodexProducerCli(args, signal, dependencies = {}) {
|
|
|
3961
4177
|
return runHostProducerCli(CODEX_PRODUCER_DESCRIPTOR, args, signal, dependencies);
|
|
3962
4178
|
}
|
|
3963
4179
|
|
|
3964
|
-
// host/service-log.ts
|
|
3965
|
-
import { copyFile, open as open5, stat, truncate } from "node:fs/promises";
|
|
3966
|
-
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
3967
|
-
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
3968
|
-
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
3969
|
-
var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
|
|
3970
|
-
function hostServiceLogPreviousPath(path) {
|
|
3971
|
-
return `${path}.1`;
|
|
3972
|
-
}
|
|
3973
|
-
async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
|
|
3974
|
-
let size;
|
|
3975
|
-
try {
|
|
3976
|
-
size = (await stat(path)).size;
|
|
3977
|
-
} catch (error) {
|
|
3978
|
-
if (isNotFound2(error)) return false;
|
|
3979
|
-
throw error;
|
|
3980
|
-
}
|
|
3981
|
-
if (size <= maxBytes) return false;
|
|
3982
|
-
await copyFile(path, hostServiceLogPreviousPath(path));
|
|
3983
|
-
await truncate(path, 0);
|
|
3984
|
-
return true;
|
|
3985
|
-
}
|
|
3986
|
-
function boundHostServiceLog(environment = process.env) {
|
|
3987
|
-
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
3988
|
-
if (path === void 0 || !isAbsolute2(path)) return () => void 0;
|
|
3989
|
-
let reported = false;
|
|
3990
|
-
const check = () => {
|
|
3991
|
-
rotateHostServiceLog(path).catch((error) => {
|
|
3992
|
-
if (reported) return;
|
|
3993
|
-
reported = true;
|
|
3994
|
-
process.stderr.write(`[host-service-log] rotation_failed code=${errorCode3(error)}
|
|
3995
|
-
`);
|
|
3996
|
-
});
|
|
3997
|
-
};
|
|
3998
|
-
check();
|
|
3999
|
-
const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
|
|
4000
|
-
timer.unref();
|
|
4001
|
-
return () => clearInterval(timer);
|
|
4002
|
-
}
|
|
4003
|
-
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
4004
|
-
function isNotFound2(error) {
|
|
4005
|
-
return errorCode3(error) === "ENOENT";
|
|
4006
|
-
}
|
|
4007
|
-
function errorCode3(error) {
|
|
4008
|
-
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
4180
|
// host/producer-command.ts
|
|
4012
4181
|
async function runProducerCommand(schemaVersion, run) {
|
|
4013
4182
|
const controller = new AbortController();
|