@maestrogtm/maestro-gtm 0.10.16

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 (115) hide show
  1. package/dist/ai-RNHSWSNV.js +158 -0
  2. package/dist/ai-RNHSWSNV.js.map +1 -0
  3. package/dist/app-PSZH2J56.js +54 -0
  4. package/dist/app-PSZH2J56.js.map +1 -0
  5. package/dist/batch-ZCHN54YJ.js +28 -0
  6. package/dist/batch-ZCHN54YJ.js.map +1 -0
  7. package/dist/campaign-XDXQA7KX.js +119 -0
  8. package/dist/campaign-XDXQA7KX.js.map +1 -0
  9. package/dist/chunk-365Q36GF.js +54 -0
  10. package/dist/chunk-365Q36GF.js.map +1 -0
  11. package/dist/chunk-4IV6QS4U.js +122 -0
  12. package/dist/chunk-4IV6QS4U.js.map +1 -0
  13. package/dist/chunk-6GLLK5KO.js +64 -0
  14. package/dist/chunk-6GLLK5KO.js.map +1 -0
  15. package/dist/chunk-6UNBW5SN.js +686 -0
  16. package/dist/chunk-6UNBW5SN.js.map +1 -0
  17. package/dist/chunk-A7JD6EYV.js +92 -0
  18. package/dist/chunk-A7JD6EYV.js.map +1 -0
  19. package/dist/chunk-ARNVJPFM.js +139 -0
  20. package/dist/chunk-ARNVJPFM.js.map +1 -0
  21. package/dist/chunk-AX6BOEF2.js +345 -0
  22. package/dist/chunk-AX6BOEF2.js.map +1 -0
  23. package/dist/chunk-C3T7QPSO.js +507 -0
  24. package/dist/chunk-C3T7QPSO.js.map +1 -0
  25. package/dist/chunk-FG43GILY.js +46 -0
  26. package/dist/chunk-FG43GILY.js.map +1 -0
  27. package/dist/chunk-FS6DCNCA.js +139 -0
  28. package/dist/chunk-FS6DCNCA.js.map +1 -0
  29. package/dist/chunk-I6GRD4X7.js +1144 -0
  30. package/dist/chunk-I6GRD4X7.js.map +1 -0
  31. package/dist/chunk-IP34URKR.js +621 -0
  32. package/dist/chunk-IP34URKR.js.map +1 -0
  33. package/dist/chunk-JFSKOY7Z.js +252 -0
  34. package/dist/chunk-JFSKOY7Z.js.map +1 -0
  35. package/dist/chunk-M25KLO7T.js +3272 -0
  36. package/dist/chunk-M25KLO7T.js.map +1 -0
  37. package/dist/chunk-M3G2WREL.js +57 -0
  38. package/dist/chunk-M3G2WREL.js.map +1 -0
  39. package/dist/chunk-MFFACSBE.js +4266 -0
  40. package/dist/chunk-MFFACSBE.js.map +1 -0
  41. package/dist/chunk-PZ5AY32C.js +10 -0
  42. package/dist/chunk-PZ5AY32C.js.map +1 -0
  43. package/dist/chunk-QZH3XFOQ.js +2636 -0
  44. package/dist/chunk-QZH3XFOQ.js.map +1 -0
  45. package/dist/chunk-SPWDMOEU.js +1940 -0
  46. package/dist/chunk-SPWDMOEU.js.map +1 -0
  47. package/dist/chunk-TP3BZDVV.js +28 -0
  48. package/dist/chunk-TP3BZDVV.js.map +1 -0
  49. package/dist/chunk-UBJUBYSQ.js +18 -0
  50. package/dist/chunk-UBJUBYSQ.js.map +1 -0
  51. package/dist/chunk-VNKXGHWY.js +20 -0
  52. package/dist/chunk-VNKXGHWY.js.map +1 -0
  53. package/dist/chunk-WKLCPIFB.js +9862 -0
  54. package/dist/chunk-WKLCPIFB.js.map +1 -0
  55. package/dist/chunk-YV5XOXRQ.js +7 -0
  56. package/dist/chunk-YV5XOXRQ.js.map +1 -0
  57. package/dist/cli-Z3BNNJYQ.js +852 -0
  58. package/dist/cli-Z3BNNJYQ.js.map +1 -0
  59. package/dist/client-Y34LNEWN.js +8 -0
  60. package/dist/client-Y34LNEWN.js.map +1 -0
  61. package/dist/config.js +17 -0
  62. package/dist/config.js.map +1 -0
  63. package/dist/configure-XSENK4X5.js +64 -0
  64. package/dist/configure-XSENK4X5.js.map +1 -0
  65. package/dist/context.js +10 -0
  66. package/dist/context.js.map +1 -0
  67. package/dist/crm-QBNHVBYV.js +86 -0
  68. package/dist/crm-QBNHVBYV.js.map +1 -0
  69. package/dist/dfy-X3OXIYRA.js +356 -0
  70. package/dist/dfy-X3OXIYRA.js.map +1 -0
  71. package/dist/dist-LGCJKGBS.js +121 -0
  72. package/dist/dist-LGCJKGBS.js.map +1 -0
  73. package/dist/engagement-C4U7LPJH.js +463 -0
  74. package/dist/engagement-C4U7LPJH.js.map +1 -0
  75. package/dist/enrich-F5GPVZFE.js +226 -0
  76. package/dist/enrich-F5GPVZFE.js.map +1 -0
  77. package/dist/extract-DS5N6SSJ.js +155 -0
  78. package/dist/extract-DS5N6SSJ.js.map +1 -0
  79. package/dist/feedback-AIXKXNM5.js +51 -0
  80. package/dist/feedback-AIXKXNM5.js.map +1 -0
  81. package/dist/fetch-QJDSPI63.js +87 -0
  82. package/dist/fetch-QJDSPI63.js.map +1 -0
  83. package/dist/handlers.js +13 -0
  84. package/dist/handlers.js.map +1 -0
  85. package/dist/index.js +38 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/list-HL7NQQJX.js +236 -0
  88. package/dist/list-HL7NQQJX.js.map +1 -0
  89. package/dist/maestro-N7Q2JX22.js +903 -0
  90. package/dist/maestro-N7Q2JX22.js.map +1 -0
  91. package/dist/prospect-RUOT43H6.js +532 -0
  92. package/dist/prospect-RUOT43H6.js.map +1 -0
  93. package/dist/providers/factory.js +10 -0
  94. package/dist/providers/factory.js.map +1 -0
  95. package/dist/providers/registry.js +8 -0
  96. package/dist/providers/registry.js.map +1 -0
  97. package/dist/provision-FT5NWN77.js +394 -0
  98. package/dist/provision-FT5NWN77.js.map +1 -0
  99. package/dist/recipe-JU3SXMZF.js +137 -0
  100. package/dist/recipe-JU3SXMZF.js.map +1 -0
  101. package/dist/review-5SB6DYDZ.js +70 -0
  102. package/dist/review-5SB6DYDZ.js.map +1 -0
  103. package/dist/sdk-LVBHNQ6T.js +3852 -0
  104. package/dist/sdk-LVBHNQ6T.js.map +1 -0
  105. package/dist/server-REKYQZ2E.js +22 -0
  106. package/dist/server-REKYQZ2E.js.map +1 -0
  107. package/dist/status-V3EEFS7S.js +114 -0
  108. package/dist/status-V3EEFS7S.js.map +1 -0
  109. package/dist/tam-J6NDBP5W.js +682 -0
  110. package/dist/tam-J6NDBP5W.js.map +1 -0
  111. package/dist/tools.js +80 -0
  112. package/dist/tools.js.map +1 -0
  113. package/dist/validation.js +12 -0
  114. package/dist/validation.js.map +1 -0
  115. package/package.json +77 -0
