@kairos-sdk/core 0.4.0 → 0.5.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.
@@ -1,175 +1,26 @@
1
1
  import {
2
- KairosError,
2
+ PromptBuilder,
3
+ inferWorkflowType
4
+ } from "./chunk-EVOAYH2K.js";
5
+ import {
6
+ GenerationError,
7
+ N8nProvider,
8
+ NullLibrary,
9
+ ResponseParseError,
10
+ ValidationError
11
+ } from "./chunk-5GAY7CSJ.js";
12
+ import {
13
+ GuardError,
3
14
  N8nApiClient,
4
15
  N8nFieldStripper,
5
16
  N8nValidator,
6
17
  PatternAnalyzer,
7
- PromptBuilder,
18
+ TelemetryCollector,
8
19
  TelemetryReader,
9
20
  generateUUID,
10
21
  nullLogger,
11
22
  scoreToMode
12
- } from "./chunk-NJ6QZBIC.js";
13
-
14
- // src/library/null-library.ts
15
- var NullLibrary = class {
16
- async initialize() {
17
- }
18
- async search(_description, _options) {
19
- return [];
20
- }
21
- async save(_workflow, _metadata) {
22
- return generateUUID();
23
- }
24
- async recordDeployment(_id) {
25
- }
26
- async recordOutcome(_id, _outcome) {
27
- }
28
- async get(_id) {
29
- return null;
30
- }
31
- async list(_filters) {
32
- return [];
33
- }
34
- };
35
-
36
- // src/errors/guard-error.ts
37
- var GuardError = class extends KairosError {
38
- constructor(message) {
39
- super(message);
40
- this.name = "GuardError";
41
- }
42
- };
43
-
44
- // src/providers/n8n/provider.ts
45
- var N8nProvider = class {
46
- constructor(client, stripper) {
47
- this.client = client;
48
- this.stripper = stripper;
49
- }
50
- client;
51
- stripper;
52
- platform = "n8n";
53
- async deploy(workflow) {
54
- const stripped = this.stripper.stripForCreate(workflow);
55
- const response = await this.client.createWorkflow(stripped);
56
- return { workflowId: response.id, name: response.name };
57
- }
58
- async update(id, workflow) {
59
- const stripped = this.stripper.stripForUpdate(workflow);
60
- const response = await this.client.updateWorkflow(id, stripped);
61
- return { workflowId: response.id, name: response.name };
62
- }
63
- async get(id) {
64
- const response = await this.client.getWorkflow(id);
65
- return {
66
- name: response.name,
67
- nodes: response.nodes,
68
- connections: response.connections,
69
- ...response.settings !== void 0 ? { settings: response.settings } : {},
70
- ...response.tags !== void 0 ? { tags: response.tags } : {}
71
- };
72
- }
73
- async list() {
74
- return this.client.listWorkflows();
75
- }
76
- async activate(id) {
77
- await this.client.activateWorkflow(id);
78
- }
79
- async deactivate(id) {
80
- await this.client.deactivateWorkflow(id);
81
- }
82
- async delete(id, options) {
83
- if (options.confirm !== true) {
84
- throw new GuardError("delete() requires { confirm: true } to prevent accidental deletion");
85
- }
86
- await this.client.deleteWorkflow(id);
87
- }
88
- async executions(workflowId, filter) {
89
- return this.client.getExecutions(workflowId, filter);
90
- }
91
- async execution(id) {
92
- return this.client.getExecution(id);
93
- }
94
- async listTags() {
95
- return this.client.listTags();
96
- }
97
- async createTag(name) {
98
- return this.client.createTag(name);
99
- }
100
- async tag(workflowId, tagIds) {
101
- await this.client.tagWorkflow(workflowId, tagIds);
102
- }
103
- async untag(workflowId, tagIds) {
104
- await this.client.untagWorkflow(workflowId, tagIds);
105
- }
106
- };
107
-
108
- // src/errors/generation-error.ts
109
- var GenerationError = class extends KairosError {
110
- constructor(message, cause) {
111
- super(message, cause);
112
- this.name = "GenerationError";
113
- }
114
- };
115
-
116
- // src/errors/response-parse-error.ts
117
- var ResponseParseError = class extends KairosError {
118
- constructor(message, cause) {
119
- super(message, cause);
120
- this.name = "ResponseParseError";
121
- }
122
- };
123
-
124
- // src/errors/validation-error.ts
125
- var ValidationError = class extends KairosError {
126
- constructor(message, issues, attemptMetadata, warnedRules) {
127
- super(message);
128
- this.issues = issues;
129
- this.attemptMetadata = attemptMetadata;
130
- this.warnedRules = warnedRules;
131
- this.name = "ValidationError";
132
- }
133
- issues;
134
- attemptMetadata;
135
- warnedRules;
136
- };
137
-
138
- // src/telemetry/collector.ts
139
- import { appendFile, mkdir } from "fs/promises";
140
- import { join } from "path";
141
- import { homedir } from "os";
142
-
143
- // src/telemetry/types.ts
144
- var TELEMETRY_SCHEMA_VERSION = 2;
145
-
146
- // src/telemetry/collector.ts
147
- var TelemetryCollector = class {
148
- dir;
149
- sessionId;
150
- dirReady = null;
151
- constructor(dir) {
152
- this.dir = dir ?? join(homedir(), ".kairos", "telemetry");
153
- this.sessionId = generateUUID();
154
- }
155
- async emit(eventType, data) {
156
- const event = {
157
- schemaVersion: TELEMETRY_SCHEMA_VERSION,
158
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
159
- sessionId: this.sessionId,
160
- eventType,
161
- data
162
- };
163
- if (!this.dirReady) {
164
- this.dirReady = mkdir(this.dir, { recursive: true }).then(() => {
165
- });
166
- }
167
- await this.dirReady;
168
- const filename = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) + ".jsonl";
169
- const filepath = join(this.dir, filename);
170
- await appendFile(filepath, JSON.stringify(event) + "\n", "utf-8");
171
- }
172
- };
23
+ } from "./chunk-KIFT5LA7.js";
173
24
 
