Skip to content

durable-workflow/cli

Repository files navigation

Durable Workflow CLI

Command-line interface for running and interacting with the Durable Workflow Server.

Installation

Three options depending on what you have installed:

1. Standalone binary (no PHP required). The easiest path — a one-liner installer that detects your OS and arch:

# Linux and macOS
curl -fsSL https://durable-workflow.com/install.sh | sh
# Windows
irm https://durable-workflow.com/install.ps1 | iex

Or download a native binary directly from the releases page. Available assets: dw-linux-x86_64, dw-linux-aarch64, dw-macos-aarch64, dw-windows-x86_64.exe.

macOS x86_64 standalone binaries are not currently produced because the macos-13 runner label is not available to this org; Intel Mac users can run the PHAR with a system PHP.

2. PHAR (requires PHP >= 8.2). Download dw.phar from the releases page and run it with php dw.phar (or chmod +x and call directly — the PHAR has a #!/usr/bin/env php shebang).

3. Composer.

composer global require durable-workflow/cli

Building from source

make phar      # Build the PHAR (requires PHP >= 8.2 and Composer)
make binary    # Build the PHAR plus a standalone native binary for the
               # current platform (downloads Box and static-php-cli on demand)
make clean     # Remove build artifacts

Build artifacts land in ./build/. See scripts/build.sh for the underlying steps; tools are cached under build/.tools/.

Configuration

Set the server URL and auth token via environment variables:

export DURABLE_WORKFLOW_SERVER_URL=http://localhost:8080
export DURABLE_WORKFLOW_AUTH_TOKEN=your-token
export DURABLE_WORKFLOW_NAMESPACE=default

Or pass them as options to any command:

dw --server=http://localhost:8080 --token=your-token --namespace=production workflow:list

The CLI targets control-plane contract version 2 automatically via X-Durable-Workflow-Control-Plane-Version: 2 and expects canonical v2 response fields such as *_name and wait_for. Non-canonical legacy aliases such as signal and wait_policy are rejected.

The server also emits a nested control_plane.contract document with schema durable-workflow.v2.control-plane-response.contract, version 1, and legacy_field_policy: reject_non_canonical. The CLI validates that nested boundary before trusting the server-emitted legacy_fields, required_fields, and success_fields metadata.

For request fields such as workflow:start --duplicate-policy and workflow:update --wait, the CLI now reads the server-published control_plane.request_contract manifest from GET /api/cluster/info before sending the command. Supported servers publish schema durable-workflow.v2.control-plane-request.contract, version 1, with an operations map. The CLI treats missing or unknown request-contract schema/version metadata as a compatibility error instead of silently guessing. Use dw server:info to inspect the current canonical values, rejected aliases, and removed fields advertised by the target server.

Compatibility

CLI version 0.1.x requires Server 2.x (versions 2.0.0+).

The CLI automatically validates server version on first invocation and raises a clear error if incompatible:

$ dw workflow:list
Server version 3.0.0 is incompatible with dw CLI 0.1.x (requires server 2.x).
Upgrade the server or use a compatible CLI version.

See the Version Compatibility documentation for the full compatibility matrix across all components.

Commands

Server

# Check server health
dw server:health

# Show server version and capabilities
dw server:info

# Start a local development server
dw server:start-dev
dw server:start-dev --port=9090 --db=sqlite

Workflows

# Start a workflow
dw workflow:start --type=order.process --input='{"order_id":123}'
dw workflow:start --type=order.process --workflow-id=order-123
dw workflow:start --type=order.process --execution-timeout=3600 --run-timeout=600

# List workflows
dw workflow:list
dw workflow:list --status=running
dw workflow:list --type=order.process

# Describe a workflow
dw workflow:describe order-123
dw workflow:describe order-123 --run-id=01HXYZ --json

# Send a signal
dw workflow:signal order-123 payment-received --input='{"amount":99.99}'

# Query workflow state
dw workflow:query order-123 current-status

# Send an update
dw workflow:update order-123 approve --input='{"approver":"admin"}'

# Cancel a workflow (workflow code can observe and clean up)
dw workflow:cancel order-123 --reason="Customer request"

# Terminate a workflow (immediate, no cleanup)
dw workflow:terminate order-123 --reason="Stuck workflow"

# View event history
dw workflow:history order-123 01HXYZ
dw workflow:history order-123 01HXYZ --follow

Namespaces

# List namespaces
dw namespace:list

# Create a namespace
dw namespace:create staging --description="Staging environment" --retention=7

# Describe a namespace
dw namespace:describe staging

# Update a namespace
dw namespace:update staging --retention=14

Schedules

# Create a schedule
dw schedule:create --workflow-type=reports.daily --cron="0 9 * * *"
dw schedule:create --schedule-id=daily-report --workflow-type=reports.daily --cron="0 9 * * *" --timezone=America/New_York

# List schedules
dw schedule:list

# Describe a schedule
dw schedule:describe daily-report

# Pause/resume
dw schedule:pause daily-report --note="Holiday freeze"
dw schedule:resume daily-report

# Trigger immediately
dw schedule:trigger daily-report

# Backfill missed runs
dw schedule:backfill daily-report --start-time=2024-01-01T00:00:00Z --end-time=2024-01-07T00:00:00Z

# Delete a schedule
dw schedule:delete daily-report

Task Queues

# List task queues
dw task-queue:list

# Describe a task queue (pollers, backlog)
dw task-queue:describe default

Activities

# Complete an activity externally
dw activity:complete TASK_ID --result='{"status":"done"}'

# Fail an activity externally
dw activity:fail TASK_ID --message="External service unavailable" --non-retryable

System Operations

# Show task repair diagnostics
dw system:repair-status

# Run a task repair sweep
dw system:repair-pass

# Show expired activity timeout diagnostics
dw system:activity-timeout-status

# Run activity timeout enforcement sweep
dw system:activity-timeout-pass

# Target specific execution IDs
dw system:activity-timeout-pass --execution-id=EXEC_ID_1 --execution-id=EXEC_ID_2

Global Options

Option Description
--server, -s Server URL (default: $DURABLE_WORKFLOW_SERVER_URL or http://localhost:8080)
--namespace Target namespace (default: $DURABLE_WORKFLOW_NAMESPACE or default)
--token Auth token (default: $DURABLE_WORKFLOW_AUTH_TOKEN)

Exit Codes

The CLI uses a stable exit-code policy so scripts and CI pipelines can react to specific failure modes without parsing stderr. Values follow Symfony Console's canonical 0/1/2 for success / failure / usage, and extend from there:

Code Name Meaning
0 SUCCESS Operation completed successfully.
1 FAILURE Generic failure — command ran but did not succeed.
2 INVALID Invalid usage — bad arguments, unknown options, or local validation. Also returned for HTTP 4xx responses that are not covered below (e.g. 400, 422).
3 NETWORK Could not reach the server (connection refused, DNS failure, TLS handshake failure, transport error).
4 AUTH Authentication or authorization failure. Returned for HTTP 401 and 403.
5 NOT_FOUND Resource not found. Returned for HTTP 404.
6 SERVER Server error. Returned for HTTP 5xx.
7 TIMEOUT Request timed out before the server responded. Also returned for HTTP 408.

Example:

dw workflow:describe chk-does-not-exist
echo $?  # 5 (NOT_FOUND)

dw server:health --server=http://unreachable:9999
echo $?  # 3 (NETWORK)

Exit codes are defined in DurableWorkflow\Cli\Support\ExitCode and are covered by tests/Commands/ExitCodePolicyTest.php.

JSON Output

Every list, describe, read, and mutating command supports --json for machine-readable output. Mutating commands (POST/PUT/DELETE) return the server's raw response body when --json is set, making them safe to pipe into jq or feed into downstream tooling.

# Read surface — stable even when no --json flag is passed for list views.
dw workflow:list --json | jq '.workflows[].workflow_id'

# Mutating surface — capture server response for idempotent automation.
wf_id=$(dw workflow:start --type=orders.Checkout --json | jq -r '.workflow_id')
dw workflow:signal "$wf_id" approve --json | jq '.command_status'

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages