@bpmnkit/cli 0.0.27 → 0.0.29
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/commands/generate.js +679 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/skills.js +32 -11
- package/dist/commands/view.js +373 -0
- package/package.json +4 -4
- package/skills/aikit.md +267 -0
- package/skills/deploy.md +2 -0
- package/skills/design.md +2 -0
- package/skills/implement.md +2 -0
- package/skills/review.md +2 -0
- package/skills/test.md +2 -0
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { Bpmn, compactify, expand } from "@bpmnkit/core";
|
|
3
|
+
// ── JSON schema reference ─────────────────────────────────────────────────────
|
|
4
|
+
const SCHEMA_HELP = `CompactDiagram JSON schema — for --definition and stdin input
|
|
5
|
+
==============================================================
|
|
6
|
+
|
|
7
|
+
TOP-LEVEL
|
|
8
|
+
{ "id": "Definitions_<unique>", "processes": [CompactProcess] }
|
|
9
|
+
|
|
10
|
+
CompactProcess
|
|
11
|
+
{ "id": "proc-id", "name"?: "Human Name",
|
|
12
|
+
"elements": [CompactElement], "flows": [CompactFlow] }
|
|
13
|
+
|
|
14
|
+
CompactElement — all fields except id+type are optional
|
|
15
|
+
{
|
|
16
|
+
"id": unique element ID,
|
|
17
|
+
"type": BpmnElementType (see below),
|
|
18
|
+
"name": display label,
|
|
19
|
+
|
|
20
|
+
serviceTask/sendTask:
|
|
21
|
+
"jobType": Zeebe job type string (e.g. "my-worker"),
|
|
22
|
+
"taskHeaders": { key: value } — Zeebe task headers,
|
|
23
|
+
"resultVariable": variable to store the response in,
|
|
24
|
+
|
|
25
|
+
callActivity:
|
|
26
|
+
"calledProcess": process ID of the called process,
|
|
27
|
+
|
|
28
|
+
userTask:
|
|
29
|
+
"formId": Camunda form ID,
|
|
30
|
+
|
|
31
|
+
businessRuleTask:
|
|
32
|
+
"decisionId": DMN decision ID,
|
|
33
|
+
"resultVariable": variable to store decision result,
|
|
34
|
+
|
|
35
|
+
events (startEvent, endEvent, boundaryEvent, intermediateCatchEvent, etc.):
|
|
36
|
+
"eventType": timer | message | signal | error | escalation
|
|
37
|
+
| terminate | cancel | conditional | link | compensate,
|
|
38
|
+
|
|
39
|
+
boundaryEvent:
|
|
40
|
+
"attachedTo": ID of the host task,
|
|
41
|
+
"interrupting": false for non-interrupting (default: true),
|
|
42
|
+
|
|
43
|
+
subProcess/eventSubProcess/transaction:
|
|
44
|
+
"children": { "elements": [...], "flows": [...] }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
CompactFlow
|
|
48
|
+
{
|
|
49
|
+
"id": unique flow ID,
|
|
50
|
+
"from": source element ID,
|
|
51
|
+
"to": target element ID,
|
|
52
|
+
"name"?: flow label,
|
|
53
|
+
"condition"?: FEEL condition expression (e.g. "= status = \\"approved\\"")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ELEMENT TYPES
|
|
57
|
+
Tasks: serviceTask userTask scriptTask businessRuleTask
|
|
58
|
+
callActivity sendTask receiveTask manualTask task
|
|
59
|
+
Events: startEvent endEvent intermediateCatchEvent intermediateThrowEvent
|
|
60
|
+
boundaryEvent
|
|
61
|
+
Gateways: exclusiveGateway parallelGateway inclusiveGateway
|
|
62
|
+
eventBasedGateway complexGateway
|
|
63
|
+
Containers: subProcess adHocSubProcess eventSubProcess transaction
|
|
64
|
+
|
|
65
|
+
HTTP CONNECTOR (serviceTask)
|
|
66
|
+
{ "type": "serviceTask", "jobType": "io.camunda:http-json:1",
|
|
67
|
+
"taskHeaders": { "url": "https://...", "method": "POST" },
|
|
68
|
+
"resultVariable": "response" }
|
|
69
|
+
|
|
70
|
+
PATCH FORMAT (for --patch and stdin when --input is set)
|
|
71
|
+
Adds elements and flows to an existing process. IDs must not collide.
|
|
72
|
+
{
|
|
73
|
+
"elements": [CompactElement, ...], // new elements to add
|
|
74
|
+
"flows": [CompactFlow, ...] // new flows to add (can reference existing IDs)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Example — add a rejection path to an existing gateway named "gw":
|
|
78
|
+
{
|
|
79
|
+
"elements": [
|
|
80
|
+
{ "id": "rejected", "type": "serviceTask", "name": "Notify Rejection", "jobType": "notify" },
|
|
81
|
+
{ "id": "end-reject","type": "endEvent", "name": "Rejected" }
|
|
82
|
+
],
|
|
83
|
+
"flows": [
|
|
84
|
+
{ "id": "f-rej1", "from": "gw", "to": "rejected", "condition": "= not approved", "name": "No" },
|
|
85
|
+
{ "id": "f-rej2", "from": "rejected", "to": "end-reject" }
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
FULL EXAMPLE — order approval
|
|
90
|
+
{
|
|
91
|
+
"id": "Definitions_order",
|
|
92
|
+
"processes": [{
|
|
93
|
+
"id": "order-approval",
|
|
94
|
+
"name": "Order Approval",
|
|
95
|
+
"elements": [
|
|
96
|
+
{ "id": "start", "type": "startEvent", "name": "Order Received" },
|
|
97
|
+
{ "id": "validate", "type": "serviceTask", "name": "Validate", "jobType": "validate-order" },
|
|
98
|
+
{ "id": "gw", "type": "exclusiveGateway","name": "Valid?" },
|
|
99
|
+
{ "id": "review", "type": "userTask", "name": "Manual Review", "formId": "review-form" },
|
|
100
|
+
{ "id": "process", "type": "serviceTask", "name": "Process Order", "jobType": "process-order" },
|
|
101
|
+
{ "id": "end-ok", "type": "endEvent", "name": "Processed" },
|
|
102
|
+
{ "id": "end-rej", "type": "endEvent", "name": "Rejected" },
|
|
103
|
+
{ "id": "err", "type": "boundaryEvent", "attachedTo": "validate", "eventType": "error" },
|
|
104
|
+
{ "id": "end-err", "type": "endEvent", "name": "Validation Error", "eventType": "error" }
|
|
105
|
+
],
|
|
106
|
+
"flows": [
|
|
107
|
+
{ "id": "f1", "from": "start", "to": "validate" },
|
|
108
|
+
{ "id": "f2", "from": "validate", "to": "gw" },
|
|
109
|
+
{ "id": "f3", "from": "gw", "to": "review", "condition": "= not valid", "name": "Invalid" },
|
|
110
|
+
{ "id": "f4", "from": "gw", "to": "process", "condition": "= valid", "name": "Valid" },
|
|
111
|
+
{ "id": "f5", "from": "review", "to": "end-rej", "name": "Rejected" },
|
|
112
|
+
{ "id": "f6", "from": "review", "to": "process", "name": "Approved" },
|
|
113
|
+
{ "id": "f7", "from": "process", "to": "end-ok" },
|
|
114
|
+
{ "id": "f8", "from": "err", "to": "end-err" }
|
|
115
|
+
]
|
|
116
|
+
}]
|
|
117
|
+
}`;
|
|
118
|
+
const TEMPLATES = {
|
|
119
|
+
empty: (id, name) => ({
|
|
120
|
+
id: `Definitions_${id}`,
|
|
121
|
+
processes: [
|
|
122
|
+
{
|
|
123
|
+
id,
|
|
124
|
+
name: name ?? "New Process",
|
|
125
|
+
elements: [{ id: "start", type: "startEvent", name: "Start" }],
|
|
126
|
+
flows: [],
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
minimal: (id, name) => ({
|
|
131
|
+
id: `Definitions_${id}`,
|
|
132
|
+
processes: [
|
|
133
|
+
{
|
|
134
|
+
id,
|
|
135
|
+
name: name ?? "New Process",
|
|
136
|
+
elements: [
|
|
137
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
138
|
+
{
|
|
139
|
+
id: "task1",
|
|
140
|
+
type: "serviceTask",
|
|
141
|
+
name: name ? `${name} Task` : "Process Request",
|
|
142
|
+
jobType: "my-worker",
|
|
143
|
+
},
|
|
144
|
+
{ id: "end", type: "endEvent", name: "End" },
|
|
145
|
+
],
|
|
146
|
+
flows: [
|
|
147
|
+
{ id: "f1", from: "start", to: "task1" },
|
|
148
|
+
{ id: "f2", from: "task1", to: "end" },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
}),
|
|
153
|
+
"user-task": (id, name) => ({
|
|
154
|
+
id: `Definitions_${id}`,
|
|
155
|
+
processes: [
|
|
156
|
+
{
|
|
157
|
+
id,
|
|
158
|
+
name: name ?? "Human Task Process",
|
|
159
|
+
elements: [
|
|
160
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
161
|
+
{ id: "ut1", type: "userTask", name: name ?? "Review", formId: "my-form" },
|
|
162
|
+
{ id: "end", type: "endEvent", name: "Done" },
|
|
163
|
+
],
|
|
164
|
+
flows: [
|
|
165
|
+
{ id: "f1", from: "start", to: "ut1" },
|
|
166
|
+
{ id: "f2", from: "ut1", to: "end" },
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
}),
|
|
171
|
+
"call-activity": (id, name) => ({
|
|
172
|
+
id: `Definitions_${id}`,
|
|
173
|
+
processes: [
|
|
174
|
+
{
|
|
175
|
+
id,
|
|
176
|
+
name: name ?? "Orchestration Process",
|
|
177
|
+
elements: [
|
|
178
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
179
|
+
{
|
|
180
|
+
id: "ca1",
|
|
181
|
+
type: "callActivity",
|
|
182
|
+
name: name ?? "Run Sub-Process",
|
|
183
|
+
calledProcess: "child-process",
|
|
184
|
+
},
|
|
185
|
+
{ id: "end", type: "endEvent", name: "Done" },
|
|
186
|
+
],
|
|
187
|
+
flows: [
|
|
188
|
+
{ id: "f1", from: "start", to: "ca1" },
|
|
189
|
+
{ id: "f2", from: "ca1", to: "end" },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
}),
|
|
194
|
+
"business-rule": (id, name) => ({
|
|
195
|
+
id: `Definitions_${id}`,
|
|
196
|
+
processes: [
|
|
197
|
+
{
|
|
198
|
+
id,
|
|
199
|
+
name: name ?? "Decision Process",
|
|
200
|
+
elements: [
|
|
201
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
202
|
+
{
|
|
203
|
+
id: "brt1",
|
|
204
|
+
type: "businessRuleTask",
|
|
205
|
+
name: name ?? "Evaluate Decision",
|
|
206
|
+
decisionId: "my-decision",
|
|
207
|
+
resultVariable: "decisionResult",
|
|
208
|
+
},
|
|
209
|
+
{ id: "end", type: "endEvent", name: "Done" },
|
|
210
|
+
],
|
|
211
|
+
flows: [
|
|
212
|
+
{ id: "f1", from: "start", to: "brt1" },
|
|
213
|
+
{ id: "f2", from: "brt1", to: "end" },
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
}),
|
|
218
|
+
approval: (id, name) => ({
|
|
219
|
+
id: `Definitions_${id}`,
|
|
220
|
+
processes: [
|
|
221
|
+
{
|
|
222
|
+
id,
|
|
223
|
+
name: name ?? "Approval Process",
|
|
224
|
+
elements: [
|
|
225
|
+
{ id: "start", type: "startEvent", name: "Request Received" },
|
|
226
|
+
{ id: "review", type: "userTask", name: "Review Request", formId: "review-form" },
|
|
227
|
+
{ id: "gw", type: "exclusiveGateway", name: "Approved?" },
|
|
228
|
+
{
|
|
229
|
+
id: "process",
|
|
230
|
+
type: "serviceTask",
|
|
231
|
+
name: "Process Approval",
|
|
232
|
+
jobType: "process-approval",
|
|
233
|
+
},
|
|
234
|
+
{ id: "end-ok", type: "endEvent", name: "Approved" },
|
|
235
|
+
{ id: "end-rej", type: "endEvent", name: "Rejected" },
|
|
236
|
+
],
|
|
237
|
+
flows: [
|
|
238
|
+
{ id: "f1", from: "start", to: "review" },
|
|
239
|
+
{ id: "f2", from: "review", to: "gw" },
|
|
240
|
+
{ id: "f3", from: "gw", to: "process", condition: "= approved", name: "Yes" },
|
|
241
|
+
{ id: "f4", from: "gw", to: "end-rej", condition: "= not approved", name: "No" },
|
|
242
|
+
{ id: "f5", from: "process", to: "end-ok" },
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
}),
|
|
247
|
+
parallel: (id, name) => ({
|
|
248
|
+
id: `Definitions_${id}`,
|
|
249
|
+
processes: [
|
|
250
|
+
{
|
|
251
|
+
id,
|
|
252
|
+
name: name ?? "Parallel Process",
|
|
253
|
+
elements: [
|
|
254
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
255
|
+
{ id: "fork", type: "parallelGateway", name: "Fork" },
|
|
256
|
+
{ id: "task1", type: "serviceTask", name: "Task A", jobType: "task-a" },
|
|
257
|
+
{ id: "task2", type: "serviceTask", name: "Task B", jobType: "task-b" },
|
|
258
|
+
{ id: "join", type: "parallelGateway", name: "Join" },
|
|
259
|
+
{ id: "end", type: "endEvent", name: "End" },
|
|
260
|
+
],
|
|
261
|
+
flows: [
|
|
262
|
+
{ id: "f1", from: "start", to: "fork" },
|
|
263
|
+
{ id: "f2", from: "fork", to: "task1" },
|
|
264
|
+
{ id: "f3", from: "fork", to: "task2" },
|
|
265
|
+
{ id: "f4", from: "task1", to: "join" },
|
|
266
|
+
{ id: "f5", from: "task2", to: "join" },
|
|
267
|
+
{ id: "f6", from: "join", to: "end" },
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
}),
|
|
272
|
+
inclusive: (id, name) => ({
|
|
273
|
+
id: `Definitions_${id}`,
|
|
274
|
+
processes: [
|
|
275
|
+
{
|
|
276
|
+
id,
|
|
277
|
+
name: name ?? "Inclusive Gateway Process",
|
|
278
|
+
elements: [
|
|
279
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
280
|
+
{ id: "split", type: "inclusiveGateway", name: "Which?" },
|
|
281
|
+
{ id: "task1", type: "serviceTask", name: "Option A", jobType: "option-a" },
|
|
282
|
+
{ id: "task2", type: "serviceTask", name: "Option B", jobType: "option-b" },
|
|
283
|
+
{ id: "merge", type: "inclusiveGateway", name: "Merge" },
|
|
284
|
+
{ id: "end", type: "endEvent", name: "End" },
|
|
285
|
+
],
|
|
286
|
+
flows: [
|
|
287
|
+
{ id: "f1", from: "start", to: "split" },
|
|
288
|
+
{ id: "f2", from: "split", to: "task1", condition: "= needsA", name: "A" },
|
|
289
|
+
{ id: "f3", from: "split", to: "task2", condition: "= needsB", name: "B" },
|
|
290
|
+
{ id: "f4", from: "task1", to: "merge" },
|
|
291
|
+
{ id: "f5", from: "task2", to: "merge" },
|
|
292
|
+
{ id: "f6", from: "merge", to: "end" },
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
}),
|
|
297
|
+
"timer-start": (id, name) => ({
|
|
298
|
+
id: `Definitions_${id}`,
|
|
299
|
+
processes: [
|
|
300
|
+
{
|
|
301
|
+
id,
|
|
302
|
+
name: name ?? "Scheduled Process",
|
|
303
|
+
elements: [
|
|
304
|
+
{ id: "start", type: "startEvent", name: "Timer", eventType: "timer" },
|
|
305
|
+
{
|
|
306
|
+
id: "task1",
|
|
307
|
+
type: "serviceTask",
|
|
308
|
+
name: name ?? "Scheduled Job",
|
|
309
|
+
jobType: "scheduled-worker",
|
|
310
|
+
},
|
|
311
|
+
{ id: "end", type: "endEvent", name: "Done" },
|
|
312
|
+
],
|
|
313
|
+
flows: [
|
|
314
|
+
{ id: "f1", from: "start", to: "task1" },
|
|
315
|
+
{ id: "f2", from: "task1", to: "end" },
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
}),
|
|
320
|
+
"message-start": (id, name) => ({
|
|
321
|
+
id: `Definitions_${id}`,
|
|
322
|
+
processes: [
|
|
323
|
+
{
|
|
324
|
+
id,
|
|
325
|
+
name: name ?? "Message-Triggered Process",
|
|
326
|
+
elements: [
|
|
327
|
+
{ id: "start", type: "startEvent", name: "Message Received", eventType: "message" },
|
|
328
|
+
{
|
|
329
|
+
id: "task1",
|
|
330
|
+
type: "serviceTask",
|
|
331
|
+
name: name ?? "Handle Message",
|
|
332
|
+
jobType: "message-handler",
|
|
333
|
+
},
|
|
334
|
+
{ id: "end", type: "endEvent", name: "Done" },
|
|
335
|
+
],
|
|
336
|
+
flows: [
|
|
337
|
+
{ id: "f1", from: "start", to: "task1" },
|
|
338
|
+
{ id: "f2", from: "task1", to: "end" },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
}),
|
|
343
|
+
"error-boundary": (id, name) => ({
|
|
344
|
+
id: `Definitions_${id}`,
|
|
345
|
+
processes: [
|
|
346
|
+
{
|
|
347
|
+
id,
|
|
348
|
+
name: name ?? "Error Handling Process",
|
|
349
|
+
elements: [
|
|
350
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
351
|
+
{ id: "task1", type: "serviceTask", name: name ?? "Risky Task", jobType: "risky-worker" },
|
|
352
|
+
{
|
|
353
|
+
id: "err",
|
|
354
|
+
type: "boundaryEvent",
|
|
355
|
+
attachedTo: "task1",
|
|
356
|
+
eventType: "error",
|
|
357
|
+
name: "Error",
|
|
358
|
+
},
|
|
359
|
+
{ id: "end-ok", type: "endEvent", name: "Success" },
|
|
360
|
+
{ id: "end-err", type: "endEvent", name: "Failed", eventType: "error" },
|
|
361
|
+
],
|
|
362
|
+
flows: [
|
|
363
|
+
{ id: "f1", from: "start", to: "task1" },
|
|
364
|
+
{ id: "f2", from: "task1", to: "end-ok" },
|
|
365
|
+
{ id: "f3", from: "err", to: "end-err" },
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
}),
|
|
370
|
+
subprocess: (id, name) => ({
|
|
371
|
+
id: `Definitions_${id}`,
|
|
372
|
+
processes: [
|
|
373
|
+
{
|
|
374
|
+
id,
|
|
375
|
+
name: name ?? "Sub-Process Demo",
|
|
376
|
+
elements: [
|
|
377
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
378
|
+
{
|
|
379
|
+
id: "sp1",
|
|
380
|
+
type: "subProcess",
|
|
381
|
+
name: name ?? "Sub-Process",
|
|
382
|
+
children: {
|
|
383
|
+
elements: [
|
|
384
|
+
{ id: "sp_start", type: "startEvent", name: "Begin" },
|
|
385
|
+
{ id: "sp_task", type: "serviceTask", name: "Inner Task", jobType: "inner-worker" },
|
|
386
|
+
{ id: "sp_end", type: "endEvent", name: "Finish" },
|
|
387
|
+
],
|
|
388
|
+
flows: [
|
|
389
|
+
{ id: "sf1", from: "sp_start", to: "sp_task" },
|
|
390
|
+
{ id: "sf2", from: "sp_task", to: "sp_end" },
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
{ id: "end", type: "endEvent", name: "End" },
|
|
395
|
+
],
|
|
396
|
+
flows: [
|
|
397
|
+
{ id: "f1", from: "start", to: "sp1" },
|
|
398
|
+
{ id: "f2", from: "sp1", to: "end" },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
}),
|
|
403
|
+
"event-subprocess": (id, name) => ({
|
|
404
|
+
id: `Definitions_${id}`,
|
|
405
|
+
processes: [
|
|
406
|
+
{
|
|
407
|
+
id,
|
|
408
|
+
name: name ?? "Process with Error Handler",
|
|
409
|
+
elements: [
|
|
410
|
+
{ id: "start", type: "startEvent", name: "Start" },
|
|
411
|
+
{ id: "task1", type: "serviceTask", name: name ?? "Main Task", jobType: "main-worker" },
|
|
412
|
+
{ id: "end", type: "endEvent", name: "End" },
|
|
413
|
+
{
|
|
414
|
+
id: "evtsp",
|
|
415
|
+
type: "eventSubProcess",
|
|
416
|
+
name: "Error Handler",
|
|
417
|
+
children: {
|
|
418
|
+
elements: [
|
|
419
|
+
{
|
|
420
|
+
id: "evtsp_start",
|
|
421
|
+
type: "startEvent",
|
|
422
|
+
name: "Error",
|
|
423
|
+
eventType: "error",
|
|
424
|
+
interrupting: false,
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
id: "evtsp_task",
|
|
428
|
+
type: "serviceTask",
|
|
429
|
+
name: "Compensate",
|
|
430
|
+
jobType: "compensate-worker",
|
|
431
|
+
},
|
|
432
|
+
{ id: "evtsp_end", type: "endEvent", name: "Handled" },
|
|
433
|
+
],
|
|
434
|
+
flows: [
|
|
435
|
+
{ id: "sf1", from: "evtsp_start", to: "evtsp_task" },
|
|
436
|
+
{ id: "sf2", from: "evtsp_task", to: "evtsp_end" },
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
flows: [
|
|
442
|
+
{ id: "f1", from: "start", to: "task1" },
|
|
443
|
+
{ id: "f2", from: "task1", to: "end" },
|
|
444
|
+
],
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
}),
|
|
448
|
+
};
|
|
449
|
+
const TEMPLATE_NAMES = Object.keys(TEMPLATES).sort();
|
|
450
|
+
// ── Stdin reader ──────────────────────────────────────────────────────────────
|
|
451
|
+
async function readStdin() {
|
|
452
|
+
const chunks = [];
|
|
453
|
+
for await (const chunk of process.stdin) {
|
|
454
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
455
|
+
}
|
|
456
|
+
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
457
|
+
}
|
|
458
|
+
// ── Command ───────────────────────────────────────────────────────────────────
|
|
459
|
+
const generateBpmnCmd = {
|
|
460
|
+
name: "bpmn",
|
|
461
|
+
description: "Generate a BPMN file from parameters, a template, or a CompactDiagram JSON definition",
|
|
462
|
+
args: [],
|
|
463
|
+
flags: [
|
|
464
|
+
{
|
|
465
|
+
name: "process-id",
|
|
466
|
+
short: "i",
|
|
467
|
+
description: "Process ID (used in templates)",
|
|
468
|
+
type: "string",
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "name",
|
|
472
|
+
short: "n",
|
|
473
|
+
description: "Process display name (used in templates)",
|
|
474
|
+
type: "string",
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "output",
|
|
478
|
+
short: "o",
|
|
479
|
+
description: "Output file path. Default: <process-id>.bpmn. Use - for stdout.",
|
|
480
|
+
type: "string",
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: "template",
|
|
484
|
+
description: `Template name. Options: ${TEMPLATE_NAMES.join(", ")}`,
|
|
485
|
+
type: "string",
|
|
486
|
+
default: "minimal",
|
|
487
|
+
enum: TEMPLATE_NAMES,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "definition",
|
|
491
|
+
short: "d",
|
|
492
|
+
description: "CompactDiagram JSON string (overrides --template). See --help-schema.",
|
|
493
|
+
type: "string",
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: "help-schema",
|
|
497
|
+
description: "Print the CompactDiagram JSON schema and exit",
|
|
498
|
+
type: "boolean",
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
name: "input",
|
|
502
|
+
short: "f",
|
|
503
|
+
description: "Existing .bpmn file to load and modify",
|
|
504
|
+
type: "string",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "patch",
|
|
508
|
+
description: 'JSON patch to apply to --input: {"elements":[...],"flows":[...]}. See --help-schema.',
|
|
509
|
+
type: "string",
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: "dump-compact",
|
|
513
|
+
description: "Print the CompactDiagram JSON of --input and exit (for AI inspection of existing files)",
|
|
514
|
+
type: "boolean",
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
examples: [
|
|
518
|
+
{
|
|
519
|
+
description: "Minimal service-task process",
|
|
520
|
+
command: "casen generate bpmn --process-id order --name 'Order Processing'",
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
description: "Approval workflow template",
|
|
524
|
+
command: "casen generate bpmn --template approval --process-id approve",
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
description: "Timer-triggered scheduled job",
|
|
528
|
+
command: "casen generate bpmn --template timer-start --process-id nightly-sync",
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
description: "Parallel branch pattern",
|
|
532
|
+
command: "casen generate bpmn --template parallel --process-id enrich --name Enrichment",
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
description: "Error boundary with error end event",
|
|
536
|
+
command: "casen generate bpmn --template error-boundary --process-id resilient",
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
description: "Full custom definition (AI/scripting path)",
|
|
540
|
+
command: 'casen generate bpmn --definition \'{"id":"Defs","processes":[{"id":"p","elements":[...],"flows":[...]}]}\'',
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
description: "Pipe CompactDiagram JSON from AI output",
|
|
544
|
+
command: "echo '{...}' | casen generate bpmn --output my-process.bpmn",
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
description: "Print full JSON schema reference",
|
|
548
|
+
command: "casen generate bpmn --help-schema",
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
description: "Inspect an existing file as compact JSON (AI planning step)",
|
|
552
|
+
command: "casen generate bpmn --input existing.bpmn --dump-compact",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
description: "Add a new gateway path to an existing file",
|
|
556
|
+
command: 'casen generate bpmn --input order.bpmn --patch \'{"elements":[{"id":"notify","type":"serviceTask","name":"Notify","jobType":"notify-worker"},{"id":"end2","type":"endEvent","name":"Notified"}],"flows":[{"id":"fn1","from":"gw","to":"notify","condition":"= urgent"},{"id":"fn2","from":"notify","to":"end2"}]}\'',
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
description: "Pipe a patch from AI output",
|
|
560
|
+
command: 'echo \'{"elements":[...],"flows":[...]}\' | casen generate bpmn --input order.bpmn',
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
description: "Re-apply auto-layout to an existing file",
|
|
564
|
+
command: "casen generate bpmn --input messy.bpmn --output clean.bpmn",
|
|
565
|
+
},
|
|
566
|
+
],
|
|
567
|
+
async run(ctx) {
|
|
568
|
+
// --help-schema: print schema and exit
|
|
569
|
+
if (ctx.flags["help-schema"]) {
|
|
570
|
+
process.stdout.write(`${SCHEMA_HELP}\n`);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const processId = ctx.flags["process-id"]?.trim() || "process";
|
|
574
|
+
const name = ctx.flags.name?.trim();
|
|
575
|
+
const outputFlag = ctx.flags.output;
|
|
576
|
+
const defFlag = ctx.flags.definition;
|
|
577
|
+
const inputFile = ctx.flags.input;
|
|
578
|
+
const patchFlag = ctx.flags.patch;
|
|
579
|
+
// ── Modify-existing mode (--input) ─────────────────────────────────────
|
|
580
|
+
if (inputFile) {
|
|
581
|
+
const xml = await readFile(inputFile, "utf-8");
|
|
582
|
+
const defs = Bpmn.parse(xml);
|
|
583
|
+
const compact = compactify(defs);
|
|
584
|
+
// --dump-compact: print JSON for AI inspection and exit
|
|
585
|
+
if (ctx.flags["dump-compact"]) {
|
|
586
|
+
process.stdout.write(`${JSON.stringify(compact, null, 2)}\n`);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
// Resolve patch from --patch flag or stdin
|
|
590
|
+
let patch = null;
|
|
591
|
+
if (patchFlag) {
|
|
592
|
+
try {
|
|
593
|
+
patch = JSON.parse(patchFlag);
|
|
594
|
+
}
|
|
595
|
+
catch {
|
|
596
|
+
throw new Error("--patch is not valid JSON. Run --help-schema to see the format.");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else if (!process.stdin.isTTY) {
|
|
600
|
+
const raw = await readStdin();
|
|
601
|
+
if (raw) {
|
|
602
|
+
try {
|
|
603
|
+
patch = JSON.parse(raw);
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
throw new Error("stdin is not valid patch JSON. Expected {elements:[...],flows:[...]}. Run --help-schema.");
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Apply patch to first process (covers all single-process cases)
|
|
611
|
+
if (patch) {
|
|
612
|
+
const proc = compact.processes[0];
|
|
613
|
+
if (!proc)
|
|
614
|
+
throw new Error("Input BPMN has no processes");
|
|
615
|
+
if (patch.elements?.length)
|
|
616
|
+
proc.elements.push(...patch.elements);
|
|
617
|
+
if (patch.flows?.length)
|
|
618
|
+
proc.flows.push(...patch.flows);
|
|
619
|
+
}
|
|
620
|
+
const patched = Bpmn.export(expand(compact));
|
|
621
|
+
if (outputFlag === "-") {
|
|
622
|
+
process.stdout.write(patched);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const outputPath = typeof outputFlag === "string" && outputFlag.length > 0 ? outputFlag : inputFile;
|
|
626
|
+
await writeFile(outputPath, patched, "utf-8");
|
|
627
|
+
ctx.output.ok(patch ? `Patched and written to ${outputPath}` : `Re-laid-out and written to ${outputPath}`);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
// ── Generate mode (template / definition / stdin) ──────────────────────
|
|
631
|
+
let compact = null;
|
|
632
|
+
if (defFlag) {
|
|
633
|
+
try {
|
|
634
|
+
compact = JSON.parse(defFlag);
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
throw new Error("--definition is not valid JSON. Run --help-schema to see the format.");
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
else if (!process.stdin.isTTY) {
|
|
641
|
+
const raw = await readStdin();
|
|
642
|
+
if (raw) {
|
|
643
|
+
try {
|
|
644
|
+
compact = JSON.parse(raw);
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
throw new Error("stdin is not valid CompactDiagram JSON. Run --help-schema to see the format.");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
let xml;
|
|
652
|
+
if (compact) {
|
|
653
|
+
xml = Bpmn.export(expand(compact));
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
const templateName = ctx.flags.template ?? "minimal";
|
|
657
|
+
const templateFn = TEMPLATES[templateName];
|
|
658
|
+
if (!templateFn) {
|
|
659
|
+
throw new Error(`Unknown template "${templateName}". Available: ${TEMPLATE_NAMES.join(", ")}`);
|
|
660
|
+
}
|
|
661
|
+
xml = Bpmn.export(expand(templateFn(processId, name)));
|
|
662
|
+
}
|
|
663
|
+
if (outputFlag === "-") {
|
|
664
|
+
process.stdout.write(xml);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const effectiveId = compact?.processes[0]?.id ?? ctx.flags["process-id"] ?? "process";
|
|
668
|
+
const outputPath = typeof outputFlag === "string" && outputFlag.length > 0 ? outputFlag : `${effectiveId}.bpmn`;
|
|
669
|
+
await writeFile(outputPath, xml, "utf-8");
|
|
670
|
+
ctx.output.ok(`BPMN written to ${outputPath}`);
|
|
671
|
+
},
|
|
672
|
+
};
|
|
673
|
+
export const generateGroup = {
|
|
674
|
+
name: "generate",
|
|
675
|
+
aliases: ["gen"],
|
|
676
|
+
description: "Generate BPMN, DMN, and form files from parameters",
|
|
677
|
+
commands: [generateBpmnCmd],
|
|
678
|
+
};
|
|
679
|
+
//# sourceMappingURL=generate.js.map
|