mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-23 23:52:18 +03:00
Merged after conflict resolution: the 5 conflicting test files were the base-red drains that #11201 already landed on the tip — kept the tip versions; the feature content is untouched. Validated on the combined batch board + this branch: codex-app-server + codex-gpt56-catalog 25/25, typecheck:core clean, docs-counts green (351 providers), provider-consistency 268/351/0. The opt-in codex-app-server transport (JSON-RPC-over-WS, turn/completed-awaited close, Responses SSE bridge) leaves the default codex path untouched. Thank you @arminanton — a 3.4k-line transport with the docs wave and tests to match!
95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
|
|
/**
|
|
* Resolved connection config for the Codex app-server WS transport.
|
|
*
|
|
* The app-server is a locally-running `codex app-server` process reachable over a
|
|
* single WebSocket speaking JSON-RPC 2.0. It self-manages OpenAI auth + model
|
|
* routing; the ONLY credential OmniRoute presents is the capability token, sent as
|
|
* `Authorization: Bearer <hex>` on the WS handshake.
|
|
*/
|
|
export interface CodexAppServerConfig {
|
|
/** ws:// or wss:// URL of the app-server (e.g. "ws://ts-egress:1456"). */
|
|
url: string;
|
|
/** Capability token (hex string) sent as `Authorization: Bearer <token>`. */
|
|
token: string;
|
|
/** Working directory passed to `thread/start { cwd }` inside the codex container. */
|
|
cwd: string;
|
|
/**
|
|
* Optional codex approval policy override (AskForApproval). Defaults to "never"
|
|
* in the executor so codex runs non-interactively and never blocks the turn on
|
|
* its own approval — the harness that consumes OmniRoute owns execution policy.
|
|
*/
|
|
approvalPolicy?: string;
|
|
/**
|
|
* Optional codex sandbox override (SandboxMode). Defaults to "danger-full-access"
|
|
* in the executor so codex's own sandbox does not gate the model; the harness is
|
|
* the real gate. Callers may tighten this per request via providerSpecificData.
|
|
*/
|
|
sandbox?: string;
|
|
}
|
|
|
|
type ProviderSpecificData = Record<string, unknown> | null | undefined;
|
|
|
|
function firstString(...values: unknown[]): string | null {
|
|
for (const value of values) {
|
|
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Read the capability token, preferring an inline token, then a token FILE path.
|
|
* The token file (produced by `codex app-server --ws-token-file <path>`) holds the
|
|
* same hex string that is presented as the bearer token.
|
|
*/
|
|
function resolveToken(psd: ProviderSpecificData): string | null {
|
|
const inline = firstString(
|
|
psd?.codexAppServerToken,
|
|
process.env.OMNIROUTE_CODEX_APPSERVER_WS_TOKEN
|
|
);
|
|
if (inline) return inline;
|
|
|
|
const tokenFile = firstString(
|
|
psd?.codexAppServerTokenFile,
|
|
process.env.OMNIROUTE_CODEX_APPSERVER_WS_TOKEN_FILE
|
|
);
|
|
if (!tokenFile) return null;
|
|
try {
|
|
const contents = readFileSync(tokenFile, "utf8").trim();
|
|
return contents.length > 0 ? contents : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function isWebSocketUrl(url: string): boolean {
|
|
return url.startsWith("ws://") || url.startsWith("wss://");
|
|
}
|
|
|
|
/**
|
|
* Resolve the app-server connection config from providerSpecificData with env
|
|
* fallbacks. Returns `null` when not fully configured (URL + token both required)
|
|
* so the gating predicate `isCodexAppServerRequired` stays false and Codex falls
|
|
* back to its other transports.
|
|
*/
|
|
export function resolveAppServerConfig(psd: ProviderSpecificData): CodexAppServerConfig | null {
|
|
const url = firstString(psd?.codexAppServerUrl, process.env.OMNIROUTE_CODEX_APPSERVER_WS);
|
|
if (!url || !isWebSocketUrl(url)) return null;
|
|
|
|
const token = resolveToken(psd);
|
|
if (!token) return null;
|
|
|
|
const cwd =
|
|
firstString(psd?.codexAppServerCwd, process.env.OMNIROUTE_CODEX_APPSERVER_CWD) ?? "/tmp";
|
|
|
|
const approvalPolicy =
|
|
firstString(psd?.codexAppServerApprovalPolicy, process.env.OMNIROUTE_CODEX_APPSERVER_APPROVAL) ??
|
|
undefined;
|
|
const sandbox =
|
|
firstString(psd?.codexAppServerSandbox, process.env.OMNIROUTE_CODEX_APPSERVER_SANDBOX) ??
|
|
undefined;
|
|
|
|
return { url, token, cwd, ...(approvalPolicy ? { approvalPolicy } : {}), ...(sandbox ? { sandbox } : {}) };
|
|
}
|