@camunda8/cli 2.7.0-alpha.1 → 2.7.0-alpha.10

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.
Files changed (144) hide show
  1. package/README.md +80 -1
  2. package/dist/command-dispatch.d.ts +16 -0
  3. package/dist/command-dispatch.d.ts.map +1 -0
  4. package/dist/command-dispatch.js +129 -0
  5. package/dist/command-dispatch.js.map +1 -0
  6. package/dist/command-framework.d.ts +248 -0
  7. package/dist/command-framework.d.ts.map +1 -0
  8. package/dist/command-framework.js +229 -0
  9. package/dist/command-framework.js.map +1 -0
  10. package/dist/command-registry.d.ts +2014 -14
  11. package/dist/command-registry.d.ts.map +1 -1
  12. package/dist/command-registry.js +653 -66
  13. package/dist/command-registry.js.map +1 -1
  14. package/dist/command-validation.d.ts +30 -4
  15. package/dist/command-validation.d.ts.map +1 -1
  16. package/dist/command-validation.js +90 -4
  17. package/dist/command-validation.js.map +1 -1
  18. package/dist/commands/completion.d.ts +25 -1
  19. package/dist/commands/completion.d.ts.map +1 -1
  20. package/dist/commands/completion.js +590 -1223
  21. package/dist/commands/completion.js.map +1 -1
  22. package/dist/commands/deployments.d.ts +2 -0
  23. package/dist/commands/deployments.d.ts.map +1 -1
  24. package/dist/commands/deployments.js +13 -2
  25. package/dist/commands/deployments.js.map +1 -1
  26. package/dist/commands/forms.d.ts +2 -20
  27. package/dist/commands/forms.d.ts.map +1 -1
  28. package/dist/commands/forms.js +76 -79
  29. package/dist/commands/forms.js.map +1 -1
  30. package/dist/commands/help.d.ts +4 -88
  31. package/dist/commands/help.d.ts.map +1 -1
  32. package/dist/commands/help.js +574 -1948
  33. package/dist/commands/help.js.map +1 -1
  34. package/dist/commands/identity-authorizations.d.ts +5 -24
  35. package/dist/commands/identity-authorizations.d.ts.map +1 -1
  36. package/dist/commands/identity-authorizations.js +113 -137
  37. package/dist/commands/identity-authorizations.js.map +1 -1
  38. package/dist/commands/identity-groups.d.ts +5 -26
  39. package/dist/commands/identity-groups.d.ts.map +1 -1
  40. package/dist/commands/identity-groups.js +91 -124
  41. package/dist/commands/identity-groups.js.map +1 -1
  42. package/dist/commands/identity-mapping-rules.d.ts +5 -30
  43. package/dist/commands/identity-mapping-rules.d.ts.map +1 -1
  44. package/dist/commands/identity-mapping-rules.js +106 -136
  45. package/dist/commands/identity-mapping-rules.js.map +1 -1
  46. package/dist/commands/identity-roles.d.ts +5 -26
  47. package/dist/commands/identity-roles.d.ts.map +1 -1
  48. package/dist/commands/identity-roles.js +91 -124
  49. package/dist/commands/identity-roles.js.map +1 -1
  50. package/dist/commands/identity-tenants.d.ts +5 -26
  51. package/dist/commands/identity-tenants.d.ts.map +1 -1
  52. package/dist/commands/identity-tenants.js +92 -126
  53. package/dist/commands/identity-tenants.js.map +1 -1
  54. package/dist/commands/identity-users.d.ts +5 -29
  55. package/dist/commands/identity-users.d.ts.map +1 -1
  56. package/dist/commands/identity-users.js +95 -129
  57. package/dist/commands/identity-users.js.map +1 -1
  58. package/dist/commands/identity.d.ts +6 -6
  59. package/dist/commands/identity.d.ts.map +1 -1
  60. package/dist/commands/identity.js +6 -7
  61. package/dist/commands/identity.js.map +1 -1
  62. package/dist/commands/incidents.d.ts +3 -16
  63. package/dist/commands/incidents.d.ts.map +1 -1
  64. package/dist/commands/incidents.js +71 -98
  65. package/dist/commands/incidents.js.map +1 -1
  66. package/dist/commands/jobs.d.ts +4 -26
  67. package/dist/commands/jobs.d.ts.map +1 -1
  68. package/dist/commands/jobs.js +143 -159
  69. package/dist/commands/jobs.js.map +1 -1
  70. package/dist/commands/mcp-proxy.d.ts +1 -0
  71. package/dist/commands/mcp-proxy.d.ts.map +1 -1
  72. package/dist/commands/mcp-proxy.js +10 -2
  73. package/dist/commands/mcp-proxy.js.map +1 -1
  74. package/dist/commands/messages.d.ts +2 -12
  75. package/dist/commands/messages.d.ts.map +1 -1
  76. package/dist/commands/messages.js +87 -81
  77. package/dist/commands/messages.js.map +1 -1
  78. package/dist/commands/open.d.ts +4 -0
  79. package/dist/commands/open.d.ts.map +1 -1
  80. package/dist/commands/open.js +18 -0
  81. package/dist/commands/open.js.map +1 -1
  82. package/dist/commands/plugins.d.ts +7 -9
  83. package/dist/commands/plugins.d.ts.map +1 -1
  84. package/dist/commands/plugins.js +32 -25
  85. package/dist/commands/plugins.js.map +1 -1
  86. package/dist/commands/process-definitions.d.ts +2 -14
  87. package/dist/commands/process-definitions.d.ts.map +1 -1
  88. package/dist/commands/process-definitions.js +57 -80
  89. package/dist/commands/process-definitions.js.map +1 -1
  90. package/dist/commands/process-instances.d.ts +8 -37
  91. package/dist/commands/process-instances.d.ts.map +1 -1
  92. package/dist/commands/process-instances.js +242 -193
  93. package/dist/commands/process-instances.js.map +1 -1
  94. package/dist/commands/profiles.d.ts +4 -0
  95. package/dist/commands/profiles.d.ts.map +1 -1
  96. package/dist/commands/profiles.js +29 -0
  97. package/dist/commands/profiles.js.map +1 -1
  98. package/dist/commands/run.d.ts +2 -0
  99. package/dist/commands/run.d.ts.map +1 -1
  100. package/dist/commands/run.js +10 -0
  101. package/dist/commands/run.js.map +1 -1
  102. package/dist/commands/search.d.ts +7 -100
  103. package/dist/commands/search.d.ts.map +1 -1
  104. package/dist/commands/search.js +530 -694
  105. package/dist/commands/search.js.map +1 -1
  106. package/dist/commands/session.d.ts +3 -0
  107. package/dist/commands/session.d.ts.map +1 -1
  108. package/dist/commands/session.js +30 -0
  109. package/dist/commands/session.js.map +1 -1
  110. package/dist/commands/topology.d.ts +1 -3
  111. package/dist/commands/topology.d.ts.map +1 -1
  112. package/dist/commands/topology.js +11 -18
  113. package/dist/commands/topology.js.map +1 -1
  114. package/dist/commands/user-tasks.d.ts +2 -16
  115. package/dist/commands/user-tasks.d.ts.map +1 -1
  116. package/dist/commands/user-tasks.js +73 -101
  117. package/dist/commands/user-tasks.js.map +1 -1
  118. package/dist/commands/watch.d.ts +1 -0
  119. package/dist/commands/watch.d.ts.map +1 -1
  120. package/dist/commands/watch.js +15 -0
  121. package/dist/commands/watch.js.map +1 -1
  122. package/dist/config.d.ts.map +1 -1
  123. package/dist/config.js +5 -0
  124. package/dist/config.js.map +1 -1
  125. package/dist/default-plugins/cluster/README.md +1 -1
  126. package/dist/default-plugins/cluster/c8ctl-plugin.js +281 -59
  127. package/dist/index.d.ts +0 -4
  128. package/dist/index.d.ts.map +1 -1
  129. package/dist/index.js +102 -979
  130. package/dist/index.js.map +1 -1
  131. package/dist/logger.d.ts +5 -0
  132. package/dist/logger.d.ts.map +1 -1
  133. package/dist/logger.js +7 -1
  134. package/dist/logger.js.map +1 -1
  135. package/dist/plugin-loader.d.ts +5 -0
  136. package/dist/plugin-loader.d.ts.map +1 -1
  137. package/dist/plugin-loader.js +1 -0
  138. package/dist/plugin-loader.js.map +1 -1
  139. package/dist/update-check.d.ts +67 -0
  140. package/dist/update-check.d.ts.map +1 -0
  141. package/dist/update-check.js +284 -0
  142. package/dist/update-check.js.map +1 -0
  143. package/package.json +3 -2
  144. /package/dist/templates/{tsconfig.json → tsconfig.json.template} +0 -0
