@bpmnkit/cli 0.0.9

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.
@@ -0,0 +1,172 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ export const connectorGroup = {
4
+ name: "connector",
5
+ description: "Generate Camunda REST connector element templates from OpenAPI specs",
6
+ commands: [
7
+ {
8
+ name: "generate",
9
+ description: "Generate connector templates from a local OpenAPI spec or catalog entry",
10
+ flags: [
11
+ {
12
+ name: "swagger",
13
+ short: "s",
14
+ description: "Path to a local OpenAPI/Swagger file (YAML or JSON)",
15
+ type: "string",
16
+ },
17
+ {
18
+ name: "api",
19
+ description: "Catalog API id (e.g. github, stripe). Use 'connector catalog' to list.",
20
+ type: "string",
21
+ },
22
+ {
23
+ name: "output",
24
+ short: "o",
25
+ description: "Output directory (default: ./connector-templates)",
26
+ type: "string",
27
+ },
28
+ { name: "base-url", description: "Override the base URL from the spec", type: "string" },
29
+ {
30
+ name: "id-prefix",
31
+ description: "Reverse-DNS id prefix (default: io.generated)",
32
+ type: "string",
33
+ },
34
+ {
35
+ name: "filter",
36
+ description: "Regex filter on operationId/summary (case-insensitive)",
37
+ type: "string",
38
+ },
39
+ {
40
+ name: "expand-body",
41
+ description: "Decompose top-level request body properties into individual fields",
42
+ type: "boolean",
43
+ default: false,
44
+ },
45
+ {
46
+ name: "auth",
47
+ description: "Default auth type: noAuth, apiKey, basic, bearer, oauth-client-credentials-flow",
48
+ type: "string",
49
+ },
50
+ {
51
+ name: "format",
52
+ description: "Output format: one-per-op (default) or array (single file)",
53
+ type: "string",
54
+ },
55
+ {
56
+ name: "dry-run",
57
+ description: "Print templates to stdout without writing files",
58
+ type: "boolean",
59
+ default: false,
60
+ },
61
+ ],
62
+ examples: [
63
+ {
64
+ description: "Generate from a local file",
65
+ command: "casen connector generate --swagger ./petstore.yaml --output ./out",
66
+ },
67
+ {
68
+ description: "Generate from the GitHub catalog entry",
69
+ command: "casen connector generate --api github --output ./out --filter issues",
70
+ },
71
+ {
72
+ description: "Dry run (print to stdout)",
73
+ command: "casen connector generate --swagger ./spec.yaml --dry-run",
74
+ },
75
+ ],
76
+ async run(ctx) {
77
+ // Lazy import so the CLI doesn't load yaml on startup
78
+ const { generate, generateFromUrl, CATALOG, getCatalogEntry } = await import("@bpmnkit/connector-gen");
79
+ const swaggerPath = ctx.flags.swagger;
80
+ const apiId = ctx.flags.api;
81
+ const outputDir = ctx.flags.output ?? "./connector-templates";
82
+ const baseUrl = ctx.flags["base-url"];
83
+ const idPrefix = ctx.flags["id-prefix"] ?? "io.generated";
84
+ const filter = ctx.flags.filter;
85
+ const expandBody = ctx.flags["expand-body"] === true;
86
+ const authFlag = ctx.flags.auth;
87
+ const format = ctx.flags.format ?? "one-per-op";
88
+ const dryRun = ctx.flags["dry-run"] === true;
89
+ const validAuth = [
90
+ "noAuth",
91
+ "apiKey",
92
+ "basic",
93
+ "bearer",
94
+ "oauth-client-credentials-flow",
95
+ ];
96
+ if (authFlag && !validAuth.includes(authFlag)) {
97
+ throw new Error(`Unknown --auth value "${authFlag}". Valid: ${validAuth.join(", ")}`);
98
+ }
99
+ const defaultAuthType = authFlag;
100
+ if (!swaggerPath && !apiId) {
101
+ throw new Error("Provide either --swagger <file> or --api <id>. Use 'casen connector catalog' to list catalog entries.");
102
+ }
103
+ if (swaggerPath && apiId) {
104
+ throw new Error("Use either --swagger or --api, not both.");
105
+ }
106
+ const opts = { idPrefix, baseUrl, expandBody, filter, defaultAuthType };
107
+ if (swaggerPath) {
108
+ const absPath = resolve(swaggerPath);
109
+ const text = await readFile(absPath, "utf8");
110
+ const templates = generate(text, opts);
111
+ if (dryRun) {
112
+ ctx.output.print(templates);
113
+ ctx.output.info(`Would generate ${templates.length} template(s)`);
114
+ return;
115
+ }
116
+ const { writeTemplates } = await import("@bpmnkit/connector-gen");
117
+ const files = await writeTemplates(templates, { outputDir: resolve(outputDir), format });
118
+ ctx.output.ok(`Generated ${files.length} template(s) → ${resolve(outputDir)}`);
119
+ for (const f of files)
120
+ ctx.output.info(` ${f}`);
121
+ return;
122
+ }
123
+ // API catalog path
124
+ if (!apiId)
125
+ return;
126
+ const entry = getCatalogEntry(apiId);
127
+ if (!entry) {
128
+ const ids = CATALOG.map((e) => e.id).join(", ");
129
+ throw new Error(`Unknown catalog entry "${apiId}". Available: ${ids}`);
130
+ }
131
+ ctx.output.info(`Downloading spec for "${entry.name}" …`);
132
+ const { generateFromUrl: _gfu } = { generateFromUrl };
133
+ const { templates, files } = await generateFromUrl(entry.url, {
134
+ ...opts,
135
+ idPrefix: opts.idPrefix === "io.generated" ? entry.idPrefix : opts.idPrefix,
136
+ defaultAuthType: opts.defaultAuthType ?? entry.defaultAuth,
137
+ ...(dryRun ? {} : { outputDir: resolve(outputDir), format }),
138
+ });
139
+ if (dryRun) {
140
+ ctx.output.print(templates);
141
+ ctx.output.info(`Would generate ${templates.length} template(s)`);
142
+ return;
143
+ }
144
+ ctx.output.ok(`Generated ${files.length} template(s) → ${resolve(outputDir)}`);
145
+ for (const f of files)
146
+ ctx.output.info(` ${f}`);
147
+ },
148
+ },
149
+ {
150
+ name: "catalog",
151
+ description: "List available API catalog entries",
152
+ examples: [{ description: "Show all catalog entries", command: "casen connector catalog" }],
153
+ async run(ctx) {
154
+ const { CATALOG } = await import("@bpmnkit/connector-gen");
155
+ ctx.output.printList({
156
+ items: CATALOG.map((e) => ({
157
+ id: e.id,
158
+ name: e.name,
159
+ auth: e.defaultAuth,
160
+ description: e.description,
161
+ })),
162
+ }, [
163
+ { key: "id", header: "ID", maxWidth: 16 },
164
+ { key: "name", header: "NAME", maxWidth: 30 },
165
+ { key: "auth", header: "AUTH", maxWidth: 32 },
166
+ { key: "description", header: "DESCRIPTION", maxWidth: 60 },
167
+ ]);
168
+ },
169
+ },
170
+ ],
171
+ };
172
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1,47 @@
1
+ import { adminCommandGroups } from "../generated/admin-commands.js";
2
+ import { decisionDefinitionGroup, decisionRequirementsGroup, generatedCommandGroups, jobGroup, processDefinitionGroup, userTaskGroup, } from "../generated/commands.js";
3
+ import { getDmnReqsXmlCmd, getDmnXmlCmd, getStartFormCmd, getUserTaskFormCmd, getXmlCmd, renderBpmnCmd, } from "./bpmn.js";
4
+ import { completionGroup } from "./completion.js";
5
+ import { connectorGroup } from "./connector.js";
6
+ import { profileGroup } from "./profile.js";
7
+ import { computeRelations } from "./relations.js";
8
+ import { settingsGroup } from "./settings.js";
9
+ import { workerCmd } from "./worker.js";
10
+ // Inject custom commands into generated groups without modifying generated files.
11
+ // Also remove the broken generated get-x-m-l commands (return text/xml, not JSON)
12
+ // and replace getstart-form / get-form with ASCII-rendering variants.
13
+ const customisedGroups = generatedCommandGroups.map((g) => {
14
+ if (g === processDefinitionGroup) {
15
+ const commands = g.commands.filter((c) => c.name !== "get-x-m-l" && c.name !== "getstart-form");
16
+ return { ...g, commands: [...commands, getXmlCmd, renderBpmnCmd, getStartFormCmd] };
17
+ }
18
+ if (g === decisionDefinitionGroup) {
19
+ const commands = g.commands.filter((c) => c.name !== "get-x-m-l");
20
+ return { ...g, commands: [...commands, getDmnXmlCmd] };
21
+ }
22
+ if (g === decisionRequirementsGroup) {
23
+ const commands = g.commands.filter((c) => c.name !== "get-x-m-l");
24
+ return { ...g, commands: [...commands, getDmnReqsXmlCmd] };
25
+ }
26
+ if (g === userTaskGroup) {
27
+ const commands = g.commands.filter((c) => c.name !== "get-form");
28
+ return { ...g, commands: [...commands, getUserTaskFormCmd] };
29
+ }
30
+ if (g === jobGroup) {
31
+ return { ...g, commands: [...g.commands, workerCmd] };
32
+ }
33
+ return g;
34
+ });
35
+ const allGroups = [
36
+ profileGroup,
37
+ settingsGroup,
38
+ connectorGroup,
39
+ ...customisedGroups,
40
+ ...adminCommandGroups,
41
+ completionGroup,
42
+ ];
43
+ // Sort alphabetically by name for the main menu
44
+ export const commandGroups = allGroups.sort((a, b) => a.name.localeCompare(b.name));
45
+ // Compute follow-up relations between commands based on shared field/arg names
46
+ computeRelations(commandGroups);
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,325 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { deleteProfile, getActiveName, getActiveProfile, getConfigFilePath, getProfile, listProfiles, saveProfile, useProfile, } from "@bpmnkit/profiles";
3
+ const API_TYPE_FLAG = {
4
+ name: "api-type",
5
+ description: "API type: c8 (default) or admin",
6
+ type: "string",
7
+ default: "c8",
8
+ placeholder: "TYPE",
9
+ };
10
+ const AUTH_FLAGS = [
11
+ {
12
+ name: "base-url",
13
+ description: "API base URL",
14
+ type: "string",
15
+ required: true,
16
+ placeholder: "URL",
17
+ },
18
+ {
19
+ name: "auth-type",
20
+ description: "Authentication type: bearer|oauth2|basic|none",
21
+ type: "string",
22
+ required: true,
23
+ placeholder: "TYPE",
24
+ },
25
+ {
26
+ name: "token",
27
+ description: "Bearer token (auth-type=bearer)",
28
+ type: "string",
29
+ placeholder: "TOKEN",
30
+ },
31
+ { name: "client-id", description: "OAuth2 client ID", type: "string", placeholder: "ID" },
32
+ {
33
+ name: "client-secret",
34
+ description: "OAuth2 client secret",
35
+ type: "string",
36
+ placeholder: "SECRET",
37
+ },
38
+ {
39
+ name: "token-url",
40
+ description: "OAuth2 token endpoint URL",
41
+ type: "string",
42
+ placeholder: "URL",
43
+ },
44
+ {
45
+ name: "audience",
46
+ description: "OAuth2 audience (auth-type=oauth2, default: zeebe.camunda.io)",
47
+ type: "string",
48
+ placeholder: "AUDIENCE",
49
+ },
50
+ { name: "username", description: "Basic auth username", type: "string", placeholder: "USER" },
51
+ { name: "password", description: "Basic auth password", type: "string", placeholder: "PASS" },
52
+ ];
53
+ // ─── Camunda Cloud credentials file parser ────────────────────────────────────
54
+ /** Parse a shell file of `export KEY='VALUE'` lines into a key-value map. */
55
+ function parseEnvFile(content) {
56
+ const result = {};
57
+ for (const line of content.split("\n")) {
58
+ const trimmed = line.trim();
59
+ if (!trimmed || trimmed.startsWith("#"))
60
+ continue;
61
+ const match = trimmed.match(/^(?:export\s+)?([A-Z_][A-Z0-9_]*)=(.*)/);
62
+ if (!match)
63
+ continue;
64
+ const key = match[1] ?? "";
65
+ let value = (match[2] ?? "").trim();
66
+ if ((value.startsWith("'") && value.endsWith("'")) ||
67
+ (value.startsWith('"') && value.endsWith('"'))) {
68
+ value = value.slice(1, -1);
69
+ }
70
+ result[key] = value;
71
+ }
72
+ return result;
73
+ }
74
+ /** Detect the API type from a parsed env map. Returns "admin" if Console vars present, else "c8". */
75
+ function detectApiType(env) {
76
+ return env.CAMUNDA_CONSOLE_CLIENT_ID || env.CAMUNDA_CONSOLE_BASE_URL ? "admin" : "c8";
77
+ }
78
+ /** Build a CamundaClientInput from a Camunda Console (Admin API) env map. */
79
+ function configFromConsoleEnv(env) {
80
+ const baseUrl = env.CAMUNDA_CONSOLE_BASE_URL;
81
+ if (!baseUrl) {
82
+ throw new Error("CAMUNDA_CONSOLE_BASE_URL not found in credentials file. " +
83
+ "Make sure you are using a Camunda Console credentials export.");
84
+ }
85
+ const clientId = env.CAMUNDA_CONSOLE_CLIENT_ID ?? "";
86
+ const clientSecret = env.CAMUNDA_CONSOLE_CLIENT_SECRET ?? "";
87
+ const tokenUrl = env.CAMUNDA_OAUTH_URL ?? "";
88
+ const audience = env.CAMUNDA_CONSOLE_OAUTH_AUDIENCE ?? "";
89
+ if (!clientId || !clientSecret || !tokenUrl) {
90
+ throw new Error("Missing required credentials. Expected: " +
91
+ "CAMUNDA_CONSOLE_CLIENT_ID, CAMUNDA_CONSOLE_CLIENT_SECRET, CAMUNDA_OAUTH_URL.");
92
+ }
93
+ return { baseUrl, auth: { type: "oauth2", clientId, clientSecret, tokenUrl, audience } };
94
+ }
95
+ /** Build a CamundaClientInput from a parsed Camunda Cloud credentials env map. */
96
+ function configFromCloudEnv(env) {
97
+ const restAddress = env.ZEEBE_REST_ADDRESS;
98
+ if (!restAddress) {
99
+ throw new Error("ZEEBE_REST_ADDRESS not found in credentials file. " +
100
+ "Make sure you are using a Camunda Cloud credentials export.");
101
+ }
102
+ const baseUrl = restAddress.endsWith("/v2") ? restAddress : `${restAddress}/v2`;
103
+ const clientId = env.CAMUNDA_CLIENT_ID ?? env.ZEEBE_CLIENT_ID ?? "";
104
+ const clientSecret = env.CAMUNDA_CLIENT_SECRET ?? env.ZEEBE_CLIENT_SECRET ?? "";
105
+ const tokenUrl = env.CAMUNDA_OAUTH_URL ?? env.ZEEBE_AUTHORIZATION_SERVER_URL ?? "";
106
+ const audience = env.CAMUNDA_TOKEN_AUDIENCE ?? "zeebe.camunda.io";
107
+ if (!clientId || !clientSecret || !tokenUrl) {
108
+ throw new Error("Missing required credentials. Expected: " +
109
+ "CAMUNDA_CLIENT_ID (or ZEEBE_CLIENT_ID), " +
110
+ "CAMUNDA_CLIENT_SECRET (or ZEEBE_CLIENT_SECRET), " +
111
+ "CAMUNDA_OAUTH_URL (or ZEEBE_AUTHORIZATION_SERVER_URL).");
112
+ }
113
+ return { baseUrl, auth: { type: "oauth2", clientId, clientSecret, tokenUrl, audience } };
114
+ }
115
+ /** Read all of stdin as a string. */
116
+ function readStdin() {
117
+ return new Promise((resolve, reject) => {
118
+ let data = "";
119
+ process.stdin.setEncoding("utf8");
120
+ process.stdin.on("data", (chunk) => {
121
+ data += chunk;
122
+ });
123
+ process.stdin.on("end", () => resolve(data));
124
+ process.stdin.on("error", reject);
125
+ });
126
+ }
127
+ // ─── Auth builder ─────────────────────────────────────────────────────────────
128
+ function buildAuth(flags) {
129
+ const type = flags["auth-type"];
130
+ switch (type) {
131
+ case "bearer": {
132
+ const token = flags.token;
133
+ if (!token)
134
+ throw new Error("--token is required for --auth-type bearer");
135
+ return { type: "bearer", token };
136
+ }
137
+ case "oauth2": {
138
+ const clientId = flags["client-id"];
139
+ const clientSecret = flags["client-secret"];
140
+ const tokenUrl = flags["token-url"];
141
+ if (!clientId || !clientSecret || !tokenUrl) {
142
+ throw new Error("--client-id, --client-secret, and --token-url are required for --auth-type oauth2");
143
+ }
144
+ const audience = flags.audience ?? "zeebe.camunda.io";
145
+ return { type: "oauth2", clientId, clientSecret, tokenUrl, audience };
146
+ }
147
+ case "basic": {
148
+ const username = flags.username;
149
+ const password = flags.password;
150
+ if (!username || !password)
151
+ throw new Error("--username and --password are required for --auth-type basic");
152
+ return { type: "basic", username, password };
153
+ }
154
+ case "none":
155
+ return { type: "none" };
156
+ default:
157
+ throw new Error(`Unknown --auth-type "${type}". Valid: bearer|oauth2|basic|none`);
158
+ }
159
+ }
160
+ export const profileGroup = {
161
+ name: "profile",
162
+ description: "Manage connection profiles",
163
+ commands: [
164
+ {
165
+ name: "create",
166
+ description: "Create or update a profile",
167
+ args: [{ name: "name", description: "Profile name", required: true }],
168
+ flags: [API_TYPE_FLAG, ...AUTH_FLAGS],
169
+ examples: [
170
+ {
171
+ description: "Bearer token profile",
172
+ command: "casen profile create local --base-url http://localhost:8080/v2 --auth-type bearer --token my-token",
173
+ },
174
+ {
175
+ description: "OAuth2 profile for Camunda SaaS",
176
+ command: "casen profile create prod --base-url https://cluster.camunda.io/v2 --auth-type oauth2 --client-id id --client-secret secret --token-url https://login.cloud.camunda.io/oauth/token",
177
+ },
178
+ {
179
+ description: "Admin API profile",
180
+ command: "casen profile create admin-prod --api-type admin --base-url https://api.cloud.camunda.io --auth-type oauth2 --client-id id --client-secret secret --token-url https://login.cloud.camunda.io/oauth/token",
181
+ },
182
+ ],
183
+ async run(ctx) {
184
+ const name = ctx.positional[0];
185
+ if (!name)
186
+ throw new Error("Missing required argument: <name>");
187
+ const baseUrl = ctx.flags["base-url"];
188
+ if (!baseUrl)
189
+ throw new Error("--base-url is required");
190
+ const auth = buildAuth(ctx.flags);
191
+ const rawApiType = ctx.flags["api-type"] ?? "c8";
192
+ const apiType = rawApiType === "admin" ? "admin" : "c8";
193
+ const config = { baseUrl, auth };
194
+ saveProfile(name, config, apiType);
195
+ ctx.output.ok(`Profile "${name}" saved [${apiType}] (${getConfigFilePath()})`);
196
+ },
197
+ },
198
+ {
199
+ name: "list",
200
+ aliases: ["ls"],
201
+ description: "List all profiles",
202
+ examples: [{ description: "List profiles", command: "casen profile list" }],
203
+ async run(ctx) {
204
+ const profiles = listProfiles();
205
+ const active = getActiveName();
206
+ if (profiles.length === 0) {
207
+ ctx.output.info("No profiles. Create one with: casen profile create <name> ...");
208
+ return;
209
+ }
210
+ ctx.output.printList({
211
+ items: profiles.map((p) => ({
212
+ active: p.name === active ? "●" : " ",
213
+ name: p.name,
214
+ apiType: p.apiType,
215
+ baseUrl: p.config.baseUrl ?? "(from env/file)",
216
+ authType: p.config.auth?.type ?? "—",
217
+ })),
218
+ }, [
219
+ { key: "active", header: " " },
220
+ { key: "name", header: "NAME" },
221
+ { key: "apiType", header: "API" },
222
+ { key: "baseUrl", header: "BASE URL", maxWidth: 50 },
223
+ { key: "authType", header: "AUTH TYPE" },
224
+ ]);
225
+ },
226
+ },
227
+ {
228
+ name: "use",
229
+ description: "Switch the active profile",
230
+ args: [{ name: "name", description: "Profile name", required: true }],
231
+ examples: [
232
+ { description: "Activate production profile", command: "casen profile use production" },
233
+ ],
234
+ async run(ctx) {
235
+ const name = ctx.positional[0];
236
+ if (!name)
237
+ throw new Error("Missing required argument: <name>");
238
+ if (!useProfile(name)) {
239
+ throw new Error(`Profile "${name}" not found. Run \`casen profile list\` to see available profiles.`);
240
+ }
241
+ ctx.output.ok(`Now using profile "${name}"`);
242
+ },
243
+ },
244
+ {
245
+ name: "show",
246
+ description: "Show profile details",
247
+ args: [{ name: "name", description: "Profile name (defaults to active)", required: false }],
248
+ examples: [
249
+ { description: "Show active profile", command: "casen profile show" },
250
+ { description: "Show specific profile", command: "casen profile show production" },
251
+ ],
252
+ async run(ctx) {
253
+ const name = ctx.positional[0];
254
+ const profile = name ? getProfile(name) : getActiveProfile();
255
+ if (!profile) {
256
+ throw new Error(name
257
+ ? `Profile "${name}" not found.`
258
+ : "No active profile. Create one with: casen profile create <name> ...");
259
+ }
260
+ const active = getActiveName();
261
+ const isActive = profile.name === active;
262
+ ctx.output.info(`Profile: ${profile.name}${isActive ? " (active)" : ""}`);
263
+ ctx.output.printItem(profile.config);
264
+ },
265
+ },
266
+ {
267
+ name: "import",
268
+ description: "Import a profile from a Camunda Cloud or Console credentials file (auto-detected)",
269
+ args: [
270
+ { name: "name", description: "Profile name", required: true },
271
+ {
272
+ name: "file",
273
+ description: "Path to credentials file (use - for stdin)",
274
+ required: true,
275
+ },
276
+ ],
277
+ examples: [
278
+ {
279
+ description: "Import C8 credentials",
280
+ command: "casen profile import prod ./camunda-credentials.sh",
281
+ },
282
+ {
283
+ description: "Import Admin API credentials",
284
+ command: "casen profile import admin ./console-credentials.sh",
285
+ },
286
+ {
287
+ description: "Import from stdin",
288
+ command: "cat credentials.sh | casen profile import prod -",
289
+ },
290
+ ],
291
+ async run(ctx) {
292
+ const name = ctx.positional[0];
293
+ if (!name)
294
+ throw new Error("Missing required argument: <name>");
295
+ const filePath = ctx.positional[1];
296
+ if (!filePath)
297
+ throw new Error("Missing required argument: <file>");
298
+ const content = filePath === "-" ? await readStdin() : readFileSync(filePath, "utf8");
299
+ const env = parseEnvFile(content);
300
+ const apiType = detectApiType(env);
301
+ const config = apiType === "admin" ? configFromConsoleEnv(env) : configFromCloudEnv(env);
302
+ saveProfile(name, config, apiType);
303
+ ctx.output.ok(`Profile "${name}" imported [${apiType}] (${getConfigFilePath()})`);
304
+ ctx.output.info(`baseUrl: ${config.baseUrl ?? ""}`);
305
+ },
306
+ },
307
+ {
308
+ name: "delete",
309
+ aliases: ["rm"],
310
+ description: "Delete a profile",
311
+ args: [{ name: "name", description: "Profile name", required: true }],
312
+ examples: [{ description: "Delete a profile", command: "casen profile delete old-profile" }],
313
+ async run(ctx) {
314
+ const name = ctx.positional[0];
315
+ if (!name)
316
+ throw new Error("Missing required argument: <name>");
317
+ if (!deleteProfile(name)) {
318
+ throw new Error(`Profile "${name}" not found.`);
319
+ }
320
+ ctx.output.ok(`Deleted profile "${name}"`);
321
+ },
322
+ },
323
+ ],
324
+ };
325
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1,28 @@
1
+ import { buildRelations } from "@bpmnkit/api";
2
+ /**
3
+ * Auto-detect follow-up relations between commands using the shared
4
+ * buildRelations() from @bpmnkit/api. Converts CommandGroups to generic
5
+ * RelationSources, computes the graph, and wires results back to commands.
6
+ * Mutates commands in-place.
7
+ */
8
+ export function computeRelations(allGroups) {
9
+ // Convert to generic RelationSources
10
+ const sources = allGroups.flatMap((group) => group.commands.map((cmd) => ({
11
+ groupName: group.name,
12
+ commandName: cmd.name,
13
+ description: cmd.description,
14
+ outputFields: cmd.columns?.map((c) => c.key) ?? [],
15
+ inputParams: cmd.args?.map((a) => a.name) ?? [],
16
+ })));
17
+ const relationsMap = buildRelations(sources);
18
+ // Apply results back to commands
19
+ for (const group of allGroups) {
20
+ for (const cmd of group.commands) {
21
+ const relations = relationsMap.get(`${group.name}/${cmd.name}`);
22
+ if (relations) {
23
+ cmd.relations = relations;
24
+ }
25
+ }
26
+ }
27
+ }
28
+ //# sourceMappingURL=relations.js.map