@bpmnkit/cli 0.0.14 → 0.0.16
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 +3 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/plugin.js +244 -0
- package/dist/generated/commands.js +0 -15
- package/dist/plugin-loader.js +89 -0
- package/dist/run.js +11 -6
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -106,6 +106,9 @@ casen instances list --state active
|
|
|
106
106
|
| [`@bpmnkit/operate`](https://www.npmjs.com/package/@bpmnkit/operate) | Monitoring & operations frontend for Camunda clusters |
|
|
107
107
|
| [`@bpmnkit/connector-gen`](https://www.npmjs.com/package/@bpmnkit/connector-gen) | Generate connector templates from OpenAPI specs |
|
|
108
108
|
| [`@bpmnkit/proxy`](https://www.npmjs.com/package/@bpmnkit/proxy) | Local AI bridge and Camunda API proxy server |
|
|
109
|
+
| [`@bpmnkit/cli-sdk`](https://www.npmjs.com/package/@bpmnkit/cli-sdk) | Plugin authoring SDK for the casen CLI |
|
|
110
|
+
| [`@bpmnkit/create-casen-plugin`](https://www.npmjs.com/package/@bpmnkit/create-casen-plugin) | Scaffold a new casen CLI plugin in seconds |
|
|
111
|
+
| [`@bpmnkit/casen-report`](https://www.npmjs.com/package/@bpmnkit/casen-report) | HTML reports from Camunda 8 incident and SLA data |
|
|
109
112
|
|
|
110
113
|
## License
|
|
111
114
|
|
package/dist/commands/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { askGroup } from "./ask.js";
|
|
|
4
4
|
import { getDmnReqsXmlCmd, getDmnXmlCmd, getStartFormCmd, getUserTaskFormCmd, getXmlCmd, renderBpmnCmd, } from "./bpmn.js";
|
|
5
5
|
import { completionGroup } from "./completion.js";
|
|
6
6
|
import { connectorGroup } from "./connector.js";
|
|
7
|
+
import { pluginGroup } from "./plugin.js";
|
|
7
8
|
import { profileGroup } from "./profile.js";
|
|
8
9
|
import { computeRelations } from "./relations.js";
|
|
9
10
|
import { settingsGroup } from "./settings.js";
|
|
@@ -35,6 +36,7 @@ const customisedGroups = generatedCommandGroups.map((g) => {
|
|
|
35
36
|
});
|
|
36
37
|
const sortedOtherGroups = [
|
|
37
38
|
connectorGroup,
|
|
39
|
+
pluginGroup,
|
|
38
40
|
...customisedGroups,
|
|
39
41
|
...adminCommandGroups,
|
|
40
42
|
completionGroup,
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { PLUGINS_DIR, readInstalledPlugins, sanitiseName, } from "../plugin-loader.js";
|
|
6
|
+
// ── npm helper ────────────────────────────────────────────────────────────────
|
|
7
|
+
function runNpm(args, cwd) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
10
|
+
const child = spawn(npmCmd, ["install", ...args], { cwd, stdio: "inherit" });
|
|
11
|
+
child.on("close", (code) => {
|
|
12
|
+
if (code === 0)
|
|
13
|
+
resolve();
|
|
14
|
+
else
|
|
15
|
+
reject(new Error(`npm install exited with code ${String(code)}`));
|
|
16
|
+
});
|
|
17
|
+
child.on("error", reject);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async function searchNpmRegistry(query) {
|
|
21
|
+
const text = `keywords:casen-plugin${query ? ` ${query}` : ""}`;
|
|
22
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(text)}&size=50`;
|
|
23
|
+
const res = await fetch(url);
|
|
24
|
+
if (!res.ok)
|
|
25
|
+
throw new Error(`npm registry returned HTTP ${res.status}`);
|
|
26
|
+
const data = (await res.json());
|
|
27
|
+
return data.objects;
|
|
28
|
+
}
|
|
29
|
+
// ── plugin group ──────────────────────────────────────────────────────────────
|
|
30
|
+
export const pluginGroup = {
|
|
31
|
+
name: "plugin",
|
|
32
|
+
description: "Manage casen CLI plugins",
|
|
33
|
+
commands: [
|
|
34
|
+
// ── list ──────────────────────────────────────────────────────────────
|
|
35
|
+
{
|
|
36
|
+
name: "list",
|
|
37
|
+
aliases: ["ls"],
|
|
38
|
+
description: "List installed plugins",
|
|
39
|
+
async run(ctx) {
|
|
40
|
+
const plugins = await readInstalledPlugins();
|
|
41
|
+
if (plugins.length === 0) {
|
|
42
|
+
ctx.output.info('No plugins installed. Run "casen plugin search" to discover plugins.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
ctx.output.printList({
|
|
46
|
+
items: plugins.map((p) => ({
|
|
47
|
+
name: p.package,
|
|
48
|
+
version: p.version,
|
|
49
|
+
installed: p.installedAt.slice(0, 10),
|
|
50
|
+
})),
|
|
51
|
+
}, [
|
|
52
|
+
{ key: "name", header: "NAME" },
|
|
53
|
+
{ key: "version", header: "VERSION" },
|
|
54
|
+
{ key: "installed", header: "INSTALLED" },
|
|
55
|
+
]);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// ── install ───────────────────────────────────────────────────────────
|
|
59
|
+
{
|
|
60
|
+
name: "install",
|
|
61
|
+
aliases: ["add"],
|
|
62
|
+
description: "Install a plugin from the npm registry or a local path",
|
|
63
|
+
args: [{ name: "name", description: "npm package name or ./local-path", required: true }],
|
|
64
|
+
examples: [
|
|
65
|
+
{ description: "Install from npm", command: "casen plugin install casen-deploy" },
|
|
66
|
+
{
|
|
67
|
+
description: "Install from local path (dev mode)",
|
|
68
|
+
command: "casen plugin install ./my-plugin",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
async run(ctx) {
|
|
72
|
+
const nameArg = ctx.positional[0];
|
|
73
|
+
if (!nameArg)
|
|
74
|
+
throw new Error("Missing required argument: <name>");
|
|
75
|
+
// Detect local path vs npm package name
|
|
76
|
+
const isLocalPath = nameArg.startsWith(".") || nameArg.startsWith("/");
|
|
77
|
+
let pkgName;
|
|
78
|
+
if (isLocalPath) {
|
|
79
|
+
const localPath = join(homedir(), nameArg.startsWith("/") ? "" : ".", nameArg);
|
|
80
|
+
const localPkgText = await readFile(join(isLocalPath ? nameArg : localPath, "package.json"), "utf8");
|
|
81
|
+
const localPkg = JSON.parse(localPkgText);
|
|
82
|
+
if (!localPkg.name)
|
|
83
|
+
throw new Error(`No "name" field found in ${nameArg}/package.json`);
|
|
84
|
+
pkgName = localPkg.name;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
pkgName = nameArg;
|
|
88
|
+
}
|
|
89
|
+
const dirName = sanitiseName(pkgName);
|
|
90
|
+
const pluginDir = join(PLUGINS_DIR, dirName);
|
|
91
|
+
ctx.output.info(`Installing ${pkgName}…`);
|
|
92
|
+
await mkdir(pluginDir, { recursive: true });
|
|
93
|
+
// Initialise a minimal host package.json so npm has a valid workspace
|
|
94
|
+
const hostPkg = { name: "_casen-plugin-host", version: "0.0.0", private: true };
|
|
95
|
+
const hostPkgPath = join(pluginDir, "package.json");
|
|
96
|
+
try {
|
|
97
|
+
await readFile(hostPkgPath, "utf8");
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Only write if it doesn't already exist
|
|
101
|
+
await writeFile(hostPkgPath, `${JSON.stringify(hostPkg, null, 2)}\n`, "utf8");
|
|
102
|
+
}
|
|
103
|
+
await runNpm([nameArg], pluginDir);
|
|
104
|
+
// Read the installed version from the plugin's own package.json
|
|
105
|
+
const installedPkgText = await readFile(join(pluginDir, "node_modules", pkgName, "package.json"), "utf8");
|
|
106
|
+
const installedPkg = JSON.parse(installedPkgText);
|
|
107
|
+
const version = installedPkg.version ?? "unknown";
|
|
108
|
+
const meta = {
|
|
109
|
+
package: pkgName,
|
|
110
|
+
version,
|
|
111
|
+
installedAt: new Date().toISOString(),
|
|
112
|
+
};
|
|
113
|
+
await writeFile(join(pluginDir, ".meta.json"), `${JSON.stringify(meta, null, 2)}\n`, "utf8");
|
|
114
|
+
ctx.output.ok(`Installed ${pkgName}@${version}`);
|
|
115
|
+
ctx.output.info("Restart casen to activate the plugin.");
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
// ── remove ────────────────────────────────────────────────────────────
|
|
119
|
+
{
|
|
120
|
+
name: "remove",
|
|
121
|
+
aliases: ["uninstall", "rm"],
|
|
122
|
+
description: "Uninstall a plugin",
|
|
123
|
+
args: [{ name: "name", description: "Plugin package name", required: true }],
|
|
124
|
+
async run(ctx) {
|
|
125
|
+
const pkgName = ctx.positional[0];
|
|
126
|
+
if (!pkgName)
|
|
127
|
+
throw new Error("Missing required argument: <name>");
|
|
128
|
+
const pluginDir = join(PLUGINS_DIR, sanitiseName(pkgName));
|
|
129
|
+
try {
|
|
130
|
+
await readFile(join(pluginDir, ".meta.json"), "utf8");
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new Error(`Plugin "${pkgName}" is not installed.`);
|
|
134
|
+
}
|
|
135
|
+
await rm(pluginDir, { recursive: true, force: true });
|
|
136
|
+
ctx.output.ok(`Removed ${pkgName}`);
|
|
137
|
+
ctx.output.info("Restart casen for the change to take effect.");
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
// ── update ────────────────────────────────────────────────────────────
|
|
141
|
+
{
|
|
142
|
+
name: "update",
|
|
143
|
+
aliases: ["upgrade"],
|
|
144
|
+
description: "Update one or all plugins to their latest versions",
|
|
145
|
+
args: [
|
|
146
|
+
{
|
|
147
|
+
name: "name",
|
|
148
|
+
description: "Plugin to update (omit to update all installed plugins)",
|
|
149
|
+
required: false,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
async run(ctx) {
|
|
153
|
+
const target = ctx.positional[0];
|
|
154
|
+
const plugins = await readInstalledPlugins();
|
|
155
|
+
if (plugins.length === 0) {
|
|
156
|
+
ctx.output.info("No plugins installed.");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const toUpdate = target ? plugins.filter((p) => p.package === target) : plugins;
|
|
160
|
+
if (toUpdate.length === 0) {
|
|
161
|
+
throw new Error(`Plugin "${String(target)}" is not installed.`);
|
|
162
|
+
}
|
|
163
|
+
for (const plugin of toUpdate) {
|
|
164
|
+
ctx.output.info(`Updating ${plugin.package}…`);
|
|
165
|
+
await runNpm([`${plugin.package}@latest`], plugin.dir);
|
|
166
|
+
const pkgText = await readFile(join(plugin.dir, "node_modules", plugin.package, "package.json"), "utf8");
|
|
167
|
+
const pkg = JSON.parse(pkgText);
|
|
168
|
+
const newVersion = pkg.version ?? "unknown";
|
|
169
|
+
const meta = {
|
|
170
|
+
package: plugin.package,
|
|
171
|
+
version: newVersion,
|
|
172
|
+
installedAt: plugin.installedAt,
|
|
173
|
+
};
|
|
174
|
+
await writeFile(join(plugin.dir, ".meta.json"), `${JSON.stringify(meta, null, 2)}\n`, "utf8");
|
|
175
|
+
ctx.output.ok(`Updated ${plugin.package} → ${newVersion}`);
|
|
176
|
+
}
|
|
177
|
+
ctx.output.info("Restart casen for the changes to take effect.");
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
// ── info ──────────────────────────────────────────────────────────────
|
|
181
|
+
{
|
|
182
|
+
name: "info",
|
|
183
|
+
description: "Show details for an installed plugin",
|
|
184
|
+
args: [{ name: "name", description: "Plugin package name", required: true }],
|
|
185
|
+
async run(ctx) {
|
|
186
|
+
const pkgName = ctx.positional[0];
|
|
187
|
+
if (!pkgName)
|
|
188
|
+
throw new Error("Missing required argument: <name>");
|
|
189
|
+
const pluginDir = join(PLUGINS_DIR, sanitiseName(pkgName));
|
|
190
|
+
const metaText = await readFile(join(pluginDir, ".meta.json"), "utf8");
|
|
191
|
+
const meta = JSON.parse(metaText);
|
|
192
|
+
const pkgText = await readFile(join(pluginDir, "node_modules", pkgName, "package.json"), "utf8");
|
|
193
|
+
const pkg = JSON.parse(pkgText);
|
|
194
|
+
const repoUrl = typeof pkg.repository === "string" ? pkg.repository : (pkg.repository?.url ?? "");
|
|
195
|
+
const author = typeof pkg.author === "string" ? pkg.author : (pkg.author?.name ?? "");
|
|
196
|
+
ctx.output.printItem({
|
|
197
|
+
name: meta.package,
|
|
198
|
+
version: meta.version,
|
|
199
|
+
installedAt: meta.installedAt,
|
|
200
|
+
description: pkg.description ?? "",
|
|
201
|
+
homepage: pkg.homepage ?? "",
|
|
202
|
+
repository: repoUrl,
|
|
203
|
+
author,
|
|
204
|
+
directory: pluginDir,
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
// ── search ────────────────────────────────────────────────────────────
|
|
209
|
+
{
|
|
210
|
+
name: "search",
|
|
211
|
+
description: "Search the npm registry for casen plugins",
|
|
212
|
+
args: [{ name: "query", description: "Search terms (optional)", required: false }],
|
|
213
|
+
examples: [
|
|
214
|
+
{ description: "Browse all plugins", command: "casen plugin search" },
|
|
215
|
+
{ description: "Search for deploy-related plugins", command: "casen plugin search deploy" },
|
|
216
|
+
],
|
|
217
|
+
async run(ctx) {
|
|
218
|
+
const query = ctx.positional[0] ?? "";
|
|
219
|
+
ctx.output.info(`Searching npm for casen plugins${query ? ` matching "${query}"` : ""}…`);
|
|
220
|
+
const results = await searchNpmRegistry(query);
|
|
221
|
+
if (results.length === 0) {
|
|
222
|
+
ctx.output.info("No plugins found.");
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
ctx.output.printList({
|
|
226
|
+
items: results.map((r) => ({
|
|
227
|
+
name: r.package.name,
|
|
228
|
+
version: r.package.version,
|
|
229
|
+
description: r.package.description ?? "",
|
|
230
|
+
publisher: r.package.publisher?.username ?? "",
|
|
231
|
+
score: r.score.final.toFixed(2),
|
|
232
|
+
})),
|
|
233
|
+
}, [
|
|
234
|
+
{ key: "name", header: "NAME" },
|
|
235
|
+
{ key: "version", header: "VERSION" },
|
|
236
|
+
{ key: "description", header: "DESCRIPTION", maxWidth: 52 },
|
|
237
|
+
{ key: "publisher", header: "PUBLISHER" },
|
|
238
|
+
{ key: "score", header: "SCORE" },
|
|
239
|
+
]);
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -406,21 +406,6 @@ export const decisionInstanceGroup = {
|
|
|
406
406
|
argName: "decisionEvaluationInstanceKey",
|
|
407
407
|
get: (client, key) => client.decisionInstance.getDecisionInstance(key),
|
|
408
408
|
}),
|
|
409
|
-
{
|
|
410
|
-
name: "delete",
|
|
411
|
-
description: "Delete decision instance",
|
|
412
|
-
args: [{ name: "decisionInstanceKey", description: "decisionInstanceKey", required: true }],
|
|
413
|
-
flags: [DATA_OPT_FLAG],
|
|
414
|
-
async run(ctx) {
|
|
415
|
-
const decisionInstanceKey = ctx.positional[0];
|
|
416
|
-
if (!decisionInstanceKey)
|
|
417
|
-
throw new Error("Missing required argument: <decisionInstanceKey>");
|
|
418
|
-
const body = parseJson(ctx.flags.data, "data");
|
|
419
|
-
const client = await ctx.getClient();
|
|
420
|
-
await client.decisionInstance.deleteDecisionInstance(decisionInstanceKey, body);
|
|
421
|
-
ctx.output.ok("delete completed.");
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
409
|
makeCreateCmd({
|
|
425
410
|
name: "delete-batch-operation",
|
|
426
411
|
description: "Delete decision instances (batch)",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
// ── Paths ─────────────────────────────────────────────────────────────────────
|
|
6
|
+
export const PLUGINS_DIR = join(homedir(), ".casen", "plugins");
|
|
7
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* Converts an npm package name to a safe directory name.
|
|
10
|
+
* "@acme/casen-deploy" → "acme__casen-deploy"
|
|
11
|
+
*/
|
|
12
|
+
export function sanitiseName(pkg) {
|
|
13
|
+
return pkg.replace(/^@/, "").replace(/\//g, "__");
|
|
14
|
+
}
|
|
15
|
+
// ── Plugin loader ─────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Loads all installed plugins from `~/.casen/plugins/` and returns their
|
|
18
|
+
* combined command groups. Failures are isolated — a broken plugin logs a
|
|
19
|
+
* warning to stderr and is skipped; it cannot crash the CLI.
|
|
20
|
+
*/
|
|
21
|
+
export async function loadPlugins() {
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = await readdir(PLUGINS_DIR);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const groups = [];
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (entry.startsWith("."))
|
|
32
|
+
continue;
|
|
33
|
+
const pluginDir = join(PLUGINS_DIR, entry);
|
|
34
|
+
try {
|
|
35
|
+
// Read metadata to get the actual npm package name
|
|
36
|
+
const metaText = await readFile(join(pluginDir, ".meta.json"), "utf8");
|
|
37
|
+
const meta = JSON.parse(metaText);
|
|
38
|
+
const pkgName = meta.package;
|
|
39
|
+
// Resolve entry point via the plugin's own package.json
|
|
40
|
+
const pkgText = await readFile(join(pluginDir, "node_modules", pkgName, "package.json"), "utf8");
|
|
41
|
+
const pkg = JSON.parse(pkgText);
|
|
42
|
+
const main = pkg.main ?? "dist/index.js";
|
|
43
|
+
const entryPath = join(pluginDir, "node_modules", pkgName, main);
|
|
44
|
+
// Dynamic import — use file URL for cross-platform compatibility
|
|
45
|
+
const mod = (await import(pathToFileURL(entryPath).href));
|
|
46
|
+
const plugin = (mod.default ?? mod);
|
|
47
|
+
if (!Array.isArray(plugin.groups)) {
|
|
48
|
+
process.stderr.write(`[plugin] ${pkgName}: no groups exported, skipping\n`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
groups.push(...plugin.groups);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55
|
+
process.stderr.write(`[plugin] ${entry}: failed to load — ${msg}\n`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return groups;
|
|
59
|
+
}
|
|
60
|
+
// ── Management helpers ────────────────────────────────────────────────────────
|
|
61
|
+
/**
|
|
62
|
+
* Returns metadata for all installed plugins.
|
|
63
|
+
* Directories without a `.meta.json` are silently ignored.
|
|
64
|
+
*/
|
|
65
|
+
export async function readInstalledPlugins() {
|
|
66
|
+
let entries;
|
|
67
|
+
try {
|
|
68
|
+
entries = await readdir(PLUGINS_DIR);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const result = [];
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
if (entry.startsWith("."))
|
|
76
|
+
continue;
|
|
77
|
+
const dir = join(PLUGINS_DIR, entry);
|
|
78
|
+
try {
|
|
79
|
+
const metaText = await readFile(join(dir, ".meta.json"), "utf8");
|
|
80
|
+
const meta = JSON.parse(metaText);
|
|
81
|
+
result.push({ ...meta, dir });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Directory without .meta.json — not a managed plugin, skip
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=plugin-loader.js.map
|
package/dist/run.js
CHANGED
|
@@ -4,6 +4,7 @@ import { commandGroups } from "./commands/index.js";
|
|
|
4
4
|
import { getRuntimeCompletions } from "./completion.js";
|
|
5
5
|
import { printCommandHelp, printGlobalHelp, printGroupHelp, printVersion } from "./help.js";
|
|
6
6
|
import { createNullWriter, createOutputWriter, printRawResponse } from "./output.js";
|
|
7
|
+
import { loadPlugins } from "./plugin-loader.js";
|
|
7
8
|
import { runProfileManager } from "./profile-tui.js";
|
|
8
9
|
import { runSettingsManager } from "./settings-tui.js";
|
|
9
10
|
import { runAskTui, runGroupTui, runMainTui } from "./tui.js";
|
|
@@ -47,6 +48,10 @@ function printError(msg, colors) {
|
|
|
47
48
|
}
|
|
48
49
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
49
50
|
export async function run(argv) {
|
|
51
|
+
// Load plugin-contributed groups and merge with built-in groups.
|
|
52
|
+
// Failures are isolated inside loadPlugins — a broken plugin cannot crash the CLI.
|
|
53
|
+
const pluginGroups = await loadPlugins();
|
|
54
|
+
const allGroups = [...commandGroups, ...pluginGroups];
|
|
50
55
|
// ── Completion protocol ───────────────────────────────────────────────────
|
|
51
56
|
// casen --complete <cursorWordIndex> -- <words...>
|
|
52
57
|
const completeIdx = argv.indexOf("--complete");
|
|
@@ -54,7 +59,7 @@ export async function run(argv) {
|
|
|
54
59
|
const cursorIdx = Number(argv[completeIdx + 1] ?? "0");
|
|
55
60
|
const dashDash = argv.indexOf("--", completeIdx + 2);
|
|
56
61
|
const words = dashDash >= 0 ? argv.slice(dashDash + 1) : [];
|
|
57
|
-
const suggestions = getRuntimeCompletions(
|
|
62
|
+
const suggestions = getRuntimeCompletions(allGroups, cursorIdx, words);
|
|
58
63
|
process.stdout.write(`${suggestions.join("\n")}\n`);
|
|
59
64
|
return;
|
|
60
65
|
}
|
|
@@ -73,11 +78,11 @@ export async function run(argv) {
|
|
|
73
78
|
// ── Top-level: main menu TUI or help ─────────────────────────────────────
|
|
74
79
|
if (positional.length === 0) {
|
|
75
80
|
if (wantHelp) {
|
|
76
|
-
printGlobalHelp(
|
|
81
|
+
printGlobalHelp(allGroups, colors);
|
|
77
82
|
}
|
|
78
83
|
else {
|
|
79
84
|
const { name: pName, info: pInfo } = buildProfileInfo(profileName);
|
|
80
|
-
await runMainTui(
|
|
85
|
+
await runMainTui(allGroups, () => Promise.resolve(createClientFromProfile(profileName)), () => Promise.resolve(createAdminClientFromProfile(profileName)), { profile: pName, profileInfo: pInfo });
|
|
81
86
|
}
|
|
82
87
|
return;
|
|
83
88
|
}
|
|
@@ -85,7 +90,7 @@ export async function run(argv) {
|
|
|
85
90
|
const getAdminClient = () => Promise.resolve(createAdminClientFromProfile(profileName));
|
|
86
91
|
// ── Find group ────────────────────────────────────────────────────────────
|
|
87
92
|
const groupToken = positional[0] ?? "";
|
|
88
|
-
const group =
|
|
93
|
+
const group = allGroups.find((g) => g.name === groupToken || g.aliases?.includes(groupToken));
|
|
89
94
|
if (!group) {
|
|
90
95
|
printError(`Unknown resource: "${groupToken}". Run \`casen --help\` to see all resources.`, colors);
|
|
91
96
|
process.exitCode = 1;
|
|
@@ -111,7 +116,7 @@ export async function run(argv) {
|
|
|
111
116
|
if (positional.length === 1 && !wantHelp) {
|
|
112
117
|
if (group.name === "ask") {
|
|
113
118
|
const { name: pName, info: pInfo } = buildProfileInfo(profileName);
|
|
114
|
-
await runAskTui(
|
|
119
|
+
await runAskTui(allGroups, getClient, getAdminClient, {
|
|
115
120
|
profile: pName,
|
|
116
121
|
profileInfo: pInfo,
|
|
117
122
|
});
|
|
@@ -124,7 +129,7 @@ export async function run(argv) {
|
|
|
124
129
|
}
|
|
125
130
|
else if (group.name !== "completion") {
|
|
126
131
|
const { name: pName, info: pInfo } = buildProfileInfo(profileName);
|
|
127
|
-
await runGroupTui(group,
|
|
132
|
+
await runGroupTui(group, allGroups, getClient, getAdminClient, {
|
|
128
133
|
profile: pName,
|
|
129
134
|
profileInfo: pInfo,
|
|
130
135
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bpmnkit/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "Command-line interface for Camunda 8 — deploy, manage, and monitor processes from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"node": ">=20"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@bpmnkit/api": "0.0.
|
|
19
|
-
"@bpmnkit/ascii": "0.0.
|
|
20
|
-
"@bpmnkit/connector-gen": "0.0.
|
|
21
|
-
"@bpmnkit/profiles": "0.0.
|
|
18
|
+
"@bpmnkit/api": "0.0.13",
|
|
19
|
+
"@bpmnkit/ascii": "0.0.14",
|
|
20
|
+
"@bpmnkit/connector-gen": "0.0.8",
|
|
21
|
+
"@bpmnkit/profiles": "0.0.10"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|