package/dist/index.js CHANGED
@@ -7,31 +7,17 @@ import { realpathSync } from "node:fs";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { parseArgs } from "node:util";
9
9
  import { createClient } from "./client.js";
10
- import { COMMAND_REGISTRY, resolveAlias } from "./command-registry.js";
11
- import { showCompletion } from "./commands/completion.js";
12
- import { deploy } from "./commands/deployments.js";
13
- import { getForm, getStartForm, getUserTaskForm } from "./commands/forms.js";
10
+ import { COMMAND_DISPATCH } from "./command-dispatch.js";
11
+ import { COMMAND_REGISTRY, deriveParseArgsOptions, getCommandDef, resolveAlias, } from "./command-registry.js";
12
+ import { detectUnknownFlags, validateFlags } from "./command-validation.js";
13
+ import { installCompletion, refreshCompletionsIfStale, showCompletion, } from "./commands/completion.js";
14
14
  import { showCommandHelp, showHelp, showVerbResources, showVersion, } from "./commands/help.js";
15
- import { createIdentityAuthorization, createIdentityGroup, createIdentityMappingRule, createIdentityRole, createIdentityTenant, createIdentityUser, deleteIdentityAuthorization, deleteIdentityGroup, deleteIdentityMappingRule, deleteIdentityRole, deleteIdentityTenant, deleteIdentityUser, getIdentityAuthorization, getIdentityGroup, getIdentityMappingRule, getIdentityRole, getIdentityTenant, getIdentityUser, handleAssign, handleUnassign, listAuthorizations, listGroups, listMappingRules, listRoles, listTenants, listUsers, searchIdentityAuthorizations, searchIdentityGroups, searchIdentityMappingRules, searchIdentityRoles, searchIdentityTenants, searchIdentityUsers, validateCreateAuthorizationOptions, } from "./commands/identity.js";
16
- import { getIncident, listIncidents, resolveIncident, } from "./commands/incidents.js";
17
- import { activateJobs, completeJob, failJob, listJobs, } from "./commands/jobs.js";
18
- import { mcpProxy } from "./commands/mcp-proxy.js";
19
- import { correlateMessage, publishMessage } from "./commands/messages.js";
20
- import { openApp, openUrl, validateOpenAppOptions } from "./commands/open.js";
21
- import { downgradePlugin, initPlugin, listPlugins, loadPlugin, syncPlugins, unloadPlugin, upgradePlugin, } from "./commands/plugins.js";
22
- import { getProcessDefinition, listProcessDefinitions, } from "./commands/process-definitions.js";
23
- import { cancelProcessInstance, createProcessInstance, getProcessInstance, listProcessInstances, } from "./commands/process-instances.js";
24
- import { addProfile, listProfiles, removeProfile, whichProfile, } from "./commands/profiles.js";
25
- import { run } from "./commands/run.js";
26
- import { detectUnknownSearchFlags, searchIncidents, searchJobs, searchProcessDefinitions, searchProcessInstances, searchUserTasks, searchVariables, } from "./commands/search.js";
27
- import { setOutputFormat, useProfile, useTenant } from "./commands/session.js";
28
- import { getTopology } from "./commands/topology.js";
29
- import { completeUserTask, listUserTasks } from "./commands/user-tasks.js";
30
- import { watchFiles } from "./commands/watch.js";
15
+ import { handleAssign, handleUnassign } from "./commands/identity.js";
31
16
  import { loadSessionState, resolveTenantId } from "./config.js";
32
17
  import { getLogger } from "./logger.js";
33
18
  import { executePluginCommand, loadInstalledPlugins } from "./plugin-loader.js";
34
19
  import { c8ctl } from "./runtime.js";
20
+ import { printUpdateNotification, startUpdateCheck } from "./update-check.js";
35
21
  /**
36
22
  * Type guard: extract a string value from parseArgs values, or undefined.
37
23
  * parseArgs with strict:false returns values typed as string | boolean | (string|boolean)[] | undefined.
@@ -46,12 +32,6 @@ function str(value) {
46
32
  function bool(value) {
47
33
  return typeof value === "boolean" ? value : undefined;
48
34
  }
49
- /**
50
- * Type guard: narrow unknown to Record<string, unknown>.
51
- */
52
- export function isRecord(value) {
53
- return value != null && typeof value === "object";
54
- }
55
35
  /**
56
36
  * Parse --version flag value into a number, or undefined if not set.
57
37
  */
@@ -61,96 +41,14 @@ function parseVersionFlag(values) {
61
41
  : undefined;
62
42
  }
63
43
  /**
64
- * Parse command line arguments
44
+ * Parse command line arguments.
45
+ * Options are derived from the command registry — no manual duplication.
65
46
  */
