@bpmnkit/proxy 0.0.8
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/adapters/claude.js +108 -0
- package/dist/adapters/copilot.js +46 -0
- package/dist/adapters/gemini.js +43 -0
- package/dist/apply-ops.js +79 -0
- package/dist/bridge.bundle.js +5298 -0
- package/dist/bridge.js +226 -0
- package/dist/index.js +592 -0
- package/dist/mcp-server.js +615 -0
- package/dist/prompt.js +189 -0
- package/package.json +35 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export const supportsMcp = true;
|
|
5
|
+
export async function available() {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
const proc = spawn("claude", ["--version"], { stdio: "ignore" });
|
|
8
|
+
proc.on("error", () => resolve(false));
|
|
9
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export async function stream(messages, systemPrompt, mcpConfigFile, onToken) {
|
|
13
|
+
// Build conversation as a single prompt string
|
|
14
|
+
const parts = [systemPrompt, ""];
|
|
15
|
+
for (const msg of messages) {
|
|
16
|
+
parts.push(`${msg.role === "user" ? "Human" : "Assistant"}: ${msg.content}`);
|
|
17
|
+
}
|
|
18
|
+
parts.push("Assistant:");
|
|
19
|
+
const fullPrompt = parts.join("\n");
|
|
20
|
+
const MCP_TOOLS = [
|
|
21
|
+
"mcp__bpmn__get_diagram",
|
|
22
|
+
"mcp__bpmn__compose_diagram",
|
|
23
|
+
"mcp__bpmn__add_elements",
|
|
24
|
+
"mcp__bpmn__remove_elements",
|
|
25
|
+
"mcp__bpmn__update_element",
|
|
26
|
+
"mcp__bpmn__set_condition",
|
|
27
|
+
"mcp__bpmn__add_http_call",
|
|
28
|
+
"mcp__bpmn__replace_diagram",
|
|
29
|
+
];
|
|
30
|
+
const args = [
|
|
31
|
+
"-p",
|
|
32
|
+
fullPrompt,
|
|
33
|
+
"--output-format",
|
|
34
|
+
"stream-json",
|
|
35
|
+
"--verbose",
|
|
36
|
+
"--dangerously-skip-permissions",
|
|
37
|
+
"--permission-mode",
|
|
38
|
+
"bypassPermissions",
|
|
39
|
+
];
|
|
40
|
+
// Write a project-level .claude/settings.json that pre-approves all bpmn tools,
|
|
41
|
+
// then spawn claude with cwd pointing there so it reads the settings.
|
|
42
|
+
let spawnCwd;
|
|
43
|
+
if (mcpConfigFile) {
|
|
44
|
+
const tmpDir = dirname(mcpConfigFile);
|
|
45
|
+
spawnCwd = tmpDir;
|
|
46
|
+
const claudeDir = join(tmpDir, ".claude");
|
|
47
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
48
|
+
writeFileSync(join(claudeDir, "settings.json"), JSON.stringify({ permissions: { allow: MCP_TOOLS } }));
|
|
49
|
+
args.push("--mcp-config", mcpConfigFile);
|
|
50
|
+
args.push("--allowedTools", MCP_TOOLS.join(","));
|
|
51
|
+
args.push("--strict-mcp-config");
|
|
52
|
+
}
|
|
53
|
+
// Strip CLAUDECODE so the nested-session guard in the CLI doesn't block us.
|
|
54
|
+
const spawnEnv = { ...process.env };
|
|
55
|
+
spawnEnv.CLAUDECODE = undefined;
|
|
56
|
+
console.log(`[claude] spawning with MCP: ${mcpConfigFile !== null}`);
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
const proc = spawn("claude", args, {
|
|
59
|
+
cwd: spawnCwd,
|
|
60
|
+
env: spawnEnv,
|
|
61
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
62
|
+
});
|
|
63
|
+
let buf = "";
|
|
64
|
+
let stderrBuf = "";
|
|
65
|
+
proc.stdout?.on("data", (chunk) => {
|
|
66
|
+
buf += chunk.toString();
|
|
67
|
+
const lines = buf.split("\n");
|
|
68
|
+
buf = lines.pop() ?? "";
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
if (!line.trim())
|
|
71
|
+
continue;
|
|
72
|
+
try {
|
|
73
|
+
const event = JSON.parse(line);
|
|
74
|
+
if (event.type === "assistant" && event.message?.content) {
|
|
75
|
+
for (const block of event.message.content) {
|
|
76
|
+
if (block.type === "text" && block.text) {
|
|
77
|
+
onToken(block.text);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* non-JSON line, skip */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
proc.stderr?.on("data", (chunk) => {
|
|
88
|
+
const text = chunk.toString();
|
|
89
|
+
stderrBuf += text;
|
|
90
|
+
process.stderr.write(`[claude stderr] ${text}`);
|
|
91
|
+
});
|
|
92
|
+
proc.on("error", (err) => {
|
|
93
|
+
console.error(`[claude] spawn error: ${String(err)}`);
|
|
94
|
+
reject(err);
|
|
95
|
+
});
|
|
96
|
+
proc.on("close", (code) => {
|
|
97
|
+
console.log(`[claude] exited with code ${code}`);
|
|
98
|
+
if (code === 0) {
|
|
99
|
+
resolve();
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const detail = stderrBuf.trim() ? `: ${stderrBuf.trim()}` : "";
|
|
103
|
+
reject(new Error(`claude exited with code ${code}${detail}`));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter for the new GitHub Copilot CLI (`copilot` / `@github/copilot`, GA Feb 2026).
|
|
3
|
+
* Note: the old `gh copilot` extension was deprecated Oct 2025 and is no longer supported.
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
export const supportsMcp = true;
|
|
7
|
+
export async function available() {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const proc = spawn("copilot", ["--version"], { stdio: "ignore" });
|
|
10
|
+
proc.on("error", () => resolve(false));
|
|
11
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export async function stream(messages, systemPrompt, mcpConfigFile, onToken) {
|
|
15
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
16
|
+
const prompt = `${systemPrompt}\n\nUser: ${lastUser?.content ?? "help"}`;
|
|
17
|
+
const args = ["-p", prompt, "--yolo"];
|
|
18
|
+
if (mcpConfigFile) {
|
|
19
|
+
args.push("--additional-mcp-config", mcpConfigFile);
|
|
20
|
+
args.push("--allow-all-tools");
|
|
21
|
+
}
|
|
22
|
+
console.log(`[copilot] spawning with MCP: ${mcpConfigFile !== null}`);
|
|
23
|
+
await new Promise((resolve, reject) => {
|
|
24
|
+
const proc = spawn("copilot", args, {
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
proc.stdout?.on("data", (chunk) => {
|
|
28
|
+
onToken(chunk.toString());
|
|
29
|
+
});
|
|
30
|
+
proc.stderr?.on("data", (chunk) => {
|
|
31
|
+
process.stderr.write(`[copilot stderr] ${chunk.toString()}`);
|
|
32
|
+
});
|
|
33
|
+
proc.on("error", (err) => {
|
|
34
|
+
console.error(`[copilot] spawn error: ${String(err)}`);
|
|
35
|
+
reject(err);
|
|
36
|
+
});
|
|
37
|
+
proc.on("close", (code) => {
|
|
38
|
+
console.log(`[copilot] exited with code ${code}`);
|
|
39
|
+
if (code === 0)
|
|
40
|
+
resolve();
|
|
41
|
+
else
|
|
42
|
+
reject(new Error(`copilot exited with code ${code}`));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=copilot.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter for Google Gemini CLI.
|
|
3
|
+
* MCP is not supported per-invocation (requires global settings.json),
|
|
4
|
+
* so this adapter falls back to the system-prompt approach.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
export const supportsMcp = false;
|
|
8
|
+
export async function available() {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const proc = spawn("gemini", ["--version"], { stdio: "ignore" });
|
|
11
|
+
proc.on("error", () => resolve(false));
|
|
12
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export async function stream(messages, systemPrompt, _mcpConfigFile, onToken) {
|
|
16
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
17
|
+
const prompt = `${systemPrompt}\n\nUser: ${lastUser?.content ?? "help"}`;
|
|
18
|
+
const args = ["--prompt", prompt, "--yolo"];
|
|
19
|
+
console.log("[gemini] spawning (no MCP support — using system prompt fallback)");
|
|
20
|
+
await new Promise((resolve, reject) => {
|
|
21
|
+
const proc = spawn("gemini", args, {
|
|
22
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
+
});
|
|
24
|
+
proc.stdout?.on("data", (chunk) => {
|
|
25
|
+
onToken(chunk.toString());
|
|
26
|
+
});
|
|
27
|
+
proc.stderr?.on("data", (chunk) => {
|
|
28
|
+
process.stderr.write(`[gemini stderr] ${chunk.toString()}`);
|
|
29
|
+
});
|
|
30
|
+
proc.on("error", (err) => {
|
|
31
|
+
console.error(`[gemini] spawn error: ${String(err)}`);
|
|
32
|
+
reject(err);
|
|
33
|
+
});
|
|
34
|
+
proc.on("close", (code) => {
|
|
35
|
+
console.log(`[gemini] exited with code ${code}`);
|
|
36
|
+
if (code === 0)
|
|
37
|
+
resolve();
|
|
38
|
+
else
|
|
39
|
+
reject(new Error(`gemini exited with code ${code}`));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// ── Apply ops ─────────────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Apply a list of operations to a copy of the given CompactDiagram.
|
|
4
|
+
* Returns the modified copy — the original is never mutated.
|
|
5
|
+
*/
|
|
6
|
+
export function applyOps(compact, ops) {
|
|
7
|
+
const result = JSON.parse(JSON.stringify(compact));
|
|
8
|
+
for (const op of ops) {
|
|
9
|
+
const proc = result.processes.find((p) => p.id === op.processId);
|
|
10
|
+
if (!proc)
|
|
11
|
+
continue;
|
|
12
|
+
if (op.op === "add") {
|
|
13
|
+
for (const elem of op.elements ?? []) {
|
|
14
|
+
if (!proc.elements.some((e) => e.id === elem.id)) {
|
|
15
|
+
proc.elements.push(elem);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const flow of op.flows ?? []) {
|
|
19
|
+
if (!proc.flows.some((f) => f.id === flow.id)) {
|
|
20
|
+
proc.flows.push(flow);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (op.op === "remove") {
|
|
25
|
+
const dropEls = new Set(op.elementIds ?? []);
|
|
26
|
+
const dropFlows = new Set(op.flowIds ?? []);
|
|
27
|
+
proc.elements = proc.elements.filter((e) => !dropEls.has(e.id));
|
|
28
|
+
proc.flows = proc.flows.filter((f) => !dropFlows.has(f.id) && !dropEls.has(f.from) && !dropEls.has(f.to));
|
|
29
|
+
}
|
|
30
|
+
else if (op.op === "update") {
|
|
31
|
+
const elem = proc.elements.find((e) => e.id === op.elementId);
|
|
32
|
+
if (elem) {
|
|
33
|
+
Object.assign(elem, op.changes);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (op.op === "condition") {
|
|
37
|
+
proc.flows = proc.flows.map((f) => {
|
|
38
|
+
if (f.id !== op.flowId)
|
|
39
|
+
return f;
|
|
40
|
+
if (op.condition === null) {
|
|
41
|
+
// Rebuild without condition field (noDelete rule)
|
|
42
|
+
return { id: f.id, from: f.from, to: f.to, ...(f.name ? { name: f.name } : {}) };
|
|
43
|
+
}
|
|
44
|
+
return { ...f, condition: op.condition };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract either an ops patch or a full CompactDiagram from the LLM response.
|
|
52
|
+
* Returns null if no valid JSON block is found or the format is unrecognised.
|
|
53
|
+
*/
|
|
54
|
+
export function parseResponse(text, current) {
|
|
55
|
+
const match = /```json\s*\n([\s\S]*?)\n```/.exec(text);
|
|
56
|
+
if (!match?.[1])
|
|
57
|
+
return null;
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(match[1]);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
66
|
+
return null;
|
|
67
|
+
// Ops format: { "ops": [...] }
|
|
68
|
+
if ("ops" in parsed && Array.isArray(parsed.ops)) {
|
|
69
|
+
if (!current)
|
|
70
|
+
return null; // ops require an existing diagram to patch
|
|
71
|
+
return applyOps(current, parsed.ops);
|
|
72
|
+
}
|
|
73
|
+
// Full CompactDiagram: { "processes": [...] }
|
|
74
|
+
if ("processes" in parsed && Array.isArray(parsed.processes)) {
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=apply-ops.js.map
|