@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,109 @@
1
+ /**
2
+ * In-memory message inbox with deduplication and timestamp ordering.
3
+ *
4
+ * Aggregates messages from both WebSocket push and poll fallback channels.
5
+ * Deduplicates by message ID (bounded to last 1000 IDs) and maintains
6
+ * messages ordered by server timestamp for consistent display.
7
+ */
8
+ export class MessageInbox {
9
+ messages = [];
10
+ seenIds = new Set();
11
+ seenOrder = [];
12
+ listeners = [];
13
+ lastMessageId = null;
14
+ /**
15
+ * Add a single message to the inbox.
16
+ * Returns true if the message was new (not a duplicate).
17
+ */
18
+ addMessage(msg) {
19
+ if (this.seenIds.has(msg.id)) {
20
+ return false;
21
+ }
22
+ // Track for dedup with bounded size
23
+ this.seenIds.add(msg.id);
24
+ this.seenOrder.push(msg.id);
25
+ if (this.seenOrder.length > 1000) {
26
+ const toRemove = this.seenOrder.shift();
27
+ this.seenIds.delete(toRemove);
28
+ }
29
+ // Insert maintaining timestamp order.
30
+ // Messages usually arrive in order, so search from the end.
31
+ let insertIdx = this.messages.length;
32
+ for (let i = this.messages.length - 1; i >= 0; i--) {
33
+ if (this.messages[i].timestamp <= msg.timestamp) {
34
+ insertIdx = i + 1;
35
+ break;
36
+ }
37
+ if (i === 0) {
38
+ insertIdx = 0;
39
+ }
40
+ }
41
+ this.messages.splice(insertIdx, 0, msg);
42
+ // Update lastMessageId to the latest message by timestamp
43
+ const last = this.messages[this.messages.length - 1];
44
+ this.lastMessageId = last.id;
45
+ // Notify listeners
46
+ for (const listener of this.listeners) {
47
+ listener();
48
+ }
49
+ return true;
50
+ }
51
+ /**
52
+ * Add multiple messages at once.
53
+ * Returns the count of new (non-duplicate) messages added.
54
+ */
55
+ addMessages(msgs) {
56
+ let count = 0;
57
+ for (const msg of msgs) {
58
+ if (this.addMessage(msg)) {
59
+ count++;
60
+ }
61
+ }
62
+ return count;
63
+ }
64
+ /**
65
+ * Get all unread messages (copy of the internal array).
66
+ */
67
+ getUnread() {
68
+ return this.messages.slice();
69
+ }
70
+ /**
71
+ * Mark messages as read by removing them from the unread list.
72
+ * Does NOT remove from seenIds -- they remain for deduplication.
73
+ */
74
+ markRead(messageIds) {
75
+ const idsToRemove = new Set(messageIds);
76
+ this.messages = this.messages.filter((m) => !idsToRemove.has(m.id));
77
+ }
78
+ /**
79
+ * Get the ID of the last message received (by timestamp order).
80
+ * Used for poll sync -- pass as lastMessageId query parameter.
81
+ */
82
+ getLastMessageId() {
83
+ return this.lastMessageId;
84
+ }
85
+ /**
86
+ * Subscribe to new message notifications.
87
+ * Returns an unsubscribe function.
88
+ */
89
+ onNewMessage(listener) {
90
+ this.listeners.push(listener);
91
+ return () => {
92
+ const idx = this.listeners.indexOf(listener);
93
+ if (idx !== -1) {
94
+ this.listeners.splice(idx, 1);
95
+ }
96
+ };
97
+ }
98
+ /**
99
+ * Clear all state: messages, seen IDs, last message ID, and listeners.
100
+ */
101
+ clear() {
102
+ this.messages = [];
103
+ this.seenIds = new Set();
104
+ this.seenOrder = [];
105
+ this.lastMessageId = null;
106
+ this.listeners = [];
107
+ }
108
+ }
109
+ //# sourceMappingURL=message-inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-inbox.js","sourceRoot":"","sources":["../../src/cloud/message-inbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import type { HttpClient } from "./http-client.js";
2
+ import type { MessageRecord } from "../types.js";
3
+ /**
4
+ * WebSocket connection state machine states.
5
+ */
6
+ export type WsState = "disconnected" | "connecting" | "connected" | "polling" | "reconnecting";
7
+ /**
8
+ * Configuration for CloudWsClient.
9
+ */
10
+ export interface CloudWsClientOptions {
11
+ /** Server base URL (http:// or https://). */
12
+ endpoint: string;
13
+ /** Cloud session ID. */
14
+ sessionId: string;
15
+ /** Peer ID. Can be empty string initially (deferred connection). */
16
+ peerId: string;
17
+ /** Bearer auth token. */
18
+ token: string;
19
+ /** HttpClient instance for poll fallback. */
20
+ http: HttpClient;
21
+ /** Callback for a single message from a "message" frame. */
22
+ onMessage: (msg: MessageRecord) => void;
23
+ /** Callback for a batch of messages from "queued_messages" frame. */
24
+ onMessages: (msgs: MessageRecord[]) => void;
25
+ /** Callback when a peer connects. */
26
+ onPeerConnected: (peer: {
27
+ peerId: string;
28
+ name: string;
29
+ project: string;
30
+ }) => void;
31
+ /** Callback when a peer disconnects. */
32
+ onPeerDisconnected: (peerId: string) => void;
33
+ /** Callback for peers snapshot frame. */
34
+ onPeersSnapshot: (peers: unknown[]) => void;
35
+ /** Callback for state changes. */
36
+ onStateChange: (state: WsState) => void;
37
+ /** Optional callback for error frames from the server. */
38
+ onError?: (frame: {
39
+ code: string;
40
+ message: string;
41
+ }) => void;
42
+ /** Poll interval in milliseconds (default: 5000). */
43
+ pollIntervalMs?: number;
44
+ }
45
+ /**
46
+ * WebSocket client with reconnection logic and poll fallback.
47
+ *
48
+ * Manages the full connection lifecycle:
49
+ * - Connects to server WS endpoint with token + peerId auth
50
+ * - Dispatches all 6 WsServerFrame types to callbacks
51
+ * - Falls back to HTTP polling on disconnect
52
+ * - Reconnects with exponential backoff (1s-30s with 10% jitter)
53
+ * - Supports deferred peerId via setPeerId() for lazy connection
54
+ * - Supports runtime credential updates via updateToken/updateSessionId
55
+ */
56
+ export declare class CloudWsClient {
57
+ private ws;
58
+ private state;
59
+ private readonly backoff;
60
+ private reconnectTimer;
61
+ private pollTimer;
62
+ private lastMessageId;
63
+ private opts;
64
+ constructor(options: CloudWsClientOptions);
65
+ /**
66
+ * Initiate WebSocket connection to the server.
67
+ * Guards against empty peerId -- call setPeerId() first.
68
+ */
69
+ connect(): void;
70
+ /**
71
+ * Disconnect from the WebSocket and stop all timers.
72
+ */
73
+ disconnect(): void;
74
+ /**
75
+ * Set or update the peerId.
76
+ * This is the trigger mechanism for deferred WS connection.
77
+ * Plan 05 creates CloudWsClient at startup with empty peerId (peerId is
78
+ * only known after register-peer succeeds). After registration, it calls
79
+ * this method to initiate the WS connection.
80
+ */
81
+ setPeerId(peerId: string): void;
82
+ /**
83
+ * Get the current connection state.
84
+ */
85
+ getState(): WsState;
86
+ /**
87
+ * Update the last seen message ID (e.g., from HttpBackend operations).
88
+ */
89
+ updateLastMessageId(id: string): void;
90
+ /**
91
+ * Update the auth token for reconnection.
92
+ * If currently connected, disconnects and reconnects with the new token.
93
+ */
94
+ updateToken(newToken: string): void;
95
+ /**
96
+ * Update the session ID.
97
+ * If currently connected, disconnects and reconnects.
98
+ */
99
+ updateSessionId(newSessionId: string): void;
100
+ /**
101
+ * Replace the HttpClient used for polling.
102
+ * The next poll cycle will use the new client automatically.
103
+ */
104
+ updateHttp(newHttp: HttpClient): void;
105
+ private setState;
106
+ private dispatchFrame;
107
+ private scheduleReconnect;
108
+ private startPolling;
109
+ private stopPolling;
110
+ private clearTimers;
111
+ }
112
+ //# sourceMappingURL=ws-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../../src/cloud/ws-client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,OAAO,GACf,cAAc,GACd,YAAY,GACZ,WAAW,GACX,SAAS,GACT,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,UAAU,CAAC;IACjB,4DAA4D;IAC5D,SAAS,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,qEAAqE;IACrE,UAAU,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC5C,qCAAqC;IACrC,eAAe,EAAE,CAAC,IAAI,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,KAAK,IAAI,CAAC;IACX,wCAAwC;IACxC,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,yCAAyC;IACzC,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAC5C,kCAAkC;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7D,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAKD;;;;;;;;;;GAUG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,IAAI,CAAkB;gBAElB,OAAO,EAAE,oBAAoB;IAQzC;;;OAGG;IACH,OAAO,IAAI,IAAI;IA+Df;;OAEG;IACH,UAAU,IAAI,IAAI;IAelB;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAa/B;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIrC;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQnC;;;OAGG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAQ3C;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;IAQrC,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAoCpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;CAOpB"}
@@ -0,0 +1,241 @@
1
+ import WebSocket from "ws";
2
+ import { ExponentialBackoff } from "./backoff.js";
3
+ import { WsCloseCode } from "./ws-frames.js";
4
+ /**
5
+ * WebSocket client with reconnection logic and poll fallback.
6
+ *
7
+ * Manages the full connection lifecycle:
8
+ * - Connects to server WS endpoint with token + peerId auth
9
+ * - Dispatches all 6 WsServerFrame types to callbacks
10
+ * - Falls back to HTTP polling on disconnect
11
+ * - Reconnects with exponential backoff (1s-30s with 10% jitter)
12
+ * - Supports deferred peerId via setPeerId() for lazy connection
13
+ * - Supports runtime credential updates via updateToken/updateSessionId
14
+ */
15
+ export class CloudWsClient {
16
+ ws = null;
17
+ state = "disconnected";
18
+ backoff;
19
+ reconnectTimer = null;
20
+ pollTimer = null;
21
+ lastMessageId = null;
22
+ opts;
23
+ constructor(options) {
24
+ this.opts = {
25
+ ...options,
26
+ pollIntervalMs: options.pollIntervalMs ?? 5000,
27
+ };
28
+ this.backoff = new ExponentialBackoff();
29
+ }
30
+ /**
31
+ * Initiate WebSocket connection to the server.
32
+ * Guards against empty peerId -- call setPeerId() first.
33
+ */
34
+ connect() {
35
+ if (!this.opts.peerId) {
36
+ console.warn("[CloudWsClient] Cannot connect WebSocket without peerId. Call setPeerId() first.");
37
+ return;
38
+ }
39
+ this.setState("connecting");
40
+ // Build WS URL: http->ws, https->wss
41
+ const wsUrl = this.opts.endpoint
42
+ .replace(/^https:/, "wss:")
43
+ .replace(/^http:/, "ws:");
44
+ const sessionPath = `/ws/${this.opts.sessionId}`;
45
+ const params = new URLSearchParams({
46
+ token: this.opts.token,
47
+ peerId: this.opts.peerId,
48
+ });
49
+ const url = `${wsUrl}${sessionPath}?${params.toString()}`;
50
+ const ws = new WebSocket(url);
51
+ this.ws = ws;
52
+ ws.on("open", () => {
53
+ this.setState("connected");
54
+ this.backoff.reset();
55
+ this.stopPolling();
56
+ });
57
+ ws.on("message", (data) => {
58
+ try {
59
+ const frame = JSON.parse(data.toString());
60
+ this.dispatchFrame(frame);
61
+ }
62
+ catch {
63
+ console.error("[CloudWsClient] Failed to parse WebSocket frame");
64
+ }
65
+ });
66
+ ws.on("close", (code, _reason) => {
67
+ this.ws = null;
68
+ // Start polling fallback
69
+ this.setState("polling");
70
+ this.startPolling();
71
+ // Use longer initial delay for service restart
72
+ if (code === WsCloseCode.SERVICE_RESTART) {
73
+ // Create a fresh backoff with 5s base for graceful restarts
74
+ const restartBackoff = new ExponentialBackoff(5000);
75
+ const delay = restartBackoff.next();
76
+ this.reconnectTimer = setTimeout(() => this.connect(), delay);
77
+ }
78
+ else {
79
+ this.scheduleReconnect();
80
+ }
81
+ });
82
+ ws.on("error", (err) => {
83
+ console.error("[CloudWsClient] WebSocket error:", err.message);
84
+ // Don't change state here -- the "close" event follows and handles it
85
+ });
86
+ }
87
+ /**
88
+ * Disconnect from the WebSocket and stop all timers.
89
+ */
90
+ disconnect() {
91
+ this.clearTimers();
92
+ if (this.ws &&
93
+ (this.ws.readyState === WebSocket.OPEN ||
94
+ this.ws.readyState === WebSocket.CONNECTING)) {
95
+ this.ws.close(WsCloseCode.NORMAL_CLOSURE);
96
+ }
97
+ this.ws = null;
98
+ this.setState("disconnected");
99
+ }
100
+ /**
101
+ * Set or update the peerId.
102
+ * This is the trigger mechanism for deferred WS connection.
103
+ * Plan 05 creates CloudWsClient at startup with empty peerId (peerId is
104
+ * only known after register-peer succeeds). After registration, it calls
105
+ * this method to initiate the WS connection.
106
+ */
107
+ setPeerId(peerId) {
108
+ this.opts.peerId = peerId;
109
+ if (peerId && this.state === "disconnected") {
110
+ // Trigger initial connection now that we have a peerId
111
+ this.connect();
112
+ }
113
+ else if (peerId && this.state !== "disconnected") {
114
+ // Already connected/connecting -- disconnect and reconnect with new peerId
115
+ this.disconnect();
116
+ this.connect();
117
+ }
118
+ }
119
+ /**
120
+ * Get the current connection state.
121
+ */
122
+ getState() {
123
+ return this.state;
124
+ }
125
+ /**
126
+ * Update the last seen message ID (e.g., from HttpBackend operations).
127
+ */
128
+ updateLastMessageId(id) {
129
+ this.lastMessageId = id;
130
+ }
131
+ /**
132
+ * Update the auth token for reconnection.
133
+ * If currently connected, disconnects and reconnects with the new token.
134
+ */
135
+ updateToken(newToken) {
136
+ this.opts.token = newToken;
137
+ if (this.state === "connected" || this.state === "connecting") {
138
+ this.disconnect();
139
+ this.connect();
140
+ }
141
+ }
142
+ /**
143
+ * Update the session ID.
144
+ * If currently connected, disconnects and reconnects.
145
+ */
146
+ updateSessionId(newSessionId) {
147
+ this.opts.sessionId = newSessionId;
148
+ if (this.state === "connected" || this.state === "connecting") {
149
+ this.disconnect();
150
+ this.connect();
151
+ }
152
+ }
153
+ /**
154
+ * Replace the HttpClient used for polling.
155
+ * The next poll cycle will use the new client automatically.
156
+ */
157
+ updateHttp(newHttp) {
158
+ this.opts.http = newHttp;
159
+ }
160
+ // -------------------------------------------------------------------------
161
+ // Private methods
162
+ // -------------------------------------------------------------------------
163
+ setState(newState) {
164
+ this.state = newState;
165
+ this.opts.onStateChange(newState);
166
+ }
167
+ dispatchFrame(frame) {
168
+ switch (frame.type) {
169
+ case "message":
170
+ this.opts.onMessage(frame.payload);
171
+ this.lastMessageId = frame.payload.id;
172
+ break;
173
+ case "queued_messages": {
174
+ const msgs = frame.payload.messages;
175
+ this.opts.onMessages(msgs);
176
+ if (msgs.length > 0) {
177
+ this.lastMessageId = msgs[msgs.length - 1].id;
178
+ }
179
+ break;
180
+ }
181
+ case "peer_connected":
182
+ this.opts.onPeerConnected(frame.payload);
183
+ break;
184
+ case "peer_disconnected":
185
+ this.opts.onPeerDisconnected(frame.payload.peerId);
186
+ break;
187
+ case "peers_snapshot":
188
+ this.opts.onPeersSnapshot(frame.payload.peers);
189
+ break;
190
+ case "error":
191
+ this.opts.onError?.(frame.payload);
192
+ break;
193
+ }
194
+ }
195
+ scheduleReconnect() {
196
+ this.setState("reconnecting");
197
+ const delay = this.backoff.next();
198
+ this.reconnectTimer = setTimeout(() => this.connect(), delay);
199
+ }
200
+ startPolling() {
201
+ if (this.pollTimer)
202
+ return;
203
+ const poll = async () => {
204
+ try {
205
+ const path = `/api/sessions/${this.opts.sessionId}/poll`;
206
+ const query = {
207
+ peerId: this.opts.peerId,
208
+ };
209
+ if (this.lastMessageId) {
210
+ query.lastMessageId = this.lastMessageId;
211
+ }
212
+ const result = await this.opts.http.get(path, query);
213
+ if (result.messages && result.messages.length > 0) {
214
+ this.opts.onMessages(result.messages);
215
+ this.lastMessageId =
216
+ result.messages[result.messages.length - 1].id;
217
+ }
218
+ }
219
+ catch (err) {
220
+ console.warn("[CloudWsClient] Poll failed:", err instanceof Error ? err.message : String(err));
221
+ }
222
+ };
223
+ // Poll immediately, then at interval
224
+ void poll();
225
+ this.pollTimer = setInterval(() => void poll(), this.opts.pollIntervalMs);
226
+ }
227
+ stopPolling() {
228
+ if (this.pollTimer) {
229
+ clearInterval(this.pollTimer);
230
+ this.pollTimer = null;
231
+ }
232
+ }
233
+ clearTimers() {
234
+ if (this.reconnectTimer) {
235
+ clearTimeout(this.reconnectTimer);
236
+ this.reconnectTimer = null;
237
+ }
238
+ this.stopPolling();
239
+ }
240
+ }
241
+ //# sourceMappingURL=ws-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../../src/cloud/ws-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { PeerInfo, MessageRecord } from "../types.js";
2
+ /**
3
+ * WebSocket close codes for different disconnect scenarios.
4
+ * Uses standard RFC 6455 / IANA-registered codes.
5
+ */
6
+ export declare const WsCloseCode: {
7
+ /** Clean client-initiated close. */
8
+ readonly NORMAL_CLOSURE: 1000;
9
+ /** Heartbeat timeout -- 3 missed pings (90s). */
10
+ readonly HEARTBEAT_TIMEOUT: 1001;
11
+ /** Invalid frame data from client. */
12
+ readonly UNSUPPORTED_DATA: 1003;
13
+ /** Unexpected server error. */
14
+ readonly INTERNAL_ERROR: 1011;
15
+ /** Server shutting down via SIGTERM. */
16
+ readonly SERVICE_RESTART: 1012;
17
+ };
18
+ export interface MessageFrame {
19
+ type: "message";
20
+ payload: MessageRecord;
21
+ timestamp: string;
22
+ }
23
+ export interface PeerConnectedFrame {
24
+ type: "peer_connected";
25
+ payload: {
26
+ peerId: string;
27
+ name: string;
28
+ project: string;
29
+ };
30
+ timestamp: string;
31
+ }
32
+ export interface PeerDisconnectedFrame {
33
+ type: "peer_disconnected";
34
+ payload: {
35
+ peerId: string;
36
+ };
37
+ timestamp: string;
38
+ }
39
+ export interface PeersSnapshotFrame {
40
+ type: "peers_snapshot";
41
+ payload: {
42
+ peers: PeerInfo[];
43
+ };
44
+ timestamp: string;
45
+ }
46
+ export interface QueuedMessagesFrame {
47
+ type: "queued_messages";
48
+ payload: {
49
+ messages: MessageRecord[];
50
+ };
51
+ timestamp: string;
52
+ }
53
+ export interface ErrorFrame {
54
+ type: "error";
55
+ payload: {
56
+ code: string;
57
+ message: string;
58
+ };
59
+ timestamp: string;
60
+ }
61
+ /**
62
+ * Discriminated union of all server-to-client WebSocket frame types.
63
+ * The `type` field acts as the discriminator for client-side dispatch.
64
+ */
65
+ export type WsServerFrame = MessageFrame | PeerConnectedFrame | PeerDisconnectedFrame | PeersSnapshotFrame | QueuedMessagesFrame | ErrorFrame;
66
+ //# sourceMappingURL=ws-frames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-frames.d.ts","sourceRoot":"","sources":["../../src/cloud/ws-frames.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,WAAW;IACtB,oCAAoC;;IAEpC,iDAAiD;;IAEjD,sCAAsC;;IAEtC,+BAA+B;;IAE/B,wCAAwC;;CAEhC,CAAC;AAMX,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE;QAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,kBAAkB,GAClB,qBAAqB,GACrB,kBAAkB,GAClB,mBAAmB,GACnB,UAAU,CAAC"}
@@ -0,0 +1,19 @@
1
+ // Client-side duplicate of server/src/ws/frames.ts type definitions.
2
+ // Keep in sync. Will move to @essentialai/cogent in Phase 7.
3
+ /**
4
+ * WebSocket close codes for different disconnect scenarios.
5
+ * Uses standard RFC 6455 / IANA-registered codes.
6
+ */
7
+ export const WsCloseCode = {
8
+ /** Clean client-initiated close. */
9
+ NORMAL_CLOSURE: 1000,
10
+ /** Heartbeat timeout -- 3 missed pings (90s). */
11
+ HEARTBEAT_TIMEOUT: 1001,
12
+ /** Invalid frame data from client. */
13
+ UNSUPPORTED_DATA: 1003,
14
+ /** Unexpected server error. */
15
+ INTERNAL_ERROR: 1011,
16
+ /** Server shutting down via SIGTERM. */
17
+ SERVICE_RESTART: 1012,
18
+ };
19
+ //# sourceMappingURL=ws-frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-frames.js","sourceRoot":"","sources":["../../src/cloud/ws-frames.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,6DAA6D;AAI7D;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,oCAAoC;IACpC,cAAc,EAAE,IAAI;IACpB,iDAAiD;IACjD,iBAAiB,EAAE,IAAI;IACvB,sCAAsC;IACtC,gBAAgB,EAAE,IAAI;IACtB,+BAA+B;IAC/B,cAAc,EAAE,IAAI;IACpB,wCAAwC;IACxC,eAAe,EAAE,IAAI;CACb,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ export declare const configSchema: z.ZodObject<{
3
+ COGENT_STATE_PATH: z.ZodDefault<z.ZodString>;
4
+ COGENT_ENDPOINT: z.ZodOptional<z.ZodString>;
5
+ COGENT_SESSION_ID: z.ZodOptional<z.ZodString>;
6
+ COGENT_SECRET: z.ZodOptional<z.ZodString>;
7
+ COGENT_POLL_INTERVAL_MS: z.ZodDefault<z.ZodNumber>;
8
+ COGENT_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
9
+ COGENT_CHAR_LIMIT: z.ZodDefault<z.ZodNumber>;
10
+ COGENT_LOG_LEVEL: z.ZodDefault<z.ZodEnum<["debug", "info", "warn", "error"]>>;
11
+ COGENT_CLAUDE_PATH: z.ZodDefault<z.ZodString>;
12
+ COGENT_STALE_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ COGENT_STATE_PATH: string;
15
+ COGENT_POLL_INTERVAL_MS: number;
16
+ COGENT_TIMEOUT_MS: number;
17
+ COGENT_CHAR_LIMIT: number;
18
+ COGENT_LOG_LEVEL: "info" | "debug" | "warn" | "error";
19
+ COGENT_CLAUDE_PATH: string;
20
+ COGENT_STALE_TIMEOUT_MS: number;
21
+ COGENT_ENDPOINT?: string | undefined;
22
+ COGENT_SESSION_ID?: string | undefined;
23
+ COGENT_SECRET?: string | undefined;
24
+ }, {
25
+ COGENT_STATE_PATH?: string | undefined;
26
+ COGENT_ENDPOINT?: string | undefined;
27
+ COGENT_SESSION_ID?: string | undefined;
28
+ COGENT_SECRET?: string | undefined;
29
+ COGENT_POLL_INTERVAL_MS?: number | undefined;
30
+ COGENT_TIMEOUT_MS?: number | undefined;
31
+ COGENT_CHAR_LIMIT?: number | undefined;
32
+ COGENT_LOG_LEVEL?: "info" | "debug" | "warn" | "error" | undefined;
33
+ COGENT_CLAUDE_PATH?: string | undefined;
34
+ COGENT_STALE_TIMEOUT_MS?: number | undefined;
35
+ }>;
36
+ export type Config = z.infer<typeof configSchema>;
37
+ export declare function loadConfig(env?: Record<string, string | undefined>): Config;
38
+ export declare function getConfig(): Config;
39
+ export declare function resetConfig(): void;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAavB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD,wBAAgB,UAAU,CACxB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,CAUR;AAED,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
package/dist/config.js ADDED
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const defaultStatePath = path.join(os.homedir(), ".cogent");
5
+ export const configSchema = z.object({
6
+ COGENT_STATE_PATH: z.string().default(defaultStatePath),
7
+ COGENT_ENDPOINT: z.string().optional(),
8
+ COGENT_SESSION_ID: z.string().optional(),
9
+ COGENT_SECRET: z.string().optional(),
10
+ COGENT_POLL_INTERVAL_MS: z.coerce.number().int().min(1000).default(5000),
11
+ COGENT_TIMEOUT_MS: z.coerce.number().int().min(1000).default(120_000),
12
+ COGENT_CHAR_LIMIT: z.coerce.number().int().min(0).default(0),
13
+ COGENT_LOG_LEVEL: z
14
+ .enum(["debug", "info", "warn", "error"])
15
+ .default("info"),
16
+ COGENT_CLAUDE_PATH: z.string().default("claude"),
17
+ COGENT_STALE_TIMEOUT_MS: z.coerce.number().int().min(0).default(1_800_000),
18
+ });
19
+ let _config = null;
20
+ export function loadConfig(env = process.env) {
21
+ const result = configSchema.safeParse(env);
22
+ if (!result.success) {
23
+ const issues = result.error.issues
24
+ .map((i) => ` ${i.path.join(".")}: ${i.message}`)
25
+ .join("\n");
26
+ throw new Error(`Invalid configuration:\n${issues}`);
27
+ }
28
+ _config = Object.freeze(result.data);
29
+ return _config;
30
+ }
31
+ export function getConfig() {
32
+ if (!_config)
33
+ throw new Error("Config not loaded. Call loadConfig() first.");
34
+ return _config;
35
+ }
36
+ export function resetConfig() {
37
+ _config = null;
38
+ }
39
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC;IACvD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACxC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,uBAAuB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IACxE,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACrE,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,gBAAgB,EAAE,CAAC;SAChB,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;SACxC,OAAO,CAAC,MAAM,CAAC;IAClB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;IAChD,uBAAuB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;CAC3E,CAAC,CAAC;AAIH,IAAI,OAAO,GAAkB,IAAI,CAAC;AAElC,MAAM,UAAU,UAAU,CACxB,MAA0C,OAAO,CAAC,GAAG;IAErD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACjD,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC"}