diff --git a/extensions/codex/src/command-formatters.ts b/extensions/codex/src/command-formatters.ts index 9a12a13e5025..08a53986bb73 100644 --- a/extensions/codex/src/command-formatters.ts +++ b/extensions/codex/src/command-formatters.ts @@ -1,3 +1,7 @@ +/** + * Formats Codex command responses for safe chat display, including status, + * lists, account summaries, and user-facing help text. + */ import type { CodexComputerUseStatus } from "./app-server/computer-use.js"; import type { CodexAppServerModelListResult } from "./app-server/models.js"; import { isJsonObject, type JsonObject, type JsonValue } from "./app-server/protocol.js"; @@ -17,6 +21,7 @@ type CodexStatusProbes = { skills: SafeValue; }; +/** Formats the combined `/codex status` probe result. */ export function formatCodexStatus(probes: CodexStatusProbes): string { const connected = probes.models.ok || probes.account.ok || probes.limits.ok || probes.mcps.ok || probes.skills.ok; @@ -64,6 +69,7 @@ export function formatCodexStatus(probes: CodexStatusProbes): string { return lines.join("\n"); } +/** Formats Codex model-list results for `/codex models`. */ export function formatModels(result: CodexAppServerModelListResult): string { if (result.models.length === 0) { return "No Codex app-server models returned."; @@ -80,6 +86,7 @@ export function formatModels(result: CodexAppServerModelListResult): string { return lines.join("\n"); } +/** Formats Codex thread-list responses with safe resume hints. */ export function formatThreads(response: JsonValue | undefined): string { const threads = extractArray(response); if (threads.length === 0) { @@ -104,6 +111,7 @@ export function formatThreads(response: JsonValue | undefined): string { ].join("\n"); } +/** Formats account and rate-limit output for `/codex account`. */ export function formatAccount( account: SafeValue, limits: SafeValue, @@ -154,6 +162,7 @@ function formatAuthRowStatus(row: CodexAccountAuthOverview["rows"][number]): str return row.billingNote ? `${row.status} ยท ${row.billingNote}` : row.status; } +/** Formats Codex Computer Use readiness and plugin/MCP availability. */ export function formatComputerUseStatus(status: CodexComputerUseStatus): string { const lines = [ `Computer Use: ${status.ready ? "ready" : status.enabled ? "not ready" : "disabled"}`, @@ -183,6 +192,7 @@ function computerUsePluginState(status: CodexComputerUseStatus): string { return status.pluginEnabled ? "installed" : "installed, disabled"; } +/** Formats generic array-like Codex app-server responses. */ export function formatList(response: JsonValue | undefined, label: string): string { const entries = extractArray(response); if (entries.length === 0) { @@ -199,6 +209,7 @@ export function formatList(response: JsonValue | undefined, label: string): stri ].join("\n"); } +/** Formats Codex skills grouped by scope, omitting disabled entries. */ export function formatSkills(response: JsonValue | undefined): string { const groups = isJsonObject(response) && Array.isArray(response.data) ? response.data : []; if (groups.length === 0) { @@ -251,6 +262,7 @@ function formatCodexResumeHint(threadId: string): string { return `/codex resume ${safe}`; } +/** Escapes Codex-originated text so it is safe to render in chat command output. */ export function formatCodexDisplayText(value: string): string { return escapeCodexChatText(formatCodexTextForDisplay(value)); } @@ -277,6 +289,8 @@ function sanitizeCodexTextForDisplay(value: string): string { } function escapeCodexChatText(value: string): string { + // Command output is public chat text. Escape markdown/control triggers and + // mention characters so Codex data cannot ping users or inject formatting. return value .replaceAll("&", "&") .replaceAll("<", "<") @@ -338,6 +352,7 @@ function isUnsafeDisplayCodePoint(codePoint: number): boolean { ); } +/** Builds the portable `/codex` command help text. */ export function buildHelp(): string { return [ "Codex commands:", @@ -492,6 +507,7 @@ function extractArray(value: JsonValue | undefined): JsonValue[] { return []; } +/** Reads and trims a non-empty string field from a JSON object. */ export function readString(record: JsonObject, key: string): string | undefined { const value = record[key]; return typeof value === "string" && value.trim() ? value.trim() : undefined; diff --git a/extensions/codex/src/commands.ts b/extensions/codex/src/commands.ts index 681444313e89..85434fdefcc7 100644 --- a/extensions/codex/src/commands.ts +++ b/extensions/codex/src/commands.ts @@ -1,3 +1,7 @@ +/** + * Registers the `/codex` plugin command and lazy-loads the app-server command + * handler implementation. + */ import type { OpenClawPluginCommandDefinition, PluginCommandContext, @@ -21,6 +25,7 @@ type CodexCommandInternalOptions = CodexCommandOptions & { loadSubcommandHandler?: () => Promise; }; +/** Creates the reserved `/codex` command definition exposed by the plugin. */ export function createCodexCommand(options: CodexCommandOptions): OpenClawPluginCommandDefinition { return { name: "codex", @@ -42,6 +47,7 @@ export function createCodexCommand(options: CodexCommandOptions): OpenClawPlugin }; } +/** Dispatches a `/codex` command to the subcommand handler and formats failures for chat. */ export async function handleCodexCommand( ctx: PluginCommandContext, options: CodexCommandInternalOptions = {}, diff --git a/extensions/codex/test-api.ts b/extensions/codex/test-api.ts index 4a5d054410e4..81cc30f27774 100644 --- a/extensions/codex/test-api.ts +++ b/extensions/codex/test-api.ts @@ -1,3 +1,7 @@ +/** + * Test-only helpers for producing Codex app-server prompt snapshots and dynamic + * tool specs without starting a live app-server. + */ import type { AnyAgentTool, EmbeddedRunAttemptParams, @@ -24,6 +28,7 @@ type CodexHarnessPromptSnapshot = { turnStartParams: ReturnType; }; +/** Resolves deterministic app-server options for prompt snapshot tests. */ export function resolveCodexPromptSnapshotAppServerOptions( pluginConfig?: unknown, ): CodexAppServerRuntimeOptions { @@ -34,6 +39,7 @@ export function resolveCodexPromptSnapshotAppServerOptions( }); } +/** Builds thread/resume/turn prompt payload snapshots for a Codex harness attempt. */ export function buildCodexHarnessPromptSnapshot(params: { attempt: EmbeddedRunAttemptParams; cwd: string; @@ -82,6 +88,7 @@ function joinPresentSections(...sections: Array): string { return sections.filter((section): section is string => Boolean(section?.trim())).join("\n\n"); } +/** Converts harness tools into Codex dynamic-tool specs for prompt snapshot tests. */ export function createCodexDynamicToolSpecsForPromptSnapshot(params: { tools: AnyAgentTool[]; pluginConfig?: Pick;