Files
windmill/system_prompts/auto-generated/cli/cli-commands.md
T
Ruben FiszelandClaude Opus 4.8 74f579e6d9 feat(pipeline): local development for data pipelines (CLI --local + pipeline dev preview) (#9840)
* feat(pipeline): local development for data pipelines (CLI --local + pipeline dev preview)

Add the local edit→preview→run loop for data pipelines (folders of `// pipeline`
scripts), the analog of `wmill dev` / `wmill app dev`, usable from a code editor
or an agentic loop — without deploying.

No backend changes: full body inference comes from the same wasm the frontend
uses (windmill-parser-wasm-asset), which returns assets + pipeline annotations in
one call; local runs reuse runScriptPreview with _wmill_skip_asset_dispatch.

- localGraph.ts: wasm-backed working-tree → asset-graph builder (the enabler)
- pipeline show/run --local; new pipeline docs (PIPELINE.md/AGENTS.md) subcommand
- pipeline dev watcher + /pipeline_dev page (PipelineDevView) rendering the same
  PipelineGraphEditor from the pushed local graph, run via preview
- cascadeRun.ts: reusable run primitives extracted from the route page
- regenerated CLI agent docs

See docs/pipeline-local-dev.md for the full design, test steps, and handoff TODOs.
The live `pipeline dev` browser preview is implemented but not yet stack-verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): improve local dev preview (run, activity, responsive)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): dev-preview args, multi-root run, ws auto-reconnect

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): connect managed-materialize producer in local dev graph

The CLI pinned windmill-parser-wasm-asset ^1.728.1, which predates managed-materialize support (added in 1.733.1); the frontend already pins 1.740.0. The CLI's wasm therefore never emitted `// materialize`, so the producer had no output edge and showed disconnected from its `// on` consumers. Bump the CLI to 1.740.0 (matching the frontend) and translate the parsed materialize target into the producer's write edge + materialize_target, mirroring frontend resolveGraph.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): harden local-dev CLI (bare-.sql crash, defaultTs, docs clobber)

Review fixes, complementary to the dev-preview/materialize/multi-root work already
on the branch (none overlap those commits):

- localGraph: a bare `.sql` (no dialect) made inferContentTypeFromFilePath throw and
  abort the whole graph build — and wedge `pipeline dev` at startup. Skip the
  unclassifiable file instead. Also map `bunnative` → parse_assets_ts and add
  ruby/rlang/nu/powershell to the `#`-comment fallback.
- show/run/docs/dev: thread the resolved `wmill.yaml` defaultTs into the graph
  builder so `.ts` infers under the workspace's runtime (bun vs deno) instead of
  always bun — `opts.defaultTs` was always undefined (no such CLI flag).
- dev: wrap the startup graph build so a half-written file can't abort the watcher.
- docs: don't clobber a user-authored AGENTS.md/CLAUDE.md — only (over)write the
  pointer when absent or already a generated `@PIPELINE.md` pointer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): bind dev WS to loopback + local-graph regression tests

- pipeline dev WS broadcast the folder's full script source (scripts[].content + temp_script_refs) unauthenticated on 0.0.0.0:3201 — bind 127.0.0.1 so it's not LAN-reachable (webview localhost + SSH/devbox port-forward still work).

- Add regression tests for the just-landed local-graph fixes: bare .sql is skipped (was a build/dev-startup crash), defaultTs threads into .ts runtime inference (bun vs deno), and #-comment languages (ruby) use the # annotation fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): --frontend flag for pipeline dev page origin

wmill pipeline dev opens <remote>/pipeline_dev, but that route only exists in this build's frontend, so it 404s against a remote whose deployed frontend predates it. --frontend <origin> points the page at a locally-run frontend (REMOTE=<remote> npm run dev) while the API/token still target the remote — enabling the live preview against a real backend before the PR is deployed. No behavior change when omitted. Regenerated CLI agent docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): WS session token + details-pane live-reload refresh

Addresses CI review (Codex/Pi/Claude):

- dev WS: a browser tab could open ws://localhost:<port>/ws and receive the folder's full source (browsers don't enforce same-origin on WS, loopback bind alone doesn't help). Gate the upgrade on an unguessable per-session token carried in the dev-page URL (verifyClient → 401 without it). Verified: no-token/bad-token connections get 401 with no bundle.

- details pane: scriptRes keyed on [workspace, selection, draftScript] didn't re-run on a pipeline dev live-reload (same selection), so the open pane showed stale source. Thread a localScriptsVersion (the pushed bundle) into the key. Verified: editing a selected node's file updates the pane source without reselect.

- docs/pipeline-local-dev.md: refresh the stale 'not yet exercised' status + done TODOs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): emit volume: annotation assets in local dev graph

Addresses CI review (Codex P1 / Pi P1): the wasm body parser doesn't surface `// volume: <name>` annotations — the frontend (infer.ts:parseVolumeAnnotations) and backend (asset_inference.rs) parse them separately and merge as rw volume assets. localGraph didn't, so a `# volume: cache` producer had no write edge and showed disconnected from its `// on volume://cache` consumer (and pipeline run --local wouldn't schedule downstream). Mirror the leading-comment-block scan (SQL excluded, matching both reference parsers) and merge into inferScriptAssets. Regression test added; verified producer -> volume://cache -> consumer connects.

Also (Codex P2): docs/pipeline-local-dev.md manual browser URL omitted the new ws_token param — without it the WS upgrade is rejected and the page sits disconnected. Doc now says to copy the URL the CLI prints (carries wm_token + ws_token) and recommends --frontend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): runAll excludes event roots + review polish

Addresses CI review (Codex P1, Claude P2/P3):

- pipeline run runAll: derive the whole-pipeline selection from validStarts + descendants instead of all runnables, so an unqualified 'pipeline run <folder>' no longer fires event-trigger roots (kafka/mqtt/…) with empty args/side effects. Verified: a kafka root is excluded from the plan.

- cascadeRun.ts runBoundedCascade: use buildLineageDownstreamMap (read-aware) so a pure-reader runs after its producer, and return cyclic — parity with the route page's bounded run (the file is meant to be THE shared correct primitive).

- PipelineGraphEditor: storedRightPaneSize starts at 0 so the orientation-aware default (55% stacked / 40% side-by-side) actually applies on first open.

- localGraph fallbackParse (go/bash): scan only the leading comment header (no body-comment phantom triggers) and strip key=value options from the asset URI; regression test added.

- docs: reject '..' in the folder arg (it writes files under f/<folder>).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): route local previews to the // tag worker

