docs: document codex command helpers

This commit is contained in:
Peter Steinberger
2026-06-04 08:55:39 -04:00
parent 796ed1b501
commit 9b9d4883c3
3 changed files with 29 additions and 0 deletions

View File

@@ -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<JsonValue | undefined>;
};
/** 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<JsonValue | undefined>,
limits: SafeValue<JsonValue | undefined>,
@@ -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("&", "&amp;")
.replaceAll("<", "&lt;")
@@ -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;

View File

@@ -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<CodexSubcommandHandler>;
};
/** 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 = {},

View File

@@ -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<typeof buildTurnStartParams>;
};
/** 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 | undefined>): 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<CodexPluginConfig, "codexDynamicToolsLoading" | "codexDynamicToolsExclude">;