whale-code 6.4.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 (319) hide show
  1. package/README.md +95 -0
  2. package/bin/swag-agent.js +9 -0
  3. package/bin/swagmanager-mcp.js +321 -0
  4. package/dist/cli/app.d.ts +26 -0
  5. package/dist/cli/app.js +64 -0
  6. package/dist/cli/chat/AgentSelector.d.ts +14 -0
  7. package/dist/cli/chat/AgentSelector.js +14 -0
  8. package/dist/cli/chat/ChatApp.d.ts +9 -0
  9. package/dist/cli/chat/ChatApp.js +267 -0
  10. package/dist/cli/chat/ChatInput.d.ts +39 -0
  11. package/dist/cli/chat/ChatInput.js +509 -0
  12. package/dist/cli/chat/MarkdownText.d.ts +10 -0
  13. package/dist/cli/chat/MarkdownText.js +20 -0
  14. package/dist/cli/chat/MessageList.d.ts +37 -0
  15. package/dist/cli/chat/MessageList.js +80 -0
  16. package/dist/cli/chat/ModelSelector.d.ts +20 -0
  17. package/dist/cli/chat/ModelSelector.js +73 -0
  18. package/dist/cli/chat/RewindViewer.d.ts +26 -0
  19. package/dist/cli/chat/RewindViewer.js +185 -0
  20. package/dist/cli/chat/StoreSelector.d.ts +14 -0
  21. package/dist/cli/chat/StoreSelector.js +24 -0
  22. package/dist/cli/chat/StreamingText.d.ts +12 -0
  23. package/dist/cli/chat/StreamingText.js +12 -0
  24. package/dist/cli/chat/SubagentPanel.d.ts +45 -0
  25. package/dist/cli/chat/SubagentPanel.js +110 -0
  26. package/dist/cli/chat/TeamPanel.d.ts +21 -0
  27. package/dist/cli/chat/TeamPanel.js +42 -0
  28. package/dist/cli/chat/ToolIndicator.d.ts +25 -0
  29. package/dist/cli/chat/ToolIndicator.js +436 -0
  30. package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
  31. package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
  32. package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
  33. package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
  34. package/dist/cli/commands/config-cmd.d.ts +10 -0
  35. package/dist/cli/commands/config-cmd.js +99 -0
  36. package/dist/cli/commands/doctor.d.ts +14 -0
  37. package/dist/cli/commands/doctor.js +172 -0
  38. package/dist/cli/commands/init.d.ts +16 -0
  39. package/dist/cli/commands/init.js +278 -0
  40. package/dist/cli/commands/mcp.d.ts +12 -0
  41. package/dist/cli/commands/mcp.js +162 -0
  42. package/dist/cli/login/LoginApp.d.ts +7 -0
  43. package/dist/cli/login/LoginApp.js +157 -0
  44. package/dist/cli/print-mode.d.ts +31 -0
  45. package/dist/cli/print-mode.js +202 -0
  46. package/dist/cli/serve-mode.d.ts +37 -0
  47. package/dist/cli/serve-mode.js +636 -0
  48. package/dist/cli/services/agent-definitions.d.ts +25 -0
  49. package/dist/cli/services/agent-definitions.js +91 -0
  50. package/dist/cli/services/agent-events.d.ts +178 -0
  51. package/dist/cli/services/agent-events.js +175 -0
  52. package/dist/cli/services/agent-loop.d.ts +90 -0
  53. package/dist/cli/services/agent-loop.js +762 -0
  54. package/dist/cli/services/agent-worker-base.d.ts +97 -0
  55. package/dist/cli/services/agent-worker-base.js +220 -0
  56. package/dist/cli/services/auth-service.d.ts +30 -0
  57. package/dist/cli/services/auth-service.js +160 -0
  58. package/dist/cli/services/background-processes.d.ts +126 -0
  59. package/dist/cli/services/background-processes.js +318 -0
  60. package/dist/cli/services/browser-auth.d.ts +24 -0
  61. package/dist/cli/services/browser-auth.js +180 -0
  62. package/dist/cli/services/claude-md-loader.d.ts +16 -0
  63. package/dist/cli/services/claude-md-loader.js +58 -0
  64. package/dist/cli/services/config-store.d.ts +47 -0
  65. package/dist/cli/services/config-store.js +79 -0
  66. package/dist/cli/services/debug-log.d.ts +10 -0
  67. package/dist/cli/services/debug-log.js +52 -0
  68. package/dist/cli/services/error-logger.d.ts +58 -0
  69. package/dist/cli/services/error-logger.js +269 -0
  70. package/dist/cli/services/file-history.d.ts +21 -0
  71. package/dist/cli/services/file-history.js +83 -0
  72. package/dist/cli/services/format-server-response.d.ts +16 -0
  73. package/dist/cli/services/format-server-response.js +440 -0
  74. package/dist/cli/services/git-context.d.ts +11 -0
  75. package/dist/cli/services/git-context.js +66 -0
  76. package/dist/cli/services/hooks.d.ts +85 -0
  77. package/dist/cli/services/hooks.js +258 -0
  78. package/dist/cli/services/interactive-tools.d.ts +125 -0
  79. package/dist/cli/services/interactive-tools.js +260 -0
  80. package/dist/cli/services/keybinding-manager.d.ts +52 -0
  81. package/dist/cli/services/keybinding-manager.js +115 -0
  82. package/dist/cli/services/local-tools.d.ts +22 -0
  83. package/dist/cli/services/local-tools.js +697 -0
  84. package/dist/cli/services/lsp-manager.d.ts +18 -0
  85. package/dist/cli/services/lsp-manager.js +717 -0
  86. package/dist/cli/services/mcp-client.d.ts +48 -0
  87. package/dist/cli/services/mcp-client.js +157 -0
  88. package/dist/cli/services/memory-manager.d.ts +16 -0
  89. package/dist/cli/services/memory-manager.js +57 -0
  90. package/dist/cli/services/model-manager.d.ts +18 -0
  91. package/dist/cli/services/model-manager.js +71 -0
  92. package/dist/cli/services/model-router.d.ts +26 -0
  93. package/dist/cli/services/model-router.js +149 -0
  94. package/dist/cli/services/permission-modes.d.ts +13 -0
  95. package/dist/cli/services/permission-modes.js +43 -0
  96. package/dist/cli/services/rewind.d.ts +84 -0
  97. package/dist/cli/services/rewind.js +194 -0
  98. package/dist/cli/services/ripgrep.d.ts +28 -0
  99. package/dist/cli/services/ripgrep.js +138 -0
  100. package/dist/cli/services/sandbox.d.ts +29 -0
  101. package/dist/cli/services/sandbox.js +97 -0
  102. package/dist/cli/services/server-tools.d.ts +61 -0
  103. package/dist/cli/services/server-tools.js +543 -0
  104. package/dist/cli/services/session-persistence.d.ts +23 -0
  105. package/dist/cli/services/session-persistence.js +99 -0
  106. package/dist/cli/services/subagent-worker.d.ts +19 -0
  107. package/dist/cli/services/subagent-worker.js +41 -0
  108. package/dist/cli/services/subagent.d.ts +47 -0
  109. package/dist/cli/services/subagent.js +647 -0
  110. package/dist/cli/services/system-prompt.d.ts +7 -0
  111. package/dist/cli/services/system-prompt.js +198 -0
  112. package/dist/cli/services/team-lead.d.ts +73 -0
  113. package/dist/cli/services/team-lead.js +512 -0
  114. package/dist/cli/services/team-state.d.ts +77 -0
  115. package/dist/cli/services/team-state.js +398 -0
  116. package/dist/cli/services/teammate.d.ts +31 -0
  117. package/dist/cli/services/teammate.js +689 -0
  118. package/dist/cli/services/telemetry.d.ts +61 -0
  119. package/dist/cli/services/telemetry.js +209 -0
  120. package/dist/cli/services/tools/agent-tools.d.ts +14 -0
  121. package/dist/cli/services/tools/agent-tools.js +347 -0
  122. package/dist/cli/services/tools/file-ops.d.ts +15 -0
  123. package/dist/cli/services/tools/file-ops.js +487 -0
  124. package/dist/cli/services/tools/search-tools.d.ts +8 -0
  125. package/dist/cli/services/tools/search-tools.js +186 -0
  126. package/dist/cli/services/tools/shell-exec.d.ts +10 -0
  127. package/dist/cli/services/tools/shell-exec.js +168 -0
  128. package/dist/cli/services/tools/task-manager.d.ts +28 -0
  129. package/dist/cli/services/tools/task-manager.js +209 -0
  130. package/dist/cli/services/tools/web-tools.d.ts +11 -0
  131. package/dist/cli/services/tools/web-tools.js +395 -0
  132. package/dist/cli/setup/SetupApp.d.ts +9 -0
  133. package/dist/cli/setup/SetupApp.js +191 -0
  134. package/dist/cli/shared/MatrixIntro.d.ts +4 -0
  135. package/dist/cli/shared/MatrixIntro.js +83 -0
  136. package/dist/cli/shared/Theme.d.ts +74 -0
  137. package/dist/cli/shared/Theme.js +127 -0
  138. package/dist/cli/shared/WhaleBanner.d.ts +10 -0
  139. package/dist/cli/shared/WhaleBanner.js +12 -0
  140. package/dist/cli/shared/markdown.d.ts +21 -0
  141. package/dist/cli/shared/markdown.js +756 -0
  142. package/dist/cli/status/StatusApp.d.ts +4 -0
  143. package/dist/cli/status/StatusApp.js +105 -0
  144. package/dist/cli/stores/StoreApp.d.ts +7 -0
  145. package/dist/cli/stores/StoreApp.js +81 -0
  146. package/dist/index.d.ts +15 -0
  147. package/dist/index.js +538 -0
  148. package/dist/local-agent/connection.d.ts +48 -0
  149. package/dist/local-agent/connection.js +332 -0
  150. package/dist/local-agent/discovery.d.ts +18 -0
  151. package/dist/local-agent/discovery.js +146 -0
  152. package/dist/local-agent/executor.d.ts +34 -0
  153. package/dist/local-agent/executor.js +241 -0
  154. package/dist/local-agent/index.d.ts +14 -0
  155. package/dist/local-agent/index.js +198 -0
  156. package/dist/node/adapters/base.d.ts +35 -0
  157. package/dist/node/adapters/base.js +10 -0
  158. package/dist/node/adapters/discord.d.ts +29 -0
  159. package/dist/node/adapters/discord.js +299 -0
  160. package/dist/node/adapters/email.d.ts +23 -0
  161. package/dist/node/adapters/email.js +218 -0
  162. package/dist/node/adapters/imessage.d.ts +17 -0
  163. package/dist/node/adapters/imessage.js +118 -0
  164. package/dist/node/adapters/slack.d.ts +26 -0
  165. package/dist/node/adapters/slack.js +259 -0
  166. package/dist/node/adapters/sms.d.ts +23 -0
  167. package/dist/node/adapters/sms.js +161 -0
  168. package/dist/node/adapters/telegram.d.ts +17 -0
  169. package/dist/node/adapters/telegram.js +101 -0
  170. package/dist/node/adapters/webchat.d.ts +27 -0
  171. package/dist/node/adapters/webchat.js +160 -0
  172. package/dist/node/adapters/whatsapp.d.ts +28 -0
  173. package/dist/node/adapters/whatsapp.js +230 -0
  174. package/dist/node/cli.d.ts +2 -0
  175. package/dist/node/cli.js +325 -0
  176. package/dist/node/config.d.ts +17 -0
  177. package/dist/node/config.js +31 -0
  178. package/dist/node/runtime.d.ts +50 -0
  179. package/dist/node/runtime.js +351 -0
  180. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
  181. package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
  182. package/dist/server/handlers/analytics.d.ts +17 -0
  183. package/dist/server/handlers/analytics.js +266 -0
  184. package/dist/server/handlers/api-keys.d.ts +6 -0
  185. package/dist/server/handlers/api-keys.js +221 -0
  186. package/dist/server/handlers/billing.d.ts +33 -0
  187. package/dist/server/handlers/billing.js +272 -0
  188. package/dist/server/handlers/browser.d.ts +10 -0
  189. package/dist/server/handlers/browser.js +517 -0
  190. package/dist/server/handlers/catalog.d.ts +99 -0
  191. package/dist/server/handlers/catalog.js +976 -0
  192. package/dist/server/handlers/comms.d.ts +254 -0
  193. package/dist/server/handlers/comms.js +588 -0
  194. package/dist/server/handlers/creations.d.ts +6 -0
  195. package/dist/server/handlers/creations.js +479 -0
  196. package/dist/server/handlers/crm.d.ts +89 -0
  197. package/dist/server/handlers/crm.js +538 -0
  198. package/dist/server/handlers/discovery.d.ts +6 -0
  199. package/dist/server/handlers/discovery.js +288 -0
  200. package/dist/server/handlers/embeddings.d.ts +92 -0
  201. package/dist/server/handlers/embeddings.js +197 -0
  202. package/dist/server/handlers/enrichment.d.ts +8 -0
  203. package/dist/server/handlers/enrichment.js +768 -0
  204. package/dist/server/handlers/image-gen.d.ts +6 -0
  205. package/dist/server/handlers/image-gen.js +409 -0
  206. package/dist/server/handlers/inventory.d.ts +319 -0
  207. package/dist/server/handlers/inventory.js +447 -0
  208. package/dist/server/handlers/kali.d.ts +10 -0
  209. package/dist/server/handlers/kali.js +210 -0
  210. package/dist/server/handlers/llm-providers.d.ts +6 -0
  211. package/dist/server/handlers/llm-providers.js +673 -0
  212. package/dist/server/handlers/local-agent.d.ts +6 -0
  213. package/dist/server/handlers/local-agent.js +118 -0
  214. package/dist/server/handlers/meta-ads.d.ts +111 -0
  215. package/dist/server/handlers/meta-ads.js +2279 -0
  216. package/dist/server/handlers/nodes.d.ts +33 -0
  217. package/dist/server/handlers/nodes.js +699 -0
  218. package/dist/server/handlers/operations.d.ts +138 -0
  219. package/dist/server/handlers/operations.js +131 -0
  220. package/dist/server/handlers/platform.d.ts +23 -0
  221. package/dist/server/handlers/platform.js +227 -0
  222. package/dist/server/handlers/supply-chain.d.ts +19 -0
  223. package/dist/server/handlers/supply-chain.js +327 -0
  224. package/dist/server/handlers/transcription.d.ts +17 -0
  225. package/dist/server/handlers/transcription.js +121 -0
  226. package/dist/server/handlers/video-gen.d.ts +6 -0
  227. package/dist/server/handlers/video-gen.js +466 -0
  228. package/dist/server/handlers/voice.d.ts +8 -0
  229. package/dist/server/handlers/voice.js +1146 -0
  230. package/dist/server/handlers/workflow-steps.d.ts +86 -0
  231. package/dist/server/handlers/workflow-steps.js +2349 -0
  232. package/dist/server/handlers/workflows.d.ts +7 -0
  233. package/dist/server/handlers/workflows.js +989 -0
  234. package/dist/server/index.d.ts +1 -0
  235. package/dist/server/index.js +2427 -0
  236. package/dist/server/lib/batch-client.d.ts +80 -0
  237. package/dist/server/lib/batch-client.js +467 -0
  238. package/dist/server/lib/code-worker-pool.d.ts +31 -0
  239. package/dist/server/lib/code-worker-pool.js +224 -0
  240. package/dist/server/lib/code-worker.d.ts +1 -0
  241. package/dist/server/lib/code-worker.js +188 -0
  242. package/dist/server/lib/compaction-service.d.ts +32 -0
  243. package/dist/server/lib/compaction-service.js +162 -0
  244. package/dist/server/lib/logger.d.ts +19 -0
  245. package/dist/server/lib/logger.js +46 -0
  246. package/dist/server/lib/otel.d.ts +38 -0
  247. package/dist/server/lib/otel.js +126 -0
  248. package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
  249. package/dist/server/lib/pg-rate-limiter.js +86 -0
  250. package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
  251. package/dist/server/lib/prompt-sanitizer.js +177 -0
  252. package/dist/server/lib/provider-capabilities.d.ts +85 -0
  253. package/dist/server/lib/provider-capabilities.js +190 -0
  254. package/dist/server/lib/provider-failover.d.ts +74 -0
  255. package/dist/server/lib/provider-failover.js +210 -0
  256. package/dist/server/lib/rate-limiter.d.ts +39 -0
  257. package/dist/server/lib/rate-limiter.js +147 -0
  258. package/dist/server/lib/server-agent-loop.d.ts +107 -0
  259. package/dist/server/lib/server-agent-loop.js +667 -0
  260. package/dist/server/lib/server-subagent.d.ts +78 -0
  261. package/dist/server/lib/server-subagent.js +203 -0
  262. package/dist/server/lib/session-checkpoint.d.ts +51 -0
  263. package/dist/server/lib/session-checkpoint.js +145 -0
  264. package/dist/server/lib/ssrf-guard.d.ts +13 -0
  265. package/dist/server/lib/ssrf-guard.js +240 -0
  266. package/dist/server/lib/supabase-client.d.ts +7 -0
  267. package/dist/server/lib/supabase-client.js +78 -0
  268. package/dist/server/lib/template-resolver.d.ts +31 -0
  269. package/dist/server/lib/template-resolver.js +215 -0
  270. package/dist/server/lib/utils.d.ts +16 -0
  271. package/dist/server/lib/utils.js +147 -0
  272. package/dist/server/local-agent-gateway.d.ts +82 -0
  273. package/dist/server/local-agent-gateway.js +426 -0
  274. package/dist/server/providers/anthropic.d.ts +20 -0
  275. package/dist/server/providers/anthropic.js +199 -0
  276. package/dist/server/providers/bedrock.d.ts +20 -0
  277. package/dist/server/providers/bedrock.js +194 -0
  278. package/dist/server/providers/gemini.d.ts +24 -0
  279. package/dist/server/providers/gemini.js +486 -0
  280. package/dist/server/providers/openai.d.ts +24 -0
  281. package/dist/server/providers/openai.js +522 -0
  282. package/dist/server/providers/registry.d.ts +32 -0
  283. package/dist/server/providers/registry.js +58 -0
  284. package/dist/server/providers/shared.d.ts +32 -0
  285. package/dist/server/providers/shared.js +124 -0
  286. package/dist/server/providers/types.d.ts +92 -0
  287. package/dist/server/providers/types.js +12 -0
  288. package/dist/server/proxy-handlers.d.ts +6 -0
  289. package/dist/server/proxy-handlers.js +89 -0
  290. package/dist/server/tool-router.d.ts +149 -0
  291. package/dist/server/tool-router.js +803 -0
  292. package/dist/server/validation.d.ts +24 -0
  293. package/dist/server/validation.js +301 -0
  294. package/dist/server/worker.d.ts +19 -0
  295. package/dist/server/worker.js +201 -0
  296. package/dist/setup.d.ts +8 -0
  297. package/dist/setup.js +181 -0
  298. package/dist/shared/agent-core.d.ts +157 -0
  299. package/dist/shared/agent-core.js +534 -0
  300. package/dist/shared/anthropic-types.d.ts +105 -0
  301. package/dist/shared/anthropic-types.js +7 -0
  302. package/dist/shared/api-client.d.ts +90 -0
  303. package/dist/shared/api-client.js +379 -0
  304. package/dist/shared/constants.d.ts +33 -0
  305. package/dist/shared/constants.js +80 -0
  306. package/dist/shared/sse-parser.d.ts +26 -0
  307. package/dist/shared/sse-parser.js +259 -0
  308. package/dist/shared/tool-dispatch.d.ts +52 -0
  309. package/dist/shared/tool-dispatch.js +191 -0
  310. package/dist/shared/types.d.ts +72 -0
  311. package/dist/shared/types.js +7 -0
  312. package/dist/updater.d.ts +25 -0
  313. package/dist/updater.js +140 -0
  314. package/dist/webchat/widget.d.ts +0 -0
  315. package/dist/webchat/widget.js +397 -0
  316. package/package.json +95 -0
  317. package/src/cli/services/builtin-skills/commit.md +19 -0
  318. package/src/cli/services/builtin-skills/review-pr.md +21 -0
  319. package/src/cli/services/builtin-skills/review.md +18 -0
