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.

90 lines • 3.81 kB
import { FileBackend } from "./file-backend.js"; import { HttpBackend } from "./http-backend.js"; import { HttpClient } from "../cloud/http-client.js"; /** * Singleton accessor for the active storage backend. * * Follows the same pattern as config.ts: explicit init for production, * lazy fallback for tests. getBackend() auto-creates a FileBackend when * no backend has been set, ensuring existing tests work without modification. */ let _backend = null; /** Set the singleton backend (called during server startup). */ export function initBackend(backend) { _backend = backend; } /** * Get the active storage backend. * * If no backend has been set via initBackend(), lazy-initializes a FileBackend. * This makes existing tests pass without modification -- they never call * initBackend(), so getBackend() auto-creates a FileBackend that delegates * to the same service functions the tests already mock via vi.mock(). */ export function getBackend() { if (!_backend) { _backend = new FileBackend(); } return _backend; } /** Reset the singleton to null (for test isolation). */ export function resetBackend() { _backend = null; } /** Check whether a value looks like a cloud endpoint (http:// or https://). */ export function isCloudEndpoint(value) { return /^https?:\/\//i.test(value); } /** * Check whether the active backend is cloud-mode (HttpBackend). * Unlike checking config.COGENT_ENDPOINT, this reflects runtime state * after reinitCloudBackend() may have swapped the backend at runtime. */ export function isCloudMode() { return _backend instanceof HttpBackend; } /** * SECURITY — cloud-configured but NOT yet joined ("deferred"). * * True when the bridge is cloud-CONFIGURED (endpoint is an http(s) relay URL) * yet the active backend is still the local FileBackend placeholder, i.e. the * agent has not completed a join/register and belongs to NO channel yet * (startup.ts deferred branch installs `createBackend(undefined)` until * reinitCloudBackend swaps in the HttpBackend). * * In this state, channel-scoped reads (`cogent_get_history` / `cogent_list_peers`) * MUST return empty rather than read the host-global `~/.cogent/cogent-state.json`, * which can contain OTHER local/deferred agents' peers and message bodies on the * same host — surfacing them would disclose another channel's content to an agent * that is not on it. `endpoint` is passed in (this module deliberately does not * import config; the caller supplies `getConfig().COGENT_ENDPOINT`). * * Genuine local mode (COGENT_LOCAL / non-http endpoint → `endpoint` undefined or * non-URL) returns false: its single-host shared-file semantics are unchanged. */ export function isCloudDeferred(endpoint) { return !!endpoint && isCloudEndpoint(endpoint) && !isCloudMode(); } /** * Factory: create a backend from an optional endpoint string. * * - If endpoint is an HTTP/HTTPS URL, creates an HttpBackend for cloud mode. * Requires sessionId and token (from env vars or tool-based session setup). * - Otherwise returns a FileBackend for local file-based state. * * Note: Does NOT import config.ts to avoid circular dependency risk * (decision from 02B-01). Caller passes params explicitly. */ export function createBackend(endpoint, sessionId, token) { if (endpoint && isCloudEndpoint(endpoint)) { if (!sessionId || !token) { throw new Error("Cloud mode requires COGENT_SESSION_ID and COGENT_SECRET " + "(or use create-session/join-session tools). " + `Got endpoint: ${endpoint}`); } const httpClient = new HttpClient(endpoint, token); return new HttpBackend(httpClient, sessionId); } return new FileBackend(); } //# sourceMappingURL=backend-provider.js.map