* feat(cli): non-interactive Slack connect/disconnect
Extract create_slack_workspace_artifacts / create_slack_instance_artifacts
from the browser OAuth callbacks and expose them via two new endpoints that
accept a pre-minted xoxb bot token:
- POST /w/{workspace}/workspaces/connect_slack (admin)
- POST /oauth/connect_slack_instance (super-admin)
Both produce bit-for-bit identical DB state to the UI browser flow.
Wire three CLI commands as thin wrappers:
- wmill workspace connect-slack
- wmill workspace disconnect-slack
- wmill instance connect-slack
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): round-trip stability for workspace settings handlers
wmill sync push was destroying UI-configured error_handler/success_handler
state on every deploy. Two orthogonal bugs:
(a) pushWorkspaceSettings called editErrorHandler with `path: undefined`
when the YAML lacked the handler block, which the backend treats as a
clear — so syncing settings.yaml that didn't mention the handler wiped
the DB row. Fix: skip the call entirely when absent from YAML.
(b) edit_error_handler omitted muted_on_cancel / muted_on_user_path when
false, but the CLI always sends them, causing perpetual deepEqual
drift and a spurious editErrorHandler call on every sync push. Fix:
always persist both booleans.
migrateToGroupedFormat now preserves explicit `null` on
error_handler / success_handler as a "clear remote" signal distinct from
absence. Widen ErrorHandlerConfig | null / SuccessHandlerConfig | null to
make this explicit in the type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(cli): sync support for workspace-level Slack OAuth override
Add slack_oauth_client_id and slack_oauth_client_secret to the v2 tarball
export and to pushWorkspaceSettings, so the workspace-level OAuth override
is now fully managed as code through settings.yaml.
Semantics:
- both defined and truthy → setWorkspaceSlackOauthConfig (upsert)
- both defined but falsy (e.g. empty strings) and remote has a value
→ deleteWorkspaceSlackOauthConfig
- either omitted → leave remote alone ("not managed by git")
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(cli): normalize workspace settings sync to "omit = clear"
Earlier commits on this branch introduced an "omit = keep" rule for
error_handler / success_handler / slack_oauth_client_{id,secret} that
diverged from every other workspace setting (webhook, deploy_to, etc. all
treat YAML as canonical: absence = clear). Normalize:
- v2 tarball always emits these 4 fields (null when remote is NULL) so
round-trip is bijective and settings.yaml is a complete snapshot.
- pushWorkspaceSettings drops the absent-from-YAML guards; YAML is
canonical. Absence and explicit null both clear the remote — same rule
as every other field.
- set_slack_oauth_config / delete_slack_oauth_config now fire
handle_deployment_metadata so UI mutations reach git-sync-enabled
workspaces' committed settings.yaml.
Policy for users: pull before push (same as every other setting). On first
post-upgrade pull, explicit `null` keys appear for any workspace whose
handlers / oauth override are unset — one-time YAML diff, no semantic
change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(cli): add unit + integration coverage for Slack settings sync
Unit tests (settings_unit.test.ts): cover migrateToGroupedFormat preserving
explicit `null` on error_handler / success_handler, and passthrough of
slack_oauth_client_id / _secret (both populated and null values).
Integration tests (slack_settings_sync.test.ts, skipped on CI per the same
convention as datatable_settings_sync.test.ts): exercise the full backend
via withTestBackend to verify
1. pull emits null for unset error_handler / success_handler /
slack_oauth_client_id / _secret;
2. round-trip with all-null handlers is idempotent;
3. push of populated slack_oauth_config upserts;
4. omitting the slack_oauth keys from YAML clears remote (universal
"omit = clear" rule);
5. explicit null error_handler in YAML clears remote;
6. round-trip preserves a populated error_handler exactly, including the
always-persisted muted_on_cancel / muted_on_user_path booleans.
Also feature-gates `use crate::oauth2_oss::workspace_connect_slack` and its
route registration behind `cfg(feature = "oauth2")`: the import caused a
build failure on subsets of the workspace without the oauth2 feature,
surfaced by the integration test harness.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref to 59b6123
Pins windmill-ee-private to the tip of branch alp/slack_cli, which
contains the companion EE changes (helper extraction, non-interactive
Slack connect handlers, git-sync for Slack settings mutations).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update SQLx metadata
* chore: regenerate system prompts for new slack CLI commands
Captures the new workspace connect-slack, workspace disconnect-slack,
and instance connect-slack commands in the auto-generated files that
CI enforces via system_prompts/check-freshness.sh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to b4a5ca11e3b96ff03793c2bd396dbc1fe6ea1022
This commit updates the EE repository reference after PR #550 was merged in windmill-ee-private.
Previous ee-repo-ref: d7e44d0519327ec9077625130365e887826f324b
New ee-repo-ref: b4a5ca11e3b96ff03793c2bd396dbc1fe6ea1022
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Windmill CLI
A simple CLI allowing interactions with windmill from the command line.