174
25
  // src/client.ts
175
26
  import Anthropic from "@anthropic-ai/sdk";
@@ -217,12 +68,12 @@ var GENERATE_WORKFLOW_TOOL = {
217
68
  }
218
69
  };
219
70
  var WorkflowDesigner = class {
220
- constructor(anthropic, model, logger) {
71
+ constructor(anthropic, model, logger, patternsPath) {
221
72
  this.anthropic = anthropic;
222
73
  this.model = model;
223
74
  this.logger = logger;
224
75
  this.validator = new N8nValidator();
225
- this.promptBuilder = new PromptBuilder();
76
+ this.promptBuilder = new PromptBuilder(patternsPath);
226
77
  }
227
78
  anthropic;
228
79
  model;
@@ -245,7 +96,8 @@ var WorkflowDesigner = class {
245
96
  const issueLines = lastErrors.map(
246
97
  (i) => `- [Rule ${i.rule}] ${i.message}${i.nodeId ? ` (node: ${i.nodeId})` : ""}`
247
98
  );
248
- userMessage = this.promptBuilder.buildCorrectionMessage(request, matches, issueLines, attempt - 1);
99
+ const failingRuleIds = lastErrors.map((i) => i.rule);
100
+ userMessage = this.promptBuilder.buildCorrectionMessage(request, matches, issueLines, attempt - 1, failingRuleIds);
249
101
  this.logger.debug(`WorkflowDesigner: correction attempt ${attempt}`, { issueCount: lastErrors.length });
250
102
  }
251
103
  const start = Date.now();
@@ -306,6 +158,11 @@ var WorkflowDesigner = class {
306
158
  }
307
159
  }
308
160
  extractToolUse(message) {
161
+ if (message.stop_reason === "max_tokens") {
162
+ throw new GenerationError(
163
+ "Claude response was truncated (max_tokens reached) \u2014 the workflow may be too large. Try a simpler description or break it into smaller workflows."
164
+ );
165
+ }
309
166
  const toolUseBlock = message.content.find(
310
167
  (block) => block.type === "tool_use"
311
168
  );
@@ -332,7 +189,9 @@ var WorkflowDesigner = class {
332
189
  };
333
190
 
334
191
  // src/client.ts
335
- var DEFAULT_MODEL = "claude-sonnet-4-6";
192
+ import { homedir } from "os";
193
+ import { join } from "path";
194
+ var DEFAULT_MODEL = process.env["KAIROS_MODEL"] ?? "claude-sonnet-4-6";
336
195
  var Kairos = class {
337
196
  provider;
338
197
  designer;
@@ -360,7 +219,8 @@ var Kairos = class {
360
219
  this.provider = null;
361
220
  }
362
221
  const anthropic = new Anthropic({ apiKey: options.anthropicApiKey });
363
- this.designer = new WorkflowDesigner(anthropic, this.model, logger);
222
+ const patternsPath = typeof options.telemetry === "string" ? join(options.telemetry, "..", "patterns.json") : join(homedir(), ".kairos", "patterns.json");
223
+ this.designer = new WorkflowDesigner(anthropic, this.model, logger, patternsPath);
364
224
  this.validator = new N8nValidator();
365
225
  this.library = options.library ?? new NullLibrary();
366
226
  this.logger = logger;
@@ -393,11 +253,13 @@ var Kairos = class {
393
253
  this.validateDescription(description);
394
254
  this.logger.info("Kairos.build", { description, dryRun: options?.dryRun });
395
255
  const buildStart = Date.now();
256
+ const runId = generateUUID();
257
+ const workflowType = inferWorkflowType(description);
396
258
  await this.telemetry?.emit("build_start", {
397
259
  description,
398
260
  model: this.model,
399
261
  dryRun: options?.dryRun ?? false
400
- });
262
+ }, runId);
401
263
  await this.library.initialize();
402
264
  const matches = await this.library.search(description);
403
265
  if (matches.length > 0) {
@@ -432,8 +294,9 @@ var Kairos = class {
432
294
  tokensOutput: meta.tokensOutput,
433
295
  validationPassed: meta.validationPassed,
434
296
  issueCount: meta.issues.length,
435
- issues: meta.issues.map((i) => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null }))
436
- });
297
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
298
+ workflowType
299
+ }, runId);
437
300
  }
