docs: document codex plugin app config

This commit is contained in:
Peter Steinberger
2026-06-04 08:46:41 -04:00
parent 22efdfa904
commit d99268ae51
3 changed files with 44 additions and 0 deletions

View File

@@ -1,3 +1,7 @@
/**
* Activates configured Codex marketplace plugins and refreshes runtime state so
* plugin-owned apps/tools are visible to native Codex turns.
*/
import fs from "node:fs/promises";
import path from "node:path";
import type { CodexAppInventoryCache, CodexAppInventoryRequest } from "./app-inventory-cache.js";
@@ -10,6 +14,7 @@ import {
} from "./plugin-inventory.js";
import type { v2 } from "./protocol.js";
/** Terminal reason reported after trying to activate one Codex plugin policy. */
export type CodexPluginActivationReason =
| "already_active"
| "installed"
@@ -19,10 +24,12 @@ export type CodexPluginActivationReason =
| "auth_required"
| "refresh_failed";
/** Human-readable diagnostic emitted during Codex plugin activation. */
export type CodexPluginActivationDiagnostic = {
message: string;
};
/** Result of ensuring one configured Codex plugin is installed and enabled. */
export type CodexPluginActivationResult = {
identity: ResolvedCodexPluginPolicy;
ok: boolean;
@@ -33,6 +40,7 @@ export type CodexPluginActivationResult = {
diagnostics: CodexPluginActivationDiagnostic[];
};
/** Inputs for activating one resolved Codex plugin policy. */
export type EnsureCodexPluginActivationParams = {
identity: ResolvedCodexPluginPolicy;
request: CodexPluginRuntimeRequest;
@@ -41,10 +49,12 @@ export type EnsureCodexPluginActivationParams = {
installEvenIfActive?: boolean;
};
/** Diagnostics from refreshing Codex runtime surfaces after plugin activation. */
export type CodexPluginRuntimeRefreshResult = {
diagnostics: CodexPluginActivationDiagnostic[];
};
/** Installs/enables a configured Codex plugin and refreshes plugin/app state. */
export async function ensureCodexPluginActivation(
params: EnsureCodexPluginActivationParams,
): Promise<CodexPluginActivationResult> {
@@ -130,6 +140,7 @@ export async function ensureCodexPluginActivation(
};
}
/** Forces Codex plugin, skill, hook, MCP, and app inventory refreshes after activation. */
export async function refreshCodexPluginRuntimeState(params: {
request: CodexPluginRuntimeRequest;
appCache?: CodexAppInventoryCache;
@@ -176,6 +187,7 @@ export async function refreshCodexPluginRuntimeState(params: {
return { diagnostics };
}
/** Ensures the Codex config enables app substrate support needed by plugin-owned apps. */
export async function ensureCodexAppsSubstrateConfig(params: {
codexHome: string;
readFile?: (filePath: string, encoding: "utf8") => Promise<string>;
@@ -211,6 +223,7 @@ export async function ensureCodexAppsSubstrateConfig(params: {
return { changed: true, configPath };
}
/** Upserts a boolean key in a TOML section while preserving the rest of the file. */
export function upsertTomlBoolean(
source: string,
section: string,

View File

@@ -1,3 +1,7 @@
/**
* Reads Codex plugin marketplace state and app inventory to decide which
* plugin-owned apps can be exposed to a native Codex thread.
*/
import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
import type {
CodexAppInventoryCache,
@@ -12,14 +16,17 @@ import {
} from "./config.js";
import type { v2 } from "./protocol.js";
/** Request callback used to call Codex app-server plugin/app methods. */
export type CodexPluginRuntimeRequest = (method: string, params?: unknown) => Promise<unknown>;
/** Stable reference to the OpenAI curated Codex plugin marketplace. */
export type CodexPluginMarketplaceRef = {
name: typeof CODEX_PLUGINS_MARKETPLACE_NAME;
path?: string;
remoteMarketplaceName?: string;
};
/** Machine-readable inventory diagnostic code used by thread config builders. */
export type CodexPluginInventoryDiagnosticCode =
| "disabled"
| "marketplace_missing"
@@ -30,12 +37,14 @@ export type CodexPluginInventoryDiagnosticCode =
| "app_inventory_stale"
| "app_ownership_ambiguous";
/** Diagnostic explaining why a configured plugin or app cannot be exposed. */
export type CodexPluginInventoryDiagnostic = {
code: CodexPluginInventoryDiagnosticCode;
plugin?: ResolvedCodexPluginPolicy;
message: string;
};
/** App owned by a Codex plugin with current accessibility/auth state. */
export type CodexPluginOwnedApp = {
id: string;
name: string;
@@ -44,6 +53,7 @@ export type CodexPluginOwnedApp = {
needsAuth: boolean;
};
/** Inventory record for one configured Codex plugin policy. */
export type CodexPluginInventoryRecord = {
policy: ResolvedCodexPluginPolicy;
summary: v2.PluginSummary;
@@ -55,6 +65,7 @@ export type CodexPluginInventoryRecord = {
apps: CodexPluginOwnedApp[];
};
/** Complete inventory result for configured Codex plugins and owned apps. */
export type CodexPluginInventory = {
policy: ResolvedCodexPluginsPolicy;
marketplace?: CodexPluginMarketplaceRef;
@@ -63,6 +74,7 @@ export type CodexPluginInventory = {
appInventory?: CodexAppInventoryCacheRead;
};
/** Inputs for reading plugin marketplace/detail state and cached app inventory. */
export type ReadCodexPluginInventoryParams = {
pluginConfig?: unknown;
policy?: ResolvedCodexPluginsPolicy;
@@ -74,6 +86,7 @@ export type ReadCodexPluginInventoryParams = {
suppressAppInventoryRefresh?: boolean;
};
/** Reads configured Codex plugin state and maps owned apps to readiness diagnostics. */
export async function readCodexPluginInventory(
params: ReadCodexPluginInventoryParams,
): Promise<CodexPluginInventory> {
@@ -195,6 +208,7 @@ export async function readCodexPluginInventory(
return inventory;
}
/** Finds one plugin summary in the OpenAI curated marketplace response. */
export function findOpenAiCuratedPluginSummary(
listed: v2.PluginListResponse,
pluginName: string,
@@ -209,6 +223,7 @@ export function findOpenAiCuratedPluginSummary(
return summary ? { marketplace: marketplaceRef(marketplaceEntry), summary } : undefined;
}
/** Builds plugin/read or plugin/install params from a marketplace reference. */
export function pluginReadParams(
marketplace: CodexPluginMarketplaceRef,
pluginName: string,

View File

@@ -1,3 +1,7 @@
/**
* Builds Codex thread config patches that expose only policy-approved
* plugin-owned apps for native Codex turns.
*/
import crypto from "node:crypto";
import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
import {
@@ -26,6 +30,7 @@ import {
} from "./plugin-inventory.js";
import type { JsonObject, JsonValue } from "./protocol.js";
/** Policy context for one app id exposed by a configured Codex plugin. */
export type PluginAppPolicyContextEntry = {
configKey: string;
marketplaceName: ResolvedCodexPluginPolicy["marketplaceName"];
@@ -34,12 +39,14 @@ export type PluginAppPolicyContextEntry = {
mcpServerNames: string[];
};
/** Stable app-to-plugin ownership context persisted with Codex thread bindings. */
export type PluginAppPolicyContext = {
fingerprint: string;
apps: Record<string, PluginAppPolicyContextEntry>;
pluginAppIds: Record<string, string[]>;
};
/** Diagnostic emitted while building app config for a native Codex thread. */
export type CodexPluginThreadConfigDiagnostic =
| CodexPluginInventoryDiagnostic
| {
@@ -48,6 +55,7 @@ export type CodexPluginThreadConfigDiagnostic =
message: string;
};
/** Complete Codex thread config patch plus inventory and policy fingerprints. */
export type CodexPluginThreadConfig = {
enabled: boolean;
configPatch?: JsonObject;
@@ -58,6 +66,7 @@ export type CodexPluginThreadConfig = {
diagnostics: CodexPluginThreadConfigDiagnostic[];
};
/** Inputs for building a Codex thread app/plugin config patch. */
export type BuildCodexPluginThreadConfigParams = {
pluginConfig?: unknown;
request: CodexPluginRuntimeRequest;
@@ -69,10 +78,12 @@ export type BuildCodexPluginThreadConfigParams = {
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION = 1;
const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION = 1;
/** Returns true when plugin config exists and thread config may need app patches. */
export function shouldBuildCodexPluginThreadConfig(pluginConfig?: unknown): boolean {
return resolveCodexPluginsPolicy(pluginConfig).configured;
}
/** Fingerprints policy and app-cache identity before runtime inventory is read. */
export function buildCodexPluginThreadConfigInputFingerprint(params: {
pluginConfig?: unknown;
appCacheKey?: string;
@@ -85,6 +96,7 @@ export function buildCodexPluginThreadConfigInputFingerprint(params: {
});
}
/** Builds the Codex apps config patch and policy context for a native thread. */
export async function buildCodexPluginThreadConfig(
params: BuildCodexPluginThreadConfigParams,
): Promise<CodexPluginThreadConfig> {
@@ -257,6 +269,7 @@ export async function buildCodexPluginThreadConfig(
};
}
/** Deep-merges optional Codex thread config patches, returning undefined when empty. */
export function mergeCodexThreadConfigs(
...configs: Array<JsonObject | undefined>
): JsonObject | undefined {
@@ -270,6 +283,7 @@ export function mergeCodexThreadConfigs(
return merged && Object.keys(merged).length > 0 ? merged : undefined;
}
/** Detects when a stored thread binding no longer matches current plugin policy inputs. */
export function isCodexPluginThreadBindingStale(params: {
codexPluginsEnabled: boolean;
bindingFingerprint?: string;
@@ -442,6 +456,8 @@ function fingerprintJson(value: JsonValue): string {
}
function stableStringify(value: JsonValue | undefined): string {
// Fingerprints must be process-stable across object insertion order so prompt
// cache and thread-binding comparisons do not churn between runs.
if (Array.isArray(value)) {
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
}