npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

camunda-cli

v0.4.3

Published

Command-line client for self-hosted Camunda 7: inspect and lint deployed BPMN models, and diagnose why an instance is stuck

Readme

camunda-cli

Command-line client for self-hosted Camunda 7, aimed at the two things that take the most time when developing BPMN: understanding a model that is already deployed, and working out why an instance is stuck. Not affiliated with Camunda GmbH.

Built and verified against Camunda 7.24 on a live multi-tenant deployment.

$ camunda lint order-process

ERROR   uncovered-value  Gateway_1jk90v6
        "amount check" branches on amount > 300 and amount < 300, so amount == 300 matches
        neither branch and the instance will fail there.

ERROR   variable-name-mismatch  flow Flow_0jpsuir (retry)
        Reads "input_huruf", which nothing in this process writes, while a form here writes
        "input_9ltnt". These names look related, so this is most likely a typo: the
        expression throws "Cannot resolve identifier 'input_huruf'" the moment it is evaluated.

A task-by-task guide in Indonesian is in PANDUAN.md.

Install

npm install -g camunda-cli
camunda login https://your-host/engine-rest

login prompts for a username and password and stores them in ~/.config/camunda-cli/config.json with mode 0600. Camunda 7's REST API authenticates with HTTP Basic on every request and has no token to exchange, so the password is kept rather than a session. It is sent only to the engine you configured.

Installing globally without root:

npm config set prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH"
npm install -g camunda-cli

What it is for

Read a deployed model. Much of what you need while debugging — which variable a gateway condition reads, whether a step is async, what a service task is actually wired to — is only in the deployed XML, not behind any REST resource. inspect reads it out and lays it flat.

camunda inspect order-process
Flow nodes
  TYPE              ID                NAME              LIVE      DETAIL
  startEvent        StartEvent_1      order received              1 field(s)
  userTask          Activity_11j96l5  check stock       [3 here]  assignee=ops 2 field(s)
  serviceTask       Activity_0dfmi5x  charge card                 async:before addon=payments/charge#98
  exclusiveGateway  Gateway_1jk90v6   paid?

Sequence flows
  FROM              TO                LABEL  CONDITION
  Gateway_1jk90v6   Event_0ufajup     yes    ${paid == true}
  Gateway_1jk90v6   Activity_retry    no     ${paid == false}

Find the bugs before an instance does. lint checks a model statically. It takes a deployed key or a local file, so the usual loop is to check while editing and only then deploy:

camunda lint ./order-process.bpmn     # no engine and no login needed
camunda lint order-process            # or the deployed version

Every rule exists because that failure was reproduced against a real engine first.

Errors are reserved for defects that are provable from the model alone, because deploy refuses to push a model that has one:

| Rule | What it catches | |---|---| | uncovered-value | > N and < N branches leaving == N with nowhere to go (ENGINE-02004) | | default-flow-with-condition | A default flow that also carries a condition, which the engine rejects outright (ENGINE-09005) | | variable-name-mismatch | A form writing one variable nothing reads, feeding a condition reading one nothing writes | | dangling-flow, dangling-boundary, no-start-event | References to elements that do not exist |

Warnings are risks that need a human to judge, since the model cannot prove them either way: no-default-flow, unwritten-variable, initiator-expression, no-op-service-task, addon-without-config, unreachable, dead-end, ambiguous-branch.

Run over 190 production models, the checks raised zero errors and did not block a single deploy, while still flagging both defects in a model built to contain them. That balance is deliberate: a static check an agent cannot trust is worse than none, because acting on a confident wrong answer breaks a process that was working.

deploy runs the same checks and refuses to push a model with an error, unless you pass --skip-lint.

Work out why an instance is stuck. diagnose gathers what is scattered across several endpoints and unpacks it:

camunda diagnose 3426894
Stopped at
    Activity_CekSisaKuota  transition  Check Remaining Quota

1 problem(s)

  open incident at 2026-08-13 08:19:33
  failing element: Activity_TinjauPengajuan
  job attached to:  Activity_CekSisaKuota  (the async marker sits here, the error came from
                    the element above)
  Unknown property used in expression: ${initiator}. Cause: Cannot resolve identifier 'initiator'

It reads more than /incident, because several real failures are invisible there. A step that fails inside the caller's transaction leaves no incident and no job behind at all; an incident points at the activity holding the job, which is often not the activity that failed; and a resolved incident disappears from /incident entirely.

It also unpacks nested integration errors. An addon failure arrives as a REST message wrapping a Java exception wrapping a JSON body whose output field is itself JSON. The sentence you need is at the bottom, so that is what gets printed first.

Commands

Session      login  logout  whoami
Models       definitions  inspect  lint  xml  stats
Instances    instances  instance  start  cancel  vars  set-var
Diagnosis    diagnose  trace  incidents  jobs  stacktrace
Tasks        tasks  task  complete  claim
Deployment   deployments  deploy  undeploy
Events       message  subscriptions
Repair       retry  run-job
Identity     users  groups  tenants

camunda <command> --help for the options on any of them.

Notes that save time

Tenants. On a shared engine the same process key exists under many tenants, and the engine's key-based endpoints answer "no matching process definition ... and no tenant-id", which reads like the process is missing when it is not. Every command taking a key accepts --tenant, and an ambiguous key lists the candidates rather than guessing.

start and complete report what happened next. A start returning HTTP 200 only means the engine accepted it; anything marked async runs after the response. Both commands wait a moment, then say whether it failed, whether the instance finished, or which task is now waiting, along with the exact command to complete it:

$ camunda start order-process --var amount=700:Integer
Started order-process v9 as instance 3435051

Now waiting at:
    3435058  check stock  ops
camunda complete 3435058 --var quantity=<value>

$ camunda complete 3435058 --var quantity=3
Task 3435058 completed.
Instance 3435051 finished in 8.0s.

--no-wait skips the follow-up.

Cleaning up after a test run. cancel --key <key> terminates every running instance of a process in one go, which matters because testing a model leaves a trail of them behind.

Variable types matter. --var n=300 sends a string, and "300" > 200 is a string comparison. Use --var n=300:Integer where a gateway compares numerically, or name:=<json> for structured values.

Transient 401s are retried. A load-balanced engine will intermittently reject a valid credential while a replica is unhealthy: five consecutive 401s followed by five 200s, same credential, seconds apart, is a real pattern observed in production. Requests are retried so this does not get misread as a wrong password. A 4xx carrying a real Camunda error body is never retried.

Output is meant to be piped. No box-drawing characters, and no colour unless stdout is a terminal. Every command takes --json for the raw API payload.

Requirements

  • Node.js 18+
  • A Camunda 7 engine with its REST API enabled

Not covered

DMN evaluation, batch operations, instance migration and modification, and authorization management. These are deliberate omissions rather than oversights: the command surface here covers what came up repeatedly while developing and debugging processes, not all 300-odd REST endpoints. Pull requests welcome.

License

MIT