@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.
- package/README.md +131 -16
- package/dist/{chunk-KIFT5LA7.js → chunk-2ZHNO37N.js} +49 -5
- package/dist/chunk-2ZHNO37N.js.map +1 -0
- package/dist/chunk-GG4B4TYG.js +153 -0
- package/dist/chunk-GG4B4TYG.js.map +1 -0
- package/dist/{chunk-5GAY7CSJ.js → chunk-PCNW5ZUD.js} +2 -2
- package/dist/chunk-SC6CLQZB.js +144 -0
- package/dist/chunk-SC6CLQZB.js.map +1 -0
- package/dist/chunk-SQS4QHDH.js +44 -0
- package/dist/chunk-SQS4QHDH.js.map +1 -0
- package/dist/{chunk-EVOAYH2K.js → chunk-STG7Z2SS.js} +2 -2
- package/dist/{chunk-HBGZTUUZ.js → chunk-YOQTEVDB.js} +5 -7
- package/dist/chunk-YOQTEVDB.js.map +1 -0
- package/dist/cli.cjs +702 -40
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +262 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +417 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -4
- package/dist/index.d.ts +86 -4
- package/dist/index.js +19 -5
- package/dist/mcp-server.cjs +139 -24
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/mcp-server.js +90 -19
- package/dist/mcp-server.js.map +1 -1
- package/dist/pack-builder-RTQWXGIS.js +9 -0
- package/dist/pack-builder-RTQWXGIS.js.map +1 -0
- package/dist/pack-exporter-KFNLSP5V.js +7 -0
- package/dist/pack-exporter-KFNLSP5V.js.map +1 -0
- package/dist/pack-validator-HZPB2XJ3.js +7 -0
- package/dist/pack-validator-HZPB2XJ3.js.map +1 -0
- package/dist/{reader-B5mV20H6.d.ts → reader-CfWGpL4V.d.cts} +2 -1
- package/dist/{reader-B5mV20H6.d.cts → reader-CfWGpL4V.d.ts} +2 -1
- package/dist/standalone.cjs +44 -4
- package/dist/standalone.cjs.map +1 -1
- package/dist/standalone.d.cts +1 -1
- package/dist/standalone.d.ts +1 -1
- package/dist/standalone.js +2 -2
- package/package.json +12 -5
- package/dist/chunk-HBGZTUUZ.js.map +0 -1
- package/dist/chunk-KIFT5LA7.js.map +0 -1
- /package/dist/{chunk-5GAY7CSJ.js.map → chunk-PCNW5ZUD.js.map} +0 -0
- /package/dist/{chunk-EVOAYH2K.js.map → chunk-STG7Z2SS.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { C as ClientOptions, B as BuildOptions,
|
|
2
|
-
export { A as ApiError,
|
|
1
|
+
import { C as ClientOptions, B as BuildOptions, b as BuildResult, r as N8nWorkflow, Z as WorkflowListItem, d as DeleteOptions, f as ExecutionFilter, g as ExecutionSummary, E as ExecutionDetail, T as Tag, c as CredentialRequirement } from './reader-CfWGpL4V.cjs';
|
|
2
|
+
export { A as ApiError, a as AttemptMetadata, D as DEFAULT_REGISTRY, e as DeployResult, F as FailurePattern, h as FileLibrary, G as GenerationError, i as GuardError, I as ILogger, j as IProvider, k as IWorkflowLibrary, K as KairosError, N as N8nApiClient, l as N8nConnections, m as N8nFieldStripper, n as N8nNode, o as N8nProvider, p as N8nSettings, q as N8nValidator, s as NodeRegistry, t as NullLibrary, O as OutcomeData, u as OutcomeStats, P as ProviderError, R as ResponseParseError, v as RuleFailureRate, S as ScoredEntry, w as SmokeTestResult, y as SourceKind, z as StoredWorkflow, H as SyncProgress, J as TelemetryCollector, L as TelemetryEvent, M as TelemetryReader, Q as TemplateSyncer, U as TrustLevel, V as ValidationError, W as ValidationIssue, X as ValidationResult, Y as WorkflowCluster, _ as WorkflowMatch, $ as WorkflowMetadataInput, a0 as buildSearchCorpus, a1 as clusterWorkflows, a2 as hybridScore, a3 as nullLogger, a4 as rerank, a5 as tokenize } from './reader-CfWGpL4V.cjs';
|
|
3
3
|
|
|
4
4
|
declare class Kairos {
|
|
5
5
|
private readonly provider;
|
|
6
6
|
private readonly designer;
|
|
7
|
-
private readonly validator;
|
|
8
7
|
private readonly library;
|
|
9
8
|
private readonly logger;
|
|
10
9
|
private readonly telemetry;
|
|
@@ -35,4 +34,87 @@ declare class Kairos {
|
|
|
35
34
|
untag(workflowId: string, tagIds: string[]): Promise<void>;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
type AssumptionType = 'safe' | 'needs_confirmation' | 'blocking';
|
|
38
|
+
type PackStatus = 'draft' | 'blocked' | 'ready_for_test' | 'ready_for_activation' | 'active' | 'needs_attention';
|
|
39
|
+
interface TypedAssumption {
|
|
40
|
+
type: AssumptionType;
|
|
41
|
+
text: string;
|
|
42
|
+
}
|
|
43
|
+
interface WorkflowPlan {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
purpose: string;
|
|
47
|
+
}
|
|
48
|
+
interface PackPlan {
|
|
49
|
+
businessContext: string;
|
|
50
|
+
workflows: WorkflowPlan[];
|
|
51
|
+
assumptions: TypedAssumption[];
|
|
52
|
+
sheetsColumns: Array<{
|
|
53
|
+
sheet: string;
|
|
54
|
+
columns: string[];
|
|
55
|
+
}>;
|
|
56
|
+
testChecklist: Array<{
|
|
57
|
+
workflow: string;
|
|
58
|
+
steps: string[];
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
interface PackWorkflowResult {
|
|
62
|
+
name: string;
|
|
63
|
+
purpose: string;
|
|
64
|
+
workflowId: string | null;
|
|
65
|
+
deployed: boolean;
|
|
66
|
+
generationAttempts: number;
|
|
67
|
+
credentialsNeeded: CredentialRequirement[];
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
70
|
+
interface WorkflowPackResult {
|
|
71
|
+
businessContext: string;
|
|
72
|
+
packName: string;
|
|
73
|
+
status: PackStatus;
|
|
74
|
+
workflows: PackWorkflowResult[];
|
|
75
|
+
allCredentials: Array<{
|
|
76
|
+
service: string;
|
|
77
|
+
credentialType: string;
|
|
78
|
+
}>;
|
|
79
|
+
sheetsColumns: Array<{
|
|
80
|
+
sheet: string;
|
|
81
|
+
columns: string[];
|
|
82
|
+
}>;
|
|
83
|
+
assumptions: TypedAssumption[];
|
|
84
|
+
testChecklist: Array<{
|
|
85
|
+
workflow: string;
|
|
86
|
+
steps: string[];
|
|
87
|
+
}>;
|
|
88
|
+
builtAt: string;
|
|
89
|
+
}
|
|
90
|
+
declare function derivePackStatus(pack: Pick<WorkflowPackResult, 'assumptions' | 'workflows'> & {
|
|
91
|
+
status?: PackStatus;
|
|
92
|
+
}): PackStatus;
|
|
93
|
+
declare class PackBuilder {
|
|
94
|
+
private client;
|
|
95
|
+
private kairos;
|
|
96
|
+
private model;
|
|
97
|
+
constructor(options: {
|
|
98
|
+
anthropicApiKey: string;
|
|
99
|
+
kairos: Kairos;
|
|
100
|
+
model?: string;
|
|
101
|
+
});
|
|
102
|
+
plan(businessContext: string): Promise<PackPlan>;
|
|
103
|
+
build(plan: PackPlan, options?: {
|
|
104
|
+
dryRun?: boolean;
|
|
105
|
+
activate?: boolean;
|
|
106
|
+
onProgress?: (workflow: WorkflowPlan, index: number, total: number) => void;
|
|
107
|
+
}): Promise<WorkflowPackResult>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare function generateHandoff(pack: WorkflowPackResult): string;
|
|
111
|
+
|
|
112
|
+
interface PackValidationIssue {
|
|
113
|
+
type: 'duplicate_name' | 'blocking_assumption' | 'unsafe_activation' | 'schedule_conflict';
|
|
114
|
+
severity: 'error' | 'warning';
|
|
115
|
+
message: string;
|
|
116
|
+
workflows?: string[];
|
|
117
|
+
}
|
|
118
|
+
declare function validatePack(pack: WorkflowPackResult): PackValidationIssue[];
|
|
119
|
+
|
|
120
|
+
export { type AssumptionType, BuildOptions, BuildResult, ClientOptions, CredentialRequirement, DeleteOptions, ExecutionDetail, ExecutionFilter, ExecutionSummary, Kairos, N8nWorkflow, PackBuilder, type PackPlan, type PackStatus, type PackValidationIssue, type PackWorkflowResult, Tag, type TypedAssumption, WorkflowListItem, type WorkflowPackResult, type WorkflowPlan, derivePackStatus, generateHandoff, validatePack };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { C as ClientOptions, B as BuildOptions,
|
|
2
|
-
export { A as ApiError,
|
|
1
|
+
import { C as ClientOptions, B as BuildOptions, b as BuildResult, r as N8nWorkflow, Z as WorkflowListItem, d as DeleteOptions, f as ExecutionFilter, g as ExecutionSummary, E as ExecutionDetail, T as Tag, c as CredentialRequirement } from './reader-CfWGpL4V.js';
|
|
2
|
+
export { A as ApiError, a as AttemptMetadata, D as DEFAULT_REGISTRY, e as DeployResult, F as FailurePattern, h as FileLibrary, G as GenerationError, i as GuardError, I as ILogger, j as IProvider, k as IWorkflowLibrary, K as KairosError, N as N8nApiClient, l as N8nConnections, m as N8nFieldStripper, n as N8nNode, o as N8nProvider, p as N8nSettings, q as N8nValidator, s as NodeRegistry, t as NullLibrary, O as OutcomeData, u as OutcomeStats, P as ProviderError, R as ResponseParseError, v as RuleFailureRate, S as ScoredEntry, w as SmokeTestResult, y as SourceKind, z as StoredWorkflow, H as SyncProgress, J as TelemetryCollector, L as TelemetryEvent, M as TelemetryReader, Q as TemplateSyncer, U as TrustLevel, V as ValidationError, W as ValidationIssue, X as ValidationResult, Y as WorkflowCluster, _ as WorkflowMatch, $ as WorkflowMetadataInput, a0 as buildSearchCorpus, a1 as clusterWorkflows, a2 as hybridScore, a3 as nullLogger, a4 as rerank, a5 as tokenize } from './reader-CfWGpL4V.js';
|
|
3
3
|
|
|
4
4
|
declare class Kairos {
|
|
5
5
|
private readonly provider;
|
|
6
6
|
private readonly designer;
|
|
7
|
-
private readonly validator;
|
|
8
7
|
private readonly library;
|
|
9
8
|
private readonly logger;
|
|
10
9
|
private readonly telemetry;
|
|
@@ -35,4 +34,87 @@ declare class Kairos {
|
|
|
35
34
|
untag(workflowId: string, tagIds: string[]): Promise<void>;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
type AssumptionType = 'safe' | 'needs_confirmation' | 'blocking';
|
|
38
|
+
type PackStatus = 'draft' | 'blocked' | 'ready_for_test' | 'ready_for_activation' | 'active' | 'needs_attention';
|
|
39
|
+
interface TypedAssumption {
|
|
40
|
+
type: AssumptionType;
|
|
41
|
+
text: string;
|
|
42
|
+
}
|
|
43
|
+
interface WorkflowPlan {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
purpose: string;
|
|
47
|
+
}
|
|
48
|
+
interface PackPlan {
|
|
49
|
+
businessContext: string;
|
|
50
|
+
workflows: WorkflowPlan[];
|
|
51
|
+
assumptions: TypedAssumption[];
|
|
52
|
+
sheetsColumns: Array<{
|
|
53
|
+
sheet: string;
|
|
54
|
+
columns: string[];
|
|
55
|
+
}>;
|
|
56
|
+
testChecklist: Array<{
|
|
57
|
+
workflow: string;
|
|
58
|
+
steps: string[];
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
interface PackWorkflowResult {
|
|
62
|
+
name: string;
|
|
63
|
+
purpose: string;
|
|
64
|
+
workflowId: string | null;
|
|
65
|
+
deployed: boolean;
|
|
66
|
+
generationAttempts: number;
|
|
67
|
+
credentialsNeeded: CredentialRequirement[];
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
70
|
+
interface WorkflowPackResult {
|
|
71
|
+
businessContext: string;
|
|
72
|
+
packName: string;
|
|
73
|
+
status: PackStatus;
|
|
74
|
+
workflows: PackWorkflowResult[];
|
|
75
|
+
allCredentials: Array<{
|
|
76
|
+
service: string;
|
|
77
|
+
credentialType: string;
|
|
78
|
+
}>;
|
|
79
|
+
sheetsColumns: Array<{
|
|
80
|
+
sheet: string;
|
|
81
|
+
columns: string[];
|
|
82
|
+
}>;
|
|
83
|
+
assumptions: TypedAssumption[];
|
|
84
|
+
testChecklist: Array<{
|
|
85
|
+
workflow: string;
|
|
86
|
+
steps: string[];
|
|
87
|
+
}>;
|
|
88
|
+
builtAt: string;
|
|
89
|
+
}
|
|
90
|
+
declare function derivePackStatus(pack: Pick<WorkflowPackResult, 'assumptions' | 'workflows'> & {
|
|
91
|
+
status?: PackStatus;
|
|
92
|
+
}): PackStatus;
|
|
93
|
+
declare class PackBuilder {
|
|
94
|
+
private client;
|
|
95
|
+
private kairos;
|
|
96
|
+
private model;
|
|
97
|
+
constructor(options: {
|
|
98
|
+
anthropicApiKey: string;
|
|
99
|
+
kairos: Kairos;
|
|
100
|
+
model?: string;
|
|
101
|
+
});
|
|
102
|
+
plan(businessContext: string): Promise<PackPlan>;
|
|
103
|
+
build(plan: PackPlan, options?: {
|
|
104
|
+
dryRun?: boolean;
|
|
105
|
+
activate?: boolean;
|
|
106
|
+
onProgress?: (workflow: WorkflowPlan, index: number, total: number) => void;
|
|
107
|
+
}): Promise<WorkflowPackResult>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare function generateHandoff(pack: WorkflowPackResult): string;
|
|
111
|
+
|
|
112
|
+
interface PackValidationIssue {
|
|
113
|
+
type: 'duplicate_name' | 'blocking_assumption' | 'unsafe_activation' | 'schedule_conflict';
|
|
114
|
+
severity: 'error' | 'warning';
|
|
115
|
+
message: string;
|
|
116
|
+
workflows?: string[];
|
|
117
|
+
}
|
|
118
|
+
declare function validatePack(pack: WorkflowPackResult): PackValidationIssue[];
|
|
119
|
+
|
|
120
|
+
export { type AssumptionType, BuildOptions, BuildResult, ClientOptions, CredentialRequirement, DeleteOptions, ExecutionDetail, ExecutionFilter, ExecutionSummary, Kairos, N8nWorkflow, PackBuilder, type PackPlan, type PackStatus, type PackValidationIssue, type PackWorkflowResult, Tag, type TypedAssumption, WorkflowListItem, type WorkflowPackResult, type WorkflowPlan, derivePackStatus, generateHandoff, validatePack };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Kairos
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-YOQTEVDB.js";
|
|
4
|
+
import "./chunk-STG7Z2SS.js";
|
|
5
5
|
import "./chunk-6FOFWVMG.js";
|
|
6
6
|
import {
|
|
7
7
|
GenerationError,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
ResponseParseError,
|
|
11
11
|
TemplateSyncer,
|
|
12
12
|
ValidationError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PCNW5ZUD.js";
|
|
14
14
|
import {
|
|
15
15
|
ApiError,
|
|
16
16
|
DEFAULT_REGISTRY,
|
|
@@ -30,7 +30,17 @@ import {
|
|
|
30
30
|
nullLogger,
|
|
31
31
|
rerank,
|
|
32
32
|
tokenize
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-2ZHNO37N.js";
|
|
34
|
+
import {
|
|
35
|
+
PackBuilder,
|
|
36
|
+
derivePackStatus
|
|
37
|
+
} from "./chunk-GG4B4TYG.js";
|
|
38
|
+
import {
|
|
39
|
+
generateHandoff
|
|
40
|
+
} from "./chunk-SC6CLQZB.js";
|
|
41
|
+
import {
|
|
42
|
+
validatePack
|
|
43
|
+
} from "./chunk-SQS4QHDH.js";
|
|
34
44
|
export {
|
|
35
45
|
ApiError,
|
|
36
46
|
DEFAULT_REGISTRY,
|
|
@@ -45,6 +55,7 @@ export {
|
|
|
45
55
|
N8nValidator,
|
|
46
56
|
NodeRegistry,
|
|
47
57
|
NullLibrary,
|
|
58
|
+
PackBuilder,
|
|
48
59
|
ProviderError,
|
|
49
60
|
ResponseParseError,
|
|
50
61
|
TelemetryCollector,
|
|
@@ -53,9 +64,12 @@ export {
|
|
|
53
64
|
ValidationError,
|
|
54
65
|
buildSearchCorpus,
|
|
55
66
|
clusterWorkflows,
|
|
67
|
+
derivePackStatus,
|
|
68
|
+
generateHandoff,
|
|
56
69
|
hybridScore,
|
|
57
70
|
nullLogger,
|
|
58
71
|
rerank,
|
|
59
|
-
tokenize
|
|
72
|
+
tokenize,
|
|
73
|
+
validatePack
|
|
60
74
|
};
|
|
61
75
|
//# sourceMappingURL=index.js.map
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// src/mcp-server.ts
|
|
5
5
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
6
6
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
7
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
8
|
+
var import_node_http = require("http");
|
|
7
9
|
var import_zod = require("zod");
|
|
8
10
|
|
|
9
11
|
// src/library/file-library.ts
|
|
@@ -899,6 +901,7 @@ var N8nValidator = class {
|
|
|
899
901
|
this.checkRule32(workflow, issues);
|
|
900
902
|
this.checkRule33(workflow, issues);
|
|
901
903
|
this.checkRule34(workflow, issues);
|
|
904
|
+
this.checkRule35(workflow, issues);
|
|
902
905
|
if (Array.isArray(workflow.nodes)) {
|
|
903
906
|
const nodeById = new Map(workflow.nodes.map((n) => [n.id, n.type]));
|
|
904
907
|
for (const issue of issues) {
|
|
@@ -1471,6 +1474,43 @@ var N8nValidator = class {
|
|
|
1471
1474
|
}
|
|
1472
1475
|
}
|
|
1473
1476
|
}
|
|
1477
|
+
// Rule 35 (WARN): email-sending node with no duplicate-prevention signal
|
|
1478
|
+
checkRule35(w, issues) {
|
|
1479
|
+
if (!Array.isArray(w.nodes)) return;
|
|
1480
|
+
const sendNodes = w.nodes.filter((node) => {
|
|
1481
|
+
if (node.type === "n8n-nodes-base.gmail") {
|
|
1482
|
+
const op = node.parameters?.["operation"];
|
|
1483
|
+
return !op || op === "send" || op === "sendEmail" || op === "reply";
|
|
1484
|
+
}
|
|
1485
|
+
return node.type === "n8n-nodes-base.emailSend" || node.type === "n8n-nodes-base.sendEmail";
|
|
1486
|
+
});
|
|
1487
|
+
if (sendNodes.length === 0) return;
|
|
1488
|
+
const workflowText = JSON.stringify(w).toLowerCase();
|
|
1489
|
+
const IDEMPOTENCY_SIGNALS = [
|
|
1490
|
+
"sent_at",
|
|
1491
|
+
"last_sent",
|
|
1492
|
+
"last_reminder",
|
|
1493
|
+
"processed_at",
|
|
1494
|
+
"already_sent",
|
|
1495
|
+
"email_sent",
|
|
1496
|
+
"notified_at",
|
|
1497
|
+
"reminder_sent",
|
|
1498
|
+
"contacted_at",
|
|
1499
|
+
"dedupe",
|
|
1500
|
+
"idempotent"
|
|
1501
|
+
];
|
|
1502
|
+
const hasIdempotencySignal = IDEMPOTENCY_SIGNALS.some((s) => workflowText.includes(s));
|
|
1503
|
+
if (!hasIdempotencySignal) {
|
|
1504
|
+
for (const node of sendNodes) {
|
|
1505
|
+
this.warn(
|
|
1506
|
+
issues,
|
|
1507
|
+
35,
|
|
1508
|
+
`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`,
|
|
1509
|
+
node.id
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1474
1514
|
// Rule 34 (WARN): webhook path contains spaces, starts with slash, or looks like a full URL
|
|
1475
1515
|
checkRule34(w, issues) {
|
|
1476
1516
|
if (!Array.isArray(w.nodes)) return;
|
|
@@ -2018,7 +2058,7 @@ Respond ONLY with a generate_workflow tool call. No prose. No markdown outside t
|
|
|
2018
2058
|
If the request is impossible or unclear, set the error field instead of generating a workflow.`;
|
|
2019
2059
|
|
|
2020
2060
|
// src/validation/rule-metadata.ts
|
|
2021
|
-
var VALIDATOR_RULE_IDS = Array.from({ length:
|
|
2061
|
+
var VALIDATOR_RULE_IDS = Array.from({ length: 35 }, (_, i) => i + 1);
|
|
2022
2062
|
var RULE_PIPELINE_STAGES = {
|
|
2023
2063
|
1: "node_generation",
|
|
2024
2064
|
2: "node_generation",
|
|
@@ -2053,7 +2093,8 @@ var RULE_PIPELINE_STAGES = {
|
|
|
2053
2093
|
31: "node_generation",
|
|
2054
2094
|
32: "node_generation",
|
|
2055
2095
|
33: "node_generation",
|
|
2056
|
-
34: "node_generation"
|
|
2096
|
+
34: "node_generation",
|
|
2097
|
+
35: "node_generation"
|
|
2057
2098
|
};
|
|
2058
2099
|
var RULE_EXAMPLES = {
|
|
2059
2100
|
17: {
|
|
@@ -2103,6 +2144,10 @@ var RULE_EXAMPLES = {
|
|
|
2103
2144
|
34: {
|
|
2104
2145
|
bad: '"path": "/my webhook"',
|
|
2105
2146
|
good: '"path": "my-webhook"'
|
|
2147
|
+
},
|
|
2148
|
+
35: {
|
|
2149
|
+
bad: '"type": "n8n-nodes-base.gmail", "parameters": { "operation": "send" } // no sent_at tracking',
|
|
2150
|
+
good: 'Add a Set node after send that writes "sent_at": "={{ $now }}" back to the sheet, or an IF node that checks sent_at before sending'
|
|
2106
2151
|
}
|
|
2107
2152
|
};
|
|
2108
2153
|
var RULE_MITIGATIONS = {
|
|
@@ -2139,7 +2184,8 @@ var RULE_MITIGATIONS = {
|
|
|
2139
2184
|
31: "Add at least one condition to the if node \u2014 conditions.conditions array must be non-empty",
|
|
2140
2185
|
32: "Add field assignments to the set node \u2014 assignments.assignments array must be non-empty for typeVersion 3.x",
|
|
2141
2186
|
33: "Add at least one schedule rule to scheduleTrigger \u2014 rule.interval array must have at least one entry",
|
|
2142
|
-
34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")'
|
|
2187
|
+
34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")',
|
|
2188
|
+
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"
|
|
2143
2189
|
};
|
|
2144
2190
|
|
|
2145
2191
|
// src/generation/prompt-builder.ts
|
|
@@ -2594,7 +2640,7 @@ var PatternAnalyzer = class _PatternAnalyzer {
|
|
|
2594
2640
|
this._cachedEvents = events;
|
|
2595
2641
|
const starts = events.filter((e) => e.eventType === "build_start");
|
|
2596
2642
|
const attempts = events.filter((e) => e.eventType === "generation_attempt");
|
|
2597
|
-
const
|
|
2643
|
+
const _passed = attempts.filter(
|
|
2598
2644
|
(a) => a.data.validationPassed === true
|
|
2599
2645
|
);
|
|
2600
2646
|
const failed = attempts.filter(
|
|
@@ -3177,12 +3223,37 @@ function inferWorkflowType(description) {
|
|
|
3177
3223
|
|
|
3178
3224
|
// src/mcp-server.ts
|
|
3179
3225
|
var import_node_fs3 = require("fs");
|
|
3180
|
-
var
|
|
3226
|
+
var import_node_path8 = require("path");
|
|
3181
3227
|
var import_node_os6 = require("os");
|
|
3182
3228
|
var import_node_url = require("url");
|
|
3229
|
+
|
|
3230
|
+
// src/utils/node-catalog-cache.ts
|
|
3231
|
+
var import_promises5 = require("fs/promises");
|
|
3232
|
+
var import_node_path7 = require("path");
|
|
3233
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3234
|
+
async function readCatalogCache(cachePath) {
|
|
3235
|
+
try {
|
|
3236
|
+
const raw = await (0, import_promises5.readFile)(cachePath, "utf-8");
|
|
3237
|
+
const cached = JSON.parse(raw);
|
|
3238
|
+
if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null;
|
|
3239
|
+
return cached.syncResult;
|
|
3240
|
+
} catch {
|
|
3241
|
+
return null;
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
async function writeCatalogCache(cachePath, syncResult) {
|
|
3245
|
+
try {
|
|
3246
|
+
await (0, import_promises5.mkdir)((0, import_node_path7.dirname)(cachePath), { recursive: true });
|
|
3247
|
+
const payload = { cachedAt: Date.now(), syncResult };
|
|
3248
|
+
await (0, import_promises5.writeFile)(cachePath, JSON.stringify(payload), "utf-8");
|
|
3249
|
+
} catch {
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/mcp-server.ts
|
|
3183
3254
|
var import_meta = {};
|
|
3184
|
-
var __dirname = (0,
|
|
3185
|
-
var pkg = JSON.parse((0, import_node_fs3.readFileSync)((0,
|
|
3255
|
+
var __dirname = (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
3256
|
+
var pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path8.join)(__dirname, "..", "package.json"), "utf-8"));
|
|
3186
3257
|
var library = new FileLibrary();
|
|
3187
3258
|
var _validator = new N8nValidator();
|
|
3188
3259
|
function getValidator() {
|
|
@@ -3201,9 +3272,9 @@ function getMcpTelemetry() {
|
|
|
3201
3272
|
function getMcpPatternsPath() {
|
|
3202
3273
|
const val = process.env["KAIROS_TELEMETRY"];
|
|
3203
3274
|
if (val && val !== "false" && val !== "true") {
|
|
3204
|
-
return (0,
|
|
3275
|
+
return (0, import_node_path8.join)(val, "..", "patterns.json");
|
|
3205
3276
|
}
|
|
3206
|
-
return (0,
|
|
3277
|
+
return (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".kairos", "patterns.json");
|
|
3207
3278
|
}
|
|
3208
3279
|
var mcpTelemetry = getMcpTelemetry();
|
|
3209
3280
|
var mcpSessions = /* @__PURE__ */ new Map();
|
|
@@ -3221,7 +3292,14 @@ function getTelemetryReader() {
|
|
|
3221
3292
|
return null;
|
|
3222
3293
|
}
|
|
3223
3294
|
}
|
|
3295
|
+
function getMcpMode() {
|
|
3296
|
+
const mode = process.env["KAIROS_MCP_MODE"]?.toLowerCase();
|
|
3297
|
+
if (mode === "readonly" || mode === "validate") return mode;
|
|
3298
|
+
return "deploy";
|
|
3299
|
+
}
|
|
3224
3300
|
function isAllowed(action) {
|
|
3301
|
+
const mode = getMcpMode();
|
|
3302
|
+
if (mode === "readonly" || mode === "validate") return false;
|
|
3225
3303
|
const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`;
|
|
3226
3304
|
return process.env[key] === "true";
|
|
3227
3305
|
}
|
|
@@ -3245,8 +3323,20 @@ function getApiClient() {
|
|
|
3245
3323
|
}
|
|
3246
3324
|
return new N8nApiClient(baseUrl, apiKey, nullLogger);
|
|
3247
3325
|
}
|
|
3326
|
+
function getCatalogCachePath() {
|
|
3327
|
+
const telemetry = process.env["KAIROS_TELEMETRY"];
|
|
3328
|
+
const base = telemetry ? (0, import_node_path8.join)(telemetry, "..") : (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".kairos");
|
|
3329
|
+
return (0, import_node_path8.join)(base, "node-catalog-cache.json");
|
|
3330
|
+
}
|
|
3248
3331
|
async function autoSync() {
|
|
3249
3332
|
if (lastSync) return lastSync;
|
|
3333
|
+
const cachePath = getCatalogCachePath();
|
|
3334
|
+
const cached = await readCatalogCache(cachePath);
|
|
3335
|
+
if (cached) {
|
|
3336
|
+
lastSync = cached;
|
|
3337
|
+
_validator = new N8nValidator(lastSync.registry);
|
|
3338
|
+
return lastSync;
|
|
3339
|
+
}
|
|
3250
3340
|
const baseUrl = process.env["N8N_BASE_URL"];
|
|
3251
3341
|
const apiKey = process.env["N8N_API_KEY"];
|
|
3252
3342
|
if (!baseUrl || !apiKey) return null;
|
|
@@ -3256,6 +3346,8 @@ async function autoSync() {
|
|
|
3256
3346
|
if (nodeTypes.length === 0) return null;
|
|
3257
3347
|
lastSync = nodeSyncer.sync(nodeTypes);
|
|
3258
3348
|
_validator = new N8nValidator(lastSync.registry);
|
|
3349
|
+
writeCatalogCache(cachePath, lastSync).catch(() => {
|
|
3350
|
+
});
|
|
3259
3351
|
return lastSync;
|
|
3260
3352
|
} catch {
|
|
3261
3353
|
return null;
|
|
@@ -3274,13 +3366,9 @@ server.tool(
|
|
|
3274
3366
|
},
|
|
3275
3367
|
async ({ description, name }) => {
|
|
3276
3368
|
evictStaleSessions();
|
|
3277
|
-
const baseUrl = process.env["N8N_BASE_URL"];
|
|
3278
|
-
const apiKey = process.env["N8N_API_KEY"];
|
|
3279
|
-
if (!baseUrl || !apiKey) {
|
|
3280
|
-
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." }));
|
|
3281
|
-
}
|
|
3282
3369
|
const runId = generateUUID();
|
|
3283
3370
|
const workflowType = inferWorkflowType(description);
|
|
3371
|
+
const hasN8nCreds = !!(process.env["N8N_BASE_URL"] && process.env["N8N_API_KEY"]);
|
|
3284
3372
|
const syncPromise = autoSync();
|
|
3285
3373
|
const syncTimeout = new Promise((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS));
|
|
3286
3374
|
await library.initialize();
|
|
@@ -3300,7 +3388,8 @@ server.tool(
|
|
|
3300
3388
|
startTime: Date.now(),
|
|
3301
3389
|
validateAttempts: 0,
|
|
3302
3390
|
warnedRules: promptBuilder.getWarnedRules(),
|
|
3303
|
-
workflowType
|
|
3391
|
+
workflowType,
|
|
3392
|
+
matchCount: matches.length
|
|
3304
3393
|
});
|
|
3305
3394
|
await mcpTelemetry.emit("build_start", { description, model: "mcp-decomposed", dryRun: false }, runId);
|
|
3306
3395
|
}
|
|
@@ -3312,7 +3401,9 @@ server.tool(
|
|
|
3312
3401
|
topMatchScore: matches[0]?.score ?? null,
|
|
3313
3402
|
nodeCatalog: syncResult ? "synced" : "static",
|
|
3314
3403
|
nodeCount: syncResult?.nodeCount ?? null,
|
|
3315
|
-
...syncResult ? {} : {
|
|
3404
|
+
...syncResult ? {} : {
|
|
3405
|
+
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."
|
|
3406
|
+
},
|
|
3316
3407
|
systemPrompt: systemText,
|
|
3317
3408
|
userMessage: built.userMessage,
|
|
3318
3409
|
outputFormat: {
|
|
@@ -3385,10 +3476,11 @@ server.tool(
|
|
|
3385
3476
|
{
|
|
3386
3477
|
workflow: import_zod.z.string().describe("The validated workflow JSON string to deploy"),
|
|
3387
3478
|
activate: import_zod.z.boolean().default(false).describe("Activate the workflow immediately after deployment"),
|
|
3479
|
+
description: import_zod.z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
|
|
3388
3480
|
kairos_run_id: import_zod.z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
|
|
3389
3481
|
kairos_secret: import_zod.z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
|
|
3390
3482
|
},
|
|
3391
|
-
async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {
|
|
3483
|
+
async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {
|
|
3392
3484
|
const authError = checkMcpAuth(kairos_secret);
|
|
3393
3485
|
if (authError) return authError;
|
|
3394
3486
|
if (!isAllowed("deploy")) {
|
|
@@ -3429,8 +3521,8 @@ server.tool(
|
|
|
3429
3521
|
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.` : "";
|
|
3430
3522
|
await library.initialize();
|
|
3431
3523
|
await library.save(parsed, {
|
|
3432
|
-
description: session?.description ?? parsed.name,
|
|
3433
|
-
generationMode: "scratch",
|
|
3524
|
+
description: session?.description ?? userDescription ?? parsed.name,
|
|
3525
|
+
generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
|
|
3434
3526
|
generationAttempts: session?.validateAttempts ?? 1,
|
|
3435
3527
|
n8nWorkflowId: response.id
|
|
3436
3528
|
});
|
|
@@ -3467,12 +3559,16 @@ server.tool(
|
|
|
3467
3559
|
{
|
|
3468
3560
|
workflow_id: import_zod.z.string().describe("The n8n workflow ID to replace"),
|
|
3469
3561
|
workflow: import_zod.z.string().describe("The validated workflow JSON string"),
|
|
3562
|
+
description: import_zod.z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
|
|
3470
3563
|
kairos_run_id: import_zod.z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
|
|
3471
3564
|
kairos_secret: import_zod.z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
|
|
3472
3565
|
},
|
|
3473
|
-
async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {
|
|
3566
|
+
async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {
|
|
3474
3567
|
const authError = checkMcpAuth(kairos_secret);
|
|
3475
3568
|
if (authError) return authError;
|
|
3569
|
+
if (!isAllowed("deploy")) {
|
|
3570
|
+
return mcpError(JSON.stringify({ error: "Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable." }));
|
|
3571
|
+
}
|
|
3476
3572
|
let parsed;
|
|
3477
3573
|
try {
|
|
3478
3574
|
parsed = JSON.parse(workflowStr);
|
|
@@ -3496,8 +3592,8 @@ server.tool(
|
|
|
3496
3592
|
Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found.` : "";
|
|
3497
3593
|
await library.initialize();
|
|
3498
3594
|
await library.save(parsed, {
|
|
3499
|
-
description: session?.description ?? parsed.name,
|
|
3500
|
-
generationMode: "scratch",
|
|
3595
|
+
description: session?.description ?? userDescription ?? parsed.name,
|
|
3596
|
+
generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
|
|
3501
3597
|
generationAttempts: session?.validateAttempts ?? 1,
|
|
3502
3598
|
n8nWorkflowId: workflow_id
|
|
3503
3599
|
});
|
|
@@ -3743,8 +3839,27 @@ async function main() {
|
|
|
3743
3839
|
"[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set \u2014 kairos_prompt will fail. Set it before using workflow generation tools.\n"
|
|
3744
3840
|
);
|
|
3745
3841
|
}
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3842
|
+
const useHttp = process.argv.includes("--http");
|
|
3843
|
+
if (useHttp) {
|
|
3844
|
+
const port = parseInt(process.env["KAIROS_MCP_PORT"] ?? "3000", 10);
|
|
3845
|
+
const transport = new import_streamableHttp.StreamableHTTPServerTransport();
|
|
3846
|
+
await server.connect(transport);
|
|
3847
|
+
const httpServer = (0, import_node_http.createServer)(async (req, res) => {
|
|
3848
|
+
if (req.method === "GET" || req.method === "POST" || req.method === "DELETE") {
|
|
3849
|
+
await transport.handleRequest(req, res);
|
|
3850
|
+
} else {
|
|
3851
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
3852
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
httpServer.listen(port, () => {
|
|
3856
|
+
process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}
|
|
3857
|
+
`);
|
|
3858
|
+
});
|
|
3859
|
+
} else {
|
|
3860
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
3861
|
+
await server.connect(transport);
|
|
3862
|
+
}
|
|
3748
3863
|
}
|
|
3749
3864
|
main().catch((err) => {
|
|
3750
3865
|
console.error("Kairos MCP server failed to start:", err);
|