Addresses CI review P1: the local graph/bundle dropped the parsed `// tag`, so a node annotated `// tag gpu` ran on the default worker in both `pipeline run --local` and `/pipeline_dev`, while the deployed pipeline routes it to that worker tag. Carry the tag through LocalScript / the pushed bundle / LocalScriptContent and pass it to runScriptPreview at all three launch sites. Verified: a duckdb node tagged `bash` produces a job tagged `bash`; regression test added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): add asset partitions/schemas routes to OpenAPI, use generated client

The ducklake asset panels (PartitionStatusGrid, SchemaHistoryPanel) hit /assets/partitions and /assets/asset_schemas via raw fetch with cookie-only auth, because those backend routes were never added to openapi.yaml so the generated client had no methods for them. On /pipeline_dev (token-via-URL, no session cookie) the raw fetches 401'd. Add both GET routes + MaterializedPartition/AssetSchemaVersion schemas to openapi.yaml and call them through AssetService, which injects the bearer token, types, and cancellation automatically. Verified: Partitions + Schema tabs load in /pipeline_dev. (backfill stays a raw fetch — it's an EE-only route not in the OSS spec — with the token added inline.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(cli): regenerate bun.lock for windmill-parser-wasm-asset

package.json / package-lock.json carry windmill-parser-wasm-asset@1.740.0 but the tracked bun.lock (the CLI installs/builds/tests via bun) was stale, so fresh bun installs would resolve a different graph than the committed lock. Regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): show asset producer + its runs in the dev-preview panel

Selecting a ducklake/asset node in /pipeline_dev showed 'No producer for this asset' because selectionProducers wasn't passed (it's derived from the deployed graph on the route page, absent here). Compute it from the local graph's w/rw write-edges (incl. the // materialize target) and pass it through, mirroring the route page — so the panel shows the producing script and its (preview) runs, including data-test failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): carry annotation metadata onto local-graph runnables

The local graph emitted only path/usage_kind/in_pipeline/materialize_target per runnable, so /pipeline_dev and pipeline show --local weren't the same surface as the deployed graph for annotated scripts — missing the badges/lineage the shared canvas renders. Map the wasm-parsed partition_kind, freshness, tag, retry, data_tests, column_lineage, and materialize_strategy (derived append/merge/replace) onto each runnable, mirroring the deployed AssetGraphRunnableNode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): exclude event handlers that are lineage descendants from runAll

The runAll guarantee ('never fires an event handler with empty args') only held for event ROOTS — validStarts excludes them, but runAll then unions in descendants(dag, start), so a kafka/mqtt/... handler that also reads an upstream pipeline asset (a lineage descendant of a valid start) still landed in the plan. Add eventTriggerScripts() and subtract it from the selection after the descendant union. +unit test.

Also: docs/pipeline-local-dev.md recipe used 'pipeline docs demo_pipeline' without --local (default queries the deployed graph → hits the empty hint); add --local.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): whole-pipeline run cuts at event handlers (drop their downstream too)

The prior runAll fix subtracted event handlers from the selection but left their downstream: for manual_root → asset_x → kafka_handler → asset_y → consumer, deleting only kafka_handler left consumer selected, and topoOrder then ran it as a root with missing/stale event-derived inputs. Replace the descendant-union+delete with reachableCutting(dag, validStarts, eventHandlers): traverse from valid starts but treat event handlers as cut points, so a node reachable ONLY through an event handler is dropped while one reachable via a non-event path stays. +unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): recover // tag in the go/bash annotation fallback

The wasm path carries out.tag, but the go/bash fallback (and the wasm-error degradation path) only recovered pipeline + on, so a // tag gpu on a bash/go node — or a temporarily-unparseable ts/py/sql node — silently routed the local preview to the default worker while the deployed pipeline routes to the tag. Scan for // tag in fallbackParse too. +test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(pipeline): extract shared assetProducers helper

The 'who writes this asset' write-edge derivation was copied verbatim in PipelineDevView and the pipeline route page — two copies that would drift. Extract assetProducers(graph, selection) into graphTraversal.ts and use it from both, keeping the dev view and route page in lockstep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): only overwrite AGENTS.md/CLAUDE.md when it's the exact generated pointer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): wire local-dev runs into the selected-node runs pane

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): exclude data_upload/webhook entrypoints from auto CLI runs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): --upload binds an object to a data_upload/webhook entry point

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): add "Run + downstream" to the dev preview detail form

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): cut non-autorun triggers on all run paths; multi-binding --upload

Address CI review: apply the data_upload/webhook/event barrier cut to the
single-root and bounded (--from/--to) paths, not just whole-pipeline; accumulate
repeatable --upload bindings per script (were overwritten); scope dev upload keys
by script+param to avoid basename clobbering; drop <script> from help text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): reseed dev run form when a local edit changes the script's args

The read-only pane is keyed on script.path only, so in /pipeline_dev the selected
node re-resolves on every WS bundle without remounting; PipelineScriptView cloned
script.schema once, so adding/removing args left the run form on a stale schema
(could run with missing inputs). Extract PipelineRunForm (owns the SchemaForm
clone) and key it on the serialized schema: a real arg change reseeds the form,
an unchanged re-resolve keeps in-progress input.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): don't cut a scheduled/manual root that also has a non-autorun trigger

Address Codex P1: the barrier set subtracted only --upload-bound scripts, so a
script with both `// on schedule` and `// on data_upload` resolved as the start
yet was also a barrier — reachableCutting skipped it, giving an empty run plan.
Subtract all valid starts (schedule/manual roots + bound handlers) from barriers:
a legitimately-scheduled root runs on its schedule path even if it also carries a
caller-input trigger; pure input-only roots stay cut. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): deployed non-autorun enrichment, s3:// storage, --to cut accounting, tag regex

Address CI review (Codex P1/P1/P2, Pi P2):
- Deployed `pipeline run` recovers marker-only data_upload/webhook/email triggers
  from script bodies (like the `show` path) so input-only entrypoints are cut
  instead of auto-run empty on the deployed graph.
- `--upload s3://<storage>/<key>` keeps the named storage (authority) instead of
  folding it into the key, matching the S3Object round-trip convention.
- Bounded `--to` targets cut by a barrier are reported in droppedEnds (+warning),
  not reachableEnds.
- fallbackParse `// tag` matches a single token (\S+), rejecting multi-word prose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): header-only deployed marker scan, fail-closed enrichment, default-storage s3 keys

Address CI review (Codex P2, cubic P1/P1/P2):
- Deployed marker recovery scans the LEADING comment header only (shared
  recoverHeaderMarkers helper, reused by the show enrichment too) so a body
  comment `// on data_upload` can't inject a phantom trigger and over-cut.
