@kairos-sdk/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +131 -16
  2. package/dist/{chunk-KIFT5LA7.js → chunk-2ZHNO37N.js} +49 -5
  3. package/dist/chunk-2ZHNO37N.js.map +1 -0
  4. package/dist/chunk-GG4B4TYG.js +153 -0
  5. package/dist/chunk-GG4B4TYG.js.map +1 -0
  6. package/dist/{chunk-5GAY7CSJ.js → chunk-PCNW5ZUD.js} +2 -2
  7. package/dist/chunk-SC6CLQZB.js +144 -0
  8. package/dist/chunk-SC6CLQZB.js.map +1 -0
  9. package/dist/chunk-SQS4QHDH.js +44 -0
  10. package/dist/chunk-SQS4QHDH.js.map +1 -0
  11. package/dist/{chunk-EVOAYH2K.js → chunk-STG7Z2SS.js} +2 -2
  12. package/dist/{chunk-HBGZTUUZ.js → chunk-YOQTEVDB.js} +5 -7
  13. package/dist/chunk-YOQTEVDB.js.map +1 -0
  14. package/dist/cli.cjs +702 -40
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.js +262 -7
  17. package/dist/cli.js.map +1 -1
  18. package/dist/index.cjs +417 -39
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +86 -4
  21. package/dist/index.d.ts +86 -4
  22. package/dist/index.js +19 -5
  23. package/dist/mcp-server.cjs +139 -24
  24. package/dist/mcp-server.cjs.map +1 -1
  25. package/dist/mcp-server.js +90 -19
  26. package/dist/mcp-server.js.map +1 -1
  27. package/dist/pack-builder-RTQWXGIS.js +9 -0
  28. package/dist/pack-builder-RTQWXGIS.js.map +1 -0
  29. package/dist/pack-exporter-KFNLSP5V.js +7 -0
  30. package/dist/pack-exporter-KFNLSP5V.js.map +1 -0
  31. package/dist/pack-validator-HZPB2XJ3.js +7 -0
  32. package/dist/pack-validator-HZPB2XJ3.js.map +1 -0
  33. package/dist/{reader-B5mV20H6.d.ts → reader-CfWGpL4V.d.cts} +2 -1
  34. package/dist/{reader-B5mV20H6.d.cts → reader-CfWGpL4V.d.ts} +2 -1
  35. package/dist/standalone.cjs +44 -4
  36. package/dist/standalone.cjs.map +1 -1
  37. package/dist/standalone.d.cts +1 -1
  38. package/dist/standalone.d.ts +1 -1
  39. package/dist/standalone.js +2 -2
  40. package/package.json +12 -5
  41. package/dist/chunk-HBGZTUUZ.js.map +0 -1
  42. package/dist/chunk-KIFT5LA7.js.map +0 -1
  43. /package/dist/{chunk-5GAY7CSJ.js.map → chunk-PCNW5ZUD.js.map} +0 -0
  44. /package/dist/{chunk-EVOAYH2K.js.map → chunk-STG7Z2SS.js.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PromptBuilder,
4
4
  inferWorkflowType
5
- } from "./chunk-EVOAYH2K.js";
5
+ } from "./chunk-STG7Z2SS.js";
6
6
  import {
7
7
  DEFAULT_REGISTRY,
8
8
  FileLibrary,
@@ -16,11 +16,13 @@ import {
16
16
  TelemetryReader,
17
17
  generateUUID,
18
18
  nullLogger
19
- } from "./chunk-KIFT5LA7.js";
19
+ } from "./chunk-2ZHNO37N.js";
20
20
 
21
21
  // src/mcp-server.ts
22
22
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
23
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
25
+ import { createServer } from "http";
24
26
  import { z } from "zod";
25
27
 
26
28
  // src/validation/node-syncer.ts
@@ -82,10 +84,35 @@ ${regularLines}`;
82
84
 
83
85
  // src/mcp-server.ts
84
86
  import { readFileSync } from "fs";
85
- import { dirname, join } from "path";
87
+ import { dirname as dirname2, join } from "path";
86
88
  import { homedir } from "os";
87
89
  import { fileURLToPath } from "url";
88
- var __dirname = dirname(fileURLToPath(import.meta.url));
90
+
91
+ // src/utils/node-catalog-cache.ts
92
+ import { readFile, writeFile, mkdir } from "fs/promises";
93
+ import { dirname } from "path";
94
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
95
+ async function readCatalogCache(cachePath) {
96
+ try {
97
+ const raw = await readFile(cachePath, "utf-8");
98
+ const cached = JSON.parse(raw);
99
+ if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null;
100
+ return cached.syncResult;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ async function writeCatalogCache(cachePath, syncResult) {
106
+ try {
107
+ await mkdir(dirname(cachePath), { recursive: true });
108
+ const payload = { cachedAt: Date.now(), syncResult };
109
+ await writeFile(cachePath, JSON.stringify(payload), "utf-8");
110
+ } catch {
111
+ }
112
+ }
113
+
114
+ // src/mcp-server.ts
115
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
89
116
  var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
90
117
  var library = new FileLibrary();
91
118
  var _validator = new N8nValidator();
@@ -125,7 +152,14 @@ function getTelemetryReader() {
125
152
  return null;
126
153
  }
127
154
  }
155
+ function getMcpMode() {
156
+ const mode = process.env["KAIROS_MCP_MODE"]?.toLowerCase();
157
+ if (mode === "readonly" || mode === "validate") return mode;
158
+ return "deploy";
159
+ }
128
160
  function isAllowed(action) {
161
+ const mode = getMcpMode();
162
+ if (mode === "readonly" || mode === "validate") return false;
129
163
  const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`;
130
164
  return process.env[key] === "true";
131
165
  }
@@ -149,8 +183,20 @@ function getApiClient() {
149
183
  }
150
184
  return new N8nApiClient(baseUrl, apiKey, nullLogger);
151
185
  }
