@openclaw/codex 2026.7.2-beta.7 → 2026.7.34
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 +3 -27
- package/dist/{command-formatters-C336eRVS.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
- package/dist/{attempt-notifications-AP-a-Q5k.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
- package/dist/{command-handlers-DAHUSry9.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
- package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
- package/dist/{compact-D_uvHurS.js → .setup/compact-BNgh7I4t.mjs} +10 -40
- package/dist/.setup/computer-use-C3QkmSO3.mjs +377 -0
- package/dist/.setup/config-CYEDnLJ2.mjs +1138 -0
- package/dist/{conversation-binding-data-BHRpYbX3.js → .setup/conversation-binding-data-CrRxtDBu.mjs} +1 -1
- package/dist/{bounded-turn-D2ZAgZVz.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +203 -109
- package/dist/{models-BBMKU4jA.js → .setup/models-C_aZk03A.mjs} +11 -6
- package/dist/.setup/notification-correlation-iYs4WieS.mjs +35 -0
- package/dist/.setup/plugin-app-cache-key-DR8jV9oS.mjs +99 -0
- package/dist/.setup/protocol-2POPqAY4.mjs +13 -0
- package/dist/{transcript-mirror-DzsdNCf5.js → .setup/protocol-validators-dZQ-UTOa.mjs} +112 -922
- package/dist/{rate-limits-CU-gVCCS.js → .setup/provider-DGxDs6g8.mjs} +269 -13
- package/dist/{dynamic-tools-D2gYkSBO.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5632 -3953
- package/dist/.setup/request-DFR0JyoV.mjs +36 -0
- package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
- package/dist/{sandbox-guard-NuhpXmPB.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -14
- package/dist/.setup/session-binding-DuihlMJc.mjs +628 -0
- package/dist/.setup/shared-client-B2KmLPRw.mjs +1854 -0
- package/dist/{side-question-_ulBk1AL.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
- package/dist/.setup/thread-lifecycle-u6CZdOOH.mjs +2593 -0
- package/dist/{web-search-provider.runtime-CCFhg2z5.js → .setup/web-search-provider.runtime-Ce2zOHxW.mjs} +3 -3
- package/dist/doctor-contract-api.js +122 -88
- package/dist/harness.js +20 -89
- package/dist/index.js +436 -1615
- package/dist/media-understanding-provider.js +1 -173
- package/dist/prompt-overlay.js +15 -0
- package/dist/provider-catalog.js +79 -0
- package/dist/provider-discovery.js +34 -0
- package/dist/provider.js +2 -0
- package/dist/web-search-contract-api.js +1 -1
- package/openclaw.plugin.json +178 -221
- package/package.json +8 -9
- package/dist/app-server-policy-BSaO2JIT.js +0 -49
- package/dist/auth-binding-B4FfbcCg.js +0 -62
- package/dist/capabilities-BFDTVYr4.js +0 -36
- package/dist/cli-metadata.js +0 -20
- package/dist/computer-use-CYEc8U3w.js +0 -693
- package/dist/conversation-control-xsIeXbo4.js +0 -287
- package/dist/effective-mcp-catalog-Bbd98Inw.js +0 -114
- package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
- package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
- package/dist/incognito-session-ET_ufDH-.js +0 -2
- package/dist/notification-correlation-CU6_AnOb.js +0 -67
- package/dist/plugin-app-cache-key-DztPafOA.js +0 -408
- package/dist/request-qKh331yu.js +0 -157
- package/dist/run-attempt-CY9WlDLc.js +0 -12413
- package/dist/runtime-artifact-CmtG_t1L.js +0 -554
- package/dist/session-binding-BXpo9ymA.js +0 -2214
- package/dist/session-catalog-DyOgR4WH.js +0 -2668
- package/dist/session-cli-2PZaFLRh.js +0 -175
- package/dist/session-upstream-marker-B6U7tLw5.js +0 -82
- package/dist/settled-turn-finalizer-CXY2Z9dT.js +0 -316
- package/dist/shared-client-DTbvH-XV.js +0 -3069
- package/dist/thread-lifecycle-DF53ogiA.js +0 -3987
- package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
- package/dist/upstream-session-fork-Dy4Xvuoj.js +0 -320
- package/dist/usage-CQ6GhD5-.js +0 -25
- /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
- /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
- /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as listAllCodexAppServerModels } from "./models-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import { a as
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { n as installCodexComputerUse, r as readCodexComputerUseStatus } from "./computer-use-
|
|
1
|
+
import { r as isJsonObject } from "./protocol-2POPqAY4.mjs";
|
|
2
|
+
import { o as isCodexFastServiceTier } from "./config-CYEDnLJ2.mjs";
|
|
3
|
+
import { n as assertCodexThreadResumeResponse } from "./protocol-validators-dZQ-UTOa.mjs";
|
|
4
|
+
import { t as listAllCodexAppServerModels } from "./models-C_aZk03A.mjs";
|
|
5
|
+
import { p as summarizeCodexAccountUsage } from "./provider-DGxDs6g8.mjs";
|
|
6
|
+
import { y as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-B2KmLPRw.mjs";
|
|
7
|
+
import { a as normalizeCodexAppServerBindingModelProvider, n as bindingStoreKey, o as reclaimCurrentCodexSessionGeneration, s as sessionBindingIdentity } from "./session-binding-DuihlMJc.mjs";
|
|
8
|
+
import { r as CODEX_CONTROL_METHODS } from "./plugin-app-cache-key-DR8jV9oS.mjs";
|
|
9
|
+
import { a as formatCodexDisplayText, c as formatList, d as formatThreads, f as readString$1, i as formatAccount, l as formatModels, o as formatCodexStatus, r as buildHelp, s as formatComputerUseStatus, u as formatSkills } from "./app-server-policy-C_qTMEMy.mjs";
|
|
10
|
+
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-DA2TQfZW.mjs";
|
|
11
|
+
import { S as canMutateCodexHost, _ as setCodexConversationPermissions, d as formatPermissionsMode, f as parseCodexFastModeArg, g as setCodexConversationModel, h as setCodexConversationFastMode, i as safeCodexControlRequest, m as readCodexConversationActiveTurn, n as readCodexStatusProbes, p as parseCodexPermissionsModeArg, r as requestOptions, s as formatCodexCliSessions, t as codexControlRequest, v as steerCodexConversationTurn, x as CODEX_NATIVE_EXECUTION_AUTH_ERROR, y as stopCodexConversationTurn } from "./command-rpc-BvYsfQ7G.mjs";
|
|
12
|
+
import { i as readCodexConversationBindingData, n as createCodexCliNodeConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-CrRxtDBu.mjs";
|
|
13
|
+
import { n as installCodexComputerUse, r as readCodexComputerUseStatus } from "./computer-use-C3QkmSO3.mjs";
|
|
14
14
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
|
-
import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAgentDir, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveProfileUnusableUntilForDisplay, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
16
|
+
import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAgentDir, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveDefaultAgentDir, resolveProfileUnusableUntilForDisplay, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
17
17
|
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
18
18
|
import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
19
|
-
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
20
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
21
|
-
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
22
|
-
import { MODEL_SELECTION_LOCKED_MESSAGE, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
|
|
23
19
|
//#region extensions/codex/src/command-account.ts
|
|
24
20
|
const OPENAI_PROVIDER_ID = "openai";
|
|
25
21
|
const OPENAI_CODEX_PROVIDER_ID = OPENAI_PROVIDER_ID;
|
|
26
22
|
async function readCodexAccountAuthOverview(params) {
|
|
27
23
|
const config = params.ctx.config;
|
|
28
|
-
const agentDir =
|
|
24
|
+
const agentDir = resolveDefaultAgentDir(config);
|
|
29
25
|
const store = ensureAuthProfileStore(agentDir, {
|
|
30
26
|
allowKeychainPrompt: false,
|
|
31
27
|
config
|
|
@@ -50,7 +46,6 @@ async function readCodexAccountAuthOverview(params) {
|
|
|
50
46
|
const activeUsage = activeIsSubscription && params.limits.ok ? summarizeCodexAccountUsage(params.limits.value, now) : void 0;
|
|
51
47
|
const subscriptionUsage = subscriptionProfileId && (!activeIsSubscription || subscriptionProfileId !== activeProfileId) ? await readSubscriptionUsage({
|
|
52
48
|
...params,
|
|
53
|
-
agentDir,
|
|
54
49
|
config,
|
|
55
50
|
subscriptionProfileId,
|
|
56
51
|
now
|
|
@@ -155,7 +150,7 @@ function resolveLiveAccountProfileId(params) {
|
|
|
155
150
|
if (!email) return firstSubscription;
|
|
156
151
|
return params.order.find((profileId) => {
|
|
157
152
|
const credential = params.store.profiles[profileId];
|
|
158
|
-
if (!
|
|
153
|
+
if (!isChatGptSubscriptionProfile(credential)) return false;
|
|
159
154
|
return (credential.email?.trim().toLowerCase() ?? extractEmailFromProfileId(profileId))?.toLowerCase() === email;
|
|
160
155
|
}) ?? firstSubscription;
|
|
161
156
|
}
|
|
@@ -167,7 +162,6 @@ function shouldInferApiKeyActiveFromRateLimitProbe(limits) {
|
|
|
167
162
|
async function readSubscriptionUsage(params) {
|
|
168
163
|
const limits = await params.safeCodexControlRequest(params.pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, {
|
|
169
164
|
config: params.config,
|
|
170
|
-
agentDir: params.agentDir,
|
|
171
165
|
authProfileId: params.subscriptionProfileId,
|
|
172
166
|
isolated: true
|
|
173
167
|
});
|
|
@@ -315,21 +309,6 @@ function formatRelativeReset(untilMs, nowMs) {
|
|
|
315
309
|
return `in ${days} ${days === 1 ? "day" : "days"}`;
|
|
316
310
|
}
|
|
317
311
|
//#endregion
|
|
318
|
-
//#region extensions/codex/src/command-diagnostics-state.ts
|
|
319
|
-
/** Runtime state for diagnostics upload throttling and confirmation handshakes. */
|
|
320
|
-
const codexDiagnosticsFeedbackState = {
|
|
321
|
-
lastUploadByThread: /* @__PURE__ */ new Map(),
|
|
322
|
-
lastUploadByScope: /* @__PURE__ */ new Map(),
|
|
323
|
-
pendingConfirmations: /* @__PURE__ */ new Map(),
|
|
324
|
-
pendingTokensByScope: /* @__PURE__ */ new Map(),
|
|
325
|
-
clear() {
|
|
326
|
-
this.lastUploadByThread.clear();
|
|
327
|
-
this.lastUploadByScope.clear();
|
|
328
|
-
this.pendingConfirmations.clear();
|
|
329
|
-
this.pendingTokensByScope.clear();
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
//#endregion
|
|
333
312
|
//#region extensions/codex/src/command-presentation.ts
|
|
334
313
|
function buildCodexCommandPickerPresentation(title, prompt, buttons) {
|
|
335
314
|
return {
|
|
@@ -350,8 +329,212 @@ function buildCodexCommandPickerPresentation(title, prompt, buttons) {
|
|
|
350
329
|
};
|
|
351
330
|
}
|
|
352
331
|
//#endregion
|
|
353
|
-
//#region extensions/codex/src/command-
|
|
354
|
-
|
|
332
|
+
//#region extensions/codex/src/command-plugins-management.ts
|
|
333
|
+
const POLICY_REFRESH_HINT = "New Codex conversations pick this up automatically. Use /new or /reset to refresh the current one.";
|
|
334
|
+
async function handleCodexPluginsSubcommand(ctx, rest, io) {
|
|
335
|
+
const [verb = "list", ...args] = rest;
|
|
336
|
+
const normalized = verb.toLowerCase();
|
|
337
|
+
if (normalized === "menu") {
|
|
338
|
+
if (args.length > 0) return { text: "Usage: /codex plugins menu" };
|
|
339
|
+
return buildPluginsMenuReply();
|
|
340
|
+
}
|
|
341
|
+
if (normalized === "help") {
|
|
342
|
+
if (args.length > 0) return { text: "Usage: /codex plugins help" };
|
|
343
|
+
return { text: buildPluginsHelp() };
|
|
344
|
+
}
|
|
345
|
+
if (normalized === "list") {
|
|
346
|
+
if (args.length > 0) return { text: "Usage: /codex plugins list" };
|
|
347
|
+
const current = await io.readConfig();
|
|
348
|
+
return { text: formatPluginList(current.plugins ?? {}, { globalEnabled: current.enabled === true }) };
|
|
349
|
+
}
|
|
350
|
+
const target = args[0];
|
|
351
|
+
if (normalized === "enable" || normalized === "disable") {
|
|
352
|
+
if (args.length === 0) return buildPluginNamePickerReply(normalized, await io.readConfig());
|
|
353
|
+
if (!target || args.length > 1) return { text: `Usage: /codex plugins ${normalized} <name>` };
|
|
354
|
+
if (!canMutateCodexHost(ctx)) return { text: `Only an owner or operator.admin gateway client can run /codex plugins ${normalized}.` };
|
|
355
|
+
const wantEnabled = normalized === "enable";
|
|
356
|
+
if (!((await io.readConfig()).plugins ?? {})[target]) return { text: `Codex sub-plugin '${formatCodexDisplayText(target)}' is not configured. Run '/codex plugins list' to see configured plugins.` };
|
|
357
|
+
await io.mutate((block) => {
|
|
358
|
+
if (wantEnabled) block.enabled = true;
|
|
359
|
+
block.plugins ??= {};
|
|
360
|
+
block.plugins[target] = {
|
|
361
|
+
...block.plugins[target],
|
|
362
|
+
enabled: wantEnabled
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
return { text: `${formatCodexDisplayText(target)}: ${wantEnabled ? "enabled" : "disabled"} in openclaw.json. ${POLICY_REFRESH_HINT}` };
|
|
366
|
+
}
|
|
367
|
+
return { text: `Unknown /codex plugins subcommand: ${formatCodexDisplayText(verb)}\n\n${buildPluginsHelp()}` };
|
|
368
|
+
}
|
|
369
|
+
function buildPluginsMenuReply() {
|
|
370
|
+
return {
|
|
371
|
+
text: [
|
|
372
|
+
"Codex sub-plugins. Pick a sub-action or type:",
|
|
373
|
+
"",
|
|
374
|
+
" 1. /codex plugins list",
|
|
375
|
+
" 2. /codex plugins enable",
|
|
376
|
+
" 3. /codex plugins disable",
|
|
377
|
+
" 4. /codex plugins help",
|
|
378
|
+
"",
|
|
379
|
+
"Type '/codex' to go back to the main menu."
|
|
380
|
+
].join("\n"),
|
|
381
|
+
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", "Pick a Codex sub-plugin action:", [
|
|
382
|
+
{
|
|
383
|
+
label: "list",
|
|
384
|
+
command: "/codex plugins list"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
label: "enable",
|
|
388
|
+
command: "/codex plugins enable"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
label: "disable",
|
|
392
|
+
command: "/codex plugins disable"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
label: "help",
|
|
396
|
+
command: "/codex plugins help"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
label: "back",
|
|
400
|
+
command: "/codex"
|
|
401
|
+
}
|
|
402
|
+
])
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function buildPluginNamePickerReply(verb, current) {
|
|
406
|
+
const globalEnabled = current.enabled === true;
|
|
407
|
+
const eligible = Object.entries(current.plugins ?? {}).toSorted(([left], [right]) => left.localeCompare(right)).filter(([, entry]) => {
|
|
408
|
+
const effectivelyEnabled = globalEnabled && entry.enabled !== false;
|
|
409
|
+
return verb === "disable" ? effectivelyEnabled : !effectivelyEnabled;
|
|
410
|
+
});
|
|
411
|
+
if (eligible.length === 0) return {
|
|
412
|
+
text: [
|
|
413
|
+
`No configured ${verb === "enable" ? "disabled" : "enabled"} Codex sub-plugins found.`,
|
|
414
|
+
"",
|
|
415
|
+
"Type '/codex plugins list' to inspect configured sub-plugins.",
|
|
416
|
+
"Type '/codex plugins menu' to go back to the plugins menu."
|
|
417
|
+
].join("\n"),
|
|
418
|
+
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", "Pick another Codex sub-plugin action:", [{
|
|
419
|
+
label: "list",
|
|
420
|
+
command: "/codex plugins list"
|
|
421
|
+
}, {
|
|
422
|
+
label: "back",
|
|
423
|
+
command: "/codex plugins menu"
|
|
424
|
+
}])
|
|
425
|
+
};
|
|
426
|
+
const buttons = [...eligible.map(([key]) => ({
|
|
427
|
+
label: formatCodexDisplayText(key),
|
|
428
|
+
command: `/codex plugins ${verb} ${key}`
|
|
429
|
+
})), {
|
|
430
|
+
label: "back",
|
|
431
|
+
command: "/codex plugins menu"
|
|
432
|
+
}];
|
|
433
|
+
return {
|
|
434
|
+
text: [
|
|
435
|
+
`Codex sub-plugins to ${verb}. Pick one or type:`,
|
|
436
|
+
"",
|
|
437
|
+
...eligible.map(([key], index) => ` ${index + 1}. /codex plugins ${verb} ${key}`),
|
|
438
|
+
"",
|
|
439
|
+
...verb === "enable" && !globalEnabled ? ["Global codexPlugins.enabled is off; enabling one configured sub-plugin turns it on.", ""] : [],
|
|
440
|
+
"Type '/codex plugins menu' to go back to the plugins menu."
|
|
441
|
+
].join("\n"),
|
|
442
|
+
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", `Pick a Codex sub-plugin to ${verb}:`, buttons)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function buildPluginsHelp() {
|
|
446
|
+
return [
|
|
447
|
+
"Codex sub-plugin management (writes only to ~/.openclaw/openclaw.json, never to ~/.codex/config.toml):",
|
|
448
|
+
"- /codex plugins (alias for list)",
|
|
449
|
+
"- /codex plugins list show all configured Codex sub-plugins",
|
|
450
|
+
"- /codex plugins enable <name> enable a configured sub-plugin",
|
|
451
|
+
"- /codex plugins disable <name> disable a configured sub-plugin"
|
|
452
|
+
].join("\n");
|
|
453
|
+
}
|
|
454
|
+
function formatPluginList(plugins, options = {}) {
|
|
455
|
+
const globalEnabled = options.globalEnabled === true;
|
|
456
|
+
const keys = Object.keys(plugins).toSorted();
|
|
457
|
+
if (keys.length === 0) return "No Codex sub-plugins configured under plugins.entries.codex.config.codexPlugins.plugins";
|
|
458
|
+
const rows = keys.map((key) => {
|
|
459
|
+
const entry = plugins[key] ?? {};
|
|
460
|
+
const state = globalEnabled && entry.enabled !== false ? "ON " : "OFF";
|
|
461
|
+
return {
|
|
462
|
+
displayKey: formatCodexDisplayText(key),
|
|
463
|
+
state,
|
|
464
|
+
pluginName: formatCodexDisplayText(entry.pluginName ?? key),
|
|
465
|
+
marketplace: formatCodexDisplayText(entry.marketplaceName ?? "?")
|
|
466
|
+
};
|
|
467
|
+
});
|
|
468
|
+
const keyW = Math.max(...rows.map((r) => r.displayKey.length));
|
|
469
|
+
const pluginW = Math.max(...rows.map((r) => r.pluginName.length));
|
|
470
|
+
return [
|
|
471
|
+
"Codex sub-plugins in Openclaw config (~/.openclaw/openclaw.json):",
|
|
472
|
+
"",
|
|
473
|
+
...rows.map((r) => ` ${r.state} ${r.displayKey.padEnd(keyW)} ${r.pluginName.padEnd(pluginW)} [${r.marketplace}]`),
|
|
474
|
+
"",
|
|
475
|
+
...globalEnabled ? [] : ["Global codexPlugins.enabled is off; configured sub-plugins are inactive.", ""],
|
|
476
|
+
"New Codex conversations pick up policy changes automatically; /new or /reset to refresh the current one."
|
|
477
|
+
].join("\n");
|
|
478
|
+
}
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region extensions/codex/src/command-handlers.ts
|
|
481
|
+
const defaultCodexCommandDeps = {
|
|
482
|
+
codexControlRequest,
|
|
483
|
+
listCodexAppServerModels: listAllCodexAppServerModels,
|
|
484
|
+
readCodexStatusProbes,
|
|
485
|
+
requestOptions,
|
|
486
|
+
safeCodexControlRequest,
|
|
487
|
+
readCodexComputerUseStatus,
|
|
488
|
+
installCodexComputerUse,
|
|
489
|
+
resolveCodexDefaultWorkspaceDir,
|
|
490
|
+
readCodexConversationActiveTurn,
|
|
491
|
+
setCodexConversationFastMode,
|
|
492
|
+
setCodexConversationModel,
|
|
493
|
+
setCodexConversationPermissions,
|
|
494
|
+
steerCodexConversationTurn,
|
|
495
|
+
stopCodexConversationTurn,
|
|
496
|
+
listCodexCliSessionsOnNode: async () => {
|
|
497
|
+
throw new Error("Codex CLI node sessions require Gateway node runtime.");
|
|
498
|
+
},
|
|
499
|
+
resolveCodexCliSessionForBindingOnNode: async () => {
|
|
500
|
+
throw new Error("Codex CLI node sessions require Gateway node runtime.");
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
const CODEX_DIAGNOSTICS_SOURCE = "openclaw-diagnostics";
|
|
504
|
+
const CODEX_DIAGNOSTICS_REASON_MAX_CHARS = 2048;
|
|
505
|
+
const CODEX_DIAGNOSTICS_COOLDOWN_MS = 6e4;
|
|
506
|
+
const CODEX_DIAGNOSTICS_ERROR_MAX_CHARS = 500;
|
|
507
|
+
const CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS = 100;
|
|
508
|
+
const CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES = 100;
|
|
509
|
+
const CODEX_DIAGNOSTICS_CONFIRMATION_TTL_MS = 3e5;
|
|
510
|
+
const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_REQUESTS_PER_SCOPE = 100;
|
|
511
|
+
const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_SCOPES = 100;
|
|
512
|
+
const CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS = 128;
|
|
513
|
+
const CODEX_RESUME_SAFE_THREAD_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
514
|
+
const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
515
|
+
"bind",
|
|
516
|
+
"resume",
|
|
517
|
+
"steer",
|
|
518
|
+
"model",
|
|
519
|
+
"fast",
|
|
520
|
+
"permissions",
|
|
521
|
+
"compact",
|
|
522
|
+
"review"
|
|
523
|
+
]);
|
|
524
|
+
const CODEX_NATIVE_CONTROL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
525
|
+
...CODEX_NATIVE_EXECUTION_SUBCOMMANDS,
|
|
526
|
+
"detach",
|
|
527
|
+
"unbind",
|
|
528
|
+
"stop"
|
|
529
|
+
]);
|
|
530
|
+
const lastCodexDiagnosticsUploadByThread = /* @__PURE__ */ new Map();
|
|
531
|
+
const lastCodexDiagnosticsUploadByScope = /* @__PURE__ */ new Map();
|
|
532
|
+
const pendingCodexDiagnosticsConfirmations = /* @__PURE__ */ new Map();
|
|
533
|
+
const pendingCodexDiagnosticsConfirmationTokensByScope = /* @__PURE__ */ new Map();
|
|
534
|
+
/**
|
|
535
|
+
* No-arg `/codex` picker. Codex owns the command tree; channels render the
|
|
536
|
+
* portable command actions as inline controls when their transport can.
|
|
537
|
+
*/
|
|
355
538
|
function buildCodexSubcommandPickerReply() {
|
|
356
539
|
const verbs = [
|
|
357
540
|
{
|
|
@@ -394,6 +577,7 @@ function buildCodexSubcommandPickerReply() {
|
|
|
394
577
|
presentation: buildCodexCommandPickerPresentation("Codex commands", "Pick a Codex subcommand:", verbs)
|
|
395
578
|
};
|
|
396
579
|
}
|
|
580
|
+
/** Sub-picker for `/codex fast menu` (on / off / status). */
|
|
397
581
|
function buildCodexFastMenuReply() {
|
|
398
582
|
const modes = [
|
|
399
583
|
"on",
|
|
@@ -418,6 +602,7 @@ function buildCodexFastMenuReply() {
|
|
|
418
602
|
presentation: buildCodexCommandPickerPresentation("Codex fast mode", "Pick a Codex fast mode:", buttons)
|
|
419
603
|
};
|
|
420
604
|
}
|
|
605
|
+
/** Sub-picker for `/codex permissions menu` (default / yolo / status). */
|
|
421
606
|
function buildCodexPermissionsMenuReply() {
|
|
422
607
|
const modes = [
|
|
423
608
|
"default",
|
|
@@ -442,6 +627,7 @@ function buildCodexPermissionsMenuReply() {
|
|
|
442
627
|
presentation: buildCodexCommandPickerPresentation("Codex permissions", "Pick a Codex permissions mode:", buttons)
|
|
443
628
|
};
|
|
444
629
|
}
|
|
630
|
+
/** Sub-picker for `/codex computer-use menu` (status / install). */
|
|
445
631
|
function buildCodexComputerUseMenuReply() {
|
|
446
632
|
const actions = ["status", "install"];
|
|
447
633
|
const buttons = [...actions.map((action) => ({
|
|
@@ -464,643 +650,457 @@ function buildCodexComputerUseMenuReply() {
|
|
|
464
650
|
presentation: buildCodexCommandPickerPresentation("Codex computer-use", "Pick a Codex computer-use action:", buttons)
|
|
465
651
|
};
|
|
466
652
|
}
|
|
653
|
+
/** Returns true when the rest-args are exactly `["menu"]` (case-insensitive). */
|
|
467
654
|
function isMenuVerb(rest) {
|
|
468
655
|
return rest.length === 1 && (rest[0] ?? "").trim().toLowerCase() === "menu";
|
|
469
656
|
}
|
|
470
|
-
function
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
tokenStarted = true;
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
if (quote) {
|
|
490
|
-
if (char === quote) quote = void 0;
|
|
491
|
-
else current += char;
|
|
492
|
-
tokenStarted = true;
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
if (char === "\"" || char === "'") {
|
|
496
|
-
quote = char;
|
|
497
|
-
tokenStarted = true;
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
if (/\s/.test(char)) {
|
|
501
|
-
if (tokenStarted) {
|
|
502
|
-
args.push(current);
|
|
503
|
-
current = "";
|
|
504
|
-
tokenStarted = false;
|
|
505
|
-
}
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
current += char;
|
|
509
|
-
tokenStarted = true;
|
|
657
|
+
async function handleCodexSubcommand(ctx, options) {
|
|
658
|
+
const deps = {
|
|
659
|
+
...defaultCodexCommandDeps,
|
|
660
|
+
...options.deps
|
|
661
|
+
};
|
|
662
|
+
const args = splitArgs(ctx.args);
|
|
663
|
+
if (args.length === 0) return buildCodexSubcommandPickerReply();
|
|
664
|
+
const [subcommand = "status", ...rest] = args;
|
|
665
|
+
const normalized = subcommand.toLowerCase();
|
|
666
|
+
if (normalized === "help") return { text: buildHelp() };
|
|
667
|
+
if (CODEX_NATIVE_CONTROL_SUBCOMMANDS.has(normalized) && !returnsBeforeNativeCodexExecution(normalized, rest) && !canMutateCodexHost(ctx)) return { text: CODEX_NATIVE_EXECUTION_AUTH_ERROR };
|
|
668
|
+
const sandboxBlock = resolveCodexNativeCommandSandboxBlock(ctx, normalized, rest);
|
|
669
|
+
if (sandboxBlock) return { text: sandboxBlock };
|
|
670
|
+
if (normalized === "plugins") {
|
|
671
|
+
if (!deps.codexPluginsManagementIo) return { text: "Codex sub-plugin management is not wired up (codexPluginsManagementIo dep is undefined). Edit ~/.openclaw/openclaw.json or use `openclaw config patch` until the runtime exposes the IO." };
|
|
672
|
+
return await handleCodexPluginsSubcommand(ctx, rest, deps.codexPluginsManagementIo);
|
|
510
673
|
}
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
function parseBindArgs(args) {
|
|
516
|
-
const parsed = {};
|
|
517
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
518
|
-
const arg = expectDefined(args[index], "current Codex bind argument");
|
|
519
|
-
if (arg === "--help" || arg === "-h") {
|
|
520
|
-
parsed.help = true;
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
if (arg === "--cwd") {
|
|
524
|
-
const value = readRequiredOptionValue(args, index);
|
|
525
|
-
if (!value || parsed.cwd !== void 0) {
|
|
526
|
-
parsed.help = true;
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
parsed.cwd = value;
|
|
530
|
-
index += 1;
|
|
531
|
-
continue;
|
|
532
|
-
}
|
|
533
|
-
if (arg === "--model") {
|
|
534
|
-
const value = readRequiredOptionValue(args, index);
|
|
535
|
-
if (!value || parsed.model !== void 0) {
|
|
536
|
-
parsed.help = true;
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
parsed.model = value;
|
|
540
|
-
index += 1;
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
if (arg === "--provider" || arg === "--model-provider") {
|
|
544
|
-
const value = readRequiredOptionValue(args, index);
|
|
545
|
-
if (!value || parsed.provider !== void 0) {
|
|
546
|
-
parsed.help = true;
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
parsed.provider = value;
|
|
550
|
-
index += 1;
|
|
551
|
-
continue;
|
|
552
|
-
}
|
|
553
|
-
if (!arg.startsWith("-") && !parsed.threadId) {
|
|
554
|
-
parsed.threadId = arg;
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
parsed.help = true;
|
|
674
|
+
if (normalized === "status") {
|
|
675
|
+
if (rest.length > 0) return { text: "Usage: /codex status" };
|
|
676
|
+
return { text: formatCodexStatus(await deps.readCodexStatusProbes(options.pluginConfig, ctx.config)) };
|
|
558
677
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
parsed.provider = normalizeOptionalString(parsed.provider);
|
|
563
|
-
return parsed;
|
|
564
|
-
}
|
|
565
|
-
function parseCodexCliSessionsArgs(args) {
|
|
566
|
-
const parsed = { filter: "" };
|
|
567
|
-
const filter = [];
|
|
568
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
569
|
-
const arg = expectDefined(args[index], "current Codex sessions argument");
|
|
570
|
-
if (arg === "--help" || arg === "-h") {
|
|
571
|
-
parsed.help = true;
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
if (arg === "--host" || arg === "--node") {
|
|
575
|
-
const value = readRequiredOptionValue(args, index);
|
|
576
|
-
if (!value || parsed.host !== void 0) {
|
|
577
|
-
parsed.help = true;
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
parsed.host = value;
|
|
581
|
-
index += 1;
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
if (arg === "--limit") {
|
|
585
|
-
const parsedLimit = parseStrictPositiveInteger(readRequiredOptionValue(args, index));
|
|
586
|
-
if (parsedLimit === void 0) {
|
|
587
|
-
parsed.help = true;
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
parsed.limit = parsedLimit;
|
|
591
|
-
index += 1;
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
if (arg.startsWith("-")) {
|
|
595
|
-
parsed.help = true;
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
filter.push(arg);
|
|
678
|
+
if (normalized === "models") {
|
|
679
|
+
if (rest.length > 0) return { text: "Usage: /codex models" };
|
|
680
|
+
return { text: formatModels(await deps.listCodexAppServerModels(deps.requestOptions(options.pluginConfig, 100, ctx.config))) };
|
|
599
681
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return
|
|
682
|
+
if (normalized === "threads") return { text: await buildThreads(deps, ctx, options.pluginConfig, rest.join(" ")) };
|
|
683
|
+
if (normalized === "sessions") return { text: await buildCodexCliSessions(deps, rest) };
|
|
684
|
+
if (normalized === "resume") return { text: await resumeThread(deps, ctx, options.pluginConfig, rest) };
|
|
685
|
+
if (normalized === "bind") return await bindConversation(deps, ctx, options.pluginConfig, rest);
|
|
686
|
+
if (normalized === "detach" || normalized === "unbind") {
|
|
687
|
+
if (rest.length > 0) return { text: "Usage: /codex detach" };
|
|
688
|
+
return { text: await detachConversation(deps, ctx) };
|
|
689
|
+
}
|
|
690
|
+
if (normalized === "binding") {
|
|
691
|
+
if (rest.length > 0) return { text: "Usage: /codex binding" };
|
|
692
|
+
return { text: await describeConversationBinding(deps, ctx) };
|
|
693
|
+
}
|
|
694
|
+
if (normalized === "stop") {
|
|
695
|
+
if (rest.length > 0) return { text: "Usage: /codex stop" };
|
|
696
|
+
return { text: await stopConversationTurn(deps, ctx, options.pluginConfig) };
|
|
697
|
+
}
|
|
698
|
+
if (normalized === "steer") return { text: await steerConversationTurn(deps, ctx, options.pluginConfig, rest.join(" ")) };
|
|
699
|
+
if (normalized === "model") return { text: await setConversationModel(deps, ctx, options.pluginConfig, rest) };
|
|
700
|
+
if (normalized === "fast") {
|
|
701
|
+
if (isMenuVerb(rest)) return buildCodexFastMenuReply();
|
|
702
|
+
return { text: await setConversationFastMode(deps, ctx, rest) };
|
|
703
|
+
}
|
|
704
|
+
if (normalized === "permissions") {
|
|
705
|
+
if (isMenuVerb(rest)) return buildCodexPermissionsMenuReply();
|
|
706
|
+
return { text: await setConversationPermissions(deps, ctx, rest) };
|
|
707
|
+
}
|
|
708
|
+
if (normalized === "compact") return { text: await startThreadAction(deps, ctx, options.pluginConfig, "compact", rest) };
|
|
709
|
+
if (normalized === "review") return { text: await startThreadAction(deps, ctx, options.pluginConfig, "review", rest) };
|
|
710
|
+
if (normalized === "diagnostics") return await handleCodexDiagnosticsFeedback(deps, ctx, options.pluginConfig, rest.join(" "), "/codex diagnostics");
|
|
711
|
+
if (normalized === "computer-use" || normalized === "computeruse") {
|
|
712
|
+
if (isMenuVerb(rest)) return buildCodexComputerUseMenuReply();
|
|
713
|
+
return { text: await handleComputerUseCommand(deps, ctx, options.pluginConfig, rest) };
|
|
714
|
+
}
|
|
715
|
+
if (normalized === "mcp") {
|
|
716
|
+
if (rest.length > 0) return { text: "Usage: /codex mcp" };
|
|
717
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
718
|
+
return { text: formatList(await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, {
|
|
719
|
+
config: ctx.config,
|
|
720
|
+
...scope
|
|
721
|
+
}), "MCP servers") };
|
|
722
|
+
}
|
|
723
|
+
if (normalized === "skills") {
|
|
724
|
+
if (rest.length > 0) return { text: "Usage: /codex skills" };
|
|
725
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
726
|
+
return { text: formatSkills(await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, {
|
|
727
|
+
config: ctx.config,
|
|
728
|
+
...scope
|
|
729
|
+
})) };
|
|
730
|
+
}
|
|
731
|
+
if (normalized === "account") {
|
|
732
|
+
if (rest.length > 0) return { text: "Usage: /codex account" };
|
|
733
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
734
|
+
const requestScope = {
|
|
735
|
+
config: ctx.config,
|
|
736
|
+
...scope
|
|
737
|
+
};
|
|
738
|
+
const [account, limits] = await Promise.all([deps.safeCodexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, requestScope), deps.safeCodexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, requestScope)]);
|
|
739
|
+
return { text: formatAccount(account, limits, await readCodexAccountAuthOverview({
|
|
740
|
+
ctx,
|
|
741
|
+
pluginConfig: options.pluginConfig,
|
|
742
|
+
safeCodexControlRequest: deps.safeCodexControlRequest,
|
|
743
|
+
account,
|
|
744
|
+
limits
|
|
745
|
+
})) };
|
|
746
|
+
}
|
|
747
|
+
return { text: `Unknown Codex command: ${formatCodexDisplayText(subcommand)}\n\n${buildHelp()}` };
|
|
603
748
|
}
|
|
604
|
-
function
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
parsed.help = true;
|
|
749
|
+
function resolveCodexNativeCommandSandboxBlock(ctx, subcommand, args) {
|
|
750
|
+
if (!CODEX_NATIVE_EXECUTION_SUBCOMMANDS.has(subcommand)) return;
|
|
751
|
+
if (returnsBeforeNativeCodexExecution(subcommand, args)) return;
|
|
752
|
+
if (isCodexCliNodeResumeBind(subcommand, args)) return resolveCodexNativeSandboxBlock({
|
|
753
|
+
config: ctx.config,
|
|
754
|
+
sessionKey: ctx.sessionKey,
|
|
755
|
+
sessionId: ctx.sessionId,
|
|
756
|
+
surface: `/${["codex", subcommand].join(" ")}`
|
|
757
|
+
});
|
|
758
|
+
return resolveCodexNativeExecutionBlock({
|
|
759
|
+
config: ctx.config,
|
|
760
|
+
agentId: ctx.agentId,
|
|
761
|
+
sessionKey: ctx.sessionKey,
|
|
762
|
+
sessionId: ctx.sessionId,
|
|
763
|
+
surface: `/${["codex", subcommand].join(" ")}`
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
function returnsBeforeNativeCodexExecution(subcommand, args) {
|
|
767
|
+
switch (subcommand) {
|
|
768
|
+
case "bind": return parseBindArgs([...args]).help === true;
|
|
769
|
+
case "resume": return returnsBeforeNativeCodexResume(args);
|
|
770
|
+
case "steer": return args.join(" ").trim() === "";
|
|
771
|
+
case "model": return args.length === 0 || args.length > 1;
|
|
772
|
+
case "fast": return args.length === 0 || args.length > 1 || parseCodexFastModeArg(args[0]) === void 0;
|
|
773
|
+
case "permissions": return args.length === 0 || args.length > 1 || parseCodexPermissionsModeArg(args[0]) === void 0;
|
|
774
|
+
case "compact":
|
|
775
|
+
case "review":
|
|
776
|
+
case "detach":
|
|
777
|
+
case "unbind":
|
|
778
|
+
case "stop": return args.length > 0;
|
|
779
|
+
default: return false;
|
|
636
780
|
}
|
|
637
|
-
parsed.threadId = normalizeOptionalString(parsed.threadId);
|
|
638
|
-
parsed.host = normalizeOptionalString(parsed.host);
|
|
639
|
-
return parsed;
|
|
640
781
|
}
|
|
641
|
-
function
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
782
|
+
function isCodexCliNodeResumeBind(subcommand, args) {
|
|
783
|
+
if (subcommand !== "resume") return false;
|
|
784
|
+
const parsed = parseResumeArgs([...args]);
|
|
785
|
+
return Boolean(parsed.host && parsed.threadId && parsed.bindHere === true && !parsed.help);
|
|
786
|
+
}
|
|
787
|
+
function returnsBeforeNativeCodexResume(args) {
|
|
788
|
+
const parsed = parseResumeArgs([...args]);
|
|
789
|
+
const normalizedThreadId = parsed.threadId?.trim();
|
|
790
|
+
if (parsed.help) return true;
|
|
791
|
+
if (parsed.host) return !normalizedThreadId || parsed.bindHere !== true;
|
|
792
|
+
return !normalizedThreadId || args.length !== 1;
|
|
793
|
+
}
|
|
794
|
+
async function handleComputerUseCommand(deps, ctx, pluginConfig, args) {
|
|
795
|
+
const parsed = parseComputerUseArgs(args);
|
|
796
|
+
if (parsed.help) return ["Usage: /codex computer-use [status|install] [--source <marketplace-source>] [--marketplace-path <path>] [--marketplace <name>]", "Checks or installs the configured Codex Computer Use plugin through app-server."].join("\n");
|
|
797
|
+
if (parsed.action === "install" && !canMutateCodexHost(ctx)) return "Only an owner or operator.admin gateway client can configure Codex Computer Use.";
|
|
798
|
+
const params = {
|
|
799
|
+
pluginConfig,
|
|
800
|
+
forceEnable: parsed.action === "install" || parsed.hasOverrides,
|
|
801
|
+
...Object.keys(parsed.overrides).length > 0 ? { overrides: parsed.overrides } : {}
|
|
647
802
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const arg = args[index];
|
|
651
|
-
if (arg === "--help" || arg === "-h") {
|
|
652
|
-
parsed.help = true;
|
|
653
|
-
continue;
|
|
654
|
-
}
|
|
655
|
-
if (arg === "status" || arg === "install") {
|
|
656
|
-
if (sawAction) {
|
|
657
|
-
parsed.help = true;
|
|
658
|
-
continue;
|
|
659
|
-
}
|
|
660
|
-
sawAction = true;
|
|
661
|
-
parsed.action = arg;
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
if (arg === "--source" || arg === "--marketplace-source") {
|
|
665
|
-
const value = readRequiredOptionValue(args, index);
|
|
666
|
-
if (!value || parsed.overrides.marketplaceSource !== void 0) {
|
|
667
|
-
parsed.help = true;
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
parsed.overrides.marketplaceSource = value;
|
|
671
|
-
index += 1;
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
if (arg === "--marketplace-path" || arg === "--path") {
|
|
675
|
-
const value = readRequiredOptionValue(args, index);
|
|
676
|
-
if (!value || parsed.overrides.marketplacePath !== void 0) {
|
|
677
|
-
parsed.help = true;
|
|
678
|
-
continue;
|
|
679
|
-
}
|
|
680
|
-
parsed.overrides.marketplacePath = value;
|
|
681
|
-
index += 1;
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
if (arg === "--marketplace") {
|
|
685
|
-
const value = readRequiredOptionValue(args, index);
|
|
686
|
-
if (!value || parsed.overrides.marketplaceName !== void 0) {
|
|
687
|
-
parsed.help = true;
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
parsed.overrides.marketplaceName = value;
|
|
691
|
-
index += 1;
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
if (arg === "--plugin" || arg === "--server" || arg === "--mcp-server") {
|
|
695
|
-
const value = readRequiredOptionValue(args, index);
|
|
696
|
-
const configKey = arg === "--plugin" ? "pluginName" : "mcpServerName";
|
|
697
|
-
if (!value || parsed.persistentIdentity[configKey] !== void 0) {
|
|
698
|
-
parsed.help = true;
|
|
699
|
-
continue;
|
|
700
|
-
}
|
|
701
|
-
parsed.persistentIdentity[configKey] = value.trim();
|
|
702
|
-
index += 1;
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
parsed.help = true;
|
|
706
|
-
}
|
|
707
|
-
parsed.overrides = normalizeComputerUseStringOverrides(parsed.overrides);
|
|
708
|
-
parsed.hasOverrides = Object.values(parsed.overrides).some(Boolean);
|
|
709
|
-
return parsed;
|
|
710
|
-
}
|
|
711
|
-
function formatComputerUsePersistentIdentityMigration(parsed) {
|
|
712
|
-
const configPrefix = "plugins.entries.codex.config.computerUse";
|
|
713
|
-
const settings = [parsed.persistentIdentity.pluginName ? `${configPrefix}.pluginName = ${JSON.stringify(parsed.persistentIdentity.pluginName)}` : void 0, parsed.persistentIdentity.mcpServerName ? `${configPrefix}.mcpServerName = ${JSON.stringify(parsed.persistentIdentity.mcpServerName)}` : void 0].filter((setting) => Boolean(setting));
|
|
714
|
-
const retryArgs = [
|
|
715
|
-
`/codex computer-use ${parsed.action}`,
|
|
716
|
-
parsed.overrides.marketplaceSource ? `--source ${JSON.stringify(parsed.overrides.marketplaceSource)}` : void 0,
|
|
717
|
-
parsed.overrides.marketplacePath ? `--marketplace-path ${JSON.stringify(parsed.overrides.marketplacePath)}` : void 0,
|
|
718
|
-
parsed.overrides.marketplaceName ? `--marketplace ${JSON.stringify(parsed.overrides.marketplaceName)}` : void 0
|
|
719
|
-
].filter((arg) => Boolean(arg));
|
|
720
|
-
return ["One-off Computer Use plugin/server overrides are no longer supported.", `Set ${settings.join(" and ")} persistently, then rerun ${retryArgs.join(" ")}.`].join(" ");
|
|
721
|
-
}
|
|
722
|
-
function readRequiredOptionValue(args, index) {
|
|
723
|
-
const value = args[index + 1];
|
|
724
|
-
const normalized = value?.trim();
|
|
725
|
-
if (!normalized || normalized.startsWith("-")) return;
|
|
726
|
-
return value;
|
|
727
|
-
}
|
|
728
|
-
function normalizeComputerUseStringOverrides(overrides) {
|
|
729
|
-
const normalized = {};
|
|
730
|
-
const marketplaceSource = normalizeOptionalString(overrides.marketplaceSource);
|
|
731
|
-
if (marketplaceSource) normalized.marketplaceSource = marketplaceSource;
|
|
732
|
-
const marketplacePath = normalizeOptionalString(overrides.marketplacePath);
|
|
733
|
-
if (marketplacePath) normalized.marketplacePath = marketplacePath;
|
|
734
|
-
const marketplaceName = normalizeOptionalString(overrides.marketplaceName);
|
|
735
|
-
if (marketplaceName) normalized.marketplaceName = marketplaceName;
|
|
736
|
-
return normalized;
|
|
803
|
+
if (parsed.action === "install") return formatComputerUseStatus(await deps.installCodexComputerUse(params));
|
|
804
|
+
return formatComputerUseStatus(await deps.readCodexComputerUseStatus(params));
|
|
737
805
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
const
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
|
|
806
|
+
async function bindConversation(deps, ctx, pluginConfig, args) {
|
|
807
|
+
const parsed = parseBindArgs(args);
|
|
808
|
+
if (parsed.help) return { text: "Usage: /codex bind [thread-id] [--cwd <path>] [--model <model>] [--provider <provider>]" };
|
|
809
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
810
|
+
const workspaceDir = parsed.cwd ?? deps.resolveCodexDefaultWorkspaceDir(pluginConfig);
|
|
811
|
+
const currentConversation = await ctx.getCurrentConversationBinding();
|
|
812
|
+
const currentConversationData = readCodexConversationBindingData(currentConversation);
|
|
813
|
+
const bindingId = currentConversationData?.kind === "codex-app-server-session" ? currentConversationData.bindingId : currentConversation ? `conversation-${currentConversation.bindingId}` : void 0;
|
|
814
|
+
const sessionOwner = ctx.sessionId ? sessionBindingIdentity({
|
|
815
|
+
sessionId: ctx.sessionId,
|
|
816
|
+
sessionKey: ctx.sessionKey,
|
|
817
|
+
agentId: scope.agentId,
|
|
818
|
+
config: ctx.config
|
|
819
|
+
}) : void 0;
|
|
820
|
+
const currentOwner = currentConversationData?.kind === "codex-app-server-session" ? conversationBindingIdentity(currentConversationData.bindingId) : sessionOwner;
|
|
821
|
+
const existingBinding = currentOwner ? await deps.bindingStore.read(currentOwner) : void 0;
|
|
822
|
+
const sessionSource = sessionOwner && existingBinding ? {
|
|
823
|
+
agentId: sessionOwner.agentId,
|
|
824
|
+
sessionId: sessionOwner.sessionId,
|
|
825
|
+
threadId: existingBinding.threadId,
|
|
826
|
+
...sessionOwner.sessionKey ? { sessionKey: sessionOwner.sessionKey } : {}
|
|
827
|
+
} : void 0;
|
|
828
|
+
const authProfileId = existingBinding?.authProfileId;
|
|
829
|
+
const data = createCodexConversationBindingData({
|
|
830
|
+
bindingId,
|
|
831
|
+
workspaceDir,
|
|
832
|
+
agentId: scope.agentId,
|
|
833
|
+
agentDir: scope.agentDir,
|
|
834
|
+
source: currentConversationData?.kind === "codex-app-server-session" ? currentConversationData.source : sessionSource,
|
|
835
|
+
start: {
|
|
836
|
+
id: crypto.randomUUID(),
|
|
837
|
+
threadId: parsed.threadId,
|
|
838
|
+
model: parsed.model,
|
|
839
|
+
modelProvider: parsed.provider,
|
|
840
|
+
authProfileId
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
const threadLabel = parsed.threadId ?? "a new thread";
|
|
844
|
+
const request = await ctx.requestConversationBinding({
|
|
845
|
+
summary: `Codex app-server thread ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}`,
|
|
846
|
+
detachHint: "/codex detach",
|
|
847
|
+
data
|
|
848
|
+
});
|
|
849
|
+
if (request.status === "pending") return request.reply;
|
|
850
|
+
if (request.status === "error") return { text: formatCodexDisplayText(request.message) };
|
|
851
|
+
return { text: `Bound this conversation to ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}. The next message will initialize it.` };
|
|
755
852
|
}
|
|
756
|
-
function
|
|
757
|
-
const
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
};
|
|
763
|
-
if ((normalizedAction === "cancel" || normalizedAction === "--cancel") && token && extra.length === 0) return {
|
|
764
|
-
action: "cancel",
|
|
765
|
-
token
|
|
766
|
-
};
|
|
767
|
-
if (normalizedAction === "confirm" || normalizedAction === "--confirm" || normalizedAction === "cancel" || normalizedAction === "--cancel") return { action: "usage" };
|
|
768
|
-
return {
|
|
769
|
-
action: "request",
|
|
770
|
-
note: args
|
|
771
|
-
};
|
|
853
|
+
async function detachConversation(deps, ctx) {
|
|
854
|
+
const current = await ctx.getCurrentConversationBinding();
|
|
855
|
+
const data = readCodexConversationBindingData(current);
|
|
856
|
+
const detached = await ctx.detachConversationBinding();
|
|
857
|
+
if (data?.kind === "codex-app-server-session") await deps.bindingStore.mutate(conversationBindingIdentity(data.bindingId), { kind: "clear" });
|
|
858
|
+
return detached.removed ? "Detached this conversation from Codex." : "No Codex conversation binding was attached.";
|
|
772
859
|
}
|
|
773
|
-
function
|
|
860
|
+
async function describeConversationBinding(deps, ctx) {
|
|
861
|
+
const current = await ctx.getCurrentConversationBinding();
|
|
862
|
+
const data = readCodexConversationBindingData(current);
|
|
863
|
+
if (!current || !data) return "No Codex conversation binding is attached.";
|
|
864
|
+
if (data.kind === "codex-cli-node-session") return [
|
|
865
|
+
"Codex conversation binding:",
|
|
866
|
+
"- Mode: Codex CLI node session",
|
|
867
|
+
`- Node: ${formatCodexDisplayText(data.nodeId)}`,
|
|
868
|
+
`- Session: ${formatCodexDisplayText(data.sessionId)}`,
|
|
869
|
+
`- Workspace: ${formatCodexDisplayText(data.cwd ?? "unknown")}`,
|
|
870
|
+
"- Active run: not tracked"
|
|
871
|
+
].join("\n");
|
|
872
|
+
const identity = conversationBindingIdentity(data.bindingId);
|
|
873
|
+
const threadBinding = await deps.bindingStore.read(identity);
|
|
874
|
+
const active = deps.readCodexConversationActiveTurn(identity);
|
|
774
875
|
return [
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
876
|
+
"Codex conversation binding:",
|
|
877
|
+
`- Thread: ${formatCodexDisplayText(threadBinding?.threadId ?? "unknown")}`,
|
|
878
|
+
`- Workspace: ${formatCodexDisplayText(data.workspaceDir)}`,
|
|
879
|
+
`- Model: ${formatCodexDisplayText(threadBinding?.model ?? "default")}`,
|
|
880
|
+
`- Fast: ${isCodexFastServiceTier(threadBinding?.serviceTier) ? "on" : "off"}`,
|
|
881
|
+
`- Permissions: ${threadBinding ? formatPermissionsMode(threadBinding) : "default"}`,
|
|
882
|
+
`- Active run: ${formatCodexDisplayText(active ? active.turnId : "none")}`,
|
|
883
|
+
`- Binding: ${formatCodexDisplayText(data.bindingId)}`
|
|
778
884
|
].join("\n");
|
|
779
885
|
}
|
|
780
|
-
function
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const oldestToken = scopeTokens.shift();
|
|
789
|
-
if (!oldestToken) break;
|
|
790
|
-
pendingCodexDiagnosticsConfirmations.delete(oldestToken);
|
|
791
|
-
}
|
|
792
|
-
const token = crypto.randomBytes(6).toString("hex");
|
|
793
|
-
scopeTokens.push(token);
|
|
794
|
-
pendingCodexDiagnosticsConfirmationTokensByScope.set(params.scopeKey, scopeTokens);
|
|
795
|
-
pendingCodexDiagnosticsConfirmations.set(token, {
|
|
796
|
-
token,
|
|
797
|
-
targets: params.targets,
|
|
798
|
-
note: params.note,
|
|
799
|
-
senderId: params.senderId,
|
|
800
|
-
channel: params.channel,
|
|
801
|
-
accountId: params.accountId,
|
|
802
|
-
channelId: params.channelId,
|
|
803
|
-
messageThreadId: params.messageThreadId,
|
|
804
|
-
threadParentId: params.threadParentId,
|
|
805
|
-
sessionKey: params.sessionKey,
|
|
806
|
-
scopeKey: params.scopeKey,
|
|
807
|
-
...params.privateRouted === void 0 ? {} : { privateRouted: params.privateRouted },
|
|
808
|
-
createdAt: params.now
|
|
886
|
+
async function buildThreads(deps, ctx, pluginConfig, filter) {
|
|
887
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
888
|
+
const response = await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
|
|
889
|
+
limit: 10,
|
|
890
|
+
...filter.trim() ? { searchTerm: filter.trim() } : {}
|
|
891
|
+
}, {
|
|
892
|
+
config: ctx.config,
|
|
893
|
+
...scope
|
|
809
894
|
});
|
|
810
|
-
return
|
|
811
|
-
}
|
|
812
|
-
function readCodexDiagnosticsConfirmationScope(ctx) {
|
|
813
|
-
return {
|
|
814
|
-
accountId: normalizeCodexDiagnosticsScopeField(ctx.accountId),
|
|
815
|
-
channelId: normalizeCodexDiagnosticsScopeField(ctx.channelId),
|
|
816
|
-
messageThreadId: typeof ctx.messageThreadId === "string" || typeof ctx.messageThreadId === "number" ? normalizeCodexDiagnosticsScopeField(String(ctx.messageThreadId)) : void 0,
|
|
817
|
-
threadParentId: normalizeCodexDiagnosticsScopeField(ctx.threadParentId),
|
|
818
|
-
sessionKey: normalizeCodexDiagnosticsScopeField(ctx.sessionKey)
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
function readCodexDiagnosticsScopeMismatch(pending, ctx) {
|
|
822
|
-
const current = readCodexDiagnosticsConfirmationScope(ctx);
|
|
823
|
-
if (pending.accountId !== current.accountId) return {
|
|
824
|
-
confirmMessage: "This Codex diagnostics confirmation belongs to a different account.",
|
|
825
|
-
cancelMessage: "This Codex diagnostics confirmation belongs to a different account."
|
|
826
|
-
};
|
|
827
|
-
if (pending.privateRouted) return;
|
|
828
|
-
if (pending.channelId !== current.channelId) return {
|
|
829
|
-
confirmMessage: "This Codex diagnostics confirmation belongs to a different channel instance.",
|
|
830
|
-
cancelMessage: "This Codex diagnostics confirmation belongs to a different channel instance."
|
|
831
|
-
};
|
|
832
|
-
if (pending.messageThreadId !== current.messageThreadId) return {
|
|
833
|
-
confirmMessage: "This Codex diagnostics confirmation belongs to a different thread.",
|
|
834
|
-
cancelMessage: "This Codex diagnostics confirmation belongs to a different thread."
|
|
835
|
-
};
|
|
836
|
-
if (pending.threadParentId !== current.threadParentId) return {
|
|
837
|
-
confirmMessage: "This Codex diagnostics confirmation belongs to a different parent thread.",
|
|
838
|
-
cancelMessage: "This Codex diagnostics confirmation belongs to a different parent thread."
|
|
839
|
-
};
|
|
840
|
-
if (pending.sessionKey !== current.sessionKey) return {
|
|
841
|
-
confirmMessage: "This Codex diagnostics confirmation belongs to a different session.",
|
|
842
|
-
cancelMessage: "This Codex diagnostics confirmation belongs to a different session."
|
|
843
|
-
};
|
|
895
|
+
return formatThreads(response);
|
|
844
896
|
}
|
|
845
|
-
function
|
|
846
|
-
|
|
847
|
-
|
|
897
|
+
async function buildCodexCliSessions(deps, args) {
|
|
898
|
+
const parsed = parseCodexCliSessionsArgs(args);
|
|
899
|
+
if (parsed.help || !parsed.host) return "Usage: /codex sessions --host <node> [filter] [--limit <n>]";
|
|
900
|
+
return formatCodexCliSessions(await deps.listCodexCliSessionsOnNode({
|
|
901
|
+
requestedNode: parsed.host,
|
|
902
|
+
filter: parsed.filter,
|
|
903
|
+
limit: parsed.limit
|
|
904
|
+
}));
|
|
848
905
|
}
|
|
849
|
-
function
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
if (
|
|
853
|
-
|
|
854
|
-
if (!
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
906
|
+
async function resumeThread(deps, ctx, pluginConfig, args) {
|
|
907
|
+
const parsed = parseResumeArgs(args);
|
|
908
|
+
const normalizedThreadId = parsed.threadId?.trim();
|
|
909
|
+
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>";
|
|
910
|
+
if (parsed.host) return await bindCodexCliNodeSession(deps, ctx, parsed);
|
|
911
|
+
if (!normalizedThreadId || args.length !== 1) return "Usage: /codex resume <thread-id>";
|
|
912
|
+
if (!ctx.sessionId) return "Cannot attach a Codex thread because this command did not include an OpenClaw session id.";
|
|
913
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
914
|
+
const identity = sessionBindingIdentity({
|
|
915
|
+
sessionId: ctx.sessionId,
|
|
916
|
+
sessionKey: ctx.sessionKey,
|
|
917
|
+
agentId: scope.agentId,
|
|
918
|
+
config: ctx.config
|
|
919
|
+
});
|
|
920
|
+
return await deps.bindingStore.withLease(identity, async () => {
|
|
921
|
+
if (!await reclaimCurrentCodexSessionGeneration({
|
|
922
|
+
bindingStore: deps.bindingStore,
|
|
923
|
+
identity,
|
|
924
|
+
config: ctx.config
|
|
925
|
+
})) throw new Error(`Codex session generation is no longer current: ${identity.sessionId}`);
|
|
926
|
+
const currentBinding = await deps.bindingStore.read(identity);
|
|
927
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
928
|
+
authProfileId: currentBinding?.authProfileId,
|
|
929
|
+
agentDir: scope.agentDir,
|
|
930
|
+
config: ctx.config
|
|
931
|
+
});
|
|
932
|
+
const response = assertCodexThreadResumeResponse(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
|
|
933
|
+
threadId: normalizedThreadId,
|
|
934
|
+
excludeTurns: true
|
|
935
|
+
}, {
|
|
936
|
+
config: ctx.config,
|
|
937
|
+
agentDir: scope.agentDir,
|
|
938
|
+
authProfileId,
|
|
939
|
+
sessionKey: ctx.sessionKey,
|
|
940
|
+
sessionId: ctx.sessionId
|
|
941
|
+
}));
|
|
942
|
+
const effectiveThreadId = response.thread.id;
|
|
943
|
+
if (effectiveThreadId !== normalizedThreadId) throw new Error(`Codex thread/resume returned ${effectiveThreadId} for ${normalizedThreadId}`);
|
|
944
|
+
const resumedCwd = response.thread.cwd;
|
|
945
|
+
if (typeof resumedCwd !== "string") throw new Error(`Codex thread/resume returned no cwd for ${normalizedThreadId}`);
|
|
946
|
+
const modelProvider = normalizeCodexAppServerBindingModelProvider({
|
|
947
|
+
authProfileId,
|
|
948
|
+
modelProvider: response.modelProvider ?? void 0,
|
|
949
|
+
agentDir: scope.agentDir,
|
|
950
|
+
config: ctx.config
|
|
951
|
+
});
|
|
952
|
+
if (!await deps.bindingStore.mutate(identity, {
|
|
953
|
+
kind: "set",
|
|
954
|
+
binding: {
|
|
955
|
+
threadId: effectiveThreadId,
|
|
956
|
+
cwd: resumedCwd,
|
|
957
|
+
authProfileId,
|
|
958
|
+
model: response.model,
|
|
959
|
+
modelProvider,
|
|
960
|
+
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
961
|
+
}
|
|
962
|
+
})) throw new Error("Codex thread binding changed while attaching the resumed thread.");
|
|
963
|
+
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
|
|
964
|
+
});
|
|
858
965
|
}
|
|
859
|
-
function
|
|
860
|
-
|
|
966
|
+
async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
967
|
+
if (!parsed.threadId || !parsed.host || parsed.bindHere !== true) return "Usage: /codex resume <session-id> --host <node> --bind here";
|
|
968
|
+
const resolved = await deps.resolveCodexCliSessionForBindingOnNode({
|
|
969
|
+
requestedNode: parsed.host,
|
|
970
|
+
sessionId: parsed.threadId
|
|
971
|
+
});
|
|
972
|
+
if (!resolved.session) return `No Codex CLI session ${formatCodexDisplayText(parsed.threadId)} was found on ${formatCodexDisplayText(parsed.host)}.`;
|
|
973
|
+
const nodeId = resolved.node.nodeId;
|
|
974
|
+
if (!nodeId) return "Cannot bind Codex CLI session because the selected node did not include a node id.";
|
|
975
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
976
|
+
const data = createCodexCliNodeConversationBindingData({
|
|
977
|
+
nodeId,
|
|
978
|
+
sessionId: parsed.threadId,
|
|
979
|
+
agentId: scope.agentId,
|
|
980
|
+
cwd: resolved.session?.cwd
|
|
981
|
+
});
|
|
982
|
+
const summary = `Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}`;
|
|
983
|
+
const request = await ctx.requestConversationBinding({
|
|
984
|
+
summary,
|
|
985
|
+
detachHint: "/codex detach",
|
|
986
|
+
data
|
|
987
|
+
});
|
|
988
|
+
if (request.status === "bound") return `Bound this conversation to Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}.`;
|
|
989
|
+
if (request.status === "pending") return request.reply.text ?? "Codex CLI session binding is pending approval.";
|
|
990
|
+
return formatCodexDisplayText(request.message);
|
|
861
991
|
}
|
|
862
|
-
function
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
992
|
+
async function stopConversationTurn(deps, ctx, pluginConfig) {
|
|
993
|
+
const target = await resolveControlTarget(ctx);
|
|
994
|
+
if (!target) return "Cannot stop Codex because this command did not include a stable binding identity.";
|
|
995
|
+
return (await deps.stopCodexConversationTurn({
|
|
996
|
+
identity: target.identity,
|
|
997
|
+
bindingStore: deps.bindingStore,
|
|
998
|
+
pluginConfig,
|
|
999
|
+
agentDir: target.agentDir,
|
|
1000
|
+
config: ctx.config
|
|
1001
|
+
})).message;
|
|
866
1002
|
}
|
|
867
|
-
function
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
target.
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1003
|
+
async function steerConversationTurn(deps, ctx, pluginConfig, message) {
|
|
1004
|
+
const target = await resolveControlTarget(ctx);
|
|
1005
|
+
if (!target) return "Cannot steer Codex because this command did not include a stable binding identity.";
|
|
1006
|
+
return (await deps.steerCodexConversationTurn({
|
|
1007
|
+
identity: target.identity,
|
|
1008
|
+
bindingStore: deps.bindingStore,
|
|
1009
|
+
message,
|
|
1010
|
+
pluginConfig,
|
|
1011
|
+
agentDir: target.agentDir,
|
|
1012
|
+
config: ctx.config
|
|
1013
|
+
})).message;
|
|
878
1014
|
}
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
lines.push("Could not send Codex diagnostics:");
|
|
889
|
-
lines.push(...failed.map(({ target, error }) => `${formatCodexDiagnosticsTargetLine(target)}: ${formatCodexErrorForDisplay(error)}`));
|
|
890
|
-
lines.push("Inspect locally:");
|
|
891
|
-
lines.push(...failed.map(({ target }) => `- ${formatCodexResumeCommandForDisplay(target.threadId)}`));
|
|
1015
|
+
async function setConversationModel(deps, ctx, pluginConfig, args) {
|
|
1016
|
+
if (args.length > 1) return "Usage: /codex model <model>";
|
|
1017
|
+
const target = await resolveControlTarget(ctx);
|
|
1018
|
+
if (!target) return "Cannot set Codex model because this command did not include a stable binding identity.";
|
|
1019
|
+
const [model = ""] = args;
|
|
1020
|
+
const normalized = model.trim();
|
|
1021
|
+
if (!normalized) {
|
|
1022
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1023
|
+
return binding?.model ? `Codex model: ${formatCodexDisplayText(binding.model)}` : "Usage: /codex model <model>";
|
|
892
1024
|
}
|
|
893
|
-
return
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1025
|
+
return await deps.setCodexConversationModel({
|
|
1026
|
+
identity: target.identity,
|
|
1027
|
+
bindingStore: deps.bindingStore,
|
|
1028
|
+
pluginConfig,
|
|
1029
|
+
model: normalized,
|
|
1030
|
+
agentDir: target.agentDir,
|
|
1031
|
+
config: ctx.config
|
|
899
1032
|
});
|
|
900
1033
|
}
|
|
901
|
-
function
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
if (target
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
return
|
|
1034
|
+
async function setConversationFastMode(deps, ctx, args) {
|
|
1035
|
+
if (args.length > 1) return "Usage: /codex fast [on|off|status]";
|
|
1036
|
+
const target = await resolveControlTarget(ctx);
|
|
1037
|
+
if (!target) return "Cannot set Codex fast mode because this command did not include a stable binding identity.";
|
|
1038
|
+
const value = args[0];
|
|
1039
|
+
const parsed = parseCodexFastModeArg(value);
|
|
1040
|
+
if (value && parsed == null && value.trim().toLowerCase() !== "status") return "Usage: /codex fast [on|off|status]";
|
|
1041
|
+
return await deps.setCodexConversationFastMode({
|
|
1042
|
+
identity: target.identity,
|
|
1043
|
+
bindingStore: deps.bindingStore,
|
|
1044
|
+
enabled: parsed
|
|
1045
|
+
});
|
|
909
1046
|
}
|
|
910
|
-
function
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1047
|
+
async function setConversationPermissions(deps, ctx, args) {
|
|
1048
|
+
if (args.length > 1) return "Usage: /codex permissions [default|yolo|status]";
|
|
1049
|
+
const target = await resolveControlTarget(ctx);
|
|
1050
|
+
if (!target) return "Cannot set Codex permissions because this command did not include a stable binding identity.";
|
|
1051
|
+
const value = args[0];
|
|
1052
|
+
const parsed = parseCodexPermissionsModeArg(value);
|
|
1053
|
+
if (value && !parsed && value.trim().toLowerCase() !== "status") return "Usage: /codex permissions [default|yolo|status]";
|
|
1054
|
+
return await deps.setCodexConversationPermissions({
|
|
1055
|
+
identity: target.identity,
|
|
1056
|
+
bindingStore: deps.bindingStore,
|
|
1057
|
+
mode: parsed
|
|
1058
|
+
});
|
|
917
1059
|
}
|
|
918
|
-
function
|
|
919
|
-
|
|
1060
|
+
async function resolveControlTarget(ctx) {
|
|
1061
|
+
const binding = await ctx.getCurrentConversationBinding();
|
|
1062
|
+
const data = readCodexConversationBindingData(binding);
|
|
1063
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
1064
|
+
if (data?.kind === "codex-app-server-session") return {
|
|
1065
|
+
identity: conversationBindingIdentity(data.bindingId),
|
|
1066
|
+
agentId: data.agentId ?? scope.agentId,
|
|
1067
|
+
agentDir: data.agentDir ?? scope.agentDir,
|
|
1068
|
+
requestedAuthProfileId: data.start?.authProfileId
|
|
1069
|
+
};
|
|
1070
|
+
return ctx.sessionId ? {
|
|
1071
|
+
identity: sessionBindingIdentity({
|
|
1072
|
+
sessionId: ctx.sessionId,
|
|
1073
|
+
sessionKey: ctx.sessionKey,
|
|
1074
|
+
agentId: scope.agentId,
|
|
1075
|
+
config: ctx.config
|
|
1076
|
+
}),
|
|
1077
|
+
agentId: scope.agentId,
|
|
1078
|
+
agentDir: scope.agentDir
|
|
1079
|
+
} : void 0;
|
|
920
1080
|
}
|
|
921
|
-
function
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1081
|
+
async function resolveCommandAppServerScope(deps, ctx) {
|
|
1082
|
+
const target = await resolveControlTarget(ctx);
|
|
1083
|
+
const fallback = resolveCodexConversationControlScope(ctx);
|
|
1084
|
+
const agentDir = target?.agentDir ?? fallback.agentDir;
|
|
1085
|
+
const binding = target ? await deps.bindingStore.read(target.identity) : void 0;
|
|
1086
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1087
|
+
authProfileId: binding?.authProfileId ?? target?.requestedAuthProfileId,
|
|
1088
|
+
agentDir,
|
|
1089
|
+
config: ctx.config
|
|
1090
|
+
});
|
|
1091
|
+
return {
|
|
1092
|
+
agentId: target?.agentId ?? fallback.agentId,
|
|
1093
|
+
agentDir,
|
|
1094
|
+
...authProfileId ? { authProfileId } : {},
|
|
1095
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
1096
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
1097
|
+
};
|
|
929
1098
|
}
|
|
930
|
-
function
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return `Codex diagnostics were already sent for thread ${formatCodexThreadIdForDisplay(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(options.cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), now);
|
|
939
|
-
if (scopeCooldownMs > 0) return `Codex diagnostics were already sent for this account or channel recently. Try again in ${Math.ceil(scopeCooldownMs / 1e3)}s.`;
|
|
940
|
-
}
|
|
941
|
-
function recordCodexDiagnosticsUpload(threadId, ctx, now, cooldownScope) {
|
|
942
|
-
pruneCodexDiagnosticsCooldowns(now);
|
|
943
|
-
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
944
|
-
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByThread, threadId, CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS, now);
|
|
945
|
-
}
|
|
946
|
-
function readCodexDiagnosticsCooldownScope(ctx) {
|
|
947
|
-
const scope = readCodexDiagnosticsConfirmationScope(ctx);
|
|
948
|
-
const payload = JSON.stringify({
|
|
949
|
-
accountId: scope.accountId ?? null,
|
|
950
|
-
channelId: scope.channelId ?? null,
|
|
951
|
-
sessionKey: scope.sessionKey ?? null,
|
|
952
|
-
messageThreadId: scope.messageThreadId ?? null,
|
|
953
|
-
threadParentId: scope.threadParentId ?? null,
|
|
954
|
-
senderId: normalizeCodexDiagnosticsScopeField(ctx.senderId) ?? null,
|
|
955
|
-
channel: normalizeCodexDiagnosticsScopeField(ctx.channel) ?? ""
|
|
956
|
-
});
|
|
957
|
-
return crypto.createHash("sha256").update(payload).digest("hex");
|
|
958
|
-
}
|
|
959
|
-
function buildDiagnosticsTags(ctx) {
|
|
960
|
-
const tags = { source: CODEX_DIAGNOSTICS_SOURCE };
|
|
961
|
-
addTag(tags, "channel", ctx.channel);
|
|
962
|
-
return tags;
|
|
963
|
-
}
|
|
964
|
-
function addTag(tags, key, value) {
|
|
965
|
-
if (typeof value === "string" && value.trim()) tags[key] = value.trim();
|
|
966
|
-
}
|
|
967
|
-
function formatCodexThreadIdForDisplay(threadId) {
|
|
968
|
-
return escapeCodexChatText(formatCodexTextForDisplay(threadId));
|
|
969
|
-
}
|
|
970
|
-
function formatCodexValueForDisplay(value) {
|
|
971
|
-
return escapeCodexChatText(formatCodexTextForDisplay(value));
|
|
972
|
-
}
|
|
973
|
-
function formatCodexCopyableValueForDisplay(value) {
|
|
974
|
-
const safe = formatCodexTextForDisplay(value);
|
|
975
|
-
if (CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safe)) return `\`${safe}\``;
|
|
976
|
-
return escapeCodexChatText(safe);
|
|
977
|
-
}
|
|
978
|
-
function readCodexDiagnosticsCooldownMs(threadId, now) {
|
|
979
|
-
const lastSentAt = lastCodexDiagnosticsUploadByThread.get(threadId);
|
|
980
|
-
if (!lastSentAt) return 0;
|
|
981
|
-
const remainingMs = Math.max(0, CODEX_DIAGNOSTICS_COOLDOWN_MS - (now - lastSentAt));
|
|
982
|
-
if (remainingMs === 0) lastCodexDiagnosticsUploadByThread.delete(threadId);
|
|
983
|
-
return remainingMs;
|
|
984
|
-
}
|
|
985
|
-
function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
986
|
-
const lastSentAt = lastCodexDiagnosticsUploadByScope.get(scope);
|
|
987
|
-
if (!lastSentAt) return 0;
|
|
988
|
-
const remainingMs = Math.max(0, CODEX_DIAGNOSTICS_COOLDOWN_MS - (now - lastSentAt));
|
|
989
|
-
if (remainingMs === 0) lastCodexDiagnosticsUploadByScope.delete(scope);
|
|
990
|
-
return remainingMs;
|
|
991
|
-
}
|
|
992
|
-
function recordBoundedCodexDiagnosticsCooldown(map, key, maxSize, now) {
|
|
993
|
-
if (!map.has(key)) while (map.size >= maxSize) {
|
|
994
|
-
const oldestKey = map.keys().next().value;
|
|
995
|
-
if (typeof oldestKey !== "string") break;
|
|
996
|
-
map.delete(oldestKey);
|
|
997
|
-
}
|
|
998
|
-
map.set(key, now);
|
|
999
|
-
}
|
|
1000
|
-
function pruneCodexDiagnosticsCooldowns(now) {
|
|
1001
|
-
pruneCodexDiagnosticsCooldownMap(lastCodexDiagnosticsUploadByThread, now);
|
|
1002
|
-
pruneCodexDiagnosticsCooldownMap(lastCodexDiagnosticsUploadByScope, now);
|
|
1003
|
-
}
|
|
1004
|
-
function pruneCodexDiagnosticsCooldownMap(map, now) {
|
|
1005
|
-
for (const [key, lastSentAt] of map) if (now - lastSentAt >= CODEX_DIAGNOSTICS_COOLDOWN_MS) map.delete(key);
|
|
1006
|
-
}
|
|
1007
|
-
function formatCodexErrorForDisplay(error) {
|
|
1008
|
-
return escapeCodexChatText(truncateUtf16Safe(formatCodexTextForDisplay(error), CODEX_DIAGNOSTICS_ERROR_MAX_CHARS)) || "unknown error";
|
|
1009
|
-
}
|
|
1010
|
-
function formatCodexResumeCommandForDisplay(threadId) {
|
|
1011
|
-
const safeThreadId = formatCodexTextForDisplay(threadId);
|
|
1012
|
-
if (!CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safeThreadId)) return "run codex resume and paste the thread id shown above";
|
|
1013
|
-
return `\`codex resume ${safeThreadId}\``;
|
|
1014
|
-
}
|
|
1015
|
-
function isUnsafeDisplayCodePoint(codePoint) {
|
|
1016
|
-
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 173 || codePoint === 1564 || codePoint === 6158 || codePoint >= 8203 && codePoint <= 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8288 && codePoint <= 8303 || codePoint === 65279 || codePoint >= 65529 && codePoint <= 65531 || codePoint >= 917504 && codePoint <= 917631;
|
|
1017
|
-
}
|
|
1018
|
-
function normalizeCodexDiagnosticsScopeField(value) {
|
|
1019
|
-
const normalized = normalizeOptionalString(value);
|
|
1020
|
-
if (!normalized) return;
|
|
1021
|
-
if (normalized.length <= CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS) return normalized;
|
|
1022
|
-
return `sha256:${crypto.createHash("sha256").update(normalized).digest("hex")}`;
|
|
1023
|
-
}
|
|
1024
|
-
//#endregion
|
|
1025
|
-
//#region extensions/codex/src/command-handler-deps.ts
|
|
1026
|
-
const defaultCodexCommandDeps = {
|
|
1027
|
-
codexControlRequest,
|
|
1028
|
-
listCodexAppServerModels: listAllCodexAppServerModels,
|
|
1029
|
-
readCodexStatusProbes,
|
|
1030
|
-
requestOptions,
|
|
1031
|
-
safeCodexControlRequest,
|
|
1032
|
-
readCodexComputerUseStatus,
|
|
1033
|
-
installCodexComputerUse,
|
|
1034
|
-
resolveCodexDefaultWorkspaceDir,
|
|
1035
|
-
readCodexConversationActiveTurn,
|
|
1036
|
-
setCodexConversationFastMode,
|
|
1037
|
-
setCodexConversationModel,
|
|
1038
|
-
setCodexConversationPermissions,
|
|
1039
|
-
steerCodexConversationTurn,
|
|
1040
|
-
stopCodexConversationTurn,
|
|
1041
|
-
listCodexCliSessionsOnNode: async () => {
|
|
1042
|
-
throw new Error("Codex CLI node sessions require Gateway node runtime.");
|
|
1043
|
-
},
|
|
1044
|
-
resolveCodexCliSessionForBindingOnNode: async () => {
|
|
1045
|
-
throw new Error("Codex CLI node sessions require Gateway node runtime.");
|
|
1046
|
-
}
|
|
1047
|
-
};
|
|
1048
|
-
function resolveCodexCommandDeps(overrides) {
|
|
1049
|
-
return {
|
|
1050
|
-
...defaultCodexCommandDeps,
|
|
1051
|
-
...overrides
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
//#endregion
|
|
1055
|
-
//#region extensions/codex/src/command-handler-scope.ts
|
|
1056
|
-
async function resolveControlTarget(ctx) {
|
|
1057
|
-
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
1058
|
-
const scope = resolveCodexConversationControlScope(ctx);
|
|
1059
|
-
if (data?.kind === "codex-app-server-session") return {
|
|
1060
|
-
identity: conversationBindingIdentity(data.bindingId),
|
|
1061
|
-
agentId: data.agentId ?? scope.agentId,
|
|
1062
|
-
agentDir: data.agentDir ?? scope.agentDir,
|
|
1063
|
-
requestedAuthProfileId: data.start?.authProfileId
|
|
1064
|
-
};
|
|
1065
|
-
return ctx.sessionId ? {
|
|
1066
|
-
identity: sessionBindingIdentity({
|
|
1067
|
-
sessionId: ctx.sessionId,
|
|
1068
|
-
sessionKey: ctx.sessionKey,
|
|
1069
|
-
agentId: scope.agentId,
|
|
1070
|
-
config: ctx.config
|
|
1071
|
-
}),
|
|
1072
|
-
agentId: scope.agentId,
|
|
1073
|
-
agentDir: scope.agentDir
|
|
1074
|
-
} : void 0;
|
|
1075
|
-
}
|
|
1076
|
-
async function resolveCommandAppServerScope(deps, ctx, pluginConfig) {
|
|
1077
|
-
const target = await resolveControlTarget(ctx);
|
|
1078
|
-
const fallback = resolveCodexConversationControlScope(ctx);
|
|
1079
|
-
const agentDir = target?.agentDir ?? fallback.agentDir;
|
|
1080
|
-
const binding = target ? await deps.bindingStore.read(target.identity) : void 0;
|
|
1081
|
-
const connection = resolveCodexBindingAppServerConnection({
|
|
1082
|
-
binding,
|
|
1083
|
-
authProfileId: binding?.connectionScope === "supervision" ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
|
|
1084
|
-
authProfileId: binding?.authProfileId ?? target?.requestedAuthProfileId,
|
|
1085
|
-
agentDir,
|
|
1086
|
-
config: ctx.config
|
|
1087
|
-
}),
|
|
1088
|
-
pluginConfig
|
|
1089
|
-
});
|
|
1090
|
-
return {
|
|
1091
|
-
agentId: target?.agentId ?? fallback.agentId,
|
|
1092
|
-
agentDir,
|
|
1093
|
-
...connection.clientAuthProfileId !== void 0 ? { authProfileId: connection.clientAuthProfileId } : {},
|
|
1094
|
-
...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {},
|
|
1095
|
-
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
1096
|
-
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
function conversationBindingIdentity(bindingId) {
|
|
1100
|
-
return {
|
|
1101
|
-
kind: "conversation",
|
|
1102
|
-
bindingId
|
|
1103
|
-
};
|
|
1099
|
+
function conversationBindingIdentity(bindingId) {
|
|
1100
|
+
return {
|
|
1101
|
+
kind: "conversation",
|
|
1102
|
+
bindingId
|
|
1103
|
+
};
|
|
1104
1104
|
}
|
|
1105
1105
|
function resolveCodexConversationControlScope(ctx) {
|
|
1106
1106
|
const { sessionAgentId } = resolveSessionAgentIds({
|
|
@@ -1113,8 +1113,6 @@ function resolveCodexConversationControlScope(ctx) {
|
|
|
1113
1113
|
agentDir: resolveAgentDir(ctx.config, sessionAgentId)
|
|
1114
1114
|
};
|
|
1115
1115
|
}
|
|
1116
|
-
//#endregion
|
|
1117
|
-
//#region extensions/codex/src/command-diagnostics.ts
|
|
1118
1116
|
async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, commandPrefix) {
|
|
1119
1117
|
if (ctx.senderIsOwner !== true) return { text: "Only an owner can send Codex diagnostics." };
|
|
1120
1118
|
const parsed = parseDiagnosticsArgs(args);
|
|
@@ -1241,20 +1239,6 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1241
1239
|
const sent = [];
|
|
1242
1240
|
const failed = [];
|
|
1243
1241
|
for (const target of targets) {
|
|
1244
|
-
let connection;
|
|
1245
|
-
try {
|
|
1246
|
-
connection = resolveCodexBindingAppServerConnection({
|
|
1247
|
-
binding: target,
|
|
1248
|
-
authProfileId: target.authProfileId,
|
|
1249
|
-
pluginConfig
|
|
1250
|
-
});
|
|
1251
|
-
} catch (error) {
|
|
1252
|
-
failed.push({
|
|
1253
|
-
target,
|
|
1254
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1255
|
-
});
|
|
1256
|
-
continue;
|
|
1257
|
-
}
|
|
1258
1242
|
const response = await deps.safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.feedback, {
|
|
1259
1243
|
classification: "bug",
|
|
1260
1244
|
threadId: target.threadId,
|
|
@@ -1264,8 +1248,7 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1264
1248
|
}, {
|
|
1265
1249
|
config: ctx.config,
|
|
1266
1250
|
agentDir: target.agentDir,
|
|
1267
|
-
...
|
|
1268
|
-
...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {},
|
|
1251
|
+
...target.authProfileId ? { authProfileId: target.authProfileId } : {},
|
|
1269
1252
|
...target.sessionId ? { sessionId: target.sessionId } : {},
|
|
1270
1253
|
...target.sessionKey ? { sessionKey: target.sessionKey } : {}
|
|
1271
1254
|
});
|
|
@@ -1348,453 +1331,286 @@ async function resolvePendingCodexDiagnosticsTargets(deps, targets, config) {
|
|
|
1348
1331
|
return resolved;
|
|
1349
1332
|
}
|
|
1350
1333
|
function resolveCodexDiagnosticsTarget(target, binding, config) {
|
|
1351
|
-
const candidate = {
|
|
1352
|
-
identity: target.identity,
|
|
1353
|
-
agentDir: target.agentDir,
|
|
1354
|
-
sessionKey: target.sessionKey,
|
|
1355
|
-
sessionId: target.sessionId,
|
|
1356
|
-
channel: target.channel,
|
|
1357
|
-
channelId: target.channelId,
|
|
1358
|
-
accountId: target.accountId,
|
|
1359
|
-
messageThreadId: target.messageThreadId,
|
|
1360
|
-
threadParentId: target.threadParentId
|
|
1361
|
-
};
|
|
1362
|
-
if (binding.connectionScope === "supervision") return {
|
|
1363
|
-
...candidate,
|
|
1364
|
-
threadId: binding.threadId,
|
|
1365
|
-
connectionScope: binding.connectionScope,
|
|
1366
|
-
appServerRuntimeFingerprint: binding.appServerRuntimeFingerprint,
|
|
1367
|
-
pendingSupervisionBranch: binding.pendingSupervisionBranch
|
|
1368
|
-
};
|
|
1369
1334
|
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1370
1335
|
authProfileId: binding.authProfileId,
|
|
1371
1336
|
agentDir: target.agentDir,
|
|
1372
1337
|
config
|
|
1373
1338
|
});
|
|
1374
1339
|
return {
|
|
1375
|
-
...
|
|
1340
|
+
...target,
|
|
1376
1341
|
threadId: binding.threadId,
|
|
1377
1342
|
authProfileId
|
|
1378
1343
|
};
|
|
1379
1344
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
hydrateSkillPromptRefs: false,
|
|
1390
|
-
readConsistency: "latest"
|
|
1391
|
-
}));
|
|
1345
|
+
function codexDiagnosticsTargetsMatch(expected, actual) {
|
|
1346
|
+
const fingerprint = (target) => JSON.stringify([
|
|
1347
|
+
bindingStoreKey(target.identity),
|
|
1348
|
+
target.threadId,
|
|
1349
|
+
target.authProfileId ?? null
|
|
1350
|
+
]);
|
|
1351
|
+
const expectedTargets = expected.map(fingerprint).toSorted();
|
|
1352
|
+
const actualTargets = actual.map(fingerprint).toSorted();
|
|
1353
|
+
return expectedTargets.length === actualTargets.length && expectedTargets.every((target, index) => target === actualTargets[index]);
|
|
1392
1354
|
}
|
|
1393
|
-
|
|
1394
|
-
const
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
sessionId: sessionOwner.sessionId,
|
|
1414
|
-
threadId: existingBinding.threadId,
|
|
1415
|
-
...sessionOwner.sessionKey ? { sessionKey: sessionOwner.sessionKey } : {}
|
|
1416
|
-
} : void 0;
|
|
1417
|
-
const authProfileId = existingBinding?.authProfileId;
|
|
1418
|
-
const data = createCodexConversationBindingData({
|
|
1419
|
-
bindingId,
|
|
1420
|
-
workspaceDir,
|
|
1421
|
-
agentId: scope.agentId,
|
|
1422
|
-
agentDir: scope.agentDir,
|
|
1423
|
-
source: currentConversationData?.kind === "codex-app-server-session" ? currentConversationData.source : sessionSource,
|
|
1424
|
-
start: {
|
|
1425
|
-
id: crypto.randomUUID(),
|
|
1426
|
-
threadId: parsed.threadId,
|
|
1427
|
-
model: parsed.model,
|
|
1428
|
-
modelProvider: parsed.provider,
|
|
1429
|
-
authProfileId
|
|
1430
|
-
}
|
|
1431
|
-
});
|
|
1432
|
-
const threadLabel = parsed.threadId ?? "a new thread";
|
|
1433
|
-
const request = await ctx.requestConversationBinding({
|
|
1434
|
-
summary: `Codex app-server thread ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}`,
|
|
1435
|
-
detachHint: "/codex detach",
|
|
1436
|
-
data
|
|
1355
|
+
function formatCodexDiagnosticsUploadResult(sent, failed) {
|
|
1356
|
+
const lines = [];
|
|
1357
|
+
if (sent.length > 0) {
|
|
1358
|
+
lines.push("Codex diagnostics sent to OpenAI servers:");
|
|
1359
|
+
lines.push(...formatCodexDiagnosticsTargetLines(sent));
|
|
1360
|
+
lines.push("Included Codex logs and spawned Codex subthreads when available.");
|
|
1361
|
+
}
|
|
1362
|
+
if (failed.length > 0) {
|
|
1363
|
+
if (lines.length > 0) lines.push("");
|
|
1364
|
+
lines.push("Could not send Codex diagnostics:");
|
|
1365
|
+
lines.push(...failed.map(({ target, error }) => `${formatCodexDiagnosticsTargetLine(target)}: ${formatCodexErrorForDisplay(error)}`));
|
|
1366
|
+
lines.push("Inspect locally:");
|
|
1367
|
+
lines.push(...failed.map(({ target }) => `- ${formatCodexResumeCommandForDisplay(target.threadId)}`));
|
|
1368
|
+
}
|
|
1369
|
+
return lines.join("\n");
|
|
1370
|
+
}
|
|
1371
|
+
function formatCodexDiagnosticsTargetLines(targets) {
|
|
1372
|
+
return targets.flatMap((target, index) => {
|
|
1373
|
+
const lines = formatCodexDiagnosticsTargetBlock(target, index);
|
|
1374
|
+
return index < targets.length - 1 ? [...lines, ""] : lines;
|
|
1437
1375
|
});
|
|
1438
|
-
if (request.status === "pending") return request.reply;
|
|
1439
|
-
if (request.status === "error") return { text: formatCodexDisplayText(request.message) };
|
|
1440
|
-
return { text: `Bound this conversation to ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}. The next message will initialize it.` };
|
|
1441
1376
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
if (
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1377
|
+
function formatCodexDiagnosticsTargetBlock(target, index) {
|
|
1378
|
+
const lines = [`Session ${index + 1}`];
|
|
1379
|
+
if (target.channel) lines.push(`Channel: ${formatCodexValueForDisplay(target.channel)}`);
|
|
1380
|
+
if (target.sessionKey) lines.push(`OpenClaw session key: ${formatCodexCopyableValueForDisplay(target.sessionKey)}`);
|
|
1381
|
+
if (target.sessionId) lines.push(`OpenClaw session id: ${formatCodexCopyableValueForDisplay(target.sessionId)}`);
|
|
1382
|
+
lines.push(`Codex thread id: ${formatCodexCopyableValueForDisplay(target.threadId)}`);
|
|
1383
|
+
lines.push(`Inspect locally: ${formatCodexResumeCommandForDisplay(target.threadId)}`);
|
|
1384
|
+
return lines;
|
|
1449
1385
|
}
|
|
1450
|
-
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
if (
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
`- Node: ${formatCodexDisplayText(data.nodeId)}`,
|
|
1458
|
-
`- Session: ${formatCodexDisplayText(data.sessionId)}`,
|
|
1459
|
-
`- Workspace: ${formatCodexDisplayText(data.cwd ?? "unknown")}`,
|
|
1460
|
-
"- Active run: not tracked"
|
|
1461
|
-
].join("\n");
|
|
1462
|
-
const identity = conversationBindingIdentity(data.bindingId);
|
|
1463
|
-
const threadBinding = await deps.bindingStore.read(identity);
|
|
1464
|
-
const active = deps.readCodexConversationActiveTurn(identity);
|
|
1465
|
-
return [
|
|
1466
|
-
"Codex conversation binding:",
|
|
1467
|
-
`- Thread: ${formatCodexDisplayText(threadBinding?.threadId ?? "unknown")}`,
|
|
1468
|
-
`- Workspace: ${formatCodexDisplayText(data.workspaceDir)}`,
|
|
1469
|
-
`- Model: ${formatCodexDisplayText(threadBinding?.model ?? "default")}`,
|
|
1470
|
-
`- Fast: ${isCodexFastServiceTier(threadBinding?.serviceTier) ? "on" : "off"}`,
|
|
1471
|
-
`- Permissions: ${threadBinding ? formatPermissionsMode(threadBinding) : "default"}`,
|
|
1472
|
-
`- Active run: ${formatCodexDisplayText(active ? active.turnId : "none")}`,
|
|
1473
|
-
`- Binding: ${formatCodexDisplayText(data.bindingId)}`
|
|
1474
|
-
].join("\n");
|
|
1386
|
+
function formatCodexDiagnosticsTargetLine(target) {
|
|
1387
|
+
const parts = [];
|
|
1388
|
+
if (target.channel) parts.push(`channel ${formatCodexValueForDisplay(target.channel)}`);
|
|
1389
|
+
const sessionLabel = target.sessionId || target.sessionKey;
|
|
1390
|
+
if (sessionLabel) parts.push(`OpenClaw session ${formatCodexValueForDisplay(sessionLabel)}`);
|
|
1391
|
+
parts.push(`Codex thread ${formatCodexThreadIdForDisplay(target.threadId)}`);
|
|
1392
|
+
return `- ${parts.join(", ")}`;
|
|
1475
1393
|
}
|
|
1476
|
-
|
|
1477
|
-
const
|
|
1478
|
-
return
|
|
1479
|
-
limit: 10,
|
|
1480
|
-
...filter.trim() ? { searchTerm: filter.trim() } : {}
|
|
1481
|
-
}, {
|
|
1482
|
-
config: ctx.config,
|
|
1483
|
-
...scope
|
|
1484
|
-
}));
|
|
1394
|
+
function normalizeDiagnosticsReason(note) {
|
|
1395
|
+
const normalized = normalizeOptionalString(note);
|
|
1396
|
+
return normalized ? normalized.slice(0, CODEX_DIAGNOSTICS_REASON_MAX_CHARS) : void 0;
|
|
1485
1397
|
}
|
|
1486
|
-
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1398
|
+
function parseDiagnosticsArgs(args) {
|
|
1399
|
+
const [action, token, ...extra] = splitArgs(args);
|
|
1400
|
+
const normalizedAction = action?.toLowerCase();
|
|
1401
|
+
if ((normalizedAction === "confirm" || normalizedAction === "--confirm") && token && extra.length === 0) return {
|
|
1402
|
+
action: "confirm",
|
|
1403
|
+
token
|
|
1404
|
+
};
|
|
1405
|
+
if ((normalizedAction === "cancel" || normalizedAction === "--cancel") && token && extra.length === 0) return {
|
|
1406
|
+
action: "cancel",
|
|
1407
|
+
token
|
|
1408
|
+
};
|
|
1409
|
+
if (normalizedAction === "confirm" || normalizedAction === "--confirm" || normalizedAction === "cancel" || normalizedAction === "--cancel") return { action: "usage" };
|
|
1410
|
+
return {
|
|
1411
|
+
action: "request",
|
|
1412
|
+
note: args
|
|
1413
|
+
};
|
|
1494
1414
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
if (isCurrentSessionModelSelectionLocked(ctx)) return MODEL_SELECTION_LOCKED_MESSAGE;
|
|
1502
|
-
if (!ctx.sessionId) return "Cannot attach a Codex thread because this command did not include an OpenClaw session id.";
|
|
1503
|
-
const scope = resolveCodexConversationControlScope(ctx);
|
|
1504
|
-
const identity = sessionBindingIdentity({
|
|
1505
|
-
sessionId: ctx.sessionId,
|
|
1506
|
-
sessionKey: ctx.sessionKey,
|
|
1507
|
-
agentId: scope.agentId,
|
|
1508
|
-
config: ctx.config
|
|
1509
|
-
});
|
|
1510
|
-
return await deps.bindingStore.withLease(identity, async () => {
|
|
1511
|
-
if (!await reclaimCurrentCodexSessionGeneration({
|
|
1512
|
-
bindingStore: deps.bindingStore,
|
|
1513
|
-
identity,
|
|
1514
|
-
config: ctx.config
|
|
1515
|
-
})) throw createCodexSessionGenerationSupersededError(identity.sessionId);
|
|
1516
|
-
const currentBinding = await deps.bindingStore.read(identity);
|
|
1517
|
-
assertCodexBindingMayBeReplaced(currentBinding, "attaching a different resumed thread");
|
|
1518
|
-
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1519
|
-
authProfileId: currentBinding?.authProfileId,
|
|
1520
|
-
agentDir: scope.agentDir,
|
|
1521
|
-
config: ctx.config
|
|
1522
|
-
});
|
|
1523
|
-
const response = assertCodexThreadResumeResponse(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
|
|
1524
|
-
threadId: normalizedThreadId,
|
|
1525
|
-
excludeTurns: true
|
|
1526
|
-
}, {
|
|
1527
|
-
config: ctx.config,
|
|
1528
|
-
agentDir: scope.agentDir,
|
|
1529
|
-
authProfileId,
|
|
1530
|
-
sessionKey: ctx.sessionKey,
|
|
1531
|
-
sessionId: ctx.sessionId
|
|
1532
|
-
}));
|
|
1533
|
-
const effectiveThreadId = response.thread.id;
|
|
1534
|
-
if (effectiveThreadId !== normalizedThreadId) throw new Error(`Codex thread/resume returned ${effectiveThreadId} for ${normalizedThreadId}`);
|
|
1535
|
-
const resumedCwd = response.thread.cwd;
|
|
1536
|
-
if (typeof resumedCwd !== "string") throw new Error(`Codex thread/resume returned no cwd for ${normalizedThreadId}`);
|
|
1537
|
-
const modelProvider = normalizeCodexAppServerBindingModelProvider({
|
|
1538
|
-
authProfileId,
|
|
1539
|
-
modelProvider: response.modelProvider ?? void 0,
|
|
1540
|
-
agentDir: scope.agentDir,
|
|
1541
|
-
config: ctx.config
|
|
1542
|
-
});
|
|
1543
|
-
assertCodexBindingMayBeReplaced(await deps.bindingStore.read(identity), "committing a different resumed thread");
|
|
1544
|
-
if (!await deps.bindingStore.mutate(identity, {
|
|
1545
|
-
kind: "set",
|
|
1546
|
-
binding: {
|
|
1547
|
-
threadId: effectiveThreadId,
|
|
1548
|
-
cwd: resumedCwd,
|
|
1549
|
-
authProfileId,
|
|
1550
|
-
model: response.model,
|
|
1551
|
-
modelProvider,
|
|
1552
|
-
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
1553
|
-
}
|
|
1554
|
-
})) throw new Error("Codex thread binding changed while attaching the resumed thread.");
|
|
1555
|
-
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
|
|
1556
|
-
});
|
|
1415
|
+
function formatDiagnosticsUsage(commandPrefix) {
|
|
1416
|
+
return [
|
|
1417
|
+
`Usage: ${commandPrefix} [note]`,
|
|
1418
|
+
`Usage: ${commandPrefix} confirm <token>`,
|
|
1419
|
+
`Usage: ${commandPrefix} cancel <token>`
|
|
1420
|
+
].join("\n");
|
|
1557
1421
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
assertCodexBindingMayBeReplaced(await deps.bindingStore.read(sessionBindingIdentity({
|
|
1564
|
-
sessionId: ctx.sessionId,
|
|
1565
|
-
sessionKey: ctx.sessionKey,
|
|
1566
|
-
agentId: scope.agentId,
|
|
1567
|
-
config: ctx.config
|
|
1568
|
-
})), "binding a Codex CLI node session");
|
|
1422
|
+
function createCodexDiagnosticsConfirmation(params) {
|
|
1423
|
+
prunePendingCodexDiagnosticsConfirmations(params.now);
|
|
1424
|
+
if (!pendingCodexDiagnosticsConfirmationTokensByScope.has(params.scopeKey) && pendingCodexDiagnosticsConfirmationTokensByScope.size >= CODEX_DIAGNOSTICS_CONFIRMATION_MAX_SCOPES) {
|
|
1425
|
+
const oldestScopeKey = pendingCodexDiagnosticsConfirmationTokensByScope.keys().next().value;
|
|
1426
|
+
if (typeof oldestScopeKey === "string") deletePendingCodexDiagnosticsConfirmationScope(oldestScopeKey);
|
|
1569
1427
|
}
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1428
|
+
const scopeTokens = pendingCodexDiagnosticsConfirmationTokensByScope.get(params.scopeKey) ?? [];
|
|
1429
|
+
while (scopeTokens.length >= CODEX_DIAGNOSTICS_CONFIRMATION_MAX_REQUESTS_PER_SCOPE) {
|
|
1430
|
+
const oldestToken = scopeTokens.shift();
|
|
1431
|
+
if (!oldestToken) break;
|
|
1432
|
+
pendingCodexDiagnosticsConfirmations.delete(oldestToken);
|
|
1433
|
+
}
|
|
1434
|
+
const token = crypto.randomBytes(6).toString("hex");
|
|
1435
|
+
scopeTokens.push(token);
|
|
1436
|
+
pendingCodexDiagnosticsConfirmationTokensByScope.set(params.scopeKey, scopeTokens);
|
|
1437
|
+
pendingCodexDiagnosticsConfirmations.set(token, {
|
|
1438
|
+
token,
|
|
1439
|
+
targets: params.targets,
|
|
1440
|
+
note: params.note,
|
|
1441
|
+
senderId: params.senderId,
|
|
1442
|
+
channel: params.channel,
|
|
1443
|
+
accountId: params.accountId,
|
|
1444
|
+
channelId: params.channelId,
|
|
1445
|
+
messageThreadId: params.messageThreadId,
|
|
1446
|
+
threadParentId: params.threadParentId,
|
|
1447
|
+
sessionKey: params.sessionKey,
|
|
1448
|
+
scopeKey: params.scopeKey,
|
|
1449
|
+
...params.privateRouted === void 0 ? {} : { privateRouted: params.privateRouted },
|
|
1450
|
+
createdAt: params.now
|
|
1589
1451
|
});
|
|
1590
|
-
|
|
1591
|
-
if (request.status === "pending") return request.reply.text ?? "Codex CLI session binding is pending approval.";
|
|
1592
|
-
return formatCodexDisplayText(request.message);
|
|
1452
|
+
return token;
|
|
1593
1453
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
"permissions",
|
|
1603
|
-
"compact",
|
|
1604
|
-
"review",
|
|
1605
|
-
"goal"
|
|
1606
|
-
]);
|
|
1607
|
-
const CODEX_NATIVE_CONTROL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
1608
|
-
...CODEX_NATIVE_EXECUTION_SUBCOMMANDS,
|
|
1609
|
-
"detach",
|
|
1610
|
-
"unbind",
|
|
1611
|
-
"stop"
|
|
1612
|
-
]);
|
|
1613
|
-
function resolveCodexNativeCommandSandboxBlock(ctx, subcommand, args) {
|
|
1614
|
-
if (isReadOnlyCodexGoalCommand(subcommand, args)) return;
|
|
1615
|
-
if (!CODEX_NATIVE_EXECUTION_SUBCOMMANDS.has(subcommand)) return;
|
|
1616
|
-
if (returnsBeforeNativeCodexExecution(subcommand, args)) return;
|
|
1617
|
-
if (isCodexCliNodeResumeBind(subcommand, args)) return resolveCodexNativeSandboxBlock({
|
|
1618
|
-
config: ctx.config,
|
|
1619
|
-
sessionKey: ctx.sessionKey,
|
|
1620
|
-
sessionId: ctx.sessionId,
|
|
1621
|
-
surface: `/${["codex", subcommand].join(" ")}`
|
|
1622
|
-
});
|
|
1623
|
-
return resolveCodexNativeExecutionBlock({
|
|
1624
|
-
config: ctx.config,
|
|
1625
|
-
agentId: ctx.agentId,
|
|
1626
|
-
sessionKey: ctx.sessionKey,
|
|
1627
|
-
sessionId: ctx.sessionId,
|
|
1628
|
-
surface: `/${["codex", subcommand].join(" ")}`
|
|
1629
|
-
});
|
|
1454
|
+
function readCodexDiagnosticsConfirmationScope(ctx) {
|
|
1455
|
+
return {
|
|
1456
|
+
accountId: normalizeCodexDiagnosticsScopeField(ctx.accountId),
|
|
1457
|
+
channelId: normalizeCodexDiagnosticsScopeField(ctx.channelId),
|
|
1458
|
+
messageThreadId: typeof ctx.messageThreadId === "string" || typeof ctx.messageThreadId === "number" ? normalizeCodexDiagnosticsScopeField(String(ctx.messageThreadId)) : void 0,
|
|
1459
|
+
threadParentId: normalizeCodexDiagnosticsScopeField(ctx.threadParentId),
|
|
1460
|
+
sessionKey: normalizeCodexDiagnosticsScopeField(ctx.sessionKey)
|
|
1461
|
+
};
|
|
1630
1462
|
}
|
|
1631
|
-
function
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1463
|
+
function readCodexDiagnosticsScopeMismatch(pending, ctx) {
|
|
1464
|
+
const current = readCodexDiagnosticsConfirmationScope(ctx);
|
|
1465
|
+
if (pending.accountId !== current.accountId) return {
|
|
1466
|
+
confirmMessage: "This Codex diagnostics confirmation belongs to a different account.",
|
|
1467
|
+
cancelMessage: "This Codex diagnostics confirmation belongs to a different account."
|
|
1468
|
+
};
|
|
1469
|
+
if (pending.privateRouted) return;
|
|
1470
|
+
if (pending.channelId !== current.channelId) return {
|
|
1471
|
+
confirmMessage: "This Codex diagnostics confirmation belongs to a different channel instance.",
|
|
1472
|
+
cancelMessage: "This Codex diagnostics confirmation belongs to a different channel instance."
|
|
1473
|
+
};
|
|
1474
|
+
if (pending.messageThreadId !== current.messageThreadId) return {
|
|
1475
|
+
confirmMessage: "This Codex diagnostics confirmation belongs to a different thread.",
|
|
1476
|
+
cancelMessage: "This Codex diagnostics confirmation belongs to a different thread."
|
|
1477
|
+
};
|
|
1478
|
+
if (pending.threadParentId !== current.threadParentId) return {
|
|
1479
|
+
confirmMessage: "This Codex diagnostics confirmation belongs to a different parent thread.",
|
|
1480
|
+
cancelMessage: "This Codex diagnostics confirmation belongs to a different parent thread."
|
|
1481
|
+
};
|
|
1482
|
+
if (pending.sessionKey !== current.sessionKey) return {
|
|
1483
|
+
confirmMessage: "This Codex diagnostics confirmation belongs to a different session.",
|
|
1484
|
+
cancelMessage: "This Codex diagnostics confirmation belongs to a different session."
|
|
1485
|
+
};
|
|
1635
1486
|
}
|
|
1636
|
-
function
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
case "resume": return returnsBeforeNativeCodexResume(args);
|
|
1640
|
-
case "steer": return args.join(" ").trim() === "";
|
|
1641
|
-
case "model": return args.length === 0 || args.length > 1;
|
|
1642
|
-
case "fast": return args.length === 0 || args.length > 1 || parseCodexFastModeArg(args[0]) === void 0;
|
|
1643
|
-
case "permissions": return args.length === 0 || args.length > 1 || parseCodexPermissionsModeArg(args[0]) === void 0;
|
|
1644
|
-
case "compact":
|
|
1645
|
-
case "review":
|
|
1646
|
-
case "detach":
|
|
1647
|
-
case "unbind":
|
|
1648
|
-
case "stop": return args.length > 0;
|
|
1649
|
-
default: return false;
|
|
1650
|
-
}
|
|
1487
|
+
function readPendingCodexDiagnosticsConfirmation(token, now) {
|
|
1488
|
+
prunePendingCodexDiagnosticsConfirmations(now);
|
|
1489
|
+
return pendingCodexDiagnosticsConfirmations.get(token);
|
|
1651
1490
|
}
|
|
1652
|
-
function
|
|
1653
|
-
if (
|
|
1654
|
-
const parsed = parseResumeArgs([...args]);
|
|
1655
|
-
return Boolean(parsed.host && parsed.threadId && parsed.bindHere === true && !parsed.help);
|
|
1491
|
+
function prunePendingCodexDiagnosticsConfirmations(now) {
|
|
1492
|
+
for (const [token, pending] of pendingCodexDiagnosticsConfirmations) if (now - pending.createdAt >= CODEX_DIAGNOSTICS_CONFIRMATION_TTL_MS) deletePendingCodexDiagnosticsConfirmation(token);
|
|
1656
1493
|
}
|
|
1657
|
-
function
|
|
1658
|
-
const
|
|
1659
|
-
|
|
1660
|
-
if (
|
|
1661
|
-
|
|
1662
|
-
|
|
1494
|
+
function deletePendingCodexDiagnosticsConfirmation(token) {
|
|
1495
|
+
const pending = pendingCodexDiagnosticsConfirmations.get(token);
|
|
1496
|
+
pendingCodexDiagnosticsConfirmations.delete(token);
|
|
1497
|
+
if (!pending) return;
|
|
1498
|
+
const scopeTokens = pendingCodexDiagnosticsConfirmationTokensByScope.get(pending.scopeKey);
|
|
1499
|
+
if (!scopeTokens) return;
|
|
1500
|
+
const tokenIndex = scopeTokens.indexOf(token);
|
|
1501
|
+
if (tokenIndex >= 0) scopeTokens.splice(tokenIndex, 1);
|
|
1502
|
+
if (scopeTokens.length === 0) pendingCodexDiagnosticsConfirmationTokensByScope.delete(pending.scopeKey);
|
|
1663
1503
|
}
|
|
1664
|
-
|
|
1665
|
-
const
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
if (parsed.action === "install" && !canMutateCodexHost(ctx)) return "Only an owner or operator.admin gateway client can configure Codex Computer Use.";
|
|
1669
|
-
const { agentDir } = resolveCodexConversationControlScope(ctx);
|
|
1670
|
-
const params = {
|
|
1671
|
-
pluginConfig,
|
|
1672
|
-
config: ctx.config,
|
|
1673
|
-
agentDir,
|
|
1674
|
-
forceEnable: parsed.action === "install" || parsed.hasOverrides,
|
|
1675
|
-
...Object.keys(parsed.overrides).length > 0 ? { overrides: parsed.overrides } : {}
|
|
1676
|
-
};
|
|
1677
|
-
if (parsed.action === "install") return formatComputerUseStatus(await deps.installCodexComputerUse(params));
|
|
1678
|
-
return formatComputerUseStatus(await deps.readCodexComputerUseStatus(params));
|
|
1504
|
+
function deletePendingCodexDiagnosticsConfirmationScope(scopeKey) {
|
|
1505
|
+
const scopeTokens = pendingCodexDiagnosticsConfirmationTokensByScope.get(scopeKey) ?? [];
|
|
1506
|
+
for (const token of scopeTokens) pendingCodexDiagnosticsConfirmations.delete(token);
|
|
1507
|
+
pendingCodexDiagnosticsConfirmationTokensByScope.delete(scopeKey);
|
|
1679
1508
|
}
|
|
1680
|
-
|
|
1681
|
-
const
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
if (
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1509
|
+
function buildDiagnosticsTags(ctx) {
|
|
1510
|
+
const tags = { source: CODEX_DIAGNOSTICS_SOURCE };
|
|
1511
|
+
addTag(tags, "channel", ctx.channel);
|
|
1512
|
+
return tags;
|
|
1513
|
+
}
|
|
1514
|
+
function addTag(tags, key, value) {
|
|
1515
|
+
if (typeof value === "string" && value.trim()) tags[key] = value.trim();
|
|
1516
|
+
}
|
|
1517
|
+
function formatCodexThreadIdForDisplay(threadId) {
|
|
1518
|
+
return escapeCodexChatText(formatCodexTextForDisplay(threadId));
|
|
1519
|
+
}
|
|
1520
|
+
function formatCodexValueForDisplay(value) {
|
|
1521
|
+
return escapeCodexChatText(formatCodexTextForDisplay(value));
|
|
1522
|
+
}
|
|
1523
|
+
function formatCodexCopyableValueForDisplay(value) {
|
|
1524
|
+
const safe = formatCodexTextForDisplay(value);
|
|
1525
|
+
if (CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safe)) return `\`${safe}\``;
|
|
1526
|
+
return escapeCodexChatText(safe);
|
|
1527
|
+
}
|
|
1528
|
+
function formatCodexTextForDisplay(value) {
|
|
1529
|
+
let safe = "";
|
|
1530
|
+
for (const character of value) {
|
|
1531
|
+
const codePoint = character.codePointAt(0);
|
|
1532
|
+
safe += codePoint != null && isUnsafeDisplayCodePoint(codePoint) ? "?" : character;
|
|
1701
1533
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1534
|
+
safe = safe.trim();
|
|
1535
|
+
return safe || "<unknown>";
|
|
1536
|
+
}
|
|
1537
|
+
function escapeCodexChatText(value) {
|
|
1538
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("@", "@").replaceAll("`", "`").replaceAll("[", "[").replaceAll("]", "]").replaceAll("(", "(").replaceAll(")", ")").replaceAll("*", "∗").replaceAll("_", "_").replaceAll("~", "~").replaceAll("|", "|");
|
|
1539
|
+
}
|
|
1540
|
+
function readCodexDiagnosticsCooldownMs(threadId, now) {
|
|
1541
|
+
const lastSentAt = lastCodexDiagnosticsUploadByThread.get(threadId);
|
|
1542
|
+
if (!lastSentAt) return 0;
|
|
1543
|
+
const remainingMs = Math.max(0, CODEX_DIAGNOSTICS_COOLDOWN_MS - (now - lastSentAt));
|
|
1544
|
+
if (remainingMs === 0) lastCodexDiagnosticsUploadByThread.delete(threadId);
|
|
1545
|
+
return remainingMs;
|
|
1546
|
+
}
|
|
1547
|
+
function readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, options = {}) {
|
|
1548
|
+
for (const target of targets) {
|
|
1549
|
+
const cooldownMs = readCodexDiagnosticsCooldownMs(target.threadId, now);
|
|
1550
|
+
if (cooldownMs > 0) {
|
|
1551
|
+
if (options.includeThreadId === false) return `Codex diagnostics were already sent for one of these Codex threads recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
|
|
1552
|
+
return `Codex diagnostics were already sent for thread ${formatCodexThreadIdForDisplay(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
|
|
1553
|
+
}
|
|
1706
1554
|
}
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
if (!requestedStatus && !isObjectiveUpdate || isObjectiveUpdate && !objective) return "Usage: /codex goal [status|set <objective>|pause|resume|block|complete|clear]";
|
|
1710
|
-
if (requestedStatus && args.length > 1) return `Usage: /codex goal ${action}`;
|
|
1711
|
-
return formatNativeGoal(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.setThreadGoal, {
|
|
1712
|
-
threadId: binding.threadId,
|
|
1713
|
-
...objective ? { objective } : {},
|
|
1714
|
-
...requestedStatus ? { status: requestedStatus } : {}
|
|
1715
|
-
}, goalRequestOptions));
|
|
1716
|
-
}
|
|
1717
|
-
function formatNativeGoal(response) {
|
|
1718
|
-
const goal = isJsonObject(response) && isJsonObject(response.goal) ? response.goal : void 0;
|
|
1719
|
-
if (!goal) return "No Codex goal is active.";
|
|
1720
|
-
const objective = readString$1(goal, "objective") ?? "unknown";
|
|
1721
|
-
const status = readString$1(goal, "status") ?? "unknown";
|
|
1722
|
-
const tokensUsed = typeof goal.tokensUsed === "number" ? goal.tokensUsed : 0;
|
|
1723
|
-
const tokenBudget = typeof goal.tokenBudget === "number" ? goal.tokenBudget : void 0;
|
|
1724
|
-
return [
|
|
1725
|
-
`Codex goal: ${formatCodexDisplayText(objective)}`,
|
|
1726
|
-
`- Status: ${formatCodexDisplayText(status)}`,
|
|
1727
|
-
`- Tokens: ${tokensUsed}${tokenBudget === void 0 ? "" : ` / ${tokenBudget}`}`
|
|
1728
|
-
].join("\n");
|
|
1555
|
+
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(options.cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), now);
|
|
1556
|
+
if (scopeCooldownMs > 0) return `Codex diagnostics were already sent for this account or channel recently. Try again in ${Math.ceil(scopeCooldownMs / 1e3)}s.`;
|
|
1729
1557
|
}
|
|
1730
|
-
|
|
1731
|
-
const
|
|
1732
|
-
if (!
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
pluginConfig,
|
|
1737
|
-
agentDir: target.agentDir,
|
|
1738
|
-
config: ctx.config
|
|
1739
|
-
})).message;
|
|
1558
|
+
function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
1559
|
+
const lastSentAt = lastCodexDiagnosticsUploadByScope.get(scope);
|
|
1560
|
+
if (!lastSentAt) return 0;
|
|
1561
|
+
const remainingMs = Math.max(0, CODEX_DIAGNOSTICS_COOLDOWN_MS - (now - lastSentAt));
|
|
1562
|
+
if (remainingMs === 0) lastCodexDiagnosticsUploadByScope.delete(scope);
|
|
1563
|
+
return remainingMs;
|
|
1740
1564
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
identity: target.identity,
|
|
1746
|
-
bindingStore: deps.bindingStore,
|
|
1747
|
-
message,
|
|
1748
|
-
pluginConfig,
|
|
1749
|
-
agentDir: target.agentDir,
|
|
1750
|
-
config: ctx.config
|
|
1751
|
-
})).message;
|
|
1565
|
+
function recordCodexDiagnosticsUpload(threadId, ctx, now, cooldownScope) {
|
|
1566
|
+
pruneCodexDiagnosticsCooldowns(now);
|
|
1567
|
+
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
1568
|
+
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByThread, threadId, CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS, now);
|
|
1752
1569
|
}
|
|
1753
|
-
|
|
1754
|
-
if (
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
const target = await resolveControlTarget(ctx);
|
|
1759
|
-
if (!target) return "Cannot set Codex model because this command did not include a stable binding identity.";
|
|
1760
|
-
if (!normalized) {
|
|
1761
|
-
const binding = await deps.bindingStore.read(target.identity);
|
|
1762
|
-
return binding?.model ? `Codex model: ${formatCodexDisplayText(binding.model)}` : "Usage: /codex model <model>";
|
|
1570
|
+
function recordBoundedCodexDiagnosticsCooldown(map, key, maxSize, now) {
|
|
1571
|
+
if (!map.has(key)) while (map.size >= maxSize) {
|
|
1572
|
+
const oldestKey = map.keys().next().value;
|
|
1573
|
+
if (typeof oldestKey !== "string") break;
|
|
1574
|
+
map.delete(oldestKey);
|
|
1763
1575
|
}
|
|
1764
|
-
|
|
1765
|
-
identity: target.identity,
|
|
1766
|
-
bindingStore: deps.bindingStore,
|
|
1767
|
-
pluginConfig,
|
|
1768
|
-
model: normalized,
|
|
1769
|
-
agentDir: target.agentDir,
|
|
1770
|
-
config: ctx.config
|
|
1771
|
-
});
|
|
1576
|
+
map.set(key, now);
|
|
1772
1577
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
const
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
enabled: parsed
|
|
1578
|
+
function readCodexDiagnosticsCooldownScope(ctx) {
|
|
1579
|
+
const scope = readCodexDiagnosticsConfirmationScope(ctx);
|
|
1580
|
+
const payload = JSON.stringify({
|
|
1581
|
+
accountId: scope.accountId ?? null,
|
|
1582
|
+
channelId: scope.channelId ?? null,
|
|
1583
|
+
sessionKey: scope.sessionKey ?? null,
|
|
1584
|
+
messageThreadId: scope.messageThreadId ?? null,
|
|
1585
|
+
threadParentId: scope.threadParentId ?? null,
|
|
1586
|
+
senderId: normalizeCodexDiagnosticsScopeField(ctx.senderId) ?? null,
|
|
1587
|
+
channel: normalizeCodexDiagnosticsScopeField(ctx.channel) ?? ""
|
|
1784
1588
|
});
|
|
1589
|
+
return crypto.createHash("sha256").update(payload).digest("hex");
|
|
1785
1590
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
const
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1591
|
+
function pruneCodexDiagnosticsCooldowns(now) {
|
|
1592
|
+
pruneCodexDiagnosticsCooldownMap(lastCodexDiagnosticsUploadByThread, now);
|
|
1593
|
+
pruneCodexDiagnosticsCooldownMap(lastCodexDiagnosticsUploadByScope, now);
|
|
1594
|
+
}
|
|
1595
|
+
function pruneCodexDiagnosticsCooldownMap(map, now) {
|
|
1596
|
+
for (const [key, lastSentAt] of map) if (now - lastSentAt >= CODEX_DIAGNOSTICS_COOLDOWN_MS) map.delete(key);
|
|
1597
|
+
}
|
|
1598
|
+
function formatCodexErrorForDisplay(error) {
|
|
1599
|
+
return escapeCodexChatText(formatCodexTextForDisplay(error).slice(0, CODEX_DIAGNOSTICS_ERROR_MAX_CHARS)) || "unknown error";
|
|
1600
|
+
}
|
|
1601
|
+
function formatCodexResumeCommandForDisplay(threadId) {
|
|
1602
|
+
const safeThreadId = formatCodexTextForDisplay(threadId);
|
|
1603
|
+
if (!CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safeThreadId)) return "run codex resume and paste the thread id shown above";
|
|
1604
|
+
return `\`codex resume ${safeThreadId}\``;
|
|
1605
|
+
}
|
|
1606
|
+
function isUnsafeDisplayCodePoint(codePoint) {
|
|
1607
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 173 || codePoint === 1564 || codePoint === 6158 || codePoint >= 8203 && codePoint <= 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8288 && codePoint <= 8303 || codePoint === 65279 || codePoint >= 65529 && codePoint <= 65531 || codePoint >= 917504 && codePoint <= 917631;
|
|
1608
|
+
}
|
|
1609
|
+
function normalizeCodexDiagnosticsScopeField(value) {
|
|
1610
|
+
const normalized = normalizeOptionalString(value);
|
|
1611
|
+
if (!normalized) return;
|
|
1612
|
+
if (normalized.length <= CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS) return normalized;
|
|
1613
|
+
return `sha256:${crypto.createHash("sha256").update(normalized).digest("hex")}`;
|
|
1798
1614
|
}
|
|
1799
1615
|
async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
|
|
1800
1616
|
const label = kind === "compact" ? "compaction" : "review";
|
|
@@ -1803,264 +1619,285 @@ async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
|
|
|
1803
1619
|
if (!target) return `Cannot start Codex ${label} because this command did not include a stable binding identity.`;
|
|
1804
1620
|
const binding = await deps.bindingStore.read(target.identity);
|
|
1805
1621
|
if (!binding?.threadId) return `No Codex thread is attached to this OpenClaw session yet.`;
|
|
1806
|
-
const connection = resolveCodexBindingAppServerConnection({
|
|
1807
|
-
binding,
|
|
1808
|
-
authProfileId: binding.authProfileId,
|
|
1809
|
-
pluginConfig
|
|
1810
|
-
});
|
|
1811
1622
|
await deps.codexControlRequest(pluginConfig, kind === "compact" ? CODEX_CONTROL_METHODS.compact : CODEX_CONTROL_METHODS.review, kind === "review" ? {
|
|
1812
1623
|
threadId: binding.threadId,
|
|
1813
1624
|
target: { type: "uncommittedChanges" }
|
|
1814
1625
|
} : { threadId: binding.threadId }, {
|
|
1815
1626
|
agentDir: target.agentDir,
|
|
1816
|
-
authProfileId:
|
|
1817
|
-
config: ctx.config
|
|
1818
|
-
...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {}
|
|
1627
|
+
authProfileId: binding.authProfileId,
|
|
1628
|
+
config: ctx.config
|
|
1819
1629
|
});
|
|
1820
1630
|
return `Started Codex ${label} for thread ${formatCodexDisplayText(binding.threadId)}.`;
|
|
1821
1631
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
const
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
}
|
|
1858
|
-
return { text: `Unknown /codex plugins subcommand: ${formatCodexDisplayText(verb)}\n\n${buildPluginsHelp()}` };
|
|
1859
|
-
}
|
|
1860
|
-
function buildPluginsMenuReply() {
|
|
1861
|
-
return {
|
|
1862
|
-
text: [
|
|
1863
|
-
"Codex sub-plugins. Pick a sub-action or type:",
|
|
1864
|
-
"",
|
|
1865
|
-
" 1. /codex plugins list",
|
|
1866
|
-
" 2. /codex plugins enable",
|
|
1867
|
-
" 3. /codex plugins disable",
|
|
1868
|
-
" 4. /codex plugins help",
|
|
1869
|
-
"",
|
|
1870
|
-
"Type '/codex' to go back to the main menu."
|
|
1871
|
-
].join("\n"),
|
|
1872
|
-
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", "Pick a Codex sub-plugin action:", [
|
|
1873
|
-
{
|
|
1874
|
-
label: "list",
|
|
1875
|
-
command: "/codex plugins list"
|
|
1876
|
-
},
|
|
1877
|
-
{
|
|
1878
|
-
label: "enable",
|
|
1879
|
-
command: "/codex plugins enable"
|
|
1880
|
-
},
|
|
1881
|
-
{
|
|
1882
|
-
label: "disable",
|
|
1883
|
-
command: "/codex plugins disable"
|
|
1884
|
-
},
|
|
1885
|
-
{
|
|
1886
|
-
label: "help",
|
|
1887
|
-
command: "/codex plugins help"
|
|
1888
|
-
},
|
|
1889
|
-
{
|
|
1890
|
-
label: "back",
|
|
1891
|
-
command: "/codex"
|
|
1632
|
+
function splitArgs(value) {
|
|
1633
|
+
const input = value ?? "";
|
|
1634
|
+
const args = [];
|
|
1635
|
+
let current = "";
|
|
1636
|
+
let quote;
|
|
1637
|
+
let escaping = false;
|
|
1638
|
+
let tokenStarted = false;
|
|
1639
|
+
for (const char of input) {
|
|
1640
|
+
if (escaping) {
|
|
1641
|
+
current += char;
|
|
1642
|
+
escaping = false;
|
|
1643
|
+
tokenStarted = true;
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
if (char === "\\" && quote !== "'") {
|
|
1647
|
+
escaping = true;
|
|
1648
|
+
tokenStarted = true;
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
if (quote) {
|
|
1652
|
+
if (char === quote) quote = void 0;
|
|
1653
|
+
else current += char;
|
|
1654
|
+
tokenStarted = true;
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
if (char === "\"" || char === "'") {
|
|
1658
|
+
quote = char;
|
|
1659
|
+
tokenStarted = true;
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
if (/\s/.test(char)) {
|
|
1663
|
+
if (tokenStarted) {
|
|
1664
|
+
args.push(current);
|
|
1665
|
+
current = "";
|
|
1666
|
+
tokenStarted = false;
|
|
1892
1667
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
const globalEnabled = current.enabled === true;
|
|
1898
|
-
const eligible = Object.entries(current.plugins ?? {}).toSorted(([left], [right]) => left.localeCompare(right)).filter(([, entry]) => {
|
|
1899
|
-
const effectivelyEnabled = globalEnabled && entry.enabled !== false;
|
|
1900
|
-
return verb === "disable" ? effectivelyEnabled : !effectivelyEnabled;
|
|
1901
|
-
});
|
|
1902
|
-
if (eligible.length === 0) return {
|
|
1903
|
-
text: [
|
|
1904
|
-
`No configured ${verb === "enable" ? "disabled" : "enabled"} Codex sub-plugins found.`,
|
|
1905
|
-
"",
|
|
1906
|
-
"Type '/codex plugins list' to inspect configured sub-plugins.",
|
|
1907
|
-
"Type '/codex plugins menu' to go back to the plugins menu."
|
|
1908
|
-
].join("\n"),
|
|
1909
|
-
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", "Pick another Codex sub-plugin action:", [{
|
|
1910
|
-
label: "list",
|
|
1911
|
-
command: "/codex plugins list"
|
|
1912
|
-
}, {
|
|
1913
|
-
label: "back",
|
|
1914
|
-
command: "/codex plugins menu"
|
|
1915
|
-
}])
|
|
1916
|
-
};
|
|
1917
|
-
const buttons = [...eligible.map(([key]) => ({
|
|
1918
|
-
label: formatCodexDisplayText(key),
|
|
1919
|
-
command: `/codex plugins ${verb} ${key}`
|
|
1920
|
-
})), {
|
|
1921
|
-
label: "back",
|
|
1922
|
-
command: "/codex plugins menu"
|
|
1923
|
-
}];
|
|
1924
|
-
return {
|
|
1925
|
-
text: [
|
|
1926
|
-
`Codex sub-plugins to ${verb}. Pick one or type:`,
|
|
1927
|
-
"",
|
|
1928
|
-
...eligible.map(([key], index) => ` ${index + 1}. /codex plugins ${verb} ${key}`),
|
|
1929
|
-
"",
|
|
1930
|
-
...verb === "enable" && !globalEnabled ? ["Global codexPlugins.enabled is off; enabling one configured sub-plugin turns it on.", ""] : [],
|
|
1931
|
-
"Type '/codex plugins menu' to go back to the plugins menu."
|
|
1932
|
-
].join("\n"),
|
|
1933
|
-
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", `Pick a Codex sub-plugin to ${verb}:`, buttons)
|
|
1934
|
-
};
|
|
1935
|
-
}
|
|
1936
|
-
function buildPluginsHelp() {
|
|
1937
|
-
return [
|
|
1938
|
-
"Codex sub-plugin management (writes only to ~/.openclaw/openclaw.json, never to ~/.codex/config.toml):",
|
|
1939
|
-
"- /codex plugins (alias for list)",
|
|
1940
|
-
"- /codex plugins list show all configured Codex sub-plugins",
|
|
1941
|
-
"- /codex plugins enable <name> enable a configured sub-plugin",
|
|
1942
|
-
"- /codex plugins disable <name> disable a configured sub-plugin"
|
|
1943
|
-
].join("\n");
|
|
1944
|
-
}
|
|
1945
|
-
function formatPluginList(plugins, options = {}) {
|
|
1946
|
-
const globalEnabled = options.globalEnabled === true;
|
|
1947
|
-
const keys = Object.keys(plugins).toSorted();
|
|
1948
|
-
if (keys.length === 0) return "No Codex sub-plugins configured under plugins.entries.codex.config.codexPlugins.plugins";
|
|
1949
|
-
const rows = keys.map((key) => {
|
|
1950
|
-
const entry = plugins[key] ?? {};
|
|
1951
|
-
const state = globalEnabled && entry.enabled !== false ? "ON " : "OFF";
|
|
1952
|
-
return {
|
|
1953
|
-
displayKey: formatCodexDisplayText(key),
|
|
1954
|
-
state,
|
|
1955
|
-
pluginName: formatCodexDisplayText(entry.pluginName ?? key),
|
|
1956
|
-
marketplace: formatCodexDisplayText(entry.marketplaceName ?? "?")
|
|
1957
|
-
};
|
|
1958
|
-
});
|
|
1959
|
-
const keyW = Math.max(...rows.map((r) => r.displayKey.length));
|
|
1960
|
-
const pluginW = Math.max(...rows.map((r) => r.pluginName.length));
|
|
1961
|
-
return [
|
|
1962
|
-
"Codex sub-plugins in Openclaw config (~/.openclaw/openclaw.json):",
|
|
1963
|
-
"",
|
|
1964
|
-
...rows.map((r) => ` ${r.state} ${r.displayKey.padEnd(keyW)} ${r.pluginName.padEnd(pluginW)} [${r.marketplace}]`),
|
|
1965
|
-
"",
|
|
1966
|
-
...globalEnabled ? [] : ["Global codexPlugins.enabled is off; configured sub-plugins are inactive.", ""],
|
|
1967
|
-
"New Codex conversations pick up policy changes automatically; /new or /reset to refresh the current one."
|
|
1968
|
-
].join("\n");
|
|
1969
|
-
}
|
|
1970
|
-
//#endregion
|
|
1971
|
-
//#region extensions/codex/src/command-handlers.ts
|
|
1972
|
-
async function handleCodexSubcommand(ctx, options) {
|
|
1973
|
-
const deps = resolveCodexCommandDeps(options.deps);
|
|
1974
|
-
const args = splitArgs(ctx.args);
|
|
1975
|
-
if (args.length === 0) return buildCodexSubcommandPickerReply();
|
|
1976
|
-
const [subcommand = "status", ...rest] = args;
|
|
1977
|
-
const normalized = subcommand.toLowerCase();
|
|
1978
|
-
if (normalized === "help") return { text: buildHelp() };
|
|
1979
|
-
if (CODEX_NATIVE_CONTROL_SUBCOMMANDS.has(normalized) && !returnsBeforeNativeCodexExecution(normalized, rest) && !isReadOnlyCodexGoalCommand(normalized, rest) && !canMutateCodexHost(ctx)) return { text: CODEX_NATIVE_EXECUTION_AUTH_ERROR };
|
|
1980
|
-
const sandboxBlock = resolveCodexNativeCommandSandboxBlock(ctx, normalized, rest);
|
|
1981
|
-
if (sandboxBlock) return { text: sandboxBlock };
|
|
1982
|
-
if (normalized === "plugins") {
|
|
1983
|
-
if (!deps.codexPluginsManagementIo) return { text: "Codex sub-plugin management is not wired up (codexPluginsManagementIo dep is undefined). Edit ~/.openclaw/openclaw.json or use `openclaw config patch` until the runtime exposes the IO." };
|
|
1984
|
-
return await handleCodexPluginsSubcommand(ctx, rest, deps.codexPluginsManagementIo);
|
|
1985
|
-
}
|
|
1986
|
-
if (normalized === "status") {
|
|
1987
|
-
if (rest.length > 0) return { text: "Usage: /codex status" };
|
|
1988
|
-
const { agentDir } = resolveCodexConversationControlScope(ctx);
|
|
1989
|
-
return { text: formatCodexStatus(await deps.readCodexStatusProbes(options.pluginConfig, ctx.config, agentDir)) };
|
|
1990
|
-
}
|
|
1991
|
-
if (normalized === "models") {
|
|
1992
|
-
if (rest.length > 0) return { text: "Usage: /codex models" };
|
|
1993
|
-
const { agentDir } = resolveCodexConversationControlScope(ctx);
|
|
1994
|
-
return { text: formatModels(await deps.listCodexAppServerModels(deps.requestOptions(options.pluginConfig, 100, ctx.config, agentDir))) };
|
|
1995
|
-
}
|
|
1996
|
-
if (normalized === "threads") return { text: await buildThreads(deps, ctx, options.pluginConfig, rest.join(" ")) };
|
|
1997
|
-
if (normalized === "goal") return { text: await handleNativeGoal(deps, ctx, options.pluginConfig, rest) };
|
|
1998
|
-
if (normalized === "sessions") return { text: await buildCodexCliSessions(deps, rest) };
|
|
1999
|
-
if (normalized === "resume") return { text: await resumeThread(deps, ctx, options.pluginConfig, rest) };
|
|
2000
|
-
if (normalized === "bind") return await bindConversation(deps, ctx, options.pluginConfig, rest);
|
|
2001
|
-
if (normalized === "detach" || normalized === "unbind") {
|
|
2002
|
-
if (rest.length > 0) return { text: "Usage: /codex detach" };
|
|
2003
|
-
return { text: await detachConversation(deps, ctx) };
|
|
2004
|
-
}
|
|
2005
|
-
if (normalized === "binding") {
|
|
2006
|
-
if (rest.length > 0) return { text: "Usage: /codex binding" };
|
|
2007
|
-
return { text: await describeConversationBinding(deps, ctx) };
|
|
2008
|
-
}
|
|
2009
|
-
if (normalized === "stop") {
|
|
2010
|
-
if (rest.length > 0) return { text: "Usage: /codex stop" };
|
|
2011
|
-
return { text: await stopConversationTurn(deps, ctx, options.pluginConfig) };
|
|
2012
|
-
}
|
|
2013
|
-
if (normalized === "steer") return { text: await steerConversationTurn(deps, ctx, options.pluginConfig, rest.join(" ")) };
|
|
2014
|
-
if (normalized === "model") return { text: await setConversationModel(deps, ctx, options.pluginConfig, rest) };
|
|
2015
|
-
if (normalized === "fast") {
|
|
2016
|
-
if (isMenuVerb(rest)) return buildCodexFastMenuReply();
|
|
2017
|
-
return { text: await setConversationFastMode(deps, ctx, rest) };
|
|
2018
|
-
}
|
|
2019
|
-
if (normalized === "permissions") {
|
|
2020
|
-
if (isMenuVerb(rest)) return buildCodexPermissionsMenuReply();
|
|
2021
|
-
return { text: await setConversationPermissions(deps, ctx, rest) };
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
current += char;
|
|
1671
|
+
tokenStarted = true;
|
|
2022
1672
|
}
|
|
2023
|
-
if (
|
|
2024
|
-
if (
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
1673
|
+
if (escaping) current += "\\";
|
|
1674
|
+
if (tokenStarted) args.push(current);
|
|
1675
|
+
return args;
|
|
1676
|
+
}
|
|
1677
|
+
function parseBindArgs(args) {
|
|
1678
|
+
const parsed = {};
|
|
1679
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1680
|
+
const arg = args[index];
|
|
1681
|
+
if (arg === "--help" || arg === "-h") {
|
|
1682
|
+
parsed.help = true;
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if (arg === "--cwd") {
|
|
1686
|
+
const value = readRequiredOptionValue(args, index);
|
|
1687
|
+
if (!value || parsed.cwd !== void 0) {
|
|
1688
|
+
parsed.help = true;
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
parsed.cwd = value;
|
|
1692
|
+
index += 1;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
if (arg === "--model") {
|
|
1696
|
+
const value = readRequiredOptionValue(args, index);
|
|
1697
|
+
if (!value || parsed.model !== void 0) {
|
|
1698
|
+
parsed.help = true;
|
|
1699
|
+
continue;
|
|
1700
|
+
}
|
|
1701
|
+
parsed.model = value;
|
|
1702
|
+
index += 1;
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
if (arg === "--provider" || arg === "--model-provider") {
|
|
1706
|
+
const value = readRequiredOptionValue(args, index);
|
|
1707
|
+
if (!value || parsed.provider !== void 0) {
|
|
1708
|
+
parsed.help = true;
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
parsed.provider = value;
|
|
1712
|
+
index += 1;
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
if (!arg.startsWith("-") && !parsed.threadId) {
|
|
1716
|
+
parsed.threadId = arg;
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
parsed.help = true;
|
|
2029
1720
|
}
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1721
|
+
parsed.threadId = normalizeOptionalString(parsed.threadId);
|
|
1722
|
+
parsed.cwd = normalizeOptionalString(parsed.cwd);
|
|
1723
|
+
parsed.model = normalizeOptionalString(parsed.model);
|
|
1724
|
+
parsed.provider = normalizeOptionalString(parsed.provider);
|
|
1725
|
+
return parsed;
|
|
1726
|
+
}
|
|
1727
|
+
function parseCodexCliSessionsArgs(args) {
|
|
1728
|
+
const parsed = { filter: "" };
|
|
1729
|
+
const filter = [];
|
|
1730
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1731
|
+
const arg = args[index];
|
|
1732
|
+
if (arg === "--help" || arg === "-h") {
|
|
1733
|
+
parsed.help = true;
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
if (arg === "--host" || arg === "--node") {
|
|
1737
|
+
const value = readRequiredOptionValue(args, index);
|
|
1738
|
+
if (!value || parsed.host !== void 0) {
|
|
1739
|
+
parsed.help = true;
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1742
|
+
parsed.host = value;
|
|
1743
|
+
index += 1;
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
if (arg === "--limit") {
|
|
1747
|
+
const value = readRequiredOptionValue(args, index);
|
|
1748
|
+
const parsedLimit = parseStrictPositiveInteger(value);
|
|
1749
|
+
if (parsedLimit === void 0) {
|
|
1750
|
+
parsed.help = true;
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
parsed.limit = parsedLimit;
|
|
1754
|
+
index += 1;
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (arg.startsWith("-")) {
|
|
1758
|
+
parsed.help = true;
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
filter.push(arg);
|
|
2037
1762
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
1763
|
+
parsed.host = normalizeOptionalString(parsed.host);
|
|
1764
|
+
parsed.filter = filter.join(" ").trim();
|
|
1765
|
+
return parsed;
|
|
1766
|
+
}
|
|
1767
|
+
function parseResumeArgs(args) {
|
|
1768
|
+
const parsed = {};
|
|
1769
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1770
|
+
const arg = args[index];
|
|
1771
|
+
if (arg === "--help" || arg === "-h") {
|
|
1772
|
+
parsed.help = true;
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
if (arg === "--host" || arg === "--node") {
|
|
1776
|
+
const value = readRequiredOptionValue(args, index);
|
|
1777
|
+
if (!value || parsed.host !== void 0) {
|
|
1778
|
+
parsed.help = true;
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1781
|
+
parsed.host = value;
|
|
1782
|
+
index += 1;
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
if (arg === "--bind") {
|
|
1786
|
+
if (readRequiredOptionValue(args, index) !== "here" || parsed.bindHere !== void 0) {
|
|
1787
|
+
parsed.help = true;
|
|
1788
|
+
continue;
|
|
1789
|
+
}
|
|
1790
|
+
parsed.bindHere = true;
|
|
1791
|
+
index += 1;
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
if (!arg.startsWith("-") && !parsed.threadId) {
|
|
1795
|
+
parsed.threadId = arg;
|
|
1796
|
+
continue;
|
|
1797
|
+
}
|
|
1798
|
+
parsed.help = true;
|
|
2045
1799
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
1800
|
+
parsed.threadId = normalizeOptionalString(parsed.threadId);
|
|
1801
|
+
parsed.host = normalizeOptionalString(parsed.host);
|
|
1802
|
+
return parsed;
|
|
1803
|
+
}
|
|
1804
|
+
function parseComputerUseArgs(args) {
|
|
1805
|
+
const parsed = {
|
|
1806
|
+
action: "status",
|
|
1807
|
+
overrides: {},
|
|
1808
|
+
hasOverrides: false
|
|
1809
|
+
};
|
|
1810
|
+
let sawAction = false;
|
|
1811
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1812
|
+
const arg = args[index];
|
|
1813
|
+
if (arg === "--help" || arg === "-h") {
|
|
1814
|
+
parsed.help = true;
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1817
|
+
if (arg === "status" || arg === "install") {
|
|
1818
|
+
if (sawAction) {
|
|
1819
|
+
parsed.help = true;
|
|
1820
|
+
continue;
|
|
1821
|
+
}
|
|
1822
|
+
sawAction = true;
|
|
1823
|
+
parsed.action = arg;
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (arg === "--source" || arg === "--marketplace-source") {
|
|
1827
|
+
const value = readRequiredOptionValue(args, index);
|
|
1828
|
+
if (!value || parsed.overrides.marketplaceSource !== void 0) {
|
|
1829
|
+
parsed.help = true;
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1832
|
+
parsed.overrides.marketplaceSource = value;
|
|
1833
|
+
index += 1;
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
if (arg === "--marketplace-path" || arg === "--path") {
|
|
1837
|
+
const value = readRequiredOptionValue(args, index);
|
|
1838
|
+
if (!value || parsed.overrides.marketplacePath !== void 0) {
|
|
1839
|
+
parsed.help = true;
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
parsed.overrides.marketplacePath = value;
|
|
1843
|
+
index += 1;
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (arg === "--marketplace") {
|
|
1847
|
+
const value = readRequiredOptionValue(args, index);
|
|
1848
|
+
if (!value || parsed.overrides.marketplaceName !== void 0) {
|
|
1849
|
+
parsed.help = true;
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
parsed.overrides.marketplaceName = value;
|
|
1853
|
+
index += 1;
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
if (arg === "--plugin") {
|
|
1857
|
+
const value = readRequiredOptionValue(args, index);
|
|
1858
|
+
if (!value || parsed.overrides.pluginName !== void 0) {
|
|
1859
|
+
parsed.help = true;
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
parsed.overrides.pluginName = value;
|
|
1863
|
+
index += 1;
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
if (arg === "--server" || arg === "--mcp-server") {
|
|
1867
|
+
const value = readRequiredOptionValue(args, index);
|
|
1868
|
+
if (!value || parsed.overrides.mcpServerName !== void 0) {
|
|
1869
|
+
parsed.help = true;
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
parsed.overrides.mcpServerName = value;
|
|
1873
|
+
index += 1;
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
parsed.help = true;
|
|
2062
1877
|
}
|
|
2063
|
-
|
|
1878
|
+
parsed.overrides = normalizeComputerUseStringOverrides(parsed.overrides);
|
|
1879
|
+
parsed.hasOverrides = Object.values(parsed.overrides).some(Boolean);
|
|
1880
|
+
return parsed;
|
|
1881
|
+
}
|
|
1882
|
+
function readRequiredOptionValue(args, index) {
|
|
1883
|
+
const value = args[index + 1];
|
|
1884
|
+
const normalized = value?.trim();
|
|
1885
|
+
if (!normalized || normalized.startsWith("-")) return;
|
|
1886
|
+
return value;
|
|
1887
|
+
}
|
|
1888
|
+
function normalizeComputerUseStringOverrides(overrides) {
|
|
1889
|
+
const normalized = {};
|
|
1890
|
+
const marketplaceSource = normalizeOptionalString(overrides.marketplaceSource);
|
|
1891
|
+
if (marketplaceSource) normalized.marketplaceSource = marketplaceSource;
|
|
1892
|
+
const marketplacePath = normalizeOptionalString(overrides.marketplacePath);
|
|
1893
|
+
if (marketplacePath) normalized.marketplacePath = marketplacePath;
|
|
1894
|
+
const marketplaceName = normalizeOptionalString(overrides.marketplaceName);
|
|
1895
|
+
if (marketplaceName) normalized.marketplaceName = marketplaceName;
|
|
1896
|
+
const pluginName = normalizeOptionalString(overrides.pluginName);
|
|
1897
|
+
if (pluginName) normalized.pluginName = pluginName;
|
|
1898
|
+
const mcpServerName = normalizeOptionalString(overrides.mcpServerName);
|
|
1899
|
+
if (mcpServerName) normalized.mcpServerName = mcpServerName;
|
|
1900
|
+
return normalized;
|
|
2064
1901
|
}
|
|
2065
1902
|
//#endregion
|
|
2066
1903
|
export { handleCodexSubcommand };
|