@essentialai/cogent-bridge 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +311 -0
  3. package/dist/backend/backend-provider.d.ts +28 -0
  4. package/dist/backend/backend-provider.d.ts.map +1 -0
  5. package/dist/backend/backend-provider.js +60 -0
  6. package/dist/backend/backend-provider.js.map +1 -0
  7. package/dist/backend/file-backend.d.ts +22 -0
  8. package/dist/backend/file-backend.d.ts.map +1 -0
  9. package/dist/backend/file-backend.js +46 -0
  10. package/dist/backend/file-backend.js.map +1 -0
  11. package/dist/backend/http-backend.d.ts +94 -0
  12. package/dist/backend/http-backend.d.ts.map +1 -0
  13. package/dist/backend/http-backend.js +185 -0
  14. package/dist/backend/http-backend.js.map +1 -0
  15. package/dist/backend/index.d.ts +5 -0
  16. package/dist/backend/index.d.ts.map +1 -0
  17. package/dist/backend/index.js +4 -0
  18. package/dist/backend/index.js.map +1 -0
  19. package/dist/backend/storage-backend.d.ts +22 -0
  20. package/dist/backend/storage-backend.d.ts.map +1 -0
  21. package/dist/backend/storage-backend.js +2 -0
  22. package/dist/backend/storage-backend.js.map +1 -0
  23. package/dist/cli.d.ts +3 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +18 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/cloud/backoff.d.ts +19 -0
  28. package/dist/cloud/backoff.d.ts.map +1 -0
  29. package/dist/cloud/backoff.js +32 -0
  30. package/dist/cloud/backoff.js.map +1 -0
  31. package/dist/cloud/credential-store.d.ts +29 -0
  32. package/dist/cloud/credential-store.d.ts.map +1 -0
  33. package/dist/cloud/credential-store.js +38 -0
  34. package/dist/cloud/credential-store.js.map +1 -0
  35. package/dist/cloud/http-client.d.ts +36 -0
  36. package/dist/cloud/http-client.d.ts.map +1 -0
  37. package/dist/cloud/http-client.js +94 -0
  38. package/dist/cloud/http-client.js.map +1 -0
  39. package/dist/cloud/index.d.ts +10 -0
  40. package/dist/cloud/index.d.ts.map +1 -0
  41. package/dist/cloud/index.js +7 -0
  42. package/dist/cloud/index.js.map +1 -0
  43. package/dist/cloud/message-inbox.d.ts +49 -0
  44. package/dist/cloud/message-inbox.d.ts.map +1 -0
  45. package/dist/cloud/message-inbox.js +109 -0
  46. package/dist/cloud/message-inbox.js.map +1 -0
  47. package/dist/cloud/ws-client.d.ts +112 -0
  48. package/dist/cloud/ws-client.d.ts.map +1 -0
  49. package/dist/cloud/ws-client.js +241 -0
  50. package/dist/cloud/ws-client.js.map +1 -0
  51. package/dist/cloud/ws-frames.d.ts +66 -0
  52. package/dist/cloud/ws-frames.d.ts.map +1 -0
  53. package/dist/cloud/ws-frames.js +19 -0
  54. package/dist/cloud/ws-frames.js.map +1 -0
  55. package/dist/config.d.ts +40 -0
  56. package/dist/config.d.ts.map +1 -0
  57. package/dist/config.js +39 -0
  58. package/dist/config.js.map +1 -0
  59. package/dist/constants.d.ts +3 -0
  60. package/dist/constants.d.ts.map +1 -0
  61. package/dist/constants.js +6 -0
  62. package/dist/constants.js.map +1 -0
  63. package/dist/e2e/helpers.d.ts +101 -0
  64. package/dist/e2e/helpers.d.ts.map +1 -0
  65. package/dist/e2e/helpers.js +228 -0
  66. package/dist/e2e/helpers.js.map +1 -0
  67. package/dist/errors.d.ts +40 -0
  68. package/dist/errors.d.ts.map +1 -0
  69. package/dist/errors.js +53 -0
  70. package/dist/errors.js.map +1 -0
  71. package/dist/index.d.ts +2 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +108 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/logger.d.ts +22 -0
  76. package/dist/logger.d.ts.map +1 -0
  77. package/dist/logger.js +79 -0
  78. package/dist/logger.js.map +1 -0
  79. package/dist/services/cc-cli.d.ts +8 -0
  80. package/dist/services/cc-cli.d.ts.map +1 -0
  81. package/dist/services/cc-cli.js +104 -0
  82. package/dist/services/cc-cli.js.map +1 -0
  83. package/dist/services/health-check.d.ts +33 -0
  84. package/dist/services/health-check.d.ts.map +1 -0
  85. package/dist/services/health-check.js +96 -0
  86. package/dist/services/health-check.js.map +1 -0
  87. package/dist/services/peer-registry.d.ts +9 -0
  88. package/dist/services/peer-registry.d.ts.map +1 -0
  89. package/dist/services/peer-registry.js +207 -0
  90. package/dist/services/peer-registry.js.map +1 -0
  91. package/dist/startup.d.ts +18 -0
  92. package/dist/startup.d.ts.map +1 -0
  93. package/dist/startup.js +270 -0
  94. package/dist/startup.js.map +1 -0
  95. package/dist/tools/create-session.d.ts +12 -0
  96. package/dist/tools/create-session.d.ts.map +1 -0
  97. package/dist/tools/create-session.js +113 -0
  98. package/dist/tools/create-session.js.map +1 -0
  99. package/dist/tools/deregister-peer.d.ts +3 -0
  100. package/dist/tools/deregister-peer.d.ts.map +1 -0
  101. package/dist/tools/deregister-peer.js +38 -0
  102. package/dist/tools/deregister-peer.js.map +1 -0
  103. package/dist/tools/get-history.d.ts +3 -0
  104. package/dist/tools/get-history.d.ts.map +1 -0
  105. package/dist/tools/get-history.js +40 -0
  106. package/dist/tools/get-history.js.map +1 -0
  107. package/dist/tools/health-check.d.ts +3 -0
  108. package/dist/tools/health-check.d.ts.map +1 -0
  109. package/dist/tools/health-check.js +28 -0
  110. package/dist/tools/health-check.js.map +1 -0
  111. package/dist/tools/join-session.d.ts +12 -0
  112. package/dist/tools/join-session.d.ts.map +1 -0
  113. package/dist/tools/join-session.js +90 -0
  114. package/dist/tools/join-session.js.map +1 -0
  115. package/dist/tools/list-peers.d.ts +3 -0
  116. package/dist/tools/list-peers.d.ts.map +1 -0
  117. package/dist/tools/list-peers.js +36 -0
  118. package/dist/tools/list-peers.js.map +1 -0
  119. package/dist/tools/register-peer.d.ts +3 -0
  120. package/dist/tools/register-peer.d.ts.map +1 -0
  121. package/dist/tools/register-peer.js +155 -0
  122. package/dist/tools/register-peer.js.map +1 -0
  123. package/dist/tools/send-message.d.ts +3 -0
  124. package/dist/tools/send-message.d.ts.map +1 -0
  125. package/dist/tools/send-message.js +121 -0
  126. package/dist/tools/send-message.js.map +1 -0
  127. package/dist/types.d.ts +31 -0
  128. package/dist/types.d.ts.map +1 -0
  129. package/dist/types.js +2 -0
  130. package/dist/types.js.map +1 -0
  131. package/dist/wizard/detect.d.ts +4 -0
  132. package/dist/wizard/detect.d.ts.map +1 -0
  133. package/dist/wizard/detect.js +21 -0
  134. package/dist/wizard/detect.js.map +1 -0
  135. package/dist/wizard/index.d.ts +5 -0
  136. package/dist/wizard/index.d.ts.map +1 -0
  137. package/dist/wizard/index.js +140 -0
  138. package/dist/wizard/index.js.map +1 -0
  139. package/dist/wizard/prompts.d.ts +18 -0
  140. package/dist/wizard/prompts.d.ts.map +1 -0
  141. package/dist/wizard/prompts.js +66 -0
  142. package/dist/wizard/prompts.js.map +1 -0
  143. package/dist/wizard/scaffold-demo.d.ts +6 -0
  144. package/dist/wizard/scaffold-demo.d.ts.map +1 -0
  145. package/dist/wizard/scaffold-demo.js +37 -0
  146. package/dist/wizard/scaffold-demo.js.map +1 -0
  147. package/dist/wizard/scaffold-real.d.ts +16 -0
  148. package/dist/wizard/scaffold-real.d.ts.map +1 -0
  149. package/dist/wizard/scaffold-real.js +64 -0
  150. package/dist/wizard/scaffold-real.js.map +1 -0
  151. package/dist/wizard/templates.d.ts +9 -0
  152. package/dist/wizard/templates.d.ts.map +1 -0
  153. package/dist/wizard/templates.js +182 -0
  154. package/dist/wizard/templates.js.map +1 -0
  155. package/package.json +71 -0
  156. package/server.json +60 -0
