@openclaw/codex 2026.5.12 → 2026.5.14-beta.1

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.
@@ -201,6 +201,7 @@ const CODEX_APP_SERVER_PARSE_BUFFER_MAX = 1e6;
201
201
  const CODEX_APP_SERVER_PARSE_BUFFER_MAX_LINES = 1e3;
202
202
  const CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS = 6e5;
203
203
  const CODEX_APP_SERVER_STDERR_TAIL_MAX = 2e3;
204
+ const UNPAIRED_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
204
205
  var CodexAppServerRpcError = class extends Error {
205
206
  constructor(error, method) {
206
207
  super(formatCodexAppServerRpcErrorMessage(error, method));
@@ -370,7 +371,7 @@ var CodexAppServerClient = class CodexAppServerClient {
370
371
  if (this.closed) return;
371
372
  const id = "id" in message ? message.id : void 0;
372
373
  const method = "method" in message ? message.method : void 0;
373
- this.child.stdin.write(`${JSON.stringify(message)}\n`, (error) => {
374
+ this.child.stdin.write(`${stringifyCodexAppServerMessage(message)}\n`, (error) => {
374
375
  if (error) embeddedAgentLog.warn("codex app-server write failed", {
375
376
  error,
376
377
  id,
@@ -551,6 +552,9 @@ function defaultServerRequestResponse(request) {
551
552
  if (request.method === "mcpServer/elicitation/request") return { action: "decline" };
552
553
  return {};
553
554
  }
555
+ function stringifyCodexAppServerMessage(message) {
556
+ return JSON.stringify(message, (_key, value) => typeof value === "string" ? value.replace(UNPAIRED_SURROGATE_RE, "") : value) ?? "null";
557
+ }
554
558
  function timeoutServerRequestResponse(request) {
555
559
  if (request.method !== "item/tool/call") return;
556
560
  return {
@@ -1,5 +1,5 @@
1
1
  //#region extensions/codex/src/app-server/client-factory.ts
2
- const defaultCodexAppServerClientFactory = (startOptions, authProfileId, agentDir, config) => import("./shared-client-Cr6W-a2G.js").then((n) => n.i).then(({ getSharedCodexAppServerClient }) => getSharedCodexAppServerClient({
2
+ const defaultCodexAppServerClientFactory = (startOptions, authProfileId, agentDir, config) => import("./shared-client-BwUqd3lh.js").then((n) => n.a).then(({ getSharedCodexAppServerClient }) => getSharedCodexAppServerClient({
3
3
  startOptions,
4
4
  authProfileId,
5
5
  agentDir,
@@ -2,6 +2,7 @@ import { t as isJsonObject } from "./protocol-C9UWI98H.js";
2
2
  //#region extensions/codex/src/app-server/rate-limits.ts
3
3
  const CODEX_LIMIT_ID = "codex";
4
4
  const LIMIT_WINDOW_KEYS = ["primary", "secondary"];
5
+ const ONE_SECOND_MS = 1e3;
5
6
  const ONE_MINUTE_MS = 6e4;
6
7
  const ONE_HOUR_MS = 60 * ONE_MINUTE_MS;
7
8
  const ONE_DAY_MS = 24 * ONE_HOUR_MS;
@@ -85,7 +86,7 @@ function summarizeRateLimitSnapshot(snapshot, nowMs) {
85
86
  });
86
87
  const reachedType = readString$1(snapshot, "rateLimitReachedType") ?? readString$1(snapshot, "rate_limit_reached_type");
87
88
  const suffix = reachedType ? ` (${formatReachedType(reachedType)})` : "";
88
- return `${label}: ${windows.join(", ") || "available"}${suffix}`;
89
+ return `${label}: ${windows.join(" · ") || "available"}${suffix}`;
89
90
  }
90
91
  function collectCodexRateLimitSnapshots(value) {
91
92
  const snapshots = [];
@@ -151,7 +152,7 @@ function formatRateLimitWindow(key, window, nowMs) {
151
152
  return `${key} ${formatRateLimitWindowDetails(window, nowMs)}`;
152
153
  }
153
154
  function formatRateLimitWindowDetails(window, nowMs) {
154
- return `${window.usedPercent === void 0 ? "usage unknown" : `${Math.round(window.usedPercent)}%`}${window.resetsAtMs > nowMs ? `, resets ${formatResetTime(window.resetsAtMs, nowMs)}` : ""}`;
155
+ return `${window.usedPercent === void 0 ? "usage unknown" : `${Math.max(0, 100 - Math.round(window.usedPercent))}% left`}${window.resetsAtMs > nowMs ? ` ⏱${formatResetDuration(window.resetsAtMs, nowMs)}` : ""}`;
155
156
  }
156
157
  function formatLimitLabel(snapshot) {
157
158
  const label = readNullableString(snapshot, "limitName") ?? readNullableString(snapshot, "limit_name") ?? readNullableString(snapshot, "limitId") ?? readNullableString(snapshot, "limit_id");
@@ -249,6 +250,17 @@ function formatRelativeDuration(durationMs) {
249
250
  const days = Math.ceil(safeMs / ONE_DAY_MS);
250
251
  return `${days} ${days === 1 ? "day" : "days"}`;
251
252
  }
253
+ function formatResetDuration(resetsAtMs, nowMs) {
254
+ const durationMs = Math.round(Math.max(ONE_SECOND_MS, resetsAtMs - nowMs) / ONE_SECOND_MS) * ONE_SECOND_MS;
255
+ const days = Math.floor(durationMs / ONE_DAY_MS);
256
+ const hours = Math.floor(durationMs % ONE_DAY_MS / ONE_HOUR_MS);
257
+ const minutes = Math.floor(durationMs % ONE_HOUR_MS / ONE_MINUTE_MS);
258
+ const seconds = Math.floor(durationMs % ONE_MINUTE_MS / ONE_SECOND_MS);
259
+ if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
260
+ if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
261
+ if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
262
+ return `${seconds}s`;
263
+ }
252
264
  function formatWindowSignature(value) {
253
265
  if (!isJsonObject(value)) return "";
254
266
  return `${readNumber(value, "usedPercent") ?? readNumber(value, "used_percent") ?? ""}:${readNumber(value, "resetsAt") ?? readNumber(value, "resets_at") ?? ""}`;
@@ -410,7 +422,9 @@ function buildHelp() {
410
422
  "- /codex status",
411
423
  "- /codex models",
412
424
  "- /codex threads [filter]",
425
+ "- /codex sessions --host <node> [filter]",
413
426
  "- /codex resume <thread-id>",
427
+ "- /codex resume <session-id> --host <node> --bind here",
414
428
  "- /codex bind [thread-id] [--cwd <path>] [--model <model>] [--provider <provider>]",
415
429
  "- /codex binding",
416
430
  "- /codex stop",
@@ -1,11 +1,11 @@
1
1
  import { i as isCodexFastServiceTier, s as resolveCodexAppServerRuntimeOptions } from "./config-0rd3LnKg.js";
2
- import { n as listCodexAppServerModels, t as listAllCodexAppServerModels } from "./models-BmLfrDci.js";
2
+ import { n as listCodexAppServerModels, t as listAllCodexAppServerModels } from "./models-GCYf5s8J.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
- import { n as CODEX_CONTROL_METHODS, r as describeControlFailure, t as requestCodexAppServerJson } from "./request-ohCy5ASa.js";
5
- import { a as formatComputerUseStatus, c as formatThreads, i as formatCodexStatus, l as readString$1, n as formatAccount, o as formatList, p as summarizeCodexAccountUsage, r as formatCodexDisplayText, s as formatModels, t as buildHelp } from "./command-formatters-Ttwc_kgX.js";
4
+ import { n as CODEX_CONTROL_METHODS, r as describeControlFailure, t as requestCodexAppServerJson } from "./request-BxAP1uyw.js";
5
+ import { a as formatComputerUseStatus, c as formatThreads, i as formatCodexStatus, l as readString$1, n as formatAccount, o as formatList, p as summarizeCodexAccountUsage, r as formatCodexDisplayText, s as formatModels, t as buildHelp } from "./command-formatters-CJH9-hFT.js";
6
6
  import { i as readCodexAppServerBinding, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-CMTXuyoz.js";
7
- import { a as parseCodexFastModeArg, c as setCodexConversationFastMode, d as steerCodexConversationTurn, f as stopCodexConversationTurn, i as formatPermissionsMode, l as setCodexConversationModel, m as resolveCodexDefaultWorkspaceDir, o as parseCodexPermissionsModeArg, p as readCodexConversationBindingData, r as startCodexConversationThread, s as readCodexConversationActiveTurn, u as setCodexConversationPermissions } from "./conversation-binding-CKofEetY.js";
8
- import { n as installCodexComputerUse, r as readCodexComputerUseStatus } from "./computer-use-Ct3ngNBt.js";
7
+ import { _ as steerCodexConversationTurn, b as readCodexConversationBindingData, d as parseCodexFastModeArg, f as parseCodexPermissionsModeArg, g as setCodexConversationPermissions, h as setCodexConversationModel, l as startCodexConversationThread, m as setCodexConversationFastMode, p as readCodexConversationActiveTurn, r as formatCodexCliSessions, u as formatPermissionsMode, v as stopCodexConversationTurn, x as resolveCodexDefaultWorkspaceDir, y as createCodexCliNodeConversationBindingData } from "./node-cli-sessions-CV5Kmdwt.js";
8
+ import { n as installCodexComputerUse, r as readCodexComputerUseStatus } from "./computer-use-DFdLwPQa.js";
9
9
  import { n as rememberCodexRateLimits } from "./rate-limit-cache-BFi-50LG.js";
10
10
  import crypto from "node:crypto";
11
11
  import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveDefaultAgentDir, resolveProfileUnusableUntilForDisplay } from "openclaw/plugin-sdk/agent-runtime";
@@ -366,7 +366,13 @@ const defaultCodexCommandDeps = {
366
366
  setCodexConversationModel,
367
367
  setCodexConversationPermissions,
368
368
  steerCodexConversationTurn,
369
- stopCodexConversationTurn
369
+ stopCodexConversationTurn,
370
+ listCodexCliSessionsOnNode: async () => {
371
+ throw new Error("Codex CLI node sessions require Gateway node runtime.");
372
+ },
373
+ resolveCodexCliSessionForBindingOnNode: async () => {
374
+ throw new Error("Codex CLI node sessions require Gateway node runtime.");
375
+ }
370
376
  };
371
377
  const CODEX_DIAGNOSTICS_SOURCE = "openclaw-diagnostics";
372
378
  const CODEX_DIAGNOSTICS_REASON_MAX_CHARS = 2048;
@@ -400,6 +406,7 @@ async function handleCodexSubcommand(ctx, options) {
400
406
  return { text: formatModels(await deps.listCodexAppServerModels(deps.requestOptions(options.pluginConfig, 100, ctx.config))) };
401
407
  }
402
408
  if (normalized === "threads") return { text: await buildThreads(deps, options.pluginConfig, rest.join(" ")) };
409
+ if (normalized === "sessions") return { text: await buildCodexCliSessions(deps, rest) };
403
410
  if (normalized === "resume") return { text: await resumeThread(deps, ctx, options.pluginConfig, rest) };
404
411
  if (normalized === "bind") return await bindConversation(deps, ctx, options.pluginConfig, rest);
405
412
  if (normalized === "detach" || normalized === "unbind") {
@@ -493,7 +500,7 @@ async function bindConversation(deps, ctx, pluginConfig, args) {
493
500
  async function detachConversation(deps, ctx) {
494
501
  const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
495
502
  const detached = await ctx.detachConversationBinding();
496
- if (data) await deps.clearCodexAppServerBinding(data.sessionFile);
503
+ if (data?.kind === "codex-app-server-session") await deps.clearCodexAppServerBinding(data.sessionFile);
497
504
  else if (ctx.sessionFile) await deps.clearCodexAppServerBinding(ctx.sessionFile);
498
505
  return detached.removed ? "Detached this conversation from Codex." : "No Codex conversation binding was attached.";
499
506
  }
@@ -501,6 +508,14 @@ async function describeConversationBinding(deps, ctx) {
501
508
  const current = await ctx.getCurrentConversationBinding();
502
509
  const data = readCodexConversationBindingData(current);
503
510
  if (!current || !data) return "No Codex conversation binding is attached.";
511
+ if (data.kind === "codex-cli-node-session") return [
512
+ "Codex conversation binding:",
513
+ "- Mode: Codex CLI node session",
514
+ `- Node: ${formatCodexDisplayText(data.nodeId)}`,
515
+ `- Session: ${formatCodexDisplayText(data.sessionId)}`,
516
+ `- Workspace: ${formatCodexDisplayText(data.cwd ?? "unknown")}`,
517
+ "- Active run: not tracked"
518
+ ].join("\n");
504
519
  const threadBinding = await deps.readCodexAppServerBinding(data.sessionFile);
505
520
  const active = deps.readCodexConversationActiveTurn(data.sessionFile);
506
521
  return [
@@ -520,9 +535,20 @@ async function buildThreads(deps, pluginConfig, filter) {
520
535
  ...filter.trim() ? { searchTerm: filter.trim() } : {}
521
536
  }));
522
537
  }
538
+ async function buildCodexCliSessions(deps, args) {
539
+ const parsed = parseCodexCliSessionsArgs(args);
540
+ if (parsed.help || !parsed.host) return "Usage: /codex sessions --host <node> [filter] [--limit <n>]";
541
+ return formatCodexCliSessions(await deps.listCodexCliSessionsOnNode({
542
+ requestedNode: parsed.host,
543
+ filter: parsed.filter,
544
+ limit: parsed.limit
545
+ }));
546
+ }
523
547
  async function resumeThread(deps, ctx, pluginConfig, args) {
524
- const [threadId] = args;
525
- const normalizedThreadId = threadId?.trim();
548
+ const parsed = parseResumeArgs(args);
549
+ const normalizedThreadId = parsed.threadId?.trim();
550
+ if (parsed.help) return args.includes("--help") || args.includes("-h") || parsed.host ? "Usage: /codex resume <thread-id>\nUsage: /codex resume <session-id> --host <node> --bind here" : "Usage: /codex resume <thread-id>";
551
+ if (parsed.host) return await bindCodexCliNodeSession(deps, ctx, parsed);
526
552
  if (!normalizedThreadId || args.length !== 1) return "Usage: /codex resume <thread-id>";
527
553
  if (!ctx.sessionFile) return "Cannot attach a Codex thread because this command did not include an OpenClaw session file.";
528
554
  const response = await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
@@ -539,6 +565,30 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
539
565
  });
540
566
  return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
541
567
  }
568
+ async function bindCodexCliNodeSession(deps, ctx, parsed) {
569
+ if (!parsed.threadId || !parsed.host || parsed.bindHere !== true) return "Usage: /codex resume <session-id> --host <node> --bind here";
570
+ const resolved = await deps.resolveCodexCliSessionForBindingOnNode({
571
+ requestedNode: parsed.host,
572
+ sessionId: parsed.threadId
573
+ });
574
+ if (!resolved.session) return `No Codex CLI session ${formatCodexDisplayText(parsed.threadId)} was found on ${formatCodexDisplayText(parsed.host)}.`;
575
+ const nodeId = resolved.node.nodeId;
576
+ if (!nodeId) return "Cannot bind Codex CLI session because the selected node did not include a node id.";
577
+ const data = createCodexCliNodeConversationBindingData({
578
+ nodeId,
579
+ sessionId: parsed.threadId,
580
+ cwd: resolved.session?.cwd
581
+ });
582
+ const summary = `Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}`;
583
+ const request = await ctx.requestConversationBinding({
584
+ summary,
585
+ detachHint: "/codex detach",
586
+ data
587
+ });
588
+ if (request.status === "bound") return `Bound this conversation to Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}.`;
589
+ if (request.status === "pending") return request.reply.text ?? "Codex CLI session binding is pending approval.";
590
+ return formatCodexDisplayText(request.message);
591
+ }
542
592
  async function stopConversationTurn(deps, ctx, pluginConfig) {
543
593
  const sessionFile = await resolveControlSessionFile(ctx);
544
594
  if (!sessionFile) return "Cannot stop Codex because this command did not include an OpenClaw session file.";
@@ -599,7 +649,8 @@ async function setConversationPermissions(deps, ctx, pluginConfig, args) {
599
649
  });
600
650
  }
601
651
  async function resolveControlSessionFile(ctx) {
602
- return readCodexConversationBindingData(await ctx.getCurrentConversationBinding())?.sessionFile ?? ctx.sessionFile;
652
+ const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
653
+ return data?.kind === "codex-app-server-session" ? data.sessionFile : ctx.sessionFile;
603
654
  }
604
655
  async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, commandPrefix) {
605
656
  if (ctx.senderIsOwner !== true) return { text: "Only an owner can send Codex diagnostics." };
@@ -1175,6 +1226,83 @@ function parseBindArgs(args) {
1175
1226
  parsed.provider = normalizeOptionalString(parsed.provider);
1176
1227
  return parsed;
1177
1228
  }
1229
+ function parseCodexCliSessionsArgs(args) {
1230
+ const parsed = { filter: "" };
1231
+ const filter = [];
1232
+ for (let index = 0; index < args.length; index += 1) {
1233
+ const arg = args[index];
1234
+ if (arg === "--help" || arg === "-h") {
1235
+ parsed.help = true;
1236
+ continue;
1237
+ }
1238
+ if (arg === "--host" || arg === "--node") {
1239
+ const value = readRequiredOptionValue(args, index);
1240
+ if (!value || parsed.host !== void 0) {
1241
+ parsed.help = true;
1242
+ continue;
1243
+ }
1244
+ parsed.host = value;
1245
+ index += 1;
1246
+ continue;
1247
+ }
1248
+ if (arg === "--limit") {
1249
+ const value = readRequiredOptionValue(args, index);
1250
+ const parsedLimit = value ? Number.parseInt(value, 10) : NaN;
1251
+ if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
1252
+ parsed.help = true;
1253
+ continue;
1254
+ }
1255
+ parsed.limit = parsedLimit;
1256
+ index += 1;
1257
+ continue;
1258
+ }
1259
+ if (arg.startsWith("-")) {
1260
+ parsed.help = true;
1261
+ continue;
1262
+ }
1263
+ filter.push(arg);
1264
+ }
1265
+ parsed.host = normalizeOptionalString(parsed.host);
1266
+ parsed.filter = filter.join(" ").trim();
1267
+ return parsed;
1268
+ }
1269
+ function parseResumeArgs(args) {
1270
+ const parsed = {};
1271
+ for (let index = 0; index < args.length; index += 1) {
1272
+ const arg = args[index];
1273
+ if (arg === "--help" || arg === "-h") {
1274
+ parsed.help = true;
1275
+ continue;
1276
+ }
1277
+ if (arg === "--host" || arg === "--node") {
1278
+ const value = readRequiredOptionValue(args, index);
1279
+ if (!value || parsed.host !== void 0) {
1280
+ parsed.help = true;
1281
+ continue;
1282
+ }
1283
+ parsed.host = value;
1284
+ index += 1;
1285
+ continue;
1286
+ }
1287
+ if (arg === "--bind") {
1288
+ if (readRequiredOptionValue(args, index) !== "here" || parsed.bindHere !== void 0) {
1289
+ parsed.help = true;
1290
+ continue;
1291
+ }
1292
+ parsed.bindHere = true;
1293
+ index += 1;
1294
+ continue;
1295
+ }
1296
+ if (!arg.startsWith("-") && !parsed.threadId) {
1297
+ parsed.threadId = arg;
1298
+ continue;
1299
+ }
1300
+ parsed.help = true;
1301
+ }
1302
+ parsed.threadId = normalizeOptionalString(parsed.threadId);
1303
+ parsed.host = normalizeOptionalString(parsed.host);
1304
+ return parsed;
1305
+ }
1178
1306
  function parseComputerUseArgs(args) {
1179
1307
  const parsed = {
1180
1308
  action: "status",
@@ -1,7 +1,7 @@
1
1
  import { s as resolveCodexAppServerRuntimeOptions } from "./config-0rd3LnKg.js";
2
2
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
3
3
  import { i as readCodexAppServerBinding } from "./session-binding-CMTXuyoz.js";
4
- import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory-DfnbdQv7.js";
4
+ import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory-BB5hgnWH.js";
5
5
  import { embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, runHarnessContextEngineMaintenance } from "openclaw/plugin-sdk/agent-harness-runtime";
6
6
  //#region extensions/codex/src/app-server/compact.ts
7
7
  const DEFAULT_CODEX_COMPACTION_WAIT_TIMEOUT_MS = 300 * 1e3;
@@ -1,5 +1,5 @@
1
1
  import { c as resolveCodexComputerUseConfig, s as resolveCodexAppServerRuntimeOptions } from "./config-0rd3LnKg.js";
2
- import { r as describeControlFailure, t as requestCodexAppServerJson } from "./request-ohCy5ASa.js";
2
+ import { r as describeControlFailure, t as requestCodexAppServerJson } from "./request-BxAP1uyw.js";
3
3
  import { existsSync } from "node:fs";
4
4
  //#region extensions/codex/src/app-server/computer-use.ts
5
5
  var CodexComputerUseSetupError = class extends Error {
package/dist/harness.js CHANGED
@@ -18,15 +18,15 @@ function createCodexAppServerAgentHarness(options) {
18
18
  };
19
19
  },
20
20
  runAttempt: async (params) => {
21
- const { runCodexAppServerAttempt } = await import("./run-attempt-4t06MuNr.js");
21
+ const { runCodexAppServerAttempt } = await import("./run-attempt-CN9AS7qv.js");
22
22
  return runCodexAppServerAttempt(params, { pluginConfig: options?.pluginConfig });
23
23
  },
24
24
  runSideQuestion: async (params) => {
25
- const { runCodexAppServerSideQuestion } = await import("./side-question-DV7KYIB8.js");
25
+ const { runCodexAppServerSideQuestion } = await import("./side-question-BsBqLpar.js");
26
26
  return runCodexAppServerSideQuestion(params, { pluginConfig: options?.pluginConfig });
27
27
  },
28
28
  compact: async (params) => {
29
- const { maybeCompactCodexAppServerSession } = await import("./compact-Bl8jRPk4.js");
29
+ const { maybeCompactCodexAppServerSession } = await import("./compact-Ck9ckRkJ.js");
30
30
  return maybeCompactCodexAppServerSession(params, { pluginConfig: options?.pluginConfig });
31
31
  },
32
32
  reset: async (params) => {
@@ -36,7 +36,7 @@ function createCodexAppServerAgentHarness(options) {
36
36
  }
37
37
  },
38
38
  dispose: async () => {
39
- const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-Cr6W-a2G.js").then((n) => n.i);
39
+ const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-BwUqd3lh.js").then((n) => n.a);
40
40
  await clearSharedCodexAppServerClientAndWait();
41
41
  }
42
42
  };
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@ import { createCodexAppServerAgentHarness } from "./harness.js";
2
2
  import { o as readCodexPluginConfig, s as resolveCodexAppServerRuntimeOptions, t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-0rd3LnKg.js";
3
3
  import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
4
4
  import { buildCodexProvider } from "./provider.js";
5
- import { r as describeControlFailure, t as requestCodexAppServerJson } from "./request-ohCy5ASa.js";
6
- import { r as formatCodexDisplayText } from "./command-formatters-Ttwc_kgX.js";
7
- import { l as resolveCodexAppServerAuthProfileIdForAgent, s as resolveCodexAppServerAuthAccountCacheKey, u as resolveCodexAppServerEnvApiKeyCacheKey } from "./shared-client-Cr6W-a2G.js";
8
- import { n as handleCodexConversationInboundClaim, t as handleCodexConversationBindingResolved } from "./conversation-binding-CKofEetY.js";
5
+ import { r as describeControlFailure, t as requestCodexAppServerJson } from "./request-BxAP1uyw.js";
6
+ import { r as formatCodexDisplayText } from "./command-formatters-CJH9-hFT.js";
7
+ import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, i as getSharedCodexAppServerClient, t as clearSharedCodexAppServerClientAndWait, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-BwUqd3lh.js";
8
+ import { a as resolveCodexCliSessionForBindingOnNode, c as handleCodexConversationInboundClaim, i as listCodexCliSessionsOnNode, n as createCodexCliSessionNodeInvokePolicies, o as resumeCodexCliSessionOnNode, s as handleCodexConversationBindingResolved, t as createCodexCliSessionNodeHostCommands } from "./node-cli-sessions-CV5Kmdwt.js";
9
9
  import { a as defaultCodexAppInventoryCache, n as pluginReadParams, t as ensureCodexPluginActivation } from "./plugin-activation-PXGqUjwY.js";
10
- import { t as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-B7eU8fNZ.js";
10
+ import { t as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DBlqE0gO.js";
11
11
  import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
12
12
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
13
13
  import os from "node:os";
@@ -39,7 +39,7 @@ async function handleCodexCommand(ctx, options = {}) {
39
39
  }
40
40
  }
41
41
  async function loadDefaultCodexSubcommandHandler() {
42
- const { handleCodexSubcommand } = await import("./command-handlers-BYf_veEb.js");
42
+ const { handleCodexSubcommand } = await import("./command-handlers-DIXo49aO.js");
43
43
  return handleCodexSubcommand;
44
44
  }
45
45
  //#endregion
@@ -124,7 +124,7 @@ async function discoverPluginDirs(codexHome) {
124
124
  manifestPath,
125
125
  sourceKind: "cache",
126
126
  migratable: false,
127
- message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory."
127
+ message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory"
128
128
  });
129
129
  return;
130
130
  }
@@ -412,7 +412,7 @@ async function discoverCodexSource(inputOrOptions) {
412
412
  const hooksPath = path.join(codexHome, "hooks", "hooks.json");
413
413
  const codexSkills = await discoverSkillDirs({
414
414
  root: codexSkillsDir,
415
- sourceLabel: "Codex CLI skill",
415
+ sourceLabel: "Codex skill",
416
416
  excludeSystem: true
417
417
  });
418
418
  const personalAgentSkills = await discoverSkillDirs({
@@ -431,13 +431,13 @@ async function discoverCodexSource(inputOrOptions) {
431
431
  id: "archive:config.toml",
432
432
  path: configPath,
433
433
  relativePath: "config.toml",
434
- message: "Codex config is archived for manual review; it is not activated automatically."
434
+ message: "Codex config is archived for manual review; it is not activated automatically"
435
435
  });
436
436
  if (await exists(hooksPath)) archivePaths.push({
437
437
  id: "archive:hooks/hooks.json",
438
438
  path: hooksPath,
439
439
  relativePath: "hooks/hooks.json",
440
- message: "Codex native hooks are archived for manual review because they can execute commands."
440
+ message: "Codex native hooks are archived for manual review because they can execute commands"
441
441
  });
442
442
  const skills = [...codexSkills, ...personalAgentSkills].toSorted((a, b) => a.source.localeCompare(b.source));
443
443
  const high = Boolean(codexSkills.length || plugins.length || archivePaths.length);
@@ -727,7 +727,6 @@ async function buildCodexMigrationPlan(ctx) {
727
727
  ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
728
728
  ...source.plugins.some((plugin) => plugin.migratable) ? ["Codex source-installed openai-curated plugins are planned for native activation; cached plugin bundles remain manual-review only."] : [],
729
729
  ...source.plugins.some((plugin) => plugin.migratable && plugin.apps && plugin.apps.length > 0) && !shouldVerifyPluginApps(ctx) ? ["Codex app-backed plugins were planned without source app accessibility verification. Re-run with --verify-plugin-apps to force a fresh source app/list check before planning native plugin activation."] : [],
730
- ...source.plugins.some((plugin) => plugin.sourceKind === "cache") ? ["Codex cached plugin bundles remain manual-review only."] : [],
731
730
  ...source.pluginDiscoveryError ? [`Codex app-server plugin inventory discovery failed: ${source.pluginDiscoveryError}. Cached plugin bundles, if any, are advisory only.`] : [],
732
731
  ...source.plugins.some((plugin) => plugin.migrationBlock?.code === "codex_subscription_required") ? [codexPluginMigrationSubscriptionWarning()] : [],
733
732
  ...source.archivePaths.length > 0 ? ["Codex config and hook files are archive-only. They are preserved in the migration report, not loaded into OpenClaw automatically."] : []
@@ -752,6 +751,11 @@ async function buildCodexMigrationPlan(ctx) {
752
751
  //#region extensions/codex/src/migration/apply.ts
753
752
  const CODEX_PLUGIN_AUTH_REQUIRED_REASON = "auth_required";
754
753
  const CODEX_PLUGIN_NOT_SELECTED_REASON = "not selected for migration";
754
+ const CODEX_CONFIG_PATCH_MODE_RETURN = "return";
755
+ const CODEX_PLUGIN_LOAD_WARNING = "Some Codex plugins could not be migrated. Run `openclaw migrate codex` after onboarding.";
756
+ const TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS = 250;
757
+ const TARGET_CODEX_MARKETPLACE_DISCOVERY_TIMEOUT_MS = 3e4;
758
+ const TARGET_CODEX_MARKETPLACE_DISCOVERY_TIMEOUT_ENV = "OPENCLAW_CODEX_MIGRATION_PLUGIN_LIST_TIMEOUT_MS";
755
759
  var CodexPluginConfigConflictError = class extends Error {
756
760
  constructor(reason) {
757
761
  super(reason);
@@ -759,6 +763,26 @@ var CodexPluginConfigConflictError = class extends Error {
759
763
  this.name = "CodexPluginConfigConflictError";
760
764
  }
761
765
  };
766
+ function shouldReturnCodexPluginConfigPatch(ctx) {
767
+ return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN;
768
+ }
769
+ function prepareTargetCodexAppServer(ctx) {
770
+ const appServer = resolveTargetCodexAppServer(ctx);
771
+ const targets = resolveCodexMigrationTargets(ctx);
772
+ const ready = getSharedCodexAppServerClient({
773
+ startOptions: appServer.start,
774
+ timeoutMs: 6e4,
775
+ agentDir: targets.agentDir,
776
+ config: ctx.config
777
+ }).then(() => void 0, () => void 0);
778
+ return { async dispose() {
779
+ await ready;
780
+ await clearSharedCodexAppServerClientAndWait({
781
+ exitTimeoutMs: 2e3,
782
+ forceKillDelayMs: 250
783
+ });
784
+ } };
785
+ }
762
786
  async function applyCodexMigrationPlan(params) {
763
787
  const plan = params.plan ?? await buildCodexMigrationPlan(params.ctx);
764
788
  const reportDir = params.ctx.reportDir ?? path.join(params.ctx.stateDir, "migration", "codex");
@@ -786,6 +810,10 @@ async function applyCodexMigrationPlan(params) {
786
810
  backupPath: params.ctx.backupPath,
787
811
  reportDir
788
812
  };
813
+ if (items.some(isCodexPluginLoadWarningItem)) {
814
+ result.warnings = [...new Set([...result.warnings ?? [], CODEX_PLUGIN_LOAD_WARNING])];
815
+ result.nextSteps = [...new Set([CODEX_PLUGIN_LOAD_WARNING, ...result.nextSteps ?? []])];
816
+ }
789
817
  await writeMigrationReport(result, { title: "Codex Migration Report" });
790
818
  return result;
791
819
  }
@@ -804,14 +832,14 @@ async function applyCodexPluginInstallItem(ctx, item) {
804
832
  const result = await ensureCodexPluginActivation({
805
833
  identity: policy,
806
834
  installEvenIfActive: true,
807
- request: async (method, requestParams) => await requestCodexAppServerJson({
835
+ request: async (method, requestParams) => await requestTargetCodexAppServerJson({
808
836
  method,
809
837
  requestParams,
810
838
  timeoutMs: 6e4,
811
839
  startOptions: appServer.start,
812
840
  agentDir: resolveCodexMigrationTargets(ctx).agentDir,
813
841
  config: ctx.config,
814
- isolated: true
842
+ isolated: false
815
843
  }),
816
844
  appCache: defaultCodexAppInventoryCache,
817
845
  appCacheKey
@@ -839,6 +867,16 @@ async function applyCodexPluginInstallItem(ctx, item) {
839
867
  appsNeedingAuth: sanitizeAppsNeedingAuth(result.installResponse?.appsNeedingAuth ?? [])
840
868
  }
841
869
  };
870
+ if (result.reason === "plugin_missing" || result.reason === "marketplace_missing") return {
871
+ ...item,
872
+ status: "warning",
873
+ reason: result.reason,
874
+ message: `Codex plugin "${policy.pluginName}" could not be migrated automatically`,
875
+ details: {
876
+ ...baseDetails,
877
+ warningReason: CODEX_PLUGIN_LOAD_WARNING
878
+ }
879
+ };
842
880
  return {
843
881
  ...item,
844
882
  status: "error",
@@ -846,10 +884,22 @@ async function applyCodexPluginInstallItem(ctx, item) {
846
884
  details: baseDetails
847
885
  };
848
886
  } catch (error) {
887
+ if (isCodexPluginInventoryLoadError(error)) return {
888
+ ...item,
889
+ status: "warning",
890
+ reason: "plugin_inventory_unavailable",
891
+ message: `Codex plugin "${policy.pluginName}" could not be migrated automatically`,
892
+ details: {
893
+ ...item.details,
894
+ code: "plugin_inventory_unavailable",
895
+ warningReason: CODEX_PLUGIN_LOAD_WARNING,
896
+ diagnostic: formatCodexMigrationError(error)
897
+ }
898
+ };
849
899
  return {
850
900
  ...item,
851
901
  status: "error",
852
- reason: error instanceof Error ? error.message : String(error),
902
+ reason: formatCodexMigrationError(error),
853
903
  details: {
854
904
  ...item.details,
855
905
  code: "plugin_install_failed"
@@ -857,9 +907,51 @@ async function applyCodexPluginInstallItem(ctx, item) {
857
907
  };
858
908
  }
859
909
  }
910
+ function isCodexPluginInventoryLoadError(error) {
911
+ return formatCodexMigrationError(error).includes("codex app-server plugin/list timed out");
912
+ }
913
+ function formatCodexMigrationError(error) {
914
+ return error instanceof Error ? error.message : String(error);
915
+ }
860
916
  function resolveTargetCodexAppServer(ctx) {
861
917
  return resolveCodexAppServerRuntimeOptions({ pluginConfig: readCodexPluginConfig(ctx.config) });
862
918
  }
919
+ async function requestTargetCodexAppServerJson(params) {
920
+ if (params.method !== "plugin/list") return await requestCodexAppServerJson(params);
921
+ const deadline = Date.now() + params.timeoutMs;
922
+ const discoveryTimeoutMs = targetCodexMarketplaceDiscoveryTimeoutMs();
923
+ const discoveryDeadline = Math.min(deadline, Date.now() + discoveryTimeoutMs);
924
+ let lastResponse;
925
+ let attempt = 0;
926
+ do {
927
+ attempt += 1;
928
+ const remainingMs = Math.max(1, discoveryDeadline - Date.now());
929
+ lastResponse = await requestCodexAppServerJson({
930
+ ...params,
931
+ timeoutMs: remainingMs
932
+ });
933
+ if (hasOpenAiCuratedMarketplace(lastResponse)) return lastResponse;
934
+ if (Date.now() >= discoveryDeadline) return lastResponse;
935
+ await sleep(Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now()));
936
+ } while (Date.now() < discoveryDeadline);
937
+ return lastResponse;
938
+ }
939
+ function hasOpenAiCuratedMarketplace(response) {
940
+ if (!response || typeof response !== "object" || !("marketplaces" in response)) return false;
941
+ const marketplaces = response.marketplaces;
942
+ return Array.isArray(marketplaces) && marketplaces.some((marketplace) => marketplace && typeof marketplace === "object" && marketplace.name === "openai-curated");
943
+ }
944
+ function targetCodexMarketplaceDiscoveryTimeoutMs() {
945
+ const configured = Number(process.env[TARGET_CODEX_MARKETPLACE_DISCOVERY_TIMEOUT_ENV]);
946
+ if (Number.isFinite(configured) && configured >= 0) return configured;
947
+ return TARGET_CODEX_MARKETPLACE_DISCOVERY_TIMEOUT_MS;
948
+ }
949
+ function isCodexPluginLoadWarningItem(item) {
950
+ return item.kind === "plugin" && item.action === "install" && item.status === "warning" && item.details?.warningReason === CODEX_PLUGIN_LOAD_WARNING;
951
+ }
952
+ async function sleep(ms) {
953
+ await new Promise((resolve) => setTimeout(resolve, ms));
954
+ }
863
955
  async function buildTargetCodexPluginAppCacheKey(ctx) {
864
956
  const targets = resolveCodexMigrationTargets(ctx);
865
957
  const appServer = resolveTargetCodexAppServer(ctx);
@@ -884,11 +976,23 @@ async function buildTargetCodexPluginAppCacheKey(ctx) {
884
976
  async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
885
977
  const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
886
978
  if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
979
+ const returnPatch = shouldReturnCodexPluginConfigPatch(ctx);
887
980
  const configApi = ctx.runtime?.config;
888
- if (!configApi?.current || !configApi.mutateConfigFile) return markMigrationItemError(item, "config runtime unavailable");
889
- const currentConfig = configApi.current();
981
+ const currentConfig = returnPatch ? ctx.config : configApi?.current?.();
982
+ if (!currentConfig) return markMigrationItemError(item, "config runtime unavailable");
890
983
  const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
891
984
  if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
985
+ const migratedItem = {
986
+ ...item,
987
+ status: "migrated",
988
+ details: {
989
+ ...item.details,
990
+ path: [...CODEX_PLUGIN_CONFIG_PATH],
991
+ value
992
+ }
993
+ };
994
+ if (returnPatch) return migratedItem;
995
+ if (!configApi?.mutateConfigFile) return markMigrationItemError(item, "config runtime unavailable");
892
996
  try {
893
997
  await configApi.mutateConfigFile({
894
998
  base: "runtime",
@@ -898,15 +1002,7 @@ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
898
1002
  writeMigrationConfigPath(draft, CODEX_PLUGIN_CONFIG_PATH, value);
899
1003
  }
900
1004
  });
901
- return {
902
- ...item,
903
- status: "migrated",
904
- details: {
905
- ...item.details,
906
- path: [...CODEX_PLUGIN_CONFIG_PATH],
907
- value
908
- }
909
- };
1005
+ return migratedItem;
910
1006
  } catch (error) {
911
1007
  if (error instanceof CodexPluginConfigConflictError) return markMigrationItemConflict(item, error.reason);
912
1008
  return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
@@ -979,6 +1075,9 @@ function buildCodexMigrationProvider(params = {}) {
979
1075
  };
980
1076
  },
981
1077
  plan: buildCodexMigrationPlan,
1078
+ prepareApply(ctx) {
1079
+ return prepareTargetCodexAppServer(ctx);
1080
+ },
982
1081
  async apply(ctx, plan) {
983
1082
  return await applyCodexMigrationPlan({
984
1083
  ctx,
@@ -1000,8 +1099,28 @@ var codex_default = definePluginEntry({
1000
1099
  api.registerProvider(buildCodexProvider({ pluginConfig: api.pluginConfig }));
1001
1100
  api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
1002
1101
  api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
1003
- api.registerCommand(createCodexCommand({ pluginConfig: api.pluginConfig }));
1004
- api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, { pluginConfig: resolveCurrentPluginConfig() }));
1102
+ for (const command of createCodexCliSessionNodeHostCommands()) api.registerNodeHostCommand(command);
1103
+ for (const policy of createCodexCliSessionNodeInvokePolicies()) api.registerNodeInvokePolicy(policy);
1104
+ api.registerCommand(createCodexCommand({
1105
+ pluginConfig: api.pluginConfig,
1106
+ deps: {
1107
+ listCodexCliSessionsOnNode: (params) => listCodexCliSessionsOnNode({
1108
+ runtime: api.runtime,
1109
+ ...params
1110
+ }),
1111
+ resolveCodexCliSessionForBindingOnNode: (params) => resolveCodexCliSessionForBindingOnNode({
1112
+ runtime: api.runtime,
1113
+ ...params
1114
+ })
1115
+ }
1116
+ }));
1117
+ api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, {
1118
+ pluginConfig: resolveCurrentPluginConfig(),
1119
+ resumeCodexCliSessionOnNode: (params) => resumeCodexCliSessionOnNode({
1120
+ runtime: api.runtime,
1121
+ ...params
1122
+ })
1123
+ }));
1005
1124
  api.onConversationBindingResolved?.(handleCodexConversationBindingResolved);
1006
1125
  }
1007
1126
  });