438
301
  await this.telemetry?.emit("build_complete", {
439
302
  description,
@@ -446,13 +309,14 @@ var Kairos = class {
446
309
  workflowId: null,
447
310
  dryRun: options?.dryRun ?? false,
448
311
  credentialsNeeded: 0,
449
- warnedRules: err.warnedRules ?? []
450
- });
312
+ warnedRules: err.warnedRules ?? [],
313
+ workflowType
314
+ }, runId);
451
315
  this.updatePatterns();
452
316
  }
453
317
  throw err;
454
318
  }
455
- await this.emitAttemptTelemetry(description, designResult);
319
+ await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
456
320
  const workflow = options?.name ? { ...designResult.workflow, name: options.name } : designResult.workflow;
457
321
  this.saveToLibrary(workflow, description, designResult, matches);
458
322
  if (options?.dryRun) {
@@ -469,8 +333,9 @@ var Kairos = class {
469
333
  workflowId: null,
470
334
  dryRun: true,
471
335
  credentialsNeeded: designResult.credentialsNeeded.length,
472
- warnedRules: designResult.warnedRules
473
- });
336
+ warnedRules: designResult.warnedRules,
337
+ workflowType
338
+ }, runId);
474
339
  this.updatePatterns();
475
340
  return {
476
341
  workflowId: null,
@@ -484,10 +349,19 @@ var Kairos = class {
484
349
  }
485
350
  const provider = this.requireProvider();
486
351
  const deployed = await provider.deploy(workflow);
487
- this.recordDeploy();
352
+ this.logger.info("Workflow deployed to n8n", { workflowId: deployed.workflowId, name: deployed.name });
353
+ this.recordDeploy(deployed.workflowId);
488
354
  if (options?.activate) {
489
355
  await provider.activate(deployed.workflowId);
490
356
  }
357
+ let smokeTestResult;
358
+ if (options?.smokeTest) {
359
+ smokeTestResult = await provider.smokeTest(deployed.workflowId, workflow).catch((err) => {
360
+ this.logger.warn("Smoke test threw unexpectedly", { err: String(err) });
361
+ return { status: "error", triggerType: "manual", error: String(err) };
362
+ });
363
+ this.logger.info("Smoke test complete", { status: smokeTestResult.status, triggerType: smokeTestResult.triggerType });
364
+ }
491
365
  const totalTokensInput = designResult.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0);
492
366
  const totalTokensOutput = designResult.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0);
493
367
  await this.telemetry?.emit("build_complete", {
@@ -501,8 +375,9 @@ var Kairos = class {
501
375
  workflowId: deployed.workflowId,
502
376
  dryRun: false,
503
377
  credentialsNeeded: designResult.credentialsNeeded.length,
504
- warnedRules: designResult.warnedRules
505
- });
378
+ warnedRules: designResult.warnedRules,
379
+ workflowType
380
+ }, runId);
506
381
  this.updatePatterns();
