@bpmnkit/proxy 0.0.23 → 0.0.25
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 +1 -0
- package/dist/aikit-mcp.js +130 -2
- package/dist/index.js +73 -1
- package/dist/prompt.js +41 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -86,6 +86,7 @@ curl -H "X-Profile: production" http://localhost:3033/api/v2/process-definitions
|
|
|
86
86
|
| [`@bpmnkit/connector-gen`](https://www.npmjs.com/package/@bpmnkit/connector-gen) | Generate connector templates from OpenAPI specs |
|
|
87
87
|
| [`@bpmnkit/cli`](https://www.npmjs.com/package/@bpmnkit/cli) | Camunda 8 command-line interface (casen) |
|
|
88
88
|
| [`@bpmnkit/patterns`](https://www.npmjs.com/package/@bpmnkit/patterns) | Domain process patterns for BPMNKit AIKit |
|
|
89
|
+
| [`@bpmnkit/reebe-wasm`](https://www.npmjs.com/package/@bpmnkit/reebe-wasm) | WebAssembly BPMN engine for browser simulation |
|
|
89
90
|
| [`@bpmnkit/worker-client`](https://www.npmjs.com/package/@bpmnkit/worker-client) | Thin Zeebe REST client for standalone workers |
|
|
90
91
|
| [`@bpmnkit/cli-sdk`](https://www.npmjs.com/package/@bpmnkit/cli-sdk) | Plugin authoring SDK for the casen CLI |
|
|
91
92
|
| [`@bpmnkit/create-casen-plugin`](https://www.npmjs.com/package/@bpmnkit/create-casen-plugin) | Scaffold a new casen CLI plugin in seconds |
|
package/dist/aikit-mcp.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* bpmn_create, bpmn_read, bpmn_update, bpmn_validate, bpmn_deploy,
|
|
7
7
|
* bpmn_simulate, bpmn_run_history,
|
|
8
8
|
* worker_list, worker_scaffold,
|
|
9
|
+
* form_create, dmn_create,
|
|
9
10
|
* pattern_list, pattern_get
|
|
10
11
|
*
|
|
11
12
|
* Usage:
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
*/
|
|
20
21
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
21
22
|
import { homedir } from "node:os";
|
|
22
|
-
import { basename, join } from "node:path";
|
|
23
|
+
import { basename, dirname, join } from "node:path";
|
|
23
24
|
import { createInterface } from "node:readline";
|
|
24
25
|
import { Bpmn, compactify, optimize } from "@bpmnkit/core";
|
|
25
26
|
import { ALL_PATTERNS, findPattern } from "@bpmnkit/patterns";
|
|
@@ -63,6 +64,7 @@ async function fetchProxyXml(endpoint, body) {
|
|
|
63
64
|
const reader = res.body.getReader();
|
|
64
65
|
const decoder = new TextDecoder();
|
|
65
66
|
let xml;
|
|
67
|
+
let json;
|
|
66
68
|
let errorMsg;
|
|
67
69
|
const tokens = [];
|
|
68
70
|
let buffer = "";
|
|
@@ -83,6 +85,8 @@ async function fetchProxyXml(endpoint, body) {
|
|
|
83
85
|
tokens.push(event.text);
|
|
84
86
|
if (event.type === "xml" && event.xml)
|
|
85
87
|
xml = event.xml;
|
|
88
|
+
if (event.type === "json" && event.json)
|
|
89
|
+
json = event.json;
|
|
86
90
|
if (event.type === "error")
|
|
87
91
|
errorMsg = event.message;
|
|
88
92
|
}
|
|
@@ -93,7 +97,7 @@ async function fetchProxyXml(endpoint, body) {
|
|
|
93
97
|
}
|
|
94
98
|
if (errorMsg)
|
|
95
99
|
throw new Error(errorMsg);
|
|
96
|
-
return { xml, text: tokens.join("") };
|
|
100
|
+
return { xml, json, text: tokens.join("") };
|
|
97
101
|
}
|
|
98
102
|
/** Write BPMN XML to disk and return the absolute path. */
|
|
99
103
|
function writeBpmn(dir, name, xml) {
|
|
@@ -562,6 +566,44 @@ function toolPatternList() {
|
|
|
562
566
|
}));
|
|
563
567
|
return JSON.stringify({ patterns, total: patterns.length }, null, 2);
|
|
564
568
|
}
|
|
569
|
+
async function toolFormCreate(bpmnPath, outputDir) {
|
|
570
|
+
const absPath = expandHome(bpmnPath);
|
|
571
|
+
if (!existsSync(absPath))
|
|
572
|
+
throw new Error(`File not found: ${bpmnPath}`);
|
|
573
|
+
const xml = readFileSync(absPath, "utf8");
|
|
574
|
+
const defs = Bpmn.parse(xml);
|
|
575
|
+
const compact = compactify(defs);
|
|
576
|
+
const userTasks = compact.processes
|
|
577
|
+
.flatMap((p) => p.elements)
|
|
578
|
+
.filter((el) => el.type === "userTask" && el.formId);
|
|
579
|
+
if (userTasks.length === 0)
|
|
580
|
+
return JSON.stringify({ forms: [] });
|
|
581
|
+
const dir = outputDir ? expandHome(outputDir) : dirname(absPath);
|
|
582
|
+
if (!existsSync(dir))
|
|
583
|
+
mkdirSync(dir, { recursive: true });
|
|
584
|
+
for (const el of userTasks) {
|
|
585
|
+
const { json } = await fetchProxyXml("/chat", {
|
|
586
|
+
messages: [
|
|
587
|
+
{
|
|
588
|
+
role: "user",
|
|
589
|
+
content: `User task: ${el.name ?? el.id}, formId: ${el.formId}, process: ${compact.processes[0]?.name ?? compact.processes[0]?.id ?? "unknown"}`,
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
action: "create-form",
|
|
593
|
+
});
|
|
594
|
+
if (!json)
|
|
595
|
+
throw new Error(`AI did not produce a form for task ${el.id}`);
|
|
596
|
+
writeFileSync(join(dir, `${el.formId}.form`), json, "utf8");
|
|
597
|
+
}
|
|
598
|
+
return JSON.stringify({
|
|
599
|
+
forms: userTasks.map((el) => ({
|
|
600
|
+
taskId: el.id,
|
|
601
|
+
taskName: el.name ?? el.id,
|
|
602
|
+
formId: el.formId,
|
|
603
|
+
path: join(dir, `${el.formId}.form`),
|
|
604
|
+
})),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
565
607
|
function toolPatternGet(domain) {
|
|
566
608
|
const pattern = findPattern(domain) ?? ALL_PATTERNS.find((p) => p.id === domain);
|
|
567
609
|
if (!pattern)
|
|
@@ -577,6 +619,44 @@ function toolPatternGet(domain) {
|
|
|
577
619
|
template: pattern.template,
|
|
578
620
|
}, null, 2);
|
|
579
621
|
}
|
|
622
|
+
async function toolDmnCreate(bpmnPath, outputDir) {
|
|
623
|
+
const absPath = expandHome(bpmnPath);
|
|
624
|
+
if (!existsSync(absPath))
|
|
625
|
+
throw new Error(`File not found: ${bpmnPath}`);
|
|
626
|
+
const xml = readFileSync(absPath, "utf8");
|
|
627
|
+
const defs = Bpmn.parse(xml);
|
|
628
|
+
const compact = compactify(defs);
|
|
629
|
+
const brtasks = compact.processes
|
|
630
|
+
.flatMap((p) => p.elements)
|
|
631
|
+
.filter((el) => el.type === "businessRuleTask" && Boolean(el.decisionId));
|
|
632
|
+
if (brtasks.length === 0)
|
|
633
|
+
return JSON.stringify({ decisions: [] });
|
|
634
|
+
const dir = outputDir ? expandHome(outputDir) : dirname(absPath);
|
|
635
|
+
if (!existsSync(dir))
|
|
636
|
+
mkdirSync(dir, { recursive: true });
|
|
637
|
+
for (const el of brtasks) {
|
|
638
|
+
const { xml: dmnXml } = await fetchProxyXml("/chat", {
|
|
639
|
+
messages: [
|
|
640
|
+
{
|
|
641
|
+
role: "user",
|
|
642
|
+
content: `Decision: ${el.name ?? el.id}, decisionId: ${el.decisionId}, process: ${compact.processes[0]?.name ?? compact.processes[0]?.id ?? "unknown"}`,
|
|
643
|
+
},
|
|
644
|
+
],
|
|
645
|
+
action: "create-dmn",
|
|
646
|
+
});
|
|
647
|
+
if (!dmnXml)
|
|
648
|
+
throw new Error(`AI did not produce DMN for task ${el.id}`);
|
|
649
|
+
writeFileSync(join(dir, `${el.decisionId}.dmn`), dmnXml, "utf8");
|
|
650
|
+
}
|
|
651
|
+
return JSON.stringify({
|
|
652
|
+
decisions: brtasks.map((el) => ({
|
|
653
|
+
taskId: el.id,
|
|
654
|
+
taskName: el.name ?? el.id,
|
|
655
|
+
decisionId: el.decisionId,
|
|
656
|
+
path: join(dir, `${el.decisionId}.dmn`),
|
|
657
|
+
})),
|
|
658
|
+
});
|
|
659
|
+
}
|
|
580
660
|
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
581
661
|
const TOOLS = [
|
|
582
662
|
{
|
|
@@ -749,6 +829,44 @@ const TOOLS = [
|
|
|
749
829
|
required: ["domain"],
|
|
750
830
|
},
|
|
751
831
|
},
|
|
832
|
+
{
|
|
833
|
+
name: "form_create",
|
|
834
|
+
description: "Generate Camunda form JSON files for all user tasks in a BPMN process that have a formId. " +
|
|
835
|
+
"Writes one .form file per user task into the output directory (default: same directory as the BPMN file).",
|
|
836
|
+
inputSchema: {
|
|
837
|
+
type: "object",
|
|
838
|
+
properties: {
|
|
839
|
+
bpmnPath: {
|
|
840
|
+
type: "string",
|
|
841
|
+
description: "Path to the BPMN file",
|
|
842
|
+
},
|
|
843
|
+
outputDir: {
|
|
844
|
+
type: "string",
|
|
845
|
+
description: "Directory to write form files (default: same directory as the BPMN file)",
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
required: ["bpmnPath"],
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: "dmn_create",
|
|
853
|
+
description: "Generate DMN decision table XML files for all business rule tasks in a BPMN process that have a decisionId. " +
|
|
854
|
+
"Writes one .dmn file per business rule task into the output directory (default: same directory as the BPMN file).",
|
|
855
|
+
inputSchema: {
|
|
856
|
+
type: "object",
|
|
857
|
+
properties: {
|
|
858
|
+
bpmnPath: {
|
|
859
|
+
type: "string",
|
|
860
|
+
description: "Path to the BPMN file",
|
|
861
|
+
},
|
|
862
|
+
outputDir: {
|
|
863
|
+
type: "string",
|
|
864
|
+
description: "Directory to write DMN files (default: same directory as the BPMN file)",
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
required: ["bpmnPath"],
|
|
868
|
+
},
|
|
869
|
+
},
|
|
752
870
|
];
|
|
753
871
|
async function callTool(name, args) {
|
|
754
872
|
process.stderr.write(`[aikit-mcp] tool: ${name} args: ${JSON.stringify(args)}\n`);
|
|
@@ -780,6 +898,16 @@ async function callTool(name, args) {
|
|
|
780
898
|
return toolPatternList();
|
|
781
899
|
case "pattern_get":
|
|
782
900
|
return toolPatternGet(args.domain);
|
|
901
|
+
case "form_create": {
|
|
902
|
+
const path = String(args.bpmnPath ?? "");
|
|
903
|
+
const outDir = args.outputDir ? String(args.outputDir) : undefined;
|
|
904
|
+
return toolFormCreate(path, outDir);
|
|
905
|
+
}
|
|
906
|
+
case "dmn_create": {
|
|
907
|
+
const path = String(args.bpmnPath ?? "");
|
|
908
|
+
const outDir = args.outputDir ? String(args.outputDir) : undefined;
|
|
909
|
+
return toolDmnCreate(path, outDir);
|
|
910
|
+
}
|
|
783
911
|
default:
|
|
784
912
|
throw new Error(`Unknown tool: ${name}`);
|
|
785
913
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { getActiveName, getActiveProfile, getAuthHeader, getProfile, listProfile
|
|
|
10
10
|
import * as claude from "./adapters/claude.js";
|
|
11
11
|
import * as copilot from "./adapters/copilot.js";
|
|
12
12
|
import * as gemini from "./adapters/gemini.js";
|
|
13
|
-
import { buildImproveSystemPrompt, buildImproveUserMessage, buildIncidentSystemPrompt, buildIncidentUserMessage, buildMcpExplainPrompt, buildMcpImprovePrompt, buildMcpSystemPrompt, buildOperateChatSystemPrompt, buildSearchSystemPrompt, buildSystemPrompt, } from "./prompt.js";
|
|
13
|
+
import { buildDmnCreateSystemPrompt, buildFormCreateSystemPrompt, buildImproveSystemPrompt, buildImproveUserMessage, buildIncidentSystemPrompt, buildIncidentUserMessage, buildMcpExplainPrompt, buildMcpImprovePrompt, buildMcpSystemPrompt, buildOperateChatSystemPrompt, buildSearchSystemPrompt, buildSystemPrompt, } from "./prompt.js";
|
|
14
14
|
import { handleDeleteRunHistory, handleGetRunHistory, handleGetRunHistoryDetail, handleRerunHistory, matchRerunHistoryRoute, matchRunHistoryRoute, } from "./routes/run-history.js";
|
|
15
15
|
import { handleWebhook, matchWebhookRoute, startTriggers } from "./triggers/index.js";
|
|
16
16
|
import { WORKER_TEMPLATES } from "./worker-templates.js";
|
|
@@ -278,6 +278,78 @@ const server = http.createServer(async (req, res) => {
|
|
|
278
278
|
return;
|
|
279
279
|
}
|
|
280
280
|
console.log(`[server] /chat → adapter: ${detected.name}, action: ${action ?? "chat"}, mcp: ${detected.adapter.supportsMcp}`);
|
|
281
|
+
// ── create-form ───────────────────────────────────────────────────────────
|
|
282
|
+
if (action === "create-form") {
|
|
283
|
+
const taskDescription = messages[0]?.content ?? "";
|
|
284
|
+
const systemPrompt = buildFormCreateSystemPrompt("", taskDescription);
|
|
285
|
+
res.writeHead(200, {
|
|
286
|
+
"Content-Type": "text/event-stream",
|
|
287
|
+
"Cache-Control": "no-cache",
|
|
288
|
+
Connection: "keep-alive",
|
|
289
|
+
});
|
|
290
|
+
const accumulated = [];
|
|
291
|
+
try {
|
|
292
|
+
await detected.adapter.stream(messages, systemPrompt, null, (token) => {
|
|
293
|
+
accumulated.push(token);
|
|
294
|
+
res.write(`data: ${JSON.stringify({ type: "token", text: token })}\n\n`);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
299
|
+
console.error(`[server] create-form adapter error: ${msg}`);
|
|
300
|
+
res.write(`data: ${JSON.stringify({ type: "error", message: msg })}\n\n`);
|
|
301
|
+
res.end();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const fullText = accumulated.join("");
|
|
305
|
+
const jsonMatch = fullText.match(/```json\s*([\s\S]*?)```/);
|
|
306
|
+
if (jsonMatch) {
|
|
307
|
+
// biome-ignore lint/style/noNonNullAssertion: capture group 1 always present when match succeeds
|
|
308
|
+
const jsonString = jsonMatch[1].trim();
|
|
309
|
+
res.write(`data: ${JSON.stringify({ type: "json", json: jsonString })}\n\n`);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
res.write(`data: ${JSON.stringify({ type: "error", message: "AI did not produce a form JSON" })}\n\n`);
|
|
313
|
+
}
|
|
314
|
+
res.end();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
// ── create-dmn ────────────────────────────────────────────────────────────
|
|
318
|
+
if (action === "create-dmn") {
|
|
319
|
+
const taskDescription = messages[0]?.content ?? "";
|
|
320
|
+
const systemPrompt = buildDmnCreateSystemPrompt("", taskDescription);
|
|
321
|
+
res.writeHead(200, {
|
|
322
|
+
"Content-Type": "text/event-stream",
|
|
323
|
+
"Cache-Control": "no-cache",
|
|
324
|
+
Connection: "keep-alive",
|
|
325
|
+
});
|
|
326
|
+
const accumulated = [];
|
|
327
|
+
try {
|
|
328
|
+
await detected.adapter.stream(messages, systemPrompt, null, (token) => {
|
|
329
|
+
accumulated.push(token);
|
|
330
|
+
res.write(`data: ${JSON.stringify({ type: "token", text: token })}\n\n`);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
335
|
+
console.error(`[server] create-dmn adapter error: ${msg}`);
|
|
336
|
+
res.write(`data: ${JSON.stringify({ type: "error", message: msg })}\n\n`);
|
|
337
|
+
res.end();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const fullText = accumulated.join("");
|
|
341
|
+
const xmlMatch = fullText.match(/```xml\s*([\s\S]*?)```/);
|
|
342
|
+
if (xmlMatch) {
|
|
343
|
+
// biome-ignore lint/style/noNonNullAssertion: capture group 1 always present when match succeeds
|
|
344
|
+
const xmlString = xmlMatch[1].trim();
|
|
345
|
+
res.write(`data: ${JSON.stringify({ type: "xml", xml: xmlString })}\n\n`);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
res.write(`data: ${JSON.stringify({ type: "error", message: "AI did not produce DMN XML" })}\n\n`);
|
|
349
|
+
}
|
|
350
|
+
res.end();
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
281
353
|
const currentCompact = context !== null && typeof context === "object" && "processes" in context
|
|
282
354
|
? context
|
|
283
355
|
: null;
|
package/dist/prompt.js
CHANGED
|
@@ -279,6 +279,47 @@ export function buildImproveUserMessage(ctx) {
|
|
|
279
279
|
lines.push("Explain your changes, then output the BpmnOperation array in a ```json block.");
|
|
280
280
|
return lines.join("\n");
|
|
281
281
|
}
|
|
282
|
+
// ── Form / DMN creation prompt builders ──────────────────────────────────────
|
|
283
|
+
export function buildFormCreateSystemPrompt(taskName, taskContext) {
|
|
284
|
+
return [
|
|
285
|
+
"You are a Camunda Form expert.",
|
|
286
|
+
`Task name: ${taskName}`,
|
|
287
|
+
`Process context: ${taskContext}`,
|
|
288
|
+
"",
|
|
289
|
+
"Generate a Camunda Form JSON for this user task.",
|
|
290
|
+
"",
|
|
291
|
+
"Output format:",
|
|
292
|
+
"```json",
|
|
293
|
+
"{",
|
|
294
|
+
' "id": "<formId>",',
|
|
295
|
+
' "fields": [',
|
|
296
|
+
' { "type": "...", "id": "...", "label": "...", "key": "...", "required": true }',
|
|
297
|
+
" ]",
|
|
298
|
+
"}",
|
|
299
|
+
"```",
|
|
300
|
+
"",
|
|
301
|
+
'Valid field types: "textfield", "textarea", "number", "select" (add a values array), "checkbox", "datetime", "group" (contains nested fields).',
|
|
302
|
+
"Infer sensible fields from the task name and process context.",
|
|
303
|
+
"Return ONLY valid JSON inside a ```json code block — no explanation.",
|
|
304
|
+
].join("\n");
|
|
305
|
+
}
|
|
306
|
+
export function buildDmnCreateSystemPrompt(decisionId, taskContext) {
|
|
307
|
+
return [
|
|
308
|
+
"You are a DMN expert.",
|
|
309
|
+
`Decision ID: ${decisionId}`,
|
|
310
|
+
`Process context: ${taskContext}`,
|
|
311
|
+
"",
|
|
312
|
+
"Generate a complete, valid DMN 1.3 decision table XML for this decision.",
|
|
313
|
+
"",
|
|
314
|
+
"Requirements:",
|
|
315
|
+
"- <definitions> with namespace https://www.omg.org/spec/DMN/20191111/MODEL/",
|
|
316
|
+
`- <decision id="${decisionId}"> containing a <decisionTable>`,
|
|
317
|
+
"- At least one input column, one output column, and one rule row.",
|
|
318
|
+
"",
|
|
319
|
+
"Infer sensible inputs and outputs from the decision ID and process context.",
|
|
320
|
+
"Return ONLY the DMN XML inside a ```xml code block — no explanation.",
|
|
321
|
+
].join("\n");
|
|
322
|
+
}
|
|
282
323
|
// ── Fallback prompt builders (for non-MCP adapters like Gemini) ───────────────
|
|
283
324
|
/** Full system prompt for non-MCP adapters that must return a CompactDiagram JSON block. */
|
|
284
325
|
export function buildSystemPrompt(context) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bpmnkit/proxy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Local proxy server for BPMN Kit — AI bridge (SSE/MCP) and Camunda API proxy using stored CLI profiles",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"better-sqlite3": "^12.8.0",
|
|
28
28
|
"imapflow": "^1.2.18",
|
|
29
29
|
"nodemailer": "^6.10.1",
|
|
30
|
-
"@bpmnkit/api": "0.0.
|
|
31
|
-
"@bpmnkit/
|
|
32
|
-
"@bpmnkit/
|
|
33
|
-
"@bpmnkit/
|
|
30
|
+
"@bpmnkit/api": "0.0.18",
|
|
31
|
+
"@bpmnkit/core": "0.0.22",
|
|
32
|
+
"@bpmnkit/patterns": "0.0.3",
|
|
33
|
+
"@bpmnkit/profiles": "0.0.16"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|