@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
@@ -0,0 +1,3 @@
1
+ export declare const SERVER_NAME = "Cogent Bridge from Essential AI Solutions (essentialai.uk)";
2
+ export declare const SERVER_VERSION: string;
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW,+DAA+D,CAAC;AACxF,eAAO,MAAM,cAAc,QAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require("../package.json");
4
+ export const SERVER_NAME = "Cogent Bridge from Essential AI Solutions (essentialai.uk)";
5
+ export const SERVER_VERSION = pkg.version;
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,4DAA4D,CAAC;AACxF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * E2E test helpers for cloud mode integration testing.
3
+ *
4
+ * Provides utilities to stand up a real Hono server (with WebSocket support)
5
+ * and create MCP client instances whose HttpBackend talks to that server.
6
+ * This enables full-stack testing: MCP tool call -> HttpBackend -> REST API
7
+ * -> server -> file persistence -> WebSocket push.
8
+ */
9
+ import WebSocket from "ws";
10
+ import type { ServerType } from "@hono/node-server";
11
+ import { SessionStore } from "../../server/src/services/session-store.js";
12
+ import { ConnectionManager } from "../../server/src/services/connection-manager.js";
13
+ import { MessageQueueService } from "../../server/src/services/message-queue.js";
14
+ import type { WsServerFrame } from "../../server/src/ws/frames.js";
15
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
17
+ import { HttpClient } from "../cloud/http-client.js";
18
+ import { HttpBackend } from "../backend/http-backend.js";
19
+ export type { WsServerFrame };
20
+ export interface E2EServer {
21
+ port: number;
22
+ server: ServerType;
23
+ sessionStore: SessionStore;
24
+ connectionManager: ConnectionManager;
25
+ messageQueue: MessageQueueService;
26
+ stateDir: string;
27
+ /** Build a ws:// URL for the given session, token, and peerId. */
28
+ wsUrl: (sessionId: string, token: string, peerId: string) => string;
29
+ /** Shut down the HTTP server and close all WS connections. */
30
+ shutdown: () => Promise<void>;
31
+ /** Remove the temp state directory (only if it was created by this call). */
32
+ cleanup: () => Promise<void>;
33
+ }
34
+ /**
35
+ * Create a real Hono server with WebSocket support on an ephemeral port.
36
+ *
37
+ * Follows the same pattern as `server/src/__tests__/helpers.ts` createTestWsApp
38
+ * but tailored for E2E testing with separate MCP clients.
39
+ *
40
+ * @param existingStateDir - Optional state directory for server restart tests.
41
+ * When provided, skips mkdtemp and uses the given dir; cleanup() only
42
+ * removes the dir if it was created by this call.
43
+ */
44
+ export declare function createE2EServer(existingStateDir?: string): Promise<E2EServer>;
45
+ export interface E2EClient {
46
+ client: Client;
47
+ mcpServer: McpServer;
48
+ backend: HttpBackend;
49
+ httpClient: HttpClient;
50
+ /**
51
+ * Swap the module-level backend singleton to this client's backend.
52
+ * Must be called BEFORE invoking any tools on this client.
53
+ */
54
+ activate: () => void;
55
+ /** Close the MCP transport pair. */
56
+ close: () => Promise<void>;
57
+ }
58
+ /**
59
+ * Create an MCP Server + Client pair connected via InMemoryTransport,
60
+ * backed by an HttpBackend pointed at the given E2E server.
61
+ *
62
+ * Since tool handlers call getBackend() at runtime (module-level singleton),
63
+ * call activate() before each tool invocation to swap the backend.
64
+ */
65
+ export declare function createE2EClient(port: number, sessionId: string, token: string, tempDir: string): Promise<E2EClient>;
66
+ /**
67
+ * Create a new cloud session via direct fetch.
68
+ * Returns the sessionId and bearer token.
69
+ */
70
+ export declare function createCloudSession(port: number, secret: string): Promise<{
71
+ sessionId: string;
72
+ token: string;
73
+ }>;
74
+ /**
75
+ * Join an existing cloud session via direct fetch.
76
+ * Returns a bearer token for the joining client.
77
+ */
78
+ export declare function joinCloudSession(port: number, sessionId: string, secret: string): Promise<{
79
+ token: string;
80
+ }>;
81
+ /**
82
+ * Extract and JSON.parse the text from an MCP tool result content array.
83
+ * Copied from src/integration.test.ts.
84
+ */
85
+ export declare function parseResult(result: {
86
+ content: unknown;
87
+ }): Record<string, unknown>;
88
+ /** Small delay helper for async settling. */
89
+ export declare const tick: (ms?: number) => Promise<void>;
90
+ /**
91
+ * Wait for the next parsed JSON message from a WebSocket connection.
92
+ * Optionally filters by frame type (ignoring non-matching frames).
93
+ * Times out after the specified duration.
94
+ */
95
+ export declare function waitForWsMessage(ws: WebSocket, type?: string, timeoutMs?: number): Promise<WsServerFrame>;
96
+ /**
97
+ * Open a WebSocket connection and wait until it's fully open.
98
+ * Rejects on connection error.
99
+ */
100
+ export declare function connectWs(url: string): Promise<WebSocket>;
101
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/e2e/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,228 @@
1
+ /**
2
+ * E2E test helpers for cloud mode integration testing.
3
+ *
4
+ * Provides utilities to stand up a real Hono server (with WebSocket support)
5
+ * and create MCP client instances whose HttpBackend talks to that server.
6
+ * This enables full-stack testing: MCP tool call -> HttpBackend -> REST API
7
+ * -> server -> file persistence -> WebSocket push.
8
+ */
9
+ import fs from "node:fs/promises";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import WebSocket from "ws";
13
+ import { createAdaptorServer } from "@hono/node-server";
14
+ import { SessionStore } from "../../server/src/services/session-store.js";
15
+ import { AuthService } from "../../server/src/services/auth-service.js";
16
+ import { ConnectionManager } from "../../server/src/services/connection-manager.js";
17
+ import { MessageQueueService } from "../../server/src/services/message-queue.js";
18
+ import { createApp } from "../../server/src/app.js";
19
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
20
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
21
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
22
+ import { HttpClient } from "../cloud/http-client.js";
23
+ import { HttpBackend } from "../backend/http-backend.js";
24
+ import { initBackend, resetBackend } from "../backend/backend-provider.js";
25
+ import { resetConfig, loadConfig } from "../config.js";
26
+ import { registerRegisterPeerTool } from "../tools/register-peer.js";
27
+ import { registerDeregisterPeerTool } from "../tools/deregister-peer.js";
28
+ import { registerSendMessageTool } from "../tools/send-message.js";
29
+ import { registerListPeersTool } from "../tools/list-peers.js";
30
+ import { registerGetHistoryTool } from "../tools/get-history.js";
31
+ import { registerHealthCheckTool } from "../tools/health-check.js";
32
+ /**
33
+ * Create a real Hono server with WebSocket support on an ephemeral port.
34
+ *
35
+ * Follows the same pattern as `server/src/__tests__/helpers.ts` createTestWsApp
36
+ * but tailored for E2E testing with separate MCP clients.
37
+ *
38
+ * @param existingStateDir - Optional state directory for server restart tests.
39
+ * When provided, skips mkdtemp and uses the given dir; cleanup() only
40
+ * removes the dir if it was created by this call.
41
+ */
42
+ export async function createE2EServer(existingStateDir) {
43
+ const ownsDir = !existingStateDir;
44
+ const stateDir = existingStateDir ?? await fs.mkdtemp(path.join(os.tmpdir(), "e2e-test-"));
45
+ const sessionStore = new SessionStore(stateDir, 500);
46
+ const authService = new AuthService(4); // Low rounds for fast tests
47
+ const connectionManager = new ConnectionManager(30_000);
48
+ const messageQueue = new MessageQueueService(sessionStore);
49
+ await sessionStore.init();
50
+ const { app, injectWebSocket } = createApp({
51
+ sessionStore,
52
+ authService,
53
+ startTime: Date.now(),
54
+ maxMessages: 500,
55
+ connectionManager,
56
+ messageQueue,
57
+ heartbeatIntervalMs: 30_000,
58
+ heartbeatMaxMissed: 3,
59
+ });
60
+ const server = createAdaptorServer(app);
61
+ injectWebSocket(server);
62
+ return new Promise((resolve) => {
63
+ server.listen(0, () => {
64
+ const addr = server.address();
65
+ const port = typeof addr === "object" && addr ? addr.port : 0;
66
+ resolve({
67
+ port,
68
+ server,
69
+ sessionStore,
70
+ connectionManager,
71
+ messageQueue,
72
+ stateDir,
73
+ wsUrl: (sessionId, token, peerId) => `ws://localhost:${port}/ws/${sessionId}?token=${encodeURIComponent(token)}&peerId=${encodeURIComponent(peerId)}`,
74
+ shutdown: async () => {
75
+ connectionManager.closeAll(1012, "Test shutdown");
76
+ return new Promise((res, rej) => {
77
+ server.close((err) => (err ? rej(err) : res()));
78
+ });
79
+ },
80
+ cleanup: async () => {
81
+ if (ownsDir) {
82
+ await fs.rm(stateDir, { recursive: true, force: true });
83
+ }
84
+ },
85
+ });
86
+ });
87
+ });
88
+ }
89
+ /**
90
+ * Create an MCP Server + Client pair connected via InMemoryTransport,
91
+ * backed by an HttpBackend pointed at the given E2E server.
92
+ *
93
+ * Since tool handlers call getBackend() at runtime (module-level singleton),
94
+ * call activate() before each tool invocation to swap the backend.
95
+ */
96
+ export async function createE2EClient(port, sessionId, token, tempDir) {
97
+ const httpClient = new HttpClient(`http://localhost:${port}`, token);
98
+ const backend = new HttpBackend(httpClient, sessionId);
99
+ const mcpServer = new McpServer({ name: "e2e-test", version: "1.0.0" });
100
+ // Register all 6 standard tools
101
+ registerRegisterPeerTool(mcpServer);
102
+ registerDeregisterPeerTool(mcpServer);
103
+ registerSendMessageTool(mcpServer);
104
+ registerListPeersTool(mcpServer);
105
+ registerGetHistoryTool(mcpServer);
106
+ registerHealthCheckTool(mcpServer);
107
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
108
+ const client = new Client({ name: "e2e-client", version: "1.0.0" });
109
+ await Promise.all([
110
+ mcpServer.connect(serverTransport),
111
+ client.connect(clientTransport),
112
+ ]);
113
+ return {
114
+ client,
115
+ mcpServer,
116
+ backend,
117
+ httpClient,
118
+ activate: () => {
119
+ // Swap the module-level backend singleton to this client's backend
120
+ resetBackend();
121
+ initBackend(backend);
122
+ // Ensure config is loaded with cloud endpoint pointing at the E2E server
123
+ resetConfig();
124
+ loadConfig({
125
+ CC_BRIDGE_STATE_PATH: tempDir,
126
+ CC_BRIDGE_ENDPOINT: `http://localhost:${port}`,
127
+ CC_BRIDGE_SESSION_ID: sessionId,
128
+ CC_BRIDGE_TIMEOUT_MS: "10000",
129
+ CC_BRIDGE_LOG_LEVEL: "error",
130
+ });
131
+ },
132
+ close: async () => {
133
+ await client.close();
134
+ await mcpServer.close();
135
+ },
136
+ };
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Cloud session REST helpers
140
+ // ---------------------------------------------------------------------------
141
+ /**
142
+ * Create a new cloud session via direct fetch.
143
+ * Returns the sessionId and bearer token.
144
+ */
145
+ export async function createCloudSession(port, secret) {
146
+ const res = await fetch(`http://localhost:${port}/api/sessions`, {
147
+ method: "POST",
148
+ headers: { "Content-Type": "application/json" },
149
+ body: JSON.stringify({ secret }),
150
+ });
151
+ if (!res.ok) {
152
+ throw new Error(`createCloudSession failed: HTTP ${res.status}`);
153
+ }
154
+ return (await res.json());
155
+ }
156
+ /**
157
+ * Join an existing cloud session via direct fetch.
158
+ * Returns a bearer token for the joining client.
159
+ */
160
+ export async function joinCloudSession(port, sessionId, secret) {
161
+ const res = await fetch(`http://localhost:${port}/api/sessions/${sessionId}/join`, {
162
+ method: "POST",
163
+ headers: { "Content-Type": "application/json" },
164
+ body: JSON.stringify({ secret }),
165
+ });
166
+ if (!res.ok) {
167
+ throw new Error(`joinCloudSession failed: HTTP ${res.status}`);
168
+ }
169
+ return (await res.json());
170
+ }
171
+ // ---------------------------------------------------------------------------
172
+ // MCP result parser
173
+ // ---------------------------------------------------------------------------
174
+ /**
175
+ * Extract and JSON.parse the text from an MCP tool result content array.
176
+ * Copied from src/integration.test.ts.
177
+ */
178
+ export function parseResult(result) {
179
+ return JSON.parse(result.content[0].text);
180
+ }
181
+ // ---------------------------------------------------------------------------
182
+ // Async utilities
183
+ // ---------------------------------------------------------------------------
184
+ /** Small delay helper for async settling. */
185
+ export const tick = (ms = 100) => new Promise((r) => setTimeout(r, ms));
186
+ /**
187
+ * Wait for the next parsed JSON message from a WebSocket connection.
188
+ * Optionally filters by frame type (ignoring non-matching frames).
189
+ * Times out after the specified duration.
190
+ */
191
+ export function waitForWsMessage(ws, type, timeoutMs = 5000) {
192
+ return new Promise((resolve, reject) => {
193
+ const timeout = setTimeout(() => {
194
+ ws.off("message", handler);
195
+ reject(new Error(`Timed out waiting for WS message${type ? ` of type "${type}"` : ""}`));
196
+ }, timeoutMs);
197
+ const handler = (data) => {
198
+ const frame = JSON.parse(data.toString());
199
+ if (!type || frame.type === type) {
200
+ clearTimeout(timeout);
201
+ ws.off("message", handler);
202
+ resolve(frame);
203
+ }
204
+ };
205
+ ws.on("message", handler);
206
+ });
207
+ }
208
+ /**
209
+ * Open a WebSocket connection and wait until it's fully open.
210
+ * Rejects on connection error.
211
+ */
212
+ export function connectWs(url) {
213
+ return new Promise((resolve, reject) => {
214
+ const ws = new WebSocket(url);
215
+ const onClose = (code) => {
216
+ if (ws.readyState !== WebSocket.OPEN) {
217
+ reject(new Error(`WebSocket closed during connect with code ${code}`));
218
+ }
219
+ };
220
+ ws.on("open", () => {
221
+ ws.off("close", onClose);
222
+ resolve(ws);
223
+ });
224
+ ws.on("error", (err) => reject(err));
225
+ ws.on("close", onClose);
226
+ });
227
+ }
228
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/e2e/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export declare enum BridgeErrorCode {
2
+ CLI_NOT_FOUND = "CLI_NOT_FOUND",
3
+ CLI_TIMEOUT = "CLI_TIMEOUT",
4
+ CLI_EXEC_FAILED = "CLI_EXEC_FAILED",
5
+ STATE_CORRUPT = "STATE_CORRUPT",
6
+ STATE_WRITE_FAILED = "STATE_WRITE_FAILED",
7
+ LOCK_TIMEOUT = "LOCK_TIMEOUT",
8
+ LOCK_STALE = "LOCK_STALE",
9
+ PEER_NOT_FOUND = "PEER_NOT_FOUND",
10
+ INVALID_INPUT = "INVALID_INPUT",
11
+ STARTUP_FAILED = "STARTUP_FAILED",
12
+ DIR_NOT_WRITABLE = "DIR_NOT_WRITABLE"
13
+ }
14
+ export declare class BridgeError extends Error {
15
+ readonly code: BridgeErrorCode;
16
+ readonly suggestion?: string;
17
+ constructor(code: BridgeErrorCode, message: string, suggestion?: string);
18
+ }
19
+ export declare function toolResult(text: string, isError?: boolean): {
20
+ isError?: true | undefined;
21
+ content: {
22
+ type: "text";
23
+ text: string;
24
+ }[];
25
+ };
26
+ export declare function errorResult(error: unknown): {
27
+ isError?: true | undefined;
28
+ content: {
29
+ type: "text";
30
+ text: string;
31
+ }[];
32
+ };
33
+ export declare function successResult(data: Record<string, unknown>): {
34
+ isError?: true | undefined;
35
+ content: {
36
+ type: "text";
37
+ text: string;
38
+ }[];
39
+ };
40
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,oBAAY,eAAe;IACzB,aAAa,kBAAkB;IAC/B,WAAW,gBAAgB;IAC3B,eAAe,oBAAoB;IACnC,aAAa,kBAAkB;IAC/B,kBAAkB,uBAAuB;IACzC,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,cAAc,mBAAmB;IACjC,gBAAgB,qBAAqB;CACtC;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAE,eAAe,CAAC;IACtC,SAAgB,UAAU,CAAC,EAAE,MAAM,CAAC;gBAExB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CASxE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;;;;;;EAKzD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO;;;;;;EA8BzC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;EAE1D"}
package/dist/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ export var BridgeErrorCode;
2
+ (function (BridgeErrorCode) {
3
+ BridgeErrorCode["CLI_NOT_FOUND"] = "CLI_NOT_FOUND";
4
+ BridgeErrorCode["CLI_TIMEOUT"] = "CLI_TIMEOUT";
5
+ BridgeErrorCode["CLI_EXEC_FAILED"] = "CLI_EXEC_FAILED";
6
+ BridgeErrorCode["STATE_CORRUPT"] = "STATE_CORRUPT";
7
+ BridgeErrorCode["STATE_WRITE_FAILED"] = "STATE_WRITE_FAILED";
8
+ BridgeErrorCode["LOCK_TIMEOUT"] = "LOCK_TIMEOUT";
9
+ BridgeErrorCode["LOCK_STALE"] = "LOCK_STALE";
10
+ BridgeErrorCode["PEER_NOT_FOUND"] = "PEER_NOT_FOUND";
11
+ BridgeErrorCode["INVALID_INPUT"] = "INVALID_INPUT";
12
+ BridgeErrorCode["STARTUP_FAILED"] = "STARTUP_FAILED";
13
+ BridgeErrorCode["DIR_NOT_WRITABLE"] = "DIR_NOT_WRITABLE";
14
+ })(BridgeErrorCode || (BridgeErrorCode = {}));
15
+ export class BridgeError extends Error {
16
+ code;
17
+ suggestion;
18
+ constructor(code, message, suggestion) {
19
+ const fullMessage = suggestion
20
+ ? `${code}: ${message}. ${suggestion}`
21
+ : `${code}: ${message}`;
22
+ super(fullMessage);
23
+ this.name = "BridgeError";
24
+ this.code = code;
25
+ this.suggestion = suggestion;
26
+ }
27
+ }
28
+ export function toolResult(text, isError) {
29
+ return {
30
+ content: [{ type: "text", text }],
31
+ ...(isError ? { isError: true } : {}),
32
+ };
33
+ }
34
+ export function errorResult(error) {
35
+ if (error instanceof BridgeError) {
36
+ return toolResult(JSON.stringify({
37
+ success: false,
38
+ error: error.code,
39
+ message: error.message,
40
+ suggestion: error.suggestion,
41
+ }, null, 2), true);
42
+ }
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ return toolResult(JSON.stringify({
45
+ success: false,
46
+ error: "INTERNAL_ERROR",
47
+ message,
48
+ }, null, 2), true);
49
+ }
50
+ export function successResult(data) {
51
+ return toolResult(JSON.stringify(data, null, 2), false);
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,eAYX;AAZD,WAAY,eAAe;IACzB,kDAA+B,CAAA;IAC/B,8CAA2B,CAAA;IAC3B,sDAAmC,CAAA;IACnC,kDAA+B,CAAA;IAC/B,4DAAyC,CAAA;IACzC,gDAA6B,CAAA;IAC7B,4CAAyB,CAAA;IACzB,oDAAiC,CAAA;IACjC,kDAA+B,CAAA;IAC/B,oDAAiC,CAAA;IACjC,wDAAqC,CAAA;AACvC,CAAC,EAZW,eAAe,KAAf,eAAe,QAY1B;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpB,IAAI,CAAkB;IACtB,UAAU,CAAU;IAEpC,YAAY,IAAqB,EAAE,OAAe,EAAE,UAAmB;QACrE,MAAM,WAAW,GAAG,UAAU;YAC5B,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,KAAK,UAAU,EAAE;YACtC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,KAAK,CAAC,WAAW,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAAiB;IACxD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;QAC1C,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QACjC,OAAO,UAAU,CACf,IAAI,CAAC,SAAS,CACZ;YACE,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,EACD,IAAI,EACJ,CAAC,CACF,EACD,IAAI,CACL,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,UAAU,CACf,IAAI,CAAC,SAAS,CACZ;QACE,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,gBAAgB;QACvB,OAAO;KACR,EACD,IAAI,EACJ,CAAC,CACF,EACD,IAAI,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAA6B;IACzD,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,108 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { SERVER_NAME, SERVER_VERSION } from "./constants.js";
4
+ import { runStartup, cloudWsClient, cloudInbox } from "./startup.js";
5
+ import { logger } from "./logger.js";
6
+ import { registerRegisterPeerTool } from "./tools/register-peer.js";
7
+ import { registerSendMessageTool } from "./tools/send-message.js";
8
+ import { registerListPeersTool } from "./tools/list-peers.js";
9
+ import { registerGetHistoryTool } from "./tools/get-history.js";
10
+ import { registerDeregisterPeerTool } from "./tools/deregister-peer.js";
11
+ import { registerHealthCheckTool } from "./tools/health-check.js";
12
+ import { registerCreateSessionTool } from "./tools/create-session.js";
13
+ import { registerJoinSessionTool } from "./tools/join-session.js";
14
+ // ---------------------------------------------------------------------------
15
+ // Global error handlers -- registered FIRST before anything else
16
+ // ---------------------------------------------------------------------------
17
+ process.on("uncaughtException", (err) => {
18
+ // EPIPE means the parent process disconnected -- exit cleanly
19
+ if (err.code === "EPIPE") {
20
+ logger.close();
21
+ process.exit(0);
22
+ return;
23
+ }
24
+ logger.error(`Uncaught exception: ${err.message}`, { stack: err.stack });
25
+ // Do NOT exit -- MCP server should try to continue
26
+ });
27
+ process.on("unhandledRejection", (reason) => {
28
+ const msg = reason instanceof Error ? reason.message : String(reason);
29
+ logger.error(`Unhandled rejection: ${msg}`);
30
+ });
31
+ // ---------------------------------------------------------------------------
32
+ // Graceful shutdown handlers
33
+ // ---------------------------------------------------------------------------
34
+ process.on("SIGTERM", () => {
35
+ logger.info("SIGTERM received, shutting down");
36
+ if (cloudWsClient) {
37
+ cloudWsClient.disconnect();
38
+ }
39
+ logger.close();
40
+ process.exit(0);
41
+ });
42
+ process.on("SIGINT", () => {
43
+ logger.info("SIGINT received, shutting down");
44
+ if (cloudWsClient) {
45
+ cloudWsClient.disconnect();
46
+ }
47
+ logger.close();
48
+ process.exit(0);
49
+ });
50
+ // ---------------------------------------------------------------------------
51
+ // Main entry point
52
+ // ---------------------------------------------------------------------------
53
+ async function main() {
54
+ // Startup MUST complete before MCP transport starts
55
+ // (handles first-run prompt, config loading, logger init, validation)
56
+ await runStartup();
57
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
58
+ // Register all 8 tools (6 existing + create-session + join-session)
59
+ registerRegisterPeerTool(server);
60
+ registerDeregisterPeerTool(server);
61
+ registerSendMessageTool(server);
62
+ registerListPeersTool(server);
63
+ registerGetHistoryTool(server);
64
+ registerHealthCheckTool(server);
65
+ registerCreateSessionTool(server);
66
+ registerJoinSessionTool(server);
67
+ // Register MCP inbox resource if cloud mode is active
68
+ if (cloudInbox) {
69
+ server.registerResource("cogent_inbox", "cogent://inbox", {
70
+ description: "Incoming messages received via cloud bridge WebSocket. Read this resource to see new messages from other peers.",
71
+ mimeType: "application/json",
72
+ }, async (uri) => ({
73
+ contents: [{
74
+ uri: uri.href,
75
+ mimeType: "application/json",
76
+ text: JSON.stringify({
77
+ messages: cloudInbox.getUnread(),
78
+ lastMessageId: cloudInbox.getLastMessageId(),
79
+ }),
80
+ }],
81
+ }));
82
+ // When WS messages arrive, notify the MCP host (Claude Code) that new messages are available
83
+ cloudInbox.onNewMessage(() => {
84
+ try {
85
+ server.server.sendResourceUpdated({ uri: "cogent://inbox" });
86
+ }
87
+ catch (err) {
88
+ // sendResourceUpdated may fail if transport is not connected yet
89
+ logger.warn(`Failed to send resource update notification: ${err}`);
90
+ }
91
+ });
92
+ }
93
+ // Connect stdio transport
94
+ const transport = new StdioServerTransport();
95
+ await server.connect(transport);
96
+ // Startup banner: always prints regardless of log level
97
+ process.stderr.write(`${SERVER_NAME} v${SERVER_VERSION} running on stdio\n`);
98
+ logger.info("Server ready. Tools: cogent_register_peer, cogent_deregister_peer, cogent_send_message, cogent_list_peers, cogent_get_history, cogent_health_check, cogent_create_session, cogent_join_session");
99
+ }
100
+ main().catch((err) => {
101
+ const msg = err instanceof Error ? err.message : String(err);
102
+ logger.error(`Fatal startup error: ${msg}`);
103
+ // Fallback to stderr in case logger failed
104
+ process.stderr.write(`Fatal error: ${msg}\n`);
105
+ logger.close();
106
+ process.exit(1);
107
+ });
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export declare class Logger {
3
+ private readonly threshold;
4
+ private textStream;
5
+ private jsonStream;
6
+ constructor(level: LogLevel, logDir?: string);
7
+ private log;
8
+ debug(message: string, data?: unknown): void;
9
+ info(message: string, data?: unknown): void;
10
+ warn(message: string, data?: unknown): void;
11
+ error(message: string, data?: unknown): void;
12
+ close(): void;
13
+ }
14
+ export declare function createLogger(level: LogLevel, logDir?: string): Logger;
15
+ /** Pre-config logger: stderr-only at info level. Replaced after startup. */
16
+ export declare let logger: Logger;
17
+ /**
18
+ * Initialize the fully-configured logger with file outputs.
19
+ * Replaces the module-level `logger` export.
20
+ */
21
+ export declare function initLogger(level: LogLevel, logDir?: string): void;
22
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,UAAU,CAA+B;gBAErC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM;IAsB5C,OAAO,CAAC,GAAG;IAoBX,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,KAAK,IAAI,IAAI;CAId;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,4EAA4E;AAC5E,eAAO,IAAI,MAAM,EAAE,MAA2B,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAGjE"}