@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.
- package/README.md +21 -10
- package/dist/chunk-5GAY7CSJ.js +411 -0
- package/dist/chunk-5GAY7CSJ.js.map +1 -0
- package/dist/chunk-6FOFWVMG.js +1 -0
- package/dist/chunk-6FOFWVMG.js.map +1 -0
- package/dist/chunk-EVOAYH2K.js +569 -0
- package/dist/chunk-EVOAYH2K.js.map +1 -0
- package/dist/{chunk-N6LRD2FN.js → chunk-HBGZTUUZ.js} +81 -380
- package/dist/chunk-HBGZTUUZ.js.map +1 -0
- package/dist/{chunk-NJ6QZBIC.js → chunk-KIFT5LA7.js} +971 -572
- package/dist/chunk-KIFT5LA7.js.map +1 -0
- package/dist/cli.cjs +1341 -236
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +83 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1259 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -540
- package/dist/index.d.ts +3 -540
- package/dist/index.js +9 -5
- package/dist/mcp-server.cjs +1473 -402
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/mcp-server.js +357 -232
- package/dist/mcp-server.js.map +1 -1
- package/dist/reader-B5mV20H6.d.cts +596 -0
- package/dist/reader-B5mV20H6.d.ts +596 -0
- package/dist/standalone.cjs +2978 -0
- package/dist/standalone.cjs.map +1 -0
- package/dist/standalone.d.cts +106 -0
- package/dist/standalone.d.ts +106 -0
- package/dist/standalone.js +58 -0
- package/dist/standalone.js.map +1 -0
- package/package.json +9 -1
- package/dist/chunk-N6LRD2FN.js.map +0 -1
- package/dist/chunk-NJ6QZBIC.js.map +0 -1
|
@@ -1,175 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
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
|
-
|
|
18
|
+
TelemetryCollector,
|
|
8
19
|
TelemetryReader,
|
|
9
20
|
generateUUID,
|
|
10
21
|
nullLogger,
|
|
11
22
|
scoreToMode
|
|
12
|
-
} from "./chunk-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
892
|
-
GuardError,
|
|
893
|
-
N8nProvider,
|
|
894
|
-
GenerationError,
|
|
895
|
-
ResponseParseError,
|
|
896
|
-
ValidationError,
|
|
897
|
-
TelemetryCollector,
|
|
898
|
-
Kairos,
|
|
899
|
-
TemplateSyncer
|
|
600
|
+
Kairos
|
|
900
601
|
};
|
|
901
|
-
//# sourceMappingURL=chunk-
|
|
602
|
+
//# sourceMappingURL=chunk-HBGZTUUZ.js.map
|