@kairos-sdk/core 0.3.0 → 0.3.2
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 +8 -7
- package/dist/{chunk-Q77XA7UC.js → chunk-KQSNT3HZ.js} +2 -2
- package/dist/{chunk-DDV7ZART.js → chunk-RYGYNOR6.js} +20 -5
- package/dist/chunk-RYGYNOR6.js.map +1 -0
- package/dist/cli.cjs +19 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +19 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -2
- package/dist/mcp-server.cjs +150 -5
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/mcp-server.js +136 -2
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DDV7ZART.js.map +0 -1
- /package/dist/{chunk-Q77XA7UC.js.map → chunk-KQSNT3HZ.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -283,6 +283,17 @@ interface N8nWorkflowResponse {
|
|
|
283
283
|
shared?: boolean;
|
|
284
284
|
isArchived?: boolean;
|
|
285
285
|
}
|
|
286
|
+
interface N8nNodeTypeInfo {
|
|
287
|
+
name: string;
|
|
288
|
+
displayName: string;
|
|
289
|
+
version: number | number[];
|
|
290
|
+
description?: string;
|
|
291
|
+
group?: string[];
|
|
292
|
+
credentials?: Array<{
|
|
293
|
+
name: string;
|
|
294
|
+
required?: boolean;
|
|
295
|
+
}>;
|
|
296
|
+
}
|
|
286
297
|
|
|
287
298
|
declare class N8nApiClient {
|
|
288
299
|
private readonly baseUrl;
|
|
@@ -304,6 +315,7 @@ declare class N8nApiClient {
|
|
|
304
315
|
createTag(name: string): Promise<Tag>;
|
|
305
316
|
tagWorkflow(workflowId: string, tagIds: string[]): Promise<void>;
|
|
306
317
|
untagWorkflow(workflowId: string, tagIds: string[]): Promise<void>;
|
|
318
|
+
getNodeTypes(): Promise<N8nNodeTypeInfo[]>;
|
|
307
319
|
private mapExecution;
|
|
308
320
|
}
|
|
309
321
|
|
package/dist/index.d.ts
CHANGED
|
@@ -283,6 +283,17 @@ interface N8nWorkflowResponse {
|
|
|
283
283
|
shared?: boolean;
|
|
284
284
|
isArchived?: boolean;
|
|
285
285
|
}
|
|
286
|
+
interface N8nNodeTypeInfo {
|
|
287
|
+
name: string;
|
|
288
|
+
displayName: string;
|
|
289
|
+
version: number | number[];
|
|
290
|
+
description?: string;
|
|
291
|
+
group?: string[];
|
|
292
|
+
credentials?: Array<{
|
|
293
|
+
name: string;
|
|
294
|
+
required?: boolean;
|
|
295
|
+
}>;
|
|
296
|
+
}
|
|
286
297
|
|
|
287
298
|
declare class N8nApiClient {
|
|
288
299
|
private readonly baseUrl;
|
|
@@ -304,6 +315,7 @@ declare class N8nApiClient {
|
|
|
304
315
|
createTag(name: string): Promise<Tag>;
|
|
305
316
|
tagWorkflow(workflowId: string, tagIds: string[]): Promise<void>;
|
|
306
317
|
untagWorkflow(workflowId: string, tagIds: string[]): Promise<void>;
|
|
318
|
+
getNodeTypes(): Promise<N8nNodeTypeInfo[]>;
|
|
307
319
|
private mapExecution;
|
|
308
320
|
}
|
|
309
321
|
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TelemetryCollector,
|
|
9
9
|
TemplateSyncer,
|
|
10
10
|
ValidationError
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KQSNT3HZ.js";
|
|
12
12
|
import {
|
|
13
13
|
ApiError,
|
|
14
14
|
DEFAULT_REGISTRY,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
nullLogger,
|
|
27
27
|
rerank,
|
|
28
28
|
tokenize
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-RYGYNOR6.js";
|
|
30
30
|
export {
|
|
31
31
|
ApiError,
|
|
32
32
|
DEFAULT_REGISTRY,
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -1144,6 +1144,14 @@ var N8nApiClient = class {
|
|
|
1144
1144
|
const remaining = (current.tags ?? []).filter((t) => !tagIds.includes(t.id)).map((t) => ({ id: t.id }));
|
|
1145
1145
|
await this.request("PUT", `/workflows/${workflowId}/tags`, remaining);
|
|
1146
1146
|
}
|
|
1147
|
+
async getNodeTypes() {
|
|
1148
|
+
try {
|
|
1149
|
+
const response = await this.request("GET", "/node-types");
|
|
1150
|
+
return response.data ?? response;
|
|
1151
|
+
} catch {
|
|
1152
|
+
return [];
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1147
1155
|
mapExecution(e) {
|
|
1148
1156
|
return {
|
|
1149
1157
|
id: e.id,
|
|
@@ -1363,9 +1371,9 @@ var RULE_REMEDIES = {
|
|
|
1363
1371
|
22: "Ensure all required parameters are set for each node type (e.g. webhook needs httpMethod and path)"
|
|
1364
1372
|
};
|
|
1365
1373
|
var PromptBuilder = class {
|
|
1366
|
-
build(request, matches, globalFailureRates = []) {
|
|
1374
|
+
build(request, matches, globalFailureRates = [], dynamicCatalog) {
|
|
1367
1375
|
const mode = this.resolveMode(matches);
|
|
1368
|
-
const system = this.buildSystem(matches, mode, globalFailureRates);
|
|
1376
|
+
const system = this.buildSystem(matches, mode, globalFailureRates, dynamicCatalog);
|
|
1369
1377
|
const userMessage = this.buildUserMessage(request, matches, mode);
|
|
1370
1378
|
return { system, userMessage, mode, matches };
|
|
1371
1379
|
}
|
|
@@ -1384,11 +1392,18 @@ Fix ALL of the above issues in your new response. Do not repeat any of these mis
|
|
|
1384
1392
|
if (!top) return "scratch";
|
|
1385
1393
|
return scoreToMode(top.score);
|
|
1386
1394
|
}
|
|
1387
|
-
buildSystem(matches, mode, globalFailureRates = []) {
|
|
1395
|
+
buildSystem(matches, mode, globalFailureRates = [], dynamicCatalog) {
|
|
1396
|
+
let basePrompt = SYSTEM_PROMPT_V1;
|
|
1397
|
+
if (dynamicCatalog) {
|
|
1398
|
+
basePrompt = basePrompt.replace(
|
|
1399
|
+
/## NODE CATALOG — exact type strings and safe typeVersions[\s\S]*?(?=## PRE-DELIVERY SELF-CHECK)/,
|
|
1400
|
+
dynamicCatalog + "\n\n"
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1388
1403
|
const blocks = [
|
|
1389
1404
|
{
|
|
1390
1405
|
type: "text",
|
|
1391
|
-
text:
|
|
1406
|
+
text: basePrompt,
|
|
1392
1407
|
cache_control: { type: "ephemeral" }
|
|
1393
1408
|
}
|
|
1394
1409
|
];
|
|
@@ -1559,6 +1574,63 @@ var TelemetryReader = class {
|
|
|
1559
1574
|
}
|
|
1560
1575
|
};
|
|
1561
1576
|
|
|
1577
|
+
// src/validation/node-syncer.ts
|
|
1578
|
+
var TRIGGER_PATTERNS = [/trigger/i, /Trigger$/];
|
|
1579
|
+
var NodeSyncer = class {
|
|
1580
|
+
baseRegistry;
|
|
1581
|
+
constructor() {
|
|
1582
|
+
this.baseRegistry = new Map(DEFAULT_REGISTRY.map((d) => [d.type, d]));
|
|
1583
|
+
}
|
|
1584
|
+
sync(liveNodes) {
|
|
1585
|
+
const merged = new Map(this.baseRegistry);
|
|
1586
|
+
let newNodes = 0;
|
|
1587
|
+
for (const node of liveNodes) {
|
|
1588
|
+
const versions = Array.isArray(node.version) ? node.version : [node.version];
|
|
1589
|
+
const isTrigger = TRIGGER_PATTERNS.some((p) => p.test(node.name));
|
|
1590
|
+
const credentialType = node.credentials?.[0]?.name;
|
|
1591
|
+
const existing = merged.get(node.name);
|
|
1592
|
+
if (existing) {
|
|
1593
|
+
const allVersions = /* @__PURE__ */ new Set([...existing.safeTypeVersions, ...versions]);
|
|
1594
|
+
merged.set(node.name, {
|
|
1595
|
+
...existing,
|
|
1596
|
+
safeTypeVersions: [...allVersions].sort((a, b) => a - b)
|
|
1597
|
+
});
|
|
1598
|
+
} else {
|
|
1599
|
+
newNodes++;
|
|
1600
|
+
merged.set(node.name, {
|
|
1601
|
+
type: node.name,
|
|
1602
|
+
safeTypeVersions: versions.sort((a, b) => a - b),
|
|
1603
|
+
requiredParams: [],
|
|
1604
|
+
...credentialType ? { credentialType } : {},
|
|
1605
|
+
...isTrigger ? { isTrigger: true } : {}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
const definitions = [...merged.values()];
|
|
1610
|
+
const registry = new NodeRegistry(definitions);
|
|
1611
|
+
const catalogText = this.buildCatalog(definitions);
|
|
1612
|
+
return { registry, catalogText, nodeCount: definitions.length, newNodes };
|
|
1613
|
+
}
|
|
1614
|
+
buildCatalog(definitions) {
|
|
1615
|
+
const triggers = definitions.filter((d) => d.isTrigger);
|
|
1616
|
+
const regular = definitions.filter((d) => !d.isTrigger);
|
|
1617
|
+
const formatEntry = (d) => {
|
|
1618
|
+
const versions = d.safeTypeVersions.join(", ");
|
|
1619
|
+
const cred = d.credentialType ? ` \u2014 cred: ${d.credentialType}` : "";
|
|
1620
|
+
return `${d.type} typeVersion: ${versions}${cred}`;
|
|
1621
|
+
};
|
|
1622
|
+
const triggerLines = triggers.map(formatEntry).join("\n");
|
|
1623
|
+
const regularLines = regular.map(formatEntry).join("\n");
|
|
1624
|
+
return `## NODE CATALOG \u2014 synced from your n8n instance (${definitions.length} node types)
|
|
1625
|
+
|
|
1626
|
+
### Triggers:
|
|
1627
|
+
${triggerLines}
|
|
1628
|
+
|
|
1629
|
+
### Regular nodes:
|
|
1630
|
+
${regularLines}`;
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
|
|
1562
1634
|
// src/utils/logger.ts
|
|
1563
1635
|
var nullLogger = {
|
|
1564
1636
|
debug() {
|
|
@@ -1580,6 +1652,8 @@ var __dirname = (0, import_node_path3.dirname)((0, import_node_url.fileURLToPath
|
|
|
1580
1652
|
var pkg = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path3.join)(__dirname, "..", "package.json"), "utf-8"));
|
|
1581
1653
|
var library = new FileLibrary();
|
|
1582
1654
|
var validator = new N8nValidator();
|
|
1655
|
+
var nodeSyncer = new NodeSyncer();
|
|
1656
|
+
var lastSync = null;
|
|
1583
1657
|
var stripper = new N8nFieldStripper();
|
|
1584
1658
|
var promptBuilder = new PromptBuilder();
|
|
1585
1659
|
function getTelemetryReader() {
|
|
@@ -1601,6 +1675,22 @@ function getApiClient() {
|
|
|
1601
1675
|
}
|
|
1602
1676
|
return new N8nApiClient(baseUrl, apiKey, nullLogger);
|
|
1603
1677
|
}
|
|
1678
|
+
async function autoSync() {
|
|
1679
|
+
if (lastSync) return lastSync;
|
|
1680
|
+
const baseUrl = process.env["N8N_BASE_URL"];
|
|
1681
|
+
const apiKey = process.env["N8N_API_KEY"];
|
|
1682
|
+
if (!baseUrl || !apiKey) return null;
|
|
1683
|
+
try {
|
|
1684
|
+
const client = new N8nApiClient(baseUrl, apiKey, nullLogger);
|
|
1685
|
+
const nodeTypes = await client.getNodeTypes();
|
|
1686
|
+
if (nodeTypes.length === 0) return null;
|
|
1687
|
+
lastSync = nodeSyncer.sync(nodeTypes);
|
|
1688
|
+
validator = new N8nValidator(lastSync.registry);
|
|
1689
|
+
return lastSync;
|
|
1690
|
+
} catch {
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1604
1694
|
var server = new import_mcp.McpServer({
|
|
1605
1695
|
name: "kairos",
|
|
1606
1696
|
version: pkg.version
|
|
@@ -1613,12 +1703,24 @@ server.tool(
|
|
|
1613
1703
|
name: import_zod.z.string().optional().describe("Optional workflow name override")
|
|
1614
1704
|
},
|
|
1615
1705
|
async ({ description, name }) => {
|
|
1706
|
+
const baseUrl = process.env["N8N_BASE_URL"];
|
|
1707
|
+
const apiKey = process.env["N8N_API_KEY"];
|
|
1708
|
+
if (!baseUrl || !apiKey) {
|
|
1709
|
+
return {
|
|
1710
|
+
content: [{
|
|
1711
|
+
type: "text",
|
|
1712
|
+
text: 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." })
|
|
1713
|
+
}],
|
|
1714
|
+
isError: true
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1616
1717
|
await library.initialize();
|
|
1718
|
+
const syncResult = await autoSync();
|
|
1617
1719
|
const matches = await library.search(description);
|
|
1618
1720
|
const telemetryReader = getTelemetryReader();
|
|
1619
1721
|
const failureRates = await telemetryReader?.getFailureRates() ?? [];
|
|
1620
1722
|
const request = { description, ...name ? { name } : {} };
|
|
1621
|
-
const built = promptBuilder.build(request, matches, failureRates);
|
|
1723
|
+
const built = promptBuilder.build(request, matches, failureRates, syncResult?.catalogText);
|
|
1622
1724
|
const systemText = built.system.map((block) => block.text).join("\n\n---\n\n");
|
|
1623
1725
|
return {
|
|
1624
1726
|
content: [{
|
|
@@ -1627,6 +1729,9 @@ server.tool(
|
|
|
1627
1729
|
mode: built.mode,
|
|
1628
1730
|
matchCount: matches.length,
|
|
1629
1731
|
topMatchScore: matches[0]?.score ?? null,
|
|
1732
|
+
nodeCatalog: syncResult ? "synced" : "static",
|
|
1733
|
+
nodeCount: syncResult?.nodeCount ?? null,
|
|
1734
|
+
...syncResult ? {} : { syncWarning: "Could not sync node types from your n8n instance. Using static fallback catalog \u2014 generated workflows may not match your exact n8n setup." },
|
|
1630
1735
|
systemPrompt: systemText,
|
|
1631
1736
|
userMessage: built.userMessage,
|
|
1632
1737
|
outputFormat: {
|
|
@@ -1806,6 +1911,46 @@ server.tool(
|
|
|
1806
1911
|
};
|
|
1807
1912
|
}
|
|
1808
1913
|
);
|
|
1914
|
+
server.tool(
|
|
1915
|
+
"kairos_sync",
|
|
1916
|
+
"Sync the node catalog from your live n8n instance. Fetches all installed node types and versions so Kairos knows exactly what your n8n supports. Automatically called by kairos_prompt when n8n credentials are set, but you can call this manually to force a refresh.",
|
|
1917
|
+
{},
|
|
1918
|
+
async () => {
|
|
1919
|
+
const baseUrl = process.env["N8N_BASE_URL"];
|
|
1920
|
+
const apiKey = process.env["N8N_API_KEY"];
|
|
1921
|
+
if (!baseUrl || !apiKey) {
|
|
1922
|
+
return {
|
|
1923
|
+
content: [{
|
|
1924
|
+
type: "text",
|
|
1925
|
+
text: JSON.stringify({ error: "N8N_BASE_URL and N8N_API_KEY are required for sync." })
|
|
1926
|
+
}],
|
|
1927
|
+
isError: true
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
lastSync = null;
|
|
1931
|
+
const result = await autoSync();
|
|
1932
|
+
if (!result) {
|
|
1933
|
+
return {
|
|
1934
|
+
content: [{
|
|
1935
|
+
type: "text",
|
|
1936
|
+
text: JSON.stringify({ error: "Failed to fetch node types from n8n. Check your credentials and that your instance is running." })
|
|
1937
|
+
}],
|
|
1938
|
+
isError: true
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
return {
|
|
1942
|
+
content: [{
|
|
1943
|
+
type: "text",
|
|
1944
|
+
text: JSON.stringify({
|
|
1945
|
+
synced: true,
|
|
1946
|
+
nodeCount: result.nodeCount,
|
|
1947
|
+
newNodes: result.newNodes,
|
|
1948
|
+
message: `Synced ${result.nodeCount} node types from your n8n instance (${result.newNodes} not in default catalog).`
|
|
1949
|
+
}, null, 2)
|
|
1950
|
+
}]
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
);
|
|
1809
1954
|
server.tool(
|
|
1810
1955
|
"kairos_list",
|
|
1811
1956
|
"List all workflows deployed on the connected n8n instance.",
|