- Deployed run enrichment fails CLOSED: a script-body fetch error aborts the run
  instead of silently letting an input-only entrypoint run with empty args.
- Revert `--upload s3://` to default-storage whole-path keys (matching pipeline
  `s3://` asset-URI semantics); named-storage authority-splitting broke nested
  default keys like `s3://raw/2026/events.csv`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): reject trailing content on fallback native markers; trim s3:/// key

Address CI review (Codex P2, cubic P3):
- fallbackParse now requires a native marker (`// on data_upload`) to stand alone;
  a line with trailing content (`// on data_upload f/foo`, `# on kafka topic`) is
  rejected, matching the canonical parser and keeping local/deployed parity.
- s3UriKey trims a leading slash so the canonical empty-authority default form
  `s3:///key` doesn't leak a leading slash into the object key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): persist dev WS token per-port so reconnect survives a CLI restart

Address Codex P2: the /pipeline_dev auto-reconnect reuses the ws_token from the
page URL, but `pipeline dev` minted a fresh random token each start, so a restart
on the same port left the open page rejected by verifyClient forever. Persist the
token per-port under the user-private config dir (0600) and reuse it on restart,
so an already-open page reconnects — matching the reconnect behavior's intent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): scope persisted dev WS token by workspace+folder+port

Address cubic P2: keying the persisted token by port alone let a stale browser
tab from a previous folder's session on the same port reconnect and receive a
different folder's source. Scope the token file by workspace+folder+port so a
same-session restart still reconnects, but a different folder on the same port
gets a distinct token that rejects stale cross-folder tabs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): caller args can't override skip-dispatch guard; hash the dev token key

Address CI review (Codex P1, cubic P2):
- makeLaunch / CLI run build args with `_wmill_skip_asset_dispatch` LAST (and drop
  any caller-supplied copy) so a run-form/`--upload` arg can't re-enable backend
  asset dispatch while the client orchestrates the cascade (double-run / running
  deployed subscribers from a local preview). Adds a cascadeRun guard test.
- Dev WS token file key is a sha256 of NUL-delimited workspace+folder+port, so
  different folders (`a/b` vs `a_b`) can't collide onto the same token file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipeline): canonical s3://storage/key --upload parsing; scope dev token by remote+root

Address Codex P1/P1:
- Restore canonical S3Object URI parsing for `--upload` s3 sources, matching the
  frontend's `parseS3Object` (`s3://<storage>/<key>`, empty authority ⇒ default,
  `s3:///key`/`s3:///nested/key` for the default store). `s3://secondary/k.csv` →
  `{ s3: "k.csv", storage: "secondary" }` so a named-storage object is read from
  the right store. (This is the canonical convention; the default-storage nested
  key is served by the `s3:///` form.)
- Scope the persisted dev WS token by remote+workspace+root+folder+port (was
  workspace+folder+port), so two profiles on different remotes (or local checkouts)
  with the same workspace/folder/port don't share a token — a stale tab can't
  reconnect across a workspace/remote boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:41:36 +02:00

45 KiB

Windmill CLI Commands

The Windmill CLI (wmill) provides commands for managing scripts, flows, apps, and other resources.

Global Options

  • --workspace <workspace:string> - Specify the target workspace. This overrides the default workspace.
  • --debug --verbose - Show debug/verbose logs
  • --show-diffs - Show diff informations when syncing (may show sensitive informations)
  • --token <token:string> - Specify an API token. This will override any stored token.
  • --base-url <baseUrl:string> - Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used.
  • --config-dir <configDir:string> - Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location.

Commands

app

app related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • app list - list all apps
    • --json - Output as JSON (for piping to jq)
  • app get <path:string> - get an app's details
    • --json - Output as JSON (for piping to jq)
  • app push [file_path:string] [remote_path:string] - push a local app. With no args, infers the app from the current directory and the remote path from its location relative to wmill.yaml.
  • app dev [app_folder:string] - Start a development server for building apps with live reload and hot module replacement
    • --port <port:number> - Port to run the dev server on (will find next available port if occupied)
    • --host <host:string> - Host to bind the dev server to
    • --entry <entry:string> - Entry point file (default: index.ts for Svelte/Vue, index.tsx otherwise)
    • --no-open - Don't automatically open the browser
  • app lint [app_folder:string] - Lint a raw app folder to validate structure and buildability
    • --fix - Attempt to fix common issues (not implemented yet)
  • app new - create a new raw app from a template
    • --summary <summary:string> - App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.
    • --path <path:string> - App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.
    • --framework <framework:string> - Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.
    • --datatable <datatable:string> - Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.
    • --schema <schema:string> - Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.
    • --overwrite - Overwrite the target directory if it already exists, without prompting.
    • --no-open-in-desktop - Do not prompt to open the new app in Claude Desktop.
  • app generate-agents [app_folder:string] - regenerate AGENTS.md and DATATABLES.md from remote workspace
  • app set-permissioned-as <path:string> <email:string> - Set the on_behalf_of_email for an app (requires admin or wm_deployers group)

audit

View audit logs (requires admin)

Subcommands:

  • audit list - List audit log entries
  • audit get <id:string> - Get a specific audit log entry
    • --json - Output as JSON (for piping to jq)

config

Show all available wmill.yaml configuration options

Options:

  • --json - Output as JSON for programmatic consumption

Subcommands:

  • config migrate - Migrate wmill.yaml from gitBranches/environments to workspaces format

datatable

datatable related commands

Subcommands:

  • datatable list - list all datatables in the workspace
    • --json - Output as JSON (for piping to jq)
  • datatable run <sql:string> - run a SQL query on a datatable
    • -n --name <name:string> - Datatable name (default: main)
    • -s --silent - Output only the final result as JSON. Useful for scripting.
  • datatable create [name:string] - register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable://
    • --resource <resource:string> - Back the datatable with an existing postgresql resource path instead of the instance database
    • --force - Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved)
  • datatable serve - Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string
    • --port <port:number> - Port to listen on (default: first free port in 5433-5500)
    • --host <host:string> - Bind address (default: 127.0.0.1)
    • --password <password:string> - Password for Postgres clients (default: generate a random password at startup)
  • datatable psql - Start a serve listener and launch psql connected to it
    • -n --name <name:string> - Datatable to connect psql to (default: main)
    • --port <port:number> - Port the proxy listens on (default: first free port in 5433-5500)
    • --host <host:string> - Bind address for the proxy (default: 127.0.0.1)
    • --password <password:string> - Password for the temporary Postgres proxy (default: generate a random password at startup)

dependencies

workspace dependencies related commands

Alias: deps

Subcommands:

  • dependencies push <file_path:string> - Push workspace dependencies from a local file

