UNPKG

@essentialai/cogent-bridge

Version:

Cogent Bridge — cross-agent comms for Claude Code, OpenAI Codex and Slack. Codex users: install with `curl -fsSL https://cogent.tools/install.sh | sh`, then start with `cogent-codex` for real-time peer wake.

129 lines • 6.8 kB
import type { Config } from "../config.js"; /** * Codex CLI flag preflight (mock-hidden-gap audit, 2026-05-27, gap 5). * * Why this exists: `execCodex` (codex-cli.ts) drives auto-relay by spawning * `codex exec resume <id> -c sandbox_mode=… --skip-git-repo-check -o <file> <msg>`. * The unit tests mock `child_process`, so they only prove the args we *intend* to * pass — NOT that the real `codex exec resume` subcommand accepts them. That gap * already caused three production breaks (`--color`, `--sandbox`, `--full-auto` * were each rejected by `exec resume` at exit 2 — see codex-cli.ts comments and * memory `cli-flag-subcommand-verification`). * * This preflight, run at startup when COGENT_PLATFORM=codex, (1) verifies the * `codex` binary resolves, and (2) runs `codex exec resume --help` and asserts * every flag we emit is documented by the installed CLI. A drift (new codex * version renames/drops a flag) surfaces as a loud startup warning instead of a * silent exit-2 on the first auto-relay. */ /** * The long-form flags `execCodex` emits on the `exec resume` subcommand. Keep * this in sync with `execCodex`/`sandboxArgs` in codex-cli.ts. We assert the * long forms (the short `-c`/`-o` always co-document with `--config`/ * `--output-last-message` in codex help output). */ export declare const EXPECTED_CODEX_RESUME_FLAGS: ReadonlyArray<{ flag: string; usedFor: string; }>; export interface CodexFlagAnalysis { missing: string[]; checked: string[]; } /** * Pure decision logic: given the text of `codex exec resume --help`, return which * expected flags are absent. Unit-testable with no spawning. */ export declare function analyzeCodexHelp(helpText: string): CodexFlagAnalysis; /** * Verify the codex binary exists and that `codex exec resume` accepts the flags * auto-relay emits. Never throws — a preflight failure must not stop startup; we * only want the problem visible in the logs. Returns the analysis, or null if the * help text could not be obtained (binary missing / call failed). */ export declare function preflightCodex(codexPath: string): Promise<CodexFlagAnalysis | null>; /** * Whether the host can initialize the Codex OS sandbox (an unprivileged Linux * user namespace, via bubblewrap). `blocked` is the failure that makes every * `codex exec resume` report "not finished" (bwrap: setting up uid map: * Permission denied). `na` = not applicable (bypass mode, or non-Linux host). */ export type SandboxProbeStatus = "ok" | "blocked" | "unknown" | "na"; export interface SandboxProbeResult { status: SandboxProbeStatus; detail: string; } /** * Pure decision logic: interpret the probe signals into a status. Unit-testable * with no spawning. `unshareOk` undefined = the `unshare` binary was unavailable, * so we fall back to the kernel sysctl signals. */ export declare function interpretSandboxProbe(s: { platform: NodeJS.Platform; mode: Config["COGENT_CODEX_SANDBOX"]; unshareOk?: boolean; apparmorRestrict?: string | null; usernsClone?: string | null; }): SandboxProbeResult; /** * Pure: map an `unshare -Ur true` execFile rejection to a userns signal. Returns * `false` (blocked) ONLY for a genuine permission-denied refusal — the process * ran, exited non-zero, and stderr shows the userns op was denied (real * Ubuntu-24.04/hardened case, e.g. "write failed /proc/self/uid_map: Operation * not permitted"). Everything else is INCONCLUSIVE → `undefined`: a timeout/kill * (`err.killed`, `code` null), a spawn error (ENOENT/EACCES/EAGAIN — `code` is a * string errno), or a CLI usage error (a busybox `unshare` that lacks `-Ur`, * which exits non-zero for an unrelated reason). An inconclusive result makes the * caller fall through to the kernel sysctls and ultimately "unknown" (which never * engages the bypass) — the line between failing SAFE and failing OPEN on a * transient startup hiccup on a sandbox-capable host. */ export declare function classifyUnshareError(err: { killed?: boolean; code?: string | number | null; stderr?: string; } | null | undefined): boolean | undefined; /** * Gather sandbox-capability signals for the host and interpret them. Never throws. * Probes `unshare -Ur true` (the exact op bubblewrap needs); if `unshare` is * missing, falls back to reading the userns sysctls. Skips the probe (returns * `na`) for `bypass` mode or non-Linux hosts. */ export declare function probeSandboxStatus(config: Config): Promise<SandboxProbeResult>; /** * Startup sandbox preflight — surface the Codex OS-sandbox capability LOUDLY so * a blocked host never silently loops on "not finished". Never throws. Returns * the probe result so startup can apply the COGENT_CODEX_SANDBOX_FALLBACK * recovery. Fallback-aware: when auto-recovery is on (the default) a blocked host * is a WARN (Cogent will bypass the dead sandbox so tasks run); when an operator * has opted out it is an ERROR (tasks will fail until they fix the host). */ export declare function preflightCodexSandbox(config: Config): Promise<SandboxProbeResult>; /** * Pure: does this `~/.codex/config.toml` text declare a STANDALONE * `[mcp_servers.cogent]` server? That name collides with the `cogent@cogent` * plugin's OWN MCP server (both named `cogent`) — Codex collapses the two by name * and MCP startup fails ("MCP client for cogent failed to start … connection * closed: initialize response"). Latent until 3.19.2 made the plugin actually * start. See docs/update-and-troubleshooting.md + memory * `codex-app-server-wake-shipped-3.20.0`. * * Matches only the TOML TABLE HEADER (anchored to line start, optional leading * whitespace) so a `#`-comment or prose mention never trips it, and requires the * server name to be exactly `cogent` (so `[mcp_servers.cogent-cli]` and other * names are ignored). A bare `[mcp_servers.cogent]` OR any subtable * (`[mcp_servers.cogent.env]`, `[mcp_servers.cogent.tools.x]`) counts. */ export declare function detectCodexCogentServerCollision(configTomlText: string): boolean; /** Absolute path to the codex config, honouring CODEX_HOME (falls back to ~/.codex). */ export declare function codexConfigPath(codexHomeDir?: string): string; /** * Startup preflight: warn LOUDLY if the codex config defines a standalone * `[mcp_servers.cogent]` that collides with the plugin's own `cogent` server. * Never throws (a missing/unreadable config is simply "no collision"). Runs for * every codex bridge (plugin users included), so plugin-only users who never * launch via `cogent-codex` still get flagged. Returns true iff a collision was * found. */ export declare function preflightCodexServerCollision(codexHomeDir?: string): Promise<boolean>; //# sourceMappingURL=codex-preflight.d.ts.map