whale-code 6.4.0 → 6.5.1

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 (187) hide show
  1. package/bin/swagmanager-mcp.js +51 -0
  2. package/dist/cli/app.js +30 -2
  3. package/dist/cli/chat/ChatApp.d.ts +4 -4
  4. package/dist/cli/chat/ChatApp.js +114 -44
  5. package/dist/cli/chat/ChatInput.d.ts +13 -6
  6. package/dist/cli/chat/ChatInput.js +433 -89
  7. package/dist/cli/chat/MemoryManager.d.ts +15 -0
  8. package/dist/cli/chat/MemoryManager.js +61 -0
  9. package/dist/cli/chat/MessageList.d.ts +8 -0
  10. package/dist/cli/chat/MessageList.js +1 -1
  11. package/dist/cli/chat/NodeManager.d.ts +30 -0
  12. package/dist/cli/chat/NodeManager.js +89 -0
  13. package/dist/cli/chat/NodeSelector.d.ts +19 -0
  14. package/dist/cli/chat/NodeSelector.js +37 -0
  15. package/dist/cli/chat/PlanApproval.d.ts +17 -0
  16. package/dist/cli/chat/PlanApproval.js +82 -0
  17. package/dist/cli/chat/SessionManager.d.ts +16 -0
  18. package/dist/cli/chat/SessionManager.js +43 -0
  19. package/dist/cli/chat/SlashMenu.d.ts +38 -0
  20. package/dist/cli/chat/SlashMenu.js +208 -0
  21. package/dist/cli/chat/StatusBar.d.ts +16 -0
  22. package/dist/cli/chat/StatusBar.js +22 -0
  23. package/dist/cli/chat/ThemeSelector.d.ts +14 -0
  24. package/dist/cli/chat/ThemeSelector.js +29 -0
  25. package/dist/cli/chat/ToolIndicator.d.ts +8 -0
  26. package/dist/cli/chat/ToolIndicator.js +33 -9
  27. package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
  28. package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
  29. package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
  30. package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
  31. package/dist/cli/commands/config-cmd.js +4 -25
  32. package/dist/cli/commands/db.d.ts +13 -0
  33. package/dist/cli/commands/db.js +243 -0
  34. package/dist/cli/commands/doctor.js +6 -9
  35. package/dist/cli/commands/mcp.js +1 -20
  36. package/dist/cli/services/agent-events.d.ts +22 -1
  37. package/dist/cli/services/agent-events.js +9 -0
  38. package/dist/cli/services/agent-loop.js +65 -8
  39. package/dist/cli/services/agent-worker-base.js +21 -6
  40. package/dist/cli/services/api-retry.d.ts +25 -0
  41. package/dist/cli/services/api-retry.js +91 -0
  42. package/dist/cli/services/auth-service.d.ts +1 -1
  43. package/dist/cli/services/auth-service.js +40 -19
  44. package/dist/cli/services/background-processes.js +26 -2
  45. package/dist/cli/services/config-store.d.ts +13 -1
  46. package/dist/cli/services/config-store.js +116 -13
  47. package/dist/cli/services/format-server-response.js +12 -6
  48. package/dist/cli/services/ink-resize-fix.d.ts +18 -0
  49. package/dist/cli/services/ink-resize-fix.js +66 -0
  50. package/dist/cli/services/interactive-tools.d.ts +14 -0
  51. package/dist/cli/services/interactive-tools.js +47 -2
  52. package/dist/cli/services/keybinding-manager.js +1 -1
  53. package/dist/cli/services/local-tools.js +35 -2
  54. package/dist/cli/services/server-tools.js +175 -3
  55. package/dist/cli/services/subagent.js +7 -6
  56. package/dist/cli/services/system-prompt.js +5 -3
  57. package/dist/cli/services/task-decomposer.d.ts +35 -0
  58. package/dist/cli/services/task-decomposer.js +199 -0
  59. package/dist/cli/services/team-lead.d.ts +18 -0
  60. package/dist/cli/services/team-lead.js +80 -0
  61. package/dist/cli/services/teammate.js +5 -5
  62. package/dist/cli/services/telemetry.d.ts +8 -2
  63. package/dist/cli/services/telemetry.js +116 -92
  64. package/dist/cli/services/tools/agent-tools.d.ts +1 -0
  65. package/dist/cli/services/tools/agent-tools.js +50 -4
  66. package/dist/cli/services/tools/file-ops.d.ts +2 -0
  67. package/dist/cli/services/tools/file-ops.js +85 -19
  68. package/dist/cli/services/tools/shell-exec.js +22 -12
  69. package/dist/cli/shared/Theme.d.ts +1 -2
  70. package/dist/cli/shared/Theme.js +1 -1
  71. package/dist/cli/shared/WhaleBanner.d.ts +4 -1
  72. package/dist/cli/shared/WhaleBanner.js +12 -8
  73. package/dist/cli/shared/markdown.d.ts +5 -4
  74. package/dist/cli/shared/markdown.js +376 -334
  75. package/dist/cli/shared/theme-manager.d.ts +27 -0
  76. package/dist/cli/shared/theme-manager.js +178 -0
  77. package/dist/cli/shared/theme-presets.d.ts +16 -0
  78. package/dist/cli/shared/theme-presets.js +265 -0
  79. package/dist/index.js +0 -51
  80. package/dist/node/adapters/imessage.d.ts +10 -0
  81. package/dist/node/adapters/imessage.js +45 -6
  82. package/dist/node/cli.js +459 -8
  83. package/dist/node/config.d.ts +17 -0
  84. package/dist/node/gateway-client.d.ts +55 -0
  85. package/dist/node/gateway-client.js +201 -0
  86. package/dist/node/portal/clipboard.d.ts +28 -0
  87. package/dist/node/portal/clipboard.js +183 -0
  88. package/dist/node/portal/discovery.d.ts +29 -0
  89. package/dist/node/portal/discovery.js +61 -0
  90. package/dist/node/portal/forward.d.ts +30 -0
  91. package/dist/node/portal/forward.js +90 -0
  92. package/dist/node/portal/index.d.ts +47 -0
  93. package/dist/node/portal/index.js +250 -0
  94. package/dist/node/portal/multiplexer.d.ts +48 -0
  95. package/dist/node/portal/multiplexer.js +207 -0
  96. package/dist/node/portal/permissions.d.ts +36 -0
  97. package/dist/node/portal/permissions.js +131 -0
  98. package/dist/node/portal/protocol.d.ts +140 -0
  99. package/dist/node/portal/protocol.js +193 -0
  100. package/dist/node/portal/screen.d.ts +18 -0
  101. package/dist/node/portal/screen.js +93 -0
  102. package/dist/node/portal/session.d.ts +68 -0
  103. package/dist/node/portal/session.js +127 -0
  104. package/dist/node/portal/shell.d.ts +26 -0
  105. package/dist/node/portal/shell.js +142 -0
  106. package/dist/node/portal/stream.d.ts +43 -0
  107. package/dist/node/portal/stream.js +90 -0
  108. package/dist/node/portal/transfer.d.ts +33 -0
  109. package/dist/node/portal/transfer.js +231 -0
  110. package/dist/node/portal/ui.d.ts +16 -0
  111. package/dist/node/portal/ui.js +148 -0
  112. package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
  113. package/dist/node/remote-desktop/compile-helper.js +73 -0
  114. package/dist/node/remote-desktop/index.d.ts +67 -0
  115. package/dist/node/remote-desktop/index.js +220 -0
  116. package/dist/node/remote-desktop/protocol.d.ts +96 -0
  117. package/dist/node/remote-desktop/protocol.js +67 -0
  118. package/dist/node/runtime.d.ts +8 -1
  119. package/dist/node/runtime.js +117 -9
  120. package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
  121. package/dist/server/handlers/__test-utils__/test-db.js +128 -0
  122. package/dist/server/handlers/api-keys.js +26 -2
  123. package/dist/server/handlers/browser.d.ts +0 -4
  124. package/dist/server/handlers/browser.js +0 -46
  125. package/dist/server/handlers/catalog.js +37 -14
  126. package/dist/server/handlers/clickhouse.d.ts +10 -0
  127. package/dist/server/handlers/clickhouse.js +215 -0
  128. package/dist/server/handlers/comms.d.ts +308 -4
  129. package/dist/server/handlers/comms.js +444 -11
  130. package/dist/server/handlers/creations.js +1 -1
  131. package/dist/server/handlers/crm.d.ts +54 -8
  132. package/dist/server/handlers/crm.js +353 -68
  133. package/dist/server/handlers/embeddings.js +3 -3
  134. package/dist/server/handlers/enrichment.js +39 -55
  135. package/dist/server/handlers/inventory.js +1 -1
  136. package/dist/server/handlers/kali.d.ts +9 -1
  137. package/dist/server/handlers/kali.js +50 -1
  138. package/dist/server/handlers/media.d.ts +8 -0
  139. package/dist/server/handlers/media.js +902 -0
  140. package/dist/server/handlers/meta-ads.js +6 -3
  141. package/dist/server/handlers/nodes.d.ts +2 -0
  142. package/dist/server/handlers/nodes.js +331 -40
  143. package/dist/server/handlers/operations.d.ts +4 -6
  144. package/dist/server/handlers/operations.js +99 -38
  145. package/dist/server/handlers/platform.js +224 -107
  146. package/dist/server/handlers/remove-bg.d.ts +6 -0
  147. package/dist/server/handlers/remove-bg.js +96 -0
  148. package/dist/server/handlers/storefront.d.ts +6 -0
  149. package/dist/server/handlers/storefront.js +477 -0
  150. package/dist/server/handlers/supply-chain.js +21 -3
  151. package/dist/server/handlers/workflow-steps.js +87 -31
  152. package/dist/server/handlers/workflows.js +4 -1
  153. package/dist/server/index.js +334 -88
  154. package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
  155. package/dist/server/lib/clickhouse-buffer.js +175 -0
  156. package/dist/server/lib/clickhouse-client.d.ts +112 -0
  157. package/dist/server/lib/clickhouse-client.js +141 -0
  158. package/dist/server/lib/coa-renderer.d.ts +91 -0
  159. package/dist/server/lib/coa-renderer.js +411 -0
  160. package/dist/server/lib/compaction-service.js +46 -1
  161. package/dist/server/lib/pdf-renderer.d.ts +143 -0
  162. package/dist/server/lib/pdf-renderer.js +867 -0
  163. package/dist/server/lib/react-pdf-layout.d.ts +40 -0
  164. package/dist/server/lib/react-pdf-layout.js +437 -0
  165. package/dist/server/lib/server-agent-loop.d.ts +2 -0
  166. package/dist/server/lib/server-agent-loop.js +36 -17
  167. package/dist/server/lib/server-subagent.d.ts +3 -0
  168. package/dist/server/lib/server-subagent.js +9 -6
  169. package/dist/server/lib/supabase-client.js +51 -3
  170. package/dist/server/lib/template-resolver.js +14 -4
  171. package/dist/server/lib/utils.js +15 -0
  172. package/dist/server/local-agent-gateway.d.ts +44 -0
  173. package/dist/server/local-agent-gateway.js +389 -49
  174. package/dist/server/providers/anthropic.js +12 -2
  175. package/dist/server/providers/gemini.js +17 -2
  176. package/dist/server/proxy-handlers.js +151 -0
  177. package/dist/server/tool-router.d.ts +2 -2
  178. package/dist/server/tool-router.js +25 -35
  179. package/dist/shared/agent-core.d.ts +25 -2
  180. package/dist/shared/agent-core.js +66 -5
  181. package/dist/shared/api-client.js +54 -3
  182. package/dist/shared/sse-parser.d.ts +1 -1
  183. package/dist/shared/sse-parser.js +5 -2
  184. package/dist/shared/tool-dispatch.js +15 -1
  185. package/package.json +16 -10
  186. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
  187. package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ClickHouse Span Buffer — batches spans for bulk insert to ClickHouse ai_spans.