dev

Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace — use wmill sync push for that.

Options:

  • --includes <pattern...:string> - Filter paths given a glob pattern or path
  • --proxy-port <port:number> - Port for a localhost reverse proxy to the remote Windmill server
  • --path <path:string> - Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)
  • --no-open - Do not open the browser automatically

docs

Search Windmill documentation.

Arguments: <query:string>

Options:

  • --json - Output results as JSON.

ducklake

ducklake related commands

Subcommands:

  • ducklake list - list all ducklakes in the workspace
    • --json - Output as JSON (for piping to jq)
  • ducklake run <sql:string> - run a SQL query on a ducklake
    • -n --name <name:string> - Ducklake name (default: main)
    • -s --silent - Output only the final result as JSON. Useful for scripting.

flow

flow related commands

Options:

  • --show-archived - Enable archived flows in output
  • --json - Output as JSON (for piping to jq)

Subcommands:

  • flow list - list all flows
    • --show-archived - Enable archived flows in output
    • --json - Output as JSON (for piping to jq)
  • flow get <path:string> - get a flow's details
    • --json - Output as JSON (for piping to jq)
  • flow push <file_path:string> <remote_path:string> - push a local flow spec. This overrides any remote versions.
    • --message <message:string> - Deployment message
  • flow run <path:string> - run a flow by path.
    • -d --data <data:string> - Inputs specified as a JSON string or a file using @ or stdin using @-.
    • -s --silent - Do not ouput anything other then the final output. Useful for scripting.
  • flow preview <flow_path:string> - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow).
    • -d --data <data:string> - Inputs specified as a JSON string or a file using @ or stdin using @-.
    • -s --silent - Do not output anything other then the final output. Useful for scripting.
    • --remote - Use deployed workspace scripts for PathScript steps instead of local files.
    • --step <step_id:string> - Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does.
  • flow new <flow_path:string> - create a new empty flow
    • --summary <summary:string> - flow summary
    • --description <description:string> - flow description
  • flow bootstrap <flow_path:string> - create a new empty flow (alias for new)
    • --summary <summary:string> - flow summary
    • --description <description:string> - flow description
  • flow history <path:string> - Show version history for a flow
    • --json - Output as JSON (for piping to jq)
  • flow show-version <path:string> <version:string> - Show a specific version of a flow
    • --json - Output as JSON (for piping to jq)
  • flow set-permissioned-as <path:string> <email:string> - Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)

folder

folder related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • folder list - list all folders
    • --json - Output as JSON (for piping to jq)
  • folder get <name:string> - get a folder's details
    • --json - Output as JSON (for piping to jq)
  • folder new <name:string> - create a new folder locally
    • --summary <summary:string> - folder summary
  • folder push <name:string> - push a local folder to the remote by name. This overrides any remote versions.
  • folder add-missing - create default folder.meta.yaml for all subdirectories of f/ that are missing one
    • -y, --yes - skip confirmation prompt
  • folder show-rules <name:string> - Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.
    • --test-path <path:string> - Test which rule matches this item path (e.g. f/prod/jobs/my_script)
    • --json - Output as JSON

generate-metadata

Regenerate stale local locks and script schemas and refresh wmill-lock.yaml content hashes (scripts, flows, apps). Writes local files only, not a deploy. Run it after edits that add or remove imports or change a script's arguments, so the lock, the auto-generated UI schema, and wmill-lock.yaml stay in sync.

Arguments: [folder:string]

Options:

  • --yes - Skip confirmation prompt
  • --dry-run - Show what would be updated without making changes
  • --lock-only - Re-generate only the lock files
  • --schema-only - Re-generate only script schemas (skips flows and apps)
  • --skip-scripts - Skip processing scripts
  • --skip-flows - Skip processing flows
  • --skip-apps - Skip processing apps
  • --strict-folder-boundaries - Only update items inside the specified folder (requires folder argument)
  • --parallel <n:number> - Number of items to process in parallel
  • -i --includes <patterns:file[]> - Comma separated patterns to specify which files to include
  • -e --excludes <patterns:file[]> - Comma separated patterns to specify which files to exclude

Subcommands:

  • generate-metadata rehash [folder:string] - Refresh wmill-lock.yaml content hashes from the on-disk .lock and .script.yaml without re-resolving dependencies or hitting the backend. Use when those files are already correct and only the hashes need updating: bootstrapping missing entries or recovering from hash drift.
    • --skip-scripts - Skip processing scripts
    • --skip-flows - Skip processing flows
    • --skip-apps - Skip processing apps
    • --parallel <n:number> - Number of items to process in parallel
    • -i --includes <patterns:file[]> - Comma separated patterns to specify which files to include
    • -e --excludes <patterns:file[]> - Comma separated patterns to specify which files to exclude

gitsync-settings

Manage git-sync settings between local wmill.yaml and Windmill backend

Subcommands:

  • gitsync-settings pull - Pull git-sync settings from Windmill backend to local wmill.yaml
    • --repository <repo:string> - Specify repository path (e.g., u/user/repo)
    • --default - Write settings to top-level defaults instead of overrides
    • --replace - Replace existing settings (non-interactive mode)
    • --override - Add branch-specific override (non-interactive mode)
    • --diff - Show differences without applying changes
    • --json-output - Output in JSON format
    • --with-backend-settings <json:string> - Use provided JSON settings instead of querying backend (for testing)
    • --yes - Skip interactive prompts and use default behavior
    • --promotion <branch:string> - Use promotionOverrides from the specified branch instead of regular overrides
  • gitsync-settings push - Push git-sync settings from local wmill.yaml to Windmill backend
    • --repository <repo:string> - Specify repository path (e.g., u/user/repo)
    • --diff - Show what would be pushed without applying changes
    • --json-output - Output in JSON format
    • --with-backend-settings <json:string> - Use provided JSON settings instead of querying backend (for testing)
    • --yes - Skip interactive prompts and use default behavior
    • --promotion <branch:string> - Use promotionOverrides from the specified branch instead of regular overrides

group

Manage workspace groups

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • group list - List all groups in the workspace
    • --json - Output as JSON (for piping to jq)
  • group get <name:string> - Get group details and members
    • --json - Output as JSON (for piping to jq)
  • group create <name:string> - Create a new group
    • --summary <summary:string> - Group summary/description
  • group delete <name:string> - Delete a group
  • group add-user <name:string> <username:string> - Add a user to a group
  • group remove-user <name:string> <username:string> - Remove a user from a group

hub

Hub related commands. EXPERIMENTAL. INTERNAL USE ONLY.

Subcommands:

  • hub pull - pull any supported definitions. EXPERIMENTAL.

