@openclaw/codex 2026.9.1 → 2026.9.3
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/dist/api.js +18 -23
- package/dist/apply-B6el6hdO.js +357 -0
- package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
- package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
- package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
- package/dist/auth-cache-key-Kq6oerr6.js +498 -0
- package/dist/auth-profile-CwzO4_RG.js +43 -0
- package/dist/auth-profile-selection-DFu9e167.js +45 -0
- package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
- package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
- package/dist/build-state-6RL6qohz.js +19 -0
- package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/client-BEKu4yDx.js +3983 -0
- package/dist/command-dispatch-CisT9Jqq.js +21 -0
- package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
- package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
- package/dist/command-rpc-BMqRQOFX.js +226 -0
- package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
- package/dist/config-JDfTI4VI.js +106 -0
- package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
- package/dist/config-runtime-BWf6JLWG.js +11 -0
- package/dist/config-utils-C57JiJrU.js +69 -0
- package/dist/conversation-binding-CxOlHiix.js +889 -0
- package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
- package/dist/doctor-contract-api.js +8 -9
- package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
- package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
- package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
- package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
- package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
- package/dist/event-projector-usage-CIyrYYid.js +102 -0
- package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
- package/dist/harness.js +1 -1
- package/dist/history-rejection-B4nOXNXF.js +13 -0
- package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
- package/dist/incognito-session-uhrBF6wJ.js +15 -0
- package/dist/index.js +413 -2893
- package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
- package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
- package/dist/managed-thread-store-D1t5jh3G.js +66 -0
- package/dist/media-understanding-provider.js +4 -1
- package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
- package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
- package/dist/native-execution-policy-umd0tgh4.js +128 -0
- package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
- package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
- package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
- package/dist/plan-BMJdMOEF.js +711 -0
- package/dist/plugin-activation-BQK-f5sV.js +133 -0
- package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
- package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
- package/dist/plugin-inventory-CoRCzWW4.js +325 -0
- package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
- package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
- package/dist/protocol-CAAcVIKF.js +275 -0
- package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
- package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
- package/dist/request-CGvGOpeR.js +198 -0
- package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
- package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
- package/dist/sandbox-guard-BcT66fWy.js +165 -0
- package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
- package/dist/session-binding-record-CaZjYwNS.js +256 -0
- package/dist/session-catalog-HaDcp7Sv.js +1533 -0
- package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
- package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
- package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
- package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
- package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
- package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
- package/dist/session-catalog-provenance-DkJHo735.js +124 -0
- package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
- package/dist/session-history-Cz-tOOYj.js +96 -0
- package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
- package/dist/session-history-read-cxPUxyVV.js +50 -0
- package/dist/session-history-worker-runtime.js +60 -0
- package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
- package/dist/session-history.worker.js +2 -0
- package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
- package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
- package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
- package/dist/session-upstream-marker-D15C9NHp.js +46 -0
- package/dist/settled-turn-context-Du6gdUDd.js +45 -0
- package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
- package/dist/shared-client-CVhez2Uo.js +2911 -0
- package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
- package/dist/source-Da-h6f7e.js +518 -0
- package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
- package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
- package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
- package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
- package/dist/thread-resume-T5cveyxA.js +52 -0
- package/dist/timeout-BVw8aaM1.js +33 -0
- package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
- package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
- package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
- package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
- package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
- package/dist/version-omm6IPI3.js +12 -0
- package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
- package/openclaw.plugin.json +22 -1
- package/package.json +6 -6
- package/dist/request-DYAI5FQE.js +0 -475
- package/dist/session-catalog-yyvTWFOw.js +0 -3120
- package/dist/session-history-C5J1AKzG.js +0 -87
- package/dist/shared-client-DxcfF9KX.js +0 -7494
- package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { a as formatCodexDisplayText } from "./command-formatters-DeTfrapr.js";
|
|
2
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
4
|
+
import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
|
|
5
|
+
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import fs from "node:fs/promises";
|
|
9
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
10
|
+
import process from "node:process";
|
|
11
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
12
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
|
+
//#region extensions/codex/src/jsonl-lines.ts
|
|
14
|
+
const JSONL_STREAM_THRESHOLD_BYTES = 4194304;
|
|
15
|
+
const JSONL_READ_CHUNK_BYTES = 1048576;
|
|
16
|
+
async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
|
|
17
|
+
let size;
|
|
18
|
+
try {
|
|
19
|
+
size = (await fs.stat(file)).size;
|
|
20
|
+
} catch {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
lineCount: 0
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (size <= JSONL_STREAM_THRESHOLD_BYTES) {
|
|
27
|
+
let content;
|
|
28
|
+
try {
|
|
29
|
+
content = await fs.readFile(file, "utf8");
|
|
30
|
+
} catch {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
lineCount: 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (content.length === 0) return {
|
|
37
|
+
ok: true,
|
|
38
|
+
lineCount: 0
|
|
39
|
+
};
|
|
40
|
+
let lineCount = 0;
|
|
41
|
+
for (const line of content.split(/\r?\n/u)) {
|
|
42
|
+
lineCount += 1;
|
|
43
|
+
if (visitor(line) === false) break;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
lineCount
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
let handle;
|
|
51
|
+
try {
|
|
52
|
+
handle = await fs.open(file, "r");
|
|
53
|
+
} catch {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
lineCount: 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const buffer = Buffer.allocUnsafe(chunkBytes);
|
|
60
|
+
const decoder = new TextDecoder();
|
|
61
|
+
let pendingFragments = [];
|
|
62
|
+
let lineCount = 0;
|
|
63
|
+
try {
|
|
64
|
+
while (true) {
|
|
65
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
66
|
+
if (bytesRead === 0) break;
|
|
67
|
+
const content = decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
|
|
68
|
+
let lineStart = 0;
|
|
69
|
+
while (true) {
|
|
70
|
+
const newline = content.indexOf("\n", lineStart);
|
|
71
|
+
if (newline === -1) break;
|
|
72
|
+
let rawLine = content.slice(lineStart, newline);
|
|
73
|
+
if (pendingFragments.length > 0) {
|
|
74
|
+
pendingFragments.push(rawLine);
|
|
75
|
+
rawLine = pendingFragments.join("");
|
|
76
|
+
pendingFragments = [];
|
|
77
|
+
}
|
|
78
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
79
|
+
lineCount += 1;
|
|
80
|
+
if (visitor(line) === false) return {
|
|
81
|
+
ok: true,
|
|
82
|
+
lineCount
|
|
83
|
+
};
|
|
84
|
+
lineStart = newline + 1;
|
|
85
|
+
}
|
|
86
|
+
if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
|
|
87
|
+
}
|
|
88
|
+
const decoderTail = decoder.decode();
|
|
89
|
+
if (decoderTail.length > 0) pendingFragments.push(decoderTail);
|
|
90
|
+
if (pendingFragments.length > 0) {
|
|
91
|
+
const rawLine = pendingFragments.join("");
|
|
92
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
93
|
+
lineCount += 1;
|
|
94
|
+
visitor(line);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
lineCount
|
|
99
|
+
};
|
|
100
|
+
} catch {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
lineCount: 0
|
|
104
|
+
};
|
|
105
|
+
} finally {
|
|
106
|
+
await handle.close().catch(() => void 0);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region extensions/codex/src/node-cli-sessions.ts
|
|
111
|
+
const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
|
|
112
|
+
const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
|
|
113
|
+
const DEFAULT_SESSION_LIMIT = 10;
|
|
114
|
+
const MAX_SESSION_LIMIT = 50;
|
|
115
|
+
const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
|
|
116
|
+
const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
117
|
+
const activeResumeSessions = /* @__PURE__ */ new Set();
|
|
118
|
+
function createCodexCliSessionNodeHostCommands() {
|
|
119
|
+
return [{
|
|
120
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
121
|
+
cap: "codex-cli-sessions",
|
|
122
|
+
handle: listLocalCodexCliSessions
|
|
123
|
+
}, {
|
|
124
|
+
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
125
|
+
cap: "codex-cli-sessions",
|
|
126
|
+
dangerous: true,
|
|
127
|
+
handle: resumeLocalCodexCliSession
|
|
128
|
+
}];
|
|
129
|
+
}
|
|
130
|
+
function createCodexCliSessionNodeInvokePolicies() {
|
|
131
|
+
return [{
|
|
132
|
+
commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
|
|
133
|
+
defaultPlatforms: [
|
|
134
|
+
"macos",
|
|
135
|
+
"linux",
|
|
136
|
+
"windows"
|
|
137
|
+
],
|
|
138
|
+
handle: (ctx) => ctx.invokeNode()
|
|
139
|
+
}, {
|
|
140
|
+
commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
|
|
141
|
+
dangerous: true,
|
|
142
|
+
handle: (ctx) => ctx.invokeNode()
|
|
143
|
+
}];
|
|
144
|
+
}
|
|
145
|
+
async function listCodexCliSessionsOnNode(params) {
|
|
146
|
+
const node = await resolveCodexCliNode({
|
|
147
|
+
runtime: params.runtime,
|
|
148
|
+
requestedNode: params.requestedNode,
|
|
149
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
node,
|
|
153
|
+
result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
|
|
154
|
+
nodeId: readNodeId(node),
|
|
155
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
156
|
+
params: {
|
|
157
|
+
limit: params.limit,
|
|
158
|
+
filter: params.filter
|
|
159
|
+
},
|
|
160
|
+
timeoutMs: 15e3,
|
|
161
|
+
scopes: ["operator.write"]
|
|
162
|
+
}))
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
async function resolveCodexCliSessionForBindingOnNode(params) {
|
|
166
|
+
const listing = await listCodexCliSessionsOnNode({
|
|
167
|
+
runtime: params.runtime,
|
|
168
|
+
requestedNode: params.requestedNode,
|
|
169
|
+
filter: params.sessionId,
|
|
170
|
+
limit: MAX_SESSION_LIMIT
|
|
171
|
+
});
|
|
172
|
+
if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
|
|
173
|
+
return {
|
|
174
|
+
node: listing.node,
|
|
175
|
+
session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function resumeCodexCliSessionOnNode(params) {
|
|
179
|
+
const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
|
|
180
|
+
nodeId: params.nodeId,
|
|
181
|
+
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
182
|
+
params: {
|
|
183
|
+
sessionId: params.sessionId,
|
|
184
|
+
prompt: params.prompt,
|
|
185
|
+
cwd: params.cwd,
|
|
186
|
+
timeoutMs: params.timeoutMs
|
|
187
|
+
},
|
|
188
|
+
timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3,
|
|
189
|
+
scopes: ["operator.write"]
|
|
190
|
+
}));
|
|
191
|
+
if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
|
|
195
|
+
text: payload.text
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function formatCodexCliSessions(params) {
|
|
199
|
+
if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
|
|
200
|
+
return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
|
|
201
|
+
const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
|
|
202
|
+
return `- ${formatCodexDisplayText(session.sessionId)}${session.lastMessage ? ` - ${formatCodexDisplayText(session.lastMessage)}` : ""}${details.length > 0 ? ` (${details.map(formatCodexDisplayText).join(", ")})` : ""}\n Bind: /codex resume ${formatCodexDisplayText(session.sessionId)} --host ${formatCodexDisplayText(readNodeId(params.node))} --bind here`;
|
|
203
|
+
})].join("\n");
|
|
204
|
+
}
|
|
205
|
+
async function listLocalCodexCliSessions(paramsJSON) {
|
|
206
|
+
const params = readRecordParam(paramsJSON);
|
|
207
|
+
const limit = normalizeLimit(params.limit);
|
|
208
|
+
const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
|
|
209
|
+
const codexHome = resolveCodexHome();
|
|
210
|
+
const summaries = await readHistorySessions(codexHome);
|
|
211
|
+
await hydrateSessionFiles(codexHome, summaries);
|
|
212
|
+
await hydrateSessionsFromSessionFiles(codexHome, summaries);
|
|
213
|
+
const sessions = [...summaries.values()].filter((session) => {
|
|
214
|
+
if (!filter) return true;
|
|
215
|
+
return [
|
|
216
|
+
session.sessionId,
|
|
217
|
+
session.cwd,
|
|
218
|
+
session.lastMessage
|
|
219
|
+
].some((value) => value?.toLowerCase().includes(filter));
|
|
220
|
+
}).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
|
|
221
|
+
return JSON.stringify({
|
|
222
|
+
sessions,
|
|
223
|
+
codexHome
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async function resumeLocalCodexCliSession(paramsJSON) {
|
|
227
|
+
const params = readRecordParam(paramsJSON);
|
|
228
|
+
const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
|
|
229
|
+
const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
|
|
230
|
+
if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
|
|
231
|
+
if (!prompt) throw new Error("Missing Codex CLI prompt.");
|
|
232
|
+
if (activeResumeSessions.has(sessionId)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
|
|
233
|
+
activeResumeSessions.add(sessionId);
|
|
234
|
+
try {
|
|
235
|
+
const text = await runCodexExecResume({
|
|
236
|
+
sessionId,
|
|
237
|
+
prompt,
|
|
238
|
+
cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
|
|
239
|
+
timeoutMs: normalizeTimeoutMs(params.timeoutMs)
|
|
240
|
+
});
|
|
241
|
+
return JSON.stringify({
|
|
242
|
+
ok: true,
|
|
243
|
+
sessionId,
|
|
244
|
+
text: text.trim() || "Codex completed without a text reply."
|
|
245
|
+
});
|
|
246
|
+
} finally {
|
|
247
|
+
activeResumeSessions.delete(sessionId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function runCodexExecResume(params) {
|
|
251
|
+
const outputPath = path.join(await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "openclaw-codex-cli-")), "last-message.txt");
|
|
252
|
+
try {
|
|
253
|
+
const args = [
|
|
254
|
+
"exec",
|
|
255
|
+
"resume",
|
|
256
|
+
"--skip-git-repo-check",
|
|
257
|
+
"--output-last-message",
|
|
258
|
+
outputPath,
|
|
259
|
+
params.sessionId,
|
|
260
|
+
"-"
|
|
261
|
+
];
|
|
262
|
+
const invocation = materializeWindowsSpawnProgram(resolveWindowsSpawnProgram({
|
|
263
|
+
command: "codex",
|
|
264
|
+
platform: process.platform,
|
|
265
|
+
env: process.env,
|
|
266
|
+
execPath: process.execPath,
|
|
267
|
+
packageName: "@openai/codex"
|
|
268
|
+
}), args);
|
|
269
|
+
const result = await runCommandBuffered([invocation.command, ...invocation.argv], {
|
|
270
|
+
cwd: params.cwd || process.cwd(),
|
|
271
|
+
input: params.prompt,
|
|
272
|
+
env: process.env,
|
|
273
|
+
killGraceMs: 2e3,
|
|
274
|
+
killProcessTree: false,
|
|
275
|
+
terminateOnOutputError: true,
|
|
276
|
+
timeoutMs: params.timeoutMs
|
|
277
|
+
});
|
|
278
|
+
if (result.termination === "timeout") throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
|
|
279
|
+
if (result.termination === "error" && result.error) throw result.error;
|
|
280
|
+
if (result.code !== 0) {
|
|
281
|
+
const message = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim() || `codex exec resume exited with code ${String(result.code)}`;
|
|
282
|
+
throw new Error(message);
|
|
283
|
+
}
|
|
284
|
+
return await fs.readFile(outputPath, "utf8");
|
|
285
|
+
} finally {
|
|
286
|
+
await fs.rm(path.dirname(outputPath), {
|
|
287
|
+
recursive: true,
|
|
288
|
+
force: true
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function readHistorySessions(codexHome) {
|
|
293
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
294
|
+
if (!(await visitJsonlLines(path.join(codexHome, "history.jsonl"), (line) => {
|
|
295
|
+
const trimmed = line.trim();
|
|
296
|
+
if (!trimmed) return;
|
|
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;
|
|
304
|
+
const sessionId = parsed.session_id.trim();
|
|
305
|
+
if (!sessionId) return;
|
|
306
|
+
const entry = summaries.get(sessionId) ?? {
|
|
307
|
+
sessionId,
|
|
308
|
+
messageCount: 0
|
|
309
|
+
};
|
|
310
|
+
entry.messageCount += 1;
|
|
311
|
+
if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
|
|
312
|
+
if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
|
|
313
|
+
summaries.set(sessionId, entry);
|
|
314
|
+
})).ok) return /* @__PURE__ */ new Map();
|
|
315
|
+
return summaries;
|
|
316
|
+
}
|
|
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
|
+
async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
|
|
335
|
+
const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
|
|
336
|
+
for (const file of files) {
|
|
337
|
+
const summary = await readSessionFileSummary(file);
|
|
338
|
+
if (!summary) continue;
|
|
339
|
+
const existing = summaries.get(summary.sessionId);
|
|
340
|
+
summaries.set(summary.sessionId, {
|
|
341
|
+
...summary,
|
|
342
|
+
...existing,
|
|
343
|
+
cwd: existing?.cwd ?? summary.cwd,
|
|
344
|
+
sessionFile: existing?.sessionFile ?? summary.sessionFile,
|
|
345
|
+
updatedAt: existing?.updatedAt ?? summary.updatedAt,
|
|
346
|
+
lastMessage: existing?.lastMessage ?? summary.lastMessage,
|
|
347
|
+
messageCount: existing?.messageCount ?? summary.messageCount
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function readSessionFileSummary(file) {
|
|
352
|
+
let sessionId = "";
|
|
353
|
+
let cwd;
|
|
354
|
+
let updatedAt;
|
|
355
|
+
let lastMessage;
|
|
356
|
+
let messageCount = 0;
|
|
357
|
+
const result = await visitJsonlLines(file, (line) => {
|
|
358
|
+
const trimmed = line.trim();
|
|
359
|
+
if (!trimmed) return;
|
|
360
|
+
let parsed;
|
|
361
|
+
try {
|
|
362
|
+
parsed = JSON.parse(trimmed);
|
|
363
|
+
} catch {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (!isRecord(parsed)) return;
|
|
367
|
+
if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
|
|
368
|
+
if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
|
|
369
|
+
if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
|
|
370
|
+
if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const messageText = readResponseItemMessageText(parsed);
|
|
374
|
+
if (messageText) {
|
|
375
|
+
messageCount += 1;
|
|
376
|
+
lastMessage = truncateText(messageText, 140);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
if (!result.ok) return null;
|
|
380
|
+
if (result.lineCount === 0) return null;
|
|
381
|
+
if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
|
|
382
|
+
if (!sessionId) return null;
|
|
383
|
+
return {
|
|
384
|
+
sessionId,
|
|
385
|
+
updatedAt: updatedAt ?? await readFileMtimeIso(file),
|
|
386
|
+
lastMessage,
|
|
387
|
+
cwd,
|
|
388
|
+
sessionFile: file,
|
|
389
|
+
messageCount
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async function findSessionFiles(dir, maxDepth) {
|
|
393
|
+
if (maxDepth < 0) return [];
|
|
394
|
+
let entries;
|
|
395
|
+
try {
|
|
396
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
397
|
+
} catch {
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
const files = [];
|
|
401
|
+
for (const entry of entries) {
|
|
402
|
+
const entryPath = path.join(dir, entry.name);
|
|
403
|
+
if (entry.isDirectory()) files.push(...await findSessionFiles(entryPath, maxDepth - 1));
|
|
404
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(entryPath);
|
|
405
|
+
}
|
|
406
|
+
return files;
|
|
407
|
+
}
|
|
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
|
+
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;
|
|
421
|
+
const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
|
|
422
|
+
if (!isRecord(entry)) return [];
|
|
423
|
+
const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
|
|
424
|
+
return text?.trim() ? [text.trim()] : [];
|
|
425
|
+
});
|
|
426
|
+
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
427
|
+
}
|
|
428
|
+
function readSessionIdFromFilename(file) {
|
|
429
|
+
return path.basename(file).match(/[0-9a-f]{8}-[0-9a-f-]{27,}/iu)?.[0];
|
|
430
|
+
}
|
|
431
|
+
async function resolveCodexCliNode(params) {
|
|
432
|
+
const list = await params.runtime.nodes.list(params.requestedNode ? void 0 : { connected: true });
|
|
433
|
+
const requested = params.requestedNode?.trim();
|
|
434
|
+
const candidates = list.nodes.filter((node) => {
|
|
435
|
+
if (requested) return [
|
|
436
|
+
node.nodeId,
|
|
437
|
+
node.displayName,
|
|
438
|
+
node.remoteIp
|
|
439
|
+
].some((value) => value === requested);
|
|
440
|
+
return node.connected === true && node.commands?.includes(params.command);
|
|
441
|
+
});
|
|
442
|
+
if (candidates.length === 0) throw new Error(requested ? `Codex CLI node ${requested} was not found.` : "No connected node exposes Codex CLI session commands.");
|
|
443
|
+
const usable = candidates.filter((node) => node.commands?.includes(params.command));
|
|
444
|
+
if (usable.length === 0) throw new Error(`Node ${requested ?? "candidate"} does not expose ${params.command}.`);
|
|
445
|
+
if (usable.length > 1) throw new Error("Multiple Codex CLI-capable nodes connected. Pass --host <node-id>.");
|
|
446
|
+
return expectDefined(usable[0], "single usable Codex CLI node");
|
|
447
|
+
}
|
|
448
|
+
function parseCodexCliSessionsListResult(raw) {
|
|
449
|
+
const payload = unwrapNodeInvokePayload(raw);
|
|
450
|
+
if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
|
|
451
|
+
return {
|
|
452
|
+
codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
|
|
453
|
+
sessions: payload.sessions.flatMap((entry) => {
|
|
454
|
+
if (!isRecord(entry) || typeof entry.sessionId !== "string") return [];
|
|
455
|
+
return [{
|
|
456
|
+
sessionId: entry.sessionId,
|
|
457
|
+
updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : void 0,
|
|
458
|
+
lastMessage: typeof entry.lastMessage === "string" ? entry.lastMessage : void 0,
|
|
459
|
+
cwd: typeof entry.cwd === "string" ? entry.cwd : void 0,
|
|
460
|
+
sessionFile: typeof entry.sessionFile === "string" ? entry.sessionFile : void 0,
|
|
461
|
+
messageCount: typeof entry.messageCount === "number" && Number.isFinite(entry.messageCount) ? entry.messageCount : 0
|
|
462
|
+
}];
|
|
463
|
+
})
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function unwrapNodeInvokePayload(raw) {
|
|
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
|
+
if (!paramsJSON?.trim()) return {};
|
|
478
|
+
try {
|
|
479
|
+
const parsed = JSON.parse(paramsJSON);
|
|
480
|
+
return isRecord(parsed) ? parsed : {};
|
|
481
|
+
} catch {
|
|
482
|
+
return {};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
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
|
+
async function readFileMtimeIso(file) {
|
|
496
|
+
try {
|
|
497
|
+
return (await fs.stat(file)).mtime.toISOString();
|
|
498
|
+
} catch {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function normalizeLimit(value) {
|
|
503
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
|
|
504
|
+
}
|
|
505
|
+
function normalizeTimeoutMs(value) {
|
|
506
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(36e5, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
|
|
507
|
+
}
|
|
508
|
+
function truncateText(value, max) {
|
|
509
|
+
if (value.length <= max) return value;
|
|
510
|
+
return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
|
|
511
|
+
}
|
|
512
|
+
function compareOptionalStringsDesc(a, b) {
|
|
513
|
+
return (b ?? "").localeCompare(a ?? "");
|
|
514
|
+
}
|
|
515
|
+
function readNodeId(node) {
|
|
516
|
+
if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
|
|
517
|
+
return node.nodeId;
|
|
518
|
+
}
|
|
519
|
+
function formatNodeLabel(node) {
|
|
520
|
+
return [
|
|
521
|
+
node.displayName,
|
|
522
|
+
node.nodeId,
|
|
523
|
+
node.remoteIp
|
|
524
|
+
].filter(Boolean).join(" / ") || "node";
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
527
|
+
export { listCodexCliSessionsOnNode as a, formatCodexCliSessions as i, createCodexCliSessionNodeHostCommands as n, resolveCodexCliSessionForBindingOnNode as o, createCodexCliSessionNodeInvokePolicies as r, resumeCodexCliSessionOnNode as s, CODEX_CLI_SESSION_RESUME_COMMAND as t };
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { n as resolveManagedCodexAppServerStartOptions, r as resolveManagedCodexNativeCommand, t as isManagedCodexDesktopCommand } from "./managed-binary-
|
|
2
|
-
import { i as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-
|
|
1
|
+
import { n as resolveManagedCodexAppServerStartOptions, r as resolveManagedCodexNativeCommand, t as isManagedCodexDesktopCommand } from "./managed-binary-DrjjRRUD.js";
|
|
2
|
+
import { i as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-BGjHoYLr.js";
|
|
3
3
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
-
import { mkdir } from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
7
4
|
import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { mkdir } from "node:fs/promises";
|
|
8
7
|
import { once } from "node:events";
|
|
8
|
+
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
9
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
10
|
+
import { stripVTControlCharacters } from "node:util";
|
|
11
|
+
import { StringDecoder } from "node:string_decoder";
|
|
9
12
|
import { sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
|
|
10
13
|
//#region extensions/codex/src/node-exec-server.runtime.ts
|
|
11
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";
|
|
12
17
|
const CODEX_EXEC_SERVER_TERMINATION_GRACE_MS = 1e3;
|
|
13
18
|
const CODEX_EXEC_SERVER_REAP_TIMEOUT_MS = 5e3;
|
|
14
19
|
const NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT = /^(?:SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|TEMP|TMP|TMPDIR)$/iu;
|
|
@@ -70,10 +75,11 @@ function createNodeExecServerProcessOwner(child, closed) {
|
|
|
70
75
|
let termination;
|
|
71
76
|
return { terminate: () => termination ??= (async () => {
|
|
72
77
|
if (process.platform === "win32" && child.pid) killProcessTree(child.pid, { graceMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS });
|
|
73
|
-
|
|
78
|
+
const { exited } = await closeCodexAppServerTransportAndWait(child, {
|
|
74
79
|
forceKillDelayMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS,
|
|
75
80
|
exitTimeoutMs: CODEX_EXEC_SERVER_REAP_TIMEOUT_MS
|
|
76
|
-
})
|
|
81
|
+
});
|
|
82
|
+
if (!exited) throw new Error("Codex node exec-server process tree did not terminate.");
|
|
77
83
|
await closed;
|
|
78
84
|
})() };
|
|
79
85
|
}
|
|
@@ -125,6 +131,12 @@ async function runCodexNodeExecServer(params) {
|
|
|
125
131
|
}).allowed;
|
|
126
132
|
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
127
133
|
params.assertExecAuthorized();
|
|
134
|
+
const nativeReady = createDeferred();
|
|
135
|
+
const exit = createDeferred();
|
|
136
|
+
let stderr = Buffer.alloc(0);
|
|
137
|
+
let startupSettled = false;
|
|
138
|
+
let pendingLine = "";
|
|
139
|
+
const decoder = new StringDecoder("utf8");
|
|
128
140
|
const child = await createStdioTransport({
|
|
129
141
|
transport: "stdio",
|
|
130
142
|
command: native,
|
|
@@ -135,29 +147,43 @@ async function runCodexNodeExecServer(params) {
|
|
|
135
147
|
env: {
|
|
136
148
|
HOME: dir,
|
|
137
149
|
CODEX_HOME: codexHome,
|
|
150
|
+
RUST_LOG: "error,opentelemetry_sdk=off,opentelemetry_otlp=off,codex_exec_server::server::transport=info",
|
|
138
151
|
...process.platform === "win32" ? { USERPROFILE: dir } : {}
|
|
139
152
|
},
|
|
140
153
|
clearEnv: ["NODE_OPTIONS"]
|
|
141
154
|
}, baseEnv, () => {
|
|
142
155
|
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
143
156
|
params.assertExecAuthorized();
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
rejectDisconnected(error);
|
|
147
|
-
});
|
|
148
|
-
let stderr = Buffer.alloc(0);
|
|
149
|
-
child.stderr.on("data", (chunk) => {
|
|
150
|
-
const bounded = (Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)).subarray(-4096);
|
|
151
|
-
stderr = Buffer.concat([stderr, bounded]).subarray(-4096);
|
|
152
|
-
});
|
|
153
|
-
const closed = new Promise((resolve) => {
|
|
154
|
-
child.once("close", (code, signal) => resolve({
|
|
157
|
+
}, (spawned) => {
|
|
158
|
+
spawned.once("close", (code, signal) => exit.resolve({
|
|
155
159
|
code,
|
|
156
160
|
signal
|
|
157
161
|
}));
|
|
162
|
+
spawned.once("error", rejectDisconnected);
|
|
163
|
+
spawned.stdin.on("error", rejectDisconnected);
|
|
164
|
+
spawned.stderr.on("data", (chunk) => {
|
|
165
|
+
const next = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
166
|
+
stderr = Buffer.concat([stderr, next.subarray(-4096)]).subarray(-4096);
|
|
167
|
+
if (startupSettled) return;
|
|
168
|
+
const lines = (pendingLine + decoder.write(next)).split("\n");
|
|
169
|
+
pendingLine = lines.pop();
|
|
170
|
+
for (const line of [...lines, pendingLine]) if (Buffer.byteLength(line, "utf8") > MAX_CODEX_EXEC_SERVER_STDERR_BYTES) {
|
|
171
|
+
startupSettled = true;
|
|
172
|
+
pendingLine = "";
|
|
173
|
+
rejectDisconnected(/* @__PURE__ */ new Error("Codex node startup diagnostic line exceeded 4 KiB."));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (lines.some((line) => stripVTControlCharacters(line).trimEnd().endsWith(CODEX_EXEC_SERVER_READY_LINE))) {
|
|
177
|
+
startupSettled = true;
|
|
178
|
+
pendingLine = "";
|
|
179
|
+
nativeReady.resolve();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
158
182
|
});
|
|
159
|
-
|
|
160
|
-
|
|
183
|
+
const closed = exit.promise;
|
|
184
|
+
const stopped = closed.then((outcome) => {
|
|
185
|
+
const diagnostic = stderr.toString("utf8").trim();
|
|
186
|
+
throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
|
|
161
187
|
});
|
|
162
188
|
const owner = createNodeExecServerProcessOwner(child, closed);
|
|
163
189
|
params.activeProcesses.add(owner.terminate);
|
|
@@ -166,7 +192,14 @@ async function runCodexNodeExecServer(params) {
|
|
|
166
192
|
rejectDisconnected(error instanceof Error ? error : new Error(String(error)));
|
|
167
193
|
});
|
|
168
194
|
try {
|
|
195
|
+
await Promise.race([
|
|
196
|
+
nativeReady.promise,
|
|
197
|
+
stopped,
|
|
198
|
+
disconnected
|
|
199
|
+
]);
|
|
169
200
|
if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
|
|
201
|
+
if (child.exitCode !== null || child.signalCode !== null) await stopped;
|
|
202
|
+
params.assertExecAuthorized();
|
|
170
203
|
params.onFrameReceiver((message) => {
|
|
171
204
|
const encoded = validateNodeExecServerMessage(message);
|
|
172
205
|
const operation = writes ? writes.then(() => writeNodeExecServerMessage(child, encoded, io.signal)) : writeNodeExecServerMessage(child, encoded, io.signal);
|
|
@@ -178,9 +211,7 @@ async function runCodexNodeExecServer(params) {
|
|
|
178
211
|
});
|
|
179
212
|
return operation;
|
|
180
213
|
});
|
|
181
|
-
|
|
182
|
-
const diagnostic = stderr.toString("utf8").trim();
|
|
183
|
-
throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
|
|
214
|
+
return await Promise.race([stopped, disconnected]);
|
|
184
215
|
} finally {
|
|
185
216
|
await owner.terminate();
|
|
186
217
|
params.activeProcesses.delete(owner.terminate);
|