mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
fix(codex): expose app-server env controls
This commit is contained in:
committed by
Peter Steinberger
parent
09c39463bb
commit
aeb007e4e5
@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Providers/Codex: pass agent and workspace directories into provider stream wrappers so Codex native `web_search` activation can evaluate the correct auth context, and smoke-test the built status-message runtime by resolving the emitted bundle name. Carries forward #67843; refs #65909. Thanks @neilofneils404.
|
||||
- Cron/models: keep `payload.model` as a per-job primary that can use configured fallbacks, while still letting `payload.fallbacks: []` make cron runs strict and avoid hidden agent-primary retries. Refs #73023. Thanks @pavelyortho-cyber.
|
||||
- Models/fallbacks: treat user-selected session models as exact choices, so `/model ollama/...` and model-picker switches fail visibly when the selected provider is unreachable instead of answering from an unrelated configured fallback. Fixes #73023. Thanks @pavelyortho-cyber.
|
||||
- Codex harness: expose `appServer.clearEnv` in the plugin config schema so deployments can keep Gateway-level `OPENAI_API_KEY` for embeddings and direct OpenAI models while removing it from the spawned native Codex app-server process. Fixes #73057. Thanks @holgergruenhagen.
|
||||
- CLI/model probes: fail local `infer model run` probes when the provider returns no text output, so unreachable local providers and empty completions no longer look like successful smoke tests. Refs #73023. Thanks @pavelyortho-cyber.
|
||||
- CLI/Ollama: run local `infer model run` through the lean provider completion path and skip global model discovery for one-shot local probes, so Ollama smoke tests no longer pay full chat-agent/tool startup cost or hang before the native `/api/chat` request. Fixes #72851. Thanks @TotalRes2020.
|
||||
- Doctor/gateway services: ignore launchd/systemd companion services that only reference the gateway as a dependency, suppress inactive Linux extra-service warnings, and avoid rewriting a running systemd gateway command/entrypoint during doctor repair. Carries forward #39118. Thanks @therk.
|
||||
|
||||
@@ -508,22 +508,47 @@ For an already-running app-server, use WebSocket transport:
|
||||
}
|
||||
```
|
||||
|
||||
Stdio app-server launches inherit OpenClaw's process environment by default.
|
||||
When the Gateway needs `OPENAI_API_KEY` for embeddings or direct OpenAI models
|
||||
but Codex should use the local ChatGPT login, clear that variable only for the
|
||||
Codex child:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: {
|
||||
enabled: true,
|
||||
config: {
|
||||
appServer: {
|
||||
clearEnv: ["OPENAI_API_KEY"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`appServer.clearEnv` only affects the spawned Codex app-server child process.
|
||||
|
||||
Supported `appServer` fields:
|
||||
|
||||
| Field | Default | Meaning |
|
||||
| ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `transport` | `"stdio"` | `"stdio"` spawns Codex; `"websocket"` connects to `url`. |
|
||||
| `command` | managed Codex binary | Executable for stdio transport. Leave unset to use the managed binary; set it only for an explicit override. |
|
||||
| `args` | `["app-server", "--listen", "stdio://"]` | Arguments for stdio transport. |
|
||||
| `url` | unset | WebSocket app-server URL. |
|
||||
| `authToken` | unset | Bearer token for WebSocket transport. |
|
||||
| `headers` | `{}` | Extra WebSocket headers. |
|
||||
| `requestTimeoutMs` | `60000` | Timeout for app-server control-plane calls. |
|
||||
| `mode` | `"yolo"` | Preset for YOLO or guardian-reviewed execution. |
|
||||
| `approvalPolicy` | `"never"` | Native Codex approval policy sent to thread start/resume/turn. |
|
||||
| `sandbox` | `"danger-full-access"` | Native Codex sandbox mode sent to thread start/resume. |
|
||||
| `approvalsReviewer` | `"user"` | Use `"auto_review"` to let Codex review native approval prompts. `guardian_subagent` remains a legacy alias. |
|
||||
| `serviceTier` | unset | Optional Codex app-server service tier: `"fast"`, `"flex"`, or `null`. Invalid legacy values are ignored. |
|
||||
| Field | Default | Meaning |
|
||||
| ------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `transport` | `"stdio"` | `"stdio"` spawns Codex; `"websocket"` connects to `url`. |
|
||||
| `command` | managed Codex binary | Executable for stdio transport. Leave unset to use the managed binary; set it only for an explicit override. |
|
||||
| `args` | `["app-server", "--listen", "stdio://"]` | Arguments for stdio transport. |
|
||||
| `url` | unset | WebSocket app-server URL. |
|
||||
| `authToken` | unset | Bearer token for WebSocket transport. |
|
||||
| `headers` | `{}` | Extra WebSocket headers. |
|
||||
| `clearEnv` | `[]` | Environment variable names removed from the spawned stdio app-server process after OpenClaw builds its inherited environment. |
|
||||
| `requestTimeoutMs` | `60000` | Timeout for app-server control-plane calls. |
|
||||
| `mode` | `"yolo"` | Preset for YOLO or guardian-reviewed execution. |
|
||||
| `approvalPolicy` | `"never"` | Native Codex approval policy sent to thread start/resume/turn. |
|
||||
| `sandbox` | `"danger-full-access"` | Native Codex sandbox mode sent to thread start/resume. |
|
||||
| `approvalsReviewer` | `"user"` | Use `"auto_review"` to let Codex review native approval prompts. `guardian_subagent` remains a legacy alias. |
|
||||
| `serviceTier` | unset | Optional Codex app-server service tier: `"fast"`, `"flex"`, or `null`. Invalid legacy values are ignored. |
|
||||
|
||||
Environment overrides remain available for local testing:
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"clearEnv": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"requestTimeoutMs": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
@@ -234,6 +238,11 @@
|
||||
"help": "Additional headers sent to the WebSocket app-server.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.clearEnv": {
|
||||
"label": "Clear Environment",
|
||||
"help": "Environment variable names removed from the spawned stdio app-server process after overrides are applied.",
|
||||
"advanced": true
|
||||
},
|
||||
"appServer.requestTimeoutMs": {
|
||||
"label": "Request Timeout",
|
||||
"help": "Maximum time to wait for Codex app-server control-plane requests.",
|
||||
|
||||
@@ -18,6 +18,7 @@ describe("Codex app-server config", () => {
|
||||
transport: "websocket",
|
||||
url: "ws://127.0.0.1:39175",
|
||||
headers: { "X-Test": "yes" },
|
||||
clearEnv: ["OPENAI_API_KEY"],
|
||||
approvalPolicy: "on-request",
|
||||
sandbox: "danger-full-access",
|
||||
approvalsReviewer: "guardian_subagent",
|
||||
@@ -40,11 +41,29 @@ describe("Codex app-server config", () => {
|
||||
transport: "websocket",
|
||||
url: "ws://127.0.0.1:39175",
|
||||
headers: { "X-Test": "yes" },
|
||||
clearEnv: ["OPENAI_API_KEY"],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("normalizes app-server environment variables to clear", () => {
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig: {
|
||||
appServer: {
|
||||
clearEnv: [" OPENAI_API_KEY ", "", " "],
|
||||
},
|
||||
},
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(runtime.start).toEqual(
|
||||
expect.objectContaining({
|
||||
clearEnv: ["OPENAI_API_KEY"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("drops invalid legacy service tiers without discarding the rest of the config", () => {
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig: {
|
||||
|
||||
@@ -66,6 +66,7 @@ export type CodexPluginConfig = {
|
||||
url?: string;
|
||||
authToken?: string;
|
||||
headers?: Record<string, string>;
|
||||
clearEnv?: string[];
|
||||
requestTimeoutMs?: number;
|
||||
approvalPolicy?: CodexAppServerApprovalPolicy;
|
||||
sandbox?: CodexAppServerSandboxMode;
|
||||
@@ -83,6 +84,7 @@ export const CODEX_APP_SERVER_CONFIG_KEYS = [
|
||||
"url",
|
||||
"authToken",
|
||||
"headers",
|
||||
"clearEnv",
|
||||
"requestTimeoutMs",
|
||||
"approvalPolicy",
|
||||
"sandbox",
|
||||
@@ -152,6 +154,7 @@ const codexPluginConfigSchema = z
|
||||
url: z.string().optional(),
|
||||
authToken: z.string().optional(),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
clearEnv: z.array(z.string()).optional(),
|
||||
requestTimeoutMs: z.number().positive().optional(),
|
||||
approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
|
||||
sandbox: codexAppServerSandboxSchema.optional(),
|
||||
@@ -188,6 +191,7 @@ export function resolveCodexAppServerRuntimeOptions(
|
||||
: "managed";
|
||||
const args = resolveArgs(config.args, env.OPENCLAW_CODEX_APP_SERVER_ARGS);
|
||||
const headers = normalizeHeaders(config.headers);
|
||||
const clearEnv = normalizeStringList(config.clearEnv);
|
||||
const authToken = readNonEmptyString(config.authToken);
|
||||
const url = readNonEmptyString(config.url);
|
||||
const policyMode =
|
||||
@@ -210,6 +214,7 @@ export function resolveCodexAppServerRuntimeOptions(
|
||||
...(url ? { url } : {}),
|
||||
...(authToken ? { authToken } : {}),
|
||||
headers,
|
||||
...(clearEnv.length > 0 ? { clearEnv } : {}),
|
||||
},
|
||||
requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 60_000),
|
||||
approvalPolicy:
|
||||
@@ -373,6 +378,15 @@ function normalizeHeaders(value: unknown): Record<string, string> {
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeStringList(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
return value
|
||||
.map((entry) => readNonEmptyString(entry))
|
||||
.filter((entry): entry is string => entry !== undefined);
|
||||
}
|
||||
|
||||
function readBooleanEnv(value: string | undefined): boolean | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user