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.

135 lines • 6.47 kB
import fs from "node:fs/promises"; import path from "node:path"; import { execFile } from "node:child_process"; import { getConfig } from "../config.js"; import { SERVER_VERSION } from "../constants.js"; import { probeSandboxStatus } from "./codex-preflight.js"; /* ------------------------------------------------------------------ */ /* Derive paths from config at call time (not module load time) */ /* ------------------------------------------------------------------ */ function getStatePath() { return path.join(getConfig().COGENT_STATE_PATH, "cogent-state.json"); } /* ------------------------------------------------------------------ */ /* Sub-checks */ /* ------------------------------------------------------------------ */ /** HLTH-01: Check state file accessibility. */ async function checkStateFile() { const statePath = getStatePath(); const dir = path.dirname(statePath); try { await fs.access(dir, fs.constants.R_OK | fs.constants.W_OK); } catch { return { ok: false, message: `State directory not accessible: ${dir}` }; } try { const raw = await fs.readFile(statePath, "utf-8"); JSON.parse(raw); return { ok: true, message: "State file readable and valid JSON" }; } catch (err) { if (err.code === "ENOENT") { return { ok: true, message: "State directory writable (no state file yet)" }; } if (err instanceof SyntaxError) { return { ok: false, message: "State file exists but contains invalid JSON" }; } return { ok: false, message: `State file error: ${err.message}` }; } } /** HLTH-02: Check lock mechanism with a separate health-check lock path. */ async function checkLockMechanism() { const healthLockPath = getStatePath() + ".health-lock"; try { await fs.writeFile(healthLockPath, String(process.pid), { flag: "wx" }); try { await fs.unlink(healthLockPath); } catch { // Cleanup failure is non-fatal } return { ok: true, message: "Lock acquire/release cycle succeeded" }; } catch (err) { if (err.code === "EEXIST") { return { ok: false, message: "Health check lock file exists unexpectedly" }; } return { ok: false, message: `Lock mechanism error: ${err.message}` }; } } /** HLTH-03: Check Claude CLI availability. */ function checkClaudeCli() { const claudePath = getConfig().COGENT_CLAUDE_PATH; return new Promise((resolve) => { execFile(claudePath, ["--version"], { timeout: 5000 }, (error, stdout) => { if (error) { resolve({ ok: false, message: `Claude CLI not found at '${claudePath}'` }); return; } resolve({ ok: true, message: "Claude CLI found", version: (stdout || "").trim() }); }); }); } /** HLTH-04: Can the host initialize the Codex OS sandbox? (codex platform only.) */ export async function checkCodexSandbox() { const config = getConfig(); if (config.COGENT_PLATFORM !== "codex") { return { ok: true, message: "n/a (platform is not codex)" }; } const probe = await probeSandboxStatus(config); if (probe.status === "blocked") { if (config.COGENT_CODEX_SANDBOX_FALLBACK) { // 🔴 SCOPE THE CLAIM. This used to end "...so codex runs unsandboxed", which reads as a // statement about the reader's own codex session. It is not, and cannot be: the fallback // calls setSandboxOverride("bypass") (startup.ts), which only reaches sandboxArgs() in // codex-cli.ts, whose single non-test caller is execCodex via exec-remote.ts — i.e. the // `codex exec resume` subprocesses COGENT spawns to wake an agent. An already-running // interactive codex session was launched separately with its own sandbox/approval profile // and is untouched by this setting. // // Reported 2026-09-17 by an agent that read this line, concluded its own session should be // unsandboxed, and then had to explain why bwrap was still failing — the message sent it // looking in the wrong place. Same family as relay-fabricates-success: a field that reports // what Cogent CONFIGURED, not what the reader's session will DO. return { ok: true, message: `Sandbox blocked (${probe.detail}) — auto-bypass is ON, so codex sessions COGENT WAKES ` + `run unsandboxed. This does NOT change an already-running interactive codex session: ` + `that keeps the sandbox/approval profile it was launched with, and on this host its ` + `bwrap-backed modes will still fail. Relaunch it with ` + `--dangerously-bypass-approvals-and-sandbox, or enable unprivileged userns, to fix that.`, }; } return { ok: false, message: `Sandbox blocked: ${probe.detail}. Codex can't edit/test — set COGENT_CODEX_SANDBOX_FALLBACK=1, COGENT_CODEX_SANDBOX=bypass, or enable unprivileged userns` }; } if (probe.status === "unknown") { return { ok: true, message: `Sandbox capability unknown: ${probe.detail}` }; } return { ok: true, message: probe.detail }; } /* ------------------------------------------------------------------ */ /* Public API */ /* ------------------------------------------------------------------ */ /** Run all health checks and return a comprehensive result. */ export async function checkHealth() { const config = getConfig(); // Run all checks (not short-circuiting) const [stateFile, lockMechanism, claudeCli, codexSandbox] = await Promise.all([ checkStateFile(), checkLockMechanism(), checkClaudeCli(), checkCodexSandbox(), ]); const checks = { stateFile, lockMechanism, claudeCli, codexSandbox }; const healthy = Object.values(checks).every((c) => c.ok); return { healthy, mcpServerVersion: SERVER_VERSION, statePath: path.join(config.COGENT_STATE_PATH, "cogent-state.json"), claudePath: config.COGENT_CLAUDE_PATH, checks, timestamp: new Date().toISOString(), }; } //# sourceMappingURL=health-check.js.map