init

Bootstrap a windmill project with a wmill.yaml file

Options:

  • --use-default - Use default settings without checking backend
  • --use-backend - Use backend git-sync settings if available
  • --repository <repo:string> - Specify repository path (e.g., u/user/repo) when using backend settings
  • --bind-profile - Automatically bind active workspace profile to current Git branch
  • --no-bind-profile - Skip workspace profile binding prompt

instance

sync local with a remote instance or the opposite (push or pull)

Subcommands:

  • instance add [instance_name:string] [remote:string] [token:string] - Add a new instance
  • instance remove <instance:string:instance> - Remove an instance
  • instance switch <instance:string:instance> - Switch the current instance
  • instance pull - Pull instance settings, users, configs, instance groups and overwrite local
    • --yes - Pull without needing confirmation
    • --dry-run - Perform a dry run without making changes
    • --skip-users - Skip pulling users
    • --skip-settings - Skip pulling settings
    • --skip-configs - Skip pulling configs (worker groups)
    • --skip-groups - Skip pulling instance groups
    • --include-workspaces - Also pull workspaces
    • --folder-per-instance - Create a folder per instance
    • --instance <instance:string> - Name of the instance to pull from, override the active instance
    • --prefix <prefix:string> - Prefix of the local workspaces to pull, used to create the folders when using --include-workspaces
    • --prefix-settings - Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance
  • instance push - Push instance settings, users, configs, group and overwrite remote
    • --yes - Push without needing confirmation
    • --dry-run - Perform a dry run without making changes
    • --skip-users - Skip pushing users
    • --skip-settings - Skip pushing settings
    • --skip-configs - Skip pushing configs (worker groups)
    • --skip-groups - Skip pushing instance groups
    • --include-workspaces - Also push workspaces
    • --folder-per-instance - Create a folder per instance
    • --instance <instance:string> - Name of the instance to push to, override the active instance
    • --prefix <prefix:string> - Prefix of the local workspaces folders to push
    • --prefix-settings - Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance
  • instance whoami - Display information about the currently logged-in user
  • instance get-config - Dump the current instance config (global settings + worker configs) as YAML
    • -o, --output-file <file:string> - Write YAML to a file instead of stdout
    • --show-secrets - Include sensitive fields (license key, JWT secret) without prompting
    • --instance <instance:string> - Name of the instance, override the active instance
  • instance connect-slack - Non-interactively connect Slack at the instance level using a pre-minted bot token (xoxb-...). Produces the same artifacts as the UI OAuth flow: global_settings 'slack' row + encrypted f/slack_bot/global_bot_token variable and resource in the admins workspace.
    • --bot-token <bot_token:string> - Slack bot token (xoxb-...)
    • --team-id <team_id:string> - Slack team id
    • --team-name <team_name:string> - Slack team name
    • --instance <instance:string> - Instance profile to connect against (defaults to the active instance)

job

Manage jobs (list, inspect, cancel)

Subcommands:

  • job list - List recent jobs
  • job get <id:string> - Get job details. For flows: shows step tree with sub-job IDs
    • --json - Output as JSON (for piping to jq)
  • job result <id:string> - Get the result of a completed job (machine-friendly)
  • job logs <id:string> - Get job logs. For flows: aggregates all step logs
  • job cancel <id:string> - Cancel a running or queued job
    • --reason <reason:string> - Reason for cancellation
  • job rerun <id:string> - Re-run a completed job with the same args. Prints the new job UUID on stdout.
  • job restart <id:string> - Restart a completed flow at a given top-level step. Prints the new flow job UUID on stdout.
    • --step <stepId:string> - Top-level step id to restart the flow from
    • --iteration <n:number> - For a top-level branchall or for-loop step, the iteration to restart at

jobs

Pull completed and queued jobs from workspace

Arguments: [workspace:string]

Options:

  • -c, --completed-output <file:string> - Completed jobs output file (default: completed_jobs.json)
  • -q, --queued-output <file:string> - Queued jobs output file (default: queued_jobs.json)
  • --skip-worker-check - Skip checking for active workers before export

Subcommands:

  • jobs pull
  • jobs push

lint

Validate Windmill flow, schedule, and trigger YAML files in a directory

Arguments: [directory:string]

Options:

  • --json - Output results in JSON format
  • --fail-on-warn - Exit with code 1 when warnings are emitted
  • --locks-required - Fail if scripts or flow inline scripts that need locks have no locks
  • -w, --watch - Watch for file changes and re-lint automatically

object-storage

Object storage (S3) related commands. Operates on the workspace's default object storage; use --storage to target a configured secondary storage.

Alias: s3

Subcommands:

  • object-storage list - List configured object storages for the workspace (default + secondary).
    • --json - Output as JSON (for piping to jq)
  • object-storage files [prefix:string] - List files in an object storage. Optionally filter by prefix.
    • --json - Output as JSON (for piping to jq)
    • --max-keys <maxKeys:number> - Page size (default 100)
    • --marker <marker:string> - Pagination marker from a previous response
    • --storage <storage:string> - Secondary storage name (omit for the workspace default)
  • object-storage upload <local_path:string> <file_key:string> - Upload a local file to object storage at the given file key.
    • --storage <storage:string> - Secondary storage name
    • --content-type <contentType:string> - Content-Type header to set on the object
    • --content-disposition <contentDisposition:string> - Content-Disposition header to set on the object
  • object-storage download <file_key:string> [output_path:string] - Download an object to a local file (or stdout). Default output path is the basename of the file key in the current directory.
    • --storage <storage:string> - Secondary storage name
    • --stdout - Write file contents to stdout instead of a file
  • object-storage delete <file_key:string> - Delete an object from object storage. Prompts for confirmation unless --yes is set.
    • --storage <storage:string> - Secondary storage name
    • --yes - Skip the confirmation prompt
  • object-storage move <src_file_key:string> <dest_file_key:string> - Move an object within the same storage (rename or relocate by key).
    • --storage <storage:string> - Secondary storage name
  • object-storage info <file_key:string> - Show metadata (size, mime, last-modified) for an object.
    • --json - Output as JSON (for piping to jq)
    • --storage <storage:string> - Secondary storage name
  • object-storage preview <file_key:string> - Preview the contents of an object (text/CSV). Use --bytes-from / --bytes-length to peek at a slice of binary files.
    • --storage <storage:string> - Secondary storage name
    • --mime <mime:string> - Override the detected mime type (e.g. text/csv)
    • --bytes-from <bytesFrom:number> - Start offset in bytes
    • --bytes-length <bytesLength:number> - Number of bytes to read
    • --csv-separator <csvSeparator:string> - CSV column separator (default ,)
    • --csv-header - Treat the first CSV row as a header

