@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.
75 lines • 3.94 kB
JavaScript
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { logger } from "../logger.js";
const execFileAsync = promisify(execFile);
/**
* Claude CLI flag preflight (mock-hidden-gap audit, 2026-05-27, gap 5 — cc-cli
* counterpart of codex-preflight.ts).
*
* Why this exists: `execClaude` (cc-cli.ts) drives auto-relay by spawning
* `claude --resume <id> --dangerously-skip-permissions --no-session-persistence
* --strict-mcp-config -p <msg>`. The unit tests mock `child_process`, so they
* only prove the args we *intend* — NOT that the installed `claude` accepts them.
* Startup previously ran only `claude --version`, which does not validate flag
* parsing. This preflight runs `claude --help` and warns loudly if the installed
* CLI drops/renames a flag we depend on, instead of failing on the first
* auto-relay. Mirrors codex-preflight.ts exactly.
*/
/**
* The long-form flags `execClaude` emits. Keep in sync with `execClaude` in
* cc-cli.ts. We assert long forms (`--print` documents the `-p` we pass; the
* short form always co-documents with its long form in claude --help).
*/
export const EXPECTED_CLAUDE_FLAGS = [
{ flag: "--resume", usedFor: "resume the target CC session" },
{ flag: "--print", usedFor: "-p non-interactive single-shot reply" },
{ flag: "--dangerously-skip-permissions", usedFor: "unattended auto-relay execution" },
{ flag: "--no-session-persistence", usedFor: "don't pollute the resumed session transcript" },
{ flag: "--strict-mcp-config", usedFor: "isolate MCP config of the resumed process" },
{ flag: "--output-format", usedFor: "structured JSON reply envelope (Slice 3 capture)" },
{ flag: "--tools", usedFor: "read-only wake: Read, Glob and Grep only (COG-86)" },
{ flag: "--append-system-prompt", usedFor: "tell a read-only wake it cannot change anything (COG-86)" },
];
/**
* Pure decision logic: given the text of `claude --help`, return which expected
* flags are absent. Unit-testable with no spawning.
*/
export function analyzeClaudeHelp(helpText) {
const checked = EXPECTED_CLAUDE_FLAGS.map((f) => f.flag);
const missing = EXPECTED_CLAUDE_FLAGS.filter((f) => !helpText.includes(f.flag)).map((f) => f.flag);
return { missing, checked };
}
/**
* Verify the claude binary exists and that `claude --help` documents the flags
* auto-relay emits. Never throws — a preflight failure must not stop startup; we
* only want visibility. Returns the analysis, or null if help could not be
* obtained (binary missing / call failed).
*/
export async function preflightClaude(claudePath) {
let helpText;
try {
const { stdout, stderr } = await execFileAsync(claudePath, ["--help"], {
timeout: 10000,
});
helpText = `${stdout}\n${stderr}`;
}
catch (err) {
logger.warn(`Claude preflight: could not run '${claudePath} --help' (${err instanceof Error ? err.message : String(err)}). The claude CLI may be missing — auto-relay to CC peers will fail until ` +
`it is installed. Set COGENT_CLAUDE_PATH if it lives in a non-standard location.`);
return null;
}
const analysis = analyzeClaudeHelp(helpText);
if (analysis.missing.length > 0) {
const detail = EXPECTED_CLAUDE_FLAGS.filter((f) => analysis.missing.includes(f.flag))
.map((f) => `${f.flag} (${f.usedFor})`)
.join(", ");
logger.error(`Claude preflight: the installed 'claude' does NOT document these flags that ` +
`auto-relay emits: ${detail}. Auto-relay to CC peers will likely fail. Update ` +
`cc-cli.ts to match the installed CLI, or install a compatible claude version.`);
}
else {
logger.info(`Claude preflight OK: 'claude --help' documents all ${analysis.checked.length} flags auto-relay emits.`);
}
return analysis;
}
//# sourceMappingURL=claude-preflight.js.map