507
382
  return {
508
383
  workflowId: deployed.workflowId,
@@ -511,18 +386,21 @@ var Kairos = class {
511
386
  credentialsNeeded: designResult.credentialsNeeded,
512
387
  activationRequired: !options?.activate,
513
388
  generationAttempts: designResult.attempts,
514
- dryRun: false
389
+ dryRun: false,
390
+ ...smokeTestResult !== void 0 ? { smokeTest: smokeTestResult } : {}
515
391
  };
516
392
  }
517
393
  async replace(id, description) {
518
394
  this.validateDescription(description);
519
395
  this.logger.info("Kairos.update", { id, description });
520
396
  const buildStart = Date.now();
397
+ const runId = generateUUID();
398
+ const workflowType = inferWorkflowType(description);
521
399
  await this.telemetry?.emit("build_start", {
522
400
  description,
523
401
  model: this.model,
524
402
  dryRun: false
525
- });
403
+ }, runId);
526
404
  await this.library.initialize();
527
405
  const matches = await this.library.search(description);
528
406
  const globalFailureRates = await this.telemetryReader?.getFailureRates() ?? [];
@@ -541,8 +419,9 @@ var Kairos = class {
541
419
  tokensOutput: meta.tokensOutput,
542
420
  validationPassed: meta.validationPassed,
543
421
  issueCount: meta.issues.length,
544
- issues: meta.issues.map((i) => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null }))
545
- });
422
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
423
+ workflowType
424
+ }, runId);
546
425
  }
547
426
  await this.telemetry?.emit("build_complete", {
548
427
  description,
@@ -555,16 +434,18 @@ var Kairos = class {
555
434
  workflowId: null,
556
435
  dryRun: false,
557
436
  credentialsNeeded: 0,
558
- warnedRules: err.warnedRules ?? []
559
- });
437
+ warnedRules: err.warnedRules ?? [],
438
+ workflowType
439
+ }, runId);
560
440
  this.updatePatterns();
561
441
  }
562
442
  throw err;
563
443
  }
564
- await this.emitAttemptTelemetry(description, designResult);
444
+ await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
565
445
  const provider = this.requireProvider();
566
446
  const deployed = await provider.update(id, designResult.workflow);
567
- this.saveToLibrary(designResult.workflow, description, designResult, matches);
447
+ this.logger.info("Workflow updated in n8n", { workflowId: deployed.workflowId, name: deployed.name });
448
+ this.saveToLibrary(designResult.workflow, description, designResult, matches, deployed.workflowId);
568
449
  this.recordDeploy();
569
450
  const totalTokensInput = designResult.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0);
570
451
  const totalTokensOutput = designResult.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0);
@@ -579,8 +460,9 @@ var Kairos = class {
579
460
  workflowId: deployed.workflowId,
580
461
  dryRun: false,
581
462
  credentialsNeeded: designResult.credentialsNeeded.length,
582
- warnedRules: designResult.warnedRules
583
- });
463
+ warnedRules: designResult.warnedRules,
464
+ workflowType
465
+ }, runId);
584
466
  this.updatePatterns();
585
467
  return {
586
468
  workflowId: deployed.workflowId,
@@ -603,7 +485,7 @@ var Kairos = class {
603
485
  return null;
604
486
  });
605
487
  }
606
- async emitAttemptTelemetry(description, designResult) {
488
+ async emitAttemptTelemetry(description, designResult, workflowType, runId) {
607
489
  for (const meta of designResult.attemptMetadata) {
608
490
  await this.telemetry?.emit("generation_attempt", {
609
491
  description,
@@ -614,14 +496,15 @@ var Kairos = class {
614
496
  tokensOutput: meta.tokensOutput,
615
497
  validationPassed: meta.validationPassed,
616
498
  issueCount: meta.issues.length,
617
- issues: meta.issues.map((i) => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null }))
618
- });
499
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
500
+ workflowType
501
+ }, runId);
619
502
  }
620
503
  }