pipeline

inspect asset-driven pipelines (scripts marked // pipeline, wired by // on <spec> annotations)

Subcommands:

  • pipeline list - list pipeline folders in the workspace
    • --json - Output as JSON (for piping to jq)
  • pipeline show <folder:string> - render a pipeline folder's DAG (sources, lineage, subscriptions) in the terminal
    • --json - Output the raw asset graph as JSON
    • --local - Build the graph from local working-tree files (// pipeline scripts) instead of the deployed workspace — no deploy needed.
  • pipeline run <folder:string> - run a bounded cascade: from a schedule/manual root, fan downstream up to the --to end node(s)
    • --from <script:string> - Start script (short name or path). Defaults to the folder's sole schedule/manual root.
    • --to <node:string> - End node(s) to stop at — script names/paths or asset URIs (e.g. datatable://main/staged). Repeatable or comma-separated. Omit to run the full downstream.
    • --dry-run - Print the topological run plan without executing.
    • --json - Output the plan as JSON (for piping to jq).
    • --local - Run the local working-tree scripts via preview (no deploy) instead of the deployed versions; the graph is built from local files.
    • --upload <binding:string> - Bind an object to a data_upload/webhook entry point so it runs in the cascade, as SCRIPT[:PARAM]=SOURCE (SOURCE is a local file or an s3://key). Local files are uploaded to the workspace store; the S3Object param is inferred when the script has exactly one. Repeatable.
  • pipeline docs <folder:string> - generate PIPELINE.md (+ AGENTS.md pointer) describing a folder's pipeline graph and datatable schemas, for an editor / agentic loop
    • --local - Build the graph from local working-tree files instead of the deployed workspace.
  • pipeline dev [folder:string] - Live-preview a data pipeline from local files: watch an f/<folder> of // pipeline scripts, push the working-tree graph to the dev page, and run the cascade via preview (no deploy).
    • --port <port:number> - Port for the dev WebSocket server.
    • --no-open - Do not open the browser automatically.
    • --frontend <origin:string> - Origin serving the /pipeline_dev page (e.g. http://localhost:3000 for a locally-run frontend). Defaults to the workspace remote; use it when the remote's deployed frontend predates the dev page.

protection-rules

Sync workspace protection rules between protection-rules.yaml and Windmill. The file is keyed by workspace name; keys must match wmill.yaml 'workspaces'.

Subcommands:

  • protection-rules pull [workspace:string] - Pull protection rules from Windmill into protection-rules.yaml for a workspace
    • --all - Pull every workspace defined in wmill.yaml
    • --dry-run - Show what would change without writing the file
    • --json-output - Output in JSON format
  • protection-rules push [workspace:string] - Push protection rules from protection-rules.yaml to Windmill for a workspace (full reconcile: creates, updates, and deletes)
    • --all - Push every workspace defined in protection-rules.yaml
    • --dry-run - Show what would change without applying
    • --json-output - Output in JSON format
    • --yes - Skip the confirmation prompt (including deletions)

queues

List all queues with their metrics

Arguments: [workspace:string] the optional workspace to filter by (default to all workspaces)

Options:

  • --instance [instance] - Name of the instance to push to, override the active instance
  • --base-url [baseUrl] - If used with --token, will be used as the base url for the instance

refresh

Refresh wmill-managed project files (AGENTS.wmill.md, skills, tsconfig.wmill.json)

Subcommands:

  • refresh prompts - Refresh AGENTS.wmill.md and managed skills. User-owned AGENTS.md and CLAUDE.md are never overwritten unless you opt in.
    • --yes - Non-interactive: append the @AGENTS.wmill.md include to an existing AGENTS.md / CLAUDE.md without prompting. Without it, a non-interactive run leaves an unlinked file untouched.
  • refresh tsconfig - Refresh the wmill-managed tsconfig.wmill.json (and Deno import map for Deno projects)
    • --yes - Non-interactive: wire an existing custom tsconfig.json/deno.json to the managed file without prompting (a previously-generated config is always migrated automatically).

resource

resource related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • resource list - list all resources
    • --json - Output as JSON (for piping to jq)
  • resource get <path:string> - get a resource's details
    • --json - Output as JSON (for piping to jq)
  • resource new <path:string> - create a new resource locally
  • resource push <file_path:string> <remote_path:string> - push a local resource spec. This overrides any remote versions.

resource-type

resource type related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • resource-type list - list all resource types
    • --schema - Show schema in the output
    • --json - Output as JSON (for piping to jq)
  • resource-type get <path:string> - get a resource type's details
    • --json - Output as JSON (for piping to jq)
  • resource-type new <name:string> - create a new resource type locally
  • resource-type push <file_path:string> <name:string> - push a local resource spec. This overrides any remote versions.
  • resource-type generate-namespace - Create a TypeScript definition file with the RT namespace generated from the resource types

schedule

schedule related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • schedule list - list all schedules
    • --json - Output as JSON (for piping to jq)
  • schedule get <path:string> - get a schedule's details
    • --json - Output as JSON (for piping to jq)
  • schedule new <path:string> - create a new schedule locally
  • schedule push <file_path:string> <remote_path:string> - push a local schedule spec. This overrides any remote versions.
  • schedule enable <path:string> - Enable a schedule
    • --force - Bypass the fork-conflict warning when the parent workspace has the same schedule (acknowledges that both crons will fire)
  • schedule disable <path:string> - Disable a schedule
  • schedule set-permissioned-as <path:string> <email:string> - Set the email (run-as user) for a schedule (requires admin or wm_deployers group)

script

script related commands

Options:

  • --show-archived - Show archived scripts instead of active ones
  • --json - Output as JSON (for piping to jq)

Subcommands:

  • script list - list all scripts
    • --show-archived - Show archived scripts instead of active ones
    • --json - Output as JSON (for piping to jq)
  • script push <path:file> - push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh)
    • --message <message:string> - Deployment message
  • script get <path:file> - get a script's details
    • --json - Output as JSON (for piping to jq)
  • script show <path:file> - show a script's content (alias for get)
  • script run <path:file> - run a script by path
    • -d --data <data:file> - Inputs specified as a JSON string or a file using @ or stdin using @-.
    • -s --silent - Do not output anything other then the final output. Useful for scripting.
  • script preview <path:file> - preview a local script without deploying it. Supports both regular and codebase scripts.
    • -d --data <data:file> - Inputs specified as a JSON string or a file using @ or stdin using @-.
    • -s --silent - Do not output anything other than the final output. Useful for scripting.
  • script new <path:file> <language:string> - create a new script
    • --summary <summary:string> - script summary
    • --description <description:string> - script description
  • script bootstrap <path:file> <language:string> - create a new script (alias for new)
    • --summary <summary:string> - script summary
    • --description <description:string> - script description
  • script set-permissioned-as <path:string> <email:string> - Set the on_behalf_of_email for a script (requires admin or wm_deployers group)
  • script history <path:string> - show version history for a script
    • --json - Output as JSON (for piping to jq)

sync

sync local with a remote workspaces or the opposite (push or pull)

Subcommands:

  • sync pull - Pull any remote changes and apply them locally.
    • --yes - Pull without needing confirmation
    • --dry-run - Show changes that would be pulled without actually pushing
    • --plain-secrets - Pull secrets as plain text
    • --json - Use JSON instead of YAML
    • --skip-variables - Skip syncing variables (including secrets)
    • --skip-secrets - Skip syncing only secrets variables
    • --include-secrets - Include secrets in sync (overrides skipSecrets in wmill.yaml)
    • --skip-resources - Skip syncing resources
    • --skip-resource-types - Skip syncing resource types
    • --skip-scripts - Skip syncing scripts
    • --skip-flows - Skip syncing flows
    • --skip-apps - Skip syncing apps
    • --skip-folders - Skip syncing folders
    • --skip-workspace-dependencies - Skip syncing workspace dependencies
    • --skip-scripts-metadata - Skip syncing scripts metadata, focus solely on logic
    • --include-schedules - Include syncing schedules
    • --include-triggers - Include syncing triggers
    • --include-users - Include syncing users
    • --include-groups - Include syncing groups
    • --include-settings - Include syncing workspace settings
    • --include-key - Include workspace encryption key
    • --skip-branch-validation - Skip git branch validation and prompts
    • --json-output - Output results in JSON format
    • -i --includes <patterns:file[]> - Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Overrides wmill.yaml includes
    • -e --excludes <patterns:file[]> - Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes
    • --extra-includes <patterns:file[]> - Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy
    • --repository <repo:string> - Specify repository path (e.g., u/user/repo) when multiple repositories exist
    • --promotion <branch:string> - Use promotionOverrides from the specified branch instead of regular overrides
    • --branch, --env <branch:string> - [Deprecated: use --workspace] Override the current git branch/environment
  • sync push - Push any local changes and apply them remotely.
    • --yes - Push without needing confirmation
    • --dry-run - Show changes that would be pushed without actually pushing
    • --plain-secrets - Push secrets as plain text
    • --json - Use JSON instead of YAML
    • --skip-variables - Skip syncing variables (including secrets)
    • --skip-secrets - Skip syncing only secrets variables
    • --include-secrets - Include secrets in sync (overrides skipSecrets in wmill.yaml)
    • --skip-resources - Skip syncing resources
    • --skip-resource-types - Skip syncing resource types
    • --skip-scripts - Skip syncing scripts
    • --skip-flows - Skip syncing flows
    • --skip-apps - Skip syncing apps
    • --skip-folders - Skip syncing folders
    • --skip-workspace-dependencies - Skip syncing workspace dependencies
    • --skip-scripts-metadata - Skip syncing scripts metadata, focus solely on logic
    • --include-schedules - Include syncing schedules
    • --include-triggers - Include syncing triggers
    • --include-users - Include syncing users
    • --include-groups - Include syncing groups
    • --include-settings - Include syncing workspace settings
    • --include-key - Include workspace encryption key
    • --skip-reencrypt-on-key-change - When the pushed encryption key differs from the remote, do NOT re-encrypt existing remote secrets. Only safe if they are already encrypted with the new key (e.g. workspace/instance migration). Default is to re-encrypt.
    • --skip-branch-validation - Skip git branch validation and prompts
    • --json-output - Output results in JSON format
    • -i --includes <patterns:file[]> - Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)
    • -e --excludes <patterns:file[]> - Comma separated patterns to specify which file to NOT take into account.
    • --extra-includes <patterns:file[]> - Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy
    • --message <message:string> - Include a message that will be added to all scripts/flows/apps updated during this push
    • --parallel <number> - Number of changes to process in parallel
    • --repository <repo:string> - Specify repository path (e.g., u/user/repo) when multiple repositories exist
    • --branch, --env <branch:string> - [Deprecated: use --workspace] Override the current git branch/environment
    • --lint - Run lint validation before pushing
    • --locks-required - Fail if scripts or flow inline scripts that need locks have no locks
    • --auto-metadata - Automatically regenerate stale metadata (locks and schemas) before pushing
    • --accept-overriding-permissioned-as-with-self - Accept that items with a different permissioned_as will be updated with your own user

token

Manage API tokens

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • token list - List API tokens
    • --json - Output as JSON (for piping to jq)
  • token create - Create a new API token
    • --label <label:string> - Token label
    • --expiration <expiration:string> - Token expiration (ISO 8601 timestamp)
  • token delete <token_prefix:string> - Delete a token by its prefix

trigger

trigger related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • trigger list - list all triggers
    • --json - Output as JSON (for piping to jq)
  • trigger get <path:string> - get a trigger's details
    • --json - Output as JSON (for piping to jq)
    • --kind <kind:string> - Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email). Recommended for faster lookup
  • trigger new <path:string> - create a new trigger locally
    • --kind <kind:string> - Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)
  • trigger push <file_path:string> <remote_path:string> - push a local trigger spec. This overrides any remote versions.
  • trigger set-permissioned-as <path:string> <email:string> - Set the email (run-as user) for a trigger (requires admin or wm_deployers group)
    • --kind <kind:string> - Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)