3
+ *
4
+ * Same batching semantics (500ms / 100 records).
5
+ * Also provides auditRowToSpan() to convert legacy row shapes to ClickHouseSpan.
6
+ */
7
+ import { type ClickHouseSpan } from "./clickhouse-client.js";
8
+ /**
9
+ * Queue a span for batch insert to ClickHouse.
10
+ */
11
+ export declare function queueSpan(span: ClickHouseSpan): void;
12
+ /**
13
+ * Flush all buffered spans to ClickHouse.
14
+ * Call this on server shutdown or at end of request processing.
15
+ */
16
+ export declare function flushSpans(): Promise<void>;
17
+ /**
18
+ * Convert an existing audit_log row to a ClickHouseSpan.
19
+ *
20
+ * Field mapping:
21
+ * ai_spans.action → operation_name
22
+ * ai_spans.source → source
23
+ * ai_spans.severity → severity
24
+ * ai_spans.store_id → store_id
25
+ * ai_spans.duration_ms → duration_ms
26
+ * ai_spans.error_message → error_message
27
+ * ai_spans.trace_id → trace_id
28
+ * ai_spans.span_id → span_id
29
+ * ai_spans.span_kind → span_kind
30
+ * ai_spans.service_name → service_name
31
+ * ai_spans.status_code → status_code
32
+ * ai_spans.start_time → started_at
33
+ * ai_spans.end_time → ended_at
34
+ * ai_spans.conversation_id → conversation_id
35
+ * ai_spans.user_id → user_id
36
+ * ai_spans.input_tokens → prompt_tokens
37
+ * ai_spans.output_tokens → completion_tokens
38
+ * ai_spans.total_cost → token_cost_usd
39
+ * ai_spans.model → model_name
40
+ * ai_spans.resource_id (mcp_tool) → tool_name
41
+ * ai_spans.details (remaining) → attributes (JSON string)
42
+ */
43
+ export declare function auditRowToSpan(row: Record<string, unknown>): ClickHouseSpan;
44
+ /**
45
+ * Classify an error message into a standard error_type category.
46
+ * Used by auditRowToSpan and callers to populate the error_type column.
47
+ */
48
+ export declare function classifyErrorType(errorMessage: string | null | undefined): string | undefined;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * ClickHouse Span Buffer — batches spans for bulk insert to ClickHouse ai_spans.
3
+ *
4
+ * Same batching semantics (500ms / 100 records).
5
+ * Also provides auditRowToSpan() to convert legacy row shapes to ClickHouseSpan.
6
+ */
7
+ import { randomUUID } from "node:crypto";
8
+ import { getClickHouseClient } from "./clickhouse-client.js";
9
+ // ============================================================================
10
+ // Buffer config
11
+ // ============================================================================
12
+ const spanBuffer = [];
13
+ const FLUSH_INTERVAL = 500; // ms
14
+ const FLUSH_MAX = 100; // max records before force flush
15
+ let flushTimer = null;
16
+ // ============================================================================
17
+ // Public API
18
+ // ============================================================================
19
+ /**
20
+ * Queue a span for batch insert to ClickHouse.
21
+ */
22
+ export function queueSpan(span) {
23
+ spanBuffer.push(span);
24
+ if (spanBuffer.length >= FLUSH_MAX) {
25
+ flushSpans();
26
+ }
27
+ else if (!flushTimer) {
28
+ flushTimer = setTimeout(() => {
29
+ flushTimer = null;
30
+ flushSpans();
31
+ }, FLUSH_INTERVAL);
32
+ }
33
+ }
34
+ /**
35
+ * Flush all buffered spans to ClickHouse.
36
+ * Call this on server shutdown or at end of request processing.
37
+ */
38
+ export async function flushSpans() {
39
+ if (flushTimer) {
40
+ clearTimeout(flushTimer);
41
+ flushTimer = null;
42
+ }
43
+ if (spanBuffer.length > 0) {
44
+ const batch = spanBuffer.splice(0, spanBuffer.length);
45
+ const client = getClickHouseClient();
46
+ await client.insert("ai_spans", batch);
47
+ }
48
+ }
49
+ // ============================================================================
50
+ // Mapper: audit_log row → ClickHouseSpan
51
+ // ============================================================================
52
+ /**
53
+ * Convert an existing audit_log row to a ClickHouseSpan.
54
+ *
55
+ * Field mapping:
56
+ * ai_spans.action → operation_name
57
+ * ai_spans.source → source
58
+ * ai_spans.severity → severity
59
+ * ai_spans.store_id → store_id
60
+ * ai_spans.duration_ms → duration_ms
61
+ * ai_spans.error_message → error_message
62
+ * ai_spans.trace_id → trace_id
63
+ * ai_spans.span_id → span_id
64
+ * ai_spans.span_kind → span_kind
65
+ * ai_spans.service_name → service_name
66
+ * ai_spans.status_code → status_code
67
+ * ai_spans.start_time → started_at
68
+ * ai_spans.end_time → ended_at
69
+ * ai_spans.conversation_id → conversation_id
70
+ * ai_spans.user_id → user_id
71
+ * ai_spans.input_tokens → prompt_tokens
72
+ * ai_spans.output_tokens → completion_tokens
73
+ * ai_spans.total_cost → token_cost_usd
74
+ * ai_spans.model → model_name
75
+ * ai_spans.resource_id (mcp_tool) → tool_name
76
+ * ai_spans.details (remaining) → attributes (JSON string)
77
+ */
78
+ export function auditRowToSpan(row) {
79
+ const details = (row.details || {});
80
+ const now = new Date().toISOString();
81
+ // Extract known fields from details that map to top-level ClickHouse columns
82
+ const { input_tokens: detailInputTokens, output_tokens: detailOutputTokens, total_cost: detailCost, model: detailModel, "gen_ai.request.model": genAiModel, "gen_ai.usage.input_tokens": genAiInput, "gen_ai.usage.output_tokens": genAiOutput, "gen_ai.usage.cost": genAiCost, "gen_ai.usage.cache_read_tokens": genAiCacheRead, "gen_ai.usage.cache_creation_tokens": genAiCacheCreation, cache_read_tokens: detailCacheRead, cache_creation_tokens: detailCacheCreation, stop_reason: detailStopReason, turn_number: detailTurnNumber, turn_count: detailTurnCount, parent_conversation_id: detailParentConvId, input_bytes: detailInputBytes, output_bytes: detailOutputBytes, error_type: detailErrorType, retryable: detailRetryable, ...remainingDetails } = details;
83
+ // Determine tool_name from resource_id when it's an mcp_tool
84
+ const toolName = row.resource_type === "mcp_tool"
85
+ ? row.resource_id || undefined
86
+ : details.tool_name || undefined;
87
+ // Compute token fields — prefer top-level, fall back to details
88
+ const promptTokens = row.input_tokens
89
+ || genAiInput
90
+ || detailInputTokens
91
+ || undefined;
92
+ const completionTokens = row.output_tokens
93
+ || genAiOutput
94
+ || detailOutputTokens
95
+ || undefined;
96
+ const tokenCostUsd = row.total_cost
97
+ || genAiCost
98
+ || detailCost
99
+ || undefined;
100
+ const modelName = row.model
101
+ || genAiModel
102
+ || detailModel
103
+ || undefined;
104
+ return {
105
+ span_id: row.span_id || randomHexId(),
106
+ trace_id: row.trace_id || row.request_id || randomUUID(),
107
+ store_id: row.store_id || undefined,
108
+ service_name: row.service_name || "agent-server",
109
+ operation_name: row.action || undefined,
110
+ span_kind: row.span_kind || "INTERNAL",
111
+ source: row.source || undefined,
112
+ started_at: row.start_time || now,
113
+ ended_at: row.end_time || now,
114
+ duration_ms: row.duration_ms || 0,
115
+ status_code: row.status_code || "OK",
116
+ severity: row.severity || "info",
117
+ model_name: modelName,
118
+ prompt_tokens: promptTokens,
119
+ completion_tokens: completionTokens,
120
+ total_tokens: promptTokens && completionTokens ? promptTokens + completionTokens : undefined,
121
+ token_cost_usd: tokenCostUsd,
122
+ agent_id: row.resource_id || details.agent_id || undefined,
123
+ conversation_id: row.conversation_id || undefined,
124
+ tool_name: toolName,
125
+ error_message: row.error_message || undefined,
126
+ attributes: Object.keys(remainingDetails).length > 0 ? JSON.stringify(remainingDetails) : undefined,
127
+ request_id: row.request_id || undefined,
128
+ user_id: row.user_id || undefined,
129
+ environment: "production",
130
+ // Enriched columns (003_enrich_spans)
131
+ user_email: row.user_email || undefined,
132
+ error_type: row.error_type || detailErrorType || undefined,
133
+ retryable: detailRetryable ? 1 : (row.severity === "error" ? 0 : undefined),
134
+ resource_type: row.resource_type || undefined,
135
+ cache_read_tokens: genAiCacheRead || detailCacheRead || undefined,
136
+ cache_creation_tokens: genAiCacheCreation || detailCacheCreation || undefined,
137
+ input_bytes: row.input_bytes || detailInputBytes || undefined,
138
+ output_bytes: row.output_bytes || detailOutputBytes || undefined,
139
+ stop_reason: row.stop_reason || detailStopReason || undefined,
140
+ turn_number: row.turn_number || detailTurnNumber || detailTurnCount || undefined,
141
+ parent_conversation_id: row.parent_conversation_id || detailParentConvId || undefined,
142
+ };
143
+ }
144
+ // ============================================================================
145
+ // Helpers
146
+ // ============================================================================
147
+ /**
148
+ * Classify an error message into a standard error_type category.
149
+ * Used by auditRowToSpan and callers to populate the error_type column.
150
+ */
151
+ export function classifyErrorType(errorMessage) {
152
+ if (!errorMessage)
153
+ return undefined;
154
+ const lower = errorMessage.toLowerCase();
155
+ if (lower.includes("rate limit") || lower.includes("429"))
156
+ return "rate_limit";
157
+ if (lower.includes("auth") || lower.includes("401") || lower.includes("403"))
158
+ return "auth";
159
+ if (lower.includes("timeout") || lower.includes("timed out"))
160
+ return "timeout";
161
+ if (lower.includes("validation") || lower.includes("invalid"))
162
+ return "validation";
163
+ if (lower.includes("not found") || lower.includes("404"))
164
+ return "not_found";
165
+ if (lower.includes("overloaded") || lower.includes("529"))
166
+ return "overloaded";
167
+ if (lower.includes("circuit breaker"))
168
+ return "circuit_breaker";
169
+ return undefined;
170
+ }
171
+ function randomHexId() {
172
+ const bytes = new Uint8Array(8);
173
+ crypto.getRandomValues(bytes);
174
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, "0")).join("");
175
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ClickHouse HTTP Client for Node.js MCP Server
3
+ *
4
+ * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).
5
+ * Uses the ClickHouse HTTP interface with JSONEachRow format.
6
+ *
7
+ * Environment variables:
8
+ * CLICKHOUSE_HOST — e.g. "xxx.us-east-1.aws.clickhouse.cloud"
9
+ * CLICKHOUSE_USER — default "default"
10
+ * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console
11
+ * CLICKHOUSE_DATABASE — default "default"
12
+ * CLICKHOUSE_ENABLED — "true" to enable (graceful no-op otherwise)
13
+ */
14
+ export interface ClickHouseSpan {
15
+ span_id: string;
16
+ trace_id: string;
17
+ parent_span_id?: string;
18
+ store_id?: string;
19
+ service_name: string;
20
+ operation_name?: string;
21
+ span_kind?: string;
22
+ source?: string;
23
+ started_at: string;
24
+ ended_at: string;
25
+ duration_ms: number;
26
+ status_code?: string;
27
+ severity?: string;
28
+ http_status?: number;
29
+ http_method?: string;
30
+ http_path?: string;
31
+ model_name?: string;
32
+ prompt_tokens?: number;
33
+ completion_tokens?: number;
34
+ total_tokens?: number;
35
+ token_cost_usd?: number;
36
+ agent_id?: string;
37
+ conversation_id?: string;
38
+ tool_name?: string;
39
+ error_message?: string;
40
+ attributes?: string;
41
+ events?: string;
42
+ request_id?: string;
43
+ user_id?: string;
44
+ environment?: string;
45
+ user_email?: string;
46
+ error_type?: string;
47
+ retryable?: number;
48
+ resource_type?: string;
49
+ cache_read_tokens?: number;
50
+ cache_creation_tokens?: number;
51
+ input_bytes?: number;
52
+ output_bytes?: number;
53
+ stop_reason?: string;
54
+ turn_number?: number;
55
+ parent_conversation_id?: string;
56
+ }
57
+ export interface ClickHouseError {
58
+ fingerprint: string;
59
+ severity?: string;
60
+ error_type: string;
61
+ error_message: string;
62
+ error_code?: string;
63
+ stack_trace?: string;
64
+ source_file?: string;
65
+ source_line?: number;
66
+ source_function?: string;
67
+ platform?: string;
68
+ service_name: string;
69
+ service_version?: string;
70
+ environment?: string;
71
+ store_id?: string;
72
+ user_id?: string;
73
+ user_email?: string;
74
+ trace_id?: string;
75
+ span_id?: string;
76
+ request_id?: string;
77
+ tags?: string;
78
+ extra?: string;
79
+ breadcrumbs?: string;
80
+ device_info?: string;
81
+ runtime_info?: string;
82
+ occurred_at: string;
83
+ }
84
+ declare class ClickHouseClient {
85
+ private host;
86
+ private user;
87
+ private password;
88
+ private database;
89
+ private enabled;
90
+ constructor();
91
+ get isEnabled(): boolean;
92
+ private get baseUrl();
93
+ private get authHeaders();
94
+ /**
95
+ * Insert rows into a ClickHouse table using JSONEachRow format.
96
+ * Fire-and-forget — logs errors but never throws.
97
+ */
98
+ insert(table: string, rows: Record<string, unknown>[]): Promise<void>;
99
+ /**
100
+ * Execute a SELECT query and return parsed JSON rows.
101
+ */
102
+ query<T = Record<string, unknown>>(sql: string): Promise<T[]>;
103
+ /**
104
+ * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).
105
+ * Useful for analytics RPCs that return one row with a complex JSON object.
106
+ */
107
+ queryOne<T = Record<string, unknown>>(sql: string): Promise<T | null>;
108
+ }
109
+ export declare function getClickHouseClient(): ClickHouseClient;
110
+ export declare function insertSpan(span: ClickHouseSpan): void;
111
+ export declare function insertError(error: ClickHouseError): void;
112
+ export {};
@@ -0,0 +1,141 @@
1
+ /**
2
+ * ClickHouse HTTP Client for Node.js MCP Server
3
+ *
4
+ * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).
5
+ * Uses the ClickHouse HTTP interface with JSONEachRow format.
6
+ *
7
+ * Environment variables:
8
+ * CLICKHOUSE_HOST — e.g. "xxx.us-east-1.aws.clickhouse.cloud"
9
+ * CLICKHOUSE_USER — default "default"
10
+ * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console
11
+ * CLICKHOUSE_DATABASE — default "default"
12
+ * CLICKHOUSE_ENABLED — "true" to enable (graceful no-op otherwise)
13
+ */
14
+ // ============================================================================
15
+ // Client
16
+ // ============================================================================
17
+ class ClickHouseClient {
18
+ host;
19
+ user;
20
+ password;
21
+ database;
22
+ enabled;
23
+ constructor() {
24
+ this.host = process.env.CLICKHOUSE_HOST || "";
25
+ this.user = process.env.CLICKHOUSE_USER || "default";
26
+ this.password = process.env.CLICKHOUSE_PASSWORD || "";
27
+ this.database = process.env.CLICKHOUSE_DATABASE || "default";
28
+ this.enabled = process.env.CLICKHOUSE_ENABLED === "true";
29
+ }
30
+ get isEnabled() {
31
+ return this.enabled && !!this.host;
32
+ }
33
+ get baseUrl() {
34
+ return `https://${this.host}:8443`;
35
+ }
36
+ get authHeaders() {
37
+ return {
38
+ "X-ClickHouse-User": this.user,
39
+ "X-ClickHouse-Key": this.password,
40
+ "X-ClickHouse-Database": this.database,
41
+ };
42
+ }
43
+ /**
44
+ * Insert rows into a ClickHouse table using JSONEachRow format.
45
+ * Fire-and-forget — logs errors but never throws.
46
+ */
47
+ async insert(table, rows) {
48
+ if (!this.isEnabled || rows.length === 0)
49
+ return;
50
+ const body = rows.map((row) => JSON.stringify(row)).join("\n");
51
+ const query = `INSERT INTO ${table} FORMAT JSONEachRow`;
52
+ try {
53
+ const response = await fetch(`${this.baseUrl}/?query=${encodeURIComponent(query)}`, {
54
+ method: "POST",
55
+ headers: {
56
+ ...this.authHeaders,
57
+ "Content-Type": "application/json",
58
+ },
59
+ body,
60
+ });
61
+ if (!response.ok) {
62
+ const text = await response.text();
63
+ console.error(`[clickhouse] insert ${table} failed (${response.status}): ${text.slice(0, 200)}`);
64
+ }
65
+ }
66
+ catch (err) {
67
+ console.error(`[clickhouse] insert ${table} error: ${err.message}`);
68
+ }
69
+ }
70
+ /**
71
+ * Execute a SELECT query and return parsed JSON rows.
72
+ */
73
+ async query(sql) {
74
+ if (!this.isEnabled)
75
+ return [];
76
+ try {
77
+ const response = await fetch(`${this.baseUrl}/?default_format=JSONEachRow`, {
78
+ method: "POST",
79
+ headers: {
80
+ ...this.authHeaders,
81
+ "Content-Type": "text/plain",
82
+ },
83
+ body: sql,
84
+ });
85
+ if (!response.ok) {
86
+ const text = await response.text();
87
+ console.error(`[clickhouse] query failed (${response.status}): ${text.slice(0, 200)}`);
88
+ return [];
89
+ }
90
+ const text = await response.text();
91
+ if (!text.trim())
92
+ return [];
93
+ return text
94
+ .trim()
95
+ .split("\n")
96
+ .map((line) => JSON.parse(line));
97
+ }
98
+ catch (err) {
99
+ console.error(`[clickhouse] query error: ${err.message}`);
100
+ return [];
101
+ }
102
+ }
103
+ /**
104
+ * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).
105
+ * Useful for analytics RPCs that return one row with a complex JSON object.
106
+ */
107
+ async queryOne(sql) {
108
+ const rows = await this.query(sql);
109
+ return rows.length > 0 ? rows[0] : null;
110
+ }
111
+ }
112
+ // Singleton — lazy init on first access
113
+ let _client = null;
114
+ export function getClickHouseClient() {
115
+ if (!_client) {
116
+ _client = new ClickHouseClient();
117
+ }
118
+ return _client;
119
+ }
120
+ // ============================================================================
121
+ // Convenience: Insert a span
122
+ // ============================================================================
123
+ export function insertSpan(span) {
124
+ const client = getClickHouseClient();
125
+ if (!client.isEnabled)
126
+ return;
127
+ client.insert("ai_spans", [span]).catch((err) => {
128
+ console.error(`[clickhouse] insertSpan failed: ${err.message}`);
129
+ });
130
+ }
131
+ // ============================================================================
132
+ // Convenience: Insert an error event
133
+ // ============================================================================
134
+ export function insertError(error) {
135
+ const client = getClickHouseClient();
136
+ if (!client.isEnabled)
137
+ return;
138
+ client.insert("error_events", [error]).catch((err) => {
139
+ console.error(`[clickhouse] insertError failed: ${err.message}`);
140
+ });
141
+ }
@@ -0,0 +1,91 @@
1
+ export interface COAData {
2
+ labName?: string;
3
+ labContact?: string;
4
+ logoUrl?: string;
5
+ labWebsite?: string;
6
+ labDirector?: string;
7
+ directorTitle?: string;
8
+ signatureUrl?: string;
9
+ approvalDate?: string;
10
+ sampleName: string;
11
+ sampleId: string;
12
+ strain?: string;
13
+ sampleType?: string;
14
+ sampleSize?: string;
15
+ batchId?: string;
16
+ dateCollected?: string;
17
+ dateReceived?: string;
18
+ dateTested?: string;
19
+ dateReported?: string;
20
+ clientName: string;
21
+ clientAddress?: string;
22
+ licenseNumber?: string;
23
+ clientState?: string;
24
+ clientType?: string;
25
+ cannabinoids?: Cannabinoid[];
26
+ totalTHC?: number;
27
+ totalCBD?: number;
28
+ totalCannabinoids?: number;
29
+ moisture?: number;
30
+ D9_THC?: number;
31
+ d9_thc?: number;
32
+ testsBatch?: boolean;
33
+ testsCannabinoids?: boolean;
34
+ testsMoisture?: boolean;
35
+ qrCodeDataUrl?: string;
36
+ notes?: string;
37
+ fullPanel?: boolean;
38
+ totalPages?: number;
39
+ microbialResults?: MicrobialResult[];
40
+ heavyMetalsResults?: HeavyMetalResult[];
41
+ mycotoxinResults?: MycotoxinResult[];
42
+ pesticidesCat1?: PesticideResult[];
43
+ pesticidesCat2?: PesticideResult[];
44
+ testsResidualSolvents?: ResidualSolventResult[];
45
+ }
46
+ interface Cannabinoid {
47
+ name: string;
48
+ lod: number;
49
+ loq: number;
50
+ result: string | number;
51
+ percentWeight: number;
52
+ mgPerG: number;
53
+ }
54
+ interface MicrobialResult {
55
+ test?: string;
56
+ analyte?: string;
57
+ result: number | "ND";
58
+ limit: number | "ND" | string;
59
+ status: "Pass" | "Fail";
60
+ }
61
+ interface HeavyMetalResult {
62
+ analyte: string;
63
+ lod: number;
64
+ loq: number;
65
+ result: number | "ND";
66
+ limit: number;
67
+ status: "Pass" | "Fail";
68
+ }
69
+ interface MycotoxinResult {
70
+ analyte: string;
71
+ lod: number;
72
+ loq: number;
73
+ result: number | "ND";
74
+ limit: number;
75
+ status: "Pass" | "Fail";
76
+ }
77
+ interface PesticideResult {
78
+ analyte: string;
79
+ result: "ND" | number;
80
+ status: "Pass" | "Fail";
81
+ }
82
+ interface ResidualSolventResult {
83
+ analyte: string;
84
+ result: string | number;
85
+ limit: number;
86
+ loq?: number;
87
+ unit: string;
88
+ status: "Pass" | "Fail";
89
+ }
90
+ export declare function renderCOAToPdf(data: COAData): Promise<Buffer>;
91
+ export {};