test: validate Codex app-server config

This commit is contained in:
Peter Steinberger
2026-04-10 22:43:15 +01:00
parent 8d72aafdbb
commit 796ea57378
4 changed files with 95 additions and 5 deletions

View File

@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import { readCodexPluginConfig, resolveCodexAppServerRuntimeOptions } from "./config.js";
describe("Codex app-server config", () => {
it("parses typed plugin config before falling back to environment knobs", () => {
const runtime = resolveCodexAppServerRuntimeOptions({
pluginConfig: {
appServer: {
transport: "websocket",
url: "ws://127.0.0.1:39175",
headers: { "X-Test": "yes" },
approvalPolicy: "on-request",
sandbox: "danger-full-access",
approvalsReviewer: "guardian_subagent",
serviceTier: "priority",
},
},
env: {
OPENCLAW_CODEX_APP_SERVER_APPROVAL_POLICY: "never",
OPENCLAW_CODEX_APP_SERVER_SANDBOX: "read-only",
},
});
expect(runtime).toEqual(
expect.objectContaining({
approvalPolicy: "on-request",
sandbox: "danger-full-access",
approvalsReviewer: "guardian_subagent",
serviceTier: "priority",
start: expect.objectContaining({
transport: "websocket",
url: "ws://127.0.0.1:39175",
headers: { "X-Test": "yes" },
}),
}),
);
});
it("rejects malformed plugin config instead of treating freeform strings as control values", () => {
expect(
readCodexPluginConfig({
appServer: {
approvalPolicy: "always",
},
}),
).toEqual({});
});
});

View File

@@ -1,3 +1,5 @@
import { z } from "zod";
export type CodexAppServerTransportMode = "stdio" | "websocket";
export type CodexAppServerApprovalPolicy = "never" | "on-request" | "on-failure" | "untrusted";
export type CodexAppServerSandboxMode = "read-only" | "workspace-write" | "danger-full-access";
@@ -41,11 +43,47 @@ export type CodexPluginConfig = {
};
};
const codexAppServerTransportSchema = z.enum(["stdio", "websocket"]);
const codexAppServerApprovalPolicySchema = z.enum([
"never",
"on-request",
"on-failure",
"untrusted",
]);
const codexAppServerSandboxSchema = z.enum(["read-only", "workspace-write", "danger-full-access"]);
const codexAppServerApprovalsReviewerSchema = z.enum(["user", "guardian_subagent"]);
const codexPluginConfigSchema = z
.object({
discovery: z
.object({
enabled: z.boolean().optional(),
timeoutMs: z.number().positive().optional(),
})
.strict()
.optional(),
appServer: z
.object({
transport: codexAppServerTransportSchema.optional(),
command: z.string().optional(),
args: z.union([z.array(z.string()), z.string()]).optional(),
url: z.string().optional(),
authToken: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
requestTimeoutMs: z.number().positive().optional(),
approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
sandbox: codexAppServerSandboxSchema.optional(),
approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
serviceTier: z.string().optional(),
})
.strict()
.optional(),
})
.strict();
export function readCodexPluginConfig(value: unknown): CodexPluginConfig {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return {};
}
return value as CodexPluginConfig;
const parsed = codexPluginConfigSchema.safeParse(value);
return parsed.success ? parsed.data : {};
}
export function resolveCodexAppServerRuntimeOptions(