user

user related commands

Subcommands:

  • user add <email:string> [password:string] - Create a user
    • --superadmin - Specify to make the new user superadmin.
    • --company <company:string> - Specify to set the company of the new user.
    • --name <name:string> - Specify to set the name of the new user.
  • user remove <email:string> - Delete a user
  • user create-token - Create a new API token for the authenticated user
    • --email <email:string> - Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.
    • --password <password:string> - Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.

variable

variable related commands

Options:

  • --json - Output as JSON (for piping to jq)

Subcommands:

  • variable list - list all variables
    • --json - Output as JSON (for piping to jq)
  • variable get <path:string> - get a variable's details
    • --json - Output as JSON (for piping to jq)
  • variable new <path:string> - create a new variable locally
  • variable push <file_path:string> <remote_path:string> - Push a local variable spec. This overrides any remote versions.
    • --plain-secrets - Push secrets as plain text
  • variable add <value:string> <remote_path:string> - Create a new variable on the remote. This will update the variable if it already exists.
    • --yes - Skip confirmation prompt when updating an existing variable
    • --secret - Mark the variable as secret (default when creating a new variable)
    • --no-secret - Mark the variable as non-secret (when updating, the existing setting is preserved if neither --secret nor --no-secret is passed)
    • --description <description:string> - Set the variable description (when updating, the existing description is preserved if not passed)
    • --plain-secrets - Push secrets as plain text
    • --public - Legacy option, use --no-secret instead