621
- recordDeploy() {
504
+ recordDeploy(n8nWorkflowId) {
622
505
  this.saveQueue = this.saveQueue.then(async (savedId) => {
623
506
  if (savedId) {
624
- await this.library.recordDeployment(savedId);
507
+ await this.library.recordDeployment(savedId, n8nWorkflowId);
625
508
  }
626
509
  return savedId;
627
510
  }).catch((err) => {
@@ -629,7 +512,7 @@ var Kairos = class {
629
512
  return null;
630
513
  });
631
514
  }
632
- saveToLibrary(workflow, description, designResult, matches) {
515
+ saveToLibrary(workflow, description, designResult, matches, n8nWorkflowId) {
633
516
  const failedAttempts = designResult.attemptMetadata.filter((m) => !m.validationPassed);
634
517
  const failurePatterns = failedAttempts.flatMap(
635
518
  (m) => m.issues.map((i) => ({ rule: i.rule, message: i.message }))
@@ -655,6 +538,7 @@ var Kairos = class {
655
538
  if (matches.length > 0) metadata.sourceWorkflowIds = matches.map((m) => m.workflow.id);
656
539
  if (topMatch) metadata.topMatchScore = topMatch.score;
657
540
  if (designResult.credentialsNeeded.length > 0) metadata.credentialsNeeded = designResult.credentialsNeeded;
541
+ if (n8nWorkflowId) metadata.n8nWorkflowId = n8nWorkflowId;
658
542
  const firstTryPass = designResult.attemptMetadata.length > 0 && designResult.attemptMetadata[0].validationPassed;
659
543
  const failedRules = Array.from(new Set(
660
544
  designResult.attemptMetadata.filter((m) => !m.validationPassed).flatMap((m) => m.issues.map((i) => i.rule))
@@ -712,190 +596,7 @@ var Kairos = class {
712
596
  }
713
597
  };
714
598
 
715
- // src/templates/safety.ts
716
- var BLOCKED_NODE_TYPES = /* @__PURE__ */ new Set([
717
- "n8n-nodes-base.code",
718
- "n8n-nodes-base.executeCommand",
719
- "n8n-nodes-base.ssh"
720
- ]);
721
- var REVIEW_NODE_TYPES = /* @__PURE__ */ new Set([
722
- "n8n-nodes-base.httpRequest"
723
- ]);
724
- var SECRET_PATTERNS = [
725
- /sk-[a-zA-Z0-9]{20,}/,
726
- /ghp_[a-zA-Z0-9]{36}/,
727
- /xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+/,
728
- /AIza[a-zA-Z0-9_-]{35}/,
729
- /AKIA[A-Z0-9]{16}/
730
- ];
731
- function assessTemplateSafety(workflow) {
732
- const reasons = [];
733
- let worst = "safe";
734
- const escalate = (level, reason) => {
735
- reasons.push(reason);
736
- if (level === "blocked") worst = "blocked";
737
- else if (level === "review" && worst === "safe") worst = "review";
738
- };
739
- for (const node of workflow.nodes) {
740
- if (BLOCKED_NODE_TYPES.has(node.type)) {
741
- escalate("blocked", `Contains ${node.type} node "${node.name}"`);
742
- }
743
- if (REVIEW_NODE_TYPES.has(node.type)) {
744
- escalate("review", `Contains ${node.type} node "${node.name}"`);
745
- }
746
- const paramStr = JSON.stringify(node.parameters);
747
- for (const pattern of SECRET_PATTERNS) {
748
- if (pattern.test(paramStr)) {
749
- escalate("blocked", `Node "${node.name}" parameters contain a hardcoded secret`);
750
- break;
751
- }
752
- }
753
- }
754
- return { trustLevel: worst, reasons };
755
- }
756
-
757
- // src/templates/syncer.ts
758
- var N8N_TEMPLATE_API = "https://api.n8n.io/api/templates";
759
- var PAGE_SIZE = 50;
760
- var DELAY_BETWEEN_FETCHES_MS = 200;
761
- var DEFAULT_SETTINGS = {
762
- executionOrder: "v1",
763
- saveManualExecutions: true,
764
- timezone: "UTC"
765
- };
766
- var TemplateSyncer = class {
767
- constructor(library, logger) {
768
- this.library = library;
769
- this.validator = new N8nValidator();
770
- this.logger = logger;
771
- }
772
- library;
773
- validator;
774
- logger;
775
- async sync(options) {
776
- const maxTemplates = options?.maxTemplates ?? 500;
777
- await this.library.initialize();
778
- const existing = await this.library.list();
779
- const existingSourceIds = new Set(
780
- existing.filter((w) => w.sourceKind === "n8n-template" && w.sourceId).map((w) => w.sourceId)
781
- );
782
- const progress = {
783
- total: 0,
784
- processed: 0,
785
- saved: 0,
786
- skippedPaid: 0,
787
- skippedDuplicate: 0,
788
- blocked: 0,
789
- reviewed: 0
790
- };
791
- const templateIds = await this.fetchTemplateIds(maxTemplates, progress);
792
- for (const id of templateIds) {
793
- if (existingSourceIds.has(String(id))) {
794
- progress.skippedDuplicate++;
795
- progress.processed++;
796
- options?.onProgress?.(progress);
797
- continue;
798
- }
799
- try {
800
- await this.processTemplate(id, progress);
801
- } catch (err) {
802
- this.logger.warn(`Failed to process template ${id}`, { err: String(err) });
803
- }
804
- progress.processed++;
805
- options?.onProgress?.(progress);
806
- await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
807
- }
808
- return progress;
809
- }
810
- async fetchTemplateIds(max, progress) {
811
- const ids = [];
812
- let page = 1;
813
- while (ids.length < max) {
814
- const url = `${N8N_TEMPLATE_API}/search?page=${page}&rows=${PAGE_SIZE}`;
815
- const response = await fetch(url);
816
- if (!response.ok) break;
817
- const data = await response.json();
818
- progress.total = Math.min(data.totalWorkflows, max);
819
- for (const template of data.workflows) {
820
- if (ids.length >= max) break;
821
- if (template.price && template.price > 0) {
822
- progress.skippedPaid++;
823
- continue;
824
- }
825
- ids.push(template.id);
826
- }
827
- if (data.workflows.length < PAGE_SIZE) break;
828
- page++;
829
- await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
830
- }
831
- return ids;
832
- }
833
- async processTemplate(id, progress) {
834
- const url = `${N8N_TEMPLATE_API}/workflows/${id}`;
835
- const response = await fetch(url);
836
- if (!response.ok) return;
837
- const data = await response.json();
838
- const templateMeta = data.workflow;
839
- const rawWorkflow = templateMeta.workflow;
840
- if (!rawWorkflow?.nodes?.length) return;
841
- const workflow = {
842
- name: templateMeta.name,
843
- nodes: rawWorkflow.nodes.filter((n) => n.type && n.name),
844
- connections: rawWorkflow.connections,
845
- settings: rawWorkflow.settings ? { executionOrder: "v1", ...rawWorkflow.settings } : { ...DEFAULT_SETTINGS }
846
- };
847
- const validation = this.validator.validate(workflow);
848
- const validationErrors = validation.issues.filter((i) => i.severity === "error");
849
- if (validationErrors.length > 0) {
850
- progress.blocked++;
851
- this.logger.debug(`Template ${id} blocked: ${validationErrors.length} validation errors`);
852
- return;
853
- }
854
- const safety = assessTemplateSafety(workflow);
855
- if (safety.trustLevel === "blocked") {
856
- progress.blocked++;
857
- this.logger.debug(`Template ${id} blocked: ${safety.reasons.join(", ")}`);
858
- return;
859
- }
860
- if (safety.trustLevel === "review") {
861
- progress.reviewed++;
862
- }
863
- const description = this.cleanDescription(templateMeta.description);
864
- const autoTags = Array.from(new Set(
865
- workflow.nodes.flatMap((n) => {
866
- const bare = n.type.split(".").pop() ?? "";
867
- const tags = [bare];
868
- if (n.type.includes("Trigger") || n.type.includes("trigger")) tags.push(`trigger:${bare}`);
869
- if (n.type.includes("langchain")) tags.push("ai");
870
- return tags;
871
- })
872
- ));
873
- const metadata = {
874
- description,
875
- tags: autoTags,
876
- sourceKind: "n8n-template",
877
- sourceId: String(id),
878
- sourceUrl: `https://n8n.io/workflows/${id}`,
879
- trustLevel: safety.trustLevel
880
- };
881
- await this.library.save(workflow, metadata);
882
- progress.saved++;
883
- this.logger.debug(`Template ${id} saved: "${templateMeta.name}" (${safety.trustLevel})`);
884
- }
885
- cleanDescription(raw) {
886
- return raw.replace(/#{1,6}\s*/g, "").replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\n{3,}/g, "\n\n").trim().slice(0, 500);
887
- }
888
- };
889
-
890
599
  export {
891
- NullLibrary,
892
- GuardError,
893
- N8nProvider,
894
- GenerationError,
895
- ResponseParseError,
896
- ValidationError,
897
- TelemetryCollector,
898
- Kairos,
899
- TemplateSyncer
600
+ Kairos
900
601
  };
901
- //# sourceMappingURL=chunk-N6LRD2FN.js.map
602
+ //# sourceMappingURL=chunk-HBGZTUUZ.js.map