You can find more information in Windmill Docs
Installation
Install the wmill CLI tool using npm install -g windmill-cli.
Update to the latest version using wmill upgrade.
Workspaces
To get started run wmill workspace add or use the instructions from the
workspace settings.
Running Flows & Scripts
Run a script or flow using wmill flow/script run u/username/path/to/script and
pass any inputs using --data + Inputs specified as a JSON string or a file
using @ <filename> or stdin using @-.
Curl-style syntax using -d @- for stdin or -d @<filename> is also supported.
Flow Steps and Logs will be streamed during execution automatically.
Pushing Resources, Scripts & More
The CLI can push specifications to a windmill instance. See the examples/ folder for formats.
Switch to a different workspace
wmill workspace switch <workspace_name>
Sync a workspace
Pull
wmill sync pull
Push
wmill sync push
We recommend using the --yaml option to use yaml instead of json as the encoding format. Yaml will be made the default soon.
Pushing individual files
You can push individual resources using
wmill <type> push <file_name> \<remote_name\>. This does not require a special
folder layout or file name, as this is given at runtime.
Listing
All commands support listing by just not providing a subcommand, ie
wmill script will result in a list of scripts. Some allow additional options,
learn about this by specifying --help.
User Management
You can add & remove users via wmill user add/remove, and list them using
wmill user
Pulling
You can pull the entire workspace using wmill pull
Completion
The CLI comes with completions out of the box via wmill completions <shell>.
(Via cliffy)
Bash
To enable bash completions add the following line to your ~/.bashrc:
source <(wmill completions bash)
Fish
To enable fish completions add the following line to your
~/.config/fish/config.fish:
source (wmill completions fish | psub)
Zsh
To enable zsh completions add the following line to your ~/.zshrc:
source <(wmill completions zsh)
Development
AI Guidance Variants
wmill init can now materialize alternate AI guidance bundles without changing
the generated defaults in the repo, but this is exposed as internal env-var
overrides rather than public CLI flags.
Examples:
WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills wmill init --use-default
WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills WMILL_INIT_AI_AGENTS_SOURCE=/path/to/AGENTS.md wmill init --use-default
WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills WMILL_INIT_AI_CLAUDE_SOURCE=/path/to/CLAUDE.md wmill init --use-default
This is the same guidance-writing path used by the benchmark CLI under
ai_evals/, so the benchmark harness and wmill init now generate the same
project guidance shape:
AGENTS.mdCLAUDE.md.claude/skills/*
Testing with a local windmill-yaml-validator
To test local changes to the validator before publishing, use npm link:
# In windmill-yaml-validator/
npm run build
npm link
# In cli/
npm link windmill-yaml-validator
Running Tests
Prerequisites:
- PostgreSQL running locally (default:
postgres://postgres:changeme@localhost:5432) - Rust toolchain installed
Run tests locally (full features):
bun test test/
Run tests in CI mode (minimal features, skips EE tests):
CI_MINIMAL_FEATURES=true bun test test/
| Variable | Description |
|---|---|
CI_MINIMAL_FEATURES |
Set to true to skip EE-dependent tests |
DATABASE_URL |
PostgreSQL connection string |
EE_LICENSE_KEY |
Enterprise license key for EE features |
