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,288 @@
1
+ // server/handlers/discovery.ts — mDNS/DNS-SD local network device discovery
2
+ // Scan for POS terminals, printers, and other services on the local network.
3
+ // Uses bonjour-service (Zeroconf/mDNS implementation in TypeScript).
4
+ import Bonjour from "bonjour-service";
5
+ // ============================================================================
6
+ // MODULE-LEVEL STATE
7
+ // ============================================================================
8
+ /** Single Bonjour instance shared across advertise operations */
9
+ let bonjourInstance = null;
10
+ /** Map of advertised service name -> Service instance for stop/list */
11
+ const advertisedServices = new Map();
12
+ function getBonjour() {
13
+ if (!bonjourInstance) {
14
+ bonjourInstance = new Bonjour();
15
+ }
16
+ return bonjourInstance;
17
+ }
18
+ /** Common service types to scan when doing a broad discovery */
19
+ const COMMON_SERVICE_TYPES = [
20
+ "http",
21
+ "https",
22
+ "printer",
23
+ "ipp",
24
+ "pos",
25
+ "airplay",
26
+ "raop",
27
+ "smb",
28
+ "ftp",
29
+ "ssh",
30
+ "mqtt",
31
+ ];
32
+ /**
33
+ * Scan the local network for mDNS services.
34
+ * Creates a short-lived Bonjour instance per scan to avoid memory leaks.
35
+ */
36
+ async function scanServices(serviceType, timeoutMs) {
37
+ return new Promise((resolve, reject) => {
38
+ let bonjour;
39
+ try {
40
+ bonjour = new Bonjour();
41
+ }
42
+ catch (err) {
43
+ reject(new Error(`mDNS not available on this host. This typically happens in Docker/cloud environments without multicast support. (${err instanceof Error ? err.message : String(err)})`));
44
+ return;
45
+ }
46
+ const services = [];
47
+ const seen = new Set();
48
+ // Strip leading underscore and trailing protocol if user passed full form
49
+ // e.g. "_http._tcp" -> "http"
50
+ let type = serviceType;
51
+ if (type.startsWith("_"))
52
+ type = type.slice(1);
53
+ if (type.endsWith("._tcp"))
54
+ type = type.slice(0, -5);
55
+ if (type.endsWith("._udp"))
56
+ type = type.slice(0, -5);
57
+ let browser;
58
+ try {
59
+ browser = bonjour.find({ type });
60
+ }
61
+ catch (err) {
62
+ bonjour.destroy();
63
+ reject(new Error(`Failed to start mDNS browser: ${err instanceof Error ? err.message : String(err)}`));
64
+ return;
65
+ }
66
+ browser.on("up", (service) => {
67
+ const key = `${service.name}::${service.type}::${service.port}`;
68
+ if (!seen.has(key)) {
69
+ seen.add(key);
70
+ services.push({
71
+ name: service.name,
72
+ type: service.type,
73
+ host: service.host,
74
+ port: service.port,
75
+ addresses: service.addresses,
76
+ txt: service.txt,
77
+ });
78
+ }
79
+ });
80
+ const timer = setTimeout(() => {
81
+ try {
82
+ browser.stop();
83
+ bonjour.destroy();
84
+ }
85
+ catch {
86
+ // Ignore cleanup errors
87
+ }
88
+ resolve(services);
89
+ }, timeoutMs);
90
+ // Safety: if the browser errors out, resolve with whatever we have
91
+ browser.on("error", () => {
92
+ clearTimeout(timer);
93
+ try {
94
+ browser.stop();
95
+ bonjour.destroy();
96
+ }
97
+ catch {
98
+ // Ignore cleanup errors
99
+ }
100
+ resolve(services);
101
+ });
102
+ });
103
+ }
104
+ /**
105
+ * Multi-type scan: run parallel scans for common service types.
106
+ */
107
+ async function scanAllCommonTypes(timeoutMs) {
108
+ const results = await Promise.allSettled(COMMON_SERVICE_TYPES.map((type) => scanServices(type, timeoutMs)));
109
+ const allServices = [];
110
+ const seen = new Set();
111
+ for (const result of results) {
112
+ if (result.status === "fulfilled") {
113
+ for (const svc of result.value) {
114
+ const key = `${svc.name}::${svc.type}::${svc.port}`;
115
+ if (!seen.has(key)) {
116
+ seen.add(key);
117
+ allServices.push(svc);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ return allServices;
123
+ }
124
+ // ============================================================================
125
+ // ADVERTISE
126
+ // ============================================================================
127
+ function advertiseService(name, type, port, txt) {
128
+ if (advertisedServices.has(name)) {
129
+ return {
130
+ success: false,
131
+ error: `Service "${name}" is already being advertised. Stop it first with stop_advertise.`,
132
+ };
133
+ }
134
+ // Strip protocol suffix from type for bonjour-service
135
+ let cleanType = type;
136
+ if (cleanType.startsWith("_"))
137
+ cleanType = cleanType.slice(1);
138
+ if (cleanType.endsWith("._tcp"))
139
+ cleanType = cleanType.slice(0, -5);
140
+ if (cleanType.endsWith("._udp"))
141
+ cleanType = cleanType.slice(0, -5);
142
+ try {
143
+ const bonjour = getBonjour();
144
+ const service = bonjour.publish({
145
+ name,
146
+ type: cleanType,
147
+ port,
148
+ txt: txt,
149
+ });
150
+ advertisedServices.set(name, service);
151
+ return { success: true };
152
+ }
153
+ catch (err) {
154
+ return {
155
+ success: false,
156
+ error: `Failed to advertise service: ${err instanceof Error ? err.message : String(err)}`,
157
+ };
158
+ }
159
+ }
160
+ // ============================================================================
161
+ // STOP ADVERTISE
162
+ // ============================================================================
163
+ function stopAdvertise(name) {
164
+ const service = advertisedServices.get(name);
165
+ if (!service) {
166
+ return {
167
+ success: false,
168
+ error: `No advertised service found with name "${name}". Currently advertised: ${advertisedServices.size === 0 ? "none" : Array.from(advertisedServices.keys()).join(", ")}`,
169
+ };
170
+ }
171
+ try {
172
+ if (service.stop) {
173
+ service.stop();
174
+ }
175
+ // The service also has a 'destroyed' flag — set externally isn't needed
176
+ // since stop() handles cleanup in bonjour-service
177
+ }
178
+ catch {
179
+ // Best-effort cleanup
180
+ }
181
+ advertisedServices.delete(name);
182
+ return { success: true };
183
+ }
184
+ // ============================================================================
185
+ // LIST ADVERTISED
186
+ // ============================================================================
187
+ function listAdvertised() {
188
+ const list = Array.from(advertisedServices.entries()).map(([name, svc]) => ({
189
+ name,
190
+ type: svc.type,
191
+ port: svc.port,
192
+ host: svc.host,
193
+ txt: svc.txt,
194
+ }));
195
+ return { success: true, data: list };
196
+ }
197
+ // ============================================================================
198
+ // MAIN HANDLER
199
+ // ============================================================================
200
+ export async function handleDiscovery(_sb, args, _storeId) {
201
+ const action = args.action;
202
+ switch (action) {
203
+ case "scan": {
204
+ const serviceType = args.service_type || "";
205
+ const timeoutMs = Math.min(Math.max(args.timeout_ms || 5000, 1000), 30000); // clamp 1-30s
206
+ try {
207
+ let services;
208
+ if (!serviceType || serviceType === "all") {
209
+ services = await scanAllCommonTypes(timeoutMs);
210
+ }
211
+ else {
212
+ services = await scanServices(serviceType, timeoutMs);
213
+ }
214
+ return {
215
+ success: true,
216
+ data: {
217
+ service_type: serviceType || "all (common types)",
218
+ timeout_ms: timeoutMs,
219
+ count: services.length,
220
+ services,
221
+ },
222
+ };
223
+ }
224
+ catch (err) {
225
+ return {
226
+ success: false,
227
+ error: err instanceof Error
228
+ ? err.message
229
+ : `Scan failed: ${String(err)}`,
230
+ };
231
+ }
232
+ }
233
+ case "advertise": {
234
+ const name = args.name;
235
+ const type = args.type;
236
+ const port = args.port;
237
+ const txt = args.txt;
238
+ if (!name)
239
+ return { success: false, error: "name is required for advertise" };
240
+ if (!type)
241
+ return { success: false, error: "type is required for advertise (e.g. _http._tcp)" };
242
+ if (!port || typeof port !== "number")
243
+ return { success: false, error: "port is required for advertise (must be a number)" };
244
+ const result = advertiseService(name, type, port, txt);
245
+ if (result.success) {
246
+ return {
247
+ success: true,
248
+ data: {
249
+ message: `Service "${name}" is now being advertised as ${type} on port ${port}`,
250
+ name,
251
+ type,
252
+ port,
253
+ txt: txt || null,
254
+ },
255
+ };
256
+ }
257
+ return result;
258
+ }
259
+ case "stop_advertise": {
260
+ const name = args.name;
261
+ if (!name)
262
+ return { success: false, error: "name is required for stop_advertise" };
263
+ const result = stopAdvertise(name);
264
+ if (result.success) {
265
+ return {
266
+ success: true,
267
+ data: { message: `Stopped advertising service "${name}"` },
268
+ };
269
+ }
270
+ return result;
271
+ }
272
+ case "list_advertised": {
273
+ const result = listAdvertised();
274
+ return {
275
+ success: true,
276
+ data: {
277
+ count: result.data.length,
278
+ services: result.data,
279
+ },
280
+ };
281
+ }
282
+ default:
283
+ return {
284
+ success: false,
285
+ error: `Unknown discovery action: "${action}". Valid actions: scan, advertise, stop_advertise, list_advertised`,
286
+ };
287
+ }
288
+ }
@@ -0,0 +1,92 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ export declare function handleEmbeddings(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
3
+ success: boolean;
4
+ error: string;
5
+ data?: undefined;
6
+ count?: undefined;
7
+ } | {
8
+ success: boolean;
9
+ data: {
10
+ id: any;
11
+ source_type: any;
12
+ source_id: any;
13
+ created_at: any;
14
+ };
15
+ error?: undefined;
16
+ count?: undefined;
17
+ } | {
18
+ success: boolean;
19
+ count: any;
20
+ data: any;
21
+ error?: undefined;
22
+ } | {
23
+ success: boolean;
24
+ data: {
25
+ indexed: number;
26
+ message: string;
27
+ total_products?: undefined;
28
+ deleted?: undefined;
29
+ source_type?: undefined;
30
+ source_id?: undefined;
31
+ total?: undefined;
32
+ by_source_type?: undefined;
33
+ };
34
+ error?: undefined;
35
+ count?: undefined;
36
+ } | {
37
+ success: boolean;
38
+ error: string;
39
+ data: {
40
+ indexed: number;
41
+ message?: undefined;
42
+ total_products?: undefined;
43
+ deleted?: undefined;
44
+ source_type?: undefined;
45
+ source_id?: undefined;
46
+ total?: undefined;
47
+ by_source_type?: undefined;
48
+ };
49
+ count?: undefined;
50
+ } | {
51
+ success: boolean;
52
+ data: {
53
+ indexed: number;
54
+ total_products: number;
55
+ message?: undefined;
56
+ deleted?: undefined;
57
+ source_type?: undefined;
58
+ source_id?: undefined;
59
+ total?: undefined;
60
+ by_source_type?: undefined;
61
+ };
62
+ error?: undefined;
63
+ count?: undefined;
64
+ } | {
65
+ success: boolean;
66
+ data: {
67
+ deleted: boolean;
68
+ source_type: string | undefined;
69
+ source_id: string | undefined;
70
+ indexed?: undefined;
71
+ message?: undefined;
72
+ total_products?: undefined;
73
+ total?: undefined;
74
+ by_source_type?: undefined;
75
+ };
76
+ error?: undefined;
77
+ count?: undefined;
78
+ } | {
79
+ success: boolean;
80
+ data: {
81
+ total: number;
82
+ by_source_type: Record<string, number>;
83
+ indexed?: undefined;
84
+ message?: undefined;
85
+ total_products?: undefined;
86
+ deleted?: undefined;
87
+ source_type?: undefined;
88
+ source_id?: undefined;
89
+ };
90
+ error?: undefined;
91
+ count?: undefined;
92
+ }>;
@@ -0,0 +1,197 @@
1
+ import OpenAI from "openai";
2
+ // ============================================================================
3
+ // EMBEDDINGS — Semantic vector search via OpenAI + pgvector
4
+ // Actions: embed, search, index_products, delete, stats
5
+ // ============================================================================
6
+ const EMBEDDING_MODEL = "text-embedding-3-small";
7
+ const MAX_CHARS = 32_000; // ~8000 tokens
8
+ const BATCH_SIZE = 100; // OpenAI max per request
9
+ /** Truncate text to stay within token limits */
10
+ function truncateText(text) {
11
+ return text.length > MAX_CHARS ? text.slice(0, MAX_CHARS) : text;
12
+ }
13
+ /** Get an OpenAI client by decrypting the stored API key */
14
+ async function getOpenAIClient(sb, storeId) {
15
+ const { data: apiKey, error } = await sb.rpc("decrypt_secret", {
16
+ p_name: "OPENAI_API_KEY",
17
+ p_store_id: storeId,
18
+ });
19
+ if (error || !apiKey) {
20
+ throw new Error("OpenAI API key not found. Add OPENAI_API_KEY to your tool secrets.");
21
+ }
22
+ return new OpenAI({ apiKey });
23
+ }
24
+ /** Generate embeddings for one or more texts (batched) */
25
+ async function generateEmbeddings(openai, texts) {
26
+ const results = [];
27
+ for (let i = 0; i < texts.length; i += BATCH_SIZE) {
28
+ const batch = texts.slice(i, i + BATCH_SIZE).map(truncateText);
29
+ const response = await openai.embeddings.create({
30
+ model: EMBEDDING_MODEL,
31
+ input: batch,
32
+ });
33
+ for (const item of response.data) {
34
+ results.push(item.embedding);
35
+ }
36
+ }
37
+ return results;
38
+ }
39
+ /** Build a text representation of a product for embedding */
40
+ function productToText(product) {
41
+ const parts = [];
42
+ if (product.name)
43
+ parts.push(String(product.name));
44
+ if (product.short_description)
45
+ parts.push(String(product.short_description));
46
+ if (product.description)
47
+ parts.push(String(product.description));
48
+ if (product.field_values && typeof product.field_values === "object") {
49
+ const fv = product.field_values;
50
+ for (const [key, val] of Object.entries(fv)) {
51
+ if (val !== null && val !== undefined && val !== "") {
52
+ parts.push(`${key}: ${String(val)}`);
53
+ }
54
+ }
55
+ }
56
+ return parts.join(". ");
57
+ }
58
+ // ============================================================================
59
+ // MAIN HANDLER
60
+ // ============================================================================
61
+ export async function handleEmbeddings(sb, args, storeId) {
62
+ const sid = storeId;
63
+ const action = args.action;
64
+ switch (action) {
65
+ // ======================== EMBED ========================
66
+ case "embed": {
67
+ const content = args.content;
68
+ if (!content)
69
+ return { success: false, error: "content is required for embed action" };
70
+ const openai = await getOpenAIClient(sb, sid);
71
+ const [embedding] = await generateEmbeddings(openai, [content]);
72
+ const sourceType = args.source_type || "text";
73
+ const sourceId = args.source_id;
74
+ const metadata = args.metadata || {};
75
+ // Upsert: delete existing embedding for same source_id if provided
76
+ if (sourceId) {
77
+ await sb.from("embeddings")
78
+ .delete()
79
+ .eq("store_id", sid)
80
+ .eq("source_type", sourceType)
81
+ .eq("source_id", sourceId);
82
+ }
83
+ const { data, error } = await sb.from("embeddings").insert({
84
+ store_id: sid,
85
+ content: truncateText(content),
86
+ embedding: JSON.stringify(embedding),
87
+ metadata,
88
+ source_type: sourceType,
89
+ source_id: sourceId || null,
90
+ }).select("id, source_type, source_id, created_at").single();
91
+ if (error)
92
+ return { success: false, error: error.message };
93
+ return { success: true, data };
94
+ }
95
+ // ======================== SEARCH ========================
96
+ case "search": {
97
+ const query = args.query;
98
+ if (!query)
99
+ return { success: false, error: "query is required for search action" };
100
+ const matchCount = args.match_count || 10;
101
+ const sourceType = args.source_type || null;
102
+ const threshold = args.similarity_threshold || 0.5;
103
+ const openai = await getOpenAIClient(sb, sid);
104
+ const [queryEmbedding] = await generateEmbeddings(openai, [query]);
105
+ const { data, error } = await sb.rpc("search_embeddings", {
106
+ p_store_id: sid,
107
+ p_query_embedding: JSON.stringify(queryEmbedding),
108
+ p_match_count: matchCount,
109
+ p_source_type: sourceType,
110
+ p_similarity_threshold: threshold,
111
+ });
112
+ if (error)
113
+ return { success: false, error: error.message };
114
+ return { success: true, count: data?.length ?? 0, data };
115
+ }
116
+ // ======================== INDEX PRODUCTS ========================
117
+ case "index_products": {
118
+ const { data: products, error: fetchErr } = await sb
119
+ .from("products")
120
+ .select("id, name, description, short_description, field_values")
121
+ .eq("store_id", sid);
122
+ if (fetchErr)
123
+ return { success: false, error: fetchErr.message };
124
+ if (!products || products.length === 0) {
125
+ return { success: true, data: { indexed: 0, message: "No products found to index." } };
126
+ }
127
+ const openai = await getOpenAIClient(sb, sid);
128
+ const texts = products.map(productToText);
129
+ const embeddings = await generateEmbeddings(openai, texts);
130
+ // Delete all existing product embeddings for this store
131
+ await sb.from("embeddings")
132
+ .delete()
133
+ .eq("store_id", sid)
134
+ .eq("source_type", "product");
135
+ // Insert in batches
136
+ let inserted = 0;
137
+ const rows = products.map((p, i) => ({
138
+ store_id: sid,
139
+ content: truncateText(texts[i]),
140
+ embedding: JSON.stringify(embeddings[i]),
141
+ metadata: { name: p.name },
142
+ source_type: "product",
143
+ source_id: p.id,
144
+ }));
145
+ for (let i = 0; i < rows.length; i += 500) {
146
+ const batch = rows.slice(i, i + 500);
147
+ const { error: insErr } = await sb.from("embeddings").insert(batch);
148
+ if (insErr)
149
+ return { success: false, error: insErr.message, data: { indexed: inserted } };
150
+ inserted += batch.length;
151
+ }
152
+ return {
153
+ success: true,
154
+ data: { indexed: inserted, total_products: products.length },
155
+ };
156
+ }
157
+ // ======================== DELETE ========================
158
+ case "delete": {
159
+ const sourceType = args.source_type;
160
+ const sourceId = args.source_id;
161
+ if (!sourceType && !sourceId) {
162
+ return { success: false, error: "Provide source_type and/or source_id to delete embeddings." };
163
+ }
164
+ let q = sb.from("embeddings").delete().eq("store_id", sid);
165
+ if (sourceType)
166
+ q = q.eq("source_type", sourceType);
167
+ if (sourceId)
168
+ q = q.eq("source_id", sourceId);
169
+ const { error } = await q;
170
+ if (error)
171
+ return { success: false, error: error.message };
172
+ return { success: true, data: { deleted: true, source_type: sourceType, source_id: sourceId } };
173
+ }
174
+ // ======================== STATS ========================
175
+ case "stats": {
176
+ const { data, error } = await sb
177
+ .from("embeddings")
178
+ .select("source_type")
179
+ .eq("store_id", sid);
180
+ if (error)
181
+ return { success: false, error: error.message };
182
+ const counts = {};
183
+ let total = 0;
184
+ for (const row of data || []) {
185
+ const st = row.source_type || "unknown";
186
+ counts[st] = (counts[st] || 0) + 1;
187
+ total++;
188
+ }
189
+ return { success: true, data: { total, by_source_type: counts } };
190
+ }
191
+ default:
192
+ return {
193
+ success: false,
194
+ error: `Unknown embeddings action: ${action}. Valid actions: embed, search, index_products, delete, stats`,
195
+ };
196
+ }
197
+ }
@@ -0,0 +1,8 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ export declare function handleEnrichment(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
3
+ success: boolean;
4
+ data?: unknown;
5
+ error?: string;
6
+ count?: number;
7
+ note?: string;
8
+ }>;