@bpmnkit/proxy 0.0.22 → 0.0.24

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/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.22",
3
+ "version": "0.0.24",
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": {
@@ -12,7 +12,8 @@
12
12
  ".": {
13
13
  "types": "./dist/index.d.ts",
14
14
  "default": "./dist/index.js"
15
- }
15
+ },
16
+ "./dist/aikit-mcp.js": "./dist/aikit-mcp.js"
16
17
  },
17
18
  "files": [
18
19
  "LICENSE",