@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.
91 lines • 4.04 kB
JavaScript
import { loadCredentials } from "../cloud/credential-store.js";
import { pollOnce } from "./poll-client.js";
import { notify as notifyOs } from "./notifier.js";
import { selectFreshDirected } from "./filter.js";
/** Parse `watch` flags. Throws if --peer is missing. */
export function parseWatchArgs(argv) {
const get = (flag) => {
const i = argv.indexOf(flag);
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : undefined;
};
const peerId = get("--peer");
if (!peerId)
throw new Error("watch: --peer <peerId> is required");
const label = get("--label");
const cwd = get("--cwd");
const intervalRaw = Number(get("--interval"));
const intervalSec = Number.isFinite(intervalRaw) && intervalRaw > 0 ? intervalRaw : 15;
return { peerId, ...(label ? { label } : {}), ...(cwd ? { cwd } : {}), intervalSec };
}
function snippet(s) {
return s.replace(/\s+/g, " ").trim().slice(0, 140);
}
/** One poll → notify cycle. Advances state; never re-notifies a seen message. */
export async function runTick(state, ctx) {
const { messages } = await ctx.poll({
endpoint: ctx.creds.endpoint,
sessionId: ctx.creds.sessionId,
peerId: ctx.target.peerId,
token: ctx.creds.token,
lastMessageId: state.lastMessageId,
});
const fresh = selectFreshDirected(messages, ctx.target, state.seen);
for (const m of fresh) {
await ctx.notify(`Cogent: ${m.fromPeerId} → ${ctx.target.peerId}`, snippet(m.message));
}
if (messages.length > 0) {
state.lastMessageId = messages[messages.length - 1].id;
}
}
/**
* Entry point for `cogent-bridge watch`. Loads creds, primes state (seeds `seen`
* from the current backlog WITHOUT notifying), then loops with exponential backoff
* on error. Runs until SIGINT/SIGTERM.
*/
export async function runWatch(argv) {
const cfg = parseWatchArgs(argv);
// Credentials resolve per-cwd via process.cwd() or COGENT_CREDENTIALS_FILE. `--cwd` is
// informational in v1 — run the watcher from the directory you registered in (documented in README).
const creds = await loadCredentials();
if (!creds) {
process.stderr.write("watch: no saved credentials for this directory. Join the channel first " +
"(/cogent:register), or run watch from the same cwd, or set COGENT_CREDENTIALS_FILE.\n");
process.exit(1);
return;
}
const target = { peerId: cfg.peerId, ...(cfg.label ? { label: cfg.label } : {}) };
const ctx = { creds, target, poll: pollOnce, notify: notifyOs };
const state = { seen: new Set() };
// Prime: seed lastMessageId + seen from the backlog without notifying.
try {
const primed = await pollOnce({ endpoint: creds.endpoint, sessionId: creds.sessionId, peerId: cfg.peerId, token: creds.token });
selectFreshDirected(primed.messages, target, state.seen);
if (primed.messages.length > 0)
state.lastMessageId = primed.messages[primed.messages.length - 1].id;
}
catch (err) {
process.stderr.write(`watch: initial poll failed (${err.message}); will retry.\n`);
}
process.stdout.write(`watch: notifying for [→ ${cfg.peerId}] on ${creds.label ?? creds.sessionId} every ${cfg.intervalSec}s. Ctrl-C to stop.\n`);
let stopped = false;
const stop = () => { stopped = true; };
process.on("SIGINT", stop);
process.on("SIGTERM", stop);
let backoff = 0;
while (!stopped) {
const waitMs = (backoff > 0 ? Math.min(cfg.intervalSec * 2 ** backoff, 120) : cfg.intervalSec) * 1000;
await new Promise((r) => setTimeout(r, waitMs));
if (stopped)
break;
try {
await runTick(state, ctx);
backoff = 0;
}
catch (err) {
backoff = Math.min(backoff + 1, 6);
process.stderr.write(`watch: poll error (${err.message}); backing off.\n`);
}
}
process.stdout.write("watch: stopped.\n");
}
//# sourceMappingURL=index.js.map