version

Show version information

worker-groups

display worker groups, pull and push worker groups configs

Subcommands:

  • worker-groups pull - Pull worker groups (similar to wmill instance pull --skip-users --skip-settings --skip-groups)
    • --instance - Name of the instance to push to, override the active instance
    • --base-url - Base url to be passed to the instance settings instead of the local one
    • --yes - Pull without needing confirmation
  • worker-groups push - Push worker groups (similar to wmill instance push --skip-users --skip-settings --skip-groups)
    • --instance [instance] - Name of the instance to push to, override the active instance
    • --base-url [baseUrl] - If used with --token, will be used as the base url for the instance
    • --yes - Push without needing confirmation

workers

List all workers grouped by worker groups

Options:

  • --instance [instance] - Name of the instance to push to, override the active instance
  • --base-url [baseUrl] - If used with --token, will be used as the base url for the instance

workspace

workspace related commands

Alias: profile

Subcommands:

  • workspace switch <workspace_name:string:workspace> - Switch to another workspace
  • workspace add [workspace_name:string] [workspace_id:string] [remote:string] - Add a workspace
    • -c --create - Create the workspace if it does not exist
    • --create-workspace-name <workspace_name:string> - Specify the workspace name. Ignored if --create is not specified or the workspace already exists. Will default to the workspace id.
    • --create-username <username:string> - Specify your own username in the newly created workspace. Ignored if --create is not specified, the workspace already exists or automatic username creation is enabled on the instance.
  • workspace remove <workspace_name:string> - Remove a workspace
  • workspace whoami - Show the currently active user
  • workspace list - List local workspace profiles
  • workspace list-remote - List workspaces on the remote server that you have access to
    • --as-superadmin - List ALL workspaces on the instance (requires the token to belong to a superadmin/devops user)
  • workspace list-forks - List forked workspaces on the remote server
  • workspace bind - Create or update a workspace entry in wmill.yaml from the active profile
    • --workspace <name:string> - Workspace name (default: current branch or workspaceId)
    • --branch <branch:string> - Git branch to associate (default: workspace name)
  • workspace unbind - Remove baseUrl and workspaceId from a workspace entry
    • --workspace <name:string> - Workspace to unbind
  • workspace fork [workspace_name:string] [workspace_id:string] - Create a forked workspace
    • --create-workspace-name <workspace_name:string> - Specify the workspace name. Ignored if --create is not specified or the workspace already exists. Will default to the workspace id.
    • --color <color:string> - Workspace color (hex code, e.g. #ff0000)
    • --datatable-behavior <behavior:string> - How to handle datatables: skip, schema_only, or schema_and_data (default: interactive prompt)
    • --from-branch <branch:string> - Non-interactive override for the 'turn my current working branch into the fork' workflow: base the fork on (its bound workspace is the parent) and rename the current branch onto wm-fork//. Usually unneeded — from a working branch wmill workspace fork offers this interactively; from a base branch it creates a fresh fork branch.
    • -y --yes - Skip interactive prompts (defaults datatable behavior to 'skip'). On a non-base branch, requires --from-branch since the base branch can't be prompted for.
  • workspace delete-fork <fork_name:string> - Delete a forked workspace and git branch
    • -y --yes - Skip confirmation prompt
  • workspace merge - Compare and deploy changes between a fork and its parent workspace
    • --direction <direction:string> - Deploy direction: to-parent or to-fork
    • --all - Deploy all changed items including conflicts
    • --skip-conflicts - Skip items modified in both workspaces
    • --include <items:string> - Comma-separated kind:path items to include (e.g. script:f/test/main,flow:f/my/flow)
    • --exclude <items:string> - Comma-separated kind:path items to exclude
    • --preserve-on-behalf-of - Preserve original on_behalf_of/permissioned_as values
    • -y --yes - Non-interactive mode (deploy without prompts)
  • workspace connect-slack - Non-interactively connect Slack to the active workspace using a pre-minted bot token (xoxb-...). Produces the same artifacts as the UI OAuth flow: workspace_settings fields, g/slack group, f/slack_bot folder, and the encrypted bot token variable + resource at f/slack_bot/bot_token.
    • --bot-token <bot_token:string> - Slack bot token (xoxb-...)
    • --team-id <team_id:string> - Slack team id
    • --team-name <team_name:string> - Slack team name
  • workspace disconnect-slack - Clear slack_team_id / slack_name on the active workspace (marks the workspace as disconnected). Does NOT remove the bot token variable/resource/folder/group — delete those from the local sync folder and run 'wmill sync push' to tear them down. Does NOT remove the workspace-level OAuth override — set slack_oauth_client_id/_secret to '' in settings.yaml and push.

Object Storage CLI

wmill object-storage (alias wmill s3) exposes the workspace's object storage (S3-compatible: AWS S3, MinIO, GCS, R2, Azure Blob) over the per-workspace /job_helpers/* endpoints.

Key concepts (not obvious from per-command --help)

  • file_key is the path inside the bucket (e.g. reports/2026-05/orders.csv), not a Windmill path. Do NOT pass u/... or f/... here — those are Windmill paths to scripts/flows/resources, unrelated to objects in the bucket.
  • Scope is the active workspace. Object storage is configured per-workspace (default storage + optional secondary storages). Switching workspaces switches which bucket the commands target.
  • --storage <name> targets a secondary storage configured on the workspace. Omit it to use the workspace's default object storage. Use wmill object-storage list to discover configured storages.
  • preview vs download: preview returns a peek (CSV first rows, text content, or a byte slice via --bytes-from/--bytes-length) without writing to disk. Use download when you want the full file on disk.

Choosing a subcommand

  • Look at what's there: wmill object-storage files [prefix] (alias ls) — paginated, use --marker to continue.
  • Inspect one file: wmill object-storage info <file_key> for size/mime/last-modified, wmill object-storage preview <file_key> for content peek.
  • Move data in: wmill object-storage upload <local_path> <file_key> — set --content-type if the receiver cares (e.g. text/csv).
  • Move data out: wmill object-storage download <file_key> [output_path]--stdout to pipe.
  • Reorganize: wmill object-storage move <src> <dest> (same storage), wmill object-storage delete <file_key> (interactive confirm unless --yes).