/** * Shared constants and small utilities for pi-c2c. * * These are pure values or simple env-reading helpers used across multiple * modules (index.ts, tools.ts, commands.ts, debug.ts). Keeping them here * avoids circular imports. */ import { createRequire } from "node:module"; import * as os from "node:os"; import * as path from "node:path"; import type { RelayMessage } from "./c2c-cli.ts"; import type { C2cMessage } from "./c2c-cli.ts"; const requirePackageJson = createRequire(import.meta.url); export const PI_C2C_VERSION = (requirePackageJson("../package.json") as { version?: string }).version ?? "unknown"; export const SPOOL_DIR = path.join(os.homedir(), ".pi", "c2c"); export const SPOOL_TTL_MS = 7 * 24 * 60 * 60 * 1000; // GC spool files older than a week // Default to 5s. The pollTick drains 3 sources (per-repo, sessions, relay) // and dedups + injects. At 5s, worst-case e2e latency is ~5s + ~1s drain = // ~6s, down from the old 30s default which made relay messages routinely // take 30-45s. C2C_PI_POLL_INTERVAL_MS overrides (min 1000ms) for power // users; inotify push delivery is the next step (see task #35). const DEFAULT_POLL_INTERVAL_MS = 5_000; export function readPollInterval(): number { const raw = Number.parseInt(process.env.C2C_PI_POLL_INTERVAL_MS ?? "", 10); return Number.isFinite(raw) && raw >= 1000 ? raw : DEFAULT_POLL_INTERVAL_MS; } export function readStatusInterval(): number { const raw = Number.parseInt(process.env.C2C_PI_STATUS_INTERVAL_MS ?? "", 10); return Number.isFinite(raw) && raw >= 500 ? raw : 2_000; } export function readAutoJoinRooms(): string[] { return (process.env.C2C_PI_AUTO_JOIN_ROOMS ?? "") .split(",") .map((s) => s.trim()) .filter(Boolean); } /** * Render the raw `collectDebugState` text as a small aligned table for * pi's `ctx.ui.notify`. The TUI strips trailing whitespace and may wrap * lines, so we deliberately keep it minimal: aligned two-column key/value * rows, no box-drawing characters, no right-side padding. Problems are * listed under a `--- problems ---` header with a remedy line per problem. */ export function formatDebugTable(raw: string): string { const KEY_WIDTH = 16; const lines: string[] = []; const problems: string[] = []; let inProblems = false; for (const line of raw.split("\n")) { if (line.startsWith("=== c2c pi debug ===")) continue; if (line.startsWith("=== problems ===")) { inProblems = true; continue; } if (inProblems) { problems.push(line); continue; } const m = line.match(/^([^:]+):\s*(.*)$/); if (!m) { if (line.length > 0) lines.push(line); continue; } const [, key, value] = m; lines.push(`${key.padEnd(KEY_WIDTH, " ")} ${value}`); } let out = lines.join("\n"); if (problems.length > 0) { out += "\n\n--- problems ---\n"; out += problems.join("\n"); } return out; } /** Type alias for the relay-to-c2c converter function. */ export type RelayToC2cFn = (msgs: RelayMessage[]) => C2cMessage[]; /** * Map a relay DM envelope (`fromAlias`/`toAlias`) into the broker DM shape * (`from_alias`/`to_alias`) so the rest of the drain pipeline is identical * to local broker messages — status filtering, dedup, spool, inject. */ export function relayToC2c(msgs: RelayMessage[]): C2cMessage[] { const out: C2cMessage[] = []; for (const m of msgs) { out.push({ from_alias: m.fromAlias, to_alias: m.toAlias, content: m.content, ts: m.ts, source: "relay", kind: "dm", }); } return out; }