camunda-cli 0.2.0
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 +167 -0
- package/bin/camunda.js +292 -0
- package/package.json +36 -0
- package/src/bpmn.js +387 -0
- package/src/client.js +400 -0
- package/src/commands/definitions.js +194 -0
- package/src/commands/deploy.js +82 -0
- package/src/commands/diagnose.js +257 -0
- package/src/commands/instances.js +205 -0
- package/src/commands/ops.js +127 -0
- package/src/commands/session.js +112 -0
- package/src/commands/tasks.js +129 -0
- package/src/config.js +33 -0
- package/src/errors.js +138 -0
- package/src/lint.js +244 -0
- package/src/output.js +135 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# camunda-cli
|
|
2
|
+
|
|
3
|
+
Command-line client for **self-hosted Camunda 7**, aimed at the two things that take the
|
|
4
|
+
most time when developing BPMN: understanding a model that is already deployed, and working
|
|
5
|
+
out why an instance is stuck. Not affiliated with Camunda GmbH.
|
|
6
|
+
|
|
7
|
+
Built and verified against Camunda 7.24 on a live multi-tenant deployment.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
$ camunda lint order-process
|
|
11
|
+
|
|
12
|
+
ERROR uncovered-value Gateway_1jk90v6
|
|
13
|
+
"amount check" branches on amount > 300 and amount < 300, so amount == 300 matches
|
|
14
|
+
neither branch and the instance will fail there.
|
|
15
|
+
|
|
16
|
+
ERROR variable-name-mismatch flow Flow_0jpsuir (retry)
|
|
17
|
+
Reads "input_huruf", which nothing in this process writes, while a form here writes
|
|
18
|
+
"input_9ltnt". These names look related, so this is most likely a typo: the
|
|
19
|
+
expression throws "Cannot resolve identifier 'input_huruf'" the moment it is evaluated.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g camunda-cli
|
|
26
|
+
camunda login https://your-host/engine-rest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`login` prompts for a username and password and stores them in
|
|
30
|
+
`~/.config/camunda-cli/config.json` with mode `0600`. Camunda 7's REST API authenticates
|
|
31
|
+
with HTTP Basic on every request and has no token to exchange, so the password is kept
|
|
32
|
+
rather than a session. It is sent only to the engine you configured.
|
|
33
|
+
|
|
34
|
+
Installing globally without root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm config set prefix ~/.npm-global
|
|
38
|
+
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
39
|
+
npm install -g camunda-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## What it is for
|
|
43
|
+
|
|
44
|
+
**Read a deployed model.** Much of what you need while debugging — which variable a gateway
|
|
45
|
+
condition reads, whether a step is async, what a service task is actually wired to — is only
|
|
46
|
+
in the deployed XML, not behind any REST resource. `inspect` reads it out and lays it flat.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
camunda inspect order-process
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Flow nodes
|
|
54
|
+
TYPE ID NAME LIVE DETAIL
|
|
55
|
+
startEvent StartEvent_1 order received 1 field(s)
|
|
56
|
+
userTask Activity_11j96l5 check stock [3 here] assignee=ops 2 field(s)
|
|
57
|
+
serviceTask Activity_0dfmi5x charge card async:before addon=payments/charge#98
|
|
58
|
+
exclusiveGateway Gateway_1jk90v6 paid?
|
|
59
|
+
|
|
60
|
+
Sequence flows
|
|
61
|
+
FROM TO LABEL CONDITION
|
|
62
|
+
Gateway_1jk90v6 Event_0ufajup yes ${paid == true}
|
|
63
|
+
Gateway_1jk90v6 Activity_retry no ${paid == false}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Find the bugs before an instance does.** `lint` checks the deployed model statically.
|
|
67
|
+
Every rule exists because that failure was reproduced against a real engine first:
|
|
68
|
+
|
|
69
|
+
| Rule | What it catches |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `uncovered-value` | `> N` and `< N` branches that leave `== N` with nowhere to go (`ENGINE-02004`) |
|
|
72
|
+
| `no-default-flow` | Every branch conditional, no default: any instance where they all fail stops dead |
|
|
73
|
+
| `variable-name-mismatch` | A condition reads `foo` while the form writes `foo_2`, so the expression throws |
|
|
74
|
+
| `unwritten-variable` | A direct `${x}` read where nothing sets `x`; throws instead of yielding null |
|
|
75
|
+
| `initiator-expression` | `${initiator}`, which exists only when started through AlurKerja's API |
|
|
76
|
+
| `no-op-service-task` | A service task with no implementation behind it |
|
|
77
|
+
| `addon-without-config` | An integration call with no config bound |
|
|
78
|
+
| `unreachable`, `dead-end`, `dangling-flow` | Structural mistakes |
|
|
79
|
+
|
|
80
|
+
`deploy` runs the same checks and refuses to push a model with blocking issues, unless you
|
|
81
|
+
pass `--skip-lint`.
|
|
82
|
+
|
|
83
|
+
**Work out why an instance is stuck.** `diagnose` gathers what is scattered across several
|
|
84
|
+
endpoints and unpacks it:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
camunda diagnose 3426894
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Stopped at
|
|
92
|
+
Activity_CekSisaKuota transition Check Remaining Quota
|
|
93
|
+
|
|
94
|
+
1 problem(s)
|
|
95
|
+
|
|
96
|
+
open incident at 2026-08-13 08:19:33
|
|
97
|
+
failing element: Activity_TinjauPengajuan
|
|
98
|
+
job attached to: Activity_CekSisaKuota (the async marker sits here, the error came from
|
|
99
|
+
the element above)
|
|
100
|
+
Unknown property used in expression: ${initiator}. Cause: Cannot resolve identifier 'initiator'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
It reads more than `/incident`, because several real failures are invisible there. A step
|
|
104
|
+
that fails inside the caller's transaction leaves no incident and no job behind at all; an
|
|
105
|
+
incident points at the activity holding the job, which is often not the activity that
|
|
106
|
+
failed; and a resolved incident disappears from `/incident` entirely.
|
|
107
|
+
|
|
108
|
+
It also unpacks nested integration errors. An addon failure arrives as a REST message
|
|
109
|
+
wrapping a Java exception wrapping a JSON body whose `output` field is itself JSON. The
|
|
110
|
+
sentence you need is at the bottom, so that is what gets printed first.
|
|
111
|
+
|
|
112
|
+
## Commands
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Session login logout whoami
|
|
116
|
+
Models definitions inspect lint xml stats
|
|
117
|
+
Instances instances instance start cancel vars set-var
|
|
118
|
+
Diagnosis diagnose trace incidents jobs stacktrace
|
|
119
|
+
Tasks tasks task complete claim
|
|
120
|
+
Deployment deployments deploy undeploy
|
|
121
|
+
Events message subscriptions
|
|
122
|
+
Repair retry run-job
|
|
123
|
+
Identity users groups tenants
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`camunda <command> --help` for the options on any of them.
|
|
127
|
+
|
|
128
|
+
## Notes that save time
|
|
129
|
+
|
|
130
|
+
**Tenants.** On a shared engine the same process key exists under many tenants, and the
|
|
131
|
+
engine's key-based endpoints answer *"no matching process definition ... and no tenant-id"*,
|
|
132
|
+
which reads like the process is missing when it is not. Every command taking a key accepts
|
|
133
|
+
`--tenant`, and an ambiguous key lists the candidates rather than guessing.
|
|
134
|
+
|
|
135
|
+
**`start` waits before reporting success.** A start returning HTTP 200 only means the engine
|
|
136
|
+
accepted it; anything marked async runs after the response. `start` looks for a failure a
|
|
137
|
+
moment later and reports it, instead of leaving you with a green message and a broken
|
|
138
|
+
instance. `--no-wait` skips that.
|
|
139
|
+
|
|
140
|
+
**Variable types matter.** `--var n=300` sends a string, and `"300" > 200` is a string
|
|
141
|
+
comparison. Use `--var n=300:Integer` where a gateway compares numerically, or `name:=<json>`
|
|
142
|
+
for structured values.
|
|
143
|
+
|
|
144
|
+
**Transient 401s are retried.** A load-balanced engine will intermittently reject a valid
|
|
145
|
+
credential while a replica is unhealthy: five consecutive 401s followed by five 200s, same
|
|
146
|
+
credential, seconds apart, is a real pattern observed in production. Requests are retried so
|
|
147
|
+
this does not get misread as a wrong password. A 4xx carrying a real Camunda error body is
|
|
148
|
+
never retried.
|
|
149
|
+
|
|
150
|
+
**Output is meant to be piped.** No box-drawing characters, and no colour unless stdout is a
|
|
151
|
+
terminal. Every command takes `--json` for the raw API payload.
|
|
152
|
+
|
|
153
|
+
## Requirements
|
|
154
|
+
|
|
155
|
+
- Node.js 18+
|
|
156
|
+
- A Camunda 7 engine with its REST API enabled
|
|
157
|
+
|
|
158
|
+
## Not covered
|
|
159
|
+
|
|
160
|
+
DMN evaluation, batch operations, instance migration and modification, and authorization
|
|
161
|
+
management. These are deliberate omissions rather than oversights: the command surface here
|
|
162
|
+
covers what came up repeatedly while developing and debugging processes, not all 300-odd
|
|
163
|
+
REST endpoints. Pull requests welcome.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
package/bin/camunda.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
|
+
import { configureOutput, isJsonMode } from '../src/output.js';
|
|
4
|
+
import { unwrapError, explain } from '../src/errors.js';
|
|
5
|
+
import { loginCommand, logoutCommand, whoamiCommand } from '../src/commands/session.js';
|
|
6
|
+
import { definitionsCommand, inspectCommand, lintCommand, xmlCommand, statsCommand } from '../src/commands/definitions.js';
|
|
7
|
+
import { instancesCommand, instanceCommand, startCommand, cancelCommand, varsCommand } from '../src/commands/instances.js';
|
|
8
|
+
import { diagnoseCommand, incidentsCommand, jobsCommand, stacktraceCommand, traceCommand } from '../src/commands/diagnose.js';
|
|
9
|
+
import { tasksCommand, taskCommand, completeCommand, claimCommand } from '../src/commands/tasks.js';
|
|
10
|
+
import { deploymentsCommand, deployCommand, undeployCommand } from '../src/commands/deploy.js';
|
|
11
|
+
import {
|
|
12
|
+
retryCommand, runJobCommand, setVarCommand, messageCommand, subscriptionsCommand,
|
|
13
|
+
usersCommand, groupsCommand, tenantsCommand,
|
|
14
|
+
} from '../src/commands/ops.js';
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
const int = (v) => parseInt(v, 10);
|
|
18
|
+
const collect = (v, prev) => [...prev, v];
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name('camunda')
|
|
22
|
+
.description(
|
|
23
|
+
'Command-line client for self-hosted Camunda 7.\n\n' +
|
|
24
|
+
'Start with "camunda inspect <key>" to read a deployed model, and\n' +
|
|
25
|
+
'"camunda diagnose <instanceId>" when an instance misbehaves.'
|
|
26
|
+
)
|
|
27
|
+
.version('0.2.0')
|
|
28
|
+
.option('--json', 'print the raw API payload instead of a formatted view')
|
|
29
|
+
.option('--no-color', 'never emit colour, even on a terminal')
|
|
30
|
+
.showHelpAfterError()
|
|
31
|
+
.hook('preAction', (thisCommand) => {
|
|
32
|
+
const opts = thisCommand.opts();
|
|
33
|
+
configureOutput({ json: opts.json, color: opts.color });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Shared option shapes. Multi-tenancy is not optional in practice: the same process key
|
|
37
|
+
// lives under many tenants in a shared engine, and every key-based lookup needs it.
|
|
38
|
+
const withTenant = (cmd) => cmd.option('-t, --tenant <tenantId>', 'restrict to one tenant');
|
|
39
|
+
const withVersion = (cmd) => cmd.option('--pd-version <n>', 'a specific definition version instead of the latest', int);
|
|
40
|
+
|
|
41
|
+
// --- session ---------------------------------------------------------------
|
|
42
|
+
program
|
|
43
|
+
.command('login <url>')
|
|
44
|
+
.description('Store credentials for an engine (the URL may omit /engine-rest)')
|
|
45
|
+
.option('-u, --username <username>', 'prompted for if omitted')
|
|
46
|
+
.option('-p, --password <password>', 'prompted for if omitted, which keeps it out of your shell history')
|
|
47
|
+
.option('--no-suffix', 'use the URL exactly as given')
|
|
48
|
+
.action(loginCommand);
|
|
49
|
+
|
|
50
|
+
program.command('logout').description('Forget the stored credentials').action(logoutCommand);
|
|
51
|
+
program.command('whoami').description('Which engine is configured, and its current totals').action(whoamiCommand);
|
|
52
|
+
|
|
53
|
+
// --- reading models --------------------------------------------------------
|
|
54
|
+
withTenant(
|
|
55
|
+
program
|
|
56
|
+
.command('definitions')
|
|
57
|
+
.alias('defs')
|
|
58
|
+
.description('List deployed process definitions')
|
|
59
|
+
.option('-k, --key <substring>', 'match the definition key')
|
|
60
|
+
.option('-n, --name <substring>', 'match the definition name')
|
|
61
|
+
.option('-l, --latest', 'only the newest version of each')
|
|
62
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
63
|
+
).action(definitionsCommand);
|
|
64
|
+
|
|
65
|
+
withVersion(
|
|
66
|
+
withTenant(
|
|
67
|
+
program
|
|
68
|
+
.command('inspect <keyOrId>')
|
|
69
|
+
.description('Structure of a deployed model: elements, flow conditions, forms, integrations')
|
|
70
|
+
.option('--no-lint', 'skip the static-check summary at the end')
|
|
71
|
+
)
|
|
72
|
+
).action(inspectCommand);
|
|
73
|
+
|
|
74
|
+
withVersion(
|
|
75
|
+
withTenant(
|
|
76
|
+
program
|
|
77
|
+
.command('lint <keyOrId>')
|
|
78
|
+
.description('Static checks over a deployed model, before an instance has to find the bugs')
|
|
79
|
+
.option('-a, --all', 'include informational findings')
|
|
80
|
+
.addOption(new Option('-s, --severity <level>', 'only one severity').choices(['error', 'warning', 'info']))
|
|
81
|
+
)
|
|
82
|
+
).action(lintCommand);
|
|
83
|
+
|
|
84
|
+
withVersion(
|
|
85
|
+
withTenant(program.command('xml <keyOrId>').description('The deployed BPMN XML').option('-o, --output <file>', 'write to a file'))
|
|
86
|
+
).action(xmlCommand);
|
|
87
|
+
|
|
88
|
+
withVersion(
|
|
89
|
+
withTenant(program.command('stats <keyOrId>').description('How many instances sit at each element, and what is failing there'))
|
|
90
|
+
).action(statsCommand);
|
|
91
|
+
|
|
92
|
+
// --- instances -------------------------------------------------------------
|
|
93
|
+
withTenant(
|
|
94
|
+
program
|
|
95
|
+
.command('instances')
|
|
96
|
+
.alias('ps')
|
|
97
|
+
.description('Running instances, or finished ones with --history')
|
|
98
|
+
.option('-k, --key <definitionKey>')
|
|
99
|
+
.option('-b, --business-key <key>')
|
|
100
|
+
.option('-i, --with-incident', 'only instances that have an incident')
|
|
101
|
+
.option('-H, --history', 'search history instead of running instances')
|
|
102
|
+
.addOption(new Option('--state <state>', 'with --history').choices(['finished', 'unfinished', 'completed', 'externallyTerminated', 'internallyTerminated']))
|
|
103
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
104
|
+
).action(instancesCommand);
|
|
105
|
+
|
|
106
|
+
program
|
|
107
|
+
.command('instance <id>')
|
|
108
|
+
.description('One instance: state, where it currently sits, tasks, variables')
|
|
109
|
+
.action(instanceCommand);
|
|
110
|
+
|
|
111
|
+
withVersion(
|
|
112
|
+
withTenant(
|
|
113
|
+
program
|
|
114
|
+
.command('start <keyOrId>')
|
|
115
|
+
.description('Start an instance, then check whether it failed immediately afterwards')
|
|
116
|
+
.option('-b, --business-key <key>')
|
|
117
|
+
.option('--var <name=value>', 'repeatable; name=value, name=value:Type, or name:=<json>', collect, [])
|
|
118
|
+
.option('--no-wait', 'do not look for asynchronous failures after starting')
|
|
119
|
+
.option('--wait <ms>', 'how long to wait before that check', int)
|
|
120
|
+
)
|
|
121
|
+
).action(startCommand);
|
|
122
|
+
|
|
123
|
+
program
|
|
124
|
+
.command('cancel <id>')
|
|
125
|
+
.description('Force-terminate a running instance; it ends as EXTERNALLY_TERMINATED')
|
|
126
|
+
.option('-r, --reason <text>', 'recorded against the instance')
|
|
127
|
+
.option('-y, --yes', 'skip the confirmation prompt')
|
|
128
|
+
.action(cancelCommand);
|
|
129
|
+
|
|
130
|
+
program
|
|
131
|
+
.command('vars <instanceId>')
|
|
132
|
+
.description('Variables on an instance, or every write to them with --history')
|
|
133
|
+
.option('-H, --history', 'each individual update, oldest first')
|
|
134
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
135
|
+
.action(varsCommand);
|
|
136
|
+
|
|
137
|
+
program
|
|
138
|
+
.command('set-var <instanceId> <name=value>')
|
|
139
|
+
.description('Overwrite a variable on a running instance')
|
|
140
|
+
.addOption(new Option('--type <type>').choices(['String', 'Integer', 'Long', 'Double', 'Boolean', 'Json']).default('String'))
|
|
141
|
+
.action(setVarCommand);
|
|
142
|
+
|
|
143
|
+
// --- diagnosis -------------------------------------------------------------
|
|
144
|
+
program
|
|
145
|
+
.command('diagnose <instanceId>')
|
|
146
|
+
.alias('why')
|
|
147
|
+
.description('Why an instance is stuck or failed, with the nested integration errors unpacked')
|
|
148
|
+
.option('--stacktrace', 'include the Java frames behind each failure')
|
|
149
|
+
.action(diagnoseCommand);
|
|
150
|
+
|
|
151
|
+
program
|
|
152
|
+
.command('trace <instanceId>')
|
|
153
|
+
.description('Every activity this instance has been through, in order')
|
|
154
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
155
|
+
.action(traceCommand);
|
|
156
|
+
|
|
157
|
+
withTenant(
|
|
158
|
+
program
|
|
159
|
+
.command('incidents')
|
|
160
|
+
.description('Open incidents, or every recorded one with --history')
|
|
161
|
+
.option('-i, --instance <processInstanceId>')
|
|
162
|
+
.option('-k, --key <definitionKey>')
|
|
163
|
+
.option('-H, --history', 'include incidents that were already resolved')
|
|
164
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
165
|
+
).action(incidentsCommand);
|
|
166
|
+
|
|
167
|
+
program
|
|
168
|
+
.command('jobs')
|
|
169
|
+
.description('Pending and failed jobs')
|
|
170
|
+
.option('-i, --instance <processInstanceId>')
|
|
171
|
+
.option('-k, --key <definitionKey>')
|
|
172
|
+
.option('-f, --failed', 'only jobs carrying an exception')
|
|
173
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
174
|
+
.action(jobsCommand);
|
|
175
|
+
|
|
176
|
+
program
|
|
177
|
+
.command('stacktrace <jobId>')
|
|
178
|
+
.description('The stack trace behind a failed job, engine internals filtered out')
|
|
179
|
+
.option('--full', 'every frame, unfiltered')
|
|
180
|
+
.action(stacktraceCommand);
|
|
181
|
+
|
|
182
|
+
// --- tasks -----------------------------------------------------------------
|
|
183
|
+
withTenant(
|
|
184
|
+
program
|
|
185
|
+
.command('tasks')
|
|
186
|
+
.description('Open human tasks')
|
|
187
|
+
.option('-a, --assignee <userId>')
|
|
188
|
+
.option('-i, --instance <processInstanceId>')
|
|
189
|
+
.option('-k, --key <definitionKey>')
|
|
190
|
+
.option('-u, --unassigned')
|
|
191
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
192
|
+
).action(tasksCommand);
|
|
193
|
+
|
|
194
|
+
program.command('task <id>').description('One task, with the form fields needed to complete it').action(taskCommand);
|
|
195
|
+
|
|
196
|
+
program
|
|
197
|
+
.command('complete <taskId>')
|
|
198
|
+
.description('Complete a task; anything it triggers synchronously is reported here')
|
|
199
|
+
.option('--var <name=value>', 'repeatable; name=value, name=value:Type, or name:=<json>', collect, [])
|
|
200
|
+
.option('--no-wait', 'skip the follow-up check')
|
|
201
|
+
.action(completeCommand);
|
|
202
|
+
|
|
203
|
+
program
|
|
204
|
+
.command('claim <taskId>')
|
|
205
|
+
.description('Assign a task to yourself or someone else')
|
|
206
|
+
.option('-u, --user <userId>', 'defaults to the logged-in user')
|
|
207
|
+
.option('--unclaim', 'remove the assignee instead')
|
|
208
|
+
.action(claimCommand);
|
|
209
|
+
|
|
210
|
+
// --- deployment ------------------------------------------------------------
|
|
211
|
+
withTenant(
|
|
212
|
+
program
|
|
213
|
+
.command('deployments')
|
|
214
|
+
.description('Recent deployments')
|
|
215
|
+
.option('-n, --name <substring>')
|
|
216
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
217
|
+
).action(deploymentsCommand);
|
|
218
|
+
|
|
219
|
+
withTenant(
|
|
220
|
+
program
|
|
221
|
+
.command('deploy <files...>')
|
|
222
|
+
.description('Deploy BPMN or DMN files, refusing models with blocking issues')
|
|
223
|
+
.option('-n, --name <name>', 'deployment name')
|
|
224
|
+
.option('--skip-lint', 'deploy without the static checks')
|
|
225
|
+
).action(deployCommand);
|
|
226
|
+
|
|
227
|
+
program
|
|
228
|
+
.command('undeploy <deploymentId>')
|
|
229
|
+
.description('Delete a deployment')
|
|
230
|
+
.option('--cascade', 'also delete its instances and their history')
|
|
231
|
+
.action(undeployCommand);
|
|
232
|
+
|
|
233
|
+
// --- events ----------------------------------------------------------------
|
|
234
|
+
program
|
|
235
|
+
.command('message <name>')
|
|
236
|
+
.description('Correlate a message to whatever is waiting for it')
|
|
237
|
+
.option('-i, --instance <processInstanceId>')
|
|
238
|
+
.option('-b, --business-key <key>')
|
|
239
|
+
.option('--all', 'correlate to every match rather than requiring exactly one')
|
|
240
|
+
.action(messageCommand);
|
|
241
|
+
|
|
242
|
+
program
|
|
243
|
+
.command('subscriptions')
|
|
244
|
+
.description('What is currently waiting on a message or signal')
|
|
245
|
+
.option('-i, --instance <processInstanceId>')
|
|
246
|
+
.addOption(new Option('--type <type>').choices(['message', 'signal', 'compensate', 'conditional']))
|
|
247
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
248
|
+
.action(subscriptionsCommand);
|
|
249
|
+
|
|
250
|
+
// --- repair ----------------------------------------------------------------
|
|
251
|
+
program
|
|
252
|
+
.command('retry <jobId>')
|
|
253
|
+
.description('Give a failed job its retries back so it runs again')
|
|
254
|
+
.option('-r, --retries <n>', 'defaults to 1', int)
|
|
255
|
+
.option('--now', 'run it immediately instead of waiting for the scheduler')
|
|
256
|
+
.action(retryCommand);
|
|
257
|
+
|
|
258
|
+
program.command('run-job <jobId>').description('Execute a pending job right now').action(runJobCommand);
|
|
259
|
+
|
|
260
|
+
// --- identity --------------------------------------------------------------
|
|
261
|
+
program
|
|
262
|
+
.command('users')
|
|
263
|
+
.description('Engine-local users (these are not your business users when an identity provider is in front)')
|
|
264
|
+
.option('-s, --search <substring>')
|
|
265
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
266
|
+
.action(usersCommand);
|
|
267
|
+
|
|
268
|
+
program.command('groups').description('Engine-local groups').option('--limit <n>', 'maximum rows', int).action(groupsCommand);
|
|
269
|
+
|
|
270
|
+
program
|
|
271
|
+
.command('tenants')
|
|
272
|
+
.description('Tenants known to the engine')
|
|
273
|
+
.option('-s, --search <substring>')
|
|
274
|
+
.option('--limit <n>', 'maximum rows', int)
|
|
275
|
+
.action(tenantsCommand);
|
|
276
|
+
|
|
277
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
278
|
+
if (isJsonMode()) {
|
|
279
|
+
console.error(JSON.stringify({ error: err.message, status: err.status ?? null, body: err.body ?? null }, null, 2));
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
const unwrapped = unwrapError(err.body?.message || err.message);
|
|
283
|
+
console.error(unwrapped.message);
|
|
284
|
+
for (const layer of unwrapped.layers) {
|
|
285
|
+
const text = typeof layer.value === 'string' ? layer.value : JSON.stringify(layer.value, null, 2);
|
|
286
|
+
console.error(`\n${layer.label}:`);
|
|
287
|
+
for (const l of text.split('\n')) console.error(` ${l}`);
|
|
288
|
+
}
|
|
289
|
+
const hint = explain(err.body?.message || err.message);
|
|
290
|
+
if (hint) console.error(`\n${hint}`);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "camunda-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Command-line client for self-hosted Camunda 7: inspect and lint deployed BPMN models, and diagnose why an instance is stuck",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/itsanla/camunda-cli.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"camunda": "bin/camunda.js"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"src"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"camunda",
|
|
23
|
+
"camunda7",
|
|
24
|
+
"bpmn",
|
|
25
|
+
"cli",
|
|
26
|
+
"self-hosted",
|
|
27
|
+
"workflow",
|
|
28
|
+
"lint",
|
|
29
|
+
"debugging"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"commander": "^12.1.0",
|
|
33
|
+
"chalk": "^5.3.0",
|
|
34
|
+
"cli-table3": "^0.6.5"
|
|
35
|
+
}
|
|
36
|
+
}
|