@@ -0,0 +1,158 @@
1
+ import {
2
+ getSupabaseOrNull,
3
+ resolveOwner
4
+ } from "./chunk-FG43GILY.js";
5
+ import {
6
+ assembleContext,
7
+ buildPromptContext
8
+ } from "./chunk-SPWDMOEU.js";
9
+ import "./chunk-UBJUBYSQ.js";
10
+ import {
11
+ logWarn
12
+ } from "./chunk-6GLLK5KO.js";
13
+ import "./chunk-PZ5AY32C.js";
14
+
15
+ // src/handlers/ai.ts
16
+ var GRAPH_ENRICHABLE_ACTIONS = ["personalize", "write_copy"];
17
+ async function handleAi(args, ctx) {
18
+ const action = args.action;
19
+ const data = args.data;
20
+ const prompt = args.prompt;
21
+ const model = args.model;
22
+ const enrichWithGraph = args.enrich_with_graph;
23
+ const aiConfig = ctx.session.getActiveContext().config.ai;
24
+ let graphContext = "";
25
+ if (enrichWithGraph && GRAPH_ENRICHABLE_ACTIONS.includes(action)) {
26
+ graphContext = await fetchGraphContext(args);
27
+ }
28
+ const systemPrompt = buildSystemPrompt(action, prompt, graphContext);
29
+ const userContent = typeof data === "string" ? data : JSON.stringify(data, null, 2);
30
+ if (!aiConfig.api_key) {
31
+ return {
32
+ mode: "passthrough",
33
+ action,
34
+ system_prompt: systemPrompt,
35
+ user_content: userContent,
36
+ expected_output: "Return JSON matching the action type. For score: {score, reasoning}. For classify: {classification, confidence}. For personalize: {message}. For clean: cleaned JSON. For write_copy: {copy: string}."
37
+ };
38
+ }
39
+ const modelId = model ?? aiConfig.model ?? getDefaultModel(aiConfig.provider);
40
+ if (aiConfig.provider === "anthropic") {
41
+ return callAnthropic(aiConfig.api_key, modelId, systemPrompt, userContent);
42
+ }
43
+ return callOpenAI(aiConfig.api_key, modelId, systemPrompt, userContent);
44
+ }
45
+ async function fetchGraphContext(args) {
46
+ try {
47
+ const supabase = getSupabaseOrNull();
48
+ if (!supabase) return "";
49
+ const owner = resolveOwner(args);
50
+ const context = await assembleContext(supabase, {
51
+ owner_id: owner.owner_id,
52
+ owner_type: owner.owner_type,
53
+ strategy: "personalization"
54
+ });
55
+ return buildPromptContext(context);
56
+ } catch (err) {
57
+ logWarn("ai.fetchGraphContext", "Graph enrichment failed \u2014 proceeding without context", {
58
+ error: err instanceof Error ? err.message : String(err)
59
+ });
60
+ return "";
61
+ }
62
+ }
63
+ function buildSystemPrompt(action, customPrompt, graphContext) {
64
+ const basePrompt = customPrompt ?? getDefaultPrompt(action);
65
+ if (!graphContext) return basePrompt;
66
+ return `${basePrompt}
67
+
68
+ ## Business Context (from Knowledge Graph)
69
+ ${graphContext}
70
+
71
+ Use the business context above to inform your work \u2014 reference the prospect's alignment with specific personas, pain points, proof points, and competitive positioning.`;
72
+ }
73
+ function getDefaultPrompt(action) {
74
+ const prompts = {
75
+ personalize: "You are a sales copywriter. Personalize the outreach message for each contact using their data. Return JSON array with personalized fields.",
76
+ classify: "You are a data classifier. Classify each record according to the criteria. Return JSON array with classification results.",
77
+ clean: "You are a data cleaning specialist. Clean and normalize the data. Fix typos, standardize formats, remove duplicates. Return cleaned JSON.",
78
+ score: "You are a lead scoring expert. Score each lead 1-100 based on fit and intent signals. Return JSON array with scores and reasoning.",
79
+ write_copy: "You are a B2B copywriter. Write the requested copy based on the data provided. Return the copy as a string."
80
+ };
81
+ return prompts[action];
82
+ }
83
+ function getDefaultModel(provider) {
84
+ return provider === "anthropic" ? "claude-haiku-4-5-20251001" : "gpt-4o";
85
+ }
86
+ async function callAnthropic(apiKey, model, system, content) {
87
+ const controller = new AbortController();
88
+ const timeout = setTimeout(() => controller.abort(), 12e4);
89
+ try {
90
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
91
+ method: "POST",
92
+ headers: {
93
+ "Content-Type": "application/json",
94
+ "x-api-key": apiKey,
95
+ "anthropic-version": "2023-06-01"
96
+ },
97
+ body: JSON.stringify({
98
+ model,
99
+ max_tokens: 4096,
100
+ system,
101
+ messages: [{ role: "user", content }]
102
+ }),
103
+ signal: controller.signal
104
+ });
105
+ if (!response.ok) {
106
+ const body = await response.json().catch(() => ({}));
107
+ throw new Error(`Anthropic API error ${response.status}: ${JSON.stringify(body)}`);
108
+ }
109
+ const result = await response.json();
110
+ const text = result.content[0]?.text ?? "";
111
+ try {
112
+ return { result: JSON.parse(text), raw: false };
113
+ } catch {
114
+ return { result: text, raw: true };
115
+ }
116
+ } finally {
117
+ clearTimeout(timeout);
118
+ }
119
+ }
120
+ async function callOpenAI(apiKey, model, system, content) {
121
+ const controller = new AbortController();
122
+ const timeout = setTimeout(() => controller.abort(), 12e4);
123
+ try {
124
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
125
+ method: "POST",
126
+ headers: {
127
+ "Content-Type": "application/json",
128
+ Authorization: `Bearer ${apiKey}`
129
+ },
130
+ body: JSON.stringify({
131
+ model,
132
+ messages: [
133
+ { role: "system", content: system },
134
+ { role: "user", content }
135
+ ],
136
+ max_tokens: 4096
137
+ }),
138
+ signal: controller.signal
139
+ });
140
+ if (!response.ok) {
141
+ const body = await response.json().catch(() => ({}));
142
+ throw new Error(`OpenAI API error ${response.status}: ${JSON.stringify(body)}`);
143
+ }
144
+ const result = await response.json();
145
+ const text = result.choices[0]?.message?.content ?? "";
146
+ try {
147
+ return { result: JSON.parse(text), raw: false };
148
+ } catch {
149
+ return { result: text, raw: true };
150
+ }
151
+ } finally {
152
+ clearTimeout(timeout);
153
+ }
154
+ }
155
+ export {
156
+ handleAi
157
+ };
158
+ //# sourceMappingURL=ai-RNHSWSNV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/ai.ts"],"sourcesContent":["/** AI handler. Runs AI data operations using configured AI provider.\n * Constraint: Uses BYOK AI keys. No hardcoded API keys.\n * Graph enrichment is opt-in and never blocks the AI call. */\n\nimport { logWarn } from '@maestro/logging';\nimport { assembleContext, buildPromptContext } from '@maestro/knowledge-graph';\n\nimport type { HandlerContext } from './index.js';\nimport { getSupabaseOrNull, resolveOwner } from '../utils/supabase.js';\n\n// ─── Types ───────────────────────────────────────────────\n\ntype AiAction = 'personalize' | 'classify' | 'clean' | 'score' | 'write_copy';\n\nconst GRAPH_ENRICHABLE_ACTIONS: AiAction[] = ['personalize', 'write_copy'];\n\n// ─── Handler ─────────────────────────────────────────────\n\nexport async function handleAi(\n args: Record<string, unknown>,\n ctx: HandlerContext\n): Promise<unknown> {\n const action = args.action as AiAction;\n const data = args.data as unknown;\n const prompt = args.prompt as string | undefined;\n const model = args.model as string | undefined;\n const enrichWithGraph = args.enrich_with_graph as boolean | undefined;\n\n const aiConfig = ctx.session.getActiveContext().config.ai;\n\n // Optionally fetch graph context for personalize/write_copy\n let graphContext = '';\n if (enrichWithGraph && GRAPH_ENRICHABLE_ACTIONS.includes(action)) {\n graphContext = await fetchGraphContext(args);\n }\n\n const systemPrompt = buildSystemPrompt(action, prompt, graphContext);\n const userContent = typeof data === 'string' ? data : JSON.stringify(data, null, 2);\n\n if (!aiConfig.api_key) {\n // Passthrough mode: return constructed prompt for the calling agent to process.\n // When invoked from Claude Code, the agent IS the AI — no external API needed.\n return {\n mode: 'passthrough',\n action,\n system_prompt: systemPrompt,\n user_content: userContent,\n expected_output:\n 'Return JSON matching the action type. For score: {score, reasoning}. ' +\n 'For classify: {classification, confidence}. For personalize: {message}. ' +\n 'For clean: cleaned JSON. For write_copy: {copy: string}.',\n };\n }\n\n const modelId = model ?? aiConfig.model ?? getDefaultModel(aiConfig.provider);\n\n if (aiConfig.provider === 'anthropic') {\n return callAnthropic(aiConfig.api_key, modelId, systemPrompt, userContent);\n }\n return callOpenAI(aiConfig.api_key, modelId, systemPrompt, userContent);\n}\n\n// ─── Graph Enrichment ───────────────────────────────────\n\n/**\n * Fetch graph context for enriching AI prompts.\n * Never throws — returns empty string on any failure (graceful degradation).\n */\nasync function fetchGraphContext(args: Record<string, unknown>): Promise<string> {\n try {\n const supabase = getSupabaseOrNull();\n if (!supabase) return '';\n\n const owner = resolveOwner(args);\n const context = await assembleContext(supabase, {\n owner_id: owner.owner_id,\n owner_type: owner.owner_type,\n strategy: 'personalization',\n });\n\n return buildPromptContext(context);\n } catch (err) {\n logWarn('ai.fetchGraphContext', 'Graph enrichment failed — proceeding without context', {\n error: err instanceof Error ? err.message : String(err),\n });\n return '';\n }\n}\n\n// ─── Prompts ─────────────────────────────────────────────\n\nfunction buildSystemPrompt(action: AiAction, customPrompt?: string, graphContext?: string): string {\n const basePrompt = customPrompt ?? getDefaultPrompt(action);\n\n if (!graphContext) return basePrompt;\n\n return `${basePrompt}\n\n## Business Context (from Knowledge Graph)\n${graphContext}\n\nUse the business context above to inform your work — reference the prospect's alignment with specific personas, pain points, proof points, and competitive positioning.`;\n}\n\nfunction getDefaultPrompt(action: AiAction): string {\n const prompts: Record<AiAction, string> = {\n personalize:\n 'You are a sales copywriter. Personalize the outreach message for each contact using their data. Return JSON array with personalized fields.',\n classify:\n 'You are a data classifier. Classify each record according to the criteria. Return JSON array with classification results.',\n clean:\n 'You are a data cleaning specialist. Clean and normalize the data. Fix typos, standardize formats, remove duplicates. Return cleaned JSON.',\n score:\n 'You are a lead scoring expert. Score each lead 1-100 based on fit and intent signals. Return JSON array with scores and reasoning.',\n write_copy:\n 'You are a B2B copywriter. Write the requested copy based on the data provided. Return the copy as a string.',\n };\n\n return prompts[action];\n}\n\nfunction getDefaultModel(provider: 'anthropic' | 'openai'): string {\n return provider === 'anthropic' ? 'claude-haiku-4-5-20251001' : 'gpt-4o';\n}\n\n// ─── API Calls ───────────────────────────────────────────\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n system: string,\n content: string\n): Promise<unknown> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 120_000);\n\n try {\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model,\n max_tokens: 4096,\n system,\n messages: [{ role: 'user', content }],\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({}));\n throw new Error(`Anthropic API error ${response.status}: ${JSON.stringify(body)}`);\n }\n\n const result = (await response.json()) as { content: Array<{ text: string }> };\n const text = result.content[0]?.text ?? '';\n\n try {\n return { result: JSON.parse(text), raw: false };\n } catch {\n return { result: text, raw: true };\n }\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function callOpenAI(\n apiKey: string,\n model: string,\n system: string,\n content: string\n): Promise<unknown> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 120_000);\n\n try {\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content },\n ],\n max_tokens: 4096,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({}));\n throw new Error(`OpenAI API error ${response.status}: ${JSON.stringify(body)}`);\n }\n\n const result = (await response.json()) as {\n choices: Array<{ message: { content: string } }>;\n };\n const text = result.choices[0]?.message?.content ?? '';\n\n try {\n return { result: JSON.parse(text), raw: false };\n } catch {\n return { result: text, raw: true };\n }\n } finally {\n clearTimeout(timeout);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ import {
2
+ APP_ROUTES,
3
+ resolvePath
4
+ } from "./chunk-ARNVJPFM.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+
7
+ // src/handlers/app.ts
8
+ async function handleApp(tool, args, client) {
9
+ const action = args.action;
10
+ const routeKey = `${tool}_${action}`;
11
+ const route = APP_ROUTES[routeKey];
12
+ if (!route) {
13
+ const available = Object.keys(APP_ROUTES).filter((k) => k.startsWith(tool + "_")).join(", ");
14
+ throw new Error(`Unknown app route: ${routeKey}. Available: ${available}`);
15
+ }
16
+ const pathParams = {};
17
+ const bodyOrQuery = {};
18
+ for (const [key, val] of Object.entries(args)) {
19
+ if (key === "action" || key === "team_id") continue;
20
+ if (typeof val === "string" && route.path.includes(`:${key}`)) {
21
+ pathParams[key] = val;
22
+ } else if (val !== void 0) {
23
+ bodyOrQuery[key] = val;
24
+ }
25
+ }
26
+ if (route.queryMapping) {
27
+ for (const [from, to] of Object.entries(route.queryMapping)) {
28
+ if (from in bodyOrQuery) {
29
+ bodyOrQuery[to] = bodyOrQuery[from];
30
+ delete bodyOrQuery[from];
31
+ }
32
+ }
33
+ }
34
+ const resolvedPath = resolvePath(route.path, pathParams);
35
+ const teamId = args.team_id;
36
+ if (route.method === "GET") {
37
+ const params = {};
38
+ for (const [k, v] of Object.entries(bodyOrQuery)) {
39
+ if (v !== void 0 && v !== null) {
40
+ params[k] = typeof v === "string" ? v : JSON.stringify(v);
41
+ }
42
+ }
43
+ return client.request("GET", resolvedPath, { params, timeout: route.timeout, teamId });
44
+ }
45
+ return client.request(route.method, resolvedPath, {
46
+ body: Object.keys(bodyOrQuery).length > 0 ? bodyOrQuery : void 0,
47
+ timeout: route.timeout,
48
+ teamId
49
+ });
50
+ }
51
+ export {
52
+ handleApp
53
+ };
54
+ //# sourceMappingURL=app-PSZH2J56.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/app.ts"],"sourcesContent":["/** App handler. Routes MCP tool calls to Maestro app HTTP API.\n * Constraint: No business logic. Maps tool action to HTTP call. */\n\nimport type { AppClient } from '../app/client.js';\nimport { APP_ROUTES, resolvePath } from '../app/routes.js';\n\n// ─── Handler ─────────────────────────────────────────────\n\nexport async function handleApp(\n tool: string,\n args: Record<string, unknown>,\n client: AppClient\n): Promise<unknown> {\n const action = args.action as string;\n const routeKey = `${tool}_${action}`;\n const route = APP_ROUTES[routeKey];\n\n if (!route) {\n const available = Object.keys(APP_ROUTES)\n .filter((k) => k.startsWith(tool + '_'))\n .join(', ');\n throw new Error(`Unknown app route: ${routeKey}. Available: ${available}`);\n }\n\n // Extract path params (id, leadMagnetId, etc.) from args\n const pathParams: Record<string, string> = {};\n const bodyOrQuery: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(args)) {\n if (key === 'action' || key === 'team_id') continue;\n if (typeof val === 'string' && route.path.includes(`:${key}`)) {\n pathParams[key] = val;\n } else if (val !== undefined) {\n bodyOrQuery[key] = val;\n }\n }\n\n // Apply query/body param renaming (e.g. { query: 'q' })\n if (route.queryMapping) {\n for (const [from, to] of Object.entries(route.queryMapping)) {\n if (from in bodyOrQuery) {\n bodyOrQuery[to] = bodyOrQuery[from];\n delete bodyOrQuery[from];\n }\n }\n }\n\n const resolvedPath = resolvePath(route.path, pathParams);\n const teamId = args.team_id as string | undefined;\n\n if (route.method === 'GET') {\n const params: Record<string, string> = {};\n for (const [k, v] of Object.entries(bodyOrQuery)) {\n if (v !== undefined && v !== null) {\n params[k] = typeof v === 'string' ? v : JSON.stringify(v);\n }\n }\n return client.request('GET', resolvedPath, { params, timeout: route.timeout, teamId });\n }\n\n return client.request(route.method, resolvedPath, {\n body: Object.keys(bodyOrQuery).length > 0 ? bodyOrQuery : undefined,\n timeout: route.timeout,\n teamId,\n });\n}\n"],"mappings":";;;;;;;AAQA,eAAsB,UACpB,MACA,MACA,QACkB;AAClB,QAAM,SAAS,KAAK;AACpB,QAAM,WAAW,GAAG,IAAI,IAAI,MAAM;AAClC,QAAM,QAAQ,WAAW,QAAQ;AAEjC,MAAI,CAAC,OAAO;AACV,UAAM,YAAY,OAAO,KAAK,UAAU,EACrC,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,GAAG,CAAC,EACtC,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM,sBAAsB,QAAQ,gBAAgB,SAAS,EAAE;AAAA,EAC3E;AAGA,QAAM,aAAqC,CAAC;AAC5C,QAAM,cAAuC,CAAC;AAE9C,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC7C,QAAI,QAAQ,YAAY,QAAQ,UAAW;AAC3C,QAAI,OAAO,QAAQ,YAAY,MAAM,KAAK,SAAS,IAAI,GAAG,EAAE,GAAG;AAC7D,iBAAW,GAAG,IAAI;AAAA,IACpB,WAAW,QAAQ,QAAW;AAC5B,kBAAY,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AAGA,MAAI,MAAM,cAAc;AACtB,eAAW,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,MAAM,YAAY,GAAG;AAC3D,UAAI,QAAQ,aAAa;AACvB,oBAAY,EAAE,IAAI,YAAY,IAAI;AAClC,eAAO,YAAY,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,YAAY,MAAM,MAAM,UAAU;AACvD,QAAM,SAAS,KAAK;AAEpB,MAAI,MAAM,WAAW,OAAO;AAC1B,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,WAAW,GAAG;AAChD,UAAI,MAAM,UAAa,MAAM,MAAM;AACjC,eAAO,CAAC,IAAI,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC;AAAA,MAC1D;AAAA,IACF;AACA,WAAO,OAAO,QAAQ,OAAO,cAAc,EAAE,QAAQ,SAAS,MAAM,SAAS,OAAO,CAAC;AAAA,EACvF;AAEA,SAAO,OAAO,QAAQ,MAAM,QAAQ,cAAc;AAAA,IAChD,MAAM,OAAO,KAAK,WAAW,EAAE,SAAS,IAAI,cAAc;AAAA,IAC1D,SAAS,MAAM;AAAA,IACf;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,28 @@
1
+ import {
2
+ runBatch
3
+ } from "./chunk-IP34URKR.js";
4
+ import "./chunk-6GLLK5KO.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+
7
+ // src/handlers/batch.ts
8
+ async function handleBatch(args, ctx, dispatchFn) {
9
+ const config = {
10
+ input_csv: args.input_csv,
11
+ output_csv: args.output_csv,
12
+ steps: args.steps ?? [],
13
+ rows: args.rows,
14
+ dry_run: args.dry_run,
15
+ concurrency: args.concurrency,
16
+ maestro_strategy: args.maestro_strategy,
17
+ output: args.output,
18
+ post_process: args.post_process
19
+ };
20
+ const dispatch = async (tool, toolArgs) => {
21
+ return dispatchFn(tool, toolArgs, ctx);
22
+ };
23
+ return runBatch(config, dispatch);
24
+ }
25
+ export {
26
+ handleBatch
27
+ };
28
+ //# sourceMappingURL=batch-ZCHN54YJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/batch.ts"],"sourcesContent":["/** Handler for gtm_batch. Bridges batch engine to MCP handler infrastructure.\n * Constraint: Thin adapter. All logic lives in batch/engine.ts. */\n\nimport { runBatch, type BatchConfig, type BatchStep } from '../batch/engine.js';\nimport type { OutputTarget } from '../batch/output.js';\nimport type { HandlerContext } from './index.js';\n\n// ─── Types ──────────────────────────────────────────────\n\ntype InternalDispatchFn = (\n tool: string,\n args: Record<string, unknown>,\n ctx: HandlerContext\n) => Promise<unknown>;\n\n// ─── Handler ────────────────────────────────────────────\n\nexport async function handleBatch(\n args: Record<string, unknown>,\n ctx: HandlerContext,\n dispatchFn: InternalDispatchFn\n): Promise<unknown> {\n const config: BatchConfig = {\n input_csv: args.input_csv as string,\n output_csv: args.output_csv as string | undefined,\n steps: (args.steps as BatchStep[]) ?? [],\n rows: args.rows as string | undefined,\n dry_run: args.dry_run as boolean | undefined,\n concurrency: args.concurrency as number | undefined,\n maestro_strategy: args.maestro_strategy as BatchConfig['maestro_strategy'],\n output: args.output as OutputTarget | undefined,\n post_process: args.post_process as string | undefined,\n };\n\n const dispatch = async (tool: string, toolArgs: Record<string, unknown>): Promise<unknown> => {\n return dispatchFn(tool, toolArgs, ctx);\n };\n\n return runBatch(config, dispatch);\n}\n"],"mappings":";;;;;;;AAiBA,eAAsB,YACpB,MACA,KACA,YACkB;AAClB,QAAM,SAAsB;AAAA,IAC1B,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,OAAQ,KAAK,SAAyB,CAAC;AAAA,IACvC,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,aAAa,KAAK;AAAA,IAClB,kBAAkB,KAAK;AAAA,IACvB,QAAQ,KAAK;AAAA,IACb,cAAc,KAAK;AAAA,EACrB;AAEA,QAAM,WAAW,OAAO,MAAc,aAAwD;AAC5F,WAAO,WAAW,MAAM,UAAU,GAAG;AAAA,EACvC;AAEA,SAAO,SAAS,QAAQ,QAAQ;AAClC;","names":[]}
@@ -0,0 +1,119 @@
1
+ import {
2
+ fetchTableRows,
3
+ mapRowsToCampaignLeads
4
+ } from "./chunk-4IV6QS4U.js";
5
+ import {
6
+ createGtmError
7
+ } from "./chunk-UBJUBYSQ.js";
8
+ import {
9
+ ApiClientError
10
+ } from "./chunk-WKLCPIFB.js";
11
+ import "./chunk-6GLLK5KO.js";
12
+ import "./chunk-PZ5AY32C.js";
13
+
14
+ // src/handlers/campaign.ts
15
+ async function handleCampaign(args, ctx) {
16
+ const action = args.action;
17
+ const channel = args.channel || void 0;
18
+ const providerName = args.provider || void 0;
19
+ let providers;
20
+ let isOutreachProvider = false;
21
+ if (providerName) {
22
+ const p = ctx.registry.get(providerName);
23
+ providers = p ? [p] : [];
24
+ if (providers.length > 0) {
25
+ isOutreachProvider = providers[0].capabilities.includes("outreach_linkedin");
26
+ }
27
+ } else if (channel === "linkedin") {
28
+ providers = ctx.registry.getForCapability("outreach_linkedin");
29
+ isOutreachProvider = providers.length > 0;
30
+ } else if (channel === "email") {
31
+ providers = ctx.registry.getForCapability("campaign_email");
32
+ } else {
33
+ providers = ctx.registry.getForCapability("campaign_email");
34
+ if (providers.length === 0) {
35
+ providers = ctx.registry.getForCapability("outreach_linkedin");
36
+ isOutreachProvider = providers.length > 0;
37
+ }
38
+ }
39
+ if (providers.length === 0) {
40
+ const channelHint = channel ? ` for channel="${channel}"` : "";
41
+ throw createGtmError(
42
+ "PROVIDER_NOT_CONFIGURED",
43
+ `No campaign providers configured${channelHint}. Add a provider with campaign_email or outreach_linkedin capability.`
44
+ );
45
+ }
46
+ const provider = providers[0];
47
+ const manager = provider;
48
+ switch (action) {
49
+ case "list":
50
+ return { provider: provider.name, campaigns: await manager.listCampaigns() };
51
+ case "stats": {
52
+ const campaignId = args.campaign_id;
53
+ if (!campaignId) {
54
+ throw createGtmError("QUERY_INVALID", "campaign_id is required for stats action");
55
+ }
56
+ return manager.getCampaignStats(campaignId);
57
+ }
58
+ case "add_leads": {
59
+ const campaignId = args.campaign_id;
60
+ const tableId = args.table_id;
61
+ if (!campaignId) {
62
+ throw createGtmError("QUERY_INVALID", "campaign_id is required for add_leads action");
63
+ }
64
+ let leads;
65
+ if (tableId) {
66
+ const rows = await fetchTableRows(tableId);
67
+ if (rows.length === 0) {
68
+ return {
69
+ added: 0,
70
+ hints: ["No rows selected in the review table. Select rows in the viewer and retry."]
71
+ };
72
+ }
73
+ leads = mapRowsToCampaignLeads(rows);
74
+ } else {
75
+ leads = args.leads;
76
+ if (!leads?.length) {
77
+ throw createGtmError("QUERY_INVALID", "leads array is required for add_leads action");
78
+ }
79
+ }
80
+ try {
81
+ return await manager.addLeadsToCampaign(campaignId, leads);
82
+ } catch (error) {
83
+ if (error instanceof ApiClientError) {
84
+ throw createGtmError(
85
+ "CAMPAIGN_NOT_FOUND",
86
+ `Campaign '${campaignId}' not found or validation failed. Use action: "list" to see available campaigns.`,
87
+ { details: { campaign_id: campaignId } }
88
+ );
89
+ }
90
+ throw error;
91
+ }
92
+ }
93
+ case "create": {
94
+ if (isOutreachProvider) {
95
+ throw createGtmError(
96
+ "QUERY_INVALID",
97
+ "LinkedIn outreach providers do not support campaign creation. Create campaigns in the HeyReach UI."
98
+ );
99
+ }
100
+ const name = args.name;
101
+ if (!name) {
102
+ throw createGtmError("QUERY_INVALID", "name is required for create action");
103
+ }
104
+ if (!manager.createCampaign) {
105
+ throw createGtmError(
106
+ "QUERY_INVALID",
107
+ `Provider "${provider.name}" does not support campaign creation. Currently only PlusVibe supports this action.`
108
+ );
109
+ }
110
+ return manager.createCampaign(name);
111
+ }
112
+ default:
113
+ throw createGtmError("QUERY_INVALID", `Unknown campaign action: ${action}`);
114
+ }
115
+ }
116
+ export {
117
+ handleCampaign
118
+ };
119
+ //# sourceMappingURL=campaign-XDXQA7KX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/campaign.ts"],"sourcesContent":["/** Campaign handler. Routes cold email and LinkedIn outreach campaign operations to providers.\n * Constraint: No direct API calls. Uses provider registry. */\n\nimport { ApiClientError } from '@maestro/integrations';\n\nimport { createGtmError } from '../errors.js';\nimport { fetchTableRows, mapRowsToCampaignLeads } from '../utils/table-bridge.js';\n\nimport type { HandlerContext } from './index.js';\nimport type {\n CampaignManager,\n CampaignLead,\n OutreachManager,\n Provider,\n} from '../providers/types.js';\n\n// ─── Types ──────────────────────────────────────────────\n\n/** Extended CampaignManager with optional create support. */\ninterface CampaignCreator {\n createCampaign?(\n name: string\n ): Promise<{ id: string; name: string; status: string; provider: string }>;\n}\n\n// ─── Handler ─────────────────────────────────────────────\n\nexport async function handleCampaign(\n args: Record<string, unknown>,\n ctx: HandlerContext\n): Promise<unknown> {\n const action = args.action as string;\n const channel = (args.channel as 'email' | 'linkedin') || undefined;\n const providerName = (args.provider as string) || undefined;\n\n // Resolve providers: explicit channel > explicit provider > auto-detect\n let providers: ReturnType<typeof ctx.registry.getForCapability>;\n let isOutreachProvider = false;\n\n if (providerName) {\n const p = ctx.registry.get(providerName);\n providers = p ? [p] : ([] as Provider[]);\n if (providers.length > 0) {\n isOutreachProvider = providers[0]!.capabilities.includes('outreach_linkedin');\n }\n } else if (channel === 'linkedin') {\n providers = ctx.registry.getForCapability('outreach_linkedin');\n isOutreachProvider = providers.length > 0;\n } else if (channel === 'email') {\n providers = ctx.registry.getForCapability('campaign_email');\n } else {\n // Auto-detect: try email first, fall back to linkedin\n providers = ctx.registry.getForCapability('campaign_email');\n if (providers.length === 0) {\n providers = ctx.registry.getForCapability('outreach_linkedin');\n isOutreachProvider = providers.length > 0;\n }\n }\n\n if (providers.length === 0) {\n const channelHint = channel ? ` for channel=\"${channel}\"` : '';\n throw createGtmError(\n 'PROVIDER_NOT_CONFIGURED',\n `No campaign providers configured${channelHint}. Add a provider with campaign_email or outreach_linkedin capability.`\n );\n }\n\n const provider = providers[0]!;\n const manager = provider as unknown as CampaignManager & OutreachManager & CampaignCreator;\n\n switch (action) {\n case 'list':\n return { provider: provider.name, campaigns: await manager.listCampaigns() };\n\n case 'stats': {\n const campaignId = args.campaign_id as string;\n if (!campaignId) {\n throw createGtmError('QUERY_INVALID', 'campaign_id is required for stats action');\n }\n return manager.getCampaignStats(campaignId);\n }\n\n case 'add_leads': {\n const campaignId = args.campaign_id as string;\n const tableId = args.table_id as string | undefined;\n\n if (!campaignId) {\n throw createGtmError('QUERY_INVALID', 'campaign_id is required for add_leads action');\n }\n\n // Resolve leads from table_id or inline leads array\n let leads: CampaignLead[];\n if (tableId) {\n const rows = await fetchTableRows(tableId);\n if (rows.length === 0) {\n return {\n added: 0,\n hints: ['No rows selected in the review table. Select rows in the viewer and retry.'],\n };\n }\n leads = mapRowsToCampaignLeads(rows) as CampaignLead[];\n } else {\n leads = args.leads as CampaignLead[];\n if (!leads?.length) {\n throw createGtmError('QUERY_INVALID', 'leads array is required for add_leads action');\n }\n }\n\n try {\n return await manager.addLeadsToCampaign(campaignId, leads);\n } catch (error) {\n if (error instanceof ApiClientError) {\n throw createGtmError(\n 'CAMPAIGN_NOT_FOUND',\n `Campaign '${campaignId}' not found or validation failed. Use action: \"list\" to see available campaigns.`,\n { details: { campaign_id: campaignId } }\n );\n }\n throw error;\n }\n }\n\n case 'create': {\n if (isOutreachProvider) {\n throw createGtmError(\n 'QUERY_INVALID',\n 'LinkedIn outreach providers do not support campaign creation. Create campaigns in the HeyReach UI.'\n );\n }\n\n const name = args.name as string;\n if (!name) {\n throw createGtmError('QUERY_INVALID', 'name is required for create action');\n }\n\n if (!manager.createCampaign) {\n throw createGtmError(\n 'QUERY_INVALID',\n `Provider \"${provider.name}\" does not support campaign creation. ` +\n 'Currently only PlusVibe supports this action.'\n );\n }\n\n return manager.createCampaign(name);\n }\n\n default:\n throw createGtmError('QUERY_INVALID', `Unknown campaign action: ${action}`);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ import {
2
+ resolveClient
3
+ } from "./chunk-JFSKOY7Z.js";
4
+
5
+ // src/context.ts
6
+ var SessionContext = class {
7
+ activeClientName;
8
+ activeClientConfig;
9
+ config;
10
+ constructor(config) {
11
+ this.config = config;
12
+ this.activeClientName = config.default_client;
13
+ this.activeClientConfig = resolveClient(config, config.default_client);
14
+ }
15
+ // ─── Queries ───────────────────────────────────────────
16
+ getActiveContext() {
17
+ return {
18
+ clientName: this.activeClientName,
19
+ config: this.activeClientConfig
20
+ };
21
+ }
22
+ listClients() {
23
+ return Object.keys(this.config.clients).map((name) => {
24
+ const resolved = resolveClient(this.config, name);
25
+ return {
26
+ name,
27
+ active: name === this.activeClientName,
28
+ providerCount: Object.keys(resolved.providers).length
29
+ };
30
+ });
31
+ }
32
+ // ─── Mutations ─────────────────────────────────────────
33
+ switchClient(name) {
34
+ try {
35
+ const resolved = resolveClient(this.config, name);
36
+ this.activeClientName = name;
37
+ this.activeClientConfig = resolved;
38
+ return { success: true, clientName: name };
39
+ } catch (error) {
40
+ return {
41
+ success: false,
42
+ error: error instanceof Error ? error.message : String(error)
43
+ };
44
+ }
45
+ }
46
+ addClient(name, overrides) {
47
+ this.config.clients[name] = overrides;
48
+ }
49
+ };
50
+
51
+ export {
52
+ SessionContext
53
+ };
54
+ //# sourceMappingURL=chunk-365Q36GF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context.ts"],"sourcesContent":["/** SessionContext. Manages active client state for the MCP session.\n * Constraint: In-memory only. No persistence beyond session. Class-based for testability. */\n\nimport { resolveClient } from './config.js';\n\nimport type { GtmConfig, ClientConfig } from './config.js';\n\n// ─── Types ───────────────────────────────────────────────\n\nexport interface ActiveContext {\n clientName: string;\n config: ClientConfig;\n}\n\nexport interface SwitchResult {\n success: boolean;\n clientName?: string;\n error?: string;\n}\n\nexport interface ClientSummary {\n name: string;\n active: boolean;\n providerCount: number;\n}\n\n// ─── Session Context ─────────────────────────────────────\n\nexport class SessionContext {\n private activeClientName: string;\n private activeClientConfig: ClientConfig;\n private config: GtmConfig;\n\n constructor(config: GtmConfig) {\n this.config = config;\n this.activeClientName = config.default_client;\n this.activeClientConfig = resolveClient(config, config.default_client);\n }\n\n // ─── Queries ───────────────────────────────────────────\n\n getActiveContext(): ActiveContext {\n return {\n clientName: this.activeClientName,\n config: this.activeClientConfig,\n };\n }\n\n listClients(): ClientSummary[] {\n return Object.keys(this.config.clients).map((name) => {\n const resolved = resolveClient(this.config, name);\n return {\n name,\n active: name === this.activeClientName,\n providerCount: Object.keys(resolved.providers).length,\n };\n });\n }\n\n // ─── Mutations ─────────────────────────────────────────\n\n switchClient(name: string): SwitchResult {\n try {\n const resolved = resolveClient(this.config, name);\n this.activeClientName = name;\n this.activeClientConfig = resolved;\n return { success: true, clientName: name };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n addClient(name: string, overrides: Partial<ClientConfig>): void {\n this.config.clients[name] = overrides;\n }\n}\n"],"mappings":";;;;;AA4BO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAAmB;AAC7B,SAAK,SAAS;AACd,SAAK,mBAAmB,OAAO;AAC/B,SAAK,qBAAqB,cAAc,QAAQ,OAAO,cAAc;AAAA,EACvE;AAAA;AAAA,EAIA,mBAAkC;AAChC,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,cAA+B;AAC7B,WAAO,OAAO,KAAK,KAAK,OAAO,OAAO,EAAE,IAAI,CAAC,SAAS;AACpD,YAAM,WAAW,cAAc,KAAK,QAAQ,IAAI;AAChD,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,SAAS,KAAK;AAAA,QACtB,eAAe,OAAO,KAAK,SAAS,SAAS,EAAE;AAAA,MACjD;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,aAAa,MAA4B;AACvC,QAAI;AACF,YAAM,WAAW,cAAc,KAAK,QAAQ,IAAI;AAChD,WAAK,mBAAmB;AACxB,WAAK,qBAAqB;AAC1B,aAAO,EAAE,SAAS,MAAM,YAAY,KAAK;AAAA,IAC3C,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,MAAc,WAAwC;AAC9D,SAAK,OAAO,QAAQ,IAAI,IAAI;AAAA,EAC9B;AACF;","names":[]}
@@ -0,0 +1,122 @@
1
+ import {
2
+ logError
3
+ } from "./chunk-6GLLK5KO.js";
4
+
5
+ // src/utils/table-bridge.ts
6
+ var VIEWER_URL = process.env.GTM_VIEWER_URL ?? "https://gtm-viewer-production.up.railway.app";
7
+ function asString(value) {
8
+ return typeof value === "string" && value.length > 0 ? value : void 0;
9
+ }
10
+ function splitName(full) {
11
+ const idx = full.indexOf(" ");
12
+ if (idx === -1) return { first_name: full, last_name: "" };
13
+ return { first_name: full.slice(0, idx), last_name: full.slice(idx + 1) };
14
+ }
15
+ async function createReviewTable(title, data, options) {
16
+ try {
17
+ const rows = data.map((row, i) => ({
18
+ _row_id: `r${i + 1}`,
19
+ ...row
20
+ }));
21
+ const body = { title, rows };
22
+ if (options?.table_id !== void 0) body.table_id = options.table_id;
23
+ if (options?.ttl_seconds !== void 0) body.ttl_seconds = options.ttl_seconds;
24
+ const response = await fetch(`${VIEWER_URL}/api/tables`, {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify(body)
28
+ });
29
+ if (!response.ok) {
30
+ logError("table-bridge.createReviewTable", new Error(`Viewer returned ${response.status}`), {
31
+ title,
32
+ status: response.status
33
+ });
34
+ return null;
35
+ }
36
+ return await response.json();
37
+ } catch (error) {
38
+ logError("table-bridge.createReviewTable", error, { title });
39
+ return null;
40
+ }
41
+ }
42
+ async function fetchTableRows(tableId) {
43
+ try {
44
+ const response = await fetch(`${VIEWER_URL}/api/tables/${tableId}/selected`);
45
+ if (!response.ok) {
46
+ logError("table-bridge.fetchTableRows", new Error(`Viewer returned ${response.status}`), {
47
+ tableId,
48
+ status: response.status
49
+ });
50
+ return [];
51
+ }
52
+ const json = await response.json();
53
+ return json.rows ?? [];
54
+ } catch (error) {
55
+ logError("table-bridge.fetchTableRows", error, { tableId });
56
+ return [];
57
+ }
58
+ }
59
+ async function patchTableRows(tableId, rows) {
60
+ try {
61
+ const response = await fetch(`${VIEWER_URL}/api/tables/${tableId}`, {
62
+ method: "PATCH",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({ rows })
65
+ });
66
+ if (!response.ok) {
67
+ logError("table-bridge.patchTableRows", new Error(`Viewer returned ${response.status}`), {
68
+ tableId,
69
+ status: response.status
70
+ });
71
+ return false;
72
+ }
73
+ return true;
74
+ } catch (error) {
75
+ logError("table-bridge.patchTableRows", error, { tableId });
76
+ return false;
77
+ }
78
+ }
79
+ function mapRowsToContacts(rows) {
80
+ return rows.map((row) => {
81
+ let firstName = asString(row.first_name);
82
+ let lastName = asString(row.last_name);
83
+ if (firstName === void 0 || lastName === void 0) {
84
+ const fullName = asString(row.name) ?? asString(row.full_name);
85
+ if (fullName) {
86
+ const parts = splitName(fullName);
87
+ firstName ??= parts.first_name;
88
+ lastName ??= parts.last_name;
89
+ }
90
+ }
91
+ return {
92
+ first_name: firstName ?? "",
93
+ last_name: lastName ?? "",
94
+ company: asString(row.company) ?? asString(row.company_name),
95
+ company_domain: asString(row.company_domain) ?? asString(row.domain),
96
+ linkedin_url: asString(row.linkedin_url) ?? asString(row.linkedin)
97
+ };
98
+ });
99
+ }
100
+ function mapRowsToCampaignLeads(rows) {
101
+ const leads = [];
102
+ for (const row of rows) {
103
+ const email = asString(row.email);
104
+ if (!email || email.includes("*")) continue;
105
+ leads.push({
106
+ email,
107
+ first_name: asString(row.first_name),
108
+ last_name: asString(row.last_name),
109
+ company: asString(row.company) ?? asString(row.company_name)
110
+ });
111
+ }
112
+ return leads;
113
+ }
114
+
115
+ export {
116
+ createReviewTable,
117
+ fetchTableRows,
118
+ patchTableRows,
119
+ mapRowsToContacts,
120
+ mapRowsToCampaignLeads
121
+ };
122
+ //# sourceMappingURL=chunk-4IV6QS4U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/table-bridge.ts"],"sourcesContent":["/** Table bridge. Centralizes all viewer communication for the pipeline.\n * Constraint: No handler logic. Pure fetch/map utilities.\n * All viewer HTTP calls go through this file. */\n\nimport { logError } from '@maestro/logging';\nimport type { ContactInput, CampaignLead } from '../providers/types.js';\n\n// ─── Types ──────────────────────────────────────────────────────\n\nexport interface ViewerRow {\n _row_id: string;\n _selected?: boolean;\n [key: string]: unknown;\n}\n\nexport interface CreateTableResult {\n table_id: string;\n url: string;\n rows: number;\n}\n\n// ─── Config ─────────────────────────────────────────────────────\n\nconst VIEWER_URL = process.env.GTM_VIEWER_URL ?? 'https://gtm-viewer-production.up.railway.app';\n\n// ─── Helpers ────────────────────────────────────────────────────\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction splitName(full: string): { first_name: string; last_name: string } {\n const idx = full.indexOf(' ');\n if (idx === -1) return { first_name: full, last_name: '' };\n return { first_name: full.slice(0, idx), last_name: full.slice(idx + 1) };\n}\n\n// ─── Public API ─────────────────────────────────────────────────\n\n/**\n * POST to the viewer to create a review table.\n * Returns { table_id, url, rows } on success, null on failure.\n * Non-blocking — callers should tolerate null.\n */\nexport async function createReviewTable(\n title: string,\n data: Record<string, unknown>[],\n options?: { table_id?: string; ttl_seconds?: number }\n): Promise<CreateTableResult | null> {\n try {\n const rows = data.map((row, i) => ({\n _row_id: `r${i + 1}`,\n ...row,\n }));\n\n const body: Record<string, unknown> = { title, rows };\n if (options?.table_id !== undefined) body.table_id = options.table_id;\n if (options?.ttl_seconds !== undefined) body.ttl_seconds = options.ttl_seconds;\n\n const response = await fetch(`${VIEWER_URL}/api/tables`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n logError('table-bridge.createReviewTable', new Error(`Viewer returned ${response.status}`), {\n title,\n status: response.status,\n });\n return null;\n }\n\n return (await response.json()) as CreateTableResult;\n } catch (error) {\n logError('table-bridge.createReviewTable', error, { title });\n return null;\n }\n}\n\n/**\n * GET selected rows from the viewer.\n * Returns ViewerRow[] on success, empty array on failure.\n * Non-blocking — callers should tolerate empty arrays.\n */\nexport async function fetchTableRows(tableId: string): Promise<ViewerRow[]> {\n try {\n const response = await fetch(`${VIEWER_URL}/api/tables/${tableId}/selected`);\n\n if (!response.ok) {\n logError('table-bridge.fetchTableRows', new Error(`Viewer returned ${response.status}`), {\n tableId,\n status: response.status,\n });\n return [];\n }\n\n const json = (await response.json()) as { rows?: ViewerRow[] };\n return json.rows ?? [];\n } catch (error) {\n logError('table-bridge.fetchTableRows', error, { tableId });\n return [];\n }\n}\n\n/**\n * PATCH rows in the viewer (merge by _row_id).\n * Returns true on success, false on failure.\n * Non-blocking — callers should tolerate false.\n */\nexport async function patchTableRows(tableId: string, rows: ViewerRow[]): Promise<boolean> {\n try {\n const response = await fetch(`${VIEWER_URL}/api/tables/${tableId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ rows }),\n });\n\n if (!response.ok) {\n logError('table-bridge.patchTableRows', new Error(`Viewer returned ${response.status}`), {\n tableId,\n status: response.status,\n });\n return false;\n }\n\n return true;\n } catch (error) {\n logError('table-bridge.patchTableRows', error, { tableId });\n return false;\n }\n}\n\n/**\n * Map viewer rows to ContactInput[].\n * Applies column aliases and splits full name if first/last are absent.\n * Does NOT filter masked emails — enrichment handles that downstream.\n */\nexport function mapRowsToContacts(rows: ViewerRow[]): ContactInput[] {\n return rows.map((row) => {\n let firstName = asString(row.first_name);\n let lastName = asString(row.last_name);\n\n if (firstName === undefined || lastName === undefined) {\n const fullName = asString(row.name) ?? asString(row.full_name);\n if (fullName) {\n const parts = splitName(fullName);\n firstName ??= parts.first_name;\n lastName ??= parts.last_name;\n }\n }\n\n return {\n first_name: firstName ?? '',\n last_name: lastName ?? '',\n company: asString(row.company) ?? asString(row.company_name),\n company_domain: asString(row.company_domain) ?? asString(row.domain),\n linkedin_url: asString(row.linkedin_url) ?? asString(row.linkedin),\n };\n });\n}\n\n/**\n * Map viewer rows to CampaignLead[].\n * Skips rows without an email or with a masked email (contains '*').\n */\nexport function mapRowsToCampaignLeads(rows: ViewerRow[]): CampaignLead[] {\n const leads: CampaignLead[] = [];\n\n for (const row of rows) {\n const email = asString(row.email);\n if (!email || email.includes('*')) continue;\n\n leads.push({\n email,\n first_name: asString(row.first_name),\n last_name: asString(row.last_name),\n company: asString(row.company) ?? asString(row.company_name),\n });\n }\n\n return leads;\n}\n"],"mappings":"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","names":[]}