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.
- package/bin/swagmanager-mcp.js +51 -0
- package/dist/cli/app.js +30 -2
- package/dist/cli/chat/ChatApp.d.ts +4 -4
- package/dist/cli/chat/ChatApp.js +114 -44
- package/dist/cli/chat/ChatInput.d.ts +13 -6
- package/dist/cli/chat/ChatInput.js +433 -89
- package/dist/cli/chat/MemoryManager.d.ts +15 -0
- package/dist/cli/chat/MemoryManager.js +61 -0
- package/dist/cli/chat/MessageList.d.ts +8 -0
- package/dist/cli/chat/MessageList.js +1 -1
- package/dist/cli/chat/NodeManager.d.ts +30 -0
- package/dist/cli/chat/NodeManager.js +89 -0
- package/dist/cli/chat/NodeSelector.d.ts +19 -0
- package/dist/cli/chat/NodeSelector.js +37 -0
- package/dist/cli/chat/PlanApproval.d.ts +17 -0
- package/dist/cli/chat/PlanApproval.js +82 -0
- package/dist/cli/chat/SessionManager.d.ts +16 -0
- package/dist/cli/chat/SessionManager.js +43 -0
- package/dist/cli/chat/SlashMenu.d.ts +38 -0
- package/dist/cli/chat/SlashMenu.js +208 -0
- package/dist/cli/chat/StatusBar.d.ts +16 -0
- package/dist/cli/chat/StatusBar.js +22 -0
- package/dist/cli/chat/ThemeSelector.d.ts +14 -0
- package/dist/cli/chat/ThemeSelector.js +29 -0
- package/dist/cli/chat/ToolIndicator.d.ts +8 -0
- package/dist/cli/chat/ToolIndicator.js +33 -9
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
- package/dist/cli/commands/config-cmd.js +4 -25
- package/dist/cli/commands/db.d.ts +13 -0
- package/dist/cli/commands/db.js +243 -0
- package/dist/cli/commands/doctor.js +6 -9
- package/dist/cli/commands/mcp.js +1 -20
- package/dist/cli/services/agent-events.d.ts +22 -1
- package/dist/cli/services/agent-events.js +9 -0
- package/dist/cli/services/agent-loop.js +65 -8
- package/dist/cli/services/agent-worker-base.js +21 -6
- package/dist/cli/services/api-retry.d.ts +25 -0
- package/dist/cli/services/api-retry.js +91 -0
- package/dist/cli/services/auth-service.d.ts +1 -1
- package/dist/cli/services/auth-service.js +40 -19
- package/dist/cli/services/background-processes.js +26 -2
- package/dist/cli/services/config-store.d.ts +13 -1
- package/dist/cli/services/config-store.js +116 -13
- package/dist/cli/services/format-server-response.js +12 -6
- package/dist/cli/services/ink-resize-fix.d.ts +18 -0
- package/dist/cli/services/ink-resize-fix.js +66 -0
- package/dist/cli/services/interactive-tools.d.ts +14 -0
- package/dist/cli/services/interactive-tools.js +47 -2
- package/dist/cli/services/keybinding-manager.js +1 -1
- package/dist/cli/services/local-tools.js +35 -2
- package/dist/cli/services/server-tools.js +175 -3
- package/dist/cli/services/subagent.js +7 -6
- package/dist/cli/services/system-prompt.js +5 -3
- package/dist/cli/services/task-decomposer.d.ts +35 -0
- package/dist/cli/services/task-decomposer.js +199 -0
- package/dist/cli/services/team-lead.d.ts +18 -0
- package/dist/cli/services/team-lead.js +80 -0
- package/dist/cli/services/teammate.js +5 -5
- package/dist/cli/services/telemetry.d.ts +8 -2
- package/dist/cli/services/telemetry.js +116 -92
- package/dist/cli/services/tools/agent-tools.d.ts +1 -0
- package/dist/cli/services/tools/agent-tools.js +50 -4
- package/dist/cli/services/tools/file-ops.d.ts +2 -0
- package/dist/cli/services/tools/file-ops.js +85 -19
- package/dist/cli/services/tools/shell-exec.js +22 -12
- package/dist/cli/shared/Theme.d.ts +1 -2
- package/dist/cli/shared/Theme.js +1 -1
- package/dist/cli/shared/WhaleBanner.d.ts +4 -1
- package/dist/cli/shared/WhaleBanner.js +12 -8
- package/dist/cli/shared/markdown.d.ts +5 -4
- package/dist/cli/shared/markdown.js +376 -334
- package/dist/cli/shared/theme-manager.d.ts +27 -0
- package/dist/cli/shared/theme-manager.js +178 -0
- package/dist/cli/shared/theme-presets.d.ts +16 -0
- package/dist/cli/shared/theme-presets.js +265 -0
- package/dist/index.js +0 -51
- package/dist/node/adapters/imessage.d.ts +10 -0
- package/dist/node/adapters/imessage.js +45 -6
- package/dist/node/cli.js +459 -8
- package/dist/node/config.d.ts +17 -0
- package/dist/node/gateway-client.d.ts +55 -0
- package/dist/node/gateway-client.js +201 -0
- package/dist/node/portal/clipboard.d.ts +28 -0
- package/dist/node/portal/clipboard.js +183 -0
- package/dist/node/portal/discovery.d.ts +29 -0
- package/dist/node/portal/discovery.js +61 -0
- package/dist/node/portal/forward.d.ts +30 -0
- package/dist/node/portal/forward.js +90 -0
- package/dist/node/portal/index.d.ts +47 -0
- package/dist/node/portal/index.js +250 -0
- package/dist/node/portal/multiplexer.d.ts +48 -0
- package/dist/node/portal/multiplexer.js +207 -0
- package/dist/node/portal/permissions.d.ts +36 -0
- package/dist/node/portal/permissions.js +131 -0
- package/dist/node/portal/protocol.d.ts +140 -0
- package/dist/node/portal/protocol.js +193 -0
- package/dist/node/portal/screen.d.ts +18 -0
- package/dist/node/portal/screen.js +93 -0
- package/dist/node/portal/session.d.ts +68 -0
- package/dist/node/portal/session.js +127 -0
- package/dist/node/portal/shell.d.ts +26 -0
- package/dist/node/portal/shell.js +142 -0
- package/dist/node/portal/stream.d.ts +43 -0
- package/dist/node/portal/stream.js +90 -0
- package/dist/node/portal/transfer.d.ts +33 -0
- package/dist/node/portal/transfer.js +231 -0
- package/dist/node/portal/ui.d.ts +16 -0
- package/dist/node/portal/ui.js +148 -0
- package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
- package/dist/node/remote-desktop/compile-helper.js +73 -0
- package/dist/node/remote-desktop/index.d.ts +67 -0
- package/dist/node/remote-desktop/index.js +220 -0
- package/dist/node/remote-desktop/protocol.d.ts +96 -0
- package/dist/node/remote-desktop/protocol.js +67 -0
- package/dist/node/runtime.d.ts +8 -1
- package/dist/node/runtime.js +117 -9
- package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
- package/dist/server/handlers/__test-utils__/test-db.js +128 -0
- package/dist/server/handlers/api-keys.js +26 -2
- package/dist/server/handlers/browser.d.ts +0 -4
- package/dist/server/handlers/browser.js +0 -46
- package/dist/server/handlers/catalog.js +37 -14
- package/dist/server/handlers/clickhouse.d.ts +10 -0
- package/dist/server/handlers/clickhouse.js +215 -0
- package/dist/server/handlers/comms.d.ts +308 -4
- package/dist/server/handlers/comms.js +444 -11
- package/dist/server/handlers/creations.js +1 -1
- package/dist/server/handlers/crm.d.ts +54 -8
- package/dist/server/handlers/crm.js +353 -68
- package/dist/server/handlers/embeddings.js +3 -3
- package/dist/server/handlers/enrichment.js +39 -55
- package/dist/server/handlers/inventory.js +1 -1
- package/dist/server/handlers/kali.d.ts +9 -1
- package/dist/server/handlers/kali.js +50 -1
- package/dist/server/handlers/media.d.ts +8 -0
- package/dist/server/handlers/media.js +902 -0
- package/dist/server/handlers/meta-ads.js +6 -3
- package/dist/server/handlers/nodes.d.ts +2 -0
- package/dist/server/handlers/nodes.js +331 -40
- package/dist/server/handlers/operations.d.ts +4 -6
- package/dist/server/handlers/operations.js +99 -38
- package/dist/server/handlers/platform.js +224 -107
- package/dist/server/handlers/remove-bg.d.ts +6 -0
- package/dist/server/handlers/remove-bg.js +96 -0
- package/dist/server/handlers/storefront.d.ts +6 -0
- package/dist/server/handlers/storefront.js +477 -0
- package/dist/server/handlers/supply-chain.js +21 -3
- package/dist/server/handlers/workflow-steps.js +87 -31
- package/dist/server/handlers/workflows.js +4 -1
- package/dist/server/index.js +334 -88
- package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
- package/dist/server/lib/clickhouse-buffer.js +175 -0
- package/dist/server/lib/clickhouse-client.d.ts +112 -0
- package/dist/server/lib/clickhouse-client.js +141 -0
- package/dist/server/lib/coa-renderer.d.ts +91 -0
- package/dist/server/lib/coa-renderer.js +411 -0
- package/dist/server/lib/compaction-service.js +46 -1
- package/dist/server/lib/pdf-renderer.d.ts +143 -0
- package/dist/server/lib/pdf-renderer.js +867 -0
- package/dist/server/lib/react-pdf-layout.d.ts +40 -0
- package/dist/server/lib/react-pdf-layout.js +437 -0
- package/dist/server/lib/server-agent-loop.d.ts +2 -0
- package/dist/server/lib/server-agent-loop.js +36 -17
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +9 -6
- package/dist/server/lib/supabase-client.js +51 -3
- package/dist/server/lib/template-resolver.js +14 -4
- package/dist/server/lib/utils.js +15 -0
- package/dist/server/local-agent-gateway.d.ts +44 -0
- package/dist/server/local-agent-gateway.js +389 -49
- package/dist/server/providers/anthropic.js +12 -2
- package/dist/server/providers/gemini.js +17 -2
- package/dist/server/proxy-handlers.js +151 -0
- package/dist/server/tool-router.d.ts +2 -2
- package/dist/server/tool-router.js +25 -35
- package/dist/shared/agent-core.d.ts +25 -2
- package/dist/shared/agent-core.js +66 -5
- package/dist/shared/api-client.js +54 -3
- package/dist/shared/sse-parser.d.ts +1 -1
- package/dist/shared/sse-parser.js +5 -2
- package/dist/shared/tool-dispatch.js +15 -1
- package/package.json +16 -10
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
- 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 {};
|