package/dist/logger.js ADDED
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const LEVELS = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3,
8
+ };
9
+ export class Logger {
10
+ threshold;
11
+ textStream = null;
12
+ jsonStream = null;
13
+ constructor(level, logDir) {
14
+ this.threshold = LEVELS[level];
15
+ if (logDir) {
16
+ fs.mkdirSync(logDir, { recursive: true });
17
+ const now = new Date()
18
+ .toISOString()
19
+ .replace(/:/g, "-")
20
+ .replace(/\.\d{3}Z$/, "");
21
+ const textFile = `bridge-${now}.log`;
22
+ const jsonFile = `bridge-${now}.json.log`;
23
+ this.textStream = fs.createWriteStream(path.join(logDir, textFile), {
24
+ flags: "a",
25
+ });
26
+ this.jsonStream = fs.createWriteStream(path.join(logDir, jsonFile), {
27
+ flags: "a",
28
+ });
29
+ }
30
+ }
31
+ log(level, message, data) {
32
+ if (LEVELS[level] < this.threshold)
33
+ return;
34
+ const timestamp = new Date().toISOString();
35
+ const humanLine = `${timestamp} [${level.toUpperCase()}] ${message}\n`;
36
+ try {
37
+ process.stderr.write(humanLine);
38
+ }
39
+ catch {
40
+ // Ignore EPIPE / broken pipe -- parent process disconnected
41
+ }
42
+ this.textStream?.write(humanLine);
43
+ const entry = { timestamp, level, message };
44
+ if (data !== undefined) {
45
+ entry.data = data;
46
+ }
47
+ this.jsonStream?.write(JSON.stringify(entry) + "\n");
48
+ }
49
+ debug(message, data) {
50
+ this.log("debug", message, data);
51
+ }
52
+ info(message, data) {
53
+ this.log("info", message, data);
54
+ }
55
+ warn(message, data) {
56
+ this.log("warn", message, data);
57
+ }
58
+ error(message, data) {
59
+ this.log("error", message, data);
60
+ }
61
+ close() {
62
+ this.textStream?.end();
63
+ this.jsonStream?.end();
64
+ }
65
+ }
66
+ export function createLogger(level, logDir) {
67
+ return new Logger(level, logDir);
68
+ }
69
+ /** Pre-config logger: stderr-only at info level. Replaced after startup. */
70
+ export let logger = new Logger("info");
71
+ /**
72
+ * Initialize the fully-configured logger with file outputs.
73
+ * Replaces the module-level `logger` export.
74
+ */
75
+ export function initLogger(level, logDir) {
76
+ logger.close();
77
+ logger = new Logger(level, logDir);
78
+ }
79
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { CliExecResult } from "../types.js";
2
+ /**
3
+ * Check whether a Claude Code session file exists on disk.
4
+ * Returns false if the file is missing (session ended or ID is wrong).
5
+ */
6
+ export declare function validateSession(sessionId: string, cwd: string): Promise<boolean>;
7
+ export declare function execClaude(sessionId: string, message: string, cwd: string, timeoutMs?: number): Promise<CliExecResult>;
8
+ //# sourceMappingURL=cc-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cc-cli.d.ts","sourceRoot":"","sources":["../../src/services/cc-cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5C;;;GAGG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAqGxB"}
@@ -0,0 +1,104 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { getConfig } from "../config.js";
6
+ /**
7
+ * Check whether a Claude Code session file exists on disk.
8
+ * Returns false if the file is missing (session ended or ID is wrong).
9
+ */
10
+ export async function validateSession(sessionId, cwd) {
11
+ // Claude Code replaces all non-alphanumeric chars (except -) with dashes
12
+ const projectHash = cwd.replace(/[^a-zA-Z0-9-]/g, "-");
13
+ const sessionPath = path.join(os.homedir(), ".claude", "projects", projectHash, `${sessionId}.jsonl`);
14
+ try {
15
+ await fs.access(sessionPath);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function execClaude(sessionId, message, cwd, timeoutMs) {
23
+ return new Promise((resolve) => {
24
+ const config = getConfig();
25
+ const effectiveTimeout = timeoutMs ?? config.COGENT_TIMEOUT_MS;
26
+ // Apply character limit: 0 means no truncation
27
+ const truncated = config.COGENT_CHAR_LIMIT > 0 &&
28
+ message.length > config.COGENT_CHAR_LIMIT
29
+ ? message.slice(0, config.COGENT_CHAR_LIMIT) + "\n...[truncated]"
30
+ : message;
31
+ const args = [
32
+ "--resume", sessionId,
33
+ "--dangerously-skip-permissions",
34
+ "--no-session-persistence",
35
+ "--strict-mcp-config",
36
+ "-p", truncated,
37
+ ];
38
+ // Strip Claude Code env vars so the subprocess doesn't think it's
39
+ // running inside a parent Claude session (which causes it to hang).
40
+ const cleanEnv = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("CLAUDE") && key !== "MCP_TRANSPORT"));
41
+ const child = spawn(config.COGENT_CLAUDE_PATH, args, {
42
+ cwd,
43
+ env: cleanEnv,
44
+ // stdin ignored: prevents child from blocking on stdin read.
45
+ // stdout/stderr piped: we capture the output.
46
+ stdio: ["ignore", "pipe", "pipe"],
47
+ });
48
+ let stdout = "";
49
+ let stderr = "";
50
+ let killed = false;
51
+ // Enforce timeout manually since spawn doesn't have a timeout option
52
+ const timer = setTimeout(() => {
53
+ killed = true;
54
+ child.kill("SIGTERM");
55
+ }, effectiveTimeout);
56
+ child.stdout.on("data", (chunk) => {
57
+ stdout += chunk.toString();
58
+ });
59
+ child.stderr.on("data", (chunk) => {
60
+ stderr += chunk.toString();
61
+ });
62
+ child.on("error", (err) => {
63
+ clearTimeout(timer);
64
+ if (err.code === "ENOENT") {
65
+ resolve({
66
+ stdout: "",
67
+ stderr: `CLI_NOT_FOUND: '${config.COGENT_CLAUDE_PATH}' not found. Install Claude Code or set COGENT_CLAUDE_PATH.`,
68
+ exitCode: 127,
69
+ });
70
+ return;
71
+ }
72
+ resolve({
73
+ stdout: stdout || "",
74
+ stderr: `CLI_EXEC_FAILED: spawn error: ${err.message}`,
75
+ exitCode: 1,
76
+ });
77
+ });
78
+ child.on("close", (code) => {
79
+ clearTimeout(timer);
80
+ if (killed) {
81
+ resolve({
82
+ stdout: stdout || "",
83
+ stderr: `CLI_TIMEOUT: CLI subprocess timed out after ${effectiveTimeout}ms`,
84
+ exitCode: null,
85
+ });
86
+ return;
87
+ }
88
+ if (code !== 0) {
89
+ resolve({
90
+ stdout: stdout || "",
91
+ stderr: `CLI_EXEC_FAILED: claude exited with code ${code}. stderr: ${stderr}`,
92
+ exitCode: code,
93
+ });
94
+ return;
95
+ }
96
+ resolve({
97
+ stdout: stdout || "",
98
+ stderr: stderr || "",
99
+ exitCode: 0,
100
+ });
101
+ });
102
+ });
103
+ }
104
+ //# sourceMappingURL=cc-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cc-cli.js","sourceRoot":"","sources":["../../src/services/cc-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ interface CheckResult {
2
+ ok: boolean;
3
+ message: string;
4
+ }
5
+ interface ClaudeCliCheckResult extends CheckResult {
6
+ version?: string;
7
+ }
8
+ export interface HealthCheckResult {
9
+ healthy: boolean;
10
+ serverVersion: string;
11
+ statePath: string;
12
+ claudePath: string;
13
+ checks: {
14
+ stateFile: CheckResult;
15
+ lockMechanism: CheckResult;
16
+ claudeCli: ClaudeCliCheckResult;
17
+ };
18
+ timestamp: string;
19
+ /** Present only in cloud mode */
20
+ mode?: "local" | "cloud";
21
+ /** Cloud relay server status (cloud mode only) */
22
+ serverStatus?: "ok" | "degraded" | "error";
23
+ /** Active sessions on cloud relay (cloud mode only) */
24
+ activeSessions?: number;
25
+ /** Connected peers across all sessions (cloud mode only) */
26
+ connectedPeers?: number;
27
+ /** Cloud relay server uptime in seconds (cloud mode only) */
28
+ serverUptime?: number;
29
+ }
30
+ /** Run all health checks and return a comprehensive result. */
31
+ export declare function checkHealth(): Promise<HealthCheckResult>;
32
+ export {};
33
+ //# sourceMappingURL=health-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../src/services/health-check.ts"],"names":[],"mappings":"AAUA,UAAU,WAAW;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,oBAAqB,SAAQ,WAAW;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QACN,SAAS,EAAE,WAAW,CAAC;QACvB,aAAa,EAAE,WAAW,CAAC;QAC3B,SAAS,EAAE,oBAAoB,CAAC;KACjC,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,IAAI,GAAG,UAAU,GAAG,OAAO,CAAC;IAC3C,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+ED,+DAA+D;AAC/D,wBAAsB,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAqB9D"}
@@ -0,0 +1,96 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { execFile } from "node:child_process";
4
+ import { getConfig } from "../config.js";
5
+ import { SERVER_VERSION } from "../constants.js";
6
+ /* ------------------------------------------------------------------ */
7
+ /* Derive paths from config at call time (not module load time) */
8
+ /* ------------------------------------------------------------------ */
9
+ function getStatePath() {
10
+ return path.join(getConfig().COGENT_STATE_PATH, "cogent-state.json");
11
+ }
12
+ /* ------------------------------------------------------------------ */
13
+ /* Sub-checks */
14
+ /* ------------------------------------------------------------------ */
15
+ /** HLTH-01: Check state file accessibility. */
16
+ async function checkStateFile() {
17
+ const statePath = getStatePath();
18
+ const dir = path.dirname(statePath);
19
+ try {
20
+ await fs.access(dir, fs.constants.R_OK | fs.constants.W_OK);
21
+ }
22
+ catch {
23
+ return { ok: false, message: `State directory not accessible: ${dir}` };
24
+ }
25
+ try {
26
+ const raw = await fs.readFile(statePath, "utf-8");
27
+ JSON.parse(raw);
28
+ return { ok: true, message: "State file readable and valid JSON" };
29
+ }
30
+ catch (err) {
31
+ if (err.code === "ENOENT") {
32
+ return { ok: true, message: "State directory writable (no state file yet)" };
33
+ }
34
+ if (err instanceof SyntaxError) {
35
+ return { ok: false, message: "State file exists but contains invalid JSON" };
36
+ }
37
+ return { ok: false, message: `State file error: ${err.message}` };
38
+ }
39
+ }
40
+ /** HLTH-02: Check lock mechanism with a separate health-check lock path. */
41
+ async function checkLockMechanism() {
42
+ const healthLockPath = getStatePath() + ".health-lock";
43
+ try {
44
+ await fs.writeFile(healthLockPath, String(process.pid), { flag: "wx" });
45
+ try {
46
+ await fs.unlink(healthLockPath);
47
+ }
48
+ catch {
49
+ // Cleanup failure is non-fatal
50
+ }
51
+ return { ok: true, message: "Lock acquire/release cycle succeeded" };
52
+ }
53
+ catch (err) {
54
+ if (err.code === "EEXIST") {
55
+ return { ok: false, message: "Health check lock file exists unexpectedly" };
56
+ }
57
+ return { ok: false, message: `Lock mechanism error: ${err.message}` };
58
+ }
59
+ }
60
+ /** HLTH-03: Check Claude CLI availability. */
61
+ function checkClaudeCli() {
62
+ const claudePath = getConfig().COGENT_CLAUDE_PATH;
63
+ return new Promise((resolve) => {
64
+ execFile(claudePath, ["--version"], { timeout: 5000 }, (error, stdout) => {
65
+ if (error) {
66
+ resolve({ ok: false, message: `Claude CLI not found at '${claudePath}'` });
67
+ return;
68
+ }
69
+ resolve({ ok: true, message: "Claude CLI found", version: (stdout || "").trim() });
70
+ });
71
+ });
72
+ }
73
+ /* ------------------------------------------------------------------ */
74
+ /* Public API */
75
+ /* ------------------------------------------------------------------ */
76
+ /** Run all health checks and return a comprehensive result. */
77
+ export async function checkHealth() {
78
+ const config = getConfig();
79
+ // Run all three checks (not short-circuiting)
80
+ const [stateFile, lockMechanism, claudeCli] = await Promise.all([
81
+ checkStateFile(),
82
+ checkLockMechanism(),
83
+ checkClaudeCli(),
84
+ ]);
85
+ const checks = { stateFile, lockMechanism, claudeCli };
86
+ const healthy = Object.values(checks).every((c) => c.ok);
87
+ return {
88
+ healthy,
89
+ serverVersion: SERVER_VERSION,
90
+ statePath: path.join(config.COGENT_STATE_PATH, "cogent-state.json"),
91
+ claudePath: config.COGENT_CLAUDE_PATH,
92
+ checks,
93
+ timestamp: new Date().toISOString(),
94
+ };
95
+ }
96
+ //# sourceMappingURL=health-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../src/services/health-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { PeerInfo, MessageRecord } from "../types.js";
2
+ export declare function registerPeer(peerId: string, sessionId: string, cwd: string, label: string): Promise<PeerInfo>;
3
+ export declare function updateLastSeen(peerId: string): Promise<void>;
4
+ export declare function deregisterPeer(peerId: string): Promise<boolean>;
5
+ export declare function getPeer(peerId: string): Promise<PeerInfo | undefined>;
6
+ export declare function listPeers(): Promise<PeerInfo[]>;
7
+ export declare function recordMessage(record: Omit<MessageRecord, "id" | "timestamp">): Promise<MessageRecord>;
8
+ export declare function getHistory(peerId?: string, limit?: number): Promise<MessageRecord[]>;
9
+ //# sourceMappingURL=peer-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-registry.d.ts","sourceRoot":"","sources":["../../src/services/peer-registry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgKtD,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAgBnB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASlE;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQrE;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAG/B;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGrD;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,CAAC,GAC9C,OAAO,CAAC,aAAa,CAAC,CAgBxB;AAED,wBAAsB,UAAU,CAC9B,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,SAAK,GACT,OAAO,CAAC,aAAa,EAAE,CAAC,CAS1B"}
@@ -0,0 +1,207 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { getConfig } from "../config.js";
5
+ import { logger } from "../logger.js";
6
+ import { BridgeError, BridgeErrorCode } from "../errors.js";
7
+ /* ------------------------------------------------------------------ */
8
+ /* File-based shared state */
9
+ /* Two or more MCP server processes share a state JSON file */
10
+ /* State path is config-driven via COGENT_STATE_PATH */
11
+ /* ------------------------------------------------------------------ */
12
+ const MAX_MESSAGES = 500;
13
+ const LOCK_RETRY_MS = 50;
14
+ const LOCK_MAX_WAIT_MS = 5_000;
15
+ /** Derive state file path from config at call time (not module load time). */
16
+ function getStatePath() {
17
+ return path.join(getConfig().COGENT_STATE_PATH, "cogent-state.json");
18
+ }
19
+ /** Derive lock file path from state path at call time. */
20
+ function getLockPath() {
21
+ return getStatePath() + ".lock";
22
+ }
23
+ /* ---- low-level helpers ------------------------------------------- */
24
+ function emptyState() {
25
+ return { peers: {}, messages: [] };
26
+ }
27
+ /** Migrate legacy state: ensure all peers have lastSeenAt field. */
28
+ function migrateState(state) {
29
+ for (const peer of Object.values(state.peers)) {
30
+ if (!peer.lastSeenAt) {
31
+ peer.lastSeenAt = peer.registeredAt;
32
+ }
33
+ }
34
+ return state;
35
+ }
36
+ async function readState() {
37
+ const statePath = getStatePath();
38
+ try {
39
+ const raw = await fs.readFile(statePath, "utf-8");
40
+ return migrateState(JSON.parse(raw));
41
+ }
42
+ catch (err) {
43
+ if (err.code === "ENOENT") {
44
+ return migrateState(emptyState());
45
+ }
46
+ if (err instanceof SyntaxError) {
47
+ // Corrupt JSON -- auto-recover with backup
48
+ const backupPath = statePath + ".corrupt." + Date.now();
49
+ try {
50
+ await fs.copyFile(statePath, backupPath);
51
+ }
52
+ catch {
53
+ // Backup failure is non-fatal
54
+ }
55
+ logger.warn(`STATE_CORRUPT: State file corrupt (invalid JSON), backed up to ${backupPath}. Starting with empty state.`);
56
+ return migrateState(emptyState());
57
+ }
58
+ throw new BridgeError(BridgeErrorCode.STATE_WRITE_FAILED, `Failed to read state file: ${err.message}`, `Check permissions on ${statePath}`);
59
+ }
60
+ }
61
+ async function writeState(state) {
62
+ const statePath = getStatePath();
63
+ const tmp = statePath + "." + process.pid + ".tmp";
64
+ try {
65
+ await fs.writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
66
+ await fs.rename(tmp, statePath); // atomic on same filesystem
67
+ }
68
+ catch (err) {
69
+ throw new BridgeError(BridgeErrorCode.STATE_WRITE_FAILED, `Failed to write state file: ${err.message}`, `Check permissions on ${statePath}`);
70
+ }
71
+ }
72
+ /* ---- file-lock (O_CREAT | O_EXCL) ------------------------------- */
73
+ async function acquireLock() {
74
+ const lockPath = getLockPath();
75
+ const deadline = Date.now() + LOCK_MAX_WAIT_MS;
76
+ while (Date.now() < deadline) {
77
+ try {
78
+ // Atomic create-if-not-exists
79
+ await fs.writeFile(lockPath, String(process.pid), {
80
+ flag: "wx", // O_WRONLY | O_CREAT | O_EXCL
81
+ });
82
+ return; // lock acquired
83
+ }
84
+ catch (err) {
85
+ if (err.code === "EEXIST") {
86
+ // Check for stale lock (process died)
87
+ try {
88
+ const content = await fs.readFile(lockPath, "utf-8");
89
+ const lockPid = parseInt(content, 10);
90
+ if (!isNaN(lockPid)) {
91
+ try {
92
+ process.kill(lockPid, 0); // just checks existence
93
+ }
94
+ catch {
95
+ // Process doesn't exist -- stale lock
96
+ logger.info(`Cleaned up stale lock from PID ${lockPid}`);
97
+ await fs.unlink(lockPath).catch(() => { });
98
+ continue; // retry immediately
99
+ }
100
+ }
101
+ }
102
+ catch { /* lock file disappeared, retry */
103
+ continue;
104
+ }
105
+ await sleep(LOCK_RETRY_MS);
106
+ continue;
107
+ }
108
+ throw err;
109
+ }
110
+ }
111
+ // Timeout -- read lock to report holder PID
112
+ let lockPid = "unknown";
113
+ try {
114
+ const content = await fs.readFile(lockPath, "utf-8");
115
+ lockPid = content.trim();
116
+ }
117
+ catch { /* ignore read failure */ }
118
+ throw new BridgeError(BridgeErrorCode.LOCK_TIMEOUT, `Failed to acquire lock within ${LOCK_MAX_WAIT_MS}ms (held by PID ${lockPid})`, `Another cogent process may be stuck. Delete ${lockPath} if no other instance is running.`);
119
+ }
120
+ async function releaseLock() {
121
+ await fs.unlink(getLockPath()).catch(() => { });
122
+ }
123
+ function sleep(ms) {
124
+ return new Promise((r) => setTimeout(r, ms));
125
+ }
126
+ /** Run a mutating operation under an exclusive file lock. */
127
+ async function withLock(fn) {
128
+ await acquireLock();
129
+ try {
130
+ return await fn();
131
+ }
132
+ finally {
133
+ await releaseLock();
134
+ }
135
+ }
136
+ /* ---- public API (all async) -------------------------------------- */
137
+ export async function registerPeer(peerId, sessionId, cwd, label) {
138
+ return withLock(async () => {
139
+ const state = await readState();
140
+ const now = new Date().toISOString();
141
+ const peer = {
142
+ peerId,
143
+ sessionId,
144
+ cwd,
145
+ label,
146
+ registeredAt: now,
147
+ lastSeenAt: now,
148
+ };
149
+ state.peers[peerId] = peer;
150
+ await writeState(state);
151
+ return peer;
152
+ });
153
+ }
154
+ export async function updateLastSeen(peerId) {
155
+ return withLock(async () => {
156
+ const state = await readState();
157
+ const peer = state.peers[peerId];
158
+ if (peer) {
159
+ peer.lastSeenAt = new Date().toISOString();
160
+ await writeState(state);
161
+ }
162
+ });
163
+ }
164
+ export async function deregisterPeer(peerId) {
165
+ return withLock(async () => {
166
+ const state = await readState();
167
+ if (!(peerId in state.peers))
168
+ return false;
169
+ delete state.peers[peerId];
170
+ await writeState(state);
171
+ return true;
172
+ });
173
+ }
174
+ export async function getPeer(peerId) {
175
+ const state = await readState();
176
+ return state.peers[peerId];
177
+ }
178
+ export async function listPeers() {
179
+ const state = await readState();
180
+ return Object.values(state.peers);
181
+ }
182
+ export async function recordMessage(record) {
183
+ return withLock(async () => {
184
+ const state = await readState();
185
+ const full = {
186
+ ...record,
187
+ id: crypto.randomUUID(),
188
+ timestamp: new Date().toISOString(),
189
+ };
190
+ state.messages.push(full);
191
+ // Cap history to prevent unbounded growth
192
+ if (state.messages.length > MAX_MESSAGES) {
193
+ state.messages = state.messages.slice(-MAX_MESSAGES);
194
+ }
195
+ await writeState(state);
196
+ return full;
197
+ });
198
+ }
199
+ export async function getHistory(peerId, limit = 50) {
200
+ const state = await readState();
201
+ let filtered = state.messages;
202
+ if (peerId) {
203
+ filtered = state.messages.filter((m) => m.fromPeerId === peerId || m.toPeerId === peerId);
204
+ }
205
+ return filtered.slice(-limit);
206
+ }
207
+ //# sourceMappingURL=peer-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-registry.js","sourceRoot":"","sources":["../../src/services/peer-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { CloudWsClient, MessageInbox } from "./cloud/index.js";
2
+ import { HttpClient } from "./cloud/http-client.js";
3
+ export declare let cloudWsClient: CloudWsClient | null;
4
+ export declare let cloudInbox: MessageInbox | null;
5
+ export declare let cloudHttpClient: HttpClient | null;
6
+ /**
7
+ * Reinitialize the cloud backend with new credentials at runtime.
8
+ * Called by create-session, join-session, and register-peer auto-create
9
+ * after obtaining new session credentials.
10
+ *
11
+ * This function:
12
+ * 1. Creates a new HttpClient with the new token
13
+ * 2. Creates a new HttpBackend and replaces the current backend
14
+ * 3. Creates or updates CloudWsClient and MessageInbox
15
+ */
16
+ export declare function reinitCloudBackend(endpoint: string, sessionId: string, token: string): Promise<void>;
17
+ export declare function runStartup(): Promise<void>;
18
+ //# sourceMappingURL=startup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAUpD,eAAO,IAAI,aAAa,EAAE,aAAa,GAAG,IAAW,CAAC;AACtD,eAAO,IAAI,UAAU,EAAE,YAAY,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,eAAe,EAAE,UAAU,GAAG,IAAW,CAAC;AAqIrD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAmCf;AAMD,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAmIhD"}