@kairos-sdk/core 0.3.2 → 0.4.5
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 +46 -11
- package/dist/{chunk-KQSNT3HZ.js → chunk-4TS6GW6O.js} +148 -368
- package/dist/chunk-4TS6GW6O.js.map +1 -0
- package/dist/chunk-6CLI43FI.js +315 -0
- package/dist/chunk-6CLI43FI.js.map +1 -0
- package/dist/chunk-6FOFWVMG.js +1 -0
- package/dist/chunk-6FOFWVMG.js.map +1 -0
- package/dist/{chunk-RYGYNOR6.js → chunk-6IXW3WCC.js} +936 -412
- package/dist/chunk-6IXW3WCC.js.map +1 -0
- package/dist/chunk-CR2NHLOH.js +523 -0
- package/dist/chunk-CR2NHLOH.js.map +1 -0
- package/dist/cli.cjs +1402 -170
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +140 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1262 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -537
- package/dist/index.d.ts +5 -537
- package/dist/index.js +8 -4
- package/dist/mcp-server.cjs +1259 -129
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/mcp-server.js +113 -8
- package/dist/mcp-server.js.map +1 -1
- package/dist/reader-CpUcHhKW.d.cts +566 -0
- package/dist/reader-CpUcHhKW.d.ts +566 -0
- package/dist/standalone.cjs +2460 -0
- package/dist/standalone.cjs.map +1 -0
- package/dist/standalone.d.cts +105 -0
- package/dist/standalone.d.ts +105 -0
- package/dist/standalone.js +58 -0
- package/dist/standalone.js.map +1 -0
- package/package.json +6 -1
- package/dist/chunk-KQSNT3HZ.js.map +0 -1
- package/dist/chunk-RYGYNOR6.js.map +0 -1
|
@@ -1,170 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
PromptBuilder,
|
|
3
|
+
inferWorkflowType
|
|
4
|
+
} from "./chunk-CR2NHLOH.js";
|
|
5
|
+
import {
|
|
6
|
+
GenerationError,
|
|
7
|
+
GuardError,
|
|
8
|
+
N8nProvider,
|
|
9
|
+
NullLibrary,
|
|
10
|
+
ResponseParseError,
|
|
11
|
+
ValidationError
|
|
12
|
+
} from "./chunk-6CLI43FI.js";
|
|
13
|
+
import {
|
|
3
14
|
N8nApiClient,
|
|
4
15
|
N8nFieldStripper,
|
|
5
16
|
N8nValidator,
|
|
6
|
-
|
|
17
|
+
PatternAnalyzer,
|
|
18
|
+
TelemetryCollector,
|
|
7
19
|
TelemetryReader,
|
|
8
20
|
generateUUID,
|
|
9
21
|
nullLogger,
|
|
10
22
|
scoreToMode
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
|
|
13
|
-
// src/library/null-library.ts
|
|
14
|
-
var NullLibrary = class {
|
|
15
|
-
async initialize() {
|
|
16
|
-
}
|
|
17
|
-
async search(_description, _options) {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
async save(_workflow, _metadata) {
|
|
21
|
-
return generateUUID();
|
|
22
|
-
}
|
|
23
|
-
async recordDeployment(_id) {
|
|
24
|
-
}
|
|
25
|
-
async recordOutcome(_id, _outcome) {
|
|
26
|
-
}
|
|
27
|
-
async get(_id) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
async list(_filters) {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// src/errors/guard-error.ts
|
|
36
|
-
var GuardError = class extends KairosError {
|
|
37
|
-
constructor(message) {
|
|
38
|
-
super(message);
|
|
39
|
-
this.name = "GuardError";
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// src/providers/n8n/provider.ts
|
|
44
|
-
var N8nProvider = class {
|
|
45
|
-
constructor(client, stripper) {
|
|
46
|
-
this.client = client;
|
|
47
|
-
this.stripper = stripper;
|
|
48
|
-
}
|
|
49
|
-
client;
|
|
50
|
-
stripper;
|
|
51
|
-
platform = "n8n";
|
|
52
|
-
async deploy(workflow) {
|
|
53
|
-
const stripped = this.stripper.stripForCreate(workflow);
|
|
54
|
-
const response = await this.client.createWorkflow(stripped);
|
|
55
|
-
return { workflowId: response.id, name: response.name };
|
|
56
|
-
}
|
|
57
|
-
async update(id, workflow) {
|
|
58
|
-
const stripped = this.stripper.stripForUpdate(workflow);
|
|
59
|
-
const response = await this.client.updateWorkflow(id, stripped);
|
|
60
|
-
return { workflowId: response.id, name: response.name };
|
|
61
|
-
}
|
|
62
|
-
async get(id) {
|
|
63
|
-
const response = await this.client.getWorkflow(id);
|
|
64
|
-
return {
|
|
65
|
-
name: response.name,
|
|
66
|
-
nodes: response.nodes,
|
|
67
|
-
connections: response.connections,
|
|
68
|
-
...response.settings !== void 0 ? { settings: response.settings } : {},
|
|
69
|
-
...response.tags !== void 0 ? { tags: response.tags } : {}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
async list() {
|
|
73
|
-
return this.client.listWorkflows();
|
|
74
|
-
}
|
|
75
|
-
async activate(id) {
|
|
76
|
-
await this.client.activateWorkflow(id);
|
|
77
|
-
}
|
|
78
|
-
async deactivate(id) {
|
|
79
|
-
await this.client.deactivateWorkflow(id);
|
|
80
|
-
}
|
|
81
|
-
async delete(id, options) {
|
|
82
|
-
if (options.confirm !== true) {
|
|
83
|
-
throw new GuardError("delete() requires { confirm: true } to prevent accidental deletion");
|
|
84
|
-
}
|
|
85
|
-
await this.client.deleteWorkflow(id);
|
|
86
|
-
}
|
|
87
|
-
async executions(workflowId, filter) {
|
|
88
|
-
return this.client.getExecutions(workflowId, filter);
|
|
89
|
-
}
|
|
90
|
-
async execution(id) {
|
|
91
|
-
return this.client.getExecution(id);
|
|
92
|
-
}
|
|
93
|
-
async listTags() {
|
|
94
|
-
return this.client.listTags();
|
|
95
|
-
}
|
|
96
|
-
async createTag(name) {
|
|
97
|
-
return this.client.createTag(name);
|
|
98
|
-
}
|
|
99
|
-
async tag(workflowId, tagIds) {
|
|
100
|
-
await this.client.tagWorkflow(workflowId, tagIds);
|
|
101
|
-
}
|
|
102
|
-
async untag(workflowId, tagIds) {
|
|
103
|
-
await this.client.untagWorkflow(workflowId, tagIds);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// src/errors/generation-error.ts
|
|
108
|
-
var GenerationError = class extends KairosError {
|
|
109
|
-
constructor(message, cause) {
|
|
110
|
-
super(message, cause);
|
|
111
|
-
this.name = "GenerationError";
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// src/errors/response-parse-error.ts
|
|
116
|
-
var ResponseParseError = class extends KairosError {
|
|
117
|
-
constructor(message, cause) {
|
|
118
|
-
super(message, cause);
|
|
119
|
-
this.name = "ResponseParseError";
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// src/errors/validation-error.ts
|
|
124
|
-
var ValidationError = class extends KairosError {
|
|
125
|
-
constructor(message, issues) {
|
|
126
|
-
super(message);
|
|
127
|
-
this.issues = issues;
|
|
128
|
-
this.name = "ValidationError";
|
|
129
|
-
}
|
|
130
|
-
issues;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// src/telemetry/collector.ts
|
|
134
|
-
import { appendFile, mkdir } from "fs/promises";
|
|
135
|
-
import { join } from "path";
|
|
136
|
-
import { homedir } from "os";
|
|
137
|
-
|
|
138
|
-
// src/telemetry/types.ts
|
|
139
|
-
var TELEMETRY_SCHEMA_VERSION = 2;
|
|
140
|
-
|
|
141
|
-
// src/telemetry/collector.ts
|
|
142
|
-
var TelemetryCollector = class {
|
|
143
|
-
dir;
|
|
144
|
-
sessionId;
|
|
145
|
-
dirReady = null;
|
|
146
|
-
constructor(dir) {
|
|
147
|
-
this.dir = dir ?? join(homedir(), ".kairos", "telemetry");
|
|
148
|
-
this.sessionId = generateUUID();
|
|
149
|
-
}
|
|
150
|
-
async emit(eventType, data) {
|
|
151
|
-
const event = {
|
|
152
|
-
schemaVersion: TELEMETRY_SCHEMA_VERSION,
|
|
153
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
154
|
-
sessionId: this.sessionId,
|
|
155
|
-
eventType,
|
|
156
|
-
data
|
|
157
|
-
};
|
|
158
|
-
if (!this.dirReady) {
|
|
159
|
-
this.dirReady = mkdir(this.dir, { recursive: true }).then(() => {
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
await this.dirReady;
|
|
163
|
-
const filename = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) + ".jsonl";
|
|
164
|
-
const filepath = join(this.dir, filename);
|
|
165
|
-
await appendFile(filepath, JSON.stringify(event) + "\n", "utf-8");
|
|
166
|
-
}
|
|
167
|
-
};
|
|
23
|
+
} from "./chunk-6IXW3WCC.js";
|
|
168
24
|
|
|
169
25
|
// src/client.ts
|
|
170
26
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -212,12 +68,12 @@ var GENERATE_WORKFLOW_TOOL = {
|
|
|
212
68
|
}
|
|
213
69
|
};
|
|
214
70
|
var WorkflowDesigner = class {
|
|
215
|
-
constructor(anthropic, model, logger) {
|
|
71
|
+
constructor(anthropic, model, logger, patternsPath) {
|
|
216
72
|
this.anthropic = anthropic;
|
|
217
73
|
this.model = model;
|
|
218
74
|
this.logger = logger;
|
|
219
75
|
this.validator = new N8nValidator();
|
|
220
|
-
this.promptBuilder = new PromptBuilder();
|
|
76
|
+
this.promptBuilder = new PromptBuilder(patternsPath);
|
|
221
77
|
}
|
|
222
78
|
anthropic;
|
|
223
79
|
model;
|
|
@@ -262,7 +118,7 @@ var WorkflowDesigner = class {
|
|
|
262
118
|
issues: validation.issues
|
|
263
119
|
});
|
|
264
120
|
if (validation.valid) {
|
|
265
|
-
return { workflow: parsed.workflow, credentialsNeeded: parsed.credentialsNeeded, attempts, attemptMetadata };
|
|
121
|
+
return { workflow: parsed.workflow, credentialsNeeded: parsed.credentialsNeeded, attempts, attemptMetadata, warnedRules: this.promptBuilder.getWarnedRules() };
|
|
266
122
|
}
|
|
267
123
|
lastErrors = errors;
|
|
268
124
|
this.logger.warn(`WorkflowDesigner: validation failed on attempt ${attempt}`, {
|
|
@@ -272,7 +128,9 @@ var WorkflowDesigner = class {
|
|
|
272
128
|
const finalIssues = attemptMetadata.at(-1)?.issues ?? lastErrors;
|
|
273
129
|
throw new ValidationError(
|
|
274
130
|
`Workflow failed validation after ${MAX_ATTEMPTS} attempts`,
|
|
275
|
-
finalIssues
|
|
131
|
+
finalIssues,
|
|
132
|
+
attemptMetadata,
|
|
133
|
+
this.promptBuilder.getWarnedRules()
|
|
276
134
|
);
|
|
277
135
|
}
|
|
278
136
|
async callClaude(system, userMessage, temperature) {
|
|
@@ -299,6 +157,11 @@ var WorkflowDesigner = class {
|
|
|
299
157
|
}
|
|
300
158
|
}
|
|
301
159
|
extractToolUse(message) {
|
|
160
|
+
if (message.stop_reason === "max_tokens") {
|
|
161
|
+
throw new GenerationError(
|
|
162
|
+
"Claude response was truncated (max_tokens reached) \u2014 the workflow may be too large. Try a simpler description or break it into smaller workflows."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
302
165
|
const toolUseBlock = message.content.find(
|
|
303
166
|
(block) => block.type === "tool_use"
|
|
304
167
|
);
|
|
@@ -325,6 +188,8 @@ var WorkflowDesigner = class {
|
|
|
325
188
|
};
|
|
326
189
|
|
|
327
190
|
// src/client.ts
|
|
191
|
+
import { homedir } from "os";
|
|
192
|
+
import { join } from "path";
|
|
328
193
|
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
329
194
|
var Kairos = class {
|
|
330
195
|
provider;
|
|
@@ -334,6 +199,7 @@ var Kairos = class {
|
|
|
334
199
|
logger;
|
|
335
200
|
telemetry;
|
|
336
201
|
telemetryReader;
|
|
202
|
+
patternAnalyzer;
|
|
337
203
|
model;
|
|
338
204
|
saveQueue = Promise.resolve(null);
|
|
339
205
|
constructor(options) {
|
|
@@ -352,19 +218,23 @@ var Kairos = class {
|
|
|
352
218
|
this.provider = null;
|
|
353
219
|
}
|
|
354
220
|
const anthropic = new Anthropic({ apiKey: options.anthropicApiKey });
|
|
355
|
-
|
|
221
|
+
const patternsPath = typeof options.telemetry === "string" ? join(options.telemetry, "..", "patterns.json") : join(homedir(), ".kairos", "patterns.json");
|
|
222
|
+
this.designer = new WorkflowDesigner(anthropic, this.model, logger, patternsPath);
|
|
356
223
|
this.validator = new N8nValidator();
|
|
357
224
|
this.library = options.library ?? new NullLibrary();
|
|
358
225
|
this.logger = logger;
|
|
359
226
|
if (options.telemetry === true) {
|
|
360
227
|
this.telemetry = new TelemetryCollector();
|
|
361
228
|
this.telemetryReader = new TelemetryReader();
|
|
229
|
+
this.patternAnalyzer = new PatternAnalyzer();
|
|
362
230
|
} else if (typeof options.telemetry === "string") {
|
|
363
231
|
this.telemetry = new TelemetryCollector(options.telemetry);
|
|
364
232
|
this.telemetryReader = new TelemetryReader(options.telemetry);
|
|
233
|
+
this.patternAnalyzer = new PatternAnalyzer(options.telemetry);
|
|
365
234
|
} else {
|
|
366
235
|
this.telemetry = null;
|
|
367
236
|
this.telemetryReader = null;
|
|
237
|
+
this.patternAnalyzer = null;
|
|
368
238
|
}
|
|
369
239
|
}
|
|
370
240
|
requireProvider() {
|
|
@@ -382,11 +252,13 @@ var Kairos = class {
|
|
|
382
252
|
this.validateDescription(description);
|
|
383
253
|
this.logger.info("Kairos.build", { description, dryRun: options?.dryRun });
|
|
384
254
|
const buildStart = Date.now();
|
|
255
|
+
const runId = generateUUID();
|
|
256
|
+
const workflowType = inferWorkflowType(description);
|
|
385
257
|
await this.telemetry?.emit("build_start", {
|
|
386
258
|
description,
|
|
387
259
|
model: this.model,
|
|
388
260
|
dryRun: options?.dryRun ?? false
|
|
389
|
-
});
|
|
261
|
+
}, runId);
|
|
390
262
|
await this.library.initialize();
|
|
391
263
|
const matches = await this.library.search(description);
|
|
392
264
|
if (matches.length > 0) {
|
|
@@ -402,12 +274,48 @@ var Kairos = class {
|
|
|
402
274
|
this.logger.info(`Telemetry: ${highFreq.length} high-frequency failure rule(s) will be warned about`);
|
|
403
275
|
}
|
|
404
276
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
277
|
+
let designResult;
|
|
278
|
+
try {
|
|
279
|
+
designResult = await this.designer.design(
|
|
280
|
+
{ description, ...options?.name ? { name: options.name } : {} },
|
|
281
|
+
matches,
|
|
282
|
+
globalFailureRates
|
|
283
|
+
);
|
|
284
|
+
} catch (err) {
|
|
285
|
+
if (err instanceof ValidationError && err.attemptMetadata) {
|
|
286
|
+
for (const meta of err.attemptMetadata) {
|
|
287
|
+
await this.telemetry?.emit("generation_attempt", {
|
|
288
|
+
description,
|
|
289
|
+
attempt: meta.attempt,
|
|
290
|
+
temperature: meta.temperature,
|
|
291
|
+
durationMs: meta.durationMs,
|
|
292
|
+
tokensInput: meta.tokensInput,
|
|
293
|
+
tokensOutput: meta.tokensOutput,
|
|
294
|
+
validationPassed: meta.validationPassed,
|
|
295
|
+
issueCount: meta.issues.length,
|
|
296
|
+
issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
|
|
297
|
+
workflowType
|
|
298
|
+
}, runId);
|
|
299
|
+
}
|
|
300
|
+
await this.telemetry?.emit("build_complete", {
|
|
301
|
+
description,
|
|
302
|
+
success: false,
|
|
303
|
+
totalAttempts: err.attemptMetadata.length,
|
|
304
|
+
totalDurationMs: Date.now() - buildStart,
|
|
305
|
+
totalTokensInput: err.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0),
|
|
306
|
+
totalTokensOutput: err.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0),
|
|
307
|
+
workflowName: null,
|
|
308
|
+
workflowId: null,
|
|
309
|
+
dryRun: options?.dryRun ?? false,
|
|
310
|
+
credentialsNeeded: 0,
|
|
311
|
+
warnedRules: err.warnedRules ?? [],
|
|
312
|
+
workflowType
|
|
313
|
+
}, runId);
|
|
314
|
+
this.updatePatterns();
|
|
315
|
+
}
|
|
316
|
+
throw err;
|
|
317
|
+
}
|
|
318
|
+
await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
|
|
411
319
|
const workflow = options?.name ? { ...designResult.workflow, name: options.name } : designResult.workflow;
|
|
412
320
|
this.saveToLibrary(workflow, description, designResult, matches);
|
|
413
321
|
if (options?.dryRun) {
|
|
@@ -423,8 +331,11 @@ var Kairos = class {
|
|
|
423
331
|
workflowName: workflow.name,
|
|
424
332
|
workflowId: null,
|
|
425
333
|
dryRun: true,
|
|
426
|
-
credentialsNeeded: designResult.credentialsNeeded.length
|
|
427
|
-
|
|
334
|
+
credentialsNeeded: designResult.credentialsNeeded.length,
|
|
335
|
+
warnedRules: designResult.warnedRules,
|
|
336
|
+
workflowType
|
|
337
|
+
}, runId);
|
|
338
|
+
this.updatePatterns();
|
|
428
339
|
return {
|
|
429
340
|
workflowId: null,
|
|
430
341
|
name: workflow.name,
|
|
@@ -453,8 +364,11 @@ var Kairos = class {
|
|
|
453
364
|
workflowName: deployed.name,
|
|
454
365
|
workflowId: deployed.workflowId,
|
|
455
366
|
dryRun: false,
|
|
456
|
-
credentialsNeeded: designResult.credentialsNeeded.length
|
|
457
|
-
|
|
367
|
+
credentialsNeeded: designResult.credentialsNeeded.length,
|
|
368
|
+
warnedRules: designResult.warnedRules,
|
|
369
|
+
workflowType
|
|
370
|
+
}, runId);
|
|
371
|
+
this.updatePatterns();
|
|
458
372
|
return {
|
|
459
373
|
workflowId: deployed.workflowId,
|
|
460
374
|
name: deployed.name,
|
|
@@ -469,16 +383,54 @@ var Kairos = class {
|
|
|
469
383
|
this.validateDescription(description);
|
|
470
384
|
this.logger.info("Kairos.update", { id, description });
|
|
471
385
|
const buildStart = Date.now();
|
|
386
|
+
const runId = generateUUID();
|
|
387
|
+
const workflowType = inferWorkflowType(description);
|
|
472
388
|
await this.telemetry?.emit("build_start", {
|
|
473
389
|
description,
|
|
474
390
|
model: this.model,
|
|
475
391
|
dryRun: false
|
|
476
|
-
});
|
|
392
|
+
}, runId);
|
|
477
393
|
await this.library.initialize();
|
|
478
394
|
const matches = await this.library.search(description);
|
|
479
395
|
const globalFailureRates = await this.telemetryReader?.getFailureRates() ?? [];
|
|
480
|
-
|
|
481
|
-
|
|
396
|
+
let designResult;
|
|
397
|
+
try {
|
|
398
|
+
designResult = await this.designer.design({ description }, matches, globalFailureRates);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
if (err instanceof ValidationError && err.attemptMetadata) {
|
|
401
|
+
for (const meta of err.attemptMetadata) {
|
|
402
|
+
await this.telemetry?.emit("generation_attempt", {
|
|
403
|
+
description,
|
|
404
|
+
attempt: meta.attempt,
|
|
405
|
+
temperature: meta.temperature,
|
|
406
|
+
durationMs: meta.durationMs,
|
|
407
|
+
tokensInput: meta.tokensInput,
|
|
408
|
+
tokensOutput: meta.tokensOutput,
|
|
409
|
+
validationPassed: meta.validationPassed,
|
|
410
|
+
issueCount: meta.issues.length,
|
|
411
|
+
issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
|
|
412
|
+
workflowType
|
|
413
|
+
}, runId);
|
|
414
|
+
}
|
|
415
|
+
await this.telemetry?.emit("build_complete", {
|
|
416
|
+
description,
|
|
417
|
+
success: false,
|
|
418
|
+
totalAttempts: err.attemptMetadata.length,
|
|
419
|
+
totalDurationMs: Date.now() - buildStart,
|
|
420
|
+
totalTokensInput: err.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0),
|
|
421
|
+
totalTokensOutput: err.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0),
|
|
422
|
+
workflowName: null,
|
|
423
|
+
workflowId: null,
|
|
424
|
+
dryRun: false,
|
|
425
|
+
credentialsNeeded: 0,
|
|
426
|
+
warnedRules: err.warnedRules ?? [],
|
|
427
|
+
workflowType
|
|
428
|
+
}, runId);
|
|
429
|
+
this.updatePatterns();
|
|
430
|
+
}
|
|
431
|
+
throw err;
|
|
432
|
+
}
|
|
433
|
+
await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
|
|
482
434
|
const provider = this.requireProvider();
|
|
483
435
|
const deployed = await provider.update(id, designResult.workflow);
|
|
484
436
|
this.saveToLibrary(designResult.workflow, description, designResult, matches);
|
|
@@ -495,8 +447,11 @@ var Kairos = class {
|
|
|
495
447
|
workflowName: deployed.name,
|
|
496
448
|
workflowId: deployed.workflowId,
|
|
497
449
|
dryRun: false,
|
|
498
|
-
credentialsNeeded: designResult.credentialsNeeded.length
|
|
499
|
-
|
|
450
|
+
credentialsNeeded: designResult.credentialsNeeded.length,
|
|
451
|
+
warnedRules: designResult.warnedRules,
|
|
452
|
+
workflowType
|
|
453
|
+
}, runId);
|
|
454
|
+
this.updatePatterns();
|
|
500
455
|
return {
|
|
501
456
|
workflowId: deployed.workflowId,
|
|
502
457
|
name: deployed.name,
|
|
@@ -511,7 +466,14 @@ var Kairos = class {
|
|
|
511
466
|
await this.saveQueue.catch(() => {
|
|
512
467
|
});
|
|
513
468
|
}
|
|
514
|
-
|
|
469
|
+
updatePatterns() {
|
|
470
|
+
if (!this.patternAnalyzer) return;
|
|
471
|
+
this.saveQueue = this.saveQueue.then(() => this.patternAnalyzer.analyzeAndSave()).then(() => null).catch((err) => {
|
|
472
|
+
this.logger.warn("Pattern analysis failed (non-fatal)", { err: String(err) });
|
|
473
|
+
return null;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
async emitAttemptTelemetry(description, designResult, workflowType, runId) {
|
|
515
477
|
for (const meta of designResult.attemptMetadata) {
|
|
516
478
|
await this.telemetry?.emit("generation_attempt", {
|
|
517
479
|
description,
|
|
@@ -522,8 +484,9 @@ var Kairos = class {
|
|
|
522
484
|
tokensOutput: meta.tokensOutput,
|
|
523
485
|
validationPassed: meta.validationPassed,
|
|
524
486
|
issueCount: meta.issues.length,
|
|
525
|
-
issues: meta.issues.map((i) => ({ rule: i.rule, message: i.message }))
|
|
526
|
-
|
|
487
|
+
issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
|
|
488
|
+
workflowType
|
|
489
|
+
}, runId);
|
|
527
490
|
}
|
|
528
491
|
}
|
|
529
492
|
recordDeploy() {
|
|
@@ -620,190 +583,7 @@ var Kairos = class {
|
|
|
620
583
|
}
|
|
621
584
|
};
|
|
622
585
|
|
|
623
|
-
// src/templates/safety.ts
|
|
624
|
-
var BLOCKED_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
625
|
-
"n8n-nodes-base.code",
|
|
626
|
-
"n8n-nodes-base.executeCommand",
|
|
627
|
-
"n8n-nodes-base.ssh"
|
|
628
|
-
]);
|
|
629
|
-
var REVIEW_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
630
|
-
"n8n-nodes-base.httpRequest"
|
|
631
|
-
]);
|
|
632
|
-
var SECRET_PATTERNS = [
|
|
633
|
-
/sk-[a-zA-Z0-9]{20,}/,
|
|
634
|
-
/ghp_[a-zA-Z0-9]{36}/,
|
|
635
|
-
/xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+/,
|
|
636
|
-
/AIza[a-zA-Z0-9_-]{35}/,
|
|
637
|
-
/AKIA[A-Z0-9]{16}/
|
|
638
|
-
];
|
|
639
|
-
function assessTemplateSafety(workflow) {
|
|
640
|
-
const reasons = [];
|
|
641
|
-
let worst = "safe";
|
|
642
|
-
const escalate = (level, reason) => {
|
|
643
|
-
reasons.push(reason);
|
|
644
|
-
if (level === "blocked") worst = "blocked";
|
|
645
|
-
else if (level === "review" && worst === "safe") worst = "review";
|
|
646
|
-
};
|
|
647
|
-
for (const node of workflow.nodes) {
|
|
648
|
-
if (BLOCKED_NODE_TYPES.has(node.type)) {
|
|
649
|
-
escalate("blocked", `Contains ${node.type} node "${node.name}"`);
|
|
650
|
-
}
|
|
651
|
-
if (REVIEW_NODE_TYPES.has(node.type)) {
|
|
652
|
-
escalate("review", `Contains ${node.type} node "${node.name}"`);
|
|
653
|
-
}
|
|
654
|
-
const paramStr = JSON.stringify(node.parameters);
|
|
655
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
656
|
-
if (pattern.test(paramStr)) {
|
|
657
|
-
escalate("blocked", `Node "${node.name}" parameters contain a hardcoded secret`);
|
|
658
|
-
break;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return { trustLevel: worst, reasons };
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// src/templates/syncer.ts
|
|
666
|
-
var N8N_TEMPLATE_API = "https://api.n8n.io/api/templates";
|
|
667
|
-
var PAGE_SIZE = 50;
|
|
668
|
-
var DELAY_BETWEEN_FETCHES_MS = 200;
|
|
669
|
-
var DEFAULT_SETTINGS = {
|
|
670
|
-
executionOrder: "v1",
|
|
671
|
-
saveManualExecutions: true,
|
|
672
|
-
timezone: "UTC"
|
|
673
|
-
};
|
|
674
|
-
var TemplateSyncer = class {
|
|
675
|
-
constructor(library, logger) {
|
|
676
|
-
this.library = library;
|
|
677
|
-
this.validator = new N8nValidator();
|
|
678
|
-
this.logger = logger;
|
|
679
|
-
}
|
|
680
|
-
library;
|
|
681
|
-
validator;
|
|
682
|
-
logger;
|
|
683
|
-
async sync(options) {
|
|
684
|
-
const maxTemplates = options?.maxTemplates ?? 500;
|
|
685
|
-
await this.library.initialize();
|
|
686
|
-
const existing = await this.library.list();
|
|
687
|
-
const existingSourceIds = new Set(
|
|
688
|
-
existing.filter((w) => w.sourceKind === "n8n-template" && w.sourceId).map((w) => w.sourceId)
|
|
689
|
-
);
|
|
690
|
-
const progress = {
|
|
691
|
-
total: 0,
|
|
692
|
-
processed: 0,
|
|
693
|
-
saved: 0,
|
|
694
|
-
skippedPaid: 0,
|
|
695
|
-
skippedDuplicate: 0,
|
|
696
|
-
blocked: 0,
|
|
697
|
-
reviewed: 0
|
|
698
|
-
};
|
|
699
|
-
const templateIds = await this.fetchTemplateIds(maxTemplates, progress);
|
|
700
|
-
for (const id of templateIds) {
|
|
701
|
-
if (existingSourceIds.has(String(id))) {
|
|
702
|
-
progress.skippedDuplicate++;
|
|
703
|
-
progress.processed++;
|
|
704
|
-
options?.onProgress?.(progress);
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
|
-
try {
|
|
708
|
-
await this.processTemplate(id, progress);
|
|
709
|
-
} catch (err) {
|
|
710
|
-
this.logger.warn(`Failed to process template ${id}`, { err: String(err) });
|
|
711
|
-
}
|
|
712
|
-
progress.processed++;
|
|
713
|
-
options?.onProgress?.(progress);
|
|
714
|
-
await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
|
|
715
|
-
}
|
|
716
|
-
return progress;
|
|
717
|
-
}
|
|
718
|
-
async fetchTemplateIds(max, progress) {
|
|
719
|
-
const ids = [];
|
|
720
|
-
let page = 1;
|
|
721
|
-
while (ids.length < max) {
|
|
722
|
-
const url = `${N8N_TEMPLATE_API}/search?page=${page}&rows=${PAGE_SIZE}`;
|
|
723
|
-
const response = await fetch(url);
|
|
724
|
-
if (!response.ok) break;
|
|
725
|
-
const data = await response.json();
|
|
726
|
-
progress.total = Math.min(data.totalWorkflows, max);
|
|
727
|
-
for (const template of data.workflows) {
|
|
728
|
-
if (ids.length >= max) break;
|
|
729
|
-
if (template.price && template.price > 0) {
|
|
730
|
-
progress.skippedPaid++;
|
|
731
|
-
continue;
|
|
732
|
-
}
|
|
733
|
-
ids.push(template.id);
|
|
734
|
-
}
|
|
735
|
-
if (data.workflows.length < PAGE_SIZE) break;
|
|
736
|
-
page++;
|
|
737
|
-
await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
|
|
738
|
-
}
|
|
739
|
-
return ids;
|
|
740
|
-
}
|
|
741
|
-
async processTemplate(id, progress) {
|
|
742
|
-
const url = `${N8N_TEMPLATE_API}/workflows/${id}`;
|
|
743
|
-
const response = await fetch(url);
|
|
744
|
-
if (!response.ok) return;
|
|
745
|
-
const data = await response.json();
|
|
746
|
-
const templateMeta = data.workflow;
|
|
747
|
-
const rawWorkflow = templateMeta.workflow;
|
|
748
|
-
if (!rawWorkflow?.nodes?.length) return;
|
|
749
|
-
const workflow = {
|
|
750
|
-
name: templateMeta.name,
|
|
751
|
-
nodes: rawWorkflow.nodes.filter((n) => n.type && n.name),
|
|
752
|
-
connections: rawWorkflow.connections,
|
|
753
|
-
settings: rawWorkflow.settings ? { executionOrder: "v1", ...rawWorkflow.settings } : { ...DEFAULT_SETTINGS }
|
|
754
|
-
};
|
|
755
|
-
const validation = this.validator.validate(workflow);
|
|
756
|
-
const validationErrors = validation.issues.filter((i) => i.severity === "error");
|
|
757
|
-
if (validationErrors.length > 0) {
|
|
758
|
-
progress.blocked++;
|
|
759
|
-
this.logger.debug(`Template ${id} blocked: ${validationErrors.length} validation errors`);
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
const safety = assessTemplateSafety(workflow);
|
|
763
|
-
if (safety.trustLevel === "blocked") {
|
|
764
|
-
progress.blocked++;
|
|
765
|
-
this.logger.debug(`Template ${id} blocked: ${safety.reasons.join(", ")}`);
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
if (safety.trustLevel === "review") {
|
|
769
|
-
progress.reviewed++;
|
|
770
|
-
}
|
|
771
|
-
const description = this.cleanDescription(templateMeta.description);
|
|
772
|
-
const autoTags = Array.from(new Set(
|
|
773
|
-
workflow.nodes.flatMap((n) => {
|
|
774
|
-
const bare = n.type.split(".").pop() ?? "";
|
|
775
|
-
const tags = [bare];
|
|
776
|
-
if (n.type.includes("Trigger") || n.type.includes("trigger")) tags.push(`trigger:${bare}`);
|
|
777
|
-
if (n.type.includes("langchain")) tags.push("ai");
|
|
778
|
-
return tags;
|
|
779
|
-
})
|
|
780
|
-
));
|
|
781
|
-
const metadata = {
|
|
782
|
-
description,
|
|
783
|
-
tags: autoTags,
|
|
784
|
-
sourceKind: "n8n-template",
|
|
785
|
-
sourceId: String(id),
|
|
786
|
-
sourceUrl: `https://n8n.io/workflows/${id}`,
|
|
787
|
-
trustLevel: safety.trustLevel
|
|
788
|
-
};
|
|
789
|
-
await this.library.save(workflow, metadata);
|
|
790
|
-
progress.saved++;
|
|
791
|
-
this.logger.debug(`Template ${id} saved: "${templateMeta.name}" (${safety.trustLevel})`);
|
|
792
|
-
}
|
|
793
|
-
cleanDescription(raw) {
|
|
794
|
-
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);
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
|
|
798
586
|
export {
|
|
799
|
-
|
|
800
|
-
GuardError,
|
|
801
|
-
N8nProvider,
|
|
802
|
-
GenerationError,
|
|
803
|
-
ResponseParseError,
|
|
804
|
-
ValidationError,
|
|
805
|
-
TelemetryCollector,
|
|
806
|
-
Kairos,
|
|
807
|
-
TemplateSyncer
|
|
587
|
+
Kairos
|
|
808
588
|
};
|
|
809
|
-
//# sourceMappingURL=chunk-
|
|
589
|
+
//# sourceMappingURL=chunk-4TS6GW6O.js.map
|