186
+ function getCatalogCachePath() {
187
+ const telemetry = process.env["KAIROS_TELEMETRY"];
188
+ const base = telemetry ? join(telemetry, "..") : join(homedir(), ".kairos");
189
+ return join(base, "node-catalog-cache.json");
190
+ }
152
191
  async function autoSync() {
153
192
  if (lastSync) return lastSync;
193
+ const cachePath = getCatalogCachePath();
194
+ const cached = await readCatalogCache(cachePath);
195
+ if (cached) {
196
+ lastSync = cached;
197
+ _validator = new N8nValidator(lastSync.registry);
198
+ return lastSync;
199
+ }
154
200
  const baseUrl = process.env["N8N_BASE_URL"];
155
201
  const apiKey = process.env["N8N_API_KEY"];
156
202
  if (!baseUrl || !apiKey) return null;
@@ -160,6 +206,8 @@ async function autoSync() {
160
206
  if (nodeTypes.length === 0) return null;
161
207
  lastSync = nodeSyncer.sync(nodeTypes);
162
208
  _validator = new N8nValidator(lastSync.registry);
209
+ writeCatalogCache(cachePath, lastSync).catch(() => {
210
+ });
163
211
  return lastSync;
164
212
  } catch {
165
213
  return null;
@@ -178,13 +226,9 @@ server.tool(
178
226
  },
179
227
  async ({ description, name }) => {
180
228
  evictStaleSessions();
181
- const baseUrl = process.env["N8N_BASE_URL"];
182
- const apiKey = process.env["N8N_API_KEY"];
183
- if (!baseUrl || !apiKey) {
184
- return mcpError(JSON.stringify({ error: "N8N_BASE_URL and N8N_API_KEY are required. Kairos needs to sync your n8n instance's node types to generate accurate workflows." }));
185
- }
186
229
  const runId = generateUUID();
187
230
  const workflowType = inferWorkflowType(description);
231
+ const hasN8nCreds = !!(process.env["N8N_BASE_URL"] && process.env["N8N_API_KEY"]);
188
232
  const syncPromise = autoSync();
189
233
  const syncTimeout = new Promise((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS));
190
234
  await library.initialize();
@@ -204,7 +248,8 @@ server.tool(
204
248
  startTime: Date.now(),
205
249
  validateAttempts: 0,
206
250
  warnedRules: promptBuilder.getWarnedRules(),
207
- workflowType
251
+ workflowType,
252
+ matchCount: matches.length
208
253
  });
209
254
  await mcpTelemetry.emit("build_start", { description, model: "mcp-decomposed", dryRun: false }, runId);
210
255
  }
@@ -216,7 +261,9 @@ server.tool(
216
261
  topMatchScore: matches[0]?.score ?? null,
217
262
  nodeCatalog: syncResult ? "synced" : "static",
218
263
  nodeCount: syncResult?.nodeCount ?? null,
219
- ...syncResult ? {} : { syncWarning: "Could not sync node types from your n8n instance. Using static fallback catalog \u2014 generated workflows may not match your exact n8n setup." },
264
+ ...syncResult ? {} : {
265
+ syncWarning: hasN8nCreds ? "Could not sync node types from your n8n instance. Using static fallback catalog \u2014 generated workflows may not match your exact n8n setup." : "N8N_BASE_URL and N8N_API_KEY are not set. Using static fallback catalog \u2014 node types may not match your n8n instance. Set these env vars to enable accurate generation and deployment."
266
+ },
220
267
  systemPrompt: systemText,
221
268
  userMessage: built.userMessage,
222
269
  outputFormat: {
@@ -289,10 +336,11 @@ server.tool(
289
336
  {
290
337
  workflow: z.string().describe("The validated workflow JSON string to deploy"),
291
338
  activate: z.boolean().default(false).describe("Activate the workflow immediately after deployment"),
339
+ description: z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
292
340
  kairos_run_id: z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
293
341
  kairos_secret: z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
294
342
  },
295
- async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {
343
+ async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {
296
344
  const authError = checkMcpAuth(kairos_secret);
297
345
  if (authError) return authError;
298
346
  if (!isAllowed("deploy")) {
@@ -333,8 +381,8 @@ server.tool(
333
381
  Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.` : "";
334
382
  await library.initialize();
335
383
  await library.save(parsed, {
336
- description: session?.description ?? parsed.name,
337
- generationMode: "scratch",
384
+ description: session?.description ?? userDescription ?? parsed.name,
385
+ generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
338
386
  generationAttempts: session?.validateAttempts ?? 1,
339
387
  n8nWorkflowId: response.id
340
388
  });
@@ -371,12 +419,16 @@ server.tool(
371
419
  {
372
420
  workflow_id: z.string().describe("The n8n workflow ID to replace"),
373
421
  workflow: z.string().describe("The validated workflow JSON string"),
422
+ description: z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
374
423
  kairos_run_id: z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
375
424
  kairos_secret: z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
376
425
  },
377
- async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {
426
+ async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {
378
427
  const authError = checkMcpAuth(kairos_secret);
379
428
  if (authError) return authError;
429
+ if (!isAllowed("deploy")) {
430
+ return mcpError(JSON.stringify({ error: "Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable." }));
431
+ }
380
432
  let parsed;
381
433
  try {
382
434
  parsed = JSON.parse(workflowStr);
@@ -400,8 +452,8 @@ server.tool(
400
452
  Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found.` : "";
401
453
  await library.initialize();
402
454
  await library.save(parsed, {
403
- description: session?.description ?? parsed.name,
404
- generationMode: "scratch",
455
+ description: session?.description ?? userDescription ?? parsed.name,
456
+ generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
405
457
  generationAttempts: session?.validateAttempts ?? 1,
406
458
  n8nWorkflowId: workflow_id
407
459
  });
@@ -647,8 +699,27 @@ async function main() {
647
699
  "[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set \u2014 kairos_prompt will fail. Set it before using workflow generation tools.\n"
648
700
  );
649
701
  }
650
- const transport = new StdioServerTransport();
651
- await server.connect(transport);
702
+ const useHttp = process.argv.includes("--http");
703
+ if (useHttp) {
704
+ const port = parseInt(process.env["KAIROS_MCP_PORT"] ?? "3000", 10);
705
+ const transport = new StreamableHTTPServerTransport();
706
+ await server.connect(transport);
707
+ const httpServer = createServer(async (req, res) => {
708
+ if (req.method === "GET" || req.method === "POST" || req.method === "DELETE") {
709
+ await transport.handleRequest(req, res);
710
+ } else {
711
+ res.writeHead(405, { "Content-Type": "application/json" });
712
+ res.end(JSON.stringify({ error: "Method not allowed" }));
713
+ }
714
+ });
715
+ httpServer.listen(port, () => {
716
+ process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}
717
+ `);
718
+ });
719
+ } else {
720
+ const transport = new StdioServerTransport();
721
+ await server.connect(transport);
722
+ }
652
723
  }
653
724
  main().catch((err) => {
654
725
  console.error("Kairos MCP server failed to start:", err);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp-server.ts","../src/validation/node-syncer.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Kairos MCP Server — decomposed architecture.\n *\n * The host LLM (Claude, GPT, Gemini, whatever) generates the workflow.\n * Kairos provides the knowledge (system prompt, library, failure patterns)\n * and guardrails (validator, deployer). Zero Anthropic API key needed.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { FileLibrary } from './library/file-library.js'\nimport { N8nValidator } from './validation/validator.js'\nimport { N8nFieldStripper } from './providers/n8n/stripper.js'\nimport { N8nApiClient } from './providers/n8n/api-client.js'\nimport { PromptBuilder } from './generation/prompt-builder.js'\nimport { TelemetryReader } from './telemetry/reader.js'\nimport { PatternAnalyzer } from './telemetry/pattern-analyzer.js'\nimport { NodeSyncer, type SyncResult } from './validation/node-syncer.js'\nimport { TelemetryCollector } from './telemetry/collector.js'\nimport { nullLogger } from './utils/logger.js'\nimport { GuardError } from './errors/guard-error.js'\nimport { generateUUID } from './utils/uuid.js'\nimport { inferWorkflowType } from './utils/workflow-type.js'\nimport type { N8nWorkflow } from './types/workflow.js'\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string }\n\nconst library = new FileLibrary()\nlet _validator = new N8nValidator()\n// Accessor so kairos_sync can swap the registry without callers closing over the old instance\nfunction getValidator(): N8nValidator { return _validator }\nconst nodeSyncer = new NodeSyncer()\nlet lastSync: SyncResult | null = null\nconst AUTO_SYNC_TIMEOUT_MS = 5_000 // cap how long kairos_prompt waits for n8n node sync\nconst stripper = new N8nFieldStripper()\nconst promptBuilder = new PromptBuilder(getMcpPatternsPath())\n\nfunction getMcpTelemetry(): TelemetryCollector | null {\n const val = process.env['KAIROS_TELEMETRY']\n if (!val || val === 'false') return null\n return val === 'true' ? new TelemetryCollector() : new TelemetryCollector(val)\n}\n\n/**\n * Derive the patterns.json path from KAIROS_TELEMETRY so the MCP server's\n * PromptBuilder reads from the same location that PatternAnalyzer.fromEnv() writes to.\n */\nfunction getMcpPatternsPath(): string {\n const val = process.env['KAIROS_TELEMETRY']\n if (val && val !== 'false' && val !== 'true') {\n return join(val, '..', 'patterns.json')\n }\n return join(homedir(), '.kairos', 'patterns.json')\n}\n\nconst mcpTelemetry = getMcpTelemetry()\n\ninterface McpBuildSession {\n description: string\n startTime: number\n validateAttempts: number\n warnedRules: number[]\n workflowType: string | null\n}\nconst mcpSessions = new Map<string, McpBuildSession>()\nconst SESSION_TTL_MS = 60 * 60 * 1000 // 1 hour: abandon sessions not completed by deploy\n\nfunction evictStaleSessions(): void {\n const cutoff = Date.now() - SESSION_TTL_MS\n for (const [id, session] of mcpSessions) {\n if (session.startTime < cutoff) mcpSessions.delete(id)\n }\n}\n\nfunction getTelemetryReader(): TelemetryReader | null {\n try {\n return new TelemetryReader()\n } catch {\n return null\n }\n}\n\nfunction isAllowed(action: 'deploy' | 'activate' | 'delete'): boolean {\n const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`\n return process.env[key] === 'true'\n}\n\ntype McpTextContent = { type: 'text'; text: string }\ntype McpToolResult = { content: McpTextContent[]; isError?: true }\n\nfunction mcpText(text: string): McpToolResult { return { content: [{ type: 'text', text }] } }\nfunction mcpError(text: string): McpToolResult { return { content: [{ type: 'text', text }], isError: true } }\n\n/**\n * Returns an error result if KAIROS_MCP_SECRET is set and the provided secret\n * doesn't match. Returns null if auth passes (no secret configured, or correct secret).\n */\nfunction checkMcpAuth(provided: string | undefined): McpToolResult | null {\n const expected = process.env['KAIROS_MCP_SECRET']\n if (!expected) return null\n if (provided === expected) return null\n return mcpError(JSON.stringify({ error: 'Unauthorized: missing or incorrect kairos_secret' }))\n}\n\nfunction getApiClient(): N8nApiClient {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n throw new GuardError('N8N_BASE_URL and N8N_API_KEY environment variables are required for n8n operations')\n }\n return new N8nApiClient(baseUrl, apiKey, nullLogger)\n}\n\nasync function autoSync(): Promise<SyncResult | null> {\n if (lastSync) return lastSync\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) return null\n try {\n const client = new N8nApiClient(baseUrl, apiKey, nullLogger)\n const nodeTypes = await client.getNodeTypes()\n if (nodeTypes.length === 0) return null\n lastSync = nodeSyncer.sync(nodeTypes)\n _validator = new N8nValidator(lastSync.registry)\n return lastSync\n } catch {\n return null\n }\n}\n\nconst server = new McpServer({\n name: 'kairos',\n version: pkg.version,\n})\n\n// ── Core generation tools (no API key needed) ──────────────────────────────\n\nserver.tool(\n 'kairos_prompt',\n 'Get the specialized n8n workflow generation context. Returns a system prompt with node catalog, connection rules, validation rules, plus library matches and failure patterns for the given description. Feed this to yourself as context, then generate the workflow JSON.',\n {\n description: z.string().describe('Plain-English description of the workflow to build'),\n name: z.string().optional().describe('Optional workflow name override'),\n },\n async ({ description, name }) => {\n evictStaleSessions()\n\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required. Kairos needs to sync your n8n instance\\'s node types to generate accurate workflows.' }))\n }\n\n const runId = generateUUID()\n const workflowType = inferWorkflowType(description)\n\n // Start sync in background — race against timeout so slow n8n instances don't block the prompt\n const syncPromise = autoSync()\n const syncTimeout = new Promise<null>((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS))\n\n await library.initialize()\n const [syncResult, matches, failureRates] = await Promise.all([\n Promise.race([syncPromise, syncTimeout]),\n library.search(description),\n (async () => {\n const reader = getTelemetryReader()\n return reader ? reader.getFailureRates() : []\n })(),\n ])\n\n const request = { description, ...(name ? { name } : {}) }\n const built = promptBuilder.build(request, matches, failureRates, syncResult?.catalogText)\n\n if (mcpTelemetry) {\n mcpSessions.set(runId, {\n description,\n startTime: Date.now(),\n validateAttempts: 0,\n warnedRules: promptBuilder.getWarnedRules(),\n workflowType,\n })\n await mcpTelemetry.emit('build_start', { description, model: 'mcp-decomposed', dryRun: false }, runId)\n }\n\n const systemText = built.system.map(block => block.text).join('\\n\\n---\\n\\n')\n\n return mcpText(JSON.stringify({\n kairos_run_id: runId,\n mode: built.mode,\n matchCount: matches.length,\n topMatchScore: matches[0]?.score ?? null,\n nodeCatalog: syncResult ? 'synced' : 'static',\n nodeCount: syncResult?.nodeCount ?? null,\n ...(syncResult ? {} : { syncWarning: 'Could not sync node types from your n8n instance. Using static fallback catalog — generated workflows may not match your exact n8n setup.' }),\n systemPrompt: systemText,\n userMessage: built.userMessage,\n outputFormat: {\n description: 'Generate a JSON object with this exact structure. The workflow field contains the n8n workflow. credentialsNeeded lists services requiring credentials.',\n schema: {\n workflow: {\n name: 'string — descriptive workflow name',\n nodes: 'array — n8n node objects with id (UUID v4), type, typeVersion, name, position, parameters',\n connections: 'object — keyed by source node NAME, maps to target nodes',\n settings: 'object — include executionOrder: \"v1\"',\n },\n credentialsNeeded: [{\n service: 'string — e.g. \"Slack\"',\n credentialType: 'string — e.g. \"slackOAuth2Api\"',\n description: 'string — what the user needs to set up',\n }],\n },\n },\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_validate',\n 'Validate n8n workflow JSON against 34 structural rules. Returns pass/fail with specific issues. If validation fails, fix the issues and call this again. Errors block deployment; warnings are advisory.',\n {\n workflow: z.string().describe('The workflow JSON string to validate'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n },\n async ({ workflow: workflowStr, kairos_run_id }) => {\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpText(JSON.stringify({ valid: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }, null, 2))\n }\n\n const result = getValidator().validate(parsed)\n const errors = result.issues.filter(i => i.severity === 'error')\n const warnings = result.issues.filter(i => i.severity === 'warn')\n\n if (mcpTelemetry && kairos_run_id) {\n const session = mcpSessions.get(kairos_run_id)\n if (session) {\n session.validateAttempts++\n await mcpTelemetry.emit('generation_attempt', {\n description: session.description,\n attempt: session.validateAttempts,\n temperature: 0,\n durationMs: 0,\n tokensInput: 0,\n tokensOutput: 0,\n validationPassed: result.valid,\n issueCount: result.issues.length,\n issues: result.issues.map(i => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null })),\n workflowType: session.workflowType,\n }, kairos_run_id)\n }\n }\n\n return mcpText(JSON.stringify({\n valid: result.valid,\n errorCount: errors.length,\n warningCount: warnings.length,\n errors: errors.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n warnings: warnings.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n deployable: errors.length === 0,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_deploy',\n 'Deploy a validated workflow to n8n. Pass the workflow JSON that passed kairos_validate. Strips server-assigned fields automatically. Requires N8N_BASE_URL and N8N_API_KEY.',\n {\n workflow: z.string().describe('The validated workflow JSON string to deploy'),\n activate: z.boolean().default(false).describe('Activate the workflow immediately after deployment'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Deploy is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before deploying',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForCreate(parsed)\n const response = await client.createWorkflow(stripped)\n\n if (activate) {\n if (!isAllowed('activate')) {\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: false,\n warning: 'Workflow deployed but activation is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.',\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2))\n }\n await client.activateWorkflow(response.id)\n }\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n\n // Warn when kairos_run_id is provided but no matching session exists — telemetry will be skipped\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.`\n : ''\n\n // Save to library (n8nWorkflowId enables dedup on future redeployment)\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? parsed.name,\n generationMode: 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: response.id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: activate,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_replace',\n 'Replace an existing n8n workflow with a new version. Validates before updating. Use kairos_prompt → kairos_validate → kairos_replace for iteration on existing workflows.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to replace'),\n workflow: z.string().describe('The validated workflow JSON string'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before replacing',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForUpdate(parsed)\n const response = await client.updateWorkflow(workflow_id, stripped)\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found.`\n : ''\n\n // Save to library — D4 dedup updates the existing entry rather than creating a duplicate\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? parsed.name,\n generationMode: 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: workflow_id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_search',\n 'Search the local workflow library for similar past builds. Returns matching workflows with scores, useful for finding examples and reusing patterns.',\n {\n query: z.string().describe('Search query — a workflow description or keywords'),\n limit: z.number().default(5).describe('Maximum number of results'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n const matches = await library.search(query)\n\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n description: m.workflow.description,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n failurePatterns: m.workflow.failurePatterns ?? [],\n })),\n null,\n 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_sync',\n 'Sync the node catalog from your live n8n instance. Fetches all installed node types and versions so Kairos knows exactly what your n8n supports. Automatically called by kairos_prompt when n8n credentials are set, but you can call this manually to force a refresh.',\n {},\n async () => {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required for sync.' }))\n }\n\n lastSync = null\n const result = await autoSync()\n if (!result) {\n return mcpError(JSON.stringify({ error: 'Failed to fetch node types from n8n. Check your credentials and that your instance is running.' }))\n }\n\n return mcpText(JSON.stringify({\n synced: true,\n nodeCount: result.nodeCount,\n newNodes: result.newNodes,\n message: `Synced ${result.nodeCount} node types from your n8n instance (${result.newNodes} not in default catalog).`,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_patterns',\n 'Analyze telemetry data and return failure patterns, build stats, and credential breakdowns. Useful for understanding what goes wrong most often and how to prevent it.',\n {\n days: z.number().default(30).describe('Number of days of telemetry to analyze'),\n limit: z.number().optional().describe('Maximum number of failure patterns to return'),\n },\n async ({ days, limit }) => {\n const analyzer = PatternAnalyzer.fromEnv()\n const analysis = await analyzer.analyzeAndSave(days)\n\n if (limit !== undefined && limit > 0) {\n analysis.topFailureRules = analysis.topFailureRules.slice(0, limit)\n }\n\n return mcpText(JSON.stringify(analysis, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_library',\n 'Browse the local Kairos workflow library. Returns saved workflow metadata. Use the optional query to search, or omit it to list all entries.',\n {\n query: z.string().optional().describe('Optional search query — omit to list all entries'),\n limit: z.number().default(20).describe('Maximum entries to return'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n\n if (query) {\n const matches = await library.search(query)\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n description: m.workflow.description,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n deployCount: m.workflow.deployCount,\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n createdAt: m.workflow.createdAt,\n })),\n null, 2,\n ))\n }\n\n const all = await library.list()\n return mcpText(JSON.stringify(\n all.slice(0, limit).map(w => ({\n id: w.id,\n description: w.description,\n nodeCount: w.workflow.nodes.length,\n nodes: w.workflow.nodes.map(n => n.name),\n deployCount: w.deployCount,\n n8nWorkflowId: w.n8nWorkflowId ?? null,\n timesRetrieved: w.timesRetrieved ?? 0,\n createdAt: w.createdAt,\n })),\n null, 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_outcome',\n 'Record the outcome of a workflow build against a library entry. Trains the pattern learning system to know what works and what fails over time.',\n {\n library_id: z.string().describe('The Kairos library entry ID (returned by kairos_deploy, kairos_replace, or kairos_library)'),\n attempts: z.number().describe('Number of generation+validation attempts before success'),\n first_try_pass: z.boolean().describe('Whether the first attempt passed validation'),\n failed_rules: z.array(z.number()).describe('Validation rule IDs that failed during generation'),\n mode: z.enum(['direct', 'reference']).describe('How the library entry was used during generation'),\n },\n async ({ library_id, attempts, first_try_pass, failed_rules, mode }) => {\n await library.initialize()\n await library.recordOutcome(library_id, {\n attempts,\n firstTryPass: first_try_pass,\n failedRules: failed_rules,\n mode,\n })\n return mcpText(JSON.stringify({ recorded: true, libraryId: library_id }))\n },\n)\n\n// ── n8n management tools (need N8N_BASE_URL + N8N_API_KEY) ─────────────────\n\nserver.tool(\n 'kairos_list',\n 'List all workflows deployed on the connected n8n instance.',\n {},\n async () => {\n const client = getApiClient()\n const workflows = await client.listWorkflows()\n\n return mcpText(JSON.stringify(workflows, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_get',\n 'Get the full JSON definition of a specific workflow by ID.',\n {\n workflow_id: z.string().describe('The n8n workflow ID'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n const workflow = await client.getWorkflow(workflow_id)\n\n return mcpText(JSON.stringify(workflow, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_activate',\n 'Activate a deployed workflow so it starts running on triggers.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to activate'),\n },\n async ({ workflow_id }) => {\n if (!isAllowed('activate')) {\n return mcpError(JSON.stringify({ error: 'Activate is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.activateWorkflow(workflow_id)\n\n return mcpText(`Activated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_deactivate',\n 'Deactivate a running workflow.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to deactivate'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n await client.deactivateWorkflow(workflow_id)\n\n return mcpText(`Deactivated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_delete',\n 'Delete a workflow from n8n. This is irreversible.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to delete'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('delete')) {\n return mcpError(JSON.stringify({ error: 'Delete is disabled. Set KAIROS_MCP_ALLOW_DELETE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.deleteWorkflow(workflow_id)\n\n return mcpText(`Deleted workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_executions',\n 'List recent executions for a workflow, showing status and timing.',\n {\n workflow_id: z.string().optional().describe('Filter to a specific workflow ID (omit for all)'),\n limit: z.number().default(20).describe('Maximum number of executions to return'),\n },\n async ({ workflow_id, limit }) => {\n const client = getApiClient()\n const executions = await client.getExecutions(workflow_id, { limit })\n\n return mcpText(JSON.stringify(executions, null, 2))\n },\n)\n\nasync function main() {\n if (!process.env['ANTHROPIC_API_KEY']) {\n process.stderr.write(\n '[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set — kairos_prompt will fail. Set it before using workflow generation tools.\\n',\n )\n }\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err: unknown) => {\n console.error('Kairos MCP server failed to start:', err)\n process.exit(1)\n})\n","import type { N8nNodeTypeInfo } from '../providers/n8n/types.js'\nimport type { NodeDefinition } from './registry.js'\nimport { NodeRegistry, DEFAULT_REGISTRY } from './registry.js'\n\nconst TRIGGER_PATTERNS = [/trigger/i, /Trigger$/]\n\nexport interface SyncResult {\n registry: NodeRegistry\n catalogText: string\n nodeCount: number\n newNodes: number\n}\n\nexport class NodeSyncer {\n private readonly baseRegistry: Map<string, NodeDefinition>\n\n constructor() {\n this.baseRegistry = new Map(DEFAULT_REGISTRY.map(d => [d.type, d]))\n }\n\n sync(liveNodes: N8nNodeTypeInfo[]): SyncResult {\n const merged = new Map(this.baseRegistry)\n let newNodes = 0\n\n for (const node of liveNodes) {\n const versions = Array.isArray(node.version) ? node.version : [node.version]\n const isTrigger = TRIGGER_PATTERNS.some(p => p.test(node.name))\n const credentialType = node.credentials?.[0]?.name\n\n const existing = merged.get(node.name)\n if (existing) {\n const allVersions = new Set([...existing.safeTypeVersions, ...versions])\n merged.set(node.name, {\n ...existing,\n safeTypeVersions: [...allVersions].sort((a, b) => a - b),\n })\n } else {\n newNodes++\n merged.set(node.name, {\n type: node.name,\n safeTypeVersions: versions.sort((a, b) => a - b),\n requiredParams: [],\n ...(credentialType ? { credentialType } : {}),\n ...(isTrigger ? { isTrigger: true } : {}),\n })\n }\n }\n\n const definitions = [...merged.values()]\n const registry = new NodeRegistry(definitions)\n const catalogText = this.buildCatalog(definitions)\n\n return { registry, catalogText, nodeCount: definitions.length, newNodes }\n }\n\n private buildCatalog(definitions: NodeDefinition[]): string {\n const triggers = definitions.filter(d => d.isTrigger)\n const regular = definitions.filter(d => !d.isTrigger)\n\n const formatEntry = (d: NodeDefinition): string => {\n const versions = d.safeTypeVersions.join(', ')\n const cred = d.credentialType ? ` — cred: ${d.credentialType}` : ''\n return `${d.type} typeVersion: ${versions}${cred}`\n }\n\n const triggerLines = triggers.map(formatEntry).join('\\n')\n const regularLines = regular.map(formatEntry).join('\\n')\n\n return `## NODE CATALOG — synced from your n8n instance (${definitions.length} node types)\n\n### Triggers:\n${triggerLines}\n\n### Regular nodes:\n${regularLines}`\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAUA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,SAAS;;;ACRlB,IAAM,mBAAmB,CAAC,YAAY,UAAU;AASzC,IAAM,aAAN,MAAiB;AAAA,EACL;AAAA,EAEjB,cAAc;AACZ,SAAK,eAAe,IAAI,IAAI,iBAAiB,IAAI,OAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAAA,EACpE;AAAA,EAEA,KAAK,WAA0C;AAC7C,UAAM,SAAS,IAAI,IAAI,KAAK,YAAY;AACxC,QAAI,WAAW;AAEf,eAAW,QAAQ,WAAW;AAC5B,YAAM,WAAW,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO;AAC3E,YAAM,YAAY,iBAAiB,KAAK,OAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAC9D,YAAM,iBAAiB,KAAK,cAAc,CAAC,GAAG;AAE9C,YAAM,WAAW,OAAO,IAAI,KAAK,IAAI;AACrC,UAAI,UAAU;AACZ,cAAM,cAAc,oBAAI,IAAI,CAAC,GAAG,SAAS,kBAAkB,GAAG,QAAQ,CAAC;AACvE,eAAO,IAAI,KAAK,MAAM;AAAA,UACpB,GAAG;AAAA,UACH,kBAAkB,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,QACzD,CAAC;AAAA,MACH,OAAO;AACL;AACA,eAAO,IAAI,KAAK,MAAM;AAAA,UACpB,MAAM,KAAK;AAAA,UACX,kBAAkB,SAAS,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,UAC/C,gBAAgB,CAAC;AAAA,UACjB,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,UAC3C,GAAI,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,QACzC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,GAAG,OAAO,OAAO,CAAC;AACvC,UAAM,WAAW,IAAI,aAAa,WAAW;AAC7C,UAAM,cAAc,KAAK,aAAa,WAAW;AAEjD,WAAO,EAAE,UAAU,aAAa,WAAW,YAAY,QAAQ,SAAS;AAAA,EAC1E;AAAA,EAEQ,aAAa,aAAuC;AAC1D,UAAM,WAAW,YAAY,OAAO,OAAK,EAAE,SAAS;AACpD,UAAM,UAAU,YAAY,OAAO,OAAK,CAAC,EAAE,SAAS;AAEpD,UAAM,cAAc,CAAC,MAA8B;AACjD,YAAM,WAAW,EAAE,iBAAiB,KAAK,IAAI;AAC7C,YAAM,OAAO,EAAE,iBAAiB,iBAAY,EAAE,cAAc,KAAK;AACjE,aAAO,GAAG,EAAE,IAAI,kBAAkB,QAAQ,GAAG,IAAI;AAAA,IACnD;AAEA,UAAM,eAAe,SAAS,IAAI,WAAW,EAAE,KAAK,IAAI;AACxD,UAAM,eAAe,QAAQ,IAAI,WAAW,EAAE,KAAK,IAAI;AAEvD,WAAO,yDAAoD,YAAY,MAAM;AAAA;AAAA;AAAA,EAG/E,YAAY;AAAA;AAAA;AAAA,EAGZ,YAAY;AAAA,EACZ;AACF;;;ADjDA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,MAAM,KAAK,MAAM,aAAa,KAAK,WAAW,MAAM,cAAc,GAAG,OAAO,CAAC;AAEnF,IAAM,UAAU,IAAI,YAAY;AAChC,IAAI,aAAa,IAAI,aAAa;AAElC,SAAS,eAA6B;AAAE,SAAO;AAAW;AAC1D,IAAM,aAAa,IAAI,WAAW;AAClC,IAAI,WAA8B;AAClC,IAAM,uBAAuB;AAC7B,IAAM,WAAW,IAAI,iBAAiB;AACtC,IAAM,gBAAgB,IAAI,cAAc,mBAAmB,CAAC;AAE5D,SAAS,kBAA6C;AACpD,QAAM,MAAM,QAAQ,IAAI,kBAAkB;AAC1C,MAAI,CAAC,OAAO,QAAQ,QAAS,QAAO;AACpC,SAAO,QAAQ,SAAS,IAAI,mBAAmB,IAAI,IAAI,mBAAmB,GAAG;AAC/E;AAMA,SAAS,qBAA6B;AACpC,QAAM,MAAM,QAAQ,IAAI,kBAAkB;AAC1C,MAAI,OAAO,QAAQ,WAAW,QAAQ,QAAQ;AAC5C,WAAO,KAAK,KAAK,MAAM,eAAe;AAAA,EACxC;AACA,SAAO,KAAK,QAAQ,GAAG,WAAW,eAAe;AACnD;AAEA,IAAM,eAAe,gBAAgB;AASrC,IAAM,cAAc,oBAAI,IAA6B;AACrD,IAAM,iBAAiB,KAAK,KAAK;AAEjC,SAAS,qBAA2B;AAClC,QAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,aAAW,CAAC,IAAI,OAAO,KAAK,aAAa;AACvC,QAAI,QAAQ,YAAY,OAAQ,aAAY,OAAO,EAAE;AAAA,EACvD;AACF;AAEA,SAAS,qBAA6C;AACpD,MAAI;AACF,WAAO,IAAI,gBAAgB;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,QAAmD;AACpE,QAAM,MAAM,oBAAoB,OAAO,YAAY,CAAC;AACpD,SAAO,QAAQ,IAAI,GAAG,MAAM;AAC9B;AAKA,SAAS,QAAQ,MAA6B;AAAE,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,EAAE;AAAE;AAC7F,SAAS,SAAS,MAA6B;AAAE,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,GAAG,SAAS,KAAK;AAAE;AAM7G,SAAS,aAAa,UAAoD;AACxE,QAAM,WAAW,QAAQ,IAAI,mBAAmB;AAChD,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,aAAa,SAAU,QAAO;AAClC,SAAO,SAAS,KAAK,UAAU,EAAE,OAAO,mDAAmD,CAAC,CAAC;AAC/F;AAEA,SAAS,eAA6B;AACpC,QAAM,UAAU,QAAQ,IAAI,cAAc;AAC1C,QAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,MAAI,CAAC,WAAW,CAAC,QAAQ;AACvB,UAAM,IAAI,WAAW,oFAAoF;AAAA,EAC3G;AACA,SAAO,IAAI,aAAa,SAAS,QAAQ,UAAU;AACrD;AAEA,eAAe,WAAuC;AACpD,MAAI,SAAU,QAAO;AACrB,QAAM,UAAU,QAAQ,IAAI,cAAc;AAC1C,QAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,MAAI,CAAC,WAAW,CAAC,OAAQ,QAAO;AAChC,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,SAAS,QAAQ,UAAU;AAC3D,UAAM,YAAY,MAAM,OAAO,aAAa;AAC5C,QAAI,UAAU,WAAW,EAAG,QAAO;AACnC,eAAW,WAAW,KAAK,SAAS;AACpC,iBAAa,IAAI,aAAa,SAAS,QAAQ;AAC/C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS,IAAI;AACf,CAAC;AAID,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,oDAAoD;AAAA,IACrF,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,EACxE;AAAA,EACA,OAAO,EAAE,aAAa,KAAK,MAAM;AAC/B,uBAAmB;AAEnB,UAAM,UAAU,QAAQ,IAAI,cAAc;AAC1C,UAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,QAAI,CAAC,WAAW,CAAC,QAAQ;AACvB,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,iIAAkI,CAAC,CAAC;AAAA,IAC9K;AAEA,UAAM,QAAQ,aAAa;AAC3B,UAAM,eAAe,kBAAkB,WAAW;AAGlD,UAAM,cAAc,SAAS;AAC7B,UAAM,cAAc,IAAI,QAAc,CAAC,YAAY,WAAW,MAAM,QAAQ,IAAI,GAAG,oBAAoB,CAAC;AAExG,UAAM,QAAQ,WAAW;AACzB,UAAM,CAAC,YAAY,SAAS,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC5D,QAAQ,KAAK,CAAC,aAAa,WAAW,CAAC;AAAA,MACvC,QAAQ,OAAO,WAAW;AAAA,OACzB,YAAY;AACX,cAAM,SAAS,mBAAmB;AAClC,eAAO,SAAS,OAAO,gBAAgB,IAAI,CAAC;AAAA,MAC9C,GAAG;AAAA,IACL,CAAC;AAED,UAAM,UAAU,EAAE,aAAa,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,EAAG;AACzD,UAAM,QAAQ,cAAc,MAAM,SAAS,SAAS,cAAc,YAAY,WAAW;AAEzF,QAAI,cAAc;AAChB,kBAAY,IAAI,OAAO;AAAA,QACrB;AAAA,QACA,WAAW,KAAK,IAAI;AAAA,QACpB,kBAAkB;AAAA,QAClB,aAAa,cAAc,eAAe;AAAA,QAC1C;AAAA,MACF,CAAC;AACD,YAAM,aAAa,KAAK,eAAe,EAAE,aAAa,OAAO,kBAAkB,QAAQ,MAAM,GAAG,KAAK;AAAA,IACvG;AAEA,UAAM,aAAa,MAAM,OAAO,IAAI,WAAS,MAAM,IAAI,EAAE,KAAK,aAAa;AAE3E,WAAO,QAAQ,KAAK,UAAU;AAAA,MACxB,eAAe;AAAA,MACf,MAAM,MAAM;AAAA,MACZ,YAAY,QAAQ;AAAA,MACpB,eAAe,QAAQ,CAAC,GAAG,SAAS;AAAA,MACpC,aAAa,aAAa,WAAW;AAAA,MACrC,WAAW,YAAY,aAAa;AAAA,MACpC,GAAI,aAAa,CAAC,IAAI,EAAE,aAAa,iJAA4I;AAAA,MACjL,cAAc;AAAA,MACd,aAAa,MAAM;AAAA,MACnB,cAAc;AAAA,QACZ,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,UAAU;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,UAAU;AAAA,UACZ;AAAA,UACA,mBAAmB,CAAC;AAAA,YAClB,SAAS;AAAA,YACT,gBAAgB;AAAA,YAChB,aAAa;AAAA,UACf,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,GAAG,MAAM,CAAC,CAAC;AAAA,EACjB;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,UAAU,EAAE,OAAO,EAAE,SAAS,sCAAsC;AAAA,IACpE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAA2D;AAAA,EAC3G;AAAA,EACA,OAAO,EAAE,UAAU,aAAa,cAAc,MAAM;AAClD,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,SAAS,GAAG;AACV,aAAO,QAAQ,KAAK,UAAU,EAAE,OAAO,OAAO,OAAO,iBAAiB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;AAAA,IAChI;AAEA,UAAM,SAAS,aAAa,EAAE,SAAS,MAAM;AAC7C,UAAM,SAAS,OAAO,OAAO,OAAO,OAAK,EAAE,aAAa,OAAO;AAC/D,UAAM,WAAW,OAAO,OAAO,OAAO,OAAK,EAAE,aAAa,MAAM;AAEhE,QAAI,gBAAgB,eAAe;AACjC,YAAM,UAAU,YAAY,IAAI,aAAa;AAC7C,UAAI,SAAS;AACX,gBAAQ;AACR,cAAM,aAAa,KAAK,sBAAsB;AAAA,UAC5C,aAAa,QAAQ;AAAA,UACrB,SAAS,QAAQ;AAAA,UACjB,aAAa;AAAA,UACb,YAAY;AAAA,UACZ,aAAa;AAAA,UACb,cAAc;AAAA,UACd,kBAAkB,OAAO;AAAA,UACzB,YAAY,OAAO,OAAO;AAAA,UAC1B,QAAQ,OAAO,OAAO,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,UAAU,EAAE,UAAU,SAAS,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK,EAAE;AAAA,UACrH,cAAc,QAAQ;AAAA,QACxB,GAAG,aAAa;AAAA,MAClB;AAAA,IACF;AAEA,WAAO,QAAQ,KAAK,UAAU;AAAA,MAC5B,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,cAAc,SAAS;AAAA,MACvB,QAAQ,OAAO,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK,EAAE;AAAA,MACxF,UAAU,SAAS,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK,EAAE;AAAA,MAC5F,YAAY,OAAO,WAAW;AAAA,IAChC,GAAG,MAAM,CAAC,CAAC;AAAA,EACb;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,UAAU,EAAE,OAAO,EAAE,SAAS,8CAA8C;AAAA,IAC5E,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK,EAAE,SAAS,oDAAoD;AAAA,IAClG,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAA2D;AAAA,IACzG,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,EAChG;AAAA,EACA,OAAO,EAAE,UAAU,aAAa,UAAU,eAAe,cAAc,MAAM;AAC3E,UAAM,YAAY,aAAa,aAAa;AAC5C,QAAI,UAAW,QAAO;AAEtB,QAAI,CAAC,UAAU,QAAQ,GAAG;AACxB,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,kEAAkE,CAAC,CAAC;AAAA,IAC9G;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,SAAS,GAAG;AACV,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,iBAAiB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;AAAA,IAC1G;AAEA,UAAM,aAAa,aAAa,EAAE,SAAS,MAAM;AACjD,UAAM,SAAS,WAAW,OAAO,OAAO,OAAK,EAAE,aAAa,OAAO;AACnE,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO,SAAS,KAAK,UAAU;AAAA,QAC7B,OAAO;AAAA,QACP,QAAQ,OAAO,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE;AAAA,MAChE,GAAG,MAAM,CAAC,CAAC;AAAA,IACb;AAEA,UAAM,SAAS,aAAa;AAC5B,UAAM,WAAW,SAAS,eAAe,MAAM;AAC/C,UAAM,WAAW,MAAM,OAAO,eAAe,QAAQ;AAErD,QAAI,UAAU;AACZ,UAAI,CAAC,UAAU,UAAU,GAAG;AAC1B,eAAO,QAAQ,KAAK,UAAU;AAAA,UAC5B,YAAY,SAAS;AAAA,UACrB,MAAM,SAAS;AAAA,UACf,WAAW;AAAA,UACX,SAAS;AAAA,UACT,KAAK,GAAG,QAAQ,IAAI,cAAc,CAAC,aAAa,SAAS,EAAE;AAAA,QAC7D,GAAG,MAAM,CAAC,CAAC;AAAA,MACb;AACA,YAAM,OAAO,iBAAiB,SAAS,EAAE;AAAA,IAC3C;AAEA,UAAM,UAAU,gBAAgB,YAAY,IAAI,aAAa,IAAI;AAGjE,UAAM,wBAAyB,iBAAiB,CAAC,UAC7C;AAAA;AAAA,uBAA4B,aAAa,wNACzC;AAGJ,UAAM,QAAQ,WAAW;AACzB,UAAM,QAAQ,KAAK,QAAQ;AAAA,MACzB,aAAa,SAAS,eAAe,OAAO;AAAA,MAC5C,gBAAgB;AAAA,MAChB,oBAAoB,SAAS,oBAAoB;AAAA,MACjD,eAAe,SAAS;AAAA,IAC1B,CAAC;AAED,QAAI,gBAAgB,iBAAiB,SAAS;AAC5C,YAAM,aAAa,KAAK,kBAAkB;AAAA,QACxC,aAAa,QAAQ;AAAA,QACrB,SAAS;AAAA,QACT,eAAe,QAAQ;AAAA,QACvB,iBAAiB,KAAK,IAAI,IAAI,QAAQ;AAAA,QACtC,kBAAkB;AAAA,QAClB,mBAAmB;AAAA,QACnB,cAAc,SAAS;AAAA,QACvB,YAAY,SAAS;AAAA,QACrB,QAAQ;AAAA,QACR,mBAAmB;AAAA,QACnB,aAAa,QAAQ;AAAA,QACrB,cAAc,QAAQ;AAAA,MACxB,GAAG,aAAa;AAChB,kBAAY,OAAO,aAAa;AAChC,sBAAgB,QAAQ,EAAE,eAAe,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC3D;AAEA,WAAO,QAAQ,KAAK,UAAU;AAAA,MAC5B,YAAY,SAAS;AAAA,MACrB,MAAM,SAAS;AAAA,MACf,WAAW;AAAA,MACX,KAAK,GAAG,QAAQ,IAAI,cAAc,CAAC,aAAa,SAAS,EAAE;AAAA,IAC7D,GAAG,MAAM,CAAC,IAAI,qBAAqB;AAAA,EACrC;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,gCAAgC;AAAA,IACjE,UAAU,EAAE,OAAO,EAAE,SAAS,oCAAoC;AAAA,IAClE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAA2D;AAAA,IACzG,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,EAChG;AAAA,EACA,OAAO,EAAE,aAAa,UAAU,aAAa,eAAe,cAAc,MAAM;AAC9E,UAAM,YAAY,aAAa,aAAa;AAC5C,QAAI,UAAW,QAAO;AAEtB,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,SAAS,GAAG;AACV,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,iBAAiB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;AAAA,IAC1G;AAEA,UAAM,aAAa,aAAa,EAAE,SAAS,MAAM;AACjD,UAAM,SAAS,WAAW,OAAO,OAAO,OAAK,EAAE,aAAa,OAAO;AACnE,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO,SAAS,KAAK,UAAU;AAAA,QAC7B,OAAO;AAAA,QACP,QAAQ,OAAO,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE;AAAA,MAChE,GAAG,MAAM,CAAC,CAAC;AAAA,IACb;AAEA,UAAM,SAAS,aAAa;AAC5B,UAAM,WAAW,SAAS,eAAe,MAAM;AAC/C,UAAM,WAAW,MAAM,OAAO,eAAe,aAAa,QAAQ;AAElE,UAAM,UAAU,gBAAgB,YAAY,IAAI,aAAa,IAAI;AACjE,UAAM,wBAAyB,iBAAiB,CAAC,UAC7C;AAAA;AAAA,uBAA4B,aAAa,oDACzC;AAGJ,UAAM,QAAQ,WAAW;AACzB,UAAM,QAAQ,KAAK,QAAQ;AAAA,MACzB,aAAa,SAAS,eAAe,OAAO;AAAA,MAC5C,gBAAgB;AAAA,MAChB,oBAAoB,SAAS,oBAAoB;AAAA,MACjD,eAAe;AAAA,IACjB,CAAC;AAED,QAAI,gBAAgB,iBAAiB,SAAS;AAC5C,YAAM,aAAa,KAAK,kBAAkB;AAAA,QACxC,aAAa,QAAQ;AAAA,QACrB,SAAS;AAAA,QACT,eAAe,QAAQ;AAAA,QACvB,iBAAiB,KAAK,IAAI,IAAI,QAAQ;AAAA,QACtC,kBAAkB;AAAA,QAClB,mBAAmB;AAAA,QACnB,cAAc,SAAS;AAAA,QACvB,YAAY,SAAS;AAAA,QACrB,QAAQ;AAAA,QACR,mBAAmB;AAAA,QACnB,aAAa,QAAQ;AAAA,QACrB,cAAc,QAAQ;AAAA,MACxB,GAAG,aAAa;AAChB,kBAAY,OAAO,aAAa;AAChC,sBAAgB,QAAQ,EAAE,eAAe,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC3D;AAEA,WAAO,QAAQ,KAAK,UAAU;AAAA,MAC5B,YAAY,SAAS;AAAA,MACrB,MAAM,SAAS;AAAA,MACf,KAAK,GAAG,QAAQ,IAAI,cAAc,CAAC,aAAa,SAAS,EAAE;AAAA,IAC7D,GAAG,MAAM,CAAC,IAAI,qBAAqB;AAAA,EACrC;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,OAAO,EAAE,OAAO,EAAE,SAAS,wDAAmD;AAAA,IAC9E,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,2BAA2B;AAAA,EACnE;AAAA,EACA,OAAO,EAAE,OAAO,MAAM,MAAM;AAC1B,UAAM,QAAQ,WAAW;AACzB,UAAM,UAAU,MAAM,QAAQ,OAAO,KAAK;AAE1C,WAAO,QAAQ,KAAK;AAAA,MAClB,QAAQ,MAAM,GAAG,KAAK,EAAE,IAAI,QAAM;AAAA,QAChC,IAAI,EAAE,SAAS;AAAA,QACf,OAAO,OAAO,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,QAChC,MAAM,EAAE;AAAA,QACR,aAAa,EAAE,SAAS;AAAA,QACxB,WAAW,EAAE,SAAS,SAAS,MAAM;AAAA,QACrC,OAAO,EAAE,SAAS,SAAS,MAAM,IAAI,OAAK,EAAE,IAAI;AAAA,QAChD,eAAe,EAAE,SAAS,iBAAiB;AAAA,QAC3C,iBAAiB,EAAE,SAAS,mBAAmB,CAAC;AAAA,MAClD,EAAE;AAAA,MACF;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA,CAAC;AAAA,EACD,YAAY;AACV,UAAM,UAAU,QAAQ,IAAI,cAAc;AAC1C,UAAM,SAAS,QAAQ,IAAI,aAAa;AACxC,QAAI,CAAC,WAAW,CAAC,QAAQ;AACvB,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,sDAAsD,CAAC,CAAC;AAAA,IAClG;AAEA,eAAW;AACX,UAAM,SAAS,MAAM,SAAS;AAC9B,QAAI,CAAC,QAAQ;AACX,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,iGAAiG,CAAC,CAAC;AAAA,IAC7I;AAEA,WAAO,QAAQ,KAAK,UAAU;AAAA,MAC5B,QAAQ;AAAA,MACR,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,MACjB,SAAS,UAAU,OAAO,SAAS,uCAAuC,OAAO,QAAQ;AAAA,IAC3F,GAAG,MAAM,CAAC,CAAC;AAAA,EACb;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,wCAAwC;AAAA,IAC9E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,EACtF;AAAA,EACA,OAAO,EAAE,MAAM,MAAM,MAAM;AACzB,UAAM,WAAW,gBAAgB,QAAQ;AACzC,UAAM,WAAW,MAAM,SAAS,eAAe,IAAI;AAEnD,QAAI,UAAU,UAAa,QAAQ,GAAG;AACpC,eAAS,kBAAkB,SAAS,gBAAgB,MAAM,GAAG,KAAK;AAAA,IACpE;AAEA,WAAO,QAAQ,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAClD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uDAAkD;AAAA,IACxF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,2BAA2B;AAAA,EACpE;AAAA,EACA,OAAO,EAAE,OAAO,MAAM,MAAM;AAC1B,UAAM,QAAQ,WAAW;AAEzB,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,QAAQ,OAAO,KAAK;AAC1C,aAAO,QAAQ,KAAK;AAAA,QAClB,QAAQ,MAAM,GAAG,KAAK,EAAE,IAAI,QAAM;AAAA,UAChC,IAAI,EAAE,SAAS;AAAA,UACf,aAAa,EAAE,SAAS;AAAA,UACxB,OAAO,OAAO,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,UAChC,MAAM,EAAE;AAAA,UACR,WAAW,EAAE,SAAS,SAAS,MAAM;AAAA,UACrC,OAAO,EAAE,SAAS,SAAS,MAAM,IAAI,OAAK,EAAE,IAAI;AAAA,UAChD,aAAa,EAAE,SAAS;AAAA,UACxB,eAAe,EAAE,SAAS,iBAAiB;AAAA,UAC3C,WAAW,EAAE,SAAS;AAAA,QACxB,EAAE;AAAA,QACF;AAAA,QAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,UAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,WAAO,QAAQ,KAAK;AAAA,MAClB,IAAI,MAAM,GAAG,KAAK,EAAE,IAAI,QAAM;AAAA,QAC5B,IAAI,EAAE;AAAA,QACN,aAAa,EAAE;AAAA,QACf,WAAW,EAAE,SAAS,MAAM;AAAA,QAC5B,OAAO,EAAE,SAAS,MAAM,IAAI,OAAK,EAAE,IAAI;AAAA,QACvC,aAAa,EAAE;AAAA,QACf,eAAe,EAAE,iBAAiB;AAAA,QAClC,gBAAgB,EAAE,kBAAkB;AAAA,QACpC,WAAW,EAAE;AAAA,MACf,EAAE;AAAA,MACF;AAAA,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,YAAY,EAAE,OAAO,EAAE,SAAS,4FAA4F;AAAA,IAC5H,UAAU,EAAE,OAAO,EAAE,SAAS,yDAAyD;AAAA,IACvF,gBAAgB,EAAE,QAAQ,EAAE,SAAS,6CAA6C;AAAA,IAClF,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,mDAAmD;AAAA,IAC9F,MAAM,EAAE,KAAK,CAAC,UAAU,WAAW,CAAC,EAAE,SAAS,kDAAkD;AAAA,EACnG;AAAA,EACA,OAAO,EAAE,YAAY,UAAU,gBAAgB,cAAc,KAAK,MAAM;AACtE,UAAM,QAAQ,WAAW;AACzB,UAAM,QAAQ,cAAc,YAAY;AAAA,MACtC;AAAA,MACA,cAAc;AAAA,MACd,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,WAAO,QAAQ,KAAK,UAAU,EAAE,UAAU,MAAM,WAAW,WAAW,CAAC,CAAC;AAAA,EAC1E;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA,CAAC;AAAA,EACD,YAAY;AACV,UAAM,SAAS,aAAa;AAC5B,UAAM,YAAY,MAAM,OAAO,cAAc;AAE7C,WAAO,QAAQ,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA,EACnD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,qBAAqB;AAAA,EACxD;AAAA,EACA,OAAO,EAAE,YAAY,MAAM;AACzB,UAAM,SAAS,aAAa;AAC5B,UAAM,WAAW,MAAM,OAAO,YAAY,WAAW;AAErD,WAAO,QAAQ,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAClD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,iCAAiC;AAAA,EACpE;AAAA,EACA,OAAO,EAAE,YAAY,MAAM;AACzB,QAAI,CAAC,UAAU,UAAU,GAAG;AAC1B,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,sEAAsE,CAAC,CAAC;AAAA,IAClH;AAEA,UAAM,SAAS,aAAa;AAC5B,UAAM,OAAO,iBAAiB,WAAW;AAEzC,WAAO,QAAQ,sBAAsB,WAAW,EAAE;AAAA,EACpD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,EACtE;AAAA,EACA,OAAO,EAAE,YAAY,MAAM;AACzB,UAAM,SAAS,aAAa;AAC5B,UAAM,OAAO,mBAAmB,WAAW;AAE3C,WAAO,QAAQ,wBAAwB,WAAW,EAAE;AAAA,EACtD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,+BAA+B;AAAA,IAChE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,EAChG;AAAA,EACA,OAAO,EAAE,aAAa,cAAc,MAAM;AACxC,UAAM,YAAY,aAAa,aAAa;AAC5C,QAAI,UAAW,QAAO;AAEtB,QAAI,CAAC,UAAU,QAAQ,GAAG;AACxB,aAAO,SAAS,KAAK,UAAU,EAAE,OAAO,kEAAkE,CAAC,CAAC;AAAA,IAC9G;AAEA,UAAM,SAAS,aAAa;AAC5B,UAAM,OAAO,eAAe,WAAW;AAEvC,WAAO,QAAQ,oBAAoB,WAAW,EAAE;AAAA,EAClD;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,IAC7F,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,wCAAwC;AAAA,EACjF;AAAA,EACA,OAAO,EAAE,aAAa,MAAM,MAAM;AAChC,UAAM,SAAS,aAAa;AAC5B,UAAM,aAAa,MAAM,OAAO,cAAc,aAAa,EAAE,MAAM,CAAC;AAEpE,WAAO,QAAQ,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAAA,EACpD;AACF;AAEA,eAAe,OAAO;AACpB,MAAI,CAAC,QAAQ,IAAI,mBAAmB,GAAG;AACrC,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAChC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,sCAAsC,GAAG;AACvD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/mcp-server.ts","../src/validation/node-syncer.ts","../src/utils/node-catalog-cache.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Kairos MCP Server — decomposed architecture.\n *\n * The host LLM (Claude, GPT, Gemini, whatever) generates the workflow.\n * Kairos provides the knowledge (system prompt, library, failure patterns)\n * and guardrails (validator, deployer). Zero Anthropic API key needed.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { createServer } from 'node:http'\nimport { z } from 'zod'\nimport { FileLibrary } from './library/file-library.js'\nimport { N8nValidator } from './validation/validator.js'\nimport { N8nFieldStripper } from './providers/n8n/stripper.js'\nimport { N8nApiClient } from './providers/n8n/api-client.js'\nimport { PromptBuilder } from './generation/prompt-builder.js'\nimport { TelemetryReader } from './telemetry/reader.js'\nimport { PatternAnalyzer } from './telemetry/pattern-analyzer.js'\nimport { NodeSyncer, type SyncResult } from './validation/node-syncer.js'\nimport { TelemetryCollector } from './telemetry/collector.js'\nimport { nullLogger } from './utils/logger.js'\nimport { GuardError } from './errors/guard-error.js'\nimport { generateUUID } from './utils/uuid.js'\nimport { inferWorkflowType } from './utils/workflow-type.js'\nimport type { N8nWorkflow } from './types/workflow.js'\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { readCatalogCache, writeCatalogCache } from './utils/node-catalog-cache.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string }\n\nconst library = new FileLibrary()\nlet _validator = new N8nValidator()\n// Accessor so kairos_sync can swap the registry without callers closing over the old instance\nfunction getValidator(): N8nValidator { return _validator }\nconst nodeSyncer = new NodeSyncer()\nlet lastSync: SyncResult | null = null\nconst AUTO_SYNC_TIMEOUT_MS = 5_000 // cap how long kairos_prompt waits for n8n node sync\nconst stripper = new N8nFieldStripper()\nconst promptBuilder = new PromptBuilder(getMcpPatternsPath())\n\nfunction getMcpTelemetry(): TelemetryCollector | null {\n const val = process.env['KAIROS_TELEMETRY']\n if (!val || val === 'false') return null\n return val === 'true' ? new TelemetryCollector() : new TelemetryCollector(val)\n}\n\n/**\n * Derive the patterns.json path from KAIROS_TELEMETRY so the MCP server's\n * PromptBuilder reads from the same location that PatternAnalyzer.fromEnv() writes to.\n */\nfunction getMcpPatternsPath(): string {\n const val = process.env['KAIROS_TELEMETRY']\n if (val && val !== 'false' && val !== 'true') {\n return join(val, '..', 'patterns.json')\n }\n return join(homedir(), '.kairos', 'patterns.json')\n}\n\nconst mcpTelemetry = getMcpTelemetry()\n\ninterface McpBuildSession {\n description: string\n startTime: number\n validateAttempts: number\n warnedRules: number[]\n workflowType: string | null\n matchCount: number\n}\nconst mcpSessions = new Map<string, McpBuildSession>()\nconst SESSION_TTL_MS = 60 * 60 * 1000 // 1 hour: abandon sessions not completed by deploy\n\nfunction evictStaleSessions(): void {\n const cutoff = Date.now() - SESSION_TTL_MS\n for (const [id, session] of mcpSessions) {\n if (session.startTime < cutoff) mcpSessions.delete(id)\n }\n}\n\nfunction getTelemetryReader(): TelemetryReader | null {\n try {\n return new TelemetryReader()\n } catch {\n return null\n }\n}\n\ntype McpMode = 'readonly' | 'validate' | 'deploy'\n\nfunction getMcpMode(): McpMode {\n const mode = process.env['KAIROS_MCP_MODE']?.toLowerCase()\n if (mode === 'readonly' || mode === 'validate') return mode\n return 'deploy'\n}\n\nfunction isAllowed(action: 'deploy' | 'activate' | 'delete'): boolean {\n // readonly and validate modes block all write ops — mode restriction overrides ALLOW_* flags\n const mode = getMcpMode()\n if (mode === 'readonly' || mode === 'validate') return false\n // deploy mode (default): require explicit opt-in via ALLOW_* flags (preserves existing behavior)\n const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`\n return process.env[key] === 'true'\n}\n\ntype McpTextContent = { type: 'text'; text: string }\ntype McpToolResult = { content: McpTextContent[]; isError?: true }\n\nfunction mcpText(text: string): McpToolResult { return { content: [{ type: 'text', text }] } }\nfunction mcpError(text: string): McpToolResult { return { content: [{ type: 'text', text }], isError: true } }\n\n/**\n * Returns an error result if KAIROS_MCP_SECRET is set and the provided secret\n * doesn't match. Returns null if auth passes (no secret configured, or correct secret).\n */\nfunction checkMcpAuth(provided: string | undefined): McpToolResult | null {\n const expected = process.env['KAIROS_MCP_SECRET']\n if (!expected) return null\n if (provided === expected) return null\n return mcpError(JSON.stringify({ error: 'Unauthorized: missing or incorrect kairos_secret' }))\n}\n\nfunction getApiClient(): N8nApiClient {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n throw new GuardError('N8N_BASE_URL and N8N_API_KEY environment variables are required for n8n operations')\n }\n return new N8nApiClient(baseUrl, apiKey, nullLogger)\n}\n\nfunction getCatalogCachePath(): string {\n const telemetry = process.env['KAIROS_TELEMETRY']\n const base = telemetry ? join(telemetry, '..') : join(homedir(), '.kairos')\n return join(base, 'node-catalog-cache.json')\n}\n\nasync function autoSync(): Promise<SyncResult | null> {\n if (lastSync) return lastSync\n\n // Try disk cache before hitting the network\n const cachePath = getCatalogCachePath()\n const cached = await readCatalogCache(cachePath)\n if (cached) {\n lastSync = cached\n _validator = new N8nValidator(lastSync.registry)\n return lastSync\n }\n\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) return null\n try {\n const client = new N8nApiClient(baseUrl, apiKey, nullLogger)\n const nodeTypes = await client.getNodeTypes()\n if (nodeTypes.length === 0) return null\n lastSync = nodeSyncer.sync(nodeTypes)\n _validator = new N8nValidator(lastSync.registry)\n writeCatalogCache(cachePath, lastSync).catch(() => {})\n return lastSync\n } catch {\n return null\n }\n}\n\nconst server = new McpServer({\n name: 'kairos',\n version: pkg.version,\n})\n\n// ── Core generation tools (no API key needed) ──────────────────────────────\n\nserver.tool(\n 'kairos_prompt',\n 'Get the specialized n8n workflow generation context. Returns a system prompt with node catalog, connection rules, validation rules, plus library matches and failure patterns for the given description. Feed this to yourself as context, then generate the workflow JSON.',\n {\n description: z.string().describe('Plain-English description of the workflow to build'),\n name: z.string().optional().describe('Optional workflow name override'),\n },\n async ({ description, name }) => {\n evictStaleSessions()\n\n const runId = generateUUID()\n const workflowType = inferWorkflowType(description)\n const hasN8nCreds = !!(process.env['N8N_BASE_URL'] && process.env['N8N_API_KEY'])\n\n // Start sync in background — race against timeout so slow n8n instances don't block the prompt\n const syncPromise = autoSync()\n const syncTimeout = new Promise<null>((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS))\n\n await library.initialize()\n const [syncResult, matches, failureRates] = await Promise.all([\n Promise.race([syncPromise, syncTimeout]),\n library.search(description),\n (async () => {\n const reader = getTelemetryReader()\n return reader ? reader.getFailureRates() : []\n })(),\n ])\n\n const request = { description, ...(name ? { name } : {}) }\n const built = promptBuilder.build(request, matches, failureRates, syncResult?.catalogText)\n\n if (mcpTelemetry) {\n mcpSessions.set(runId, {\n description,\n startTime: Date.now(),\n validateAttempts: 0,\n warnedRules: promptBuilder.getWarnedRules(),\n workflowType,\n matchCount: matches.length,\n })\n await mcpTelemetry.emit('build_start', { description, model: 'mcp-decomposed', dryRun: false }, runId)\n }\n\n const systemText = built.system.map(block => block.text).join('\\n\\n---\\n\\n')\n\n return mcpText(JSON.stringify({\n kairos_run_id: runId,\n mode: built.mode,\n matchCount: matches.length,\n topMatchScore: matches[0]?.score ?? null,\n nodeCatalog: syncResult ? 'synced' : 'static',\n nodeCount: syncResult?.nodeCount ?? null,\n ...(syncResult ? {} : {\n syncWarning: hasN8nCreds\n ? 'Could not sync node types from your n8n instance. Using static fallback catalog — generated workflows may not match your exact n8n setup.'\n : 'N8N_BASE_URL and N8N_API_KEY are not set. Using static fallback catalog — node types may not match your n8n instance. Set these env vars to enable accurate generation and deployment.',\n }),\n systemPrompt: systemText,\n userMessage: built.userMessage,\n outputFormat: {\n description: 'Generate a JSON object with this exact structure. The workflow field contains the n8n workflow. credentialsNeeded lists services requiring credentials.',\n schema: {\n workflow: {\n name: 'string — descriptive workflow name',\n nodes: 'array — n8n node objects with id (UUID v4), type, typeVersion, name, position, parameters',\n connections: 'object — keyed by source node NAME, maps to target nodes',\n settings: 'object — include executionOrder: \"v1\"',\n },\n credentialsNeeded: [{\n service: 'string — e.g. \"Slack\"',\n credentialType: 'string — e.g. \"slackOAuth2Api\"',\n description: 'string — what the user needs to set up',\n }],\n },\n },\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_validate',\n 'Validate n8n workflow JSON against 34 structural rules. Returns pass/fail with specific issues. If validation fails, fix the issues and call this again. Errors block deployment; warnings are advisory.',\n {\n workflow: z.string().describe('The workflow JSON string to validate'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n },\n async ({ workflow: workflowStr, kairos_run_id }) => {\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpText(JSON.stringify({ valid: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }, null, 2))\n }\n\n const result = getValidator().validate(parsed)\n const errors = result.issues.filter(i => i.severity === 'error')\n const warnings = result.issues.filter(i => i.severity === 'warn')\n\n if (mcpTelemetry && kairos_run_id) {\n const session = mcpSessions.get(kairos_run_id)\n if (session) {\n session.validateAttempts++\n await mcpTelemetry.emit('generation_attempt', {\n description: session.description,\n attempt: session.validateAttempts,\n temperature: 0,\n durationMs: 0,\n tokensInput: 0,\n tokensOutput: 0,\n validationPassed: result.valid,\n issueCount: result.issues.length,\n issues: result.issues.map(i => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null })),\n workflowType: session.workflowType,\n }, kairos_run_id)\n }\n }\n\n return mcpText(JSON.stringify({\n valid: result.valid,\n errorCount: errors.length,\n warningCount: warnings.length,\n errors: errors.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n warnings: warnings.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n deployable: errors.length === 0,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_deploy',\n 'Deploy a validated workflow to n8n. Pass the workflow JSON that passed kairos_validate. Strips server-assigned fields automatically. Requires N8N_BASE_URL and N8N_API_KEY.',\n {\n workflow: z.string().describe('The validated workflow JSON string to deploy'),\n activate: z.boolean().default(false).describe('Activate the workflow immediately after deployment'),\n description: z.string().optional().describe('The original user intent / description for this workflow — used to improve library search quality over time'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Deploy is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before deploying',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForCreate(parsed)\n const response = await client.createWorkflow(stripped)\n\n if (activate) {\n if (!isAllowed('activate')) {\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: false,\n warning: 'Workflow deployed but activation is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.',\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2))\n }\n await client.activateWorkflow(response.id)\n }\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n\n // Warn when kairos_run_id is provided but no matching session exists — telemetry will be skipped\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.`\n : ''\n\n // Save to library (n8nWorkflowId enables dedup on future redeployment)\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? userDescription ?? parsed.name,\n generationMode: session && session.matchCount > 0 ? 'reference' : 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: response.id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: activate,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_replace',\n 'Replace an existing n8n workflow with a new version. Validates before updating. Use kairos_prompt → kairos_validate → kairos_replace for iteration on existing workflows.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to replace'),\n workflow: z.string().describe('The validated workflow JSON string'),\n description: z.string().optional().describe('The original user intent / description for this workflow — used to improve library search quality over time'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before replacing',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForUpdate(parsed)\n const response = await client.updateWorkflow(workflow_id, stripped)\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found.`\n : ''\n\n // Save to library — D4 dedup updates the existing entry rather than creating a duplicate\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? userDescription ?? parsed.name,\n generationMode: session && session.matchCount > 0 ? 'reference' : 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: workflow_id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_search',\n 'Search the local workflow library for similar past builds. Returns matching workflows with scores, useful for finding examples and reusing patterns.',\n {\n query: z.string().describe('Search query — a workflow description or keywords'),\n limit: z.number().default(5).describe('Maximum number of results'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n const matches = await library.search(query)\n\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n description: m.workflow.description,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n failurePatterns: m.workflow.failurePatterns ?? [],\n })),\n null,\n 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_sync',\n 'Sync the node catalog from your live n8n instance. Fetches all installed node types and versions so Kairos knows exactly what your n8n supports. Automatically called by kairos_prompt when n8n credentials are set, but you can call this manually to force a refresh.',\n {},\n async () => {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required for sync.' }))\n }\n\n lastSync = null\n const result = await autoSync()\n if (!result) {\n return mcpError(JSON.stringify({ error: 'Failed to fetch node types from n8n. Check your credentials and that your instance is running.' }))\n }\n\n return mcpText(JSON.stringify({\n synced: true,\n nodeCount: result.nodeCount,\n newNodes: result.newNodes,\n message: `Synced ${result.nodeCount} node types from your n8n instance (${result.newNodes} not in default catalog).`,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_patterns',\n 'Analyze telemetry data and return failure patterns, build stats, and credential breakdowns. Useful for understanding what goes wrong most often and how to prevent it.',\n {\n days: z.number().default(30).describe('Number of days of telemetry to analyze'),\n limit: z.number().optional().describe('Maximum number of failure patterns to return'),\n },\n async ({ days, limit }) => {\n const analyzer = PatternAnalyzer.fromEnv()\n const analysis = await analyzer.analyzeAndSave(days)\n\n if (limit !== undefined && limit > 0) {\n analysis.topFailureRules = analysis.topFailureRules.slice(0, limit)\n }\n\n return mcpText(JSON.stringify(analysis, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_library',\n 'Browse the local Kairos workflow library. Returns saved workflow metadata. Use the optional query to search, or omit it to list all entries.',\n {\n query: z.string().optional().describe('Optional search query — omit to list all entries'),\n limit: z.number().default(20).describe('Maximum entries to return'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n\n if (query) {\n const matches = await library.search(query)\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n description: m.workflow.description,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n deployCount: m.workflow.deployCount,\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n createdAt: m.workflow.createdAt,\n })),\n null, 2,\n ))\n }\n\n const all = await library.list()\n return mcpText(JSON.stringify(\n all.slice(0, limit).map(w => ({\n id: w.id,\n description: w.description,\n nodeCount: w.workflow.nodes.length,\n nodes: w.workflow.nodes.map(n => n.name),\n deployCount: w.deployCount,\n n8nWorkflowId: w.n8nWorkflowId ?? null,\n timesRetrieved: w.timesRetrieved ?? 0,\n createdAt: w.createdAt,\n })),\n null, 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_outcome',\n 'Record the outcome of a workflow build against a library entry. Trains the pattern learning system to know what works and what fails over time.',\n {\n library_id: z.string().describe('The Kairos library entry ID (returned by kairos_deploy, kairos_replace, or kairos_library)'),\n attempts: z.number().describe('Number of generation+validation attempts before success'),\n first_try_pass: z.boolean().describe('Whether the first attempt passed validation'),\n failed_rules: z.array(z.number()).describe('Validation rule IDs that failed during generation'),\n mode: z.enum(['direct', 'reference']).describe('How the library entry was used during generation'),\n },\n async ({ library_id, attempts, first_try_pass, failed_rules, mode }) => {\n await library.initialize()\n await library.recordOutcome(library_id, {\n attempts,\n firstTryPass: first_try_pass,\n failedRules: failed_rules,\n mode,\n })\n return mcpText(JSON.stringify({ recorded: true, libraryId: library_id }))\n },\n)\n\n// ── n8n management tools (need N8N_BASE_URL + N8N_API_KEY) ─────────────────\n\nserver.tool(\n 'kairos_list',\n 'List all workflows deployed on the connected n8n instance.',\n {},\n async () => {\n const client = getApiClient()\n const workflows = await client.listWorkflows()\n\n return mcpText(JSON.stringify(workflows, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_get',\n 'Get the full JSON definition of a specific workflow by ID.',\n {\n workflow_id: z.string().describe('The n8n workflow ID'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n const workflow = await client.getWorkflow(workflow_id)\n\n return mcpText(JSON.stringify(workflow, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_activate',\n 'Activate a deployed workflow so it starts running on triggers.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to activate'),\n },\n async ({ workflow_id }) => {\n if (!isAllowed('activate')) {\n return mcpError(JSON.stringify({ error: 'Activate is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.activateWorkflow(workflow_id)\n\n return mcpText(`Activated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_deactivate',\n 'Deactivate a running workflow.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to deactivate'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n await client.deactivateWorkflow(workflow_id)\n\n return mcpText(`Deactivated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_delete',\n 'Delete a workflow from n8n. This is irreversible.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to delete'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('delete')) {\n return mcpError(JSON.stringify({ error: 'Delete is disabled. Set KAIROS_MCP_ALLOW_DELETE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.deleteWorkflow(workflow_id)\n\n return mcpText(`Deleted workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_executions',\n 'List recent executions for a workflow, showing status and timing.',\n {\n workflow_id: z.string().optional().describe('Filter to a specific workflow ID (omit for all)'),\n limit: z.number().default(20).describe('Maximum number of executions to return'),\n },\n async ({ workflow_id, limit }) => {\n const client = getApiClient()\n const executions = await client.getExecutions(workflow_id, { limit })\n\n return mcpText(JSON.stringify(executions, null, 2))\n },\n)\n\nasync function main() {\n if (!process.env['ANTHROPIC_API_KEY']) {\n process.stderr.write(\n '[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set — kairos_prompt will fail. Set it before using workflow generation tools.\\n',\n )\n }\n\n const useHttp = process.argv.includes('--http')\n\n if (useHttp) {\n const port = parseInt(process.env['KAIROS_MCP_PORT'] ?? '3000', 10)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transport = new StreamableHTTPServerTransport() as any\n await server.connect(transport)\n\n const httpServer = createServer(async (req, res) => {\n if (req.method === 'GET' || req.method === 'POST' || req.method === 'DELETE') {\n await transport.handleRequest(req, res)\n } else {\n res.writeHead(405, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Method not allowed' }))\n }\n })\n\n httpServer.listen(port, () => {\n process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}\\n`)\n })\n } else {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n }\n}\n\nmain().catch((err: unknown) => {\n console.error('Kairos MCP server failed to start:', err)\n process.exit(1)\n})\n","import type { N8nNodeTypeInfo } from '../providers/n8n/types.js'\nimport type { NodeDefinition } from './registry.js'\nimport { NodeRegistry, DEFAULT_REGISTRY } from './registry.js'\n\nconst TRIGGER_PATTERNS = [/trigger/i, /Trigger$/]\n\nexport interface SyncResult {\n registry: NodeRegistry\n catalogText: string\n nodeCount: number\n newNodes: number\n}\n\nexport class NodeSyncer {\n private readonly baseRegistry: Map<string, NodeDefinition>\n\n constructor() {\n this.baseRegistry = new Map(DEFAULT_REGISTRY.map(d => [d.type, d]))\n }\n\n sync(liveNodes: N8nNodeTypeInfo[]): SyncResult {\n const merged = new Map(this.baseRegistry)\n let newNodes = 0\n\n for (const node of liveNodes) {\n const versions = Array.isArray(node.version) ? node.version : [node.version]\n const isTrigger = TRIGGER_PATTERNS.some(p => p.test(node.name))\n const credentialType = node.credentials?.[0]?.name\n\n const existing = merged.get(node.name)\n if (existing) {\n const allVersions = new Set([...existing.safeTypeVersions, ...versions])\n merged.set(node.name, {\n ...existing,\n safeTypeVersions: [...allVersions].sort((a, b) => a - b),\n })\n } else {\n newNodes++\n merged.set(node.name, {\n type: node.name,\n safeTypeVersions: versions.sort((a, b) => a - b),\n requiredParams: [],\n ...(credentialType ? { credentialType } : {}),\n ...(isTrigger ? { isTrigger: true } : {}),\n })\n }\n }\n\n const definitions = [...merged.values()]\n const registry = new NodeRegistry(definitions)\n const catalogText = this.buildCatalog(definitions)\n\n return { registry, catalogText, nodeCount: definitions.length, newNodes }\n }\n\n private buildCatalog(definitions: NodeDefinition[]): string {\n const triggers = definitions.filter(d => d.isTrigger)\n const regular = definitions.filter(d => !d.isTrigger)\n\n const formatEntry = (d: NodeDefinition): string => {\n const versions = d.safeTypeVersions.join(', ')\n const cred = d.credentialType ? ` — cred: ${d.credentialType}` : ''\n return `${d.type} typeVersion: ${versions}${cred}`\n }\n\n const triggerLines = triggers.map(formatEntry).join('\\n')\n const regularLines = regular.map(formatEntry).join('\\n')\n\n return `## NODE CATALOG — synced from your n8n instance (${definitions.length} node types)\n\n### Triggers:\n${triggerLines}\n\n### Regular nodes:\n${regularLines}`\n }\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport type { SyncResult } from '../validation/node-syncer.js'\n\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\n\ninterface CachedCatalog {\n cachedAt: number\n syncResult: SyncResult\n}\n\nexport async function readCatalogCache(cachePath: string): Promise<SyncResult | null> {\n try {\n const raw = await readFile(cachePath, 'utf-8')\n const cached = JSON.parse(raw) as CachedCatalog\n if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null\n return cached.syncResult\n } catch {\n return null\n }\n}\n\nexport async function writeCatalogCache(cachePath: string, syncResult: SyncResult): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true })\n const payload: CachedCatalog = { cachedAt: Date.now(), syncResult }\n await writeFile(cachePath, JSON.stringify(payload), 'utf-8')\n } catch {\n // Cache write failure is non-fatal — next startup will just re-fetch\n }\n}\n\nexport function isCacheExpired(cachedAt: number, ttlMs = CACHE_TTL_MS): boolean {\n return Date.now() - cachedAt > ttlMs\n}\n"],"mappings":"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","names":["dirname","dirname"]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ PackBuilder,
3
+ derivePackStatus
4
+ } from "./chunk-GG4B4TYG.js";
5
+ export {
6
+ PackBuilder,
7
+ derivePackStatus
8
+ };
9
+ //# sourceMappingURL=pack-builder-RTQWXGIS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ generateHandoff
3
+ } from "./chunk-SC6CLQZB.js";
4
+ export {
5
+ generateHandoff
6
+ };
7
+ //# sourceMappingURL=pack-exporter-KFNLSP5V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ validatePack
3
+ } from "./chunk-SQS4QHDH.js";
4
+ export {
5
+ validatePack
6
+ };
7
+ //# sourceMappingURL=pack-validator-HZPB2XJ3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -489,6 +489,7 @@ declare class N8nValidator {
489
489
  private checkRule31;
490
490
  private checkRule32;
491
491
  private checkRule33;
492
+ private checkRule35;
492
493
  private checkRule34;
493
494
  }
494
495
 
@@ -593,4 +594,4 @@ declare class TelemetryReader {
593
594
  private readRecentEvents;
594
595
  }
595
596
 
596
- export { buildSearchCorpus as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, type DeleteOptions as D, type ExecutionFilter as E, type FailurePattern as F, GenerationError as G, TelemetryCollector as H, type ILogger as I, type TelemetryEvent as J, KairosError as K, TelemetryReader as L, TemplateSyncer as M, type N8nWorkflow as N, type OutcomeData as O, ProviderError as P, type TrustLevel as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type ValidationIssue as U, ValidationError as V, type WorkflowListItem as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowMatch as Z, type WorkflowMetadataInput as _, type BuildResult as a, clusterWorkflows as a0, hybridScore as a1, nullLogger as a2, rerank as a3, tokenize as a4, type SmokeTestStatus as a5, type ExecutionSummary as b, type ExecutionDetail as c, type AttemptMetadata as d, type CredentialRequirement as e, DEFAULT_REGISTRY as f, type DeployResult as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, N8nApiClient as l, type N8nConnections as m, N8nFieldStripper as n, type N8nNode as o, N8nProvider as p, type N8nSettings as q, N8nValidator as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SourceKind as x, type StoredWorkflow as y, type SyncProgress as z };
597
+ export { type WorkflowMetadataInput as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, DEFAULT_REGISTRY as D, type ExecutionDetail as E, type FailurePattern as F, GenerationError as G, type SyncProgress as H, type ILogger as I, TelemetryCollector as J, KairosError as K, type TelemetryEvent as L, TelemetryReader as M, N8nApiClient as N, type OutcomeData as O, ProviderError as P, TemplateSyncer as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type TrustLevel as U, ValidationError as V, type ValidationIssue as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowListItem as Z, type WorkflowMatch as _, type AttemptMetadata as a, buildSearchCorpus as a0, clusterWorkflows as a1, hybridScore as a2, nullLogger as a3, rerank as a4, tokenize as a5, type BuildResult as b, type CredentialRequirement as c, type DeleteOptions as d, type DeployResult as e, type ExecutionFilter as f, type ExecutionSummary as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, type N8nConnections as l, N8nFieldStripper as m, type N8nNode as n, N8nProvider as o, type N8nSettings as p, N8nValidator as q, type N8nWorkflow as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SmokeTestStatus as x, type SourceKind as y, type StoredWorkflow as z };
@@ -489,6 +489,7 @@ declare class N8nValidator {
489
489
  private checkRule31;
490
490
  private checkRule32;
491
491
  private checkRule33;
492
+ private checkRule35;
492
493
  private checkRule34;
493
494
  }
494
495
 
@@ -593,4 +594,4 @@ declare class TelemetryReader {
593
594
  private readRecentEvents;
594
595
  }
595
596
 
596
- export { buildSearchCorpus as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, type DeleteOptions as D, type ExecutionFilter as E, type FailurePattern as F, GenerationError as G, TelemetryCollector as H, type ILogger as I, type TelemetryEvent as J, KairosError as K, TelemetryReader as L, TemplateSyncer as M, type N8nWorkflow as N, type OutcomeData as O, ProviderError as P, type TrustLevel as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type ValidationIssue as U, ValidationError as V, type WorkflowListItem as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowMatch as Z, type WorkflowMetadataInput as _, type BuildResult as a, clusterWorkflows as a0, hybridScore as a1, nullLogger as a2, rerank as a3, tokenize as a4, type SmokeTestStatus as a5, type ExecutionSummary as b, type ExecutionDetail as c, type AttemptMetadata as d, type CredentialRequirement as e, DEFAULT_REGISTRY as f, type DeployResult as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, N8nApiClient as l, type N8nConnections as m, N8nFieldStripper as n, type N8nNode as o, N8nProvider as p, type N8nSettings as q, N8nValidator as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SourceKind as x, type StoredWorkflow as y, type SyncProgress as z };
597
+ export { type WorkflowMetadataInput as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, DEFAULT_REGISTRY as D, type ExecutionDetail as E, type FailurePattern as F, GenerationError as G, type SyncProgress as H, type ILogger as I, TelemetryCollector as J, KairosError as K, type TelemetryEvent as L, TelemetryReader as M, N8nApiClient as N, type OutcomeData as O, ProviderError as P, TemplateSyncer as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type TrustLevel as U, ValidationError as V, type ValidationIssue as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowListItem as Z, type WorkflowMatch as _, type AttemptMetadata as a, buildSearchCorpus as a0, clusterWorkflows as a1, hybridScore as a2, nullLogger as a3, rerank as a4, tokenize as a5, type BuildResult as b, type CredentialRequirement as c, type DeleteOptions as d, type DeployResult as e, type ExecutionFilter as f, type ExecutionSummary as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, type N8nConnections as l, N8nFieldStripper as m, type N8nNode as n, N8nProvider as o, type N8nSettings as p, N8nValidator as q, type N8nWorkflow as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SmokeTestStatus as x, type SourceKind as y, type StoredWorkflow as z };
@@ -1388,6 +1388,7 @@ var N8nValidator = class {
1388
1388
  this.checkRule32(workflow, issues);
1389
1389
  this.checkRule33(workflow, issues);
1390
1390
  this.checkRule34(workflow, issues);
1391
+ this.checkRule35(workflow, issues);
1391
1392
  if (Array.isArray(workflow.nodes)) {
1392
1393
  const nodeById = new Map(workflow.nodes.map((n) => [n.id, n.type]));
1393
1394
  for (const issue of issues) {
@@ -1960,6 +1961,43 @@ var N8nValidator = class {
1960
1961
  }
1961
1962
  }
1962
1963
  }
1964
+ // Rule 35 (WARN): email-sending node with no duplicate-prevention signal
1965
+ checkRule35(w, issues) {
1966
+ if (!Array.isArray(w.nodes)) return;
1967
+ const sendNodes = w.nodes.filter((node) => {
1968
+ if (node.type === "n8n-nodes-base.gmail") {
1969
+ const op = node.parameters?.["operation"];
1970
+ return !op || op === "send" || op === "sendEmail" || op === "reply";
1971
+ }
1972
+ return node.type === "n8n-nodes-base.emailSend" || node.type === "n8n-nodes-base.sendEmail";
1973
+ });
1974
+ if (sendNodes.length === 0) return;
1975
+ const workflowText = JSON.stringify(w).toLowerCase();
1976
+ const IDEMPOTENCY_SIGNALS = [
1977
+ "sent_at",
1978
+ "last_sent",
1979
+ "last_reminder",
1980
+ "processed_at",
1981
+ "already_sent",
1982
+ "email_sent",
1983
+ "notified_at",
1984
+ "reminder_sent",
1985
+ "contacted_at",
1986
+ "dedupe",
1987
+ "idempotent"
1988
+ ];
1989
+ const hasIdempotencySignal = IDEMPOTENCY_SIGNALS.some((s) => workflowText.includes(s));
1990
+ if (!hasIdempotencySignal) {
1991
+ for (const node of sendNodes) {
1992
+ this.warn(
1993
+ issues,
1994
+ 35,
1995
+ `Node "${node.name}" sends email but no duplicate-prevention signal detected \u2014 add a sent_at timestamp field, a prior-send IF check, or a deduplication key to avoid repeat sends`,
1996
+ node.id
1997
+ );
1998
+ }
1999
+ }
2000
+ }
1963
2001
  // Rule 34 (WARN): webhook path contains spaces, starts with slash, or looks like a full URL
1964
2002
  checkRule34(w, issues) {
1965
2003
  if (!Array.isArray(w.nodes)) return;
@@ -2394,7 +2432,7 @@ var import_node_path5 = require("path");
2394
2432
  var import_node_os4 = require("os");
2395
2433
 
2396
2434
  // src/validation/rule-metadata.ts
2397
- var VALIDATOR_RULE_IDS = Array.from({ length: 34 }, (_, i) => i + 1);
2435
+ var VALIDATOR_RULE_IDS = Array.from({ length: 35 }, (_, i) => i + 1);
2398
2436
  var RULE_PIPELINE_STAGES = {
2399
2437
  1: "node_generation",
2400
2438
  2: "node_generation",
@@ -2429,7 +2467,8 @@ var RULE_PIPELINE_STAGES = {
2429
2467
  31: "node_generation",
2430
2468
  32: "node_generation",
2431
2469
  33: "node_generation",
2432
- 34: "node_generation"
2470
+ 34: "node_generation",
2471
+ 35: "node_generation"
2433
2472
  };
2434
2473
  var RULE_MITIGATIONS = {
2435
2474
  1: "Provide a non-empty workflow name string",
@@ -2465,7 +2504,8 @@ var RULE_MITIGATIONS = {
2465
2504
  31: "Add at least one condition to the if node \u2014 conditions.conditions array must be non-empty",
2466
2505
  32: "Add field assignments to the set node \u2014 assignments.assignments array must be non-empty for typeVersion 3.x",
2467
2506
  33: "Add at least one schedule rule to scheduleTrigger \u2014 rule.interval array must have at least one entry",
2468
- 34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")'
2507
+ 34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")',
2508
+ 35: "Add duplicate-prevention to email-sending workflows: a sent_at timestamp field updated after each send, or an IF node that checks prior-send status before sending"
2469
2509
  };
2470
2510
 
2471
2511
  // src/telemetry/pattern-analyzer.ts
@@ -2523,7 +2563,7 @@ var PatternAnalyzer = class _PatternAnalyzer {
2523
2563
  this._cachedEvents = events;
2524
2564
  const starts = events.filter((e) => e.eventType === "build_start");
2525
2565
  const attempts = events.filter((e) => e.eventType === "generation_attempt");
2526
- const passed = attempts.filter(
2566
+ const _passed = attempts.filter(
2527
2567
  (a) => a.data.validationPassed === true
2528
2568
  );
2529
2569
  const failed = attempts.filter(