@@ -0,0 +1,351 @@
1
+ import { cpus, totalmem, hostname } from "node:os";
2
+ import { IMessageAdapter } from "./adapters/imessage.js";
3
+ import { TelegramAdapter } from "./adapters/telegram.js";
4
+ import { DiscordAdapter } from "./adapters/discord.js";
5
+ import { SlackAdapter } from "./adapters/slack.js";
6
+ import { WebchatAdapter } from "./adapters/webchat.js";
7
+ import { WhatsAppAdapter } from "./adapters/whatsapp.js";
8
+ import { SmsAdapter } from "./adapters/sms.js";
9
+ import { EmailAdapter } from "./adapters/email.js";
10
+ import { captureError, addBreadcrumb } from "../cli/services/error-logger.js";
11
+ const HEARTBEAT_INTERVAL_MS = 60_000;
12
+ const POLL_INTERVAL_MS = 2_000;
13
+ const VERSION = "1.1.0";
14
+ // Retry configuration
15
+ const MAX_RELAY_RETRIES = 3;
16
+ const INITIAL_RETRY_DELAY_MS = 1_000;
17
+ const MAX_RETRY_DELAY_MS = 30_000;
18
+ export class NodeRuntime {
19
+ config;
20
+ adapters = new Map();
21
+ heartbeatTimer = null;
22
+ pollTimers = new Map();
23
+ running = false;
24
+ startedAt = null;
25
+ // Connection health tracking
26
+ status = "starting";
27
+ consecutiveServerFailures = 0;
28
+ lastServerContact = null;
29
+ heartbeatsSent = 0;
30
+ heartbeatsFailed = 0;
31
+ messagesRelayed = 0;
32
+ messagesFailed = 0;
33
+ constructor(config) {
34
+ this.config = config;
35
+ }
36
+ getStats() {
37
+ const adapterStats = {};
38
+ for (const [id, adapter] of this.adapters) {
39
+ const s = adapter.getStats();
40
+ adapterStats[id] = {
41
+ type: adapter.type,
42
+ running: adapter.isRunning(),
43
+ messages_in: s.messages_in,
44
+ messages_out: s.messages_out,
45
+ errors: s.errors,
46
+ };
47
+ }
48
+ return {
49
+ status: this.status,
50
+ uptime_seconds: this.startedAt ? Math.floor((Date.now() - this.startedAt.getTime()) / 1000) : 0,
51
+ heartbeats_sent: this.heartbeatsSent,
52
+ heartbeats_failed: this.heartbeatsFailed,
53
+ messages_relayed: this.messagesRelayed,
54
+ messages_failed: this.messagesFailed,
55
+ consecutive_server_failures: this.consecutiveServerFailures,
56
+ last_server_contact: this.lastServerContact?.toISOString() || null,
57
+ adapters: adapterStats,
58
+ };
59
+ }
60
+ async start() {
61
+ this.running = true;
62
+ this.startedAt = new Date();
63
+ this.status = "starting";
64
+ addBreadcrumb("node", `Starting node ${this.config.node_id} v${VERSION}`, "info");
65
+ console.log(`[node] Starting WhaleNode "${this.config.node_id}" v${VERSION}...`);
66
+ // Start heartbeat — initial heartbeat validates server connectivity
67
+ const heartbeatOk = await this.sendHeartbeat();
68
+ this.status = heartbeatOk ? "connected" : "degraded";
69
+ this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
70
+ // Initialize adapters for each channel
71
+ for (const ch of this.config.channels) {
72
+ await this.startAdapter(ch);
73
+ }
74
+ // Handle graceful shutdown
75
+ process.on("SIGTERM", () => this.stop());
76
+ process.on("SIGINT", () => this.stop());
77
+ console.log(`[node] Running with ${this.adapters.size} channel adapter(s). Status: ${this.status}. Press Ctrl+C to stop.`);
78
+ }
79
+ async stop() {
80
+ console.log(`[node] Shutting down...`);
81
+ this.running = false;
82
+ this.status = "disconnected";
83
+ if (this.heartbeatTimer)
84
+ clearInterval(this.heartbeatTimer);
85
+ for (const [, timer] of this.pollTimers)
86
+ clearInterval(timer);
87
+ for (const [name, adapter] of this.adapters) {
88
+ console.log(`[node] Stopping adapter: ${name}`);
89
+ await adapter.stop();
90
+ }
91
+ console.log(`[node] Stopped.`);
92
+ process.exit(0);
93
+ }
94
+ async startAdapter(ch) {
95
+ let adapter;
96
+ switch (ch.type) {
97
+ case "imessage":
98
+ adapter = new IMessageAdapter(ch.name, ch.config);
99
+ break;
100
+ case "telegram":
101
+ adapter = new TelegramAdapter(ch.name, ch.config);
102
+ break;
103
+ case "discord":
104
+ adapter = new DiscordAdapter(ch.name, ch.config);
105
+ break;
106
+ case "slack":
107
+ adapter = new SlackAdapter(ch.name, ch.config);
108
+ break;
109
+ case "webchat":
110
+ adapter = new WebchatAdapter(ch.name, ch.config);
111
+ break;
112
+ case "whatsapp":
113
+ adapter = new WhatsAppAdapter(ch.name, ch.config);
114
+ break;
115
+ case "sms":
116
+ adapter = new SmsAdapter(ch.name, ch.config);
117
+ break;
118
+ case "email":
119
+ adapter = new EmailAdapter(ch.name, ch.config);
120
+ break;
121
+ default:
122
+ console.warn(`[node] Unknown channel type: ${ch.type}`);
123
+ return;
124
+ }
125
+ // Wire up inbound message handler — relay to server and deliver agent response
126
+ adapter.setMessageHandler(async (msg) => {
127
+ await this.relayAndDeliver(ch.id, msg);
128
+ });
129
+ await adapter.start();
130
+ this.adapters.set(ch.id, adapter);
131
+ // Start polling for queued outbound messages (async agent responses)
132
+ const pollTimer = setInterval(() => this.pollOutbound(ch.id, adapter), POLL_INTERVAL_MS);
133
+ this.pollTimers.set(ch.id, pollTimer);
134
+ console.log(`[node] Adapter started: ${ch.type} "${ch.name}"`);
135
+ }
136
+ /** Fetch with retry and exponential backoff */
137
+ async fetchWithRetry(url, options, maxRetries = MAX_RELAY_RETRIES) {
138
+ let lastError = null;
139
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
140
+ try {
141
+ const res = await fetch(url, {
142
+ ...options,
143
+ signal: AbortSignal.timeout(30_000), // 30s timeout per attempt
144
+ });
145
+ // Retry on 5xx (server errors, including Cloudflare 520/522/524)
146
+ if (res.status >= 500 && attempt < maxRetries) {
147
+ const delay = Math.min(INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
148
+ console.warn(`[fetch] ${res.status} from ${url}, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
149
+ await sleep(delay);
150
+ continue;
151
+ }
152
+ // Success or non-retryable error — update health tracking
153
+ this.recordServerContact(res.ok);
154
+ return res;
155
+ }
156
+ catch (err) {
157
+ lastError = err;
158
+ if (attempt < maxRetries) {
159
+ const delay = Math.min(INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
160
+ console.warn(`[fetch] Error: ${lastError.message}, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
161
+ await sleep(delay);
162
+ }
163
+ }
164
+ }
165
+ this.recordServerContact(false);
166
+ throw lastError || new Error("Fetch failed after retries");
167
+ }
168
+ /** Track server connectivity health */
169
+ recordServerContact(success) {
170
+ if (success) {
171
+ this.consecutiveServerFailures = 0;
172
+ this.lastServerContact = new Date();
173
+ if (this.status === "degraded" || this.status === "disconnected") {
174
+ console.log(`[node] Server connectivity restored`);
175
+ this.status = "connected";
176
+ }
177
+ }
178
+ else {
179
+ this.consecutiveServerFailures++;
180
+ if (this.consecutiveServerFailures >= 3 && this.status === "connected") {
181
+ console.warn(`[node] Server connectivity degraded (${this.consecutiveServerFailures} consecutive failures)`);
182
+ this.status = "degraded";
183
+ }
184
+ if (this.consecutiveServerFailures >= 10 && this.status !== "disconnected") {
185
+ console.error(`[node] Server appears unreachable (${this.consecutiveServerFailures} consecutive failures)`);
186
+ this.status = "disconnected";
187
+ }
188
+ }
189
+ }
190
+ /** Poll for undelivered outbound messages and deliver via adapter */
191
+ async pollOutbound(channelId, adapter) {
192
+ if (!this.running)
193
+ return;
194
+ try {
195
+ const url = `${this.config.server_url}/channels/${channelId}/messages?undelivered=true&limit=10`;
196
+ const res = await this.fetchWithRetry(url, {
197
+ method: "GET",
198
+ headers: {
199
+ Authorization: `Bearer ${this.config.api_key}`,
200
+ },
201
+ }, 1); // Only 1 retry for polling (runs every 2s anyway)
202
+ if (!res.ok)
203
+ return;
204
+ const data = await res.json();
205
+ if (!data.success || !data.messages?.length)
206
+ return;
207
+ for (const msg of data.messages) {
208
+ const outMsg = {
209
+ id: msg.id,
210
+ content: msg.content || "",
211
+ sender_id: msg.sender_id || "agent",
212
+ conversation_id: msg.conversation_id,
213
+ metadata: msg.metadata,
214
+ };
215
+ const delivered = await adapter.sendMessage(outMsg);
216
+ if (delivered) {
217
+ await this.markDelivered(channelId, msg.id);
218
+ console.log(`[poll] Delivered queued message ${msg.id}`);
219
+ }
220
+ }
221
+ }
222
+ catch {
223
+ // Silent — polling errors are expected during connectivity blips
224
+ }
225
+ }
226
+ /** Relay inbound and deliver response synchronously with retry */
227
+ async relayAndDeliver(channelId, msg) {
228
+ try {
229
+ const res = await this.fetchWithRetry(`${this.config.server_url}/channels/${channelId}/messages`, {
230
+ method: "POST",
231
+ headers: {
232
+ "Content-Type": "application/json",
233
+ Authorization: `Bearer ${this.config.api_key}`,
234
+ },
235
+ body: JSON.stringify({
236
+ direction: "inbound",
237
+ ...msg,
238
+ }),
239
+ });
240
+ const data = await res.json();
241
+ this.messagesRelayed++;
242
+ if (data.success && data.agent_response) {
243
+ const adapter = this.adapters.get(channelId);
244
+ if (adapter) {
245
+ const outMsg = {
246
+ id: data.agent_response.id,
247
+ content: data.agent_response.content,
248
+ sender_id: "agent",
249
+ conversation_id: data.conversation_id || data.agent_response.conversation_id,
250
+ metadata: msg.metadata,
251
+ };
252
+ const delivered = await adapter.sendMessage(outMsg);
253
+ if (delivered) {
254
+ await this.markDelivered(channelId, data.agent_response.id);
255
+ }
256
+ }
257
+ }
258
+ }
259
+ catch (err) {
260
+ this.messagesFailed++;
261
+ console.error(`[relay] Failed after retries:`, err.message);
262
+ captureError({
263
+ error: err instanceof Error ? err : new Error(String(err)),
264
+ severity: "error",
265
+ tags: { source: "whale-node", operation: "relay", node_id: this.config.node_id, channel_id: channelId },
266
+ });
267
+ }
268
+ }
269
+ async markDelivered(channelId, messageId) {
270
+ try {
271
+ await this.fetchWithRetry(`${this.config.server_url}/channels/${channelId}/messages/${messageId}/delivered`, {
272
+ method: "POST",
273
+ headers: {
274
+ "Content-Type": "application/json",
275
+ Authorization: `Bearer ${this.config.api_key}`,
276
+ },
277
+ }, 1);
278
+ }
279
+ catch {
280
+ // Best effort — message was already delivered to user
281
+ }
282
+ }
283
+ async sendHeartbeat() {
284
+ try {
285
+ const channelStatuses = Array.from(this.adapters.entries()).map(([, adapter]) => ({
286
+ type: adapter.type,
287
+ name: adapter.name,
288
+ status: adapter.isRunning() ? "active" : "error",
289
+ stats: adapter.getStats(),
290
+ }));
291
+ const res = await this.fetchWithRetry(`${this.config.server_url}/nodes/heartbeat`, {
292
+ method: "POST",
293
+ headers: {
294
+ "Content-Type": "application/json",
295
+ Authorization: `Bearer ${this.config.api_key}`,
296
+ },
297
+ body: JSON.stringify({
298
+ hardware: getHardwareInfo(),
299
+ capabilities: getCapabilities(),
300
+ version: VERSION,
301
+ channels: channelStatuses,
302
+ runtime_stats: {
303
+ uptime_seconds: this.startedAt ? Math.floor((Date.now() - this.startedAt.getTime()) / 1000) : 0,
304
+ messages_relayed: this.messagesRelayed,
305
+ messages_failed: this.messagesFailed,
306
+ status: this.status,
307
+ },
308
+ }),
309
+ }, 2);
310
+ if (res.ok) {
311
+ this.heartbeatsSent++;
312
+ return true;
313
+ }
314
+ console.warn(`[heartbeat] Server returned ${res.status}`);
315
+ this.heartbeatsFailed++;
316
+ return false;
317
+ }
318
+ catch (err) {
319
+ console.warn(`[heartbeat] Failed:`, err.message);
320
+ this.heartbeatsFailed++;
321
+ // Only capture after multiple consecutive failures to avoid noise
322
+ if (this.consecutiveServerFailures >= 3) {
323
+ captureError({
324
+ error: err instanceof Error ? err : new Error(String(err)),
325
+ severity: "warning",
326
+ tags: { source: "whale-node", operation: "heartbeat", node_id: this.config.node_id, failures: String(this.consecutiveServerFailures) },
327
+ });
328
+ }
329
+ return false;
330
+ }
331
+ }
332
+ }
333
+ function sleep(ms) {
334
+ return new Promise(resolve => setTimeout(resolve, ms));
335
+ }
336
+ function getHardwareInfo() {
337
+ return {
338
+ os: process.platform === "darwin" ? "macOS" : process.platform,
339
+ arch: process.arch,
340
+ cpu: cpus()[0]?.model || "unknown",
341
+ cores: cpus().length,
342
+ ram_gb: Math.round(totalmem() / (1024 ** 3)),
343
+ hostname: hostname(),
344
+ };
345
+ }
346
+ function getCapabilities() {
347
+ const caps = ["messaging", "discord", "slack", "telegram", "webchat", "whatsapp", "sms", "email"];
348
+ if (process.platform === "darwin")
349
+ caps.push("imessage");
350
+ return caps;
351
+ }
@@ -0,0 +1,11 @@
1
+ /** Pre-populate a table with rows for the current test */
2
+ export declare function setTableData(table: string, rows: unknown[]): void;
3
+ /** Pre-configure the return value for an RPC call */
4
+ export declare function setRpcResponse(name: string, data: unknown, error?: unknown): void;
5
+ /** Force a table to return an error on any operation */
6
+ export declare function setTableError(table: string, message: string, code?: string): void;
7
+ /** Reset all in-memory state between tests */
8
+ export declare function resetMock(): void;
9
+ /** Get current in-memory rows for a table (for post-operation assertions) */
10
+ export declare function getTableData(table: string): unknown[];
11
+ export declare function createMockSupabase(): unknown;