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,327 @@
1
+ // ============================================================================
2
+ // UNIFIED RPC FUNCTIONS — same functions used by Swift app
3
+ // PO: create_purchase_order_atomic, update_po_status, receive_po_items
4
+ // Transfer: create_inventory_transfer, approve_inventory_transfer,
5
+ // mark_transfer_in_transit, complete_inventory_transfer,
6
+ // cancel_inventory_transfer
7
+ // ============================================================================
8
+ export async function handlePurchaseOrders(sb, args, storeId) {
9
+ const sid = storeId;
10
+ switch (args.action) {
11
+ case "list": {
12
+ let q = sb.from("purchase_orders").select("*, supplier:suppliers(external_name, external_company), location:locations(name)")
13
+ .eq("store_id", sid).order("created_at", { ascending: false }).limit(args.limit || 25);
14
+ if (args.status)
15
+ q = q.eq("status", args.status);
16
+ const { data, error } = await q;
17
+ if (error)
18
+ return { success: false, error: error.message };
19
+ // Flatten supplier/location joins for table display
20
+ const flattened = (data || []).map((row) => {
21
+ const { supplier, location, ...rest } = row;
22
+ return { ...rest, supplier_name: supplier?.external_name || supplier?.external_company || "—", location_name: location?.name || "—" };
23
+ });
24
+ return { success: true, data: flattened };
25
+ }
26
+ case "get": {
27
+ const { data, error } = await sb.from("purchase_orders")
28
+ .select("*, items:purchase_order_items(id, product_id, quantity, unit_price, received_quantity, subtotal, product:products(name, sku)), supplier:suppliers(id, external_name, external_company, contact_email, contact_phone)")
29
+ .eq("id", args.purchase_order_id).eq("store_id", sid).single();
30
+ if (error)
31
+ return { success: false, error: error.message };
32
+ // Flatten items so product names appear as columns (formatter drops nested objects in sub-tables)
33
+ const po = data;
34
+ const items = (po.items || []).map((item) => ({
35
+ id: item.id, product_id: item.product_id,
36
+ product_name: item.product?.name || "—", product_sku: item.product?.sku || "—",
37
+ quantity: item.quantity, unit_price: item.unit_price,
38
+ received: item.received_quantity || 0, subtotal: item.subtotal,
39
+ }));
40
+ const { items: _, supplier, ...poFields } = po;
41
+ return {
42
+ success: true,
43
+ data: {
44
+ ...poFields,
45
+ supplier_name: supplier?.external_name || supplier?.external_company || "—",
46
+ supplier_email: supplier?.contact_email || null,
47
+ items,
48
+ }
49
+ };
50
+ }
51
+ case "create": {
52
+ // create_purchase_order_atomic RPC — SECURITY DEFINER, returns JSONB {success, po_id, po_number}
53
+ // p_items is TEXT type (JSON string), not JSONB
54
+ const items = (args.items || []).map(item => ({
55
+ product_id: item.product_id,
56
+ quantity: Math.max(Number(item.quantity) || 1, 0),
57
+ unit_cost: Math.max(Number(item.unit_cost || item.unit_price) || 0, 0),
58
+ }));
59
+ const { data: result, error: rpcError } = await sb.rpc("create_purchase_order_atomic", {
60
+ p_store_id: sid,
61
+ p_po_type: "inbound",
62
+ p_supplier_id: args.supplier_id || null,
63
+ p_location_id: args.location_id || null,
64
+ p_expected_delivery_date: args.expected_delivery_date || null,
65
+ p_notes: args.notes || null,
66
+ p_items: JSON.stringify(items),
67
+ });
68
+ if (rpcError)
69
+ return { success: false, error: rpcError.message };
70
+ const rpcResult = parseRpcResult(result);
71
+ if (!rpcResult.success)
72
+ return { success: false, error: rpcResult.error || "PO creation failed" };
73
+ // Fetch full PO for response — flatten nested joins
74
+ const { data: fullPo } = await sb.from("purchase_orders")
75
+ .select("*, items:purchase_order_items(id, product_id, quantity, unit_price, subtotal, product:products(name, sku)), supplier:suppliers(id, external_name, external_company)")
76
+ .eq("id", rpcResult.po_id).single();
77
+ if (!fullPo)
78
+ return { success: true, data: { po_id: rpcResult.po_id, po_number: rpcResult.po_number } };
79
+ const po = fullPo;
80
+ const flatItems = (po.items || []).map((item) => ({
81
+ id: item.id, product_id: item.product_id,
82
+ product_name: item.product?.name || "—", product_sku: item.product?.sku || "—",
83
+ quantity: item.quantity, unit_price: item.unit_price, subtotal: item.subtotal,
84
+ }));
85
+ const { items: _poItems, supplier, ...poFields } = po;
86
+ return {
87
+ success: true,
88
+ data: {
89
+ ...poFields,
90
+ supplier_name: supplier?.external_name || supplier?.external_company || "—",
91
+ items: flatItems,
92
+ }
93
+ };
94
+ }
95
+ case "add_items": {
96
+ // No atomic RPC exists for adding items to an existing PO — direct insert
97
+ const poId = args.purchase_order_id;
98
+ if (!poId)
99
+ return { success: false, error: "purchase_order_id is required" };
100
+ const items = args.items;
101
+ if (!items || !Array.isArray(items) || !items.length)
102
+ return { success: false, error: "items array is required" };
103
+ const { data: po } = await sb.from("purchase_orders").select("status").eq("id", poId).eq("store_id", sid).single();
104
+ if (!po)
105
+ return { success: false, error: "PO not found" };
106
+ if (po.status === "received")
107
+ return { success: false, error: "Cannot add items — PO already received" };
108
+ if (po.status === "cancelled")
109
+ return { success: false, error: "Cannot add items — PO is cancelled" };
110
+ const rows = items.map(item => {
111
+ const qty = Math.max(Number(item.quantity) || 1, 0);
112
+ const price = Math.max(Number(item.unit_cost || item.unit_price) || 0, 0);
113
+ return { purchase_order_id: poId, product_id: item.product_id, quantity: qty, unit_price: price, subtotal: qty * price };
114
+ });
115
+ const { data, error } = await sb.from("purchase_order_items").insert(rows).select("*, product:products(name, sku)");
116
+ return error ? { success: false, error: error.message } : { success: true, data };
117
+ }
118
+ case "approve":
119
+ case "mark_ordered": {
120
+ // PO flow: draft → ordered. "approve" = mark as ordered.
121
+ // update_po_status RPC — SECURITY DEFINER, returns JSONB {success, old_status, new_status}
122
+ const { data: result, error: rpcError } = await sb.rpc("update_po_status", {
123
+ p_po_id: args.purchase_order_id,
124
+ p_status: "ordered",
125
+ });
126
+ if (rpcError)
127
+ return { success: false, error: rpcError.message };
128
+ const rpcResult = parseRpcResult(result);
129
+ if (!rpcResult.success)
130
+ return { success: false, error: rpcResult.error || "Status update failed" };
131
+ return { success: true, data: { old_status: rpcResult.old_status, new_status: rpcResult.new_status } };
132
+ }
133
+ case "receive": {
134
+ // receive_po_items RPC — SECURITY DEFINER, returns JSONB {success, items_processed}
135
+ // Params: p_po_id, p_location_id, p_items [{item_id, quantity}]
136
+ const poId = args.purchase_order_id;
137
+ const { data: po, error: poErr } = await sb.from("purchase_orders")
138
+ .select("location_id, items:purchase_order_items(id, quantity, received_quantity)")
139
+ .eq("id", poId).eq("store_id", sid).single();
140
+ if (poErr || !po)
141
+ return { success: false, error: poErr?.message || "PO not found" };
142
+ // If caller provides specific items use those, otherwise receive all remaining
143
+ const receiveItems = args.items
144
+ ? args.items
145
+ : (po.items || [])
146
+ .map((item) => ({
147
+ item_id: item.id,
148
+ quantity: Math.max((item.quantity || 0) - (item.received_quantity || 0), 0),
149
+ }))
150
+ .filter((item) => item.quantity > 0);
151
+ if (!receiveItems.length)
152
+ return { success: false, error: "No items to receive (all fully received)" };
153
+ const locationId = args.location_id || po.location_id;
154
+ if (!locationId)
155
+ return { success: false, error: "No location_id on PO — cannot receive" };
156
+ const { data: result, error: rpcError } = await sb.rpc("receive_po_items", {
157
+ p_po_id: poId,
158
+ p_location_id: locationId,
159
+ p_items: receiveItems,
160
+ });
161
+ if (rpcError)
162
+ return { success: false, error: rpcError.message };
163
+ const rpcResult = parseRpcResult(result);
164
+ if (!rpcResult.success)
165
+ return { success: false, error: rpcResult.error || "PO receive failed" };
166
+ return {
167
+ success: true,
168
+ data: {
169
+ po_id: poId,
170
+ items_processed: rpcResult.items_processed,
171
+ message: `Received ${rpcResult.items_processed || 0} item(s) into inventory`,
172
+ }
173
+ };
174
+ }
175
+ case "cancel": {
176
+ // update_po_status RPC — SECURITY DEFINER, returns JSONB {success, old_status, new_status}
177
+ const { data: result, error: rpcError } = await sb.rpc("update_po_status", {
178
+ p_po_id: args.purchase_order_id,
179
+ p_status: "cancelled",
180
+ });
181
+ if (rpcError)
182
+ return { success: false, error: rpcError.message };
183
+ const rpcResult = parseRpcResult(result);
184
+ if (!rpcResult.success)
185
+ return { success: false, error: rpcResult.error || "Cancel failed" };
186
+ return { success: true, data: { old_status: rpcResult.old_status, new_status: rpcResult.new_status } };
187
+ }
188
+ default:
189
+ return { success: false, error: `Unknown purchase_orders action: ${args.action}. Valid: list, get, create, add_items, approve, mark_ordered, receive, cancel` };
190
+ }
191
+ }
192
+ export async function handleTransfers(sb, args, storeId) {
193
+ const sid = storeId;
194
+ switch (args.action) {
195
+ case "list": {
196
+ let q = sb.from("inventory_transfers")
197
+ .select("*, from_location:locations!source_location_id(name), to_location:locations!destination_location_id(name)")
198
+ .eq("store_id", sid).order("created_at", { ascending: false }).limit(args.limit || 25);
199
+ if (args.status)
200
+ q = q.eq("status", args.status);
201
+ const { data, error } = await q;
202
+ if (error)
203
+ return { success: false, error: error.message };
204
+ // Flatten location joins for table display
205
+ const flattened = (data || []).map((row) => {
206
+ const { from_location, to_location, ...rest } = row;
207
+ return { ...rest, from_location: from_location?.name || "—", to_location: to_location?.name || "—" };
208
+ });
209
+ return { success: true, data: flattened };
210
+ }
211
+ case "create": {
212
+ // create_inventory_transfer RPC — SECURITY DEFINER, returns JSONB {success, transfer_id, transfer_number}
213
+ // p_items is JSONB (pass as object, not string)
214
+ const sourceLocId = (args.from_location_id || args.source_location_id);
215
+ const destLocId = (args.to_location_id || args.destination_location_id);
216
+ const items = (args.items || []).map(i => ({
217
+ product_id: i.product_id,
218
+ quantity: i.quantity,
219
+ }));
220
+ const params = {
221
+ p_store_id: sid,
222
+ p_source_location_id: sourceLocId,
223
+ p_destination_location_id: destLocId,
224
+ p_items: items,
225
+ };
226
+ if (args.notes)
227
+ params.p_notes = args.notes;
228
+ const { data: result, error: rpcError } = await sb.rpc("create_inventory_transfer", params);
229
+ if (rpcError)
230
+ return { success: false, error: rpcError.message };
231
+ const rpcResult = parseRpcResult(result);
232
+ if (!rpcResult.success)
233
+ return { success: false, error: rpcResult.error || "Transfer creation failed" };
234
+ return { success: true, data: { transfer_id: rpcResult.transfer_id, transfer_number: rpcResult.transfer_number } };
235
+ }
236
+ case "get": {
237
+ const { data, error } = await sb.from("inventory_transfers")
238
+ .select("*, items:inventory_transfer_items(id, product_id, quantity, product:products(name, sku)), from_location:locations!source_location_id(id, name), to_location:locations!destination_location_id(id, name)")
239
+ .eq("id", args.transfer_id).eq("store_id", sid).single();
240
+ if (error)
241
+ return { success: false, error: error.message };
242
+ // Flatten nested joins so items and locations are visible
243
+ const transfer = data;
244
+ const items = (transfer.items || []).map((item) => ({
245
+ id: item.id, product_id: item.product_id,
246
+ product_name: item.product?.name || "—", product_sku: item.product?.sku || "—",
247
+ quantity: item.quantity,
248
+ }));
249
+ const { items: _, from_location, to_location, ...transferFields } = transfer;
250
+ return {
251
+ success: true,
252
+ data: {
253
+ ...transferFields,
254
+ from_location_name: from_location?.name || "—",
255
+ to_location_name: to_location?.name || "—",
256
+ items,
257
+ }
258
+ };
259
+ }
260
+ case "approve": {
261
+ // approve_inventory_transfer RPC — SECURITY DEFINER, returns boolean
262
+ // Validates stock, draft → approved
263
+ const { data: result, error: rpcError } = await sb.rpc("approve_inventory_transfer", {
264
+ p_transfer_id: args.transfer_id,
265
+ });
266
+ if (rpcError)
267
+ return { success: false, error: rpcError.message };
268
+ return { success: true, data: { approved: result } };
269
+ }
270
+ case "ship":
271
+ case "mark_in_transit": {
272
+ // mark_transfer_in_transit RPC — SECURITY DEFINER, returns boolean
273
+ // Validates stock, creates holds, draft/approved → in_transit
274
+ const { data: result, error: rpcError } = await sb.rpc("mark_transfer_in_transit", {
275
+ p_transfer_id: args.transfer_id,
276
+ });
277
+ if (rpcError)
278
+ return { success: false, error: rpcError.message };
279
+ return { success: true, data: { in_transit: result } };
280
+ }
281
+ case "receive":
282
+ case "complete": {
283
+ // complete_inventory_transfer RPC — SECURITY DEFINER, returns JSONB {success, items_processed}
284
+ // Deducts source, adds to destination, releases holds
285
+ const { data: result, error: rpcError } = await sb.rpc("complete_inventory_transfer", {
286
+ p_transfer_id: args.transfer_id,
287
+ });
288
+ if (rpcError)
289
+ return { success: false, error: rpcError.message };
290
+ const rpcResult = parseRpcResult(result);
291
+ if (!rpcResult.success)
292
+ return { success: false, error: rpcResult.error || "Transfer complete failed" };
293
+ return {
294
+ success: true,
295
+ data: {
296
+ transfer_id: args.transfer_id,
297
+ items_processed: rpcResult.items_processed,
298
+ message: `Completed transfer, ${rpcResult.items_processed || 0} item(s) moved to destination`,
299
+ }
300
+ };
301
+ }
302
+ case "cancel": {
303
+ // cancel_inventory_transfer RPC — SECURITY DEFINER, returns boolean
304
+ // Releases holds, any non-final → cancelled
305
+ const { data: result, error: rpcError } = await sb.rpc("cancel_inventory_transfer", {
306
+ p_transfer_id: args.transfer_id,
307
+ });
308
+ if (rpcError)
309
+ return { success: false, error: rpcError.message };
310
+ return { success: true, data: { cancelled: result, message: "Transfer cancelled" } };
311
+ }
312
+ default:
313
+ return { success: false, error: `Unknown transfers action: ${args.action}. Valid: list, get, create, approve, ship, mark_in_transit, receive, complete, cancel` };
314
+ }
315
+ }
316
+ /** Parse RPC result — handles both raw objects and stringified JSON */
317
+ function parseRpcResult(result) {
318
+ if (typeof result === "string") {
319
+ try {
320
+ return JSON.parse(result);
321
+ }
322
+ catch {
323
+ return { success: false, error: result };
324
+ }
325
+ }
326
+ return result || { success: false, error: "Empty RPC response" };
327
+ }
@@ -0,0 +1,17 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ export interface TranscribeResult {
3
+ success: boolean;
4
+ transcript?: string;
5
+ language?: string;
6
+ duration?: number;
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Transcribe audio using OpenAI Whisper API.
11
+ *
12
+ * @param sb - Supabase client (service role) for credential lookup
13
+ * @param storeId - Store ID to resolve OpenAI API key
14
+ * @param audioBase64 - Base64-encoded audio data
15
+ * @param mediaType - MIME type (e.g. "audio/mpeg", "audio/wav")
16
+ */
17
+ export declare function handleTranscribe(sb: SupabaseClient, storeId: string, audioBase64: string, mediaType: string): Promise<TranscribeResult>;
@@ -0,0 +1,121 @@
1
+ // server/handlers/transcription.ts — Audio transcription via OpenAI Whisper
2
+ const WHISPER_URL = "https://api.openai.com/v1/audio/transcriptions";
3
+ const WHISPER_MODEL = "whisper-1";
4
+ /** Map media types to file extensions for the Whisper API */
5
+ const MEDIA_TYPE_EXTENSIONS = {
6
+ "audio/mpeg": "mp3",
7
+ "audio/wav": "wav",
8
+ "audio/aiff": "aiff",
9
+ "audio/aac": "aac",
10
+ "audio/ogg": "ogg",
11
+ "audio/flac": "flac",
12
+ "audio/mp4": "m4a",
13
+ "audio/webm": "webm",
14
+ };
15
+ /**
16
+ * Transcribe audio using OpenAI Whisper API.
17
+ *
18
+ * @param sb - Supabase client (service role) for credential lookup
19
+ * @param storeId - Store ID to resolve OpenAI API key
20
+ * @param audioBase64 - Base64-encoded audio data
21
+ * @param mediaType - MIME type (e.g. "audio/mpeg", "audio/wav")
22
+ */
23
+ export async function handleTranscribe(sb, storeId, audioBase64, mediaType) {
24
+ // Resolve OpenAI API key
25
+ let apiKey = null;
26
+ try {
27
+ const { data, error } = await sb.rpc("decrypt_secret", {
28
+ p_name: "OPENAI_API_KEY",
29
+ p_store_id: storeId,
30
+ });
31
+ if (!error && data)
32
+ apiKey = data;
33
+ }
34
+ catch {
35
+ // key not found
36
+ }
37
+ if (!apiKey) {
38
+ return {
39
+ success: false,
40
+ error: "OpenAI API key not configured. Add OPENAI_API_KEY to your tool secrets for audio transcription.",
41
+ };
42
+ }
43
+ // Decode base64 to buffer
44
+ const audioBuffer = Buffer.from(audioBase64, "base64");
45
+ // Determine file extension from media type
46
+ const ext = MEDIA_TYPE_EXTENSIONS[mediaType] || "mp3";
47
+ const filename = `audio.${ext}`;
48
+ // Build multipart form data for Whisper API
49
+ const blob = new Blob([audioBuffer], { type: mediaType });
50
+ const formData = new FormData();
51
+ formData.append("file", blob, filename);
52
+ formData.append("model", WHISPER_MODEL);
53
+ formData.append("response_format", "verbose_json");
54
+ try {
55
+ const response = await fetch(WHISPER_URL, {
56
+ method: "POST",
57
+ headers: {
58
+ Authorization: `Bearer ${apiKey}`,
59
+ },
60
+ body: formData,
61
+ });
62
+ if (!response.ok) {
63
+ const errText = await response.text();
64
+ return {
65
+ success: false,
66
+ error: `Whisper API error ${response.status}: ${errText}`,
67
+ };
68
+ }
69
+ const data = await response.json();
70
+ // Format transcript with timestamps if segments available
71
+ let transcript;
72
+ if (data.segments && data.segments.length > 0) {
73
+ const lines = data.segments.map((seg) => {
74
+ const start = formatTimestamp(seg.start);
75
+ return `[${start}] ${seg.text.trim()}`;
76
+ });
77
+ transcript = lines.join("\n");
78
+ }
79
+ else {
80
+ transcript = data.text || "";
81
+ }
82
+ // Add metadata header
83
+ const durationStr = data.duration ? formatDuration(data.duration) : "unknown";
84
+ const langStr = data.language || "unknown";
85
+ const header = `Audio transcript (${durationStr}, ${langStr})`;
86
+ return {
87
+ success: true,
88
+ transcript: `${header}\n\n${transcript}`,
89
+ language: data.language,
90
+ duration: data.duration,
91
+ };
92
+ }
93
+ catch (err) {
94
+ return {
95
+ success: false,
96
+ error: `Transcription failed: ${err.message}`,
97
+ };
98
+ }
99
+ }
100
+ /** Format seconds to MM:SS or HH:MM:SS */
101
+ function formatTimestamp(seconds) {
102
+ const h = Math.floor(seconds / 3600);
103
+ const m = Math.floor((seconds % 3600) / 60);
104
+ const s = Math.floor(seconds % 60);
105
+ if (h > 0) {
106
+ return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
107
+ }
108
+ return `${m}:${String(s).padStart(2, "0")}`;
109
+ }
110
+ /** Format total duration */
111
+ function formatDuration(seconds) {
112
+ if (seconds < 60)
113
+ return `${Math.round(seconds)}s`;
114
+ const m = Math.floor(seconds / 60);
115
+ const s = Math.round(seconds % 60);
116
+ if (m < 60)
117
+ return `${m}:${String(s).padStart(2, "0")}`;
118
+ const h = Math.floor(m / 60);
119
+ const rm = m % 60;
120
+ return `${h}:${String(rm).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
121
+ }
@@ -0,0 +1,6 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ export declare function handleVideoGen(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
3
+ success: boolean;
4
+ data?: unknown;
5
+ error?: string;
6
+ }>;