66
47
  function parseCliArgs() {
67
48
  try {
68
49
  const { values, positionals } = parseArgs({
69
50
  args: process.argv.slice(2),
70
- options: {
71
- help: { type: "boolean", short: "h" },
72
- version: { type: "string", short: "v" },
73
- all: { type: "boolean" },
74
- xml: { type: "boolean" },
75
- profile: { type: "string" },
76
- bpmnProcessId: { type: "string" },
77
- id: { type: "string" },
78
- processDefinitionId: { type: "string" },
79
- processInstanceKey: { type: "string" },
80
- processDefinitionKey: { type: "string" },
81
- parentProcessInstanceKey: { type: "string" },
82
- variables: { type: "string" },
83
- state: { type: "string" },
84
- assignee: { type: "string" },
85
- type: { type: "string" },
86
- correlationKey: { type: "string" },
87
- timeToLive: { type: "string" },
88
- maxJobsToActivate: { type: "string" },
89
- timeout: { type: "string" },
90
- worker: { type: "string" },
91
- retries: { type: "string" },
92
- errorMessage: { type: "string" },
93
- baseUrl: { type: "string" },
94
- clientId: { type: "string" },
95
- clientSecret: { type: "string" },
96
- audience: { type: "string" },
97
- oAuthUrl: { type: "string" },
98
- defaultTenantId: { type: "string" },
99
- from: { type: "string" },
100
- name: { type: "string" },
101
- key: { type: "string" },
102
- elementId: { type: "string" },
103
- errorType: { type: "string" },
104
- awaitCompletion: { type: "boolean" },
105
- fetchVariables: { type: "boolean" },
106
- requestTimeout: { type: "string" },
107
- value: { type: "string" },
108
- scopeKey: { type: "string" },
109
- fullValue: { type: "boolean" },
110
- userTask: { type: "boolean" },
111
- processDefinition: { type: "boolean" },
112
- iname: { type: "string" },
113
- iid: { type: "string" },
114
- iassignee: { type: "string" },
115
- ierrorMessage: { type: "string" },
116
- itype: { type: "string" },
117
- ivalue: { type: "string" },
118
- sortBy: { type: "string" },
119
- asc: { type: "boolean" },
120
- desc: { type: "boolean" },
121
- limit: { type: "string" },
122
- between: { type: "string" },
123
- dateField: { type: "string" },
124
- fields: { type: "string" },
125
- "dry-run": { type: "boolean" },
126
- verbose: { type: "boolean" },
127
- force: { type: "boolean" },
128
- none: { type: "boolean" },
129
- "from-file": { type: "string" },
130
- "from-env": { type: "boolean" },
131
- username: { type: "string" },
132
- email: { type: "string" },
133
- password: { type: "string" },
134
- ownerId: { type: "string" },
135
- ownerType: { type: "string" },
136
- resourceType: { type: "string" },
137
- resourceId: { type: "string" },
138
- permissions: { type: "string" },
139
- roleId: { type: "string" },
140
- groupId: { type: "string" },
141
- tenantId: { type: "string" },
142
- claimName: { type: "string" },
143
- claimValue: { type: "string" },
144
- mappingRuleId: { type: "string" },
145
- "to-user": { type: "string" },
146
- "to-group": { type: "string" },
147
- "to-tenant": { type: "string" },
148
- "to-mapping-rule": { type: "string" },
149
- "from-user": { type: "string" },
150
- "from-group": { type: "string" },
151
- "from-tenant": { type: "string" },
152
- "from-mapping-rule": { type: "string" },
153
- },
51
+ options: deriveParseArgsOptions(),
154
52
  allowPositionals: true,
155
53
  strict: false,
156
54
  });
@@ -171,16 +69,19 @@ export function resolveProcessDefinitionId(values) {
171
69
  str(values.bpmnProcessId));
172
70
  }
173
71
  /**
174
- * Warn about unrecognized flags for a search resource.
72
+ * Warn about unrecognized flags for a verb × resource combination.
175
73
  */
176
- function warnUnknownSearchFlags(logger, unknownFlags, resource) {
74
+ function warnUnknownFlags(logger, unknownFlags, verb, resource) {
177
75
  if (unknownFlags.length === 0)
178
76
  return;
179
77
  const flagList = unknownFlags.map((f) => `--${f}`).join(", ");
180
- logger.warn(`Flag(s) ${flagList} not recognized for 'search ${resource}'. They will be ignored. Run "c8ctl help search" for valid options.`);
78
+ const command = resource ? `${verb} ${resource}` : verb;
79
+ logger.warn(`Flag(s) ${flagList} not recognized for '${command}'. They will be ignored. Run "c8ctl help ${verb}" for valid options.`);
181
80
  }
182
81
  /** Verbs that require a resource argument — derived from COMMAND_REGISTRY (includes aliases). */
183
- const VERB_REQUIRES_RESOURCE = new Set(Object.entries(COMMAND_REGISTRY)
82
+ const VERB_REQUIRES_RESOURCE = new Set(
83
+ // biome-ignore lint/plugin: widen to CommandDef to access optional aliases property
84
+ Object.entries(COMMAND_REGISTRY)
184
85
  .filter(([, def]) => def.requiresResource)
185
86
  .flatMap(([verb, def]) => [verb, ...(def.aliases ?? [])]));
186
87
  /**
@@ -189,6 +90,8 @@ const VERB_REQUIRES_RESOURCE = new Set(Object.entries(COMMAND_REGISTRY)
189
90
  async function main() {
190
91
  // Load session state from disk at startup
191
92
  loadSessionState();
93
+ // Fire-and-forget: check for CLI updates in the background
94
+ startUpdateCheck(c8ctl.version);
192
95
  const { values, positionals } = parseCliArgs();
193
96
  // Initialize logger with current output mode from c8ctl runtime
194
97
  const logger = getLogger(c8ctl.outputMode);
@@ -224,6 +127,8 @@ async function main() {
224
127
  c8ctl.init({ createClient, resolveTenantId, getLogger });
225
128
  // Load installed plugins
226
129
  await loadInstalledPlugins();
130
+ // Auto-refresh installed completions if CLI version changed
131
+ refreshCompletionsIfStale();
227
132
  // Extract command and resource
228
133
  const [verb, resource, ...args] = positionals;
229
134
  // Handle global --version flag (only when no verb/command is provided)
@@ -246,7 +151,7 @@ async function main() {
246
151
  verb === "-h") {
247
152
  // Check if user wants help for a specific command
248
153
  if (resource) {
249
- showCommandHelp(resource);
154
+ await showCommandHelp(resource);
250
155
  }
251
156
  else {
252
157
  showHelp();
@@ -255,6 +160,14 @@ async function main() {
255
160
  }
256
161
  // Handle completion command
257
162
  if (verb === "completion") {
163
+ // Run unknown-flag detection before early return (completion returns
164
+ // before the general detectUnknownFlags call below).
165
+ const completionUnknownFlags = detectUnknownFlags(verb, resource ?? "", values);
166
+ warnUnknownFlags(logger, completionUnknownFlags, verb, resource ?? "");
167
+ if (resource === "install") {
168
+ installCompletion(str(values.shell));
169
+ return;
170
+ }
258
171
  showCompletion(resource);
259
172
  return;
260
173
  }
@@ -263,893 +176,101 @@ async function main() {
263
176
  // Resource validation guard — single chokepoint for all verbs that require a resource.
264
177
  // Derived from COMMAND_REGISTRY.requiresResource.
265
178
  // help/completion are dispatched before this point.
179
+ // If --help is passed, show verb help instead of the resource error.
266
180
  if (!resource && VERB_REQUIRES_RESOURCE.has(verb)) {
267
- showVerbResources(verb);
268
- return;
269
- }
270
- // Handle session commands
271
- if (verb === "use") {
272
- if (normalizedResource === "profile") {
273
- if (values.none) {
274
- useProfile("--none");
275
- return;
276
- }
277
- if (!args[0]) {
278
- logger.error("Profile name required. Usage: c8 use profile <name>");
279
- process.exit(1);
280
- }
281
- useProfile(args[0]);
181
+ if (values.help) {
182
+ showVerbResources(verb);
282
183
  return;
283
184
  }
284
- if (normalizedResource === "tenant") {
285
- if (!args[0]) {
286
- logger.error("Tenant ID required. Usage: c8 use tenant <id>");
287
- process.exit(1);
288
- }
289
- useTenant(args[0]);
290
- return;
291
- }
292
- showVerbResources("use");
293
- return;
294
- }
295
- if (verb === "output") {
296
- if (!resource) {
297
- logger.info(`Current output mode: ${c8ctl.outputMode}`);
298
- if (c8ctl.outputMode === "text") {
299
- logger.info("");
300
- }
301
- logger.info("Available modes: json|text");
302
- return;
303
- }
304
- setOutputFormat(resource);
305
- return;
306
- }
307
- // Handle profile commands
308
- if (verb === "list" && normalizedResource === "profile") {
309
- listProfiles();
310
- return;
311
- }
312
- if (verb === "add" && normalizedResource === "profile") {
313
- if (!args[0]) {
314
- logger.error("Profile name required. Usage: c8 add profile <name> --baseUrl=<url>");
315
- process.exit(1);
316
- }
317
- const envFile = typeof values["from-file"] === "string" ? values["from-file"] : undefined;
318
- const fromEnv = values["from-env"] === true;
319
- addProfile(args[0], {
320
- url: typeof values.baseUrl === "string" ? values.baseUrl : undefined,
321
- clientId: typeof values.clientId === "string" ? values.clientId : undefined,
322
- clientSecret: typeof values.clientSecret === "string"
323
- ? values.clientSecret
324
- : undefined,
325
- audience: typeof values.audience === "string" ? values.audience : undefined,
326
- oauthUrl: typeof values.oAuthUrl === "string" ? values.oAuthUrl : undefined,
327
- tenantId: typeof values.defaultTenantId === "string"
328
- ? values.defaultTenantId
329
- : undefined,
330
- envFile,
331
- fromEnv,
332
- });
333
- return;
334
- }
335
- if ((verb === "remove" || verb === "rm") &&
336
- normalizedResource === "profile") {
337
- if (!args[0]) {
338
- logger.error("Profile name required. Usage: c8 remove profile <name>");
339
- process.exit(1);
340
- }
341
- removeProfile(args[0]);
342
- return;
343
- }
344
- if (verb === "which" && normalizedResource === "profile") {
345
- whichProfile();
346
- return;
347
- }
348
- // Handle plugin commands
349
- if (verb === "list" && normalizedResource === "plugin") {
350
- listPlugins();
351
- return;
352
- }
353
- if (verb === "load" && normalizedResource === "plugin") {
354
- const fromUrl = str(values.from);
355
- const packageName = args[0];
356
- await loadPlugin(packageName, fromUrl);
357
- return;
358
- }
359
- if ((verb === "unload" || verb === "remove" || verb === "rm") &&
360
- normalizedResource === "plugin") {
361
- if (!args[0]) {
362
- logger.error("Package name required. Usage: c8 unload plugin <package-name>");
363
- process.exit(1);
364
- }
365
- await unloadPlugin(args[0], { force: bool(values.force) });
366
- return;
367
- }
368
- if (verb === "sync" && normalizedResource === "plugin") {
369
- await syncPlugins();
370
- return;
371
- }
372
- if (verb === "upgrade" && normalizedResource === "plugin") {
373
- if (!args[0]) {
374
- logger.error("Package name required. Usage: c8 upgrade plugin <package-name> [version]");
375
- process.exit(1);
376
- }
377
- await upgradePlugin(args[0], args[1]);
378
- return;
379
- }
380
- if (verb === "downgrade" && normalizedResource === "plugin") {
381
- if (!args[0] || !args[1]) {
382
- logger.error("Package name and version required. Usage: c8 downgrade plugin <package-name> <version>");
383
- process.exit(1);
384
- }
385
- await downgradePlugin(args[0], args[1]);
386
- return;
387
- }
388
- if (verb === "init" && normalizedResource === "plugin") {
389
- await initPlugin(args[0]);
390
- return;
391
- }
392
- // Handle process instance commands
393
- if (verb === "list" &&
394
- (normalizedResource === "process-instance" ||
395
- normalizedResource === "process-instances")) {
396
- await listProcessInstances({
397
- profile: str(values.profile),
398
- processDefinitionId: resolveProcessDefinitionId(values),
399
- version: parseVersionFlag(values),
400
- state: str(values.state),
401
- all: bool(values.all),
402
- sortBy: str(values.sortBy),
403
- sortOrder,
404
- limit,
405
- between: str(values.between),
406
- dateField: str(values.dateField),
407
- });
408
- return;
409
- }
410
- if (verb === "get" && normalizedResource === "process-instance") {
411
- if (!args[0]) {
412
- logger.error("Process instance key required. Usage: c8 get pi <key>");
413
- process.exit(1);
414
- }
415
- // Check if --variables flag is present (for get command, it's a boolean flag)
416
- const includeVariables = process.argv.includes("--variables");
417
- await getProcessInstance(args[0], {
418
- profile: str(values.profile),
419
- variables: includeVariables,
420
- });
421
- return;
422
- }
423
- if (verb === "create" && normalizedResource === "process-instance") {
424
- await createProcessInstance({
425
- profile: str(values.profile),
426
- processDefinitionId: resolveProcessDefinitionId(values),
427
- version: parseVersionFlag(values),
428
- variables: str(values.variables),
429
- awaitCompletion: bool(values.awaitCompletion),
430
- fetchVariables: bool(values.fetchVariables),
431
- requestTimeout: values.requestTimeout && typeof values.requestTimeout === "string"
432
- ? parseInt(values.requestTimeout, 10)
433
- : undefined,
434
- });
435
- return;
436
- }
437
- if (verb === "cancel" && normalizedResource === "process-instance") {
438
- if (!args[0]) {
439
- logger.error("Process instance key required. Usage: c8 cancel pi <key>");
440
- process.exit(1);
441
- }
442
- await cancelProcessInstance(args[0], {
443
- profile: str(values.profile),
444
- });
445
- return;
446
- }
447
- // Handle await command - alias for create with awaitCompletion
448
- if (verb === "await" && normalizedResource === "process-instance") {
449
- // await pi is an alias for create pi with --awaitCompletion
450
- // It supports the same flags as create (id, variables, version, etc.)
451
- await createProcessInstance({
452
- profile: str(values.profile),
453
- processDefinitionId: resolveProcessDefinitionId(values),
454
- version: parseVersionFlag(values),
455
- variables: str(values.variables),
456
- awaitCompletion: true, // Always true for await command
457
- fetchVariables: bool(values.fetchVariables),
458
- requestTimeout: values.requestTimeout && typeof values.requestTimeout === "string"
459
- ? parseInt(values.requestTimeout, 10)
460
- : undefined,
461
- });
462
- return;
463
- }
464
- // Handle process definition commands
465
- if (verb === "list" &&
466
- (normalizedResource === "process-definition" ||
467
- normalizedResource === "process-definitions")) {
468
- await listProcessDefinitions({
469
- profile: str(values.profile),
470
- sortBy: str(values.sortBy),
471
- sortOrder,
472
- limit,
473
- });
474
- return;
185
+ showVerbResources(verb);
186
+ process.exit(1);
475
187
  }
476
- if (verb === "get" && normalizedResource === "process-definition") {
188
+ // Flag validation run all registered validators before dispatch.
189
+ // Validators throw on invalid input; validateFlags catches and exits.
190
+ const commandDef = getCommandDef(verb);
191
+ if (commandDef) {
192
+ validateFlags(values, commandDef.flags);
193
+ }
194
+ // Unknown flag detection — warn about flags not recognised for this verb × resource.
195
+ // Derived from COMMAND_REGISTRY; resource-scoped for search/list.
196
+ const unknownFlags = detectUnknownFlags(verb, normalizedResource, values);
197
+ warnUnknownFlags(logger, unknownFlags, verb, resource);
198
+ // ── Assign / unassign — legacy delegation (not yet migrated to defineCommand) ──
199
+ if (verb === "assign") {
477
200
  if (!args[0]) {
478
- logger.error("Process definition key required. Usage: c8 get pd <key>");
201
+ logger.error(`ID required. Usage: c8 assign ${normalizedResource} <id> --to-<target>=<targetId>`);
479
202
  process.exit(1);
480
203
  }
481
- await getProcessDefinition(args[0], {
204
+ await handleAssign(normalizedResource, args[0], values, {
482
205
  profile: str(values.profile),
483
- xml: bool(values.xml),
484
206
  });
485
207
  return;
486
208
  }
487
- // Handle user task commands
488
- if (verb === "list" &&
489
- (normalizedResource === "user-task" || normalizedResource === "user-tasks")) {
490
- await listUserTasks({
491
- profile: str(values.profile),
492
- state: str(values.state),
493
- assignee: str(values.assignee),
494
- all: bool(values.all),
495
- sortBy: str(values.sortBy),
496
- sortOrder,
497
- limit,
498
- between: str(values.between),
499
- dateField: str(values.dateField),
500
- });
501
- return;
502
- }
503
- if (verb === "complete" && normalizedResource === "user-task") {
209
+ if (verb === "unassign") {
504
210
  if (!args[0]) {
505
- logger.error("User task key required. Usage: c8 complete ut <key>");
211
+ logger.error(`ID required. Usage: c8 unassign ${normalizedResource} <id> --from-<target>=<targetId>`);
506
212
  process.exit(1);
507
213
  }
508
- await completeUserTask(args[0], {
214
+ await handleUnassign(normalizedResource, args[0], values, {
509
215
  profile: str(values.profile),
510
- variables: str(values.variables),
511
216
  });
512
217
  return;
513
218
  }
514
- // Handle incident commands
515
- if (verb === "list" &&
516
- (normalizedResource === "incident" || normalizedResource === "incidents")) {
517
- await listIncidents({
518
- profile: str(values.profile),
519
- state: str(values.state),
520
- processInstanceKey: str(values.processInstanceKey),
521
- sortBy: str(values.sortBy),
219
+ // ── Registry-driven dispatch ───────────────────────────────────────────
220
+ // For verbs with enumerated resources (e.g. `list process-instance`),
221
+ // the dispatch key includes the normalised resource.
222
+ // For verbs without enumerated resources (e.g. `deploy`, `run`, `watch`),
223
+ // the resource slot holds the first positional argument (a file path, etc.)
224
+ // and the dispatch key uses an empty resource suffix.
225
+ const hasEnumeratedResources = commandDef !== undefined &&
226
+ commandDef.resources !== undefined &&
227
+ commandDef.resources.length > 0;
228
+ const useResourceKey = VERB_REQUIRES_RESOURCE.has(verb) && hasEnumeratedResources;
229
+ const dispatchKey = useResourceKey
230
+ ? `${verb}:${normalizedResource}`
231
+ : `${verb}:`;
232
+ // For verbs with enumerated resources, fall back to "verb:" when the
233
+ // specific resource key isn't found (lets the handler validate/reject).
234
+ const handler = COMMAND_DISPATCH.get(dispatchKey) ??
235
+ (useResourceKey ? COMMAND_DISPATCH.get(`${verb}:`) : undefined);
236
+ if (handler) {
237
+ const profile = str(values.profile);
238
+ // Lazy config access: createClient() and resolveTenantId() are deferred
239
+ // until first access, so commands that never touch ctx.client or
240
+ // ctx.tenantId (e.g. session/profile management) skip config resolution.
241
+ let _client;
242
+ let _tenantId;
243
+ let _tenantResolved = false;
244
+ const ctx = {
245
+ get client() {
246
+ if (!_client)
247
+ _client = createClient(profile);
248
+ return _client;
249
+ },
250
+ logger,
251
+ get tenantId() {
252
+ if (!_tenantResolved) {
253
+ _tenantId = resolveTenantId(profile);
254
+ _tenantResolved = true;
255
+ }
256
+ return _tenantId;
257
+ },
258
+ resource: useResourceKey ? normalizedResource : resource || "",
259
+ positionals: args,
522
260
  sortOrder,
523
- limit,
524
- between: str(values.between),
525
- });
526
- return;
527
- }
528
- if (verb === "get" && normalizedResource === "incident") {
529
- if (!args[0]) {
530
- logger.error("Incident key required. Usage: c8 get inc <key>");
531
- process.exit(1);
532
- }
533
- await getIncident(args[0], {
534
- profile: str(values.profile),
535
- });
536
- return;
537
- }
538
- if (verb === "resolve" && normalizedResource === "incident") {
539
- if (!args[0]) {
540
- logger.error("Incident key required. Usage: c8 resolve inc <key>");
541
- process.exit(1);
542
- }
543
- await resolveIncident(args[0], {
544
- profile: str(values.profile),
545
- });
546
- return;
547
- }
548
- // Handle job commands
549
- if (verb === "list" && normalizedResource === "jobs") {
550
- await listJobs({
551
- profile: str(values.profile),
552
- state: str(values.state),
553
- type: str(values.type),
554
261
  sortBy: str(values.sortBy),
555
- sortOrder,
556
262
  limit,
263
+ all: bool(values.all),
557
264
  between: str(values.between),
558
265
  dateField: str(values.dateField),
559
- });
560
- return;
561
- }
562
- if (verb === "activate" && normalizedResource === "jobs") {
563
- if (!args[0]) {
564
- logger.error("Job type required. Usage: c8 activate jobs <type>");
565
- process.exit(1);
566
- }
567
- await activateJobs(args[0], {
568
- profile: str(values.profile),
569
- maxJobsToActivate: values.maxJobsToActivate && typeof values.maxJobsToActivate === "string"
570
- ? parseInt(values.maxJobsToActivate, 10)
571
- : undefined,
572
- timeout: values.timeout && typeof values.timeout === "string"
573
- ? parseInt(values.timeout, 10)
574
- : undefined,
575
- worker: str(values.worker),
576
- });
577
- return;
578
- }
579
- if (verb === "complete" && normalizedResource === "job") {
580
- if (!args[0]) {
581
- logger.error("Job key required. Usage: c8 complete job <key>");
582
- process.exit(1);
583
- }
584
- await completeJob(args[0], {
585
- profile: str(values.profile),
586
- variables: str(values.variables),
587
- });
588
- return;
589
- }
590
- if (verb === "fail" && normalizedResource === "job") {
591
- if (!args[0]) {
592
- logger.error("Job key required. Usage: c8 fail job <key>");
593
- process.exit(1);
594
- }
595
- await failJob(args[0], {
596
- profile: str(values.profile),
597
- retries: values.retries && typeof values.retries === "string"
598
- ? parseInt(values.retries, 10)
599
- : undefined,
600
- errorMessage: str(values.errorMessage),
601
- });
602
- return;
603
- }
604
- // Handle message commands
605
- if (verb === "publish" && normalizedResource === "message") {
606
- if (!args[0]) {
607
- logger.error("Message name required. Usage: c8 publish msg <name>");
608
- process.exit(1);
609
- }
610
- await publishMessage(args[0], {
611
- profile: str(values.profile),
612
- correlationKey: str(values.correlationKey),
613
- variables: str(values.variables),
614
- timeToLive: values.timeToLive && typeof values.timeToLive === "string"
615
- ? parseInt(values.timeToLive, 10)
616
- : undefined,
617
- });
618
- return;
619
- }
620
- if (verb === "correlate" && normalizedResource === "message") {
621
- if (!args[0]) {
622
- logger.error("Message name required. Usage: c8 correlate msg <name>");
623
- process.exit(1);
624
- }
625
- await correlateMessage(args[0], {
626
- profile: str(values.profile),
627
- correlationKey: str(values.correlationKey),
628
- variables: str(values.variables),
629
- timeToLive: values.timeToLive && typeof values.timeToLive === "string"
630
- ? parseInt(values.timeToLive, 10)
631
- : undefined,
632
- });
633
- return;
634
- }
635
- // Handle topology command
636
- if (verb === "get" && normalizedResource === "topology") {
637
- await getTopology({
638
- profile: str(values.profile),
639
- });
640
- return;
641
- }
642
- // Handle form commands
643
- if (verb === "get" && normalizedResource === "form") {
644
- if (!args[0]) {
645
- logger.error("Key required. Usage: c8 get form <key> [--userTask|--ut] [--processDefinition|--pd]");
646
- process.exit(1);
647
- }
648
- // Check for flags and their aliases
649
- const isUserTask = process.argv.includes("--userTask") || process.argv.includes("--ut");
650
- const isProcessDefinition = process.argv.includes("--processDefinition") ||
651
- process.argv.includes("--pd");
652
- // If both flags specified, error
653
- if (isUserTask && isProcessDefinition) {
654
- logger.error("Cannot specify both --userTask|--ut and --processDefinition|--pd. Use one or the other, or omit both to search both types.");
655
- process.exit(1);
656
- }
657
- // If specific flag provided, use that API
658
- if (isUserTask) {
659
- await getUserTaskForm(args[0], {
660
- profile: str(values.profile),
661
- });
662
- }
663
- else if (isProcessDefinition) {
664
- await getStartForm(args[0], {
665
- profile: str(values.profile),
666
- });
667
- }
668
- else {
669
- // No flag provided - try both
670
- await getForm(args[0], {
671
- profile: str(values.profile),
672
- });
673
- }
674
- return;
675
- }
676
- // Handle deploy command
677
- if (verb === "deploy") {
678
- const paths = resource
679
- ? [resource, ...args]
680
- : args.length > 0
681
- ? args
682
- : ["."];
683
- await deploy(paths, {
684
- profile: str(values.profile),
685
- });
686
- return;
687
- }
688
- // Handle run command
689
- if (verb === "run") {
690
- await run(resource, {
691
- profile: str(values.profile),
692
- variables: str(values.variables),
693
- });
694
- return;
695
- }
696
- // Handle watch command
697
- if (verb === "watch" || verb === "w") {
698
- const paths = resource
699
- ? [resource, ...args]
700
- : args.length > 0
701
- ? args
702
- : ["."];
703
- await watchFiles(paths, {
704
- profile: str(values.profile),
705
- force: bool(values.force),
706
- });
707
- return;
708
- }
709
- // Handle open command
710
- if (verb === "open") {
711
- const validated = validateOpenAppOptions(resource, {
712
- profile: str(values.profile),
713
- dryRun: bool(values["dry-run"]),
714
- });
715
- await openApp(validated);
716
- return;
717
- }
718
- // Handle feedback command
719
- if (verb === "feedback") {
720
- const logger = getLogger();
721
- const url = "https://github.com/camunda/c8ctl/issues";
722
- logger.info(`Opening feedback page: ${url}`);
723
- openUrl(url);
724
- return;
725
- }
726
- // Handle mcp-proxy command
727
- if (verb === "mcp-proxy") {
728
- await mcpProxy(positionals.slice(1), {
729
- profile: str(values.profile),
730
- });
731
- return;
732
- }
733
- // Handle search commands
734
- if (verb === "search") {
735
- const normalizedSearchResource = resolveAlias(resource);
736
- const unknownFlags = detectUnknownSearchFlags(values, normalizedSearchResource);
737
- warnUnknownSearchFlags(logger, unknownFlags, resource);
738
- if (normalizedSearchResource === "process-definition" ||
739
- normalizedSearchResource === "process-definitions") {
740
- await searchProcessDefinitions({
741
- profile: str(values.profile),
742
- processDefinitionId: resolveProcessDefinitionId(values),
743
- name: str(values.name),
744
- version: parseVersionFlag(values),
745
- key: str(values.key),
746
- iProcessDefinitionId: str(values.iid),
747
- iName: str(values.iname),
748
- sortBy: str(values.sortBy),
749
- sortOrder,
750
- _unknownFlags: unknownFlags,
751
- });
752
- return;
753
- }
754
- if (normalizedSearchResource === "process-instance" ||
755
- normalizedSearchResource === "process-instances") {
756
- await searchProcessInstances({
757
- profile: str(values.profile),
758
- processDefinitionId: resolveProcessDefinitionId(values),
759
- processDefinitionKey: str(values.processDefinitionKey),
760
- version: parseVersionFlag(values),
761
- state: str(values.state),
762
- key: str(values.key),
763
- parentProcessInstanceKey: str(values.parentProcessInstanceKey),
764
- iProcessDefinitionId: str(values.iid),
765
- sortBy: str(values.sortBy),
766
- sortOrder,
767
- _unknownFlags: unknownFlags,
768
- between: str(values.between),
769
- dateField: str(values.dateField),
770
- });
771
- return;
772
- }
773
- if (normalizedSearchResource === "user-task" ||
774
- normalizedSearchResource === "user-tasks") {
775
- await searchUserTasks({
776
- profile: str(values.profile),
777
- state: str(values.state),
778
- assignee: str(values.assignee),
779
- processInstanceKey: str(values.processInstanceKey),
780
- processDefinitionKey: str(values.processDefinitionKey),
781
- elementId: str(values.elementId),
782
- iAssignee: str(values.iassignee),
783
- sortBy: str(values.sortBy),
784
- sortOrder,
785
- _unknownFlags: unknownFlags,
786
- between: str(values.between),
787
- dateField: str(values.dateField),
788
- });
789
- return;
790
- }
791
- if (normalizedSearchResource === "incident" ||
792
- normalizedSearchResource === "incidents") {
793
- await searchIncidents({
794
- profile: str(values.profile),
795
- state: str(values.state),
796
- processInstanceKey: str(values.processInstanceKey),
797
- processDefinitionKey: str(values.processDefinitionKey),
798
- processDefinitionId: resolveProcessDefinitionId(values),
799
- errorType: str(values.errorType),
800
- errorMessage: str(values.errorMessage),
801
- iErrorMessage: str(values.ierrorMessage),
802
- iProcessDefinitionId: str(values.iid),
803
- sortBy: str(values.sortBy),
804
- sortOrder,
805
- _unknownFlags: unknownFlags,
806
- between: str(values.between),
807
- });
808
- return;
809
- }
810
- if (normalizedSearchResource === "jobs") {
811
- await searchJobs({
812
- profile: str(values.profile),
813
- state: str(values.state),
814
- type: str(values.type),
815
- processInstanceKey: str(values.processInstanceKey),
816
- processDefinitionKey: str(values.processDefinitionKey),
817
- iType: str(values.itype),
818
- sortBy: str(values.sortBy),
819
- sortOrder,
820
- _unknownFlags: unknownFlags,
821
- between: str(values.between),
822
- dateField: str(values.dateField),
823
- });
824
- return;
825
- }
826
- if (normalizedSearchResource === "variable" ||
827
- normalizedSearchResource === "variables") {
828
- await searchVariables({
829
- profile: str(values.profile),
830
- name: str(values.name),
831
- value: str(values.value),
832
- processInstanceKey: str(values.processInstanceKey),
833
- scopeKey: str(values.scopeKey),
834
- fullValue: bool(values.fullValue),
835
- iName: str(values.iname),
836
- iValue: str(values.ivalue),
837
- sortBy: str(values.sortBy),
838
- sortOrder,
839
- limit,
840
- _unknownFlags: unknownFlags,
841
- });
842
- return;
843
- }
844
- if (normalizedSearchResource === "user") {
845
- await searchIdentityUsers({
846
- profile: str(values.profile),
847
- username: str(values.username),
848
- name: str(values.name),
849
- email: str(values.email),
850
- sortBy: str(values.sortBy),
851
- sortOrder,
852
- limit,
853
- });
854
- return;
855
- }
856
- if (normalizedSearchResource === "role") {
857
- await searchIdentityRoles({
858
- profile: str(values.profile),
859
- roleId: str(values.roleId),
860
- name: str(values.name),
861
- sortBy: str(values.sortBy),
862
- sortOrder,
863
- limit,
864
- });
865
- return;
866
- }
867
- if (normalizedSearchResource === "group") {
868
- await searchIdentityGroups({
869
- profile: str(values.profile),
870
- groupId: str(values.groupId),
871
- name: str(values.name),
872
- sortBy: str(values.sortBy),
873
- sortOrder,
874
- limit,
875
- });
876
- return;
877
- }
878
- if (normalizedSearchResource === "tenant") {
879
- await searchIdentityTenants({
880
- profile: str(values.profile),
881
- name: str(values.name),
882
- tenantId: str(values.tenantId),
883
- sortBy: str(values.sortBy),
884
- sortOrder,
885
- limit,
886
- });
887
- return;
888
- }
889
- if (normalizedSearchResource === "authorization") {
890
- await searchIdentityAuthorizations({
891
- profile: str(values.profile),
892
- ownerId: str(values.ownerId),
893
- ownerType: str(values.ownerType),
894
- resourceType: str(values.resourceType),
895
- resourceId: str(values.resourceId),
896
- sortBy: str(values.sortBy),
897
- sortOrder,
898
- limit,
899
- });
900
- return;
901
- }
902
- if (normalizedSearchResource === "mapping-rule") {
903
- await searchIdentityMappingRules({
904
- profile: str(values.profile),
905
- mappingRuleId: str(values.mappingRuleId),
906
- name: str(values.name),
907
- claimName: str(values.claimName),
908
- claimValue: str(values.claimValue),
909
- sortBy: str(values.sortBy),
910
- sortOrder,
911
- limit,
912
- });
913
- return;
914
- }
915
- // If resource not recognized for search, show available resources
916
- showVerbResources("search");
917
- return;
918
- }
919
- // Handle identity list commands
920
- if (verb === "list" && normalizedResource === "user") {
921
- await listUsers({
922
- profile: str(values.profile),
923
- sortBy: str(values.sortBy),
924
- sortOrder,
925
- limit,
926
- });
927
- return;
928
- }
929
- if (verb === "list" && normalizedResource === "role") {
930
- await listRoles({
931
- profile: str(values.profile),
932
- sortBy: str(values.sortBy),
933
- sortOrder,
934
- limit,
935
- });
936
- return;
937
- }
938
- if (verb === "list" && normalizedResource === "group") {
939
- await listGroups({
940
- profile: str(values.profile),
941
- sortBy: str(values.sortBy),
942
- sortOrder,
943
- limit,
944
- });
945
- return;
946
- }
947
- if (verb === "list" && normalizedResource === "tenant") {
948
- await listTenants({
949
- profile: str(values.profile),
950
- sortBy: str(values.sortBy),
951
- sortOrder,
952
- limit,
953
- });
954
- return;
955
- }
956
- if (verb === "list" && normalizedResource === "authorization") {
957
- await listAuthorizations({
958
- profile: str(values.profile),
959
- sortBy: str(values.sortBy),
960
- sortOrder,
961
- limit,
962
- });
963
- return;
964
- }
965
- if (verb === "list" && normalizedResource === "mapping-rule") {
966
- await listMappingRules({
967
- profile: str(values.profile),
968
- sortBy: str(values.sortBy),
969
- sortOrder,
970
- limit,
971
- });
972
- return;
973
- }
974
- // Handle identity get commands
975
- if (verb === "get" && normalizedResource === "user") {
976
- if (!args[0]) {
977
- logger.error("Username required. Usage: c8 get user <username>");
978
- process.exit(1);
979
- }
980
- await getIdentityUser(args[0], { profile: str(values.profile) });
981
- return;
982
- }
983
- if (verb === "get" && normalizedResource === "role") {
984
- if (!args[0]) {
985
- logger.error("Role ID required. Usage: c8 get role <roleId>");
986
- process.exit(1);
987
- }
988
- await getIdentityRole(args[0], { profile: str(values.profile) });
989
- return;
990
- }
991
- if (verb === "get" && normalizedResource === "group") {
992
- if (!args[0]) {
993
- logger.error("Group ID required. Usage: c8 get group <groupId>");
994
- process.exit(1);
995
- }
996
- await getIdentityGroup(args[0], { profile: str(values.profile) });
997
- return;
998
- }
999
- if (verb === "get" && normalizedResource === "tenant") {
1000
- if (!args[0]) {
1001
- logger.error("Tenant ID required. Usage: c8 get tenant <tenantId>");
1002
- process.exit(1);
1003
- }
1004
- await getIdentityTenant(args[0], { profile: str(values.profile) });
1005
- return;
1006
- }
1007
- if (verb === "get" && normalizedResource === "authorization") {
1008
- if (!args[0]) {
1009
- logger.error("Authorization key required. Usage: c8 get auth <key>");
1010
- process.exit(1);
1011
- }
1012
- await getIdentityAuthorization(args[0], { profile: str(values.profile) });
1013
- return;
1014
- }
1015
- if (verb === "get" && normalizedResource === "mapping-rule") {
1016
- if (!args[0]) {
1017
- logger.error("Mapping rule ID required. Usage: c8 get mapping-rule <id>");
1018
- process.exit(1);
1019
- }
1020
- await getIdentityMappingRule(args[0], { profile: str(values.profile) });
1021
- return;
1022
- }
1023
- // Handle identity create commands
1024
- if (verb === "create" && normalizedResource === "user") {
1025
- await createIdentityUser({
1026
- profile: str(values.profile),
1027
- username: str(values.username),
1028
- name: str(values.name),
1029
- email: str(values.email),
1030
- password: str(values.password),
1031
- });
1032
- return;
1033
- }
1034
- if (verb === "create" && normalizedResource === "role") {
1035
- await createIdentityRole({
1036
- profile: str(values.profile),
1037
- roleId: str(values.roleId),
1038
- name: str(values.name),
1039
- });
1040
- return;
1041
- }
1042
- if (verb === "create" && normalizedResource === "group") {
1043
- await createIdentityGroup({
1044
- profile: str(values.profile),
1045
- groupId: str(values.groupId),
1046
- name: str(values.name),
1047
- });
1048
- return;
1049
- }
1050
- if (verb === "create" && normalizedResource === "tenant") {
1051
- await createIdentityTenant({
1052
- profile: str(values.profile),
1053
- tenantId: str(values.tenantId),
1054
- name: str(values.name),
1055
- });
1056
- return;
1057
- }
1058
- if (verb === "create" && normalizedResource === "authorization") {
1059
- const validated = validateCreateAuthorizationOptions({
1060
- profile: str(values.profile),
1061
- ownerId: str(values.ownerId),
1062
- ownerType: str(values.ownerType),
1063
- resourceType: str(values.resourceType),
1064
- resourceId: str(values.resourceId),
1065
- permissions: str(values.permissions),
1066
- });
1067
- await createIdentityAuthorization(validated);
1068
- return;
1069
- }
1070
- if (verb === "create" && normalizedResource === "mapping-rule") {
1071
- await createIdentityMappingRule({
1072
- profile: str(values.profile),
1073
- mappingRuleId: str(values.mappingRuleId),
1074
- name: str(values.name),
1075
- claimName: str(values.claimName),
1076
- claimValue: str(values.claimValue),
1077
- });
1078
- return;
1079
- }
1080
- // Handle identity delete commands
1081
- if (verb === "delete" && normalizedResource === "user") {
1082
- if (!args[0]) {
1083
- logger.error("Username required. Usage: c8 delete user <username>");
1084
- process.exit(1);
1085
- }
1086
- await deleteIdentityUser(args[0], { profile: str(values.profile) });
1087
- return;
1088
- }
1089
- if (verb === "delete" && normalizedResource === "role") {
1090
- if (!args[0]) {
1091
- logger.error("Role ID required. Usage: c8 delete role <roleId>");
1092
- process.exit(1);
1093
- }
1094
- await deleteIdentityRole(args[0], { profile: str(values.profile) });
1095
- return;
1096
- }
1097
- if (verb === "delete" && normalizedResource === "group") {
1098
- if (!args[0]) {
1099
- logger.error("Group ID required. Usage: c8 delete group <groupId>");
1100
- process.exit(1);
1101
- }
1102
- await deleteIdentityGroup(args[0], { profile: str(values.profile) });
1103
- return;
1104
- }
1105
- if (verb === "delete" && normalizedResource === "tenant") {
1106
- if (!args[0]) {
1107
- logger.error("Tenant ID required. Usage: c8 delete tenant <tenantId>");
1108
- process.exit(1);
1109
- }
1110
- await deleteIdentityTenant(args[0], { profile: str(values.profile) });
1111
- return;
1112
- }
1113
- if (verb === "delete" && normalizedResource === "authorization") {
1114
- if (!args[0]) {
1115
- logger.error("Authorization key required. Usage: c8 delete auth <key>");
1116
- process.exit(1);
1117
- }
1118
- await deleteIdentityAuthorization(args[0], {
1119
- profile: str(values.profile),
1120
- });
1121
- return;
1122
- }
1123
- if (verb === "delete" && normalizedResource === "mapping-rule") {
1124
- if (!args[0]) {
1125
- logger.error("Mapping rule ID required. Usage: c8 delete mapping-rule <id>");
1126
- process.exit(1);
1127
- }
1128
- await deleteIdentityMappingRule(args[0], { profile: str(values.profile) });
1129
- return;
1130
- }
1131
- // Handle assign/unassign commands
1132
- if (verb === "assign") {
1133
- if (!args[0]) {
1134
- logger.error(`ID required. Usage: c8 assign ${normalizedResource} <id> --to-<target>=<targetId>`);
1135
- process.exit(1);
1136
- }
1137
- await handleAssign(normalizedResource, args[0], values, {
1138
- profile: str(values.profile),
1139
- });
1140
- return;
1141
- }
1142
- if (verb === "unassign") {
1143
- if (!args[0]) {
1144
- logger.error(`ID required. Usage: c8 unassign ${normalizedResource} <id> --from-<target>=<targetId>`);
1145
- process.exit(1);
1146
- }
1147
- await handleUnassign(normalizedResource, args[0], values, {
1148
- profile: str(values.profile),
1149
- });
266
+ version: parseVersionFlag(values),
267
+ dryRun: c8ctl.dryRun,
268
+ profile,
269
+ };
270
+ await handler.execute(ctx, values, args);
1150
271
  return;
1151
272
  }
1152
- // Try to execute plugin command (before verb-only check)
273
+ // Try to execute plugin command (before unknown-command error)
1153
274
  if (await executePluginCommand(verb, resource ? [resource, ...args] : args)) {
1154
275
  return;
1155
276
  }
@@ -1162,7 +283,9 @@ async function main() {
1162
283
  // Use realpathSync to resolve symlinks (e.g. when installed globally via npm link)
1163
284
  try {
1164
285
  if (realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
1165
- main().catch((error) => {
286
+ main()
287
+ .then(() => printUpdateNotification())
288
+ .catch((error) => {
1166
289
  if (c8ctl.verbose) {
1167
290
  throw error;
1168
291
  }