@openclaw/codex 2026.9.5 → 2026.9.7
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 +2 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/{account-usage-runtime-C2MOIvfq.mjs → account-usage-runtime-DFhbh1zz.mjs} +6 -6
- package/dist/.setup/{apply-CJ0ZfmBG.mjs → apply-D3GcSoSa.mjs} +35 -55
- package/dist/.setup/{attempt-client-cleanup-BpYKdTA0.mjs → attempt-client-cleanup-DOP-cW5j.mjs} +179 -73
- package/dist/.setup/attempt-workspace-context-C5HigshI.mjs +164 -0
- package/dist/.setup/{auth-binding-DhLWa_7K.mjs → auth-binding-BLWz7stm.mjs} +14 -1
- package/dist/.setup/{auth-cache-key-QirmgiCo.mjs → auth-cache-key-Dbvq-QgB.mjs} +41 -111
- package/dist/.setup/{auth-profile-WqZtZfXN.mjs → auth-profile-B0okYl2D.mjs} +1 -2
- package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
- package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
- package/dist/.setup/auth-start-options-BPIPeEX8.mjs +44 -0
- package/dist/.setup/{binding-connection-Bm38niE8.mjs → binding-connection-DTtN1KrX.mjs} +2 -2
- package/dist/.setup/{binding-connection.runtime-CnRMC0ta.mjs → binding-connection.runtime-6TpgD1M6.mjs} +5 -5
- package/dist/.setup/{bounded-turn-BtlZcivC.mjs → bounded-turn-CbjoN2OW.mjs} +140 -37
- package/dist/.setup/bounded-turn-isolation-B8K_ylqk.mjs +14 -0
- package/dist/.setup/{build-state-Xx9ybZqa.mjs → build-state-Da1_z0kd.mjs} +1 -1
- package/dist/.setup/{capabilities-_vXcZGSx.mjs → capabilities-B3WEZl4e.mjs} +2 -10
- package/dist/.setup/{client-vXs2929Q.mjs → client-N0JpgSzF.mjs} +635 -872
- package/dist/.setup/client-catalog-response-5HHLdcde.mjs +285 -0
- package/dist/.setup/{command-dispatch-naHIZwwF.mjs → command-dispatch-BeAoaovI.mjs} +9 -8
- package/dist/.setup/{command-formatters-CGEkdawF.mjs → command-formatters-R3ckE3Co.mjs} +36 -64
- package/dist/.setup/{command-handlers-BtAJEW1X.mjs → command-handlers-ylTMS00z.mjs} +301 -488
- package/dist/.setup/{command-rpc-DHFpn4le.mjs → command-rpc-CGJGUQ6Y.mjs} +40 -28
- package/dist/.setup/compact-C2WamavS.mjs +776 -0
- package/dist/.setup/{config-CedDWjM-.mjs → config-DKhXquav.mjs} +26 -28
- package/dist/.setup/{config-options-BsanJ8QG.mjs → config-options-CzyWLTJt.mjs} +68 -65
- package/dist/.setup/{config-parsing-CcB9iPoq.mjs → config-parsing-b088LyhT.mjs} +57 -60
- package/dist/.setup/{config-runtime-CeAhl_h4.mjs → config-runtime-C0NbLms1.mjs} +2 -2
- package/dist/.setup/{config-security-BRQ2mgW9.mjs → config-security-CE-FcGw9.mjs} +51 -215
- package/dist/.setup/{config-utils-DujwEnhg.mjs → config-utils-D0CXecki.mjs} +3 -13
- package/dist/.setup/context-engine-projection-PezSESth.mjs +387 -0
- package/dist/.setup/{conversation-binding-DhzrpTM6.mjs → conversation-binding-3Min8qvP.mjs} +71 -105
- package/dist/.setup/{conversation-binding-data-CrcyZk6O.mjs → conversation-binding-data-C746z5Pn.mjs} +8 -12
- package/dist/.setup/{conversation-control-DN53ns41.mjs → conversation-control-CEP8X5JC.mjs} +30 -69
- package/dist/.setup/{cyber-failover-DYwbmf1o.mjs → cyber-failover-XV0nNRZu.mjs} +1 -1
- package/dist/.setup/{cyber-failover-notice-e9G3J9-e.mjs → cyber-failover-notice-RNpGpTyP.mjs} +1 -1
- package/dist/.setup/{dynamic-tools-D16lOp67.mjs → dynamic-tools-DzlsLUuS.mjs} +3136 -3167
- package/dist/.setup/{effective-mcp-catalog-K6kLCN8X.mjs → effective-mcp-catalog-CxUtC-xG.mjs} +8 -17
- package/dist/.setup/{ephemeral-turn-R1gA8Sn6.mjs → ephemeral-turn-CDsPJOa2.mjs} +9 -6
- package/dist/.setup/event-projector-assistant-message-CigreO4K.mjs +69 -0
- package/dist/.setup/{event-projector-usage-C7Ntzs-f.mjs → event-projector-usage-03R0I2dM.mjs} +4 -26
- package/dist/.setup/event-projector-values-DJSz8BTk.mjs +106 -0
- package/dist/.setup/incognito-session-DWYapfwf.mjs +2 -0
- package/dist/.setup/{inference-context-fulhZuz8.mjs → inference-context-D-9gWlTR.mjs} +16 -18
- package/dist/.setup/inference-dispatch-CMZtjQyz.mjs +359 -0
- package/dist/.setup/inference-metadata-CgIf7sOx.mjs +81 -0
- package/dist/.setup/inference-proxy-DhcXOgUE.mjs +561 -0
- package/dist/.setup/{isolated-completion-B6srt48J.mjs → isolated-completion-Dm3PzxJk.mjs} +15 -11
- package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
- package/dist/.setup/{managed-binary-DP6Rib3P.mjs → managed-binary-kiWRhNRE.mjs} +16 -9
- package/dist/.setup/{model-catalog-DkvUuBey.mjs → model-catalog-PAuZgJC6.mjs} +14 -14
- package/dist/.setup/model-runtime-BdqzOMkU.mjs +11 -0
- package/dist/.setup/{models-BDFgm_G-.mjs → models-DHXZtVVk.mjs} +9 -18
- package/dist/.setup/{native-auth-BLZGqFjw.mjs → native-auth-CuG53tu4.mjs} +5 -5
- package/dist/.setup/{native-execution-policy-YBMHSzC4.mjs → native-execution-policy-BxZJy5mP.mjs} +6 -31
- package/dist/.setup/{native-subagent-monitor-B1hxzIOy.mjs → native-subagent-monitor-Dpsmf2wz.mjs} +4422 -3808
- package/dist/.setup/native-subagent-pending-assignments-DwkCzLvD.mjs +203 -0
- package/dist/.setup/native-task-assignments-DMQ0g3hu.mjs +194 -0
- package/dist/.setup/{node-cli-sessions-Cl8b2ZF3.mjs → node-cli-sessions-HC4W6FLI.mjs} +99 -111
- package/dist/.setup/node-exec-server.runtime-BgBfWQqo.mjs +244 -0
- package/dist/.setup/{plan-xD7WKYTM.mjs → plan-Y-QIvrKB.mjs} +118 -163
- package/dist/.setup/{plugin-activation-r8nq5Jzp.mjs → plugin-activation-cNHRfaxU.mjs} +10 -47
- package/dist/.setup/{plugin-app-cache-key-BOxKP7db.mjs → plugin-app-cache-key-BL9B5H0L.mjs} +9 -42
- package/dist/.setup/{plugin-inventory-DUle8DLz.mjs → plugin-inventory-Cky2XJUw.mjs} +32 -23
- package/dist/.setup/{plugin-marketplace-discovery-P-Vndfqq.mjs → plugin-marketplace-discovery-D_dV9FXa.mjs} +22 -25
- package/dist/.setup/{plugin-metadata-cache-DsS5buNa.mjs → plugin-metadata-cache-DsjsL1dr.mjs} +328 -318
- package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
- package/dist/.setup/provider-DH8NSzGt.mjs +51 -0
- package/dist/.setup/{provider-capabilities-B8J4Dy-O.mjs → provider-capabilities-CAxoF-X1.mjs} +35 -12
- package/dist/.setup/{rate-limits-DOEGYRNs.mjs → rate-limits-B7-X2TgG.mjs} +123 -166
- package/dist/.setup/{request-BioXJLYa.mjs → request-BHZ1nz8d.mjs} +25 -16
- package/dist/.setup/responses-oauth-twnuTaAa.mjs +93 -0
- package/dist/.setup/rpc-error-BlD2vuzn.mjs +31 -0
- package/dist/.setup/{run-attempt-rlA07pUv.mjs → run-attempt-D8RZUOzY.mjs} +2046 -1454
- package/dist/.setup/{run-attempt-lifecycle-B7JbY4-F.mjs → run-attempt-lifecycle-C-6O3Exn.mjs} +10 -24
- package/dist/.setup/{runtime-artifact-Dy38Z5iU.mjs → runtime-artifact-DDQ-HGXf.mjs} +43 -47
- package/dist/.setup/runtime-artifact-connection-D68qQn1m.mjs +78 -0
- package/dist/.setup/sandbox-guard-DjCz2eOw.mjs +126 -0
- package/dist/.setup/scope-DOC1Xjc8.mjs +6 -0
- package/dist/.setup/session-binding-DGCYAfvD.mjs +443 -0
- package/dist/.setup/{session-binding-record-TH3xA0zj.mjs → session-binding-record-Oe9L-CNY.mjs} +73 -132
- package/dist/.setup/{session-binding-sidecars-CKYEcfNn.mjs → session-binding-sidecars-jo6H8dFr.mjs} +242 -60
- package/dist/.setup/{session-catalog-fUdWDx-U.mjs → session-catalog-4sCdv5qx.mjs} +598 -452
- package/dist/.setup/{session-catalog-adoption-CR_whkEW.mjs → session-catalog-adoption-BU_pHWG7.mjs} +59 -26
- package/dist/.setup/{session-catalog-archive-CwZaPacf.mjs → session-catalog-archive-spHfihri.mjs} +7 -7
- package/dist/.setup/session-catalog-events-DePDzzjc.mjs +139 -0
- package/dist/.setup/session-catalog-home-id-vlih0B_g.mjs +22 -0
- package/dist/.setup/{session-catalog-index-B141u15s.mjs → session-catalog-index-CZvDzC6z.mjs} +286 -182
- package/dist/.setup/{session-catalog-index-order-Cc5BzlQr.mjs → session-catalog-index-order-BcQpHks3.mjs} +2 -2
- package/dist/.setup/{session-catalog-index-state-BAuDztHN.mjs → session-catalog-index-state-TeO7EyO-.mjs} +6 -2
- package/dist/.setup/session-catalog-limits-DDxOsQeC.mjs +14 -0
- package/dist/.setup/session-catalog-list-request-CvtxrYSe.mjs +170 -0
- package/dist/.setup/session-catalog-native-projection-DjhwGL6x.mjs +178 -0
- package/dist/.setup/{session-catalog-node-adoption-BVRsYOSh.mjs → session-catalog-node-adoption-DgXvSGu0.mjs} +30 -35
- package/dist/.setup/{session-catalog-node-continue-Dowv8osw.mjs → session-catalog-node-continue-DjKXHBbi.mjs} +25 -18
- package/dist/.setup/session-catalog-node-lookup-CtAXI5W5.mjs +56 -0
- package/dist/.setup/{session-catalog-native-projection-ChV3cCtm.mjs → session-catalog-parsing-CPGaPYr-.mjs} +14 -180
- package/dist/.setup/{session-catalog-projection-C1Bj9Jl-.mjs → session-catalog-projection-Cr5qP5Dj.mjs} +15 -5
- package/dist/.setup/session-catalog-provenance-DoAvktcj.mjs +95 -0
- package/dist/.setup/{session-cli-DIj-IE-E.mjs → session-cli-Cn_nCqfq.mjs} +11 -24
- package/dist/.setup/{session-history-C--SrLI8.mjs → session-history-D83lJWHG.mjs} +16 -23
- package/dist/.setup/{session-history-import-JHXT5Xfi.mjs → session-history-import-WhX-X33a.mjs} +7 -10
- package/dist/.setup/session-history-read-Ov3StuL4.mjs +133 -0
- package/dist/.setup/{session-history.worker-CpNj7H_P.mjs → session-history.worker-MnPKncqP.mjs} +14 -23
- package/dist/.setup/session-initialization-6aveB9qb.mjs +22 -0
- package/dist/.setup/{session-permission-policy-DhurXY0l.mjs → session-permission-policy-Ci_fVX6y.mjs} +4 -8
- package/dist/.setup/{session-retirement-CU87AOg9.mjs → session-retirement-qMTxkNx2.mjs} +31 -12
- package/dist/.setup/{settled-turn-context-fmwouT5I.mjs → settled-turn-context-D4yj4rxf.mjs} +1 -1
- package/dist/.setup/{settled-turn-finalizer-CiY2bLFK.mjs → settled-turn-finalizer-BsvEabkw.mjs} +17 -13
- package/dist/.setup/{shared-client-BpTtuLQ5.mjs → shared-client-DqyTMYug.mjs} +826 -485
- package/dist/.setup/shared-client-lifecycle-DJIgqgMw.mjs +153 -0
- package/dist/.setup/{side-question-DSCZyoBD.mjs → side-question-C4kCmQy1.mjs} +206 -334
- package/dist/.setup/{source-Bjp4UQij.mjs → source-CS_7GFJZ.mjs} +82 -139
- package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
- package/dist/.setup/{thread-archive-guard-jmecf6OJ.mjs → thread-archive-guard-C9frICzX.mjs} +10 -3
- package/dist/.setup/thread-lifecycle-DPflntEJ.mjs +2362 -0
- package/dist/.setup/{incognito-session-uhrBF6wJ.mjs → thread-ownership-queue-Dp24d7-F.mjs} +1 -2
- package/dist/.setup/{thread-ownership-skomxnIt.mjs → thread-ownership-roNP0l1c.mjs} +47 -10
- package/dist/.setup/thread-policy-DTd-DtkX.mjs +109 -0
- package/dist/.setup/thread-requests-B_bRnMGN.mjs +2102 -0
- package/dist/.setup/{thread-resume-BkNpj9Pz.mjs → thread-resume-CdDaoc7T.mjs} +6 -6
- package/dist/.setup/timeout-CXOfNoaX.mjs +46 -0
- package/dist/.setup/transcript-history-projection-DRSifI-1.mjs +201 -0
- package/dist/.setup/{transcript-mirror-attestation-CzM-94NC.mjs → transcript-mirror-attestation-YSegaktH.mjs} +3 -4
- package/dist/.setup/{transcript-mirror-D4qp_leP.mjs → transcript-mirror-kxmCYsuF.mjs} +224 -206
- package/dist/.setup/{transport-stdio-Cmx94Oiy.mjs → transport-stdio-Bdegpn2V.mjs} +138 -75
- package/dist/.setup/transport-websocket-BTBE-LD9.mjs +211 -0
- package/dist/.setup/{upstream-prompt-provenance-LphB8slG.mjs → upstream-prompt-provenance-fXkKbPVC.mjs} +3 -9
- package/dist/.setup/{upstream-session-fork-D3UKxgVD.mjs → upstream-session-fork-CBeVjBpT.mjs} +48 -40
- package/dist/.setup/{usage-BOgBEKso.mjs → usage-BMzNxeR_.mjs} +5 -4
- package/dist/.setup/{usage-limit-error-C0ow7DOc.mjs → usage-limit-error-QXerDo3i.mjs} +5 -20
- package/dist/.setup/{web-search-provider.runtime-CTGuJt6i.mjs → web-search-provider.runtime-P77OUeoo.mjs} +10 -20
- package/dist/api.js +25 -16
- package/dist/catalog-page-worker-entrypoint.js +12 -0
- package/dist/catalog-page.worker.js +9 -0
- package/dist/cli-metadata.js +1 -1
- package/dist/doctor-contract-api.js +50 -8
- package/dist/harness.js +344 -1
- package/dist/index.js +345 -346
- package/dist/media-understanding-provider.js +9 -22
- package/dist/migration-provider-api.js +1 -1
- package/dist/provider-discovery.js +1 -1
- package/dist/session-history-worker-runtime.js +16 -33
- package/dist/session-history.worker.js +1 -1
- package/openclaw.plugin.json +14 -2
- package/package.json +11 -7
- package/dist/.setup/attempt-context-VXVwTCfS.mjs +0 -636
- package/dist/.setup/compact-C3i3qlzj.mjs +0 -687
- package/dist/.setup/event-projector-assistant-message-DNUR1s0A.mjs +0 -105
- package/dist/.setup/event-projector-items-DyTQ_XqL.mjs +0 -200
- package/dist/.setup/harness-rxaaoN-o.mjs +0 -349
- package/dist/.setup/history-rejection-B4nOXNXF.mjs +0 -13
- package/dist/.setup/image-payload-sanitizer-tupAr-2o.mjs +0 -79
- package/dist/.setup/inference-proxy-CC15HuSi.mjs +0 -311
- package/dist/.setup/launch-args-DnMak49W.mjs +0 -111
- package/dist/.setup/local-runtime-attribution-B_2mnqhb.mjs +0 -19
- package/dist/.setup/native-subagent-history-YXaL6dMd.mjs +0 -135
- package/dist/.setup/native-subagent-task-ids-DsuOonKv.mjs +0 -411
- package/dist/.setup/node-exec-server.runtime-BaViBKNz.mjs +0 -226
- package/dist/.setup/plugin-approval-roundtrip-CjfzafAZ.mjs +0 -122
- package/dist/.setup/provider-fFh_-VR3.mjs +0 -50
- package/dist/.setup/rolldown-runtime-8H4AJuhK.mjs +0 -14
- package/dist/.setup/sandbox-guard-BBP-RbTf.mjs +0 -166
- package/dist/.setup/session-binding-_0W_fHRr.mjs +0 -721
- package/dist/.setup/session-catalog-events-jMcYfQyd.mjs +0 -250
- package/dist/.setup/session-history-read-B3ZfSUbF.mjs +0 -50
- package/dist/.setup/session-initialization-DH1z6fXn.mjs +0 -75
- package/dist/.setup/thread-history-page-BTx-4xsg.mjs +0 -83
- package/dist/.setup/thread-lifecycle-Cik28qWn.mjs +0 -4904
- package/dist/.setup/thread-policy-Cjs4B8xf.mjs +0 -54
- package/dist/.setup/timeout-DZF05XrR.mjs +0 -63
- package/dist/.setup/transcript-history-projection-DHNBDhCU.mjs +0 -243
- package/dist/.setup/version-CoqzXPgK.mjs +0 -12
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as unwrapNodeInvokePayload, x as readBoundedOptionalString } from "./session-catalog-parsing-CPGaPYr-.mjs";
|
|
2
|
+
import { r as resolveCodexAppServerUserHomeDir } from "./auth-start-options-BPIPeEX8.mjs";
|
|
3
|
+
import { o as formatCodexDisplayText } from "./command-formatters-R3ckE3Co.mjs";
|
|
4
|
+
import { t as codexCatalogHomeId } from "./session-catalog-home-id-vlih0B_g.mjs";
|
|
2
5
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
+
import { runCommandBuffered, withCommandProcessScope } from "openclaw/plugin-sdk/process-runtime";
|
|
3
7
|
import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
4
|
-
import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
|
|
5
8
|
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
|
|
6
9
|
import path from "node:path";
|
|
7
|
-
import os from "node:os";
|
|
8
10
|
import fs from "node:fs/promises";
|
|
9
11
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
10
12
|
import process from "node:process";
|
|
13
|
+
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
11
14
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
12
15
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
16
|
//#region extensions/codex/src/jsonl-lines.ts
|
|
14
17
|
const JSONL_STREAM_THRESHOLD_BYTES = 4194304;
|
|
15
18
|
const JSONL_READ_CHUNK_BYTES = 1048576;
|
|
16
|
-
async function visitJsonlLines(file, visitor
|
|
19
|
+
async function visitJsonlLines(file, visitor) {
|
|
17
20
|
let size;
|
|
18
21
|
try {
|
|
19
22
|
size = (await fs.stat(file)).size;
|
|
@@ -40,7 +43,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
|
|
|
40
43
|
let lineCount = 0;
|
|
41
44
|
for (const line of content.split(/\r?\n/u)) {
|
|
42
45
|
lineCount += 1;
|
|
43
|
-
|
|
46
|
+
visitor(line);
|
|
44
47
|
}
|
|
45
48
|
return {
|
|
46
49
|
ok: true,
|
|
@@ -56,7 +59,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
|
|
|
56
59
|
lineCount: 0
|
|
57
60
|
};
|
|
58
61
|
}
|
|
59
|
-
const buffer = Buffer.allocUnsafe(
|
|
62
|
+
const buffer = Buffer.allocUnsafe(JSONL_READ_CHUNK_BYTES);
|
|
60
63
|
const decoder = new TextDecoder();
|
|
61
64
|
let pendingFragments = [];
|
|
62
65
|
let lineCount = 0;
|
|
@@ -77,10 +80,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
|
|
|
77
80
|
}
|
|
78
81
|
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
79
82
|
lineCount += 1;
|
|
80
|
-
|
|
81
|
-
ok: true,
|
|
82
|
-
lineCount
|
|
83
|
-
};
|
|
83
|
+
visitor(line);
|
|
84
84
|
lineStart = newline + 1;
|
|
85
85
|
}
|
|
86
86
|
if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
|
|
@@ -110,21 +110,25 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
|
|
|
110
110
|
//#region extensions/codex/src/node-cli-sessions.ts
|
|
111
111
|
const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
|
|
112
112
|
const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
|
|
113
|
+
const CODEX_CLI_SESSION_SOURCE_CAPABILITY = "codex-cli-session-source";
|
|
114
|
+
const CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE = "Update the node and approve its refreshed capabilities before continuing this Codex catalog session.";
|
|
113
115
|
const DEFAULT_SESSION_LIMIT = 10;
|
|
114
116
|
const MAX_SESSION_LIMIT = 50;
|
|
115
117
|
const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
|
|
116
118
|
const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
117
119
|
const activeResumeSessions = /* @__PURE__ */ new Set();
|
|
118
|
-
function createCodexCliSessionNodeHostCommands() {
|
|
120
|
+
function createCodexCliSessionNodeHostCommands(resolveCatalogSource) {
|
|
119
121
|
return [{
|
|
120
122
|
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
121
123
|
cap: "codex-cli-sessions",
|
|
124
|
+
hasActiveWork: () => false,
|
|
122
125
|
handle: listLocalCodexCliSessions
|
|
123
126
|
}, {
|
|
124
127
|
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
125
|
-
cap:
|
|
128
|
+
cap: CODEX_CLI_SESSION_SOURCE_CAPABILITY,
|
|
126
129
|
dangerous: true,
|
|
127
|
-
|
|
130
|
+
hasActiveWork: () => activeResumeSessions.size > 0,
|
|
131
|
+
handle: (paramsJSON, _io, context) => resumeLocalCodexCliSession(paramsJSON, resolveCatalogSource, context)
|
|
128
132
|
}];
|
|
129
133
|
}
|
|
130
134
|
function createCodexCliSessionNodeInvokePolicies() {
|
|
@@ -139,7 +143,11 @@ function createCodexCliSessionNodeInvokePolicies() {
|
|
|
139
143
|
}, {
|
|
140
144
|
commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
|
|
141
145
|
dangerous: true,
|
|
142
|
-
handle: (ctx) => ctx.
|
|
146
|
+
handle: (ctx) => isRecord(ctx.params) && (ctx.params.agentId !== void 0 || ctx.params.sourceHomeId !== void 0) && !ctx.node?.caps?.includes("codex-cli-session-source") ? {
|
|
147
|
+
ok: false,
|
|
148
|
+
code: "CODEX_NODE_SOURCE_UNAVAILABLE",
|
|
149
|
+
message: CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE
|
|
150
|
+
} : ctx.invokeNode()
|
|
143
151
|
}];
|
|
144
152
|
}
|
|
145
153
|
async function listCodexCliSessionsOnNode(params) {
|
|
@@ -176,18 +184,38 @@ async function resolveCodexCliSessionForBindingOnNode(params) {
|
|
|
176
184
|
};
|
|
177
185
|
}
|
|
178
186
|
async function resumeCodexCliSessionOnNode(params) {
|
|
179
|
-
|
|
187
|
+
let catalogAgentId;
|
|
188
|
+
let catalogHomeId;
|
|
189
|
+
if (params.sessionKey) {
|
|
190
|
+
const { adoptionSessionKeyRest, CODEX_NODE_SESSION_KEY_PREFIX, readNodeSessionMarker } = await import("./session-catalog-node-adoption-DgXvSGu0.mjs");
|
|
191
|
+
const entry = params.runtime.agent.session.getSessionEntry({
|
|
192
|
+
sessionKey: params.sessionKey,
|
|
193
|
+
readConsistency: "latest"
|
|
194
|
+
});
|
|
195
|
+
const codex = entry?.pluginExtensions?.codex;
|
|
196
|
+
if (adoptionSessionKeyRest(params.sessionKey).startsWith(CODEX_NODE_SESSION_KEY_PREFIX) || isRecord(codex) && codex.sessionCatalog !== void 0) {
|
|
197
|
+
const marker = entry ? readNodeSessionMarker(entry) : void 0;
|
|
198
|
+
catalogAgentId = params.agentId?.trim();
|
|
199
|
+
if (!catalogAgentId || parseAgentSessionKey(params.sessionKey)?.agentId !== catalogAgentId || !marker || marker.initializing === true || marker.nodeId !== params.nodeId || marker.sourceHostId !== `node:${params.nodeId}` || marker.sourceThreadId !== params.sessionId || entry?.initializationPending === true || entry?.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) throw new Error("Codex catalog session changed before its node turn could run.");
|
|
200
|
+
if (!marker.sourceHomeId) throw new Error("This Codex catalog session has no saved source home. Reopen it from the catalog to continue in a new chat.");
|
|
201
|
+
catalogHomeId = marker.sourceHomeId;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const raw = await params.runtime.nodes.invoke({
|
|
180
205
|
nodeId: params.nodeId,
|
|
181
206
|
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
182
207
|
params: {
|
|
183
208
|
sessionId: params.sessionId,
|
|
209
|
+
...catalogAgentId ? { agentId: catalogAgentId } : {},
|
|
210
|
+
...catalogHomeId ? { sourceHomeId: catalogHomeId } : {},
|
|
184
211
|
prompt: params.prompt,
|
|
185
212
|
cwd: params.cwd,
|
|
186
213
|
timeoutMs: params.timeoutMs
|
|
187
214
|
},
|
|
188
215
|
timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3,
|
|
189
216
|
scopes: ["operator.write"]
|
|
190
|
-
})
|
|
217
|
+
});
|
|
218
|
+
const payload = unwrapNodeInvokePayload(raw, "Codex CLI node command returned malformed payloadJSON.");
|
|
191
219
|
if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
|
|
192
220
|
return {
|
|
193
221
|
ok: true,
|
|
@@ -203,12 +231,11 @@ function formatCodexCliSessions(params) {
|
|
|
203
231
|
})].join("\n");
|
|
204
232
|
}
|
|
205
233
|
async function listLocalCodexCliSessions(paramsJSON) {
|
|
206
|
-
const params =
|
|
234
|
+
const params = parseJsonRecord(paramsJSON);
|
|
207
235
|
const limit = normalizeLimit(params.limit);
|
|
208
236
|
const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
|
|
209
|
-
const codexHome =
|
|
237
|
+
const codexHome = resolveCodexAppServerUserHomeDir();
|
|
210
238
|
const summaries = await readHistorySessions(codexHome);
|
|
211
|
-
await hydrateSessionFiles(codexHome, summaries);
|
|
212
239
|
await hydrateSessionsFromSessionFiles(codexHome, summaries);
|
|
213
240
|
const sessions = [...summaries.values()].filter((session) => {
|
|
214
241
|
if (!filter) return true;
|
|
@@ -217,26 +244,45 @@ async function listLocalCodexCliSessions(paramsJSON) {
|
|
|
217
244
|
session.cwd,
|
|
218
245
|
session.lastMessage
|
|
219
246
|
].some((value) => value?.toLowerCase().includes(filter));
|
|
220
|
-
}).toSorted((a, b) =>
|
|
247
|
+
}).toSorted((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")).slice(0, limit);
|
|
221
248
|
return JSON.stringify({
|
|
222
249
|
sessions,
|
|
223
250
|
codexHome
|
|
224
251
|
});
|
|
225
252
|
}
|
|
226
|
-
async function resumeLocalCodexCliSession(paramsJSON) {
|
|
227
|
-
|
|
253
|
+
async function resumeLocalCodexCliSession(paramsJSON, resolveCatalogSource, context) {
|
|
254
|
+
context?.signal?.throwIfAborted();
|
|
255
|
+
const params = parseJsonRecord(paramsJSON);
|
|
228
256
|
const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
|
|
229
257
|
const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
|
|
258
|
+
const expectedHomeId = readBoundedOptionalString(params, "sourceHomeId", 256);
|
|
230
259
|
if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
|
|
231
260
|
if (!prompt) throw new Error("Missing Codex CLI prompt.");
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
let codexHome = resolveCodexAppServerUserHomeDir();
|
|
262
|
+
let sourceHomeId;
|
|
263
|
+
let assertCurrent;
|
|
264
|
+
if (params.agentId !== void 0) {
|
|
265
|
+
if (typeof params.agentId !== "string" || !params.agentId.trim()) throw new Error("Codex catalog agent id must be a nonempty string.");
|
|
266
|
+
const source = await resolveCatalogSource(params.agentId.trim());
|
|
267
|
+
if (source.transport !== "stdio") throw new Error("Codex CLI continuation requires a local Codex catalog source.");
|
|
268
|
+
codexHome = source.codexHome;
|
|
269
|
+
sourceHomeId = source.sourceHomeId;
|
|
270
|
+
assertCurrent = () => source.assertCurrent();
|
|
271
|
+
} else sourceHomeId = codexCatalogHomeId(codexHome);
|
|
272
|
+
if (expectedHomeId && expectedHomeId !== sourceHomeId) throw new Error("Codex catalog source home changed. Reopen the session from the catalog.");
|
|
273
|
+
context?.signal?.throwIfAborted();
|
|
274
|
+
const resumeKey = `${sourceHomeId}\0${sessionId}`;
|
|
275
|
+
if (activeResumeSessions.has(resumeKey)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
|
|
276
|
+
activeResumeSessions.add(resumeKey);
|
|
234
277
|
try {
|
|
235
278
|
const text = await runCodexExecResume({
|
|
236
279
|
sessionId,
|
|
237
280
|
prompt,
|
|
238
281
|
cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
|
|
239
|
-
timeoutMs: normalizeTimeoutMs(params.timeoutMs)
|
|
282
|
+
timeoutMs: normalizeTimeoutMs(params.timeoutMs),
|
|
283
|
+
codexHome,
|
|
284
|
+
assertCurrent,
|
|
285
|
+
signal: context?.signal
|
|
240
286
|
});
|
|
241
287
|
return JSON.stringify({
|
|
242
288
|
ok: true,
|
|
@@ -244,7 +290,7 @@ async function resumeLocalCodexCliSession(paramsJSON) {
|
|
|
244
290
|
text: text.trim() || "Codex completed without a text reply."
|
|
245
291
|
});
|
|
246
292
|
} finally {
|
|
247
|
-
activeResumeSessions.delete(
|
|
293
|
+
activeResumeSessions.delete(resumeKey);
|
|
248
294
|
}
|
|
249
295
|
}
|
|
250
296
|
async function runCodexExecResume(params) {
|
|
@@ -266,22 +312,31 @@ async function runCodexExecResume(params) {
|
|
|
266
312
|
execPath: process.execPath,
|
|
267
313
|
packageName: "@openai/codex"
|
|
268
314
|
}), args);
|
|
269
|
-
const result = await
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
315
|
+
const result = await withCommandProcessScope(() => {
|
|
316
|
+
params.assertCurrent?.();
|
|
317
|
+
return runCommandBuffered([invocation.command, ...invocation.argv], {
|
|
318
|
+
cwd: params.cwd || process.cwd(),
|
|
319
|
+
input: params.prompt,
|
|
320
|
+
env: {
|
|
321
|
+
...process.env,
|
|
322
|
+
CODEX_HOME: params.codexHome
|
|
323
|
+
},
|
|
324
|
+
killGraceMs: 2e3,
|
|
325
|
+
signal: params.signal,
|
|
326
|
+
terminateOnOutputError: true,
|
|
327
|
+
timeoutMs: params.timeoutMs
|
|
328
|
+
});
|
|
329
|
+
}, params.signal);
|
|
330
|
+
params.signal?.throwIfAborted();
|
|
278
331
|
if (result.termination === "timeout") throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
|
|
279
332
|
if (result.termination === "error" && result.error) throw result.error;
|
|
280
333
|
if (result.code !== 0) {
|
|
281
334
|
const message = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim() || `codex exec resume exited with code ${String(result.code)}`;
|
|
282
335
|
throw new Error(message);
|
|
283
336
|
}
|
|
284
|
-
|
|
337
|
+
const text = await fs.readFile(outputPath, "utf8");
|
|
338
|
+
params.signal?.throwIfAborted();
|
|
339
|
+
return text;
|
|
285
340
|
} finally {
|
|
286
341
|
await fs.rm(path.dirname(outputPath), {
|
|
287
342
|
recursive: true,
|
|
@@ -292,15 +347,8 @@ async function runCodexExecResume(params) {
|
|
|
292
347
|
async function readHistorySessions(codexHome) {
|
|
293
348
|
const summaries = /* @__PURE__ */ new Map();
|
|
294
349
|
if (!(await visitJsonlLines(path.join(codexHome, "history.jsonl"), (line) => {
|
|
295
|
-
const
|
|
296
|
-
if (
|
|
297
|
-
let parsed;
|
|
298
|
-
try {
|
|
299
|
-
parsed = JSON.parse(trimmed);
|
|
300
|
-
} catch {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
if (!isRecord(parsed) || typeof parsed.session_id !== "string") return;
|
|
350
|
+
const parsed = parseJsonRecord(line.trim());
|
|
351
|
+
if (typeof parsed.session_id !== "string") return;
|
|
304
352
|
const sessionId = parsed.session_id.trim();
|
|
305
353
|
if (!sessionId) return;
|
|
306
354
|
const entry = summaries.get(sessionId) ?? {
|
|
@@ -314,23 +362,6 @@ async function readHistorySessions(codexHome) {
|
|
|
314
362
|
})).ok) return /* @__PURE__ */ new Map();
|
|
315
363
|
return summaries;
|
|
316
364
|
}
|
|
317
|
-
async function hydrateSessionFiles(codexHome, summaries) {
|
|
318
|
-
if (summaries.size === 0) return;
|
|
319
|
-
const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
|
|
320
|
-
const pending = new Set(summaries.keys());
|
|
321
|
-
for (const file of files) {
|
|
322
|
-
const basename = path.basename(file);
|
|
323
|
-
const sessionId = [...pending].find((id) => basename.includes(id));
|
|
324
|
-
if (!sessionId) continue;
|
|
325
|
-
const entry = summaries.get(sessionId);
|
|
326
|
-
if (!entry) continue;
|
|
327
|
-
entry.sessionFile = file;
|
|
328
|
-
const cwd = readSessionMetaCwd(await readFirstLine(file) ?? "");
|
|
329
|
-
if (cwd) entry.cwd = cwd;
|
|
330
|
-
pending.delete(sessionId);
|
|
331
|
-
if (pending.size === 0) return;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
365
|
async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
|
|
335
366
|
const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
|
|
336
367
|
for (const file of files) {
|
|
@@ -355,15 +386,7 @@ async function readSessionFileSummary(file) {
|
|
|
355
386
|
let lastMessage;
|
|
356
387
|
let messageCount = 0;
|
|
357
388
|
const result = await visitJsonlLines(file, (line) => {
|
|
358
|
-
const
|
|
359
|
-
if (!trimmed) return;
|
|
360
|
-
let parsed;
|
|
361
|
-
try {
|
|
362
|
-
parsed = JSON.parse(trimmed);
|
|
363
|
-
} catch {
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
if (!isRecord(parsed)) return;
|
|
389
|
+
const parsed = parseJsonRecord(line.trim());
|
|
367
390
|
if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
|
|
368
391
|
if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
|
|
369
392
|
if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
|
|
@@ -376,8 +399,7 @@ async function readSessionFileSummary(file) {
|
|
|
376
399
|
lastMessage = truncateText(messageText, 140);
|
|
377
400
|
}
|
|
378
401
|
});
|
|
379
|
-
if (!result.ok) return null;
|
|
380
|
-
if (result.lineCount === 0) return null;
|
|
402
|
+
if (!result.ok || result.lineCount === 0) return null;
|
|
381
403
|
if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
|
|
382
404
|
if (!sessionId) return null;
|
|
383
405
|
return {
|
|
@@ -405,19 +427,8 @@ async function findSessionFiles(dir, maxDepth) {
|
|
|
405
427
|
}
|
|
406
428
|
return files;
|
|
407
429
|
}
|
|
408
|
-
function readSessionMetaCwd(line) {
|
|
409
|
-
try {
|
|
410
|
-
const parsed = JSON.parse(line);
|
|
411
|
-
if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return;
|
|
412
|
-
return typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim() ? parsed.payload.cwd.trim() : void 0;
|
|
413
|
-
} catch {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
430
|
function readResponseItemMessageText(parsed) {
|
|
418
|
-
if (parsed.type !== "response_item" || !isRecord(parsed.payload)) return;
|
|
419
|
-
if (parsed.payload.type !== "message") return;
|
|
420
|
-
if ((typeof parsed.payload.role === "string" ? parsed.payload.role : "") !== "user") return;
|
|
431
|
+
if (parsed.type !== "response_item" || !isRecord(parsed.payload) || parsed.payload.type !== "message" || parsed.payload.role !== "user") return;
|
|
421
432
|
const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
|
|
422
433
|
if (!isRecord(entry)) return [];
|
|
423
434
|
const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
|
|
@@ -446,7 +457,7 @@ async function resolveCodexCliNode(params) {
|
|
|
446
457
|
return expectDefined(usable[0], "single usable Codex CLI node");
|
|
447
458
|
}
|
|
448
459
|
function parseCodexCliSessionsListResult(raw) {
|
|
449
|
-
const payload = unwrapNodeInvokePayload(raw);
|
|
460
|
+
const payload = unwrapNodeInvokePayload(raw, "Codex CLI node command returned malformed payloadJSON.");
|
|
450
461
|
if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
|
|
451
462
|
return {
|
|
452
463
|
codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
|
|
@@ -463,17 +474,7 @@ function parseCodexCliSessionsListResult(raw) {
|
|
|
463
474
|
})
|
|
464
475
|
};
|
|
465
476
|
}
|
|
466
|
-
function
|
|
467
|
-
const record = isRecord(raw) ? raw : {};
|
|
468
|
-
if (typeof record.payloadJSON === "string" && record.payloadJSON.trim()) try {
|
|
469
|
-
return JSON.parse(record.payloadJSON);
|
|
470
|
-
} catch (error) {
|
|
471
|
-
throw new Error("Codex CLI node command returned malformed payloadJSON.", { cause: error });
|
|
472
|
-
}
|
|
473
|
-
if ("payload" in record) return record.payload;
|
|
474
|
-
return raw;
|
|
475
|
-
}
|
|
476
|
-
function readRecordParam(paramsJSON) {
|
|
477
|
+
function parseJsonRecord(paramsJSON) {
|
|
477
478
|
if (!paramsJSON?.trim()) return {};
|
|
478
479
|
try {
|
|
479
480
|
const parsed = JSON.parse(paramsJSON);
|
|
@@ -482,16 +483,6 @@ function readRecordParam(paramsJSON) {
|
|
|
482
483
|
return {};
|
|
483
484
|
}
|
|
484
485
|
}
|
|
485
|
-
function resolveCodexHome() {
|
|
486
|
-
return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
|
|
487
|
-
}
|
|
488
|
-
async function readFirstLine(file) {
|
|
489
|
-
let firstLine;
|
|
490
|
-
return (await visitJsonlLines(file, (line) => {
|
|
491
|
-
firstLine = line;
|
|
492
|
-
return false;
|
|
493
|
-
}, 65536)).ok ? firstLine : void 0;
|
|
494
|
-
}
|
|
495
486
|
async function readFileMtimeIso(file) {
|
|
496
487
|
try {
|
|
497
488
|
return (await fs.stat(file)).mtime.toISOString();
|
|
@@ -509,9 +500,6 @@ function truncateText(value, max) {
|
|
|
509
500
|
if (value.length <= max) return value;
|
|
510
501
|
return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
|
|
511
502
|
}
|
|
512
|
-
function compareOptionalStringsDesc(a, b) {
|
|
513
|
-
return (b ?? "").localeCompare(a ?? "");
|
|
514
|
-
}
|
|
515
503
|
function readNodeId(node) {
|
|
516
504
|
if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
|
|
517
505
|
return node.nodeId;
|
|
@@ -524,4 +512,4 @@ function formatNodeLabel(node) {
|
|
|
524
512
|
].filter(Boolean).join(" / ") || "node";
|
|
525
513
|
}
|
|
526
514
|
//#endregion
|
|
527
|
-
export {
|
|
515
|
+
export { createCodexCliSessionNodeInvokePolicies as a, resolveCodexCliSessionForBindingOnNode as c, createCodexCliSessionNodeHostCommands as i, resumeCodexCliSessionOnNode as l, CODEX_CLI_SESSION_SOURCE_CAPABILITY as n, formatCodexCliSessions as o, CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE as r, listCodexCliSessionsOnNode as s, CODEX_CLI_SESSION_RESUME_COMMAND as t };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { i as resolveManagedCodexNativeCommand, r as resolveManagedCodexAppServerStartOptions, t as isManagedCodexDesktopCommand } from "./managed-binary-kiWRhNRE.mjs";
|
|
2
|
+
import { a as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-Bdegpn2V.mjs";
|
|
3
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5
|
+
import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { mkdir } from "node:fs/promises";
|
|
8
|
+
import { once } from "node:events";
|
|
9
|
+
import { resolvePreferredOpenClawTmpDir, tempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
10
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
11
|
+
import { StringDecoder } from "node:string_decoder";
|
|
12
|
+
import { sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
|
|
13
|
+
//#region extensions/codex/src/node-exec-server.runtime.ts
|
|
14
|
+
const MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES = 67108864;
|
|
15
|
+
const MAX_CODEX_EXEC_SERVER_STDERR_BYTES = 4096;
|
|
16
|
+
const CODEX_EXEC_SERVER_READY_LINE = "codex_exec_server::server::transport: codex-exec-server listening on stdio";
|
|
17
|
+
const CODEX_EXEC_SERVER_TERMINATION_GRACE_MS = 1e3;
|
|
18
|
+
const CODEX_EXEC_SERVER_REAP_TIMEOUT_MS = 5e3;
|
|
19
|
+
const NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT = /^(?:SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|TEMP|TMP|TMPDIR)$/iu;
|
|
20
|
+
function validateNodeExecServerMessage(message) {
|
|
21
|
+
if (message.byteLength === 0 || message.byteLength > MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES) throw new Error("Codex exec-server JSON-RPC message exceeds its 64 MiB limit.");
|
|
22
|
+
const encoded = Buffer.from(message.buffer, message.byteOffset, message.byteLength);
|
|
23
|
+
if (encoded.includes(10) || encoded.includes(13)) throw new Error("Codex exec-server JSON-RPC frames must contain exactly one message.");
|
|
24
|
+
let decoded;
|
|
25
|
+
try {
|
|
26
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(encoded);
|
|
27
|
+
decoded = JSON.parse(text);
|
|
28
|
+
} catch {
|
|
29
|
+
throw new Error("Codex exec-server received malformed UTF-8 or JSON-RPC.");
|
|
30
|
+
}
|
|
31
|
+
if (!isRecord(decoded) || decoded.jsonrpc !== void 0 && decoded.jsonrpc !== "2.0" || typeof decoded.method !== "string" && !("id" in decoded && ("result" in decoded || "error" in decoded))) throw new Error("Codex exec-server received an invalid JSON-RPC message.");
|
|
32
|
+
return encoded;
|
|
33
|
+
}
|
|
34
|
+
function nodeExecServerAbortError(signal) {
|
|
35
|
+
return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Codex node exec-server connection closed.");
|
|
36
|
+
}
|
|
37
|
+
function writeNodeExecServerMessage(child, message, signal) {
|
|
38
|
+
if (signal.aborted) throw nodeExecServerAbortError(signal);
|
|
39
|
+
const payload = Buffer.concat([message, Buffer.from("\n")]);
|
|
40
|
+
if (!child.stdin.write(payload)) return once(child.stdin, "drain", { signal }).then(() => void 0);
|
|
41
|
+
}
|
|
42
|
+
async function relayNodeExecServerOutput(child, send) {
|
|
43
|
+
let fragments = [];
|
|
44
|
+
let pendingBytes = 0;
|
|
45
|
+
for await (const rawChunk of child.stdout) {
|
|
46
|
+
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
|
|
47
|
+
let offset = 0;
|
|
48
|
+
while (offset < chunk.byteLength) {
|
|
49
|
+
const newline = chunk.indexOf(10, offset);
|
|
50
|
+
const fragment = chunk.subarray(offset, newline === -1 ? chunk.byteLength : newline);
|
|
51
|
+
const nextLength = pendingBytes + fragment.byteLength;
|
|
52
|
+
if (nextLength > 67108865) throw new Error("Codex exec-server stdout message exceeds its 64 MiB limit.");
|
|
53
|
+
if (fragment.byteLength > 0) fragments.push(fragment);
|
|
54
|
+
pendingBytes = nextLength;
|
|
55
|
+
if (newline === -1) {
|
|
56
|
+
if (pendingBytes > MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES && fragment[fragment.byteLength - 1] !== 13) throw new Error("Codex exec-server stdout message exceeds its 64 MiB limit.");
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const trailing = fragments.at(-1);
|
|
60
|
+
if (trailing?.[trailing.byteLength - 1] === 13) {
|
|
61
|
+
pendingBytes -= 1;
|
|
62
|
+
if (trailing.byteLength === 1) fragments.pop();
|
|
63
|
+
else fragments[fragments.length - 1] = trailing.subarray(0, trailing.byteLength - 1);
|
|
64
|
+
}
|
|
65
|
+
const message = validateNodeExecServerMessage(fragments.length === 1 ? fragments[0] : Buffer.concat(fragments, pendingBytes));
|
|
66
|
+
fragments = [];
|
|
67
|
+
pendingBytes = 0;
|
|
68
|
+
await send(message);
|
|
69
|
+
offset = newline + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (pendingBytes > 0) throw new Error("Codex exec-server stdout ended with an unterminated JSON-RPC message.");
|
|
73
|
+
}
|
|
74
|
+
function createNodeExecServerProcessOwner(child, closed, releaseResources, activeProcesses) {
|
|
75
|
+
let termination;
|
|
76
|
+
let hasClosed = false;
|
|
77
|
+
const settled = closed.then(async () => {
|
|
78
|
+
hasClosed = true;
|
|
79
|
+
await termination?.catch(() => {});
|
|
80
|
+
await releaseResources();
|
|
81
|
+
activeProcesses.delete(terminate);
|
|
82
|
+
});
|
|
83
|
+
settled.catch(() => {});
|
|
84
|
+
const terminate = async () => {
|
|
85
|
+
if (!hasClosed) await (termination ??= (async () => {
|
|
86
|
+
if (process.platform === "win32" && child.pid) killProcessTree(child.pid, { graceMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS });
|
|
87
|
+
const { exited } = await closeCodexAppServerTransportAndWait(child, {
|
|
88
|
+
forceKillDelayMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS,
|
|
89
|
+
exitTimeoutMs: CODEX_EXEC_SERVER_REAP_TIMEOUT_MS
|
|
90
|
+
});
|
|
91
|
+
if (!exited) throw new Error("Codex node exec-server process tree did not terminate.");
|
|
92
|
+
})());
|
|
93
|
+
await settled;
|
|
94
|
+
};
|
|
95
|
+
activeProcesses.add(terminate);
|
|
96
|
+
return terminate;
|
|
97
|
+
}
|
|
98
|
+
/** Runs the one-connection paired-node exec-server after lightweight command admission. */
|
|
99
|
+
async function runCodexNodeExecServer(params) {
|
|
100
|
+
const { io, workspace } = params;
|
|
101
|
+
const frames = io.frames;
|
|
102
|
+
const cwd = workspace.workspaceDir;
|
|
103
|
+
let writes;
|
|
104
|
+
let output;
|
|
105
|
+
let temporaryHome;
|
|
106
|
+
let unsubscribe;
|
|
107
|
+
const releaseResources = async () => {
|
|
108
|
+
try {
|
|
109
|
+
await Promise.allSettled([output, writes]);
|
|
110
|
+
await temporaryHome?.cleanup();
|
|
111
|
+
} finally {
|
|
112
|
+
workspace.release();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
let close = releaseResources;
|
|
116
|
+
const { promise: disconnected, reject: rejectDisconnected } = createDeferred();
|
|
117
|
+
disconnected.catch(() => {});
|
|
118
|
+
const onAbort = () => {
|
|
119
|
+
const error = nodeExecServerAbortError(io.signal);
|
|
120
|
+
rejectDisconnected(error);
|
|
121
|
+
};
|
|
122
|
+
io.signal.addEventListener("abort", onAbort, { once: true });
|
|
123
|
+
try {
|
|
124
|
+
if (!frames) throw new Error("Codex node exec-server requires duplex frames.");
|
|
125
|
+
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
126
|
+
temporaryHome = await tempWorkspace({
|
|
127
|
+
rootDir: resolvePreferredOpenClawTmpDir(),
|
|
128
|
+
prefix: "codex-node-exec-server-"
|
|
129
|
+
});
|
|
130
|
+
const { dir } = temporaryHome;
|
|
131
|
+
const codexHome = path.join(dir, ".codex");
|
|
132
|
+
await mkdir(codexHome, {
|
|
133
|
+
recursive: true,
|
|
134
|
+
mode: 448
|
|
135
|
+
});
|
|
136
|
+
const resolved = await resolveManagedCodexAppServerStartOptions({
|
|
137
|
+
transport: "stdio",
|
|
138
|
+
command: "codex",
|
|
139
|
+
commandSource: "managed",
|
|
140
|
+
managedCommandOrder: "package-first",
|
|
141
|
+
args: [
|
|
142
|
+
"exec-server",
|
|
143
|
+
"--listen",
|
|
144
|
+
"stdio"
|
|
145
|
+
],
|
|
146
|
+
headers: {}
|
|
147
|
+
});
|
|
148
|
+
const native = resolveManagedCodexNativeCommand(resolved.command);
|
|
149
|
+
if (!native || isManagedCodexDesktopCommand(resolved.command)) throw new Error("Codex node exec-server requires the pinned managed package binary.");
|
|
150
|
+
const baseEnv = sanitizeEnvVars(process.env, {
|
|
151
|
+
strictMode: true,
|
|
152
|
+
customAllowedPatterns: [NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT]
|
|
153
|
+
}).allowed;
|
|
154
|
+
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
155
|
+
params.assertExecAuthorized();
|
|
156
|
+
const nativeReady = createDeferred();
|
|
157
|
+
const exit = createDeferred();
|
|
158
|
+
let stderr = Buffer.alloc(0);
|
|
159
|
+
let startupSettled = false;
|
|
160
|
+
let pendingLine = "";
|
|
161
|
+
const decoder = new StringDecoder("utf8");
|
|
162
|
+
const child = await createStdioTransport({
|
|
163
|
+
transport: "stdio",
|
|
164
|
+
command: native,
|
|
165
|
+
commandSource: "resolved-managed",
|
|
166
|
+
args: resolved.args,
|
|
167
|
+
headers: {},
|
|
168
|
+
cwd,
|
|
169
|
+
env: {
|
|
170
|
+
HOME: workspace.homeDir ?? dir,
|
|
171
|
+
CODEX_HOME: codexHome,
|
|
172
|
+
RUST_LOG: "error,opentelemetry_sdk=off,opentelemetry_otlp=off,codex_exec_server::server::transport=info",
|
|
173
|
+
...process.platform === "win32" ? { USERPROFILE: workspace.homeDir ?? dir } : {}
|
|
174
|
+
},
|
|
175
|
+
clearEnv: ["NODE_OPTIONS"]
|
|
176
|
+
}, baseEnv, () => {
|
|
177
|
+
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
178
|
+
params.assertExecAuthorized();
|
|
179
|
+
}, (spawned) => {
|
|
180
|
+
spawned.once("close", (code, signal) => exit.resolve({
|
|
181
|
+
code,
|
|
182
|
+
signal
|
|
183
|
+
}));
|
|
184
|
+
close = createNodeExecServerProcessOwner(spawned, exit.promise, releaseResources, params.activeProcesses);
|
|
185
|
+
output = relayNodeExecServerOutput(spawned, frames.send.bind(frames));
|
|
186
|
+
output.catch((error) => {
|
|
187
|
+
rejectDisconnected(error instanceof Error ? error : new Error(String(error)));
|
|
188
|
+
});
|
|
189
|
+
spawned.once("error", rejectDisconnected);
|
|
190
|
+
spawned.stdin.on("error", rejectDisconnected);
|
|
191
|
+
spawned.stderr.on("data", (chunk) => {
|
|
192
|
+
const next = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
193
|
+
stderr = Buffer.concat([stderr, next.subarray(-4096)]).subarray(-4096);
|
|
194
|
+
if (startupSettled) return;
|
|
195
|
+
const lines = (pendingLine + decoder.write(next)).split("\n");
|
|
196
|
+
pendingLine = lines.pop();
|
|
197
|
+
for (const line of [...lines, pendingLine]) if (Buffer.byteLength(line, "utf8") > MAX_CODEX_EXEC_SERVER_STDERR_BYTES) {
|
|
198
|
+
startupSettled = true;
|
|
199
|
+
pendingLine = "";
|
|
200
|
+
rejectDisconnected(/* @__PURE__ */ new Error("Codex node startup diagnostic line exceeded 4 KiB."));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (lines.some((line) => stripVTControlCharacters(line).trimEnd().endsWith(CODEX_EXEC_SERVER_READY_LINE))) {
|
|
204
|
+
startupSettled = true;
|
|
205
|
+
pendingLine = "";
|
|
206
|
+
nativeReady.resolve();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
const stopped = exit.promise.then((outcome) => {
|
|
211
|
+
const diagnostic = stderr.toString("utf8").trim();
|
|
212
|
+
throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
|
|
213
|
+
});
|
|
214
|
+
await Promise.race([
|
|
215
|
+
nativeReady.promise,
|
|
216
|
+
stopped,
|
|
217
|
+
disconnected
|
|
218
|
+
]);
|
|
219
|
+
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
220
|
+
if (child.exitCode !== null || child.signalCode !== null) await stopped;
|
|
221
|
+
params.assertExecAuthorized();
|
|
222
|
+
unsubscribe = frames.onMessage((message) => {
|
|
223
|
+
const encoded = validateNodeExecServerMessage(message);
|
|
224
|
+
const operation = writes ? writes.then(() => writeNodeExecServerMessage(child, encoded, io.signal)) : writeNodeExecServerMessage(child, encoded, io.signal);
|
|
225
|
+
if (!operation) return;
|
|
226
|
+
const observed = operation.catch(() => {});
|
|
227
|
+
writes = observed;
|
|
228
|
+
observed.then(() => {
|
|
229
|
+
if (writes === observed) writes = void 0;
|
|
230
|
+
});
|
|
231
|
+
return operation;
|
|
232
|
+
});
|
|
233
|
+
return await Promise.race([stopped, disconnected]);
|
|
234
|
+
} finally {
|
|
235
|
+
io.signal.removeEventListener("abort", onAbort);
|
|
236
|
+
try {
|
|
237
|
+
unsubscribe?.();
|
|
238
|
+
} finally {
|
|
239
|
+
await close();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
export { runCodexNodeExecServer };
|