Compare commits

...

122 Commits

Author SHA1 Message Date
Ruben Fiszel
db4df60fb8 fix: create parent dirs in script new and accept python as language alias
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-24 13:10:03 +00:00
Ruben Fiszel
37886edda1 fix: show effective isolation level on workers page (#8491)
* Show effective isolation level on workers page, not configured

The workers page displayed the configured isolation level (nsjail/unshare)
even when the binary wasn't actually available, which was misleading.

Now shows "none (nsjail unavailable)" or "none (unshare unavailable)"
when the setting is enabled but the binary failed its startup test,
so admins can immediately see the mismatch from the UI.

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

* Simplify: use standard 'none' value when isolation binary unavailable

Keep the string as one of the 3 known values (nsjail/unshare/none)
since the frontend checks === 'none' for the warning badge. Now if
nsjail/unshare is configured but the binary is unavailable, it
correctly reports 'none' so the warning badge shows up.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:48:56 +00:00
Diego Imbert
5d1c54d9b3 feat: Debounce node (#8324)
* Debounce node works

* sqlx prepare

* sqlx prepare

* fix: address PR review issues for flow node debouncing

- Add sibling check in parent-walking loop to avoid killing branchall siblings
- Remove stale .sqlx cache files from earlier iterations
- Remove single-variant FlowNodeDebounceResult enum, use Result<()>
- Parse flow value once in version guard, recurse into nested modules
- Fix Svelte reactivity when switching selected flow modules
- Fix Tab indentation in FlowModuleComponent
- Use integer types in OpenAPI spec for debounce fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ee repo ref

* nit sqlx

* add Debouncing: None

* ee repo ref

* ee repo

* sqlx update

* fix: reject node-level debouncing inside branches (branchall/branchone)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert "fix: reject node-level debouncing inside branches (branchall/branchone)"

This reverts commit fa4820dde2.

* ee repo

* sqlx prepare

* sqlx prepare

* feat: add MIN_VERSION_SUPPORTS_NODE_DEBOUNCING (1.658.0) version guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: mark node-level debouncing as EE only in openflow schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: guard node debouncing against parallel steps (len > 1)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* generate system prompts

* system prompts

* chore: update ee-repo-ref to c04f3851c03758662e4936ff4b6e71bc56dbae7e

This commit updates the EE repository reference after PR #451 was merged in windmill-ee-private.

Previous ee-repo-ref: d140bb8944dfe3efb23cf8c12f556eacf30e2f87

New ee-repo-ref: c04f3851c03758662e4936ff4b6e71bc56dbae7e

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-24 09:47:41 +00:00
Diego Imbert
aa30fd252d fix: Move database manager SQL queries to backend (#8306)
* SQL Query builders in Rust

* Remove frontend sql scripts and substitute at execution

* fix null value bug

* Handle WM_INTERNAL_DB marker for apps deployed prior

* Revert policy handling

* Fix database studio empty string as where clause

* check policy

* Revert "check policy"

This reverts commit 3ea7899979.

* Revert "Fix database studio empty string as where clause"

This reverts commit 432fc87915.

* Revert

* legacy comments

* Move DDL queries to backend

* tests

* move bigquery bun scripts to backend

* expand markers + other nits

* fix: escape sql literals in query builders and async preview sql

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: quote all user-supplied identifiers in query builders to prevent SQL injection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: suppress dead_code warnings for deserialization-only fields and test-only helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct DDL test assertions and drop_table schema handling for non-schema DBs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* MySQL fix

* Fix 0/1 bool

* MySQL fix Yes/No casing

* Better error toasts

* Fix ms sql ntext cast

* fix: quote table name in Snowflake SHOW PRIMARY KEYS query

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: quote schema and table in Snowflake SHOW IMPORTED KEYS query

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: quote BigQuery dataset name in metadata query

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove invalid + separator in MSSQL CONCAT for count query

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-24 09:45:37 +00:00
hugocasa
37ebaf4d0a feat: add typed request body to OpenAPI spec generation (#8481)
* feat: add typed request body schema to OpenAPI spec for runnables without preprocessor

For HTTP routes and webhooks whose runnables (scripts/flows) don't have a
preprocessor, generate a typed request body in the OpenAPI spec using the
runnable's argument schema. Routes with preprocessors or wrap_body keep
the existing generic default request body.

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

* fix sqlx

* fix: add warning logs for schema fetch failures and strip non-OpenAPI keys

- Log tracing::warn when DB queries for schema fail instead of silently
  swallowing errors with .ok()
- Strip $schema and order keys from the JSON Schema before embedding in
  the OpenAPI spec for broader client compatibility

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

* fix: add tracing dependency to windmill-api-openapi

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:29:06 +00:00
Ruben Fiszel
cbe47c0b6c fix: Fix worker panic when job_isolation changed to unshare at runtime (#8490)
* Fix worker panic when job_isolation changed to unshare at runtime

When an admin changes the Instance Setting "job_isolation" to "unshare"
while UNSHARE_PATH was never initialized (binary not available at startup),
the worker panics in build_command_with_isolation().

This happens because reload_job_isolation_setting() in monitor.rs validates
nsjail availability but not unshare availability before applying the setting.

Fix:
- Add unshare availability check in reload_job_isolation_setting(), matching
  the existing nsjail check
- Replace panic! in build_command_with_isolation() with an error log and
  graceful fallback to running without isolation

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

* Improve error logging for unshare/nsjail unavailability at startup

- Upgrade unshare init logs from warn/debug to error level with detailed
  diagnostics (exit code, stderr, common causes, impact on job isolation)
- Upgrade nsjail init logs from info/warn to error level with clear
  messaging about unavailability consequences
- Force both UNSHARE_PATH and NSJAIL_AVAILABLE initialization at worker
  startup (not just when isolation is currently enabled) so availability
  is always logged regardless of current config
- Add explicit startup warnings when worker is configured for isolation
  but the binary is unavailable, referencing the init errors above

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:25:42 +00:00
centdix
e0d35ade72 chore: fix Claude action + add skills for codex + update autonomous mode docs (#8489)
* chore: fix Claude action overlap with /ai-fast

* chore: add Codex skills under .agents

* chore: remove user_invocable from Codex skills

* docs: require draft PR creation in autonomous mode
2026-03-24 09:23:06 +00:00
Diego Imbert
c13b95f8b2 Fix SAML Redirect (#8486)
* Fix SAML redirect

* Fix SAML redirect 2

* ee repo ref

* Apply suggestion from @claude[bot]

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* chore: update ee-repo-ref to 50a6626ce12771d7e0ca18bbcb0efad31cc7f1f2

This commit updates the EE repository reference after PR #475 was merged in windmill-ee-private.

Previous ee-repo-ref: c56747af8c420dd2222829f303b7fe6009ab9892

New ee-repo-ref: 50a6626ce12771d7e0ca18bbcb0efad31cc7f1f2

Automated by sync-ee-ref workflow.

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-23 22:44:38 +00:00
Ruben Fiszel
3c8d351c97 fix: improve SQS retries 2026-03-23 21:04:28 +00:00
Pyra
9643006f1e feat(cli): better stale scripts detection #3 (#8480)
* fix

Signed-off-by: pyranota <pyra@duck.com>

* reduce tests

Signed-off-by: pyranota <pyra@duck.com>

* update

Signed-off-by: pyranota <pyra@duck.com>

* fix

Signed-off-by: pyranota <pyra@duck.com>

* update

Signed-off-by: pyranota <pyra@duck.com>

* WIP: stash changes after merge with origin/main

* Delete backend/parsers/windmill-parser-wasm/Cargo.lock

* reset cargo.toml

* feat(cli): integrate dependency tree into generate-metadata command

- Add isDirectlyStale field to DependencyNode for staleness tracking
- Update addScript to accept itemType, folder, isRawApp, isDirectlyStale
- Update propagateStaleness to use isDirectlyStale field instead of parameter
- Handlers now determine staleness and pass it to tree.addScript
- generate-metadata calls propagateStaleness() and populates staleItems from tree
- Pass legacyBehaviour=false and tree to handlers during generation phase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(cli): store originalPath in tree for correct handler invocation

Scripts need the path with extension to be passed to the handler.
Added originalPath field to DependencyNode to track this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix parsers

Signed-off-by: pyranota <pyra@duck.com>

* rever sqlx removal

* update sqlx

* feat: make py-imports parser WASM-compatible and add as separate WASM package

Gate heavy deps (sqlx, windmill-common, async-recursion, toml, pep440_rs,
tracing) behind cfg(not(wasm32)). Make parse_code_for_imports,
parse_relative_imports, NImport, and ImportPin public. Remove duplicate
import_parser from parser-py (reset to origin/main). Add py-imports-parser
feature to windmill-parser-wasm and py-imports target to build.nu.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* safer return

* update

* fix: CLI metadata fixes - folder filter, staleness detection, WASM py-imports setup

- Fix lazy_static cfg gating for WASM compatibility (split into separate blocks)
- Fix folder argument filter to match specific file paths (not just directories)
- Fix staleness detection to use checkHash with conf (includes module hashes)
- Convert relative_imports_skip tests from Deno to bun APIs
- Add windmill-parser-wasm-py-imports to CLI and build-npm dependencies
- Relax module stale test to not require per-module change detail in output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore temp_script_refs parameter in parse_python_imports

Re-adds the temp_script_refs parameter that was lost when resetting
py-imports crate to origin/main. This enables resolving relative imports
from not-yet-deployed scripts during CLI lock generation.

* fixes

* extend testsuit

* update ee repo ref

* fix: diff endpoint bytea cast, upload only mismatched scripts

- Add POST /scripts/raw_temp/diff endpoint to batch-compare local content
  hashes against deployed versions using Postgres sha256()
- Use convert_to(content, 'UTF8') instead of content::bytea to avoid
  failure on scripts containing backslash sequences (e.g. \n)
- CLI now diffs all scripts against deployed, uploads only mismatched ones
- propagateStaleness no longer deletes non-stale nodes (needed for diff)
- Suppress verbose log.info messages during metadata generation
- Add E2E tests for locally modified and unpushed helper scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* rework

* sqlx

* fixes

* add index

* expand tests

* fix flows

* archive script before executing

* disable tests for ci

* skip Python-dependent E2E tests on CI

Tests requiring the python backend feature are skipped when
CI_MINIMAL_FEATURES=true since CI builds with zip-only features.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make flow fixture lock optional and reset nonDottedPaths after tests

Flow fixtures no longer emit an empty lock file by default. The lockContent
parameter controls whether a lock: "!inline ..." line appears in flow.yaml.
This prevents flows from appearing "up-to-date" when they should be processed
by generate-metadata.

Also adds afterAll to reset setNonDottedPaths(false) so global state doesn't
leak between test files when run together.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: add error logging in withTestBackend to diagnose CI failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: add --bail 1 to CI test runner to show full error on first failure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: include CLI stdout/stderr in assertion message for workspace deps test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: set WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES in test backend

The workspace deps feature requires workers to report their version, but
in test/CI there are no separate workers (standalone mode). The version
check fails because workers haven't had time to ping yet. Setting this
env var bypasses the version check.

Also reverts --bail 1 from CI workflow now that the root cause is fixed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: add --bail 1 to Windows CI and assertion messages for Windows failure diagnosis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace TEMP_SCRIPT_REFS_PLACEHOLDER in bun builder tests

The loader.bun.js now includes a TEMP_SCRIPT_REFS_PLACEHOLDER that must
be replaced before execution. The builder tests were missing this
replacement, causing all 6 bun_builder_tests to fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use cdirFwd in Windows loader filterLoad regex

Raw cdir (with backslashes) interpolated into RegExp causes \r to
become carriage return and \w to become word-char, so filterLoad
never matches main.ts. This prevents replaceRelativeImports from
running, leaving bare relative imports like "./script_b" in the
bundled output, which scanImports then misparses as package ".".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Windows filterLoad regex + graceful fallback for old backends

- Fix filterLoad in loader.bun.windows.js to match both native backslash
  and forward-slash paths from Bun's resolver by escaping cdir for regex
- Wrap uploadScripts in try/catch so generate-metadata degrades gracefully
  when the backend lacks /raw_temp endpoints (locks use deployed versions)
- Add TODO for missing TEMP_SCRIPT_REFS support in Windows loader

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: add loader/builder debug logging for Windows CI diagnosis

Temporary console.log statements to understand:
- What path Bun passes to onLoad for main.ts
- Whether filterLoad regex matches
- Whether replaceRelativeImports fires
- What the bundled output contains
- What imports scanImports extracts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: trigger CI for cli path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: trigger CI via workflow file change

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add TEMP_SCRIPT_REFS to Windows loader, use .ts extensions in test imports

- Add TEMP_SCRIPT_REFS_PLACEHOLDER support to loader.bun.windows.js
  (mirrors loader.bun.js) so CLI lock generation can resolve imports
  from locally-modified scripts on Windows
- Use .ts extensions in all test relative imports to work around the
  Windows filterLoad regex bug (replaceRelativeImports doesn't fire
  on Windows, so extensionless imports fail)
- Remove unused uploadSucceeded variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove debug logging from loader_builder.bun.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove windmill-parser-wasm-py-imports from frontend package.json

This dependency is only needed by the CLI, not the frontend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* debug: add temp_script_refs logging for Windows CI investigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: remove --bail 1 from Windows CLI tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: normalize backslashes in folder filter treePath lookup (Windows)

On Windows, item.path (originalPath) uses backslashes but tree keys
use forward slashes. The isRelevant filter's touchesFolder call
passed the unnormalized path to traverseTransitive, which couldn't
find the node. This caused cross-folder importers to be excluded
from generate-metadata when a folder argument was specified.

Also removes debug logging from previous commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update cli-tests.yml

* fix: normalize backslashes in strict-folder-boundaries warning message (Windows)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update ee-repo-ref to fe8f0d1d7448464c98474d994e6492c0a45e8e38

This commit updates the EE repository reference after PR #467 was merged in windmill-ee-private.

Previous ee-repo-ref: 03e6eaf950776c96b9581848a583af9ad735be60

New ee-repo-ref: fe8f0d1d7448464c98474d994e6492c0a45e8e38

Automated by sync-ee-ref workflow.

* revert cli-tests.yml

---------

Signed-off-by: pyranota <pyra@duck.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-23 18:20:19 +00:00
Pyra
010753c73a fix: skip debounce arg accumulation when batch table is empty (CE) (#8485)
On CE (without private feature), v2_job_debounce_batch is never
populated because maybe_debounce_post_preprocessing is EE-only.
The accumulation query returns zero rows, producing an empty array
that replaces the original nodes_to_relock value. This causes flow
modules to never get relocked when triggered by relative imports.

Fix: only replace the original value when the batch query actually
returned entries to accumulate.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:59:46 +00:00
Ruben Fiszel
f329ee7aae fix: respect NO_COLOR env variable for stdout log output (#8483)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:01:01 +00:00
Diego Imbert
34a392fed3 add AZ_ACCOUNT_NAME_WORKSPACE_RESTRICTIONS env var (#8482)
* feat: add AZ_ACCOUNT_NAME_WORKSPACE_RESTRICTIONS env var

Add workspace restrictions by Azure account name, similar to the existing
S3_BUCKETS_WORKSPACE_RESTRICTIONS for bucket names. Refactored parsing
into a shared parse_restrictions_from_str function.

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

* chore: update ee-repo-ref.txt

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

* chore: update ee-repo-ref to a997285e976d0642b72584e1966a70a79d84e7dc

This commit updates the EE repository reference after PR #472 was merged in windmill-ee-private.

Previous ee-repo-ref: 5718dc7deca18ad52ffb413813e97b8ca75805b8

New ee-repo-ref: a997285e976d0642b72584e1966a70a79d84e7dc

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-23 16:55:47 +00:00
Alexander Petric
911df958e7 fix(cli): add Svelte 5 event delegation guidance and safe push to raw-app skill (#8466)
- Add documentation about the $.delegated runtime error that occurs when
  the Svelte runtime version in node_modules doesn't match the compiler
  version used by wmill sync push.
- Change the push command in CLI reference to use --extra-includes for
  targeted pushes instead of blanket wmill sync push.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 17:18:41 +00:00
Ruben Fiszel
fb2bdc6a53 SSRF protection for SAML and MCP OAuth endpoints (#8473)
* fix: add SSRF protection to SAML and MCP OAuth endpoints

- Add shared SSRF URL validation utility (windmill-common/ssrf.rs) that blocks private/loopback/link-local IPs and validates DNS resolution
- Move test_metadata to authed service requiring superadmin access
- Strip response body from SAML metadata parsing errors
- Add SSRF blocklist to MCP OAuth discover, start, and client registration endpoints

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

* chore: update ee-repo-ref.txt for SSRF fix

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

* chore: update ee-repo-ref.txt

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

* chore: update ee-repo-ref to 563877bf1c8b4184f638bab51be89b1c0aec6dad

This commit updates the EE repository reference after PR #471 was merged in windmill-ee-private.

Previous ee-repo-ref: a600fe1807ea267f87a57360f4b48bf917776723

New ee-repo-ref: 563877bf1c8b4184f638bab51be89b1c0aec6dad

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-22 17:18:22 +00:00
hugocasa
1503bf948e fix: stop_after_if with empty error_message prevents flow from stopping (#8464)
* fix: stop_after_if with empty error_message no longer prevents flow from stopping

When skip_if_stopped=true and error_message="" were both set, the flow
would continue executing instead of stopping because the empty string
was converted to a default error message, which triggered the error
handler path. Now skip_if_stopped takes precedence and the two options
are treated as mutually exclusive in both backend and frontend.

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

* chore: regenerate system prompts after openflow schema change

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:04:51 +01:00
Ruben Fiszel
039b79dfe6 chore(main): release 1.662.0 (#8463)
* chore(main): release 1.662.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-20 17:47:23 +00:00
hugocasa
efb4a27d51 fix: replace email with permissioned_as for triggers/schedules (#8439)
* refactor: replace email with permissioned_as for triggers/schedules

Add a new `permissioned_as` column (format: `u/{username}`, `g/{group}`,
or raw email) to all trigger tables and schedule. This value is used
directly for job permission checks, removing the need for email lookups
when creating/updating triggers.

- Migration: add permissioned_as to all 9 trigger tables + schedule,
  drop email from trigger tables (schedule keeps it for backwards compat)
- Backend: resolve_email() (async, DB) -> resolve_permissioned_as() (sync)
- Email cache: get_email_from_permissioned_as() with quick_cache for
  places that still need email (fetch_api_authed, schedule backwards compat)
- Frontend: rename email/preserve_email -> permissioned_as/preserve_permissioned_as
  in deploy data and OpenAPI schemas
- Tests updated for new field names and u/{username} format

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

* fix sqlx/build

* update ee ref

* refactor: simplify resolve_edited_by to always use authed username

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

* fix compile + migration

* update ref

* test: add trigger trait method tests for permissioned_as queries

Add tests that call TriggerCrud and Listener trait methods directly
to verify dynamic SQL correctly references the permissioned_as column.
Covers get_trigger_by_path, list_triggers, set_trigger_mode, and
fetch_enabled_unlistened_triggers for all trigger types.

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

* update sqlx

* fix: use permissioned_as directly for schedules and fix audit RLS for groups

- Schedule: permissioned_as only set on create, not on edit/set_enabled
- Schedule: stop reading email column, use get_email_from_permissioned_as
- Triggers: use fetch_api_authed_from_permissioned_as instead of edited_by
- Triggers: rename listener fields for clarity (username -> edited_by)
- Fix audit author username for group permissioned_as (g/test -> group-test)
  to match session.user, preventing RLS policy violations on audit_partitioned
- OpenAPI: remove permissioned_as/preserve_permissioned_as from EditSchedule
- Add backwards-compat comments for schedule email writes

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

* chore: regenerate system prompts for permissioned_as field

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

* fix build

* refactor: generalize onBehalfOf naming, add permissioned_as to EditSchedule

- Frontend: rename onBehalfOfPermissionedAs -> onBehalfOf with comments
  explaining it carries emails for flows/scripts and permissioned_as for
  triggers/schedules
- Frontend: rename getOnBehalfOfEmail -> getOnBehalfOf,
  getOnBehalfOfPermissionedAsForDeploy -> getOnBehalfOfForDeploy,
  customOnBehalfOfEmails -> customOnBehalfOf
- Backend: add optional permissioned_as/preserve_permissioned_as to
  EditSchedule with COALESCE (only updates when provided)
- Backend: add on_behalf_of audit log for schedule edit
- Backend: remove unused resolve_on_behalf_of_permissioned_as
- Tests: remove email assertions from schedule update test (email is
  just backwards compat, only permissioned_as matters)

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

* fix: preserve email column when permissioned_as is preserved on schedule edit

Derive email from the preserved permissioned_as via cache lookup instead
of always writing authed.email. This keeps the email column consistent
with the old behavior for backwards compat with old workers.

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

* fix: update deploy UI labels from "edited by" to "run as" for triggers

Triggers now use permissioned_as (not edited_by) for permissions, so
update the deploy UI wording to reflect this. Also update wm_deployers
group description to mention schedules and permissioned_as.

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

* fix: use u/username format for custom trigger/schedule deploy selection

When picking a custom user for trigger/schedule deployment, store
u/${username} (permissioned_as format) instead of the email. Flows/scripts
continue to use email format for on_behalf_of_email.

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

* fix: show u/username format for "me" option in trigger deploy selector

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

* refactor: simplify OnBehalfOfSelector to return the right format per kind

OnBehalfOfSelector now handles the email vs permissioned_as format
internally based on kind:
- triggers: returns u/username, displays u/username in all options
- flows/scripts/apps: returns email, displays username

The onSelect callback now takes (choice, value?) where value is already
in the correct format. Parent components just store it directly without
needing to know about the format difference.

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

* fix: always show u/username format in OnBehalfOfSelector for all kinds

Display is now consistent: all kinds show u/username in the selector.
The returned value still differs (email for flows/scripts, u/username
for triggers) since the backend APIs expect different formats.

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

* fix: replace email with permissioned_as in http_trigger test insert

The email column was dropped from trigger tables in the migration.

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

* fix: review fixes — migration, app policy, capture cleanup, naming

- Migration: remove DEFAULT '', use nullable → populate → SET NOT NULL
- App policy: set both on_behalf_of and on_behalf_of_email for all choices
- OnBehalfOfSelector: return OnBehalfOfDetails {email, permissionedAs} instead of ambiguous value
- Remove unused email field from Capture struct and query
- Rename getSourceEmail/getTargetEmail → getSourceOnBehalfOf/getTargetOnBehalfOf
- Rename test functions from preserve_email to preserve_permissioned_as

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

* fix: add permissioned_as to all test schedule INSERTs

Since the migration no longer uses DEFAULT '', all INSERTs must
explicitly provide permissioned_as. Updated test fixtures and
schedule_push tests.

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

* fix: strip permissioned_as from exports/sync, fix OpenAPI required field

- Add permissioned_as to workspace export strip list (like edited_by)
- Add permissioned_as to CLI TriggerFile Omit list
- Fix TriggerExtraProperty.required: email → permissioned_as
- Regenerate frontend and CLI types

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

* fix: remove accidentally committed generated files

These directories are gitignored and should not be tracked.

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

* chore: regenerate system prompts for permissioned_as schema changes

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

* fix: remove permissioned_as from CLI TriggerFile Omit list

Already stripped in workspace export, no need to also omit from the type.

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

* fix: optimize email cache key and revert TriggerFile Omit change

- Use single concatenated string for cache key instead of (String, String) tuple
- Remove permissioned_as from CLI TriggerFile Omit (already stripped in export)

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

* fix: zero-allocation email cache lookups using Equivalent trait

Use a borrowed EmailCacheKey(&str, &str) for cache lookups via
quick_cache's Equivalent support. Only allocates (String, String)
on cache miss for insert. This is called on every trigger fire
and schedule push.

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

* fix: add permissioned_as to Schedule required fields in OpenAPI spec

The backend always returns permissioned_as (non-optional String),
so the schema should reflect that.

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

* fix: handle group- prefix in migration UPDATE statements

edited_by can be 'group-{name}' for group-owned triggers/schedules.
The migration now correctly maps these to 'g/{name}' format instead
of incorrectly producing 'u/group-{name}'.

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

* Revert "fix: handle group- prefix in migration UPDATE statements"

This reverts commit 0971392b38.

* fix: use superadmin email to resolve permissioned_as in schedule migration

For users upgrading from older versions where edited_by may not reflect
the actual schedule owner, check if the email belongs to a superadmin
and look up their username. Otherwise fall back to edited_by.

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

* fix: fall back to superadmin email when not in workspace usr table

If the superadmin isn't a member of the workspace, use their email
as raw permissioned_as instead of falling back to edited_by.

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

* fix: always update permissioned_as and email on schedule edit

Consistent with pre-refactor behavior where email and edited_by
were always updated on every edit. permissioned_as is now always
set (to editing user or preserved value), removing the COALESCE
that previously preserved it when not provided.

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

* feat: add schedule permission tests and centralize group prefix constants

Tests: schedule create/update for normal user, workspace admin, and
superadmin not in workspace. Verifies schedule fields (email,
permissioned_as, edited_by) and pushed job fields (permissioned_as,
permissioned_as_email).

Constants: centralize "u/", "g/", "group-" as PERMISSIONED_AS_USER_PREFIX,
PERMISSIONED_AS_GROUP_PREFIX, USERNAME_GROUP_PREFIX.

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

* fix: use @unknown.windmill.dev for synthetic email fallback

Prevents privilege escalation: a user with username like
'superadmin_secret' would get superadmin via the synthetic
email matching SUPERADMIN_SECRET_EMAIL. Using a different
subdomain avoids any collision with hardcoded @windmill.dev emails.

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

* update ee ref

* sqlx

* chore: regenerate system prompts after main merge

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

* chore: update ee-repo-ref to bda51bc33bcb573659e7ff07d0a23ff6e23b8148

This commit updates the EE repository reference after PR #468 was merged in windmill-ee-private.

Previous ee-repo-ref: 8cf1802f8fe183f430830590b4f3172a50207843

New ee-repo-ref: bda51bc33bcb573659e7ff07d0a23ff6e23b8148

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-20 16:28:38 +00:00
Alexander Petric
51957f7d92 feat: mcp oauth gateway (#8443)
* feat: extract McpScopeSelector into reusable component

Extract scope selection UI from CreateToken.svelte and mcp_authorize page
into a shared McpScopeSelector.svelte component to reduce duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add MCP gateway endpoint for workspace-agnostic access

Add /api/mcp/gateway endpoint that allows MCP clients to connect without
knowing the workspace ID upfront. During OAuth, the user picks their
workspace on the consent page. The token is then scoped to that workspace.

This enables a single URL for the Anthropic connectors directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review nits

- Use onClick prop instead of legacy on:click directive in McpScopeSelector
- Remove unused catch variable in workspace loading

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: deduplicate gateway OAuth handlers into shared inner functions

Extract build_oauth_metadata, build_protected_resource_metadata,
oauth_authorize_inner, and oauth_approve_inner so gateway handlers
are thin wrappers. Also revert formatting-only changes in auth.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: gate run_inline test helpers behind cfg(feature = "run_inline")

Imports and helper functions were not gated, causing unused-import and
dead-code errors when compiling without the run_inline feature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update SQLx metadata

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-20 16:12:59 +00:00
centdix
533609989f handle OSS onboarding error gracefully (#8459)
* fix: handle OSS onboarding error gracefully in setup wizard

When creating a custom admin account fails on OSS builds (Enterprise-only
feature), show a helpful dialog instead of a generic error, guiding the
user to continue with default credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use more precise error check for OSS account creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct error message — not an EE feature, just not implemented in OSS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove misleading "change from user settings" since set_password is also OSS-stubbed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: move default credentials info to frontend dialog only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 12:37:05 +00:00
centdix
88ad376791 fix: strip invalid enum values from MCP schemas (#8462)
* fix: harden MCP tool schemas for Claude compatibility

* fix: strip invalid enum values from MCP schemas
2026-03-20 12:36:43 +00:00
centdix
f2f178eb31 chore: remove dead users_oss module (#8458) 2026-03-19 17:21:07 +00:00
Ruben Fiszel
c4be206c5a chore(main): release 1.661.0 (#8448)
* chore(main): release 1.661.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-19 17:09:33 +00:00
wendrul
0e022b14fd fix: full code apps deployable on merge UI and deploy UI (#8451)
* fix: full code apps deployable on merge UI and deploy UI

* update ee repo ref

* preapare sqlx

* split app and raw_app

* update eereporef

* fix displayy showing raw apps appropriately

* chore: update ee-repo-ref to b3b8005d45e3f2aa7228c61d2e4ae86a17d89a30

This commit updates the EE repository reference after PR #470 was merged in windmill-ee-private.

Previous ee-repo-ref: 78d1f6cc4b15ec4c0768969635ba6b8f166a7742

New ee-repo-ref: b3b8005d45e3f2aa7228c61d2e4ae86a17d89a30

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-19 17:01:56 +00:00
Ruben Fiszel
b2c1e3de0a fix: resolve blank inline script panel for components with underscores in ID (#8457)
* fix: resolve blank inline script panel for components with underscores in ID

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

* perf: compute matched grid item once per selection instead of per-item

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:54:30 +00:00
Ruben Fiszel
ff78f448be webmux nits 2026-03-19 16:06:07 +00:00
Ruben Fiszel
4e0b6db4ea webmux nits 2026-03-19 16:04:42 +00:00
Ruben Fiszel
041e1dcf82 simplify webmux ports 2026-03-19 16:03:21 +00:00
Ruben Fiszel
49f943b51d use BACKEND_PORT/FRONTEND_PORT as port fallbacks in backend and vite (#8454)
* feat: use WM_BACKEND_PORT/WM_FRONTEND_PORT env vars as port fallbacks

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

* refactor: use BACKEND_PORT/FRONTEND_PORT instead of WM_ prefixed vars

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

* fix: keep app.windmill.dev as ws proxy fallback

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:16:11 +00:00
Ruben Fiszel
75b191b3ad add gateway reverse proxy for extra services (#8456)
* feat: add gateway reverse proxy for extra services

Add a lightweight Node.js gateway on port 3000 that routes requests
by URL prefix (/ws/*, /ws_mp/*, /ws_debug/*) to the correct backend
service, stripping the prefix before forwarding. This allows all
extra services to be accessed through a single port.

Also makes the multiplayer server more tolerant by generically
stripping /ws_mp/ prefix on HTTP requests.

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

* chore: enable gateway by default for extra services

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

* chore: add REMOTE_EXTRA env var for unified extra services proxy

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

* chore: make gateway port configurable via PORT env var

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

* chore: simplify Caddyfile extra services routing

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:11:05 +00:00
Ruben Fiszel
4e59a1a166 fix: prevent raw app iframe reload on userStore refresh (#8455)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:17:06 +00:00
centdix
278c8fe416 chore: restore backend/.env copy in worktree setup (#8452)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 12:16:01 +00:00
Diego Imbert
446afb5b36 fix: fix datatable setup on RDS (#8450)
* Fix Datatable setup on RDS

* nit

* unused import

* add replication
2026-03-19 10:02:41 +00:00
Ruben Fiszel
fd7f0d3da9 fix: improve DND drag feedback in EditableSchemaForm (#8449)
Three issues fixed:
- Dragged element clone was invisible because morphDraggedElementToBeLike
  ran before the clone was in the DOM, copying 0-height from the
  uninitialized ResizeTransitionWrapper shadow. Fixed with morphDisabled.
- Shadow placeholder was inconsistently hidden because the DND library's
  inline visibility:hidden was overwritten by RTW's reactive style binding.
  Fixed with !visible CSS class that overrides inline styles.
- Small cursor movements immediately triggered field reordering. Added a
  200ms grace period after drag start before processing reorder events.

The shadow element now shows a dashed blue drop-target indicator instead
of being fully hidden.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:15:37 +00:00
Ruben Fiszel
7de98c0df4 feat: add OTel metrics support (#8442)
* [ee] feat: add OTel metrics support

Add OpenTelemetry metrics export for Windmill operational metrics.
When the OTel metrics toggle is enabled in instance settings (EE),
Windmill exports 16 metrics to any OTLP-compatible collector, letting
users observe queue depths, worker execution, DB pool state, and health
without a separate Prometheus setup.

Changes:
- otel_oss.rs: no-op stubs for OSS builds
- monitor.rs: queue count/running count gauges, zombie counters, DB pool
  monitoring (shared single DB query and loop with Prometheus)
- worker.rs: execution count/duration, worker busy, pull duration
- jobs.rs: queue push/delete/pull counters
- health.rs: DB latency gauge
- main.rs: call monitor_pool_otel unconditionally
- InstanceSetting.svelte: enable metrics toggle for EE licenses

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

* chore: update ee-repo-ref.txt for OTel metrics

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

* feat: add worker_started, worker_uptime, health_status, health_db_unresponsive OTel metrics

Wire up 5 additional metrics to reach parity with Prometheus:
- worker_execution_failed: wired in add_completed_job_error (was defined but unused)
- worker.started: incremented on worker startup
- worker.uptime: recorded each loop iteration
- health.status: phase gauge (healthy/degraded/unhealthy)
- health.db_unresponsive: flag (0/1)

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

* chore: update ee-repo-ref.txt

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

* chore: update ee-repo-ref to fbe68e4aa621e30378995cfd328a6ccf74176614

This commit updates the EE repository reference after PR #469 was merged in windmill-ee-private.

Previous ee-repo-ref: 6fa1881aafdfb60f4abf11a37f01f6fedaecb3ec

New ee-repo-ref: fbe68e4aa621e30378995cfd328a6ccf74176614

Automated by sync-ee-ref workflow.

* fix: remove duplicate cfg attr and duplicate OTel pool reporting

- Remove duplicate #[cfg(feature = "prometheus")] on monitor_pool
- Remove OTel block from monitor_pool; monitor_pool_otel is the sole
  OTel reporter, eliminating duplicate windmill.db.pool.* metrics in
  EE builds
- Simplify monitor_pool back to its original Prometheus-only structure

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-19 08:10:37 +00:00
Ruben Fiszel
1bca2e931b chore(main): release 1.660.1 (#8445)
* chore(main): release 1.660.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-19 07:00:38 +00:00
Ruben Fiszel
0ab9a81e20 field reorder on rename in EditableSchemaForm (#8447)
* fix: track schema.properties reference not keys in EditableSchemaForm

Object.keys() tracked key enumeration, so renaming a field triggered
onSchemaChange -> alignOrderWithProperties -> reorder. schema?.order
created a feedback loop since alignOrderWithProperties writes to it.

Only schema?.properties (the object reference) is needed to detect
when inferArgs replaces properties (schema.properties = {}).

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

* fix: revert EditableSchemaForm effect to original

The added schema?.order and Object.keys(schema?.properties) tracking
caused field reordering on rename — Object.keys returns the renamed
key at the end (JS insertion order after delete+add), and schema?.order
created a feedback loop with alignOrderWithProperties. Revert to the
original schema reference-only tracking.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 06:35:02 +00:00
Ruben Fiszel
c0edbe4317 fix: schema inference not updating on reset and language switch (#8446)
Three root causes:

1. Editor.setCode() never dispatched `change` — it pre-set `code = ncode`
   before the Monaco edit, so the debounced updateCode() saw code == ncode
   and skipped dispatch. The Reset button, copilot accept, and other
   setCode callers never triggered schema inference. Fixed by capturing
   `changed` before the pre-set and dispatching directly when true.

2. EditableSchemaForm's $effect only tracked the schema reference, not
   its properties. Since inferArgs mutates schema in-place through the
   Svelte 5 proxy, the reference never changes and the effect never
   re-ran. Added schema?.order and Object.keys(schema?.properties ?? {})
   reads to detect in-place mutations (matching SchemaForm's pattern).

3. ScriptEditor's $effect depended on both selectedTab and code, causing
   a redundant double inferSchema call on every code change (racing with
   the on:change handler and initContent's explicit call). Moved code
   into untrack() so the effect only fires on tab switches.

Also removed the no-op `testPanelSchema = testPanelSchema` in
inferModuleSchema.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 06:23:38 +00:00
Ruben Fiszel
a8fa0cccef fix: prevent S3 file browser crash when selecting storage (#8444)
VirtualList crashes with 'Requested index 0 is outside of range 0..0'
when it mounts with itemCount=0 and a positive height. This happened
because the old condition allowed VirtualList to remount during loading
with zero items but a stale listDivHeight from a previous mount.

Change the guard to displayedFileKeys.length === 0 so VirtualList is
never rendered when there are no items. Show a centered loading spinner
or "no files" message instead.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 05:49:44 +00:00
Ruben Fiszel
f2334e6564 chore(main): release 1.660.0 (#8428)
* chore(main): release 1.660.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-18 22:33:26 +00:00
hugocasa
f4489cbe64 fix: prevent AI agent tool jobs from becoming zombies on cancellation (#8437)
* fix: prevent AI agent tool jobs from becoming zombies on cancellation

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

* kill tool tasks on cancel timeout

* fix: address review feedback and update sqlx cache

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:30:31 +00:00
Ruben Fiszel
2171cc8e0a chore: separate csharp publish output dir from source dir to fix flaky build (#8441)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:30:09 +00:00
Alexander Petric
1cfb40bdaa feat: MCP server readiness for Anthropic connectors directory (#8438)
* feat: MCP server readiness for Anthropic integrations directory

- Add CORS layer to MCP streamable HTTP endpoint for browser clients
- Add tool result truncation (25K token limit) to prevent oversized responses
- Add HEAD method support on OAuth authorize endpoint
- Skip workspace selection redirect during MCP OAuth flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #8438 review feedback

- Add DELETE to CORS allowed methods (MCP spec requires DELETE for session termination)
- Add CORS layer to deprecated /sse endpoint for completeness
- Remove redundant .head() on OAuth authorize (axum auto-handles HEAD via GET)
- Fix comment: "chars/token" → "bytes/token" since len() returns bytes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:19:43 +00:00
Ruben Fiszel
bee928276e fix: show cancelled WAC jobs as done in workflow timeline (#8436)
* fix: show cancelled WAC jobs as done in workflow timeline

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

* fix: keep polling after cancel so WAC timeline updates to completed state

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

* fix: treat WAC as done in LogPanel when loader stops after cancel

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

* feat: show preview badge and hide _MODULES arg in run history

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

* fix: show preview badge alongside status dot, not instead of it

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:50:50 +00:00
Ruben Fiszel
391da1d5af add cloud quota usage display and version pruning (#8433)
* feat: add cloud quota usage display and version pruning

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

* fix: hard-delete pruned scripts so quota actually decreases

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

* feat: update quota error messages to reference workspace settings

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:03:01 +00:00
centdix
9ca86f7a11 bump utils internal version (#8435) 2026-03-18 12:02:47 +00:00
Ruben Fiszel
19129aa019 nit serve_ui + workmux 2026-03-18 11:38:22 +00:00
centdix
435de95e7d feat(cli): use local scripts when previewing flows (#8365)
* feat(cli): use local scripts when previewing flows

When previewing a flow, PathScript modules (type: "script") now resolve
to local file content instead of remote versions. This ensures flow
preview and dev mode test the actual local changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(cli): add tests for PathScript local replacement in flow preview

Unit tests for replacePathScriptsWithLocal covering:
- basic PathScript→RawScript conversion
- tag_override preservation
- missing local file fallback
- mixed module types
- nested structures (loops, branches)

Integration test verifying flow preview with a PathScript step
uses the local script file content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): extract shared helpers and add aiagent support for PathScript replacement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): replace `as any` casts with proper type assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cli): preserve local flow preview script context

* fix(cli): normalize inline flow preview bundles for bun

* fix(cli): make local flow path scripts opt-in

* fix(cli): only merge flow preview config for local mode

* chore(system-prompts): regenerate cli command guidance

* fix(cli): skip deno defaultTs test in CI without deno runtime

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(cli): clean up local path script helpers

* feat(cli): make flow preview use local path scripts

* fix(cli): ignore normalized preview metadata drift

* chore(cli): address review follow-ups

* test(cli): cover custom bundler path quoting

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:29:30 +00:00
Ruben Fiszel
997dd6ac3a windows volume mount symlinks for integration tests (#8431)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-18 10:27:41 +00:00
Ruben Fiszel
9a6ce44c84 fix: exclude wm_deployers group from CE group limit check (#8429)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-18 10:21:52 +00:00
Sascha Egerer
66a8e844a6 perf: cache composer vendor dir to skip reinstall on repeated php executions (#8330)
* perf: cache composer vendor dir to skip reinstall on repeated php executions

* feat: add COMPOSER_VENDOR_CACHE_DISABLED env var to opt out of vendor caching

---------

Co-authored-by: hugocasa <hugo@casademont.ch>
2026-03-18 09:41:37 +00:00
Ruben Fiszel
e0857421aa handle /ws_debug/health in debugger and add request logging (#8426)
- Fix debugger HTTP health endpoint to also match /ws_debug/health
  (ingress forwards the full path, not just /health)
- Add request logging to all three extra services (LSP, multiplayer,
  debugger) for HTTP and WebSocket ping/upgrade events

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:21:38 +00:00
Ruben Fiszel
d859b47874 chore(main): release 1.659.1 (#8423)
* chore(main): release 1.659.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-18 08:23:33 +00:00
Diego Imbert
ad03a5dbd7 fix: cleanup job debounce batch (#8420)
* delete from v2_job_debounce on accumulate

* sqlx prepare

* Unconditionally remove from v2_job_debounce_batch

* sqlx prepare
2026-03-18 08:18:58 +00:00
Ruben Fiszel
4829f447ed fix: add checkpoint.json mount to python nsjail config for WAC v2 (#8421)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:17:08 +00:00
Ruben Fiszel
f481ea4059 fix(frontend): fix output of resultnode + svelte5 nits (#8424)
* fix(frontend): remove banned $bindable('') pattern from ClearableInput

Switching format types in the flow input editor caused a
props_invalid_value error because ClearableInput used
value = $bindable(''), which conflicts with undefined bindings.

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

* fix(frontend): restore flow result display in result node

The fix in #8390 changed updateLastJob() to only use testJob when
actively running/streaming, preferring flowStateStore for completed
results. But the result node has moduleId='' and no flowStateStore
entry, so the early return made it always show the empty state.

Add !moduleId to the testJob condition so the result node (which has
no flowStateStore entry) still uses testJob as its only data source.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:15:04 +00:00
Ruben Fiszel
0f261695a3 fix: per-tab test panel in script editor for WAC v2 modules (#8422)
When switching to a non-main module tab, the test panel now infers
args from the module's code and runs the module's code on Test/Cmd+Enter.
Per-module args and schema are persisted across tab switches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:49:04 +00:00
Ruben Fiszel
7d800f209d chore(main): release 1.659.0 (#8397)
* chore(main): release 1.659.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-17 20:26:15 +00:00
Diego Imbert
ebf9347d3f fix: Folders as presets in FilterSearchbar (#8409)
* Folder presets in filter search bar

* nit max preset height
2026-03-17 20:14:33 +00:00
hugocasa
8c769aebbf improve analytics (#8418)
* [ee] improve analytics: add git sync & AI chat telemetry, HMAC-signed download

- Add ai_chat_usage table to track chat sessions (session_id, provider, model, mode, message_count)
- Add POST /w/{workspace}/workspaces/log_chat endpoint with upsert on session_id
- Frontend fires logAiChat on every sendRequest, using HistoryManager's existing chat ID
- EE stats: add git_sync_usage (sync vs promotion repo count) and ai_chat_usage (30-day aggregates)
- Replace RSA+AES-GCM encrypted telemetry download with plaintext JSON + HMAC-SHA256 signature
- Signature (12 hex chars) included in download filename for verification
- Update instance settings telemetry descriptions for both EE and CE

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

* fix: make StatsDownload struct pub to fix private-interfaces error

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

* chore: update ee-repo-ref to 878cc2044717e0177228529a50433fe2768e70b5

This commit updates the EE repository reference after PR #464 was merged in windmill-ee-private.

Previous ee-repo-ref: 33eb863b6b881bd54ed69a540e0c65d5fe125024

New ee-repo-ref: 878cc2044717e0177228529a50433fe2768e70b5

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-17 20:14:02 +00:00
hugocasa
fe051aa22b feat(cli): add --env alias for --branch and environments config alias (#8415)
* feat(cli): add --env alias for --branch and environments config alias

Add --env as a CLI alias for --branch on sync pull, sync push, workspace
bind, and workspace unbind commands. Add environments as a permanent
config alias for gitBranches in wmill.yaml. This helps users who use
single-branch multi-environment workflows where "branch" terminology
is confusing.

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

* chore: regenerate auto-generated system prompts

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 20:13:04 +00:00
Alexander Petric
9f10b44c18 update cloudformation template to use latest cli/images + fix cl… (#8417)
* fix: update cloudformation template to use latest cli/images + fix cleanup script

* fix: narrow SG cleanup to k8s-created groups + add CLI install error handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 20:12:04 +00:00
Ruben Fiszel
c4c524fade wac v2 improvements (#8419)
* all

* sqlx
2026-03-17 19:55:22 +00:00
wendrul
920a7f9fa4 fix: devops getting logged out on workers page (#8416)
* fix: devops getting logged out on workers page

* rename local vars
2026-03-17 17:04:18 +00:00
hugocasa
d43eca7b4b chore(frontend): add missing integration icons and fix dark mode visibility (#8413)
* feat: add 93 missing integration icons and fix dark mode visibility

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

* feat: add 11 more integration icons (round 2)

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

* feat: add 5 more integration icons (round 3)

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:02:54 +00:00
hugocasa
7d9fb57368 feat: DB-backed instance events webhook with superadmin UI (#8402)
* feat: make instance events webhook URL configurable via superadmin UI

The instance events webhook was previously only configurable via the
INSTANCE_EVENTS_WEBHOOK env var, requiring a restart to change. This
adds a DB-backed global setting with a UI in superadmin settings under
Monitoring > Webhooks, while keeping the env var as an override.

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

* fix: address PR review - prometheus timer bug and cleaner cache init

- Bind prometheus timer to `let timer` and call `stop_and_record()`
  after the POST (was silently discarded before)
- Use `Option<Instant>` with `map_or` instead of `checked_sub` trick
  for clearer "not yet read" semantics

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

* chore: remove env var mention from webhook setting description

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

* chore: list all instance events explicitly in webhook description

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

* fix: restore send_instance_event guard with AtomicBool for DB setting

Use a shared Arc<AtomicBool> between send_instance_event and the event
loop so we skip channel sends when no webhook is configured (env or DB).
Starts optimistic (true) so the first event triggers a DB read, then
the loop updates it after each cache refresh.

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

* fix: use static AtomicBool + notify handler for webhook guard

Replace the Arc<AtomicBool> instance field with a global static
INSTANCE_EVENTS_WEBHOOK_DB_ENABLED, updated by the
notify_global_setting_change handler in main.rs. This follows the
established pattern (like REQUIRE_PREEXISTING_USER_FOR_OAUTH) and
avoids the deadlock where the bool could never flip back to true.

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

* refactor: single Arc<RwLock<Option<String>>> for instance webhook URL

Replace the separate INSTANCE_EVENTS_WEBHOOK env var lazy_static and
INSTANCE_EVENTS_WEBHOOK_DB_ENABLED AtomicBool with a single shared
variable. Initialized from env var, then the reload function overwrites
from DB (falls back to env var when DB has no value). Follows the same
pattern as SCIM_TOKEN and other settings.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-03-17 15:26:04 +00:00
Ruben Fiszel
73fe45b6cb feat: workspace-specific registry overrides (#8406)
* feat: add workspace-specific registry overrides

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

* fix: move workspace registries to end of registries tab

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

* feat: workspace overrides use field selector instead of showing all fields

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

* style: polish workspace registries UI to match design guidelines

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

* fix: show field selector directly and fix addField initialization logic

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

* feat: namespace pip_resolution_cache by workspace when registry overrides exist

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

* feat: namespace binary/bundle caches by workspace when registry overrides exist

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

* perf: zero-cost cache suffix when no workspace overrides exist

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

* feat: reload workspace_registries via notify events on setting change

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

* fix: address PR review findings

- Fix discardCategory not reverting workspace_registries changes
- Fix get_no_default: convert to async fn with owned Uuid param
- Fix append_logs: use windmill_queue import already available
- Fix ruby URL parsing: support both comma and whitespace delimiters
- Add WorkspaceRegistryMap type alias to reduce inline type noise

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

* all

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 12:49:25 +00:00
Ruben Fiszel
372023e995 feat: add ws_base_url instance setting for WebSocket URL override (#8405)
* feat: add ws_base_url instance setting to override WebSocket base URL

Allow deployments behind reverse proxies to route WebSocket traffic
(LSP, debugger, multiplayer) to a different host/port than the main
frontend via a new instance setting.

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

* feat: move ws_base_url to Advanced section with toggle and connectivity test

- Move setting from Core to Advanced > WebSocket section
- Render as toggle "Custom websocket base url from frontend to
  multiplayer/lsp/debugger" with conditional URL text field
- Add Test connectivity button (always visible) that checks HTTP health
  and WebSocket ping for all three services (LSP, Multiplayer, Debugger)
- Add /ws/ping and /ws/health endpoints to LSP service
- Add /ws_mp/health HTTP and __ping__ WS handlers to multiplayer service
- Add /ping WS handler to debugger service
- Add CORS headers to health endpoints for cross-origin testing

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

* fix: toggle enabled check and testWs promise resolution

- Fix enabled derived to check only for null (not empty string),
  otherwise the toggle never turns on since toggleEnabled sets ''
- Fix testWs onclose handler to resolve(false) so the promise
  doesn't hang if the server closes without sending a message

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

* fix: make connectivity test work with existing services

- HTTP test: accept plain text "ok"/"okay" (old services) in addition
  to JSON {"status": "ok"} (new services), reject HTML (SPA fallback)
- WS test: resolve on onopen (connection established) instead of
  waiting for a specific pong message, so the test works even with
  services that don't have the new /ping handler yet

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 12:48:59 +00:00
Diego Imbert
08215c708b Display workspace ID in select subtitle when creating token (#8407) 2026-03-17 12:33:11 +00:00
Ruben Fiszel
8cd2d06f01 nit fix app 2026-03-17 01:50:16 +00:00
Ruben Fiszel
4e7be0d27a chore: run windows backend tests on release tags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:39:24 +00:00
Ruben Fiszel
ced6f62207 chore: trigger CLI tests on migration changes and release tags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:36:03 +00:00
Ruben Fiszel
6165e01e8a fix migration clash 2026-03-17 01:27:31 +00:00
Ruben Fiszel
31d6660d56 feat: script module mode with CLI sync, preview, and WAC UI improvements (#8380)
* feat: add script module mode with folder model for Bun and Python

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add missing modules field to RawCode in bun_executor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* sqlx

* feat: enrich WAC templates with checkpoint and replay semantics

Add prominent comments explaining that all computation must happen
inside task/step/taskScript or it will be replayed on resume/retry.
Clarify that waitForApproval does not hold a worker and that
approve/reject URLs are available in the timeline step details.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cli): script module sync idempotency, per-module hash tracking, and preview support

- Fix pull→push idempotency: use `??` instead of `||` for module lock
  field so empty strings are preserved (matches API's `lock: ""`)
- Add per-module hash tracking in wmill-lock.yaml following the flow
  inline script pattern (SCRIPT_TOP_HASH + per-module subpath hashes)
- Selective module lock regeneration: only regenerate locks for modules
  whose content actually changed, not all modules
- Use unfiltered rawWorkspaceDependencies for module hashes to match
  what updateModuleLocks passes to fetchScriptLock
- Show changed module names in stale script output for clarity
- Add module support to `script preview` command: read modules from
  __mod/ folder and pass them in the preview API request
- Add preview tests for taskScript pattern (flat and folder layout)
- Update test assertion for module stale detection output

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

* feat(frontend): WAC UI improvements — reorder templates, module tab rename, import consolidation

- Reorder WAC template buttons: TypeScript before Python in
  ScriptBuilder, CreateActionsScript, and CreateActionsFlow
- Remove dropdown items from +Script button (simplify to direct link)
- Move "Import Workflow-as-Code" to +Flow dropdown with dedicated drawer
- Add module tab rename: pencil icon on hover opens popover with
  validation, fixed-width icon container prevents layout shift

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

* feat: remaining module-mode changes from working branch

- Backend parser updates for WAC detection
- CLI sync/types updates for raw app path and module support
- Frontend UI polish (Dev.svelte, ScriptRow, script hash page)
- Test fixture updates

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

* test(cli): add test for module modification detection in generate-metadata

Verifies that modifying a single module file re-triggers stale
detection and only the changed module is listed, not all modules.

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

* fix(backend): critical fixes from PR review

- Fix hardcoded dev path in bun_executor.rs WAC v2 wrapper — use
  "windmill-client" import instead of absolute filesystem path
- Fix missed no_main_func → auto_kind rename in parser TS test
- Add modules column to clone_script SQL (windmill-common and
  windmill-api-workspaces) so cloned scripts retain their modules
- Add modules: None to RawCode structs in worker tests
- Restore complete sqlx cache (merge main's cache + our new queries)

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

* fix(backend): fix clone warning treated as error in CI

Change `.clone()` on double reference to `*k` dereference in
scripts.rs hash implementation. Update sqlx cache with new query
hashes from modified clone_script SQL.

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

* fix(frontend): use published parser wasm versions for CI build

The local file:// paths for windmill-parser-wasm-py and
windmill-parser-wasm-ts don't exist in the Cloudflare Pages build
environment. Revert to published npm versions (1.655.0).

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

* fix(frontend): update parser wasm packages to 1.657.2

Use newly published windmill-parser-wasm-ts and windmill-parser-wasm-py
v1.657.2 which include auto_kind/WAC detection changes.

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

* fix(frontend): regenerate package-lock.json for npm ci compatibility

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

* fix(frontend): use main's lockfile as base, update only parser wasm packages

Regenerating package-lock.json from scratch pulled different dependency
versions causing svelte-check type errors. Instead, start from main's
lockfile and only update the two changed packages.

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

* fix(backend): add modules column to fetch_script_for_update query

The Script<SR> struct has a modules field (FromRow), but
fetch_script_for_update didn't SELECT modules, causing a runtime
error "no column found for name: modules" when the worker processed
dependency jobs. This was the root cause of the relock_skip test
timeout.

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

* fix(backend): fix script module execution for Python and Bun

- Fix modules not passed through job queue: inject _MODULES into
  PushArgs.extra when pushing Code jobs so worker can extract them
- Fix Python module imports: use relative imports (from .helper)
  and add sys.path.insert for module directory in wrapper
- Fix Python tests: use relative imports and empty lock to prevent
  pip from resolving module names as packages
- Add local file check in Bun loader for module resolution
- Ignore Bun module test (bundle mode loader integration tracked
  separately)
- Add missing modules column to fetch_script_for_update query

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

* fix(backend): remove unnecessary empty lock in Python module tests

Relative imports (from .helper) are not parsed as pip packages,
so the empty lock workaround is not needed.

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

* fix(backend): fix module execution for Python and Bun — all tests pass

Python modules:
- Use relative imports (from .helper import greet) since scripts run
  as packages
- Add sys.path.insert for module directory in wrapper to ensure local
  modules take precedence over pip packages with same name

Bun modules:
- Use bundled output (./out/main.js) as wrapper import when modules
  are present — the bundled output has module content inlined by
  Bun.build, avoiding runtime loader resolution issues
- Add local file check in loader.bun.js onResolve to short-circuit
  API URL resolution for module files on disk

Job queue:
- Inject _MODULES into PushArgs.extra when pushing Code jobs so
  the worker can extract them at execution time

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

* refactor: address PR review — simplify, fix correctness, remove dead code

Critical fixes:
- Replace all CLI `no_main_func` references with `auto_kind` (string)
  to match the backend migration and API changes
- Remove duplicated `compute_python_module_dir` in worker.rs, use
  the canonical version from python_executor.rs

High priority:
- Auto-create `__init__.py` in intermediate directories for nested
  Python modules so imports like `from .utils.math import add` work
  without users manually creating __init__.py files
- Remove redundant `sys_path_insert` — relative imports use Python's
  package system, not sys.path

Medium:
- Fix lock file base name extraction: use regex to strip only the
  final extension (`.replace(/\.[^.]+$/, '')`) instead of `indexOf(".")`
  which breaks for files like `helper.test.ts`

Simplification:
- Remove dead `{#if false}` Popover block in ScriptEditor.svelte
- Guard loader.bun.js local file check to only run for relative paths
  (matching the Windows loader pattern)
- Add clarifying comment on Bun dual mechanism (build + run phases)
- Add maintenance comment on manual Hash impl for NewScript

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

* fix: final review fixes — stale cleanup, baseName, auto_kind export

- Fix sync.ts baseName extraction using indexOf(".") → regex
  (same fix as script.ts/metadata.ts, missed this instance)
- Add stale module file cleanup in writeModulesToDisk: removes files
  from __mod/ that are no longer in the modules map before writing,
  fixing the pull→push cycle that couldn't delete modules
- Log warning when _MODULES serialization fails in job push instead
  of silently dropping modules
- Use strict equality (===) for auto_kind comparison
- Exclude auto_kind from workspace export — it is auto-detected by
  the parser at deploy time from script content

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

* fix(cli): remove auto_kind from push, comparison, and metadata

auto_kind is auto-detected by the parser at deploy time, so the CLI
should not send it, compare it, or write it to script.yaml.

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

* fix: remove erroneously added backend/backend/.sqlx directory

Duplicate .sqlx cache was committed at the wrong nested path.

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

* fix: address PR review feedback + fix CI dead_code warning

Frontend (ScriptEditor.svelte):
- Fix switchToMain() missing lastSyncedCode update — prevents stale
  code sync on external changes while editing a module tab
- Fix formatAction saving module code to main script's localStorage
  draft — now saves main code when on a module tab
- Fix non-null assertion on inferModuleLang in renameModule — fall
  back to original language instead of force unwrap
- Remove redundant activeModuleTab truthy check in runTest

CLI (script.ts):
- Clean up empty directories after removing stale module files in
  writeModulesToDisk

Backend:
- Add path traversal guard in write_module_files — reject module
  paths containing ".."
- Fix dead_code warning on auto_kind field in workspace export struct

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

* feat(frontend): improve auto_kind UX + address review findings

- Rename "Include without main function" toggle to "Include library
  scripts" in script list (ItemsList.svelte)
- Update NoMainFuncBadge: "No main" → "Library" with clearer tooltip
- Filter module file extensions by main script language — Python
  scripts only allow .py modules, TypeScript only .ts, etc.
- Split flushModuleState into flushModuleContent (no UI side-effect)
  and flushModuleState (flush + reset tab), reducing duplication
- Dynamic placeholder and hint text in add module popover based on
  main script language

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 01:20:09 +00:00
Alexander Petric
18b3528ba4 feat: instance groups instance-level role support (#8404)
* [ee] feat: instance groups instance-level role support

Add ability to assign instance-level roles (superadmin/devops) to
instance groups. Group members automatically receive the role with
proper precedence: manual elevated roles always win, superadmin > devops.

- Migration: add instance_role to instance_group, role_source to password
- Role propagation on all mutation paths (add/remove/update/delete/import)
- SCIM sync integration (companion PR: windmill-ee-private#463)
- Frontend: role toggle in group editor, role column in tables,
  role source indicator in superadmin settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update ee-repo-ref to 278a3887f759f9d1146554baa0765518d5bc70f2

This commit updates the EE repository reference after PR #463 was merged in windmill-ee-private.

Previous ee-repo-ref: b407fe4604153d09ff223e11d43c2df83a1de5d0

New ee-repo-ref: 278a3887f759f9d1146554baa0765518d5bc70f2

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 <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>
2026-03-17 01:19:46 +00:00
Ruben Fiszel
f2be625348 feat: store hashed tokens instead of plaintext (#8217)
* feat: store hashed tokens in the token table instead of plaintext

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review issues in token hash migration

- Update all base.sql fixtures to include token_hash/token_prefix columns
- Keep plaintext token for webhook tokens (needed for URL reconstruction)
- Restore get_token_by_prefix to query DB for webhook tokens
- Fix down migration to delete NULL-token rows before restoring NOT NULL
- Update parser fixture standalone schema
- Update EE dedicated_worker_ee.rs to use token_hash/token_prefix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore sqlx offline cache (only add new query files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: keep writing plaintext token column for backward compat

Write to token column alongside token_hash until MIN_VERSION_SUPPORTS_TOKEN_HASH
(1.649.0) is reached. This ensures older workers can still authenticate
during rolling upgrades. Remove the separate UPDATE in new_webhook_token
since create_token_internal now writes plaintext directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: branch on MIN_VERSION to write plaintext token or null

Check MIN_VERSION_SUPPORTS_TOKEN_HASH at runtime: write plaintext to
token column while old workers exist, switch to NULL once all workers
are >= 1.649.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: set MIN_VERSION_SUPPORTS_TOKEN_HASH to 1.650.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use token_hash for email lookup and expiry notifications

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rotate webhook tokens instead of recovering plaintext from DB

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use token_hash for native trigger token lookups and deletes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* sqlx

* refactor: drop webhook_token_prefix from native_trigger table

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: backward compat for token rotation and make webhook_token_hash NOT NULL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: prevent panic on short superadmin secret token prefix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: prevent panic on short superadmin secret token prefix

Replace all `token[0..TOKEN_PREFIX_LEN]` slicing with
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent
panics when a token shorter than 10 chars is provided (e.g.
malformed Authorization header, short superadmin secret).

Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>

* fix: prevent panic on short token prefix slicing

Replace all `token[0..TOKEN_PREFIX_LEN]` with safe
`token.get(..TOKEN_PREFIX_LEN).unwrap_or(token)` to prevent panics
on malformed tokens shorter than 10 characters.

Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert "fix: prevent panic on short superadmin secret token prefix"

This reverts commit 37ec2e5ad5.

* revert: remove unnecessary defensive token prefix slicing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add token_hash to end_user_email test fixture

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add integration tests for token hash migration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: correct token_hash test assertions for cache and version

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: add plaintext column removal reminder to test fixtures

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: log count of orphaned triggers deleted during migration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: preserve orphaned triggers with error instead of deleting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename token_expiry_notification.token to token_hash and copy owner/expiration in rotate

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: hash existing plaintext values before renaming token_expiry_notification column

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove unnecessary length check in token_expiry_notification migration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* update dates and version

* updat ee ref + sqlx

* improve mcp migration

* fix: atomic token rotation with rollback on trigger update failure

rotate_webhook_token now atomically inserts the new token and deletes
the old one in a single transaction, preventing token leaks.

Returns new_token_hash so callers can clean up the new token if their
subsequent trigger update fails (which involves external HTTP calls
and cannot be in the same DB transaction).

- Handler: wraps post-rotation work; deletes new token on failure
- Google renewal: deletes new token if service_config update fails
- Tests updated to match new atomic semantics

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

* higher min version

* fix: defer old token deletion to avoid breaking triggers on update failure

rotate_webhook_token now keeps the old token alive and returns
old_token_hash. Callers delete it only after the trigger row has been
successfully updated. If the external service call or DB update fails,
the trigger keeps working with the old token.

Worst case: if the best-effort delete fails, the old token leaks as an
extra DB row — harmless compared to breaking the trigger.

Also update summarized_schema.txt for renamed columns.

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

* chore: update ee-repo-ref to 2d0823a471014e2bc2d898c63518323946b7474f

This commit updates the EE repository reference after PR #437 was merged in windmill-ee-private.

Previous ee-repo-ref: 7aef8b06cb6f54c2bc89dd57b70947deed72553c

New ee-repo-ref: 2d0823a471014e2bc2d898c63518323946b7474f

Automated by sync-ee-ref workflow.

* fix: prevent panic on short tokens by using safe prefix extraction

Add safe_token_prefix() helper that uses .get(..TOKEN_PREFIX_LEN).unwrap_or(token)
instead of direct slice indexing, preventing panics when tokens are shorter than
10 characters (e.g., short superadmin secrets or malformed Bearer tokens).

Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: HugoCasa <hugo@casademont.ch>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: hugocasa <hugocasa@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
2026-03-17 01:15:38 +00:00
Ruben Fiszel
fd41cd12b4 fix: improve OOM killer observability for debugging pod-level kills (#8398)
* fix: improve OOM killer observability for debugging pod-level kills

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

* fix: lower worker oom_score_adj to protect it from OOM killer

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

* fix: address PR review feedback on OOM observability

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:49:21 +00:00
hugocasa
de5b13b840 feat: add end_user_email claim to OIDC ID tokens (#8401)
* feat: add end_user_email claim to OIDC ID tokens

When a job is triggered by an app end user, the OIDC ID token now
includes the end_user_email claim automatically. The claim is omitted
for jobs without an end user (regular script/flow runs).

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

* update ee ref

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:17:03 +00:00
centdix
5751e9b26b chore: return structured error object on AI agent max iterations (#8403)
* fix: return structured error object on AI agent max iterations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: avoid double serialization in max iterations error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace unwrap with ? for to_raw_value in max iterations error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add step_id to max iterations error for consistency with SerializedError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 23:12:29 +00:00
hugocasa
0b65c3d8fa index EE files in wm-ts-nav code navigator (#8400)
EE files (*_ee.rs, *_ee.ts, *_ee.svelte) are symlinks from
windmill-ee-private that are gitignored. The walker skipped them
because it respects .gitignore. This adds a separate recursive scan
for _ee files and merges them into the index. Also fixes outline
resolving symlinks via canonicalize, causing path mismatches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:57:38 +00:00
Ruben Fiszel
9554876d8b chore(main): release 1.658.0 (#8382)
* chore(main): release 1.658.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-16 15:41:17 +00:00
Ruben Fiszel
b31a475c88 feat: add workspace dependencies support for powershell (#8395)
* feat: add workspace dependencies support for powershell

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

* feat: improve workspace deps editor UX for powershell

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

* feat: add powershell workspace deps support to CLI

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:37:28 +00:00
hugocasa
1eee89d99f fix(native-triggers): preserve API error response body in HttpRequestError (#8392)
* fix(native-triggers): preserve API error response body in HttpRequestError

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(frontend): use instance credentials for reconnect when instance_shared

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-16 15:30:59 +00:00
Ruben Fiszel
8417c5b792 chore: split debug info for EE release builds (#8396)
* chore: split debug info for EE release builds

Generate line-table debug info in release builds and split it into
a separate .debug file. The shipped binary remains stripped (same
size as before), while the .debug files are attached to GitHub
releases for both amd64 and arm64 EE builds.

This enables production debugging with gdb/perf by copying the
matching .debug file into a running pod.

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

* chore: extract debug info via separate Docker stage

Use a `FROM scratch AS debuginfo` stage instead of copying the .debug
file to the final image. This keeps the shipped image at exactly the
same size as before. CI extracts the .debug file using depot's
--target debuginfo with cache hits from the main build.

Also adds gnu_debuglink so gdb auto-discovers the debug file when
placed next to the binary.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:30:29 +00:00
Ruben Fiszel
50b24cfdc8 feat: add GET /api/saml/metadata endpoint (#8394)
* [ee] feat: add GET /api/saml/metadata endpoint for SP metadata XML

Serves static SAML 2.0 EntityDescriptor XML so IDPs can configure SSO
without requiring IDP metadata to be set up first in Windmill.

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

* chore: update ee-repo-ref to b2fde51087d0d0ee0223c15cafb4e8badddd2d13

This commit updates the EE repository reference after PR #461 was merged in windmill-ee-private.

Previous ee-repo-ref: 187f12947b69e584523ace93957d0be0ceb7b37c

New ee-repo-ref: b2fde51087d0d0ee0223c15cafb4e8badddd2d13

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-16 15:18:23 +00:00
centdix
1a1e8a164c fix: soft error on AI agent max iterations + rename retries tab to error handling (#8366)
* fix: soft error when AI agent reaches max iterations instead of hard error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: set output to error message when AI agent reaches max iterations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add error field to AI agent result for max iterations soft error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore default max iterations to 10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include partial result in max iterations error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: rename retries tab to error handling and reorganize sections

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:13:22 +00:00
centdix
020de59fcf make flake dev env vars respect per-worktree overrides (#8374)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:12:52 +00:00
claude[bot]
54841b7549 feat: support multiple secret variables during resource creation (#8386)
* feat: support multiple secret variables during resource creation

When creating a resource, users can now select multiple fields to be
stored as secret variables. If only one field is selected, behavior
is unchanged (single variable with same path as resource). If multiple
fields are selected, each gets its own variable with _field_name appended
to the resource path.

Closes #8384

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>

* fix: delete all linked secret variables when resource is deleted

When a resource with multiple secret fields is deleted, also delete
variables matching the {path}_{field_name} pattern in addition to
the exact path variable. Each deleted variable gets its own deployment
metadata update and webhook notification.

Co-authored-by: Diego Imbert <diegoimbert@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update backend/windmill-store/src/resources.rs

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* fix: only delete linked variables that are actually referenced in resource value

Instead of deleting variables purely based on path prefix matching
(which could accidentally delete unrelated variables), the deletion
now reads the resource value first, extracts all $var: references,
and only deletes variables that are actually used in the resource.

Co-authored-by: Diego Imbert <diegoimbert@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Diego Imbert <diegoimbert@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-03-16 14:10:29 +00:00
Diego Imbert
9fdea3e058 nit console log (#8385) 2026-03-16 12:07:45 +00:00
Guilhem
115e476c8a gitignore: exclude .webmux.local.yaml (#8388)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:07:34 +00:00
Diego Imbert
a519d41130 fix: /updatesqlx now uses ee-repo-ref.txt commit hash (#8387)
The /updatesqlx workflow was checking out windmill-ee-private at its
default branch HEAD, ignoring the specific commit pinned in
backend/ee-repo-ref.txt. This could cause sqlx metadata to be generated
against a mismatched EE version.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:07:18 +00:00
hugocasa
65a92d9899 fix: set nsjail time_limit from job timeout so configured defaults are respected (#8389)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:06:31 +00:00
Diego Imbert
2907084ca6 fix: OutputPicker shows stale result after 'Test up to here' (#8390)
OutputPickerInner.updateLastJob() unconditionally returned testJob
(from individual step tests) even when flowStateStore had newer results
from a flow test. Now testJob only takes priority when a step test is
actively running/streaming; otherwise flowStateStore is the source of
truth.

Also reset stepHistoryLoader initial flags when a flow test completes
so the "Run loaded from history" indicator doesn't persist.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:06:18 +00:00
Ruben Fiszel
50ef9e79fc fix: propagate enterprise feature to windmill-api-schedule (#8391)
The enterprise feature was not being forwarded to windmill-api-schedule
in windmill-api's Cargo.toml, causing the #[cfg(not(feature = "enterprise"))]
guards in create_schedule to remain active even in EE builds. This made
on_recovery, on_success, and on_failure_times>1 incorrectly rejected
with "only available in enterprise version" for enterprise customers.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 11:59:15 +00:00
centdix
5acb367cf9 feat: support custom headers in customai resource type (#8364)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 11:31:22 +00:00
Ruben Fiszel
68fd900076 fix: use bookworm-based php image to fix glibc 2.38 incompatibility (#8381)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-15 19:03:09 +00:00
Ruben Fiszel
82bfa9613c chore(main): release 1.657.2 (#8376)
* chore(main): release 1.657.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-15 05:05:49 +00:00
Ruben Fiszel
b1b9c984e3 make wmill init generated skills respect nonDottedPaths config (#8377)
* docs: add nonDottedPaths convention to CLAUDE.md

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

* docs(cli): update generated skills to use non-dotted path conventions

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

* fix(cli): make generated skills respect nonDottedPaths config

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

* fix(cli): inject nonDottedPaths placeholders in generate.py for skills.ts

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

* fix: default system prompts to non-dotted path conventions

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:05:31 +00:00
Travis Pew
eb03ebbb04 fix(cli): Fix nonDottedPaths handling in cli flow lock generation (#8375)
* fix(cli): preserve non-dotted flow lock filenames

* test(cli): add non-dotted path tests for generate-metadata and sync pull

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

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:36:04 +00:00
Ruben Fiszel
5296adeddf test: add powershell module detection and execution tests (#8373)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:11:43 +00:00
Ruben Fiszel
1a061892e9 chore(main): release 1.657.1 (#8372)
* chore(main): release 1.657.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-14 23:24:31 +00:00
Ruben Fiszel
daade374b3 restore flat module detection with file existence check (#8371)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:11:23 +00:00
Ruben Fiszel
3a268a9cf1 fix: powershell WindmillClient module loading on Windows workers (#8370)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:09:00 +00:00
Ruben Fiszel
b6da492d1b chore(main): release 1.657.0 (#8368)
* chore(main): release 1.657.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-14 04:52:06 +00:00
Ruben Fiszel
87215193ca system promps generate metadata 2026-03-14 04:51:54 +00:00
Alexander Petric
5df37fb0db feat: add datatable config support to CLI settings sync and backend export (#8024)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-14 04:44:11 +00:00
Ruben Fiszel
6fa2543647 chore(main): release 1.656.0 (#8346) 2026-03-13 22:32:57 +00:00
hugocasa
c431053a1e fix(frontend): prevent duplicate and reserved agent tool names (#8367)
* fix(frontend): prevent duplicate and reserved agent tool names

Extend tool name validation to detect duplicates within an agent step
and reserved names (like 'preprocessor', 'failure'). Show specific error
messages in the editor panel and red styling in the graph view.

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

* fix(frontend): remove duplicate banner for agent tool name errors

The inline per-tool error messages are sufficient — the panel-level
banner was redundant and showed a double error.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 22:30:53 +00:00
Ruben Fiszel
a079dd500f i pkg 2026-03-13 22:21:28 +00:00
centdix
9d2c439e2a fix: resource drawer opening behind dialog in chat mode (#8328)
* fix: resource drawer opening behind dialog in chat mode

Integrate Modal into the Disposable z-index stacking system so drawers
opened from within a modal (e.g. "Add a new resource") correctly appear
above the dialog instead of behind it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resource drawer opening behind dialog in chat mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: simplify minZIndex tracking by removing unnecessary refcount

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use map-based minZIndex tracking and conditional chat elevation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use plain object instead of Map for reactive minZIndex tracking

$state(new Map()) is not deeply reactive in Svelte 5 — only plain
objects and arrays are proxied. Replaced with Record<string, number>
so that property assignments properly trigger $derived updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:33:16 +00:00
hugocasa
fb12b31df0 fix(frontend): improve native mode alert message and fix workspaced tag detection (#8361)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:32:29 +00:00
Pyra
51933be3ca fix(cli): suppress verbose lock generation messages in generate-metadata (#8357)
* fix(cli): suppress verbose lock generation messages in generate-metadata

Pass noStaleMessage flag through to updateRawAppRunnables and
updateAppInlineScripts to suppress verbose "Generating lock for..."
messages when running generate-metadata command. Also fixes a stray
`}` character in a template literal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(cli): show updated inline scripts in generate-metadata output

Display inline script names that were relocked when processing flows
and apps in the generate-metadata command output. For example:

  [4/5] app    u/admin/test__raw_app: a, b, c

This provides visibility into what work was done without verbose
per-script logging that clutters the output.

- Add AppLocksResult and FlowLocksResult types to track updated scripts
- Update internal functions to return lists of updated script names
- Display script names inline with progress in generate-metadata

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-13 20:32:08 +00:00
Pyra
404ae09d42 fix(cli): normalize path separators in generate-metadata folder filter for Windows (#8358)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-13 20:31:44 +00:00
Diego Imbert
e0e78442b7 Support T | T[] in debounce (#8340)
* Detect union types in TS

* display union type arguments

* Handle single values at accumulation time

* nit propagate otyp

* Python support

* npm package update
2026-03-13 20:31:25 +00:00
hugocasa
0d31c35f3e fix(frontend): filter webhook/email tokens by scope instead of label (#8363)
The backend already filters tokens by scope matching the script/flow
path. Remove the redundant client-side label prefix filter so that all
tokens with matching scopes are shown, not just those with a specific
label convention.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:31:06 +00:00
Pyra
060687b1fa fix(cli): exclude raw app backend files from script metadata generation (#8362)
Files inside .raw_app/backend/ were incorrectly being processed by
`script generate-metadata` and `generate-metadata --skip-flows --skip-apps`
because the filter only checked isFlowPath and isAppPath, but not isRawAppPath.

This caused backend runnables to be treated as standalone scripts, creating
incorrect .script.yaml files at wrong locations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-13 20:30:23 +00:00
HugoCasa
8301d86800 docs: rewrite Code Navigation section with MUST for outline/body and condensed limitations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:45:06 +01:00
Ruben Fiszel
44dd3ee8cd fix(ci): remove provenance flag and use NPM_TOKEN for npm publish
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:43:45 +00:00
Ruben Fiszel
2a8e276b6d fix(ci): add NODE_AUTH_TOKEN for npm publish authentication
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:38:41 +00:00
Ruben Fiszel
bc35c94616 ci 2026-03-13 12:29:58 +00:00
Guilhem
b585dee64d fix(frontend): collapse flow topbar buttons to icon-only in narrow panes (#8322)
* feat: collapse flow topbar buttons to icon-only mode in narrow panes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: show delete button on top-right of compact error handler

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: preserve bug icon and diff action bar in compact error handler

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: avoid duplicate delete buttons when diff action bar is active

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: use undefined instead of empty string for wrapperClasses

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-13 12:08:19 +00:00
hugocasa
96229575e6 chore: dev tooling — wm-ts-nav navigator, format hooks, review skill (#8337)
* chore: remove wm-cursor, add local-review skill, update PR skill for EE

- Remove the unused wm-cursor script and all references to it in
  README_WORKMUX_DEV.md and worktree-common.sh
- Add /local-review skill for code review (bugs + CLAUDE.md compliance)
- Add EE companion PR workflow to the /pr skill

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add wm-ts-nav tree-sitter navigator and fix format hooks

- Add wm-ts-nav: standalone tree-sitter code navigator with SQLite index
  for fast symbol search, definition lookup, and file outlines across
  Rust, TypeScript, and Svelte files (~12ms warm, ~1s cold for 482 files)
- Fix format hooks: surface errors instead of swallowing with 2>/dev/null,
  use direct prettier path with svelte plugin, add success feedback
- Add wm-ts-nav commands to settings allow list
- Document wm-ts-nav usage in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): add refs command and --parent filter

- refs: find usages of a symbol in code, skipping comments and strings
  (tree-sitter AST walk, ~46ms for 482 files vs grep's 4ms but no noise)
- --parent filter on search: find all methods on a type across all files
  (e.g. search "%" --kind function --parent ServiceName)
- Update CLAUDE.md with clearer when-to-use guidance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): index refs in DB with import-path resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(wm-ts-nav): add body, callers, callees commands and refs --file/--caller

- body: extract a symbol's source code from disk using indexed line ranges
- callers: cross-file call graph via SQL join of refs + symbols tables
- callees: list all identifiers referenced within a symbol's body
- refs --file: scope results to files matching a substring
- refs --caller: annotate each ref with the containing function name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): add auto-rebuilding wrapper script

The `wm-ts-nav/nav` wrapper checks if source files are newer than the
binary and rebuilds automatically. Invoked via `sh wm-ts-nav/nav` to
avoid needing executable permissions after clone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: tighten CLAUDE.md nav section for actionable guidance

Remove redundant question→command mapping, latency numbers, and
excessive examples. Lead with "prefer wm-ts-nav over Read to save
context window" and keep only the patterns that change behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: revert backend/Cargo.lock to main

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: promote wm-ts-nav in workflow, copy binary to worktrees

- CLAUDE.md: integrate wm-ts-nav into Workflow step 1 and Core
  Principles so agents use outline/body before full file reads
- workmux: copy built binary via files.copy
- worktree-common.sh: copy binary in wm_copy_dependencies for webmux

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wm-ts-nav): fix double indexing, add TSX grammar, remove needless clone

- Reuse index stats from the pre-query update instead of indexing twice
  on the Index command
- Add Lang::Tsx variant so .tsx/.jsx files use LANGUAGE_TSX instead of
  LANGUAGE_TYPESCRIPT (Svelte stays on TS since script blocks are pure TS)
- Remove source.clone() for non-Svelte files — move directly instead

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wm-ts-nav): fix svelte line numbers, add class methods, innermost caller

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:07:49 +00:00
735 changed files with 39455 additions and 5916 deletions

View File

@@ -0,0 +1,59 @@
---
name: commit
description: Create a git commit with conventional commit format. MUST use anytime you want to commit changes.
---
# Git Commit Skill
Create a focused, single-line commit following conventional commit conventions.
## Instructions
1. **Analyze changes**: Run `git status` and `git diff` to understand what was modified
2. **Stage only modified files**: Add files individually by name. NEVER use `git add -A` or `git add .`
3. **Write commit message**: Follow the conventional commit format as a single line
## Conventional Commit Format
```
<type>: <description>
```
### Types
- `feat`: New feature or capability
- `fix`: Bug fix
- `refactor`: Code change that neither fixes a bug nor adds a feature
- `docs`: Documentation only changes
- `style`: Formatting, missing semicolons, etc (no code change)
- `test`: Adding or correcting tests
- `chore`: Maintenance tasks, dependency updates, etc
- `perf`: Performance improvement
### Rules
- Message MUST be a single line (no multi-line messages)
- Description should be lowercase, imperative mood ("add" not "added")
- No period at the end
- Keep under 72 characters total
### Examples
```
feat: add token usage tracking for AI providers
fix: resolve null pointer in job executor
refactor: extract common validation logic
docs: update API endpoint documentation
chore: upgrade sqlx to 0.7
```
## Execution Steps
1. Run `git status` to see all changes
2. Run `git diff` to understand the changes in detail
3. Run `git log --oneline -5` to see recent commit style
4. Stage ONLY the modified/relevant files: `git add <file1> <file2> ...`
5. Create the commit with conventional format:
```bash
git commit -m "<type>: <description>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
```
6. Run `git status` to verify the commit succeeded

View File

@@ -0,0 +1,97 @@
---
name: local-review
description: Code review a pull request for bugs and CLAUDE.md compliance. MUST use when asked to review code.
---
# Local Code Review Skill
Review a pull request for real bugs and CLAUDE.md compliance violations. This review targets HIGH SIGNAL issues only.
## Review Philosophy
- **Only flag issues you are certain about.** If you are not sure an issue is real, do not flag it. False positives erode trust and waste reviewer time.
- Think like a senior engineer doing a final review — flag things that would cause incidents, not things that are merely imperfect.
## What to Flag
- Code that won't compile or parse (syntax errors, type errors, missing imports)
- Code that will definitely produce wrong results regardless of inputs
- Clear, unambiguous CLAUDE.md violations (quote the exact rule being violated)
- Security issues in introduced code (injection, auth bypass, data exposure)
- Incorrect logic that will fail in production
## What NOT to Flag
- Code style or quality concerns
- Potential issues that depend on specific inputs or runtime state
- Subjective suggestions or improvements
- Pre-existing issues not introduced by this PR
- Pedantic nitpicks a senior engineer wouldn't flag
- Issues a linter or type checker will catch
- General quality concerns unless explicitly prohibited in CLAUDE.md
- Issues silenced via lint ignore comments
## Execution Steps
1. **Determine the PR scope**:
- If an argument is provided, use it as the PR number or branch
- Otherwise, detect from the current branch vs main
- Run `gh pr view` if a PR exists, or use `git diff main...HEAD`
2. **Find relevant CLAUDE.md files**:
- Read the root `CLAUDE.md`
- Check for CLAUDE.md files in directories containing changed files
3. **Get the diff and metadata**:
- `gh pr diff` or `git diff main...HEAD` for the full diff
- `gh pr view` or `git log main..HEAD --oneline` for context
4. **Read changed files** where the diff alone is insufficient to understand context
5. **Review for**:
- CLAUDE.md compliance — check each rule against the changed code
- Bugs and logic errors — will this code work correctly?
- Security issues — injection, auth, data exposure in new code
6. **Self-validate each finding**: Before reporting, ask yourself:
- "Is this definitely a real issue, not a false positive?"
- "Would a senior engineer flag this in review?"
- If the answer to either is no, discard the finding
7. **Output findings** to the terminal (default) or post as PR comments (with `--comment` flag)
## Output Format
```
## Code review
Found N issues:
1. <description> (<reason: CLAUDE.md adherence | bug | security>)
<file_path:line_number>
2. <description> (<reason>)
<file_path:line_number>
```
If no issues are found:
```
## Code review
No issues found. Checked for bugs and CLAUDE.md compliance.
```
## Posting Comments (--comment flag)
If the user passes `--comment`, post findings as inline PR comments using:
```bash
gh pr review --comment --body "<summary>"
```
Or for inline comments on specific lines:
```bash
gh api repos/{owner}/{repo}/pulls/{pr}/reviews -f body="<summary>" -f event="COMMENT" -f comments="[...]"
```

View File

@@ -0,0 +1,777 @@
# Skill: Adding Native Trigger Services
This skill provides comprehensive guidance for adding new native trigger services to Windmill. Native triggers allow external services (like Nextcloud, Google Drive, etc.) to trigger Windmill scripts/flows via webhooks or push notifications.
## Architecture Overview
The native trigger system consists of:
1. **Database Layer** - PostgreSQL tables and enum types
2. **Backend Rust Implementation** - Core trait, handlers, and service modules in the `windmill-native-triggers` crate
3. **Frontend Svelte Components** - Configuration forms and UI components
### Key Files
| Component | Path |
|-----------|------|
| Core module with `External` trait | `backend/windmill-native-triggers/src/lib.rs` |
| Generic CRUD handlers | `backend/windmill-native-triggers/src/handler.rs` |
| Background sync logic | `backend/windmill-native-triggers/src/sync.rs` |
| OAuth/workspace integration | `backend/windmill-native-triggers/src/workspace_integrations.rs` |
| Re-export shim (windmill-api) | `backend/windmill-api/src/native_triggers/mod.rs` |
| TriggerKind enum | `backend/windmill-common/src/triggers.rs` |
| JobTriggerKind enum | `backend/windmill-common/src/jobs.rs` |
| Frontend service registry | `frontend/src/lib/components/triggers/native/utils.ts` |
| Frontend trigger utilities | `frontend/src/lib/components/triggers/utils.ts` |
| Trigger badges (icons + counts) | `frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte` |
| Workspace integrations UI | `frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte` |
| OAuth config form component | `frontend/src/lib/components/workspaceSettings/OAuthClientConfig.svelte` |
| OpenAPI spec | `backend/windmill-api/openapi.yaml` |
| Reference: Nextcloud module | `backend/windmill-native-triggers/src/nextcloud/` |
| Reference: Google module | `backend/windmill-native-triggers/src/google/` |
### Crate Structure
The native trigger code lives in the `windmill-native-triggers` crate (`backend/windmill-native-triggers/`). The `windmill-api` crate re-exports everything via a shim:
```rust
// backend/windmill-api/src/native_triggers/mod.rs
pub use windmill_native_triggers::*;
```
All new service modules go in `backend/windmill-native-triggers/src/`.
---
## Core Concepts
### The `External` Trait
Every native trigger service implements the `External` trait defined in `lib.rs`:
```rust
#[async_trait]
pub trait External: Send + Sync + 'static {
// Associated types:
type ServiceConfig: Debug + DeserializeOwned + Serialize + Send + Sync;
type TriggerData: Debug + Serialize + Send + Sync;
type OAuthData: DeserializeOwned + Serialize + Clone + Send + Sync;
type CreateResponse: DeserializeOwned + Send + Sync;
// Constants:
const SUPPORT_WEBHOOK: bool;
const SERVICE_NAME: ServiceName;
const DISPLAY_NAME: &'static str;
const TOKEN_ENDPOINT: &'static str;
const REFRESH_ENDPOINT: &'static str;
const AUTH_ENDPOINT: &'static str;
// Required methods:
async fn create(&self, w_id, oauth_data, webhook_token, data, db, tx) -> Result<Self::CreateResponse>;
async fn update(&self, w_id, oauth_data, external_id, webhook_token, data, db, tx) -> Result<serde_json::Value>;
async fn get(&self, w_id, oauth_data, external_id, db, tx) -> Result<Self::TriggerData>;
async fn delete(&self, w_id, oauth_data, external_id, db, tx) -> Result<()>;
async fn exists(&self, w_id, oauth_data, external_id, db, tx) -> Result<bool>;
async fn maintain_triggers(&self, db, workspace_id, triggers, oauth_data, synced, errors);
fn external_id_and_metadata_from_response(&self, resp) -> (String, Option<serde_json::Value>);
// Methods with defaults:
async fn prepare_webhook(&self, db, w_id, headers, body, script_path, is_flow) -> Result<PushArgsOwned>;
fn service_config_from_create_response(&self, data, resp) -> Option<serde_json::Value>;
fn additional_routes(&self) -> axum::Router;
async fn http_client_request<T, B>(&self, url, method, workspace_id, tx, db, headers, body) -> Result<T>;
}
```
Key design points:
- **`update()` returns `serde_json::Value`** - the resolved service_config to store. Each service is responsible for building the final config.
- **`maintain_triggers()`** - periodic background maintenance. Each service implements its own strategy (Nextcloud: reconcile with external state; Google: renew expiring channels).
- **No `list_all()` in the trait** - services that need it (Nextcloud) implement it privately; services that don't (Google) use different maintenance strategies.
- **No `get_external_id_from_trigger_data()` or `extract_service_config_from_trigger_data()`** - removed in favor of the `maintain_triggers` pattern.
### Create Lifecycle: Two Paths
The `create_native_trigger` handler in `handler.rs` supports two creation flows, controlled by `service_config_from_create_response()`:
**Path A: Short (Google pattern)** - `service_config_from_create_response()` returns `Some(config)`:
1. `create()` registers on external service
2. `external_id_and_metadata_from_response()` extracts the ID
3. `service_config_from_create_response()` builds the config directly from input data + response metadata
4. Stores trigger in DB -- done, no extra round-trip
Use this when the external_id is known before the create call (e.g., Google generates the channel_id as a UUID upfront and includes it in the webhook URL).
**Path B: Long (Nextcloud pattern)** - `service_config_from_create_response()` returns `None` (default):
1. `create()` registers on external service (webhook URL has no external_id yet)
2. `external_id_and_metadata_from_response()` extracts the ID
3. `update()` is called to fix the webhook URL with the now-known external_id
4. `update()` returns the resolved service_config
5. Stores trigger in DB
Use this when the external_id is assigned by the remote service and the webhook URL needs to be corrected after creation.
### OAuth Token Storage (Three-Table Pattern)
OAuth tokens are stored across three tables, NOT in `workspace_integrations.oauth_data` directly:
| Table | What's Stored |
|-------|---------------|
| `workspace_integrations` | `oauth_data` JSON with `base_url`, `client_id`, `client_secret`, `instance_shared` flag; `resource_path` pointing to the variable |
| `variable` | Encrypted `access_token` (at the path stored in `resource_path`), linked to `account` via `account` column |
| `account` | `refresh_token`, keyed by `workspace_id` + `client` (service name) + `is_workspace_integration = true` |
The `decrypt_oauth_data()` function in `lib.rs` assembles these into a unified struct:
```rust
pub struct OAuthConfig {
pub base_url: String,
pub access_token: String, // decrypted from variable
pub refresh_token: Option<String>, // from account table
pub client_id: String, // from oauth_data or instance settings
pub client_secret: String, // from oauth_data or instance settings
}
```
Instance-level sharing: when `oauth_data.instance_shared == true`, `client_id` and `client_secret` are read from global settings instead of workspace_integrations.
### URL Resolution
The `resolve_endpoint()` helper handles both absolute and relative OAuth URLs:
```rust
pub fn resolve_endpoint(base_url: &str, endpoint: &str) -> String {
if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
endpoint.to_string() // Google: absolute URLs
} else {
format!("{}{}", base_url, endpoint) // Nextcloud: relative paths
}
}
```
### ServiceName Methods
`ServiceName` is the central registry enum. Each variant must implement these match arms:
| Method | Purpose |
|--------|---------|
| `as_str()` | Lowercase identifier (e.g., `"google"`) |
| `as_trigger_kind()` | Maps to `TriggerKind` enum |
| `as_job_trigger_kind()` | Maps to `JobTriggerKind` enum |
| `token_endpoint()` | OAuth token endpoint (relative or absolute) |
| `auth_endpoint()` | OAuth authorization endpoint |
| `oauth_scopes()` | Space-separated OAuth scopes |
| `resource_type()` | Resource type for token storage (e.g., `"gworkspace"`) |
| `extra_auth_params()` | Extra OAuth params (e.g., Google needs `access_type=offline`, `prompt=consent`) |
| `integration_service()` | Maps to the workspace integration service (usually `*self`) |
| `TryFrom<String>` | Parse from string |
| `Display` | Delegates to `as_str()` |
---
## Step-by-Step Implementation Guide
### Step 1: Database Migration
Create a new migration file: `backend/migrations/YYYYMMDDHHMMSS_newservice_trigger.up.sql`
```sql
-- Add the service to the native_trigger_service enum
ALTER TYPE native_trigger_service ADD VALUE IF NOT EXISTS 'newservice';
-- Add to TRIGGER_KIND enum (used for trigger tracking)
ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'newservice';
-- Add to job_trigger_kind enum (used for job tracking)
ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'newservice';
```
Also create the corresponding down migration.
### Step 2: Update windmill-common Enums
#### `backend/windmill-common/src/triggers.rs`
Add variant to `TriggerKind` enum, and update `to_key()` and `fmt()` implementations.
#### `backend/windmill-common/src/jobs.rs`
Add variant to `JobTriggerKind` enum and update the `Display` implementation.
### Step 3: Backend Service Module
Create a new directory: `backend/windmill-native-triggers/src/newservice/`
#### `mod.rs` - Type Definitions
```rust
use serde::{Deserialize, Serialize};
pub mod external;
// pub mod routes; // Only if you need additional service-specific routes
/// OAuth data deserialized from the three-table pattern.
/// The actual structure is built by decrypt_oauth_data() from variable + account + workspace_integrations.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NewServiceOAuthData {
pub base_url: String, // from workspace_integrations.oauth_data
pub access_token: String, // decrypted from variable table
pub refresh_token: Option<String>, // from account table
// Note: client_id and client_secret are in OAuthConfig, not here
// unless the service needs them at runtime for API calls
}
/// Configuration provided by user when creating/updating a trigger.
/// Stored as JSON in native_trigger.service_config.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewServiceConfig {
// Service-specific configuration fields
pub folder_path: String,
pub file_filter: Option<String>,
}
/// Data retrieved from the external service about a trigger.
/// Returned by the get() method and shown in the UI.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewServiceTriggerData {
pub folder_path: String,
pub file_filter: Option<String>,
// Fields that shouldn't affect service_config comparison should use #[serde(skip_serializing)]
}
/// Response from external service when creating a trigger/webhook.
#[derive(Debug, Deserialize)]
pub struct CreateTriggerResponse {
pub id: String,
}
/// Handler struct (stateless, used for routing)
#[derive(Copy, Clone)]
pub struct NewService;
```
#### `external.rs` - External Trait Implementation
```rust
use async_trait::async_trait;
use reqwest::Method;
use sqlx::PgConnection;
use std::collections::HashMap;
use windmill_common::{
error::{Error, Result},
BASE_URL, DB,
};
use crate::{
generate_webhook_service_url, External, NativeTrigger, NativeTriggerData, ServiceName,
sync::{SyncError, TriggerSyncInfo},
};
use super::{NewService, NewServiceConfig, NewServiceOAuthData, NewServiceTriggerData, CreateTriggerResponse};
#[async_trait]
impl External for NewService {
type ServiceConfig = NewServiceConfig;
type TriggerData = NewServiceTriggerData;
type OAuthData = NewServiceOAuthData;
type CreateResponse = CreateTriggerResponse;
const SERVICE_NAME: ServiceName = ServiceName::NewService;
const DISPLAY_NAME: &'static str = "New Service";
const SUPPORT_WEBHOOK: bool = true;
const TOKEN_ENDPOINT: &'static str = "/oauth/token";
const REFRESH_ENDPOINT: &'static str = "/oauth/token";
const AUTH_ENDPOINT: &'static str = "/oauth/authorize";
async fn create(
&self,
w_id: &str,
oauth_data: &Self::OAuthData,
webhook_token: &str,
data: &NativeTriggerData<Self::ServiceConfig>,
db: &DB,
tx: &mut PgConnection,
) -> Result<Self::CreateResponse> {
let base_url = &*BASE_URL.read().await;
// external_id is None during create (we get it from the response)
let webhook_url = generate_webhook_service_url(
base_url, w_id, &data.script_path, data.is_flow,
None, Self::SERVICE_NAME, webhook_token,
);
let url = format!("{}/api/webhooks/create", oauth_data.base_url);
let payload = serde_json::json!({
"callback_url": webhook_url,
"folder_path": data.service_config.folder_path,
});
let response: CreateTriggerResponse = self
.http_client_request(&url, Method::POST, w_id, tx, db, None, Some(&payload))
.await?;
Ok(response)
}
/// Update returns the resolved service_config as JSON.
/// For services using the update+get pattern, call self.get() and serialize.
async fn update(
&self,
w_id: &str,
oauth_data: &Self::OAuthData,
external_id: &str,
webhook_token: &str,
data: &NativeTriggerData<Self::ServiceConfig>,
db: &DB,
tx: &mut PgConnection,
) -> Result<serde_json::Value> {
let base_url = &*BASE_URL.read().await;
let webhook_url = generate_webhook_service_url(
base_url, w_id, &data.script_path, data.is_flow,
Some(external_id), Self::SERVICE_NAME, webhook_token,
);
let url = format!("{}/api/webhooks/{}", oauth_data.base_url, external_id);
let payload = serde_json::json!({
"callback_url": webhook_url,
"folder_path": data.service_config.folder_path,
});
let _: serde_json::Value = self
.http_client_request(&url, Method::PUT, w_id, tx, db, None, Some(&payload))
.await?;
// Fetch back the updated state to get the resolved config
let trigger_data = self.get(w_id, oauth_data, external_id, db, tx).await?;
serde_json::to_value(&trigger_data)
.map_err(|e| Error::InternalErr(format!("Failed to serialize trigger data: {}", e)))
}
async fn get(
&self,
w_id: &str,
oauth_data: &Self::OAuthData,
external_id: &str,
db: &DB,
tx: &mut PgConnection,
) -> Result<Self::TriggerData> {
let url = format!("{}/api/webhooks/{}", oauth_data.base_url, external_id);
self.http_client_request::<_, ()>(&url, Method::GET, w_id, tx, db, None, None).await
}
async fn delete(
&self,
w_id: &str,
oauth_data: &Self::OAuthData,
external_id: &str,
db: &DB,
tx: &mut PgConnection,
) -> Result<()> {
let url = format!("{}/api/webhooks/{}", oauth_data.base_url, external_id);
let _: serde_json::Value = self
.http_client_request::<_, ()>(&url, Method::DELETE, w_id, tx, db, None, None)
.await
.or_else(|e| match &e {
Error::InternalErr(msg) if msg.contains("404") => Ok(serde_json::Value::Null),
_ => Err(e),
})?;
Ok(())
}
async fn exists(
&self,
w_id: &str,
oauth_data: &Self::OAuthData,
external_id: &str,
db: &DB,
tx: &mut PgConnection,
) -> Result<bool> {
match self.get(w_id, oauth_data, external_id, db, tx).await {
Ok(_) => Ok(true),
Err(Error::NotFound(_)) => Ok(false),
Err(e) => Err(e),
}
}
/// Background maintenance. Choose the right pattern for your service:
/// - For services with queryable external state: use reconcile_with_external_state()
/// - For channel-based services with expiration: implement renewal logic
async fn maintain_triggers(
&self,
db: &DB,
workspace_id: &str,
triggers: &[NativeTrigger],
oauth_data: &Self::OAuthData,
synced: &mut Vec<TriggerSyncInfo>,
errors: &mut Vec<SyncError>,
) {
// Option A: Reconcile with external state (Nextcloud pattern)
// Fetch all triggers from external service and compare with DB
let external_triggers = match self.list_all(workspace_id, oauth_data, db).await {
Ok(triggers) => triggers,
Err(e) => {
errors.push(SyncError {
resource_path: format!("workspace:{}", workspace_id),
error_message: format!("Failed to list triggers: {}", e),
error_type: "api_error".to_string(),
});
return;
}
};
// Convert to (external_id, config_json) pairs
let external_pairs: Vec<(String, serde_json::Value)> = external_triggers
.into_iter()
.map(|t| (t.id.clone(), serde_json::to_value(&t).unwrap_or_default()))
.collect();
crate::sync::reconcile_with_external_state(
db, workspace_id, Self::SERVICE_NAME, triggers, &external_pairs, synced, errors,
).await;
}
fn external_id_and_metadata_from_response(
&self,
resp: &Self::CreateResponse,
) -> (String, Option<serde_json::Value>) {
(resp.id.clone(), None)
}
// service_config_from_create_response: NOT overridden (returns None).
// This means the handler uses the update+get pattern after create.
// Override and return Some(...) to skip the update+get cycle (Google pattern).
}
impl NewService {
/// Private helper to list all triggers from the external service.
async fn list_all(
&self,
w_id: &str,
oauth_data: &<Self as External>::OAuthData,
db: &DB,
) -> Result<Vec<<Self as External>::TriggerData>> {
// Implementation depends on the external service's API
todo!()
}
}
```
### Step 4: Update lib.rs Registry
In `backend/windmill-native-triggers/src/lib.rs`:
```rust
// Service modules - add new services here:
#[cfg(feature = "native_trigger")]
pub mod newservice; // <-- Add this
// ServiceName enum - add variant:
pub enum ServiceName {
Nextcloud,
Google,
NewService, // <-- Add this
}
// Then add match arms in ALL ServiceName methods:
// as_str(), as_trigger_kind(), as_job_trigger_kind(), token_endpoint(),
// auth_endpoint(), oauth_scopes(), resource_type(), extra_auth_params(),
// integration_service(), TryFrom<String>, Display
```
### Step 5: Update handler.rs Routes
In `backend/windmill-native-triggers/src/handler.rs`:
```rust
pub fn generate_native_trigger_routers() -> Router {
// ...
#[cfg(feature = "native_trigger")]
{
use crate::newservice::NewService;
return router
.nest("/nextcloud", service_routes(NextCloud))
.nest("/google", service_routes(Google))
.nest("/newservice", service_routes(NewService)); // <-- Add this
}
// ...
}
```
### Step 6: Update sync.rs
In `backend/windmill-native-triggers/src/sync.rs`:
```rust
pub async fn sync_all_triggers(db: &DB) -> Result<BackgroundSyncResult> {
// ...
#[cfg(feature = "native_trigger")]
{
use crate::newservice::NewService;
// ... existing service syncs ...
// New service sync
let (service_name, result) = sync_service_triggers(db, NewService).await;
total_synced += result.synced_triggers.len();
total_errors += result.errors.len();
service_results.insert(service_name, result);
}
// ...
}
```
### Step 7: Frontend Service Registry
In `frontend/src/lib/components/triggers/native/utils.ts`:
Add to `NATIVE_TRIGGER_SERVICES`, `getTriggerIconName()`, and `getServiceIcon()`.
### Step 8: Frontend Trigger Form Component
Create: `frontend/src/lib/components/triggers/native/services/newservice/NewServiceTriggerForm.svelte`
### Step 9: Frontend Icon Component
Create: `frontend/src/lib/components/icons/NewServiceIcon.svelte`
### Step 10: Update NativeTriggerEditor
Check `frontend/src/lib/components/triggers/native/NativeTriggerEditor.svelte` to ensure it dynamically loads form components based on service name.
### Step 11: Workspace Integration UI
Add your service to the `supportedServices` map in `frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte`:
```typescript
const supportedServices: Record<string, ServiceConfig> = {
// ... existing services ...
newservice: {
name: 'newservice',
displayName: 'New Service',
description: 'Connect to New Service for triggers',
icon: NewServiceIcon,
docsUrl: 'https://www.windmill.dev/docs/integrations/newservice',
requiresBaseUrl: false, // false for cloud services, true for self-hosted
setupInstructions: [
'Step 1: Create an OAuth app on the service',
'Step 2: Configure the redirect URI shown below',
'Step 3: Enter the client credentials below'
]
}
}
```
### Step 12: Update `frontend/src/lib/components/triggers/utils.ts`
Update ALL of these maps/functions:
1. `triggerIconMap` - import and add icon
2. `triggerDisplayNamesMap` - add display name
3. `triggerTypeOrder` in `sortTriggers()` - add type
4. `getLightConfig()` - add case for your service
5. `getTriggerLabel()` - add case for your service
6. `jobTriggerKinds` - add to array
7. `countPropertyMap` - add count property
8. `triggerSaveFunctions` - add save function
### Step 13: Update TriggersBadge Component
In `frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte`:
1. Import the icon
2. Add to `baseConfig` with `countKey` (the dynamic `availableNativeServices` loop does NOT set `countKey`)
3. Add to the `allTypes` array
### Step 14: Update TriggersWrapper.svelte
In `frontend/src/lib/components/triggers/TriggersWrapper.svelte`:
Add a `{:else if selectedTrigger.type === 'yourservice'}` case that renders `<NativeTriggersPanel service="yourservice" ...>` with the same props pattern as the existing native trigger cases (e.g., `nextcloud`).
### Step 15: Update AddTriggersButton.svelte
In `frontend/src/lib/components/triggers/AddTriggersButton.svelte`:
1. Add `yourserviceAvailable` state variable
2. Add `setYourserviceState()` async function using `isServiceAvailable('yourservice', $workspaceStore!)`
3. Call it at module level
4. Add a dropdown entry to `addTriggerItems` with `hidden: !yourserviceAvailable`
### Step 16: Update TriggersEditor.svelte Delete Handling
In `frontend/src/lib/components/triggers/TriggersEditor.svelte`:
Add your service to the `nativeTriggerServices` map in `deleteDeployedTrigger()`. Native triggers use `NativeTriggerService.deleteNativeTrigger({ workspace, serviceName, externalId })` instead of the standard `path`-based delete.
### Step 17: Update OpenAPI Spec and Regenerate Types
Add to `JobTriggerKind` enum in `backend/windmill-api/openapi.yaml`, then:
```bash
cd frontend && npm run generate-backend-client
```
---
## Special Patterns
### Unified Service with `trigger_type` (Google Pattern)
When a single service handles multiple trigger types (e.g., Google Drive + Calendar share OAuth and API patterns), use a single `ServiceName` variant with a discriminator field:
```rust
pub enum GoogleTriggerType { Drive, Calendar }
pub struct GoogleServiceConfig {
pub trigger_type: GoogleTriggerType,
// Drive-specific fields (only used when trigger_type = Drive)
pub resource_id: Option<String>,
pub resource_name: Option<String>,
// Calendar-specific fields (only used when trigger_type = Calendar)
pub calendar_id: Option<String>,
pub calendar_name: Option<String>,
// Metadata set after creation
pub google_resource_id: Option<String>,
pub expiration: Option<String>,
}
```
Branch in trait methods based on `trigger_type`. Frontend uses a `ToggleButtonGroup` to switch between types. This keeps the codebase simpler (one service, one OAuth flow, one set of routes).
See `backend/windmill-native-triggers/src/google/` for the reference implementation.
### Skipping update+get After Create (Google Pattern)
Override `service_config_from_create_response()` to return `Some(config)` when the external_id is known before the create call:
```rust
fn service_config_from_create_response(
&self,
data: &NativeTriggerData<Self::ServiceConfig>,
resp: &Self::CreateResponse,
) -> Option<serde_json::Value> {
// Clone input config, add metadata from response
let mut config = data.service_config.clone();
config.google_resource_id = Some(resp.resource_id.clone());
config.expiration = Some(resp.expiration.clone());
Some(serde_json::to_value(&config).unwrap())
}
```
### Services with Absolute OAuth Endpoints (Google)
Unlike self-hosted services where OAuth endpoints are relative paths appended to `base_url`, services like Google have absolute URLs:
```rust
// Nextcloud: relative paths
ServiceName::Nextcloud => "/apps/oauth2/api/v1/token",
// Google: absolute URLs
ServiceName::Google => "https://oauth2.googleapis.com/token",
```
The `resolve_endpoint()` function handles both. For services with absolute endpoints:
- `base_url` can be empty
- `requiresBaseUrl: false` in the frontend workspace integration config
- Add `extra_auth_params()` if needed (Google requires `access_type=offline` and `prompt=consent`)
### Channel-Based Push Notifications with Renewal (Google Pattern)
For services using expiring watch channels instead of persistent webhooks:
1. Store expiration in `service_config` (as part of `ServiceConfig`)
2. In `maintain_triggers()`, implement renewal logic instead of using `reconcile_with_external_state()`:
```rust
async fn maintain_triggers(&self, db, workspace_id, triggers, oauth_data, synced, errors) {
for trigger in triggers {
if should_renew_channel(trigger) {
self.renew_channel(db, trigger, oauth_data).await;
}
}
}
```
3. Renewal: best-effort stop old channel, create new one with same external_id, update service_config with new expiration
4. Google example: Drive channels expire in 24h (renew when <1h left), Calendar channels expire in 7 days (renew when <1 day left)
### reconcile_with_external_state (Nextcloud Pattern)
The reusable function in `sync.rs` compares external triggers with DB state:
- Triggers missing externally: sets error "Trigger no longer exists on external service"
- Triggers present externally: clears errors, updates service_config if it differs
Usage in `maintain_triggers()`:
```rust
let external_pairs: Vec<(String, serde_json::Value)> = /* fetch from external */;
crate::sync::reconcile_with_external_state(
db, workspace_id, Self::SERVICE_NAME, triggers, &external_pairs, synced, errors,
).await;
```
### Webhook Payload Processing
Override `prepare_webhook()` to parse service-specific payloads into script/flow args:
```rust
async fn prepare_webhook(&self, db, w_id, headers, body, script_path, is_flow) -> Result<PushArgsOwned> {
let mut args = HashMap::new();
args.insert("event_type".to_string(), Box::new(headers.get("x-event-type").cloned()) as _);
args.insert("payload".to_string(), Box::new(serde_json::from_str::<serde_json::Value>(&body)?) as _);
Ok(PushArgsOwned { extra: None, args })
}
```
Then register in `prepare_native_trigger_args()` in `lib.rs`:
```rust
pub async fn prepare_native_trigger_args(service_name, db, w_id, headers, body) -> Result<Option<PushArgsOwned>> {
match service_name {
ServiceName::Google => { /* ... */ Ok(Some(args)) }
ServiceName::NewService => { /* ... */ Ok(Some(args)) }
ServiceName::Nextcloud => Ok(None), // Uses default body parsing
}
}
```
### Instance-Level OAuth Credentials
When `workspace_integrations.oauth_data.instance_shared == true`, `decrypt_oauth_data()` reads `client_id` and `client_secret` from instance-level global settings instead of workspace-level. This allows admins to share OAuth app credentials across workspaces.
The frontend handles this via the `generate_instance_connect_url` endpoint in `workspace_integrations.rs`.
---
## Testing Checklist
- [ ] Database migration runs successfully
- [ ] `cargo check -p windmill-native-triggers --features native_trigger` passes
- [ ] `npx svelte-check --threshold error` passes (in frontend/)
- [ ] Service appears in workspace integrations list
- [ ] OAuth flow completes successfully
- [ ] Can create a new trigger
- [ ] Can view trigger details
- [ ] Can update trigger configuration
- [ ] Can delete trigger
- [ ] Webhook receives and processes payloads
- [ ] Background sync works correctly (reconciliation or channel renewal)
- [ ] Error handling works (expired tokens, service unavailable)
---
## Reference Implementations
### Nextcloud (Self-Hosted, Update+Get Pattern)
| File | Purpose |
|------|---------|
| `nextcloud/mod.rs` | Types: NextCloudOAuthData, NextcloudServiceConfig, NextCloudTriggerData |
| `nextcloud/external.rs` | External trait: uses update+get pattern, reconcile_with_external_state for sync |
| `nextcloud/routes.rs` | Additional route: `GET /events` |
Key patterns: relative OAuth endpoints, base_url required, list_all + reconcile for sync, update returns JSON from get().
### Google (Cloud, Unified Service, Short Create)
| File | Purpose |
|------|---------|
| `google/mod.rs` | Types: GoogleServiceConfig with trigger_type discriminator, GoogleTriggerType enum |
| `google/external.rs` | External trait: overrides service_config_from_create_response, channel renewal for sync |
| `google/routes.rs` | Additional routes: `GET /calendars`, `GET /drive/files`, `GET /drive/shared_drives` |
Key patterns: absolute OAuth endpoints, empty base_url, trigger_type for Drive/Calendar, expiring watch channels with renewal, service_config_from_create_response skips update+get, get() reconstructs data from stored service_config (no external "get channel" API).

109
.agents/skills/pr/SKILL.md Normal file
View File

@@ -0,0 +1,109 @@
---
name: pr
description: Open a draft pull request on GitHub. MUST use when you want to create/open a PR.
---
# Pull Request Skill
Create a draft pull request with a clear title and explicit description of changes.
## Instructions
1. **Analyze branch changes**: Understand all commits since diverging from main
2. **Push to remote**: Ensure all commits are pushed
3. **Create draft PR**: Always open as draft for review before merging
## PR Title Format
Follow conventional commit format for the PR title:
```
<type>: <description>
```
### Types
- `feat`: New feature or capability
- `fix`: Bug fix
- `refactor`: Code restructuring
- `docs`: Documentation changes
- `chore`: Maintenance tasks
- `perf`: Performance improvements
### Title Rules
- Keep under 70 characters
- Use lowercase, imperative mood
- No period at the end
- If `*_ee.rs` files were modified, prefix with `[ee]`: `[ee] <type>: <description>`
## PR Body Format
The body MUST be explicit about what changed. Structure:
```markdown
## Summary
<Clear description of what this PR does and why>
## Changes
- <Specific change 1>
- <Specific change 2>
- <Specific change 3>
## Test plan
- [ ] <How to verify change 1>
- [ ] <How to verify change 2>
---
Generated with [Claude Code](https://claude.com/claude-code)
```
## Execution Steps
1. Run `git status` to check for uncommitted changes
2. Run `git log main..HEAD --oneline` to see all commits in this branch
3. Run `git diff main...HEAD` to see the full diff against main
4. Check if remote branch exists and is up to date:
```bash
git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || echo "no upstream"
```
5. Push to remote if needed: `git push -u origin HEAD`
6. Create draft PR using gh CLI:
```bash
gh pr create --draft --title "<type>: <description>" --body "$(cat <<'EOF'
## Summary
<description>
## Changes
- <change 1>
- <change 2>
## Test plan
- [ ] <test 1>
- [ ] <test 2>
---
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
7. Return the PR URL to the user
## EE Companion PR (when `*_ee.rs` files were modified)
The `*_ee.rs` files in the windmill repo are **symlinks** to `windmill-ee-private` — changes won't appear in `git diff` of the windmill repo. Instead, check the EE repo for uncommitted or unpushed changes.
Follow the full EE PR workflow in `docs/enterprise.md`. The key PR-specific details:
1. Find the EE repo/worktree: see "Finding the EE Repo" in `docs/enterprise.md`
2. Check for changes: `git -C <ee-path> status --short`
- If there are no changes in the EE repo, skip this entire section
3. Follow steps 15 from the "EE PR Workflow" in `docs/enterprise.md`
4. Create the companion PR (title does NOT get the `[ee]` prefix):
```bash
gh pr create --draft --repo windmill-labs/windmill-ee-private --title "<type>: <description>" --body "$(cat <<'EOF'
Companion PR for windmill-labs/windmill#<PR_NUMBER>
---
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
5. Commit `ee-repo-ref.txt` and push the updated windmill branch

View File

@@ -0,0 +1,38 @@
---
name: refine
description: End-of-session reflection. Reviews friction encountered during the session and proposes updates to docs/ to capture lessons learned.
---
# Refine Skill
Reflect on the current session and update documentation with lessons learned.
## Instructions
1. **Identify friction**: Review what happened in this session:
- Run `git diff main...HEAD --stat` to see what files were touched
- Think about: what was slow, what failed, what required multiple attempts, what information was missing or hard to find
2. **Read current docs**: Read the docs that were relevant to this session:
- `docs/validation.md`
- `docs/enterprise.md`
- `docs/autonomous-mode.md`
- Any skills that were invoked
3. **Propose updates**: For each piece of friction, decide if it warrants a doc update:
- **Missing knowledge**: Information you had to discover that should be documented
- **Wrong guidance**: Instructions that led you astray
- **Missing validation rule**: A check that should be in the validation matrix
- **New pattern**: A codebase pattern worth capturing for next time
4. **Apply updates**: Edit the relevant `docs/` files. Keep changes minimal and specific — add only what would have saved time this session.
5. **Report**: Summarize what was added/changed and why.
## Rules
- Only add knowledge confirmed by this session — no speculative additions
- Keep docs concise — add a line or two, not a paragraph
- If a whole new doc is needed, create it in `docs/` and add a pointer in `CLAUDE.md`
- Don't update skills unless a coding pattern was genuinely wrong
- Don't add things Claude already knows — only Windmill-specific knowledge

View File

@@ -0,0 +1,107 @@
---
name: rust-backend
description: Rust coding guidelines for the Windmill backend. MUST use when writing or modifying Rust code in the backend directory.
---
# Windmill Rust Patterns
Apply these Windmill-specific patterns when writing Rust code in `backend/`.
## Error Handling
Use `Error` from `windmill_common::error`. Return `Result<T, Error>` or `JsonResult<T>`:
```rust
use windmill_common::error::{Error, Result};
pub async fn get_job(db: &DB, id: Uuid) -> Result<Job> {
sqlx::query_as!(Job, "SELECT id, workspace_id FROM v2_job WHERE id = $1", id)
.fetch_optional(db)
.await?
.ok_or_else(|| Error::NotFound("job not found".to_string()))?;
}
```
Never panic in library code. Reserve `.unwrap()` for compile-time guarantees.
## SQLx Patterns
**Never use `SELECT *`** — always list columns explicitly. Critical for backwards compatibility when workers lag behind API version:
```rust
// Correct
sqlx::query_as!(Job, "SELECT id, workspace_id, path FROM v2_job WHERE id = $1", id)
// Wrong — breaks when columns are added
sqlx::query_as!(Job, "SELECT * FROM v2_job WHERE id = $1", id)
```
Use batch operations to avoid N+1:
```rust
// Preferred — single query with IN clause
sqlx::query!("SELECT ... WHERE id = ANY($1)", &ids[..]).fetch_all(db).await?
```
Use transactions for multi-step operations. Parameterize all queries.
## JSON Handling
Prefer `Box<serde_json::value::RawValue>` over `serde_json::Value` when storing/passing JSON without inspection:
```rust
pub struct Job {
pub args: Option<Box<serde_json::value::RawValue>>,
}
```
Only use `serde_json::Value` when you need to inspect or modify the JSON.
## Serde Optimizations
```rust
#[derive(Serialize, Deserialize)]
pub struct Job {
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_job: Option<Uuid>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default)]
pub priority: i32,
}
```
## Async & Concurrency
Never block the async runtime. Use `spawn_blocking` for CPU-intensive work:
```rust
let result = tokio::task::spawn_blocking(move || expensive_computation(&data)).await?;
```
**Mutex selection**: Prefer `std::sync::Mutex` (or `parking_lot::Mutex`) for data protection. Only use `tokio::sync::Mutex` when holding locks across `.await` points.
Use `tokio::sync::mpsc` (bounded) for channels. Avoid `std::thread::sleep` in async contexts.
## Module Structure & Visibility
- Use `pub(crate)` instead of `pub` when possible
- Place new code in the appropriate crate based on functionality
- API endpoints go in `windmill-api/src/` organized by domain
- Shared functionality goes in `windmill-common/src/`
## Code Navigation
Always use rust-analyzer LSP for go-to-definition, find-references, and type info. Do not guess at module paths.
## Axum Handlers
Destructure extractors directly in function signatures:
```rust
async fn process_job(
Extension(db): Extension<DB>,
Path((workspace, job_id)): Path<(String, Uuid)>,
Query(pagination): Query<Pagination>,
) -> Result<Json<Job>> { ... }
```

View File

@@ -0,0 +1,80 @@
---
name: svelte-frontend
description: Svelte coding guidelines for the Windmill frontend. MUST use when writing or modifying code in the frontend directory.
---
# Windmill Svelte Patterns
Apply these Windmill-specific patterns when writing Svelte code in `frontend/`. For general Svelte 5 syntax (runes, snippets, event handling), use the Svelte MCP server.
## Windmill UI Components (MUST use)
Always use Windmill's design-system components. Never use raw HTML elements.
### Buttons — `<Button>`
```svelte
<script>
import { Button } from '$lib/components/common'
import { ChevronLeft } from 'lucide-svelte'
</script>
<Button variant="default" onclick={handleClick}>Label</Button>
<Button startIcon={{ icon: ChevronLeft }} iconOnly onclick={prev} />
```
Props: `variant?: 'accent' | 'accent-secondary' | 'default' | 'subtle'`, `unifiedSize?: 'sm' | 'md' | 'lg'`, `startIcon?: { icon: SvelteComponent }`, `iconOnly?: boolean`, `disabled?: boolean`
### Text inputs — `<TextInput>`
```svelte
<script>
import { TextInput } from '$lib/components/common'
</script>
<TextInput bind:value={val} placeholder="Enter value" />
```
Props: `value?: string | number` (bindable), `placeholder?: string`, `disabled?: boolean`, `error?: string | boolean`, `size?: 'sm' | 'md' | 'lg'`
### Selects — `<Select>`
```svelte
<script>
import Select from '$lib/components/select/Select.svelte'
</script>
<Select items={[{ label: 'Jan', value: 1 }]} bind:value={selected} />
```
Props: `items?: Array<{ label?: string; value: any }>`, `value` (bindable), `placeholder?: string`, `clearable?: boolean`, `size?: 'sm' | 'md' | 'lg'`
### Icons — `lucide-svelte`
Never write inline SVGs. Import from `lucide-svelte`:
```svelte
<script>
import { ChevronLeft, X } from 'lucide-svelte'
</script>
<ChevronLeft size={16} />
```
## Form Components
Form components (TextInput, Toggle, Select, etc.) should use the unified size system when placed together.
## Styling
- Use Tailwind CSS for all styling — no custom CSS
- Use Windmill's theming classes for colors/surfaces (see `frontend/brand-guidelines.md`)
- Read component props JSDoc before using them
## Svelte MCP Server
Use the Svelte MCP tools when working on Svelte code:
1. **list-sections**: Call first to discover available docs
2. **get-documentation**: Fetch relevant sections based on use_cases
3. **svelte-autofixer**: MUST use on all Svelte code before finalizing — keep calling until no issues
4. **playground-link**: Only after user confirms and code was NOT written to project files

View File

@@ -13,8 +13,10 @@ fi
# Check if the file is in the backend directory and is a Rust file
if [[ "$FILE_PATH" == *"/backend/"* ]] && [[ "$FILE_PATH" =~ \.rs$ ]]; then
cd "$CLAUDE_PROJECT_DIR/backend" || exit 0
# Run rustfmt with config from rustfmt.toml (edition=2021)
rustfmt --config-path rustfmt.toml "$FILE_PATH" 2>/dev/null || true
# Run rustfmt, surface errors as context but don't block Claude
if rustfmt --config-path rustfmt.toml "$FILE_PATH" 2>&1; then
echo "Formatted $(basename "$FILE_PATH")"
fi
fi
exit 0

View File

@@ -15,8 +15,10 @@ if [[ "$FILE_PATH" == *"/frontend/"* ]]; then
# Check if it's a formattable file type
if [[ "$FILE_PATH" =~ \.(ts|js|svelte|json|css|html|md)$ ]]; then
cd "$CLAUDE_PROJECT_DIR/frontend" || exit 0
# Run prettier silently, don't fail the hook if prettier fails
npx prettier --write "$FILE_PATH" 2>/dev/null || true
# Run prettier, surface errors as context but don't block Claude
if ./node_modules/.bin/prettier --plugin prettier-plugin-svelte --write "$FILE_PATH" 2>&1; then
echo "Formatted $(basename "$FILE_PATH")"
fi
fi
fi

View File

@@ -28,6 +28,12 @@
"Bash(git show:*)",
"Bash(git blame:*)",
"Bash(cargo check:*)",
"Bash(cargo build --release:*)",
"Bash(sh wm-ts-nav/nav:*)",
"Bash(wm-ts-nav/nav:*)",
"Bash(./wm-ts-nav/nav:*)",
"Bash(wm-ts-nav/target/release/wm-ts-nav:*)",
"Bash(./wm-ts-nav/target/release/wm-ts-nav:*)",
"mcp__ide__getDiagnostics",
"Bash(npm run generate-backend-client:*)",
"Bash(npm run check:*)",

View File

@@ -0,0 +1,98 @@
---
name: local-review
user_invocable: true
description: Code review a pull request for bugs and CLAUDE.md compliance. MUST use when asked to review code.
---
# Local Code Review Skill
Review a pull request for real bugs and CLAUDE.md compliance violations. This review targets HIGH SIGNAL issues only.
## Review Philosophy
- **Only flag issues you are certain about.** If you are not sure an issue is real, do not flag it. False positives erode trust and waste reviewer time.
- Think like a senior engineer doing a final review — flag things that would cause incidents, not things that are merely imperfect.
## What to Flag
- Code that won't compile or parse (syntax errors, type errors, missing imports)
- Code that will definitely produce wrong results regardless of inputs
- Clear, unambiguous CLAUDE.md violations (quote the exact rule being violated)
- Security issues in introduced code (injection, auth bypass, data exposure)
- Incorrect logic that will fail in production
## What NOT to Flag
- Code style or quality concerns
- Potential issues that depend on specific inputs or runtime state
- Subjective suggestions or improvements
- Pre-existing issues not introduced by this PR
- Pedantic nitpicks a senior engineer wouldn't flag
- Issues a linter or type checker will catch
- General quality concerns unless explicitly prohibited in CLAUDE.md
- Issues silenced via lint ignore comments
## Execution Steps
1. **Determine the PR scope**:
- If an argument is provided, use it as the PR number or branch
- Otherwise, detect from the current branch vs main
- Run `gh pr view` if a PR exists, or use `git diff main...HEAD`
2. **Find relevant CLAUDE.md files**:
- Read the root `CLAUDE.md`
- Check for CLAUDE.md files in directories containing changed files
3. **Get the diff and metadata**:
- `gh pr diff` or `git diff main...HEAD` for the full diff
- `gh pr view` or `git log main..HEAD --oneline` for context
4. **Read changed files** where the diff alone is insufficient to understand context
5. **Review for**:
- CLAUDE.md compliance — check each rule against the changed code
- Bugs and logic errors — will this code work correctly?
- Security issues — injection, auth, data exposure in new code
6. **Self-validate each finding**: Before reporting, ask yourself:
- "Is this definitely a real issue, not a false positive?"
- "Would a senior engineer flag this in review?"
- If the answer to either is no, discard the finding
7. **Output findings** to the terminal (default) or post as PR comments (with `--comment` flag)
## Output Format
```
## Code review
Found N issues:
1. <description> (<reason: CLAUDE.md adherence | bug | security>)
<file_path:line_number>
2. <description> (<reason>)
<file_path:line_number>
```
If no issues are found:
```
## Code review
No issues found. Checked for bugs and CLAUDE.md compliance.
```
## Posting Comments (--comment flag)
If the user passes `--comment`, post findings as inline PR comments using:
```bash
gh pr review --comment --body "<summary>"
```
Or for inline comments on specific lines:
```bash
gh api repos/{owner}/{repo}/pulls/{pr}/reviews -f body="<summary>" -f event="COMMENT" -f comments="[...]"
```

View File

@@ -33,6 +33,7 @@ Follow conventional commit format for the PR title:
- Keep under 70 characters
- Use lowercase, imperative mood
- No period at the end
- If `*_ee.rs` files were modified, prefix with `[ee]`: `[ee] <type>: <description>`
## PR Body Format
@@ -85,3 +86,25 @@ Generated with [Claude Code](https://claude.com/claude-code)
)"
```
7. Return the PR URL to the user
## EE Companion PR (when `*_ee.rs` files were modified)
The `*_ee.rs` files in the windmill repo are **symlinks** to `windmill-ee-private` — changes won't appear in `git diff` of the windmill repo. Instead, check the EE repo for uncommitted or unpushed changes.
Follow the full EE PR workflow in `docs/enterprise.md`. The key PR-specific details:
1. Find the EE repo/worktree: see "Finding the EE Repo" in `docs/enterprise.md`
2. Check for changes: `git -C <ee-path> status --short`
- If there are no changes in the EE repo, skip this entire section
3. Follow steps 15 from the "EE PR Workflow" in `docs/enterprise.md`
4. Create the companion PR (title does NOT get the `[ee]` prefix):
```bash
gh pr create --draft --repo windmill-labs/windmill-ee-private --title "<type>: <description>" --body "$(cat <<'EOF'
Companion PR for windmill-labs/windmill#<PR_NUMBER>
---
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
5. Commit `ee-repo-ref.txt` and push the updated windmill branch

6
.envrc
View File

@@ -1 +1,7 @@
use flake
# Per-worktree overrides (ports, DATABASE_URL, etc.) written by webmux/workmux
# post-create hooks. Must come after `use flake` so they take precedence over
# the flake's defaults.
# shellcheck source=/dev/null
[ -f .env.local ] && source .env.local

View File

@@ -5,6 +5,8 @@ on:
push:
branches:
- "ci-windows-tests"
tags:
- "v*"
env:
CARGO_INCREMENTAL: 0

View File

@@ -13,10 +13,10 @@ on:
jobs:
check-membership:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/ai')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/ai')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '/ai')) ||
(github.event_name == 'issues' && contains(github.event.issue.body, '/ai'))
(github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) ||
(github.event_name == 'pull_request_review_comment' && startsWith(github.event.comment.body, '/ai') && !startsWith(github.event.comment.body, '/ai-fast')) ||
(github.event_name == 'pull_request_review' && startsWith(github.event.review.body, '/ai') && !startsWith(github.event.review.body, '/ai-fast')) ||
(github.event_name == 'issues' && startsWith(github.event.issue.body, '/ai') && !startsWith(github.event.issue.body, '/ai-fast'))
uses: ./.github/workflows/check-org-membership.yml
secrets:
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}

View File

@@ -5,11 +5,13 @@ on:
branches: [main]
paths:
- "cli/**"
- "backend/migrations/**"
- ".github/workflows/cli-tests.yml"
pull_request:
branches: [main]
paths:
- "cli/**"
- "backend/migrations/**"
- ".github/workflows/cli-tests.yml"
env:

View File

@@ -212,6 +212,59 @@ jobs:
${{ steps.extract-ee.outputs.destination }}/*
${{ steps.extract-duckdb-ffi-internal.outputs.destination }}/*
attach_ee_debug_to_release:
needs: [build_ee]
runs-on: ubicloud
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
strategy:
matrix:
platform: [linux/amd64, linux/arm64]
include:
- platform: linux/amd64
arch: amd64
- platform: linux/arm64
arch: arm64
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Read EE repo commit hash
run: |
echo "ee_repo_ref=$(cat ./backend/ee-repo-ref.txt)" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
with:
repository: windmill-labs/windmill-ee-private
path: ./windmill-ee-private
ref: ${{ env.ee_repo_ref }}
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
- name: Substitute EE code
run: |
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
- uses: depot/setup-action@v1
- name: Extract EE debug info from builder stage (depot cache hit)
uses: depot/build-push-action@v1
with:
context: .
platforms: ${{ matrix.platform }}
target: debuginfo
build-args: |
features=ee
outputs: type=local,dest=./debuginfo
- name: Rename debug file with corresponding architecture
run: |
mv ./debuginfo/windmill.debug ./debuginfo/windmill-ee-${{ matrix.arch }}.debug
- name: Attach debug file to release
uses: softprops/action-gh-release@v2
with:
files: ./debuginfo/windmill-ee-${{ matrix.arch }}.debug
# attach_arm64_binary_to_release:
# needs: [build, build_ee]
# runs-on: ubicoud

View File

@@ -106,6 +106,19 @@ jobs:
git config --local user.name "windmill-internal-app[bot]"
git config pull.rebase true
git pull origin $BRANCH_NAME
# Checkout the correct windmill-ee-private commit from ee-repo-ref.txt
if [ -f backend/ee-repo-ref.txt ]; then
EE_REF=$(cat backend/ee-repo-ref.txt | tr -d '[:space:]')
echo "Checking out windmill-ee-private at commit: $EE_REF"
cd windmill-ee-private
git fetch origin $EE_REF
git checkout $EE_REF
cd ..
else
echo "Warning: ee-repo-ref.txt not found, using default branch"
fi
mkdir -p frontend/build
cd backend
cargo install sqlx-cli --version 0.8.5

View File

@@ -14,7 +14,7 @@ jobs:
with:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
- run: cd typescript-client && ./publish.sh && cd ..
- run: cd typescript-client && ./publish.sh --access public && cd ..
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish_cli:
@@ -28,6 +28,6 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: cd cli && ./build.sh && cd npm && npm publish
- run: cd cli && ./build.sh && cd npm && npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ rust-client/Cargo.toml
# Worktree-generated port isolation
.env.local
.webmux.local.yaml
# Worktree-specific Claude Code settings (generated by scripts/worktree-env)
.claude/settings.local.json

View File

@@ -44,7 +44,7 @@ profiles:
- Pane 1: backend (cargo watch -x run)
- Pane 2: frontend (npm run dev)
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (backend) or \`tmux capture-pane -t .2 -p -S -50\` (frontend).
When restarting backend or frontend, make sure to use ${BACKEND_PORT} and ${FRONTEND_PORT}.
For this window specifically, backend is running on: ${BACKEND_PORT} and frontend is running on: ${FRONTEND_PORT}.
To connect to the database, use this connection string: ${DATABASE_URL}
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check.
IMPORTANT: Read docs/autonomous-mode.md before starting any work.
@@ -55,11 +55,11 @@ profiles:
- id: backend
kind: command
split: right
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/backend" && cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"
- id: frontend
kind: command
split: bottom
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --host 0.0.0.0
frontendOnly:
runtime: host
@@ -71,7 +71,7 @@ profiles:
- Pane 0: this pane (claude agent)
- Pane 1: frontend (npm run dev)
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (frontend).
When restarting frontend, make sure to use ${FRONTEND_PORT}.
On this window specifically, frontend is running on: ${FRONTEND_PORT}.
To connect to the database, use this connection string: ${DATABASE_URL}
Because we are running frontend with npm run dev, to verify your changes, just check the logs in the frontend pane. No need for npm run build.
IMPORTANT: Read docs/autonomous-mode.md before starting any work.
@@ -82,7 +82,7 @@ profiles:
- id: frontend
kind: command
split: right
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --host 0.0.0.0
agentOnly:
runtime: host

View File

@@ -1,73 +0,0 @@
main_branch: main
merge_strategy: rebase
# worktree_dir: .worktrees
worktree_naming: basename
worktree_prefix: ""
# Default: "wm-"
window_prefix: "wm-"
auto_name:
model: "gemini-2.5-flash-lite"
system_prompt: |
Generate a concise git branch name based on the task description.
Rules:
- Use kebab-case (lowercase with hyphens)
- Keep it short: 1-3 words, max 4 if necessary
- Focus on the core task/feature, not implementation details
- No prefixes like feat/, fix/, chore/
Examples of good branch names:
- "Add dark mode toggle" → dark-mode
- "Fix the search results not showing" → fix-search
- "Refactor the authentication module" → auth-refactor
- "Add CSV export to reports" → export-csv
- "Shell completion is broken" → shell-completion
Output ONLY the branch name, nothing else.
background: true
# Commands to run in new worktree before tmux window opens.
# These block window creation - use for short tasks only.
# Use "<global>" to inherit from global config.
# Set to empty list to disable: `post_create: []`
# post_create:
# - "<global>"
# - mise use
post_create:
- ./scripts/worktree-env
pre_remove:
- ./scripts/worktree-cleanup
panes:
- command: >-
claude --dangerously-skip-permissions --append-system-prompt
"You are running inside a tmux session with other panes running services.\n
Pane layout (current window):\n
- Pane 0: this pane (claude agent)\n
- Pane 1: backend (cargo watch -x run)\n
- Pane 2: frontend (npm run dev)\n\n
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (backend) or \`tmux capture-pane -t .2 -p -S -50\` (frontend).\n
When restarting backend or frontend, make sure to use the ports listed in .env.local.\n
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check.\n\n
IMPORTANT: Read docs/autonomous-mode.md before starting any work."
focus: true
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"'
split: horizontal
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/frontend" && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0'
split: vertical
files:
copy:
- backend/.env
- scripts/
sandbox:
enabled: false
toolchain: off

View File

@@ -1,5 +1,158 @@
# Changelog
## [1.662.0](https://github.com/windmill-labs/windmill/compare/v1.661.0...v1.662.0) (2026-03-20)
### Features
* mcp oauth gateway ([#8443](https://github.com/windmill-labs/windmill/issues/8443)) ([51957f7](https://github.com/windmill-labs/windmill/commit/51957f7d921b624fc132ca9ea03cdd30a5810e51))
### Bug Fixes
* replace email with permissioned_as for triggers/schedules ([#8439](https://github.com/windmill-labs/windmill/issues/8439)) ([efb4a27](https://github.com/windmill-labs/windmill/commit/efb4a27d5181bf9db3deb5e8100ec60adbe45e7f))
* strip invalid enum values from MCP schemas ([#8462](https://github.com/windmill-labs/windmill/issues/8462)) ([88ad376](https://github.com/windmill-labs/windmill/commit/88ad3767916b86c4e0b272d040ee0b75a0580d76))
## [1.661.0](https://github.com/windmill-labs/windmill/compare/v1.660.1...v1.661.0) (2026-03-19)
### Features
* add OTel metrics support ([#8442](https://github.com/windmill-labs/windmill/issues/8442)) ([7de98c0](https://github.com/windmill-labs/windmill/commit/7de98c0df464d8a7c9cf5d04228753294183f759))
### Bug Fixes
* fix datatable setup on RDS ([#8450](https://github.com/windmill-labs/windmill/issues/8450)) ([446afb5](https://github.com/windmill-labs/windmill/commit/446afb5b36211e5cbe8a279ce68f2f790a5953b9))
* full code apps deployable on merge UI and deploy UI ([#8451](https://github.com/windmill-labs/windmill/issues/8451)) ([0e022b1](https://github.com/windmill-labs/windmill/commit/0e022b14fd36e897106219010917bd7ceabf4078))
* improve DND drag feedback in EditableSchemaForm ([#8449](https://github.com/windmill-labs/windmill/issues/8449)) ([fd7f0d3](https://github.com/windmill-labs/windmill/commit/fd7f0d3da9153d91c15df5847aaae51e67479cde))
* prevent raw app iframe reload on userStore refresh ([#8455](https://github.com/windmill-labs/windmill/issues/8455)) ([4e59a1a](https://github.com/windmill-labs/windmill/commit/4e59a1a166847045897a6b576812bb53546e683b))
* resolve blank inline script panel for components with underscores in ID ([#8457](https://github.com/windmill-labs/windmill/issues/8457)) ([b2c1e3d](https://github.com/windmill-labs/windmill/commit/b2c1e3de0a263f606127f0decedb11a2ce0b822b))
## [1.660.1](https://github.com/windmill-labs/windmill/compare/v1.660.0...v1.660.1) (2026-03-19)
### Bug Fixes
* prevent S3 file browser crash when selecting storage ([#8444](https://github.com/windmill-labs/windmill/issues/8444)) ([a8fa0cc](https://github.com/windmill-labs/windmill/commit/a8fa0cccef870f841c68be77832d9be12109badb))
* schema inference not updating on reset and language switch ([#8446](https://github.com/windmill-labs/windmill/issues/8446)) ([c0edbe4](https://github.com/windmill-labs/windmill/commit/c0edbe431773f878201e96a79ce291d4b37a10bb))
## [1.660.0](https://github.com/windmill-labs/windmill/compare/v1.659.1...v1.660.0) (2026-03-18)
### Features
* **cli:** use local scripts when previewing flows ([#8365](https://github.com/windmill-labs/windmill/issues/8365)) ([435de95](https://github.com/windmill-labs/windmill/commit/435de95e7d5c9433dafac5369cfc533fd738fc22))
* MCP server readiness for Anthropic connectors directory ([#8438](https://github.com/windmill-labs/windmill/issues/8438)) ([1cfb40b](https://github.com/windmill-labs/windmill/commit/1cfb40bdaa877f1616fc1c1cf5fb6b6aa1832b86))
### Bug Fixes
* exclude wm_deployers group from CE group limit check ([#8429](https://github.com/windmill-labs/windmill/issues/8429)) ([9a6ce44](https://github.com/windmill-labs/windmill/commit/9a6ce44c8414810292ebc8a1ae64950ee2c76307))
* prevent AI agent tool jobs from becoming zombies on cancellation ([#8437](https://github.com/windmill-labs/windmill/issues/8437)) ([f4489cb](https://github.com/windmill-labs/windmill/commit/f4489cbe645489a892994c70d17df2284b494568))
* show cancelled WAC jobs as done in workflow timeline ([#8436](https://github.com/windmill-labs/windmill/issues/8436)) ([bee9282](https://github.com/windmill-labs/windmill/commit/bee928276e098ce7b17e20af74e34458e5c5353e))
### Performance Improvements
* cache composer vendor dir to skip reinstall on repeated php executions ([#8330](https://github.com/windmill-labs/windmill/issues/8330)) ([66a8e84](https://github.com/windmill-labs/windmill/commit/66a8e844a64d91d57dcabb7ad31d9308dec99032))
## [1.659.1](https://github.com/windmill-labs/windmill/compare/v1.659.0...v1.659.1) (2026-03-18)
### Bug Fixes
* add checkpoint.json mount to python nsjail config for WAC v2 ([#8421](https://github.com/windmill-labs/windmill/issues/8421)) ([4829f44](https://github.com/windmill-labs/windmill/commit/4829f447ed3df8489995c5e54955fbfe6b31e37d))
* cleanup job debounce batch ([#8420](https://github.com/windmill-labs/windmill/issues/8420)) ([ad03a5d](https://github.com/windmill-labs/windmill/commit/ad03a5dbd7f93748115037791143249ba0ab6161))
* **frontend:** fix output of resultnode + svelte5 nits ([#8424](https://github.com/windmill-labs/windmill/issues/8424)) ([f481ea4](https://github.com/windmill-labs/windmill/commit/f481ea4059b4e5cb01273cffeb53ff340e8bd5bd))
* per-tab test panel in script editor for WAC v2 modules ([#8422](https://github.com/windmill-labs/windmill/issues/8422)) ([0f26169](https://github.com/windmill-labs/windmill/commit/0f261695a3cb2c3a95d16390e54aa7a6ac3e11e7))
## [1.659.0](https://github.com/windmill-labs/windmill/compare/v1.658.0...v1.659.0) (2026-03-17)
### Features
* add end_user_email claim to OIDC ID tokens ([#8401](https://github.com/windmill-labs/windmill/issues/8401)) ([de5b13b](https://github.com/windmill-labs/windmill/commit/de5b13b840f90e23df1871f80317fdcc2b98174d))
* add ws_base_url instance setting for WebSocket URL override ([#8405](https://github.com/windmill-labs/windmill/issues/8405)) ([372023e](https://github.com/windmill-labs/windmill/commit/372023e99560885a76e8da3487ae705fd2f861d4))
* **cli:** add --env alias for --branch and environments config alias ([#8415](https://github.com/windmill-labs/windmill/issues/8415)) ([fe051aa](https://github.com/windmill-labs/windmill/commit/fe051aa22b59cc1c450b14af9c5f203448bb3dd5))
* DB-backed instance events webhook with superadmin UI ([#8402](https://github.com/windmill-labs/windmill/issues/8402)) ([7d9fb57](https://github.com/windmill-labs/windmill/commit/7d9fb57368ad3b2c719523ef649c9bd5fddf17a5))
* instance groups instance-level role support ([#8404](https://github.com/windmill-labs/windmill/issues/8404)) ([18b3528](https://github.com/windmill-labs/windmill/commit/18b3528ba4188721d918fd47f0f86a6b41209453))
* script module mode with CLI sync, preview, and WAC UI improvements ([#8380](https://github.com/windmill-labs/windmill/issues/8380)) ([31d6660](https://github.com/windmill-labs/windmill/commit/31d6660d56cd23d9269133d430b0607d58314229))
* store hashed tokens instead of plaintext ([#8217](https://github.com/windmill-labs/windmill/issues/8217)) ([f2be625](https://github.com/windmill-labs/windmill/commit/f2be625348ef308e9768d487e110abbd44d27855))
* workspace-specific registry overrides ([#8406](https://github.com/windmill-labs/windmill/issues/8406)) ([73fe45b](https://github.com/windmill-labs/windmill/commit/73fe45b6cb97ce50d029240c6bd63917b301abe1))
### Bug Fixes
* devops getting logged out on workers page ([#8416](https://github.com/windmill-labs/windmill/issues/8416)) ([920a7f9](https://github.com/windmill-labs/windmill/commit/920a7f9fa4719015885947b9de0c35e5e618fcc8))
* Folders as presets in FilterSearchbar ([#8409](https://github.com/windmill-labs/windmill/issues/8409)) ([ebf9347](https://github.com/windmill-labs/windmill/commit/ebf9347d3fd876689dba58bc24399e9036ef5b67))
* improve OOM killer observability for debugging pod-level kills ([#8398](https://github.com/windmill-labs/windmill/issues/8398)) ([fd41cd1](https://github.com/windmill-labs/windmill/commit/fd41cd12b444fb2439214fcd25536280e5baacb2))
## [1.658.0](https://github.com/windmill-labs/windmill/compare/v1.657.2...v1.658.0) (2026-03-16)
### Features
* add GET /api/saml/metadata endpoint ([#8394](https://github.com/windmill-labs/windmill/issues/8394)) ([50b24cf](https://github.com/windmill-labs/windmill/commit/50b24cfdc8bf54656adbdc3315037aa773632076))
* support custom headers in customai resource type ([#8364](https://github.com/windmill-labs/windmill/issues/8364)) ([5acb367](https://github.com/windmill-labs/windmill/commit/5acb367cf9b4b96ac7129c91df229d1a25258f5b))
* support multiple secret variables during resource creation ([#8386](https://github.com/windmill-labs/windmill/issues/8386)) ([54841b7](https://github.com/windmill-labs/windmill/commit/54841b7549d5c9719d4dc3cb43e282ba057cd0f3))
### Bug Fixes
* /updatesqlx now uses ee-repo-ref.txt commit hash ([#8387](https://github.com/windmill-labs/windmill/issues/8387)) ([a519d41](https://github.com/windmill-labs/windmill/commit/a519d4113086430ace1d7ac8795bd2c2a8cf99e9))
* **native-triggers:** preserve API error response body in HttpRequestError ([#8392](https://github.com/windmill-labs/windmill/issues/8392)) ([1eee89d](https://github.com/windmill-labs/windmill/commit/1eee89d99fbf31751d6257a4015e0b22e3871372))
* OutputPicker shows stale result after 'Test up to here' ([#8390](https://github.com/windmill-labs/windmill/issues/8390)) ([2907084](https://github.com/windmill-labs/windmill/commit/2907084ca653fc5540bb04a409d2789ddaeec05b))
* propagate enterprise feature to windmill-api-schedule ([#8391](https://github.com/windmill-labs/windmill/issues/8391)) ([50ef9e7](https://github.com/windmill-labs/windmill/commit/50ef9e79fcef8ee2cccd789b5eb1aacf5647365f))
* set nsjail time_limit from job timeout so configured defaults are respected ([#8389](https://github.com/windmill-labs/windmill/issues/8389)) ([65a92d9](https://github.com/windmill-labs/windmill/commit/65a92d98994dbe4ae90a5e554e55b3ab44463f86))
* soft error on AI agent max iterations + rename retries tab to error handling ([#8366](https://github.com/windmill-labs/windmill/issues/8366)) ([1a1e8a1](https://github.com/windmill-labs/windmill/commit/1a1e8a164cccbfcc663b963cb062af9208ff51be))
* use bookworm-based php image to fix glibc 2.38 incompatibility ([#8381](https://github.com/windmill-labs/windmill/issues/8381)) ([68fd900](https://github.com/windmill-labs/windmill/commit/68fd900076ecf8b20f6622cd5794f1b52c0f5cab))
## [1.657.2](https://github.com/windmill-labs/windmill/compare/v1.657.1...v1.657.2) (2026-03-15)
### Bug Fixes
* **cli:** Fix nonDottedPaths handling in cli flow lock generation ([#8375](https://github.com/windmill-labs/windmill/issues/8375)) ([eb03ebb](https://github.com/windmill-labs/windmill/commit/eb03ebbb0486b33c290fba3c34ea959e6e82fd13))
## [1.657.1](https://github.com/windmill-labs/windmill/compare/v1.657.0...v1.657.1) (2026-03-14)
### Bug Fixes
* powershell WindmillClient module loading on Windows workers ([#8370](https://github.com/windmill-labs/windmill/issues/8370)) ([3a268a9](https://github.com/windmill-labs/windmill/commit/3a268a9cf16add2ea2530e6eab247120a4d4754e))
## [1.657.0](https://github.com/windmill-labs/windmill/compare/v1.656.0...v1.657.0) (2026-03-14)
### Features
* add datatable config support to CLI settings sync and backend export ([#8024](https://github.com/windmill-labs/windmill/issues/8024)) ([5df37fb](https://github.com/windmill-labs/windmill/commit/5df37fb0dbf9190a430f066cf2d3c48914782e53))
## [1.656.0](https://github.com/windmill-labs/windmill/compare/v1.655.0...v1.656.0) (2026-03-13)
### Features
* add GitHub Enterprise Server (GHES) support for GitHub App git sync ([#8344](https://github.com/windmill-labs/windmill/issues/8344)) ([2e430c4](https://github.com/windmill-labs/windmill/commit/2e430c4c0b8540df7b6997434a7a9f9134858026))
* **cli:** add unified generate-metadata command ([#8335](https://github.com/windmill-labs/windmill/issues/8335)) ([4c2c165](https://github.com/windmill-labs/windmill/commit/4c2c165a5b757bd5f2f49074bb290407bce3b2fb))
### Bug Fixes
* **ci:** add NODE_AUTH_TOKEN for npm publish authentication ([2a8e276](https://github.com/windmill-labs/windmill/commit/2a8e276b6d2761bb2798b6bc5f8d90ab34fbb403))
* **ci:** remove provenance flag and use NPM_TOKEN for npm publish ([44dd3ee](https://github.com/windmill-labs/windmill/commit/44dd3ee8cd05d288828d1d46c84cbcdf40f8fa78))
* **cli:** exclude raw app backend files from script metadata generation ([#8362](https://github.com/windmill-labs/windmill/issues/8362)) ([060687b](https://github.com/windmill-labs/windmill/commit/060687b1fa6b627a7b06fbdc4b3f4eb0b63411c0))
* **cli:** normalize path separators in generate-metadata folder filter for Windows ([#8358](https://github.com/windmill-labs/windmill/issues/8358)) ([404ae09](https://github.com/windmill-labs/windmill/commit/404ae09d429fb545610ba17d747e1903c542d4a3))
* **cli:** suppress verbose lock generation messages in generate-metadata ([#8357](https://github.com/windmill-labs/windmill/issues/8357)) ([51933be](https://github.com/windmill-labs/windmill/commit/51933be3cabd853960d384cd358c7bcaef6bfa86))
* **frontend:** collapse flow topbar buttons to icon-only in narrow panes ([#8322](https://github.com/windmill-labs/windmill/issues/8322)) ([b585dee](https://github.com/windmill-labs/windmill/commit/b585dee64dfd63d20812ca969b17ff9ee9989493))
* **frontend:** filter webhook/email tokens by scope instead of label ([#8363](https://github.com/windmill-labs/windmill/issues/8363)) ([0d31c35](https://github.com/windmill-labs/windmill/commit/0d31c35f3e12d637c757a95fe350294002cbf640))
* **frontend:** improve native mode alert message and fix workspaced tag detection ([#8361](https://github.com/windmill-labs/windmill/issues/8361)) ([fb12b31](https://github.com/windmill-labs/windmill/commit/fb12b31df081b2f1ac63becea6e6538ca80f8c46))
* **frontend:** prevent duplicate and reserved agent tool names ([#8367](https://github.com/windmill-labs/windmill/issues/8367)) ([c431053](https://github.com/windmill-labs/windmill/commit/c431053a1e24ef29cd551a86de4d013fd7f158be))
* graceful shutdown instead of panic on job completion channel failure ([#8345](https://github.com/windmill-labs/windmill/issues/8345)) ([724d135](https://github.com/windmill-labs/windmill/commit/724d1350d070fcf078034a52166d3048fb74e6f3))
* Linked resources and vars not triggering both sync jobs on delete ([#8342](https://github.com/windmill-labs/windmill/issues/8342)) ([8e3b8bd](https://github.com/windmill-labs/windmill/commit/8e3b8bdfd2ded9652bc7e876c6bcd0ac2cfae148))
* lower default indexer memory/batch settings to prevent OOM ([#8347](https://github.com/windmill-labs/windmill/issues/8347)) ([d9d45cf](https://github.com/windmill-labs/windmill/commit/d9d45cf2f9235b0e7118d0fc97ccdc0776ca9726))
## [1.655.0](https://github.com/windmill-labs/windmill/compare/v1.654.0...v1.655.0) (2026-03-12)

View File

@@ -4,7 +4,7 @@ Open-source platform for internal tools, workflows, API integrations, background
## Workflow
1. **Understand**: Before coding, read relevant docs from `docs/` to understand the area you're changing
1. **Understand**: Before coding, explore the codebase (see Code Navigation below). Use `outline` to understand file structure, `body` to read specific symbols, `def`/`callers`/`callees` to trace code, `Grep` to find usages. Read `docs/` for domain context.
2. **Plan**: For non-trivial changes, use plan mode. For large features, break into reviewable stages
3. **Execute**: Follow coding patterns from skills (`rust-backend`, `svelte-frontend`)
4. **Validate**: After every change, run the appropriate checks per `docs/validation.md`
@@ -15,6 +15,7 @@ Open-source platform for internal tools, workflows, API integrations, background
- **Enterprise**: `docs/enterprise.md` — EE file conventions and PR workflow
- **Backend patterns**: use the `rust-backend` skill when writing Rust code
- **Frontend patterns**: use the `svelte-frontend` skill when writing Svelte code. Do NOT edit svelte files unless you have read that skill.
- **Code review**: use `/local-review` to review a PR for bugs and CLAUDE.md compliance
- **Domain guides**: `.claude/skills/native-trigger/` and `frontend/tutorial-system-guide.mdc`
- **Brand/UI guidelines**: `frontend/brand-guidelines.md`
@@ -49,8 +50,37 @@ let { my_prop = $bindable(default_value) }: { my_prop?: string } = $props()
2. **Create a `useMyPropState()` helper** — encapsulate the undefined-handling logic in a reusable function and call it higher in the component tree, so the child component always receives a defined value.
## Code Navigation
`wm-ts-nav` is an AST-aware code navigator. Use **wm-ts-nav** for structural queries — it skips comments/strings and understands symbol boundaries.
**MUST use `outline` before `Read`** on unfamiliar files — a 500-line file costs ~500 lines of context, while `outline` costs ~20. Then **MUST use `body "X"`** instead of reading a full file to see one function/struct. Use `Read` with offset/limit only when you need surrounding context that `body` doesn't capture.
- `refs "X" --caller` instead of reading files to find which function contains each reference
- `callers "X"` / `callees "X"` for call-graph questions
EE files (`*_ee.rs`, `*_ee.ts`, `*_ee.svelte`) are indexed — you can `outline`, `def`, `body`, `refs` etc. on them just like regular files.
```bash
NAV="sh wm-ts-nav/nav"
# Use --root backend for Rust, --root frontend/src for TS/Svelte
$NAV --root backend outline backend/path/to/file.rs # file structure
$NAV --root backend def "ServiceName" # find definition
$NAV --root backend body "decrypt_oauth_data" # extract source code
$NAV --root backend search "%" --parent ServiceName # methods on a type
$NAV --root backend search "Trigger" --kind struct # find by kind
$NAV --root backend refs "X" --file handler.rs --caller # scoped refs with caller
$NAV --root backend callers "X" # who calls X?
$NAV --root backend callees "X" # what does X call?
```
**Limitations** — syntax-level analysis, no type inference. Use **Grep** instead when completeness matters (finding all usages, exhaustiveness checks):
- `refs`/`callers`/`callees` can't follow re-exports, glob imports, or different import paths to the same symbol
- Trait impls, macro-generated symbols (`sqlx::FromRow`), and namespace member access (`ns.X`) are invisible
- `callees` shows all identifiers in a function body, not just actual calls
## Core Principles
- **MUST `outline` before `Read`** on unfamiliar files — then `body` or `Read` with offset/limit for specifics
- Search for existing code to reuse before writing new code
- Follow established patterns in the codebase
- Keep changes focused — don't refactor beyond what's asked

View File

@@ -11,18 +11,8 @@
{$BASE_URL} {
bind {$ADDRESS}
# LSP - Language Server Protocol for code intelligence (windmill_extra:3001)
reverse_proxy /ws/* http://windmill_extra:3001
# Multiplayer - Real-time collaboration, Enterprise Edition (windmill_extra:3002)
# Uncomment and set ENABLE_MULTIPLAYER=true in docker-compose.yml
# reverse_proxy /ws_mp/* http://windmill_extra:3002
# Debugger - Interactive debugging via DAP WebSocket (windmill_extra:3003)
# Set ENABLE_DEBUGGER=true in docker-compose.yml to enable
handle_path /ws_debug/* {
reverse_proxy http://windmill_extra:3003
}
# Extra services: LSP, Multiplayer, Debugger (windmill_extra gateway)
reverse_proxy /ws/* /ws_mp/* /ws_debug/* http://windmill_extra:3000
# Search indexer, Enterprise Edition (windmill_indexer:8002)
# reverse_proxy /api/srch/* http://windmill_indexer:8002

View File

@@ -118,6 +118,18 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
# Split debug info into a separate file, then strip the binary.
# The .debug file can be extracted as a CI artifact for production debugging.
# The debuglink allows gdb to auto-discover the debug file when placed next to the binary.
RUN objcopy --only-keep-debug /windmill/target/release/windmill /windmill/target/release/windmill.debug \
&& strip /windmill/target/release/windmill \
&& objcopy --add-gnu-debuglink=/windmill/target/release/windmill.debug /windmill/target/release/windmill
# Standalone stage for extracting the .debug file without including it in the final image.
# Build with: docker build --target debuginfo --output type=local,dest=./out .
FROM scratch AS debuginfo
COPY --from=builder /windmill/target/release/windmill.debug /windmill.debug
FROM ${DEBIAN_IMAGE}
ARG TARGETPLATFORM
@@ -268,7 +280,7 @@ RUN bun install -g windmill-cli \
RUN curl -fsSL https://claude.ai/install.sh | bash \
&& cp /root/.local/share/claude/versions/* /usr/bin/claude
COPY --from=php:8.3.30-cli /usr/local/bin/php /usr/bin/php
COPY --from=php:8.3.30-cli-bookworm /usr/local/bin/php /usr/bin/php
COPY --from=composer:2.9.5 /usr/bin/composer /usr/bin/composer
# add the docker client to call docker from a worker if enabled

View File

@@ -192,70 +192,6 @@ sandbox:
This mounts both the main EE repo (used by the main worktree) and the EE worktrees directory (used by feature worktrees) into every sandbox container.
## Cursor SSH Integration (`wmc`)
`wm-cursor` (aliased as `wmc`) gives each worktree its own Cursor SSH remote window with an independently-focused tmux session. All windows are visible in the status bar across all Cursor terminals, but each one is focused on its own worktree.
This uses **grouped tmux sessions** — multiple sessions that share the same window list but track focus independently:
```
tmux session: main <-- your main Cursor terminal
tmux session: cursor-feat-a <-- Cursor window for feat-a (focused on wm-feat-a)
tmux session: cursor-feat-b <-- Cursor window for feat-b (focused on wm-feat-b)
\__ all three share the same windows in the status bar
```
### Setup
Run once from inside tmux on the remote:
```bash
./scripts/wm-cursor setup /home/hugo/projects/windmill
```
This:
1. **Merges `.vscode/settings.json`** — adds the `wm-tmux` terminal profile (auto-attaches to the `main` tmux session), disables auto port forwarding, configures forwarding for ports 8000/3000/5432, and stops rust-analyzer from auto-starting. Existing settings are preserved.
2. **Creates `.vscode/tasks.json`** — auto-starts the dev database (`start-dev-db.sh`) when the folder opens.
3. **Adds `wmc` alias to `~/.zshrc`** — so you can use `wmc` from any tmux window.
4. **Adds `eval "$(wmc completions)"`** to `~/.zshrc` — provides tab-completion for subcommands and worktree names (for `open`, `open-ee`, and `close`).
After setup, reopen Cursor's terminal to pick up the new profile.
### Usage
All commands run from inside a tmux session (i.e., from Cursor's integrated terminal after setup).
**Create a new worktree + open Cursor:**
```bash
wmc add -A -p "implement feature X"
```
This runs `workmux add`, creates a grouped tmux session, writes `.vscode/settings.json` in the worktree (with port forwarding matching the worktree's assigned ports), and opens a new Cursor window.
**Open Cursor for an existing worktree:**
```bash
wmc open my-feature
```
**Open the EE worktree in Cursor (no tmux session):**
```bash
wmc open-ee my-feature
```
This finds the matching `windmill-ee-private__worktrees/<name>` directory and opens it in a new Cursor window.
**Close a worktree's Cursor window and tmux window (keeps the worktree):**
```bash
wmc close my-feature
```
This kills the grouped tmux session and calls `workmux close` to close the tmux window. The worktree and branch are preserved. Grouped sessions are also automatically cleaned up when you `workmux rm` a worktree (via `scripts/worktree-cleanup`).
## Cargo Features
To build the backend with specific Cargo features (e.g., `enterprise`, `parquet`), pass them via `CARGO_FEATURES`. The backend pane reads this from `.env.local` and appends `--features <value>` to the `cargo watch` command.
@@ -270,20 +206,6 @@ CARGO_FEATURES="enterprise,parquet" wm add my-feature
This gets written to `.env.local` by the `post_create` hook (`scripts/worktree-env`), and the backend pane picks it up automatically.
**With `wmc` (wm-cursor):**
Use the `--features` flag:
```bash
# Create a new worktree with features
wmc add --features "enterprise,parquet" -A -p "implement feature X"
# Open an existing worktree with different features
wmc open my-feature --features "enterprise,parquet"
```
The `--features` flag exports `CARGO_FEATURES` so the `post_create` hook writes it to `.env.local`. When using `wmc open`, it updates the existing `.env.local` with the new features.
## Login
Default credentials: `admin@windmill.dev` / `changeme`

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email, server_id, error\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as, server_id, error\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -18,5 +18,5 @@
},
"nullable": []
},
"hash": "1d4bb4f53574ef95ef1016b760f849ec2372ac6a21bb2556d17a96dc72ea4980"
"hash": "02748cae17e8966dbd57a33017ccb747c84fcc12fbfd93c6c749570b94d35696"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::trigger_mode)\n ",
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::trigger_mode)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -28,5 +28,5 @@
},
"nullable": []
},
"hash": "7c1ae9cac13d1387cfa94149f039054dd8c30c16b4657e73cdb0d7c7f1cb3b6d"
"hash": "02e04f9ebc0e14f98f290bf2dc3eb00bc613ba7d29f8dd5ff31a4acd0ef3adfd"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, login_type::text, verified, super_admin, devops, name, company, username, NULL::bool as operator_only, first_time_user FROM password ORDER BY super_admin DESC, devops DESC, email LIMIT $1 OFFSET $2",
"query": "SELECT email, login_type::text, verified, super_admin, devops, name, company, username, NULL::bool as operator_only, first_time_user, role_source FROM password ORDER BY super_admin DESC, devops DESC, email LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
@@ -52,6 +52,11 @@
"ordinal": 9,
"name": "first_time_user",
"type_info": "Bool"
},
{
"ordinal": 10,
"name": "role_source",
"type_info": "Varchar"
}
],
"parameters": {
@@ -70,8 +75,9 @@
true,
true,
null,
false,
false
]
},
"hash": "b5ade857a358f2fee4bb7d005e5fef1cabea003419c891f8b1e52bc2c0156b0b"
"hash": "05027983ffdb11824190543754d0be922e1463d2046753cf80377369a90013ab"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -18,5 +18,5 @@
},
"nullable": []
},
"hash": "1cad2ebfbdc46f9c0d93329897a71701f17a33b708b334d909563c9a0dcc9c23"
"hash": "066c9690d1606bf889879b7e3c686529c37db0d5f18c83706bfbc63c8c3e4315"
}

View File

@@ -46,11 +46,11 @@
]
},
"nullable": [
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
true,
true
]

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT name, summary, array_remove(array_agg(email_to_igroup.email), null) as emails FROM email_to_igroup RIGHT JOIN instance_group ON instance_group.name = email_to_igroup.igroup GROUP BY name",
"query": "SELECT name, summary, array_remove(array_agg(email_to_igroup.email), null) as emails, instance_role FROM email_to_igroup RIGHT JOIN instance_group ON instance_group.name = email_to_igroup.igroup GROUP BY name, summary, instance_role",
"describe": {
"columns": [
{
@@ -17,6 +17,11 @@
"ordinal": 2,
"name": "emails",
"type_info": "VarcharArray"
},
{
"ordinal": 3,
"name": "instance_role",
"type_info": "Varchar"
}
],
"parameters": {
@@ -25,8 +30,9 @@
"nullable": [
false,
true,
null
null,
true
]
},
"hash": "a00f3f18087326432c9114998e47cff4f78d1b28cdb8adc6b18b937e1cf142d1"
"hash": "0aef85e3dc8910d7243f9e5a26795d3488e0969f2c334ab7c0bfcd5235e3dd82"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT name, summary, array_remove(array_agg(email_to_igroup.email), null) as emails FROM email_to_igroup RIGHT JOIN instance_group ON instance_group.name = email_to_igroup.igroup GROUP BY name, summary",
"query": "SELECT name, summary, array_remove(array_agg(email_to_igroup.email), null) as emails, instance_role FROM email_to_igroup RIGHT JOIN instance_group ON instance_group.name = email_to_igroup.igroup GROUP BY name, instance_role",
"describe": {
"columns": [
{
@@ -17,6 +17,11 @@
"ordinal": 2,
"name": "emails",
"type_info": "VarcharArray"
},
{
"ordinal": 3,
"name": "instance_role",
"type_info": "Varchar"
}
],
"parameters": {
@@ -25,8 +30,9 @@
"nullable": [
false,
true,
null
null,
true
]
},
"hash": "10f6d3ffd7406146572b1becdce5c8da5242b58f6ce46ab10296cff9d6a3a6c4"
"hash": "0b0f601716c6713f8b521a65dba01303a7756f654d1a2c04bd47c0f2d1122155"
}

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, permissioned_as, edited_by FROM schedule WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "permissioned_as",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "edited_by",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "0f26c74f604e1c3c613de8ba654cac1a41b20b1d3ea0f1a1c4ea2fcbbd314d7e"
}

View File

@@ -1,17 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM variable WHERE path = $1 AND workspace_id = $2 RETURNING path",
"query": "SELECT token_hash FROM token WHERE token_hash = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"name": "token_hash",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
@@ -19,5 +18,5 @@
false
]
},
"hash": "3317484a9c09c07c2c9db9debaecc4a4d518093ab48e79365dbb808068e0b8ff"
"hash": "0f5a31f328e59befb7dd3c3cb44439a0405d479e02ac79c2f4ec9a97636bd80d"
}

View File

@@ -1,12 +1,12 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM token WHERE expiration <= now()\n RETURNING substring(token for 10) as token_prefix, label, email, workspace_id",
"query": "DELETE FROM token WHERE expiration <= now()\n RETURNING token_prefix, label, email, workspace_id",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "token_prefix",
"type_info": "Text"
"type_info": "Varchar"
},
{
"ordinal": 1,
@@ -28,11 +28,11 @@
"Left": []
},
"nullable": [
null,
false,
true,
true,
true
]
},
"hash": "bb446cbb20166f274a7ee6e88abaa27e233e60e18b3d35545005eb680701241f"
"hash": "104fc7e5433abd7247323c5ef76b85f937776a6b47cd99c648bb4d819d3cfe57"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE password SET devops = $1 WHERE email = $2",
"query": "UPDATE password SET super_admin = $1, role_source = 'manual' WHERE email = $2",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
"hash": "411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824"
"hash": "11d89b437b9fe5d493e1806438dd56ef8c427aa1bbcfe2a11ba34cc8eab9fb4e"
}

View File

@@ -1,41 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlestepflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE\n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id!",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "workspace_id!",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "ping",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "counter",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Int4"
]
},
"nullable": [
false,
false,
true,
null
]
},
"hash": "12d37d75a429c0ddf2b2c190ab28bea5aefd27d0ed8a1bb2c8b3c1b0ece4efb7"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, email, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, permissioned_as, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -50,5 +50,5 @@
},
"nullable": []
},
"hash": "6afa076744233fc5e92188ff978990fa3a704afe3eec523f4e203f7f6e247261"
"hash": "13d60d85694b5a5fcfc7687a07b78a54ff53245271466b1f3a9d9edf43cdaa1f"
}

View File

@@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token, label, super_admin, email)\n VALUES ($1, $2, $3, $4)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Bool",
"Varchar"
]
},
"nullable": []
},
"hash": "15ef5759a2ccd7b7f9fd3f2ce0d54d01fe0a2c7e9692ac4ce29a86eb509e1a1d"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token (token_hash, token_prefix, token, email, label, super_admin)\n VALUES ($1, $2, $3, 'test@windmill.dev', 'webhook-test', false)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "1a2470da1015634d15952819f482749ef04e1a8c944c0fb7696e387d10370217"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND script_path = $3\n AND is_flow = $4\n LIMIT 1\n ",
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_hash,\n service_config,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND script_path = $3\n AND is_flow = $4\n LIMIT 1\n ",
"describe": {
"columns": [
{
@@ -40,7 +40,7 @@
},
{
"ordinal": 5,
"name": "webhook_token_prefix",
"name": "webhook_token_hash",
"type_info": "Varchar"
},
{
@@ -95,5 +95,5 @@
false
]
},
"hash": "ee537def1ead8bee48bb9f5c1f57d42e7add6011c34d91761ba23e2c74c4032c"
"hash": "1a69ef11a3f361f105c2a8af7b7fa182f3953150ade1756259b31a50e9308fce"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n email = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n mode = $11\n WHERE\n workspace_id = $12 AND path = $13\n ",
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n permissioned_as = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n mode = $11\n WHERE\n workspace_id = $12 AND path = $13\n ",
"describe": {
"columns": [],
"parameters": {
@@ -33,5 +33,5 @@
},
"nullable": []
},
"hash": "388ff2abd495cf71e87cf0c4ddc73b6c84867fb966df91b320c54acdd5e61315"
"hash": "1b7803a2060a19cb6e71f1e97619891ea8449b4a9433908cf717738846f7eec5"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "PostgreSQL",
"query": "SELECT git_sync FROM workspace_settings WHERE git_sync IS NOT NULL",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "git_sync",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": []
},
"nullable": [
true
]
},
"hash": "1bf189625a4f14e12e0d0510eb534600b68125fb55f77ad3abf3333ebab22416"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL)\n ORDER BY created_at DESC LIMIT $2 OFFSET $3",
"query": "SELECT label, token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL)\n ORDER BY created_at DESC LIMIT $2 OFFSET $3",
"describe": {
"columns": [
{
@@ -11,7 +11,7 @@
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
"type_info": "Varchar"
},
{
"ordinal": 2,
@@ -43,12 +43,12 @@
},
"nullable": [
true,
null,
false,
true,
false,
false,
true
]
},
"hash": "8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513"
"hash": "1bf4a93cb85c6eed313a2f393da9408dd2aa4e47ef7a38a0d3ccca944a09f5bb"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n route_path,\n http_method AS \"http_method: _\",\n request_type AS \"request_type: _\",\n workspaced_route,\n summary,\n description,\n authentication_method AS \"authentication_method: _\",\n authentication_resource_path\n FROM\n http_trigger\n WHERE\n path ~ ANY($1) AND\n route_path ~ ANY($2) AND\n workspace_id = $3\n ",
"query": "\n SELECT\n route_path,\n http_method AS \"http_method: _\",\n request_type AS \"request_type: _\",\n workspaced_route,\n summary,\n description,\n authentication_method AS \"authentication_method: _\",\n authentication_resource_path,\n script_path,\n is_flow,\n wrap_body\n FROM\n http_trigger\n WHERE\n path ~ ANY($1) AND\n route_path ~ ANY($2) AND\n workspace_id = $3\n ",
"describe": {
"columns": [
{
@@ -80,6 +80,21 @@
"ordinal": 7,
"name": "authentication_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 10,
"name": "wrap_body",
"type_info": "Bool"
}
],
"parameters": {
@@ -97,8 +112,11 @@
true,
true,
false,
true
true,
false,
false,
false
]
},
"hash": "9360d00990822f153ff09c7905ae3180f07d02f38ac12d07a5664d93f160e7ee"
"hash": "1cb21a66ffc89ebe53fd8f58690eec4cf11cb4b7816738202b474e8d3ffef427"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE sqs_trigger\n SET\n queue_url = $1,\n aws_resource_path = $2,\n message_attributes = $3,\n aws_auth_resource_type = $4,\n script_path = $5,\n path = $6,\n is_flow = $7,\n edited_by = $8,\n email = $9,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE\n workspace_id = $10 AND path = $11\n ",
"query": "\n UPDATE sqs_trigger\n SET\n queue_url = $1,\n aws_resource_path = $2,\n message_attributes = $3,\n aws_auth_resource_type = $4,\n script_path = $5,\n path = $6,\n is_flow = $7,\n edited_by = $8,\n permissioned_as = $9,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE\n workspace_id = $10 AND path = $11\n ",
"describe": {
"columns": [],
"parameters": {
@@ -33,5 +33,5 @@
},
"nullable": []
},
"hash": "c723c3a5066a487b93e2642993f3bf624a1f50d06c7de75157420d97cf144763"
"hash": "1d2514b3d75ffb6cc0eb09ceb8fde974a07881eb08164e885448d9a17c4ca6db"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO email_trigger (\n path, local_part, workspaced_local_part, script_path,\n is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
"query": "\n INSERT INTO email_trigger (\n path, local_part, workspaced_local_part, script_path,\n is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -17,5 +17,5 @@
},
"nullable": []
},
"hash": "1074c6c98e6a0c83ac04172a39abea21c793f58947051d39931d4da0868a1d77"
"hash": "1e28751bb98a1c477c0e582a2a39f81bf34e2d72f35ef1ea5d8c057ec9e694d8"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, email, auto_commit\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ",
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, permissioned_as, auto_commit\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -19,5 +19,5 @@
},
"nullable": []
},
"hash": "45fc21026fa76e5d69f00a68a7be81abb3ec627578f2d14f0ce33896dc6ab4cf"
"hash": "1ef63255389bdc47d5392a84aad38adf1ccc3a4923f988d8abaafc9749307c0e"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE\n mqtt_trigger\n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7,\n edited_by = $8,\n email = $9,\n script_path = $10,\n path = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
"query": "\n UPDATE\n mqtt_trigger\n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7,\n edited_by = $8,\n permissioned_as = $9,\n script_path = $10,\n path = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
"describe": {
"columns": [],
"parameters": {
@@ -35,5 +35,5 @@
},
"nullable": []
},
"hash": "e486a64b76da5de97e404c81dd6e29d333ada2dcfbbddb028f37794b85778ca8"
"hash": "1f693e2fba9885f7fc49bd2994240c4421fdae4aab496a3397c5c07d960582f2"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT label,\n concat(substring(token for 10)) AS token_prefix,\n expiration,\n created_at,\n last_used_at,\n scopes,\n email\n FROM token\n WHERE workspace_id = $1\n AND (\n scopes @> ARRAY['jobs:run:flows:' || $2]::text[]\n OR scopes @> ARRAY['run:flow/' || $2]::text[]\n )\n ",
"query": "\n SELECT label,\n token_prefix,\n expiration,\n created_at,\n last_used_at,\n scopes,\n email\n FROM token\n WHERE workspace_id = $1\n AND (\n scopes @> ARRAY['jobs:run:scripts:' || $2]::text[]\n OR scopes @> ARRAY['run:script/' || $2]::text[]\n )\n ",
"describe": {
"columns": [
{
@@ -11,7 +11,7 @@
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
"type_info": "Varchar"
},
{
"ordinal": 2,
@@ -47,7 +47,7 @@
},
"nullable": [
true,
null,
false,
true,
false,
false,
@@ -55,5 +55,5 @@
true
]
},
"hash": "6a254de9005594dc75a59a545546417c8a5aa7635be1dc0b37dc29d0f9e7c163"
"hash": "207106aa8267fe756989f3ee1eadb7e169d07463f67f1da79c8bc23c1079c185"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "WITH email_lookup AS (\n SELECT email FROM token WHERE token = $1\n )\n DELETE FROM token\n WHERE email = (SELECT email FROM email_lookup) AND label = 'session'\n RETURNING email",
"query": "WITH email_lookup AS (\n SELECT email FROM token WHERE token_hash = $1\n )\n DELETE FROM token\n WHERE email = (SELECT email FROM email_lookup) AND label = 'session'\n RETURNING email",
"describe": {
"columns": [
{
@@ -18,5 +18,5 @@
true
]
},
"hash": "1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf"
"hash": "215163b5a2791c51f9b28681c1ca1a47475dcf1a388c613a9e0154aef6582a23"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ",
"query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n mode,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -33,5 +33,5 @@
},
"nullable": []
},
"hash": "fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210"
"hash": "21d7ce033b5f67499f579aeae98806599401fcfa9765a58b8cd45a20b411d0ca"
}

View File

@@ -0,0 +1,21 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token (token_hash, token_prefix, token, email, label, expiration, scopes, workspace_id)\n SELECT $1::varchar, $2::varchar, $3::varchar, $4::varchar, $5::varchar, now() + ($6 || ' seconds')::interval, $7::text[], $8::varchar\n WHERE NOT EXISTS(SELECT 1 FROM workspace WHERE id = $8 AND deleted = true)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Text",
"TextArray",
"Varchar"
]
},
"nullable": []
},
"hash": "223fbd972728d5b3ec5b1708e3f2e1f4901b0382fca50704c9544cdec5f9352c"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM raw_script_temp WHERE created_at < NOW() - INTERVAL '1 week'",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "25ac66a1022c41267df199a95f532b0f778c25fbe0f7a7f9734c1f7e536ed6ce"
}

View File

@@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_by, permissioned_as, permissioned_as_email\n FROM v2_job\n WHERE workspace_id = 'test-workspace'\n AND trigger_kind = 'schedule'\n AND trigger = $1\n ORDER BY created_at DESC\n LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "created_by",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "permissioned_as",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "permissioned_as_email",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "270cfaea4f888e73e21a957e0328ec1f990fce409160eb7b0e807bff56defff4"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "PostgreSQL",
"query": "SELECT encode(sha256('SECRET_TOKEN'::bytea), 'hex') AS hash",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Text"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "27cafd840e5f2c85d1c1e02d84a1b372e9d40dee29a10fb8fec89492fc501556"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE v2_job_completed SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n workflow_as_code_status,\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'duration_ms'],\n to_jsonb($2::bigint)\n )\n WHERE id = $3 AND workflow_as_code_status IS NOT NULL",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Int8",
"Uuid"
]
},
"nullable": []
},
"hash": "29935e89475f637d765c516f1aa2be2f0f31fb50d519b42a056d0d73417599a3"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO script (\n workspace_id, hash, path, parent_hashes, summary, description, content,\n created_by, created_at, archived, schema, deleted, is_template,\n extra_perms, lock, lock_error_logs, language, kind, tag, draft_only,\n envs, concurrent_limit, concurrency_time_window_s, cache_ttl,\n dedicated_worker, ws_error_handler_muted, priority, timeout,\n delete_after_use, restart_unless_cancelled, concurrency_key,\n visible_to_runner_only, auto_kind, codebase, has_preprocessor,\n on_behalf_of_email, assets, modules\n )\n SELECT\n $1, hash, path, parent_hashes, summary, description, content,\n created_by, created_at, archived, schema, deleted, is_template,\n extra_perms, lock, lock_error_logs, language, kind, tag, draft_only,\n envs, concurrent_limit, concurrency_time_window_s, cache_ttl,\n dedicated_worker, ws_error_handler_muted, priority, timeout,\n delete_after_use, restart_unless_cancelled, concurrency_key,\n visible_to_runner_only, auto_kind, codebase, has_preprocessor,\n on_behalf_of_email, assets, modules\n FROM script\n WHERE workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
},
"nullable": []
},
"hash": "2c256552a430877c42224055aeb81df33d88ff295483cb28369eda42ce58afec"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO raw_script_temp (workspace_id, hash, content, created_at)\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (workspace_id, hash) DO UPDATE SET created_at = NOW()",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Bpchar",
"Text"
]
},
"nullable": []
},
"hash": "2d523cd0d5b7107b15846b885fa40af492d4c8a8871cef972980150f319fe6ff"
}

View File

@@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "SELECT parent_job, flow_step_id FROM v2_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "parent_job",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "flow_step_id",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true,
true
]
},
"hash": "32ca7941db013dacd2479962fa9ed5c8c64daec45ba820a6c8f7d7ab76cc40c9"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO app_bundles (app_version_id, w_id, file_type, data)\n VALUES ($1, $2, $3, $4)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Varchar",
"Varchar",
"Bytea"
]
},
"nullable": []
},
"hash": "3630aa84c1e84418e0f644a71849aea87a85c7199a5d3fa2a43f0e91b0a5be9d"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT j.id\n FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_runtime r USING (id) LEFT JOIN v2_job_status s USING (id)\n WHERE r.ping < now() - ($1 || ' seconds')::interval\n AND q.running = true AND j.kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlestepflow') AND j.same_worker = false",
"query": "SELECT j.id\n FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_runtime r USING (id) LEFT JOIN v2_job_status s USING (id)\n WHERE r.ping < now() - ($1 || ' seconds')::interval\n AND q.running = true AND j.kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlestepflow') AND j.same_worker = false AND q.suspend_until IS NULL",
"describe": {
"columns": [
{
@@ -18,5 +18,5 @@
false
]
},
"hash": "0186c1058f147e012b8120c342caf8688a6d1643747be3ec4f784c3029a59e52"
"hash": "36b556a1c8630547cb7f5f88a1a0f02effb9e62409cd61fa4de60d11d50ee206"
}

View File

@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n f.schema AS \"schema: serde_json::Value\",\n fv.value->>'preprocessor_module' IS NOT NULL AS \"has_preprocessor: bool\"\n FROM flow f\n LEFT JOIN flow_version fv ON fv.id = f.versions[array_length(f.versions, 1)]\n AND fv.workspace_id = f.workspace_id\n WHERE f.path = $1 AND f.workspace_id = $2 AND NOT f.archived",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "schema: serde_json::Value",
"type_info": "Json"
},
{
"ordinal": 1,
"name": "has_preprocessor: bool",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true,
null
]
},
"hash": "372ec62fad93831b00d44f6c52a148b6bfa6008e49b2a80bec06c76d9432ec77"
}

View File

@@ -1,11 +1,11 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, edited_by FROM http_trigger WHERE path = $1 AND workspace_id = $2",
"query": "SELECT permissioned_as, edited_by FROM websocket_trigger WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "email",
"name": "permissioned_as",
"type_info": "Varchar"
},
{
@@ -25,5 +25,5 @@
false
]
},
"hash": "8311a553c44221751ffdbbe6a997d6feba8d43292daf6c5433b66bd8450e8854"
"hash": "39062cdb183b97906c25602000321a76d4e629ba027364190a2487cc9bd93235"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n mode\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ",
"query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n mode\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -32,5 +32,5 @@
},
"nullable": []
},
"hash": "3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3"
"hash": "3aa3d0362fa8ed97dd034454b05f14880c9bb43d0c3ce4ea9b4511aa2d092d0c"
}

View File

@@ -0,0 +1,44 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT provider, model, mode,\n COUNT(*)::BIGINT as \"session_count!\",\n COALESCE(SUM(message_count), 0)::BIGINT as \"message_count!\"\n FROM ai_chat_usage\n WHERE created_at > NOW() - INTERVAL '30 days'\n GROUP BY provider, model, mode\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "provider",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "model",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "mode",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "session_count!",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "message_count!",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
false,
false,
null,
null
]
},
"hash": "3ec92c1682f3ce701028f66f7ce83030e7a7ce32971a5621aceb738a3673f943"
}

View File

@@ -0,0 +1,58 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, email, scopes, workspace_id, super_admin, owner, expiration FROM token WHERE token_hash = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "label",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "scopes",
"type_info": "TextArray"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "super_admin",
"type_info": "Bool"
},
{
"ordinal": 5,
"name": "owner",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "expiration",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true,
true,
true,
true,
false,
true,
true
]
},
"hash": "406bcbf55758b10243c8eaff1c349b8082c0052d626bf67e08317e56ab9ad026"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE native_trigger\n SET script_path = $1, is_flow = $2, webhook_token_prefix = $3, service_config = $4, error = NULL, updated_at = NOW()\n WHERE\n workspace_id = $5\n AND service_name = $6\n AND external_id = $7\n ",
"query": "\n UPDATE native_trigger\n SET script_path = $1, is_flow = $2, webhook_token_hash = $3, service_config = $4, error = NULL, updated_at = NOW()\n WHERE\n workspace_id = $5\n AND service_name = $6\n AND external_id = $7\n ",
"describe": {
"columns": [],
"parameters": {
@@ -26,5 +26,5 @@
},
"nullable": []
},
"hash": "27ada97cb533c8595f1d73987c7823d8e54c96889e06895c57cafae9ca27bf8b"
"hash": "40a8bf6a5a42c275d73221bc5f386f2e18cb911352551d0a34bf1933e558674e"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM v2_job_debounce_batch WHERE debounce_batch = (\n SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = $1\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": []
},
"hash": "40bcbfdcae9842c7919eb6dcfe44d844508304700b292059b24c3f74454a7cca"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n mode,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n email,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ",
"query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n mode,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n permissioned_as,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -81,5 +81,5 @@
},
"nullable": []
},
"hash": "ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db"
"hash": "41ad9954bebe31b0545837147b1616da2e6743f3a45775cd556a7414bc8f9726"
}

View File

@@ -0,0 +1,27 @@
{
"db_name": "PostgreSQL",
"query": "\n WITH step_index AS (\n SELECT idx::text AS idx\n FROM v2_job_status,\n jsonb_array_elements(flow_status->'modules') WITH ORDINALITY arr(elem, idx)\n WHERE id = $1\n AND elem->>'id' = $5\n LIMIT 1\n ), completed AS (\n INSERT INTO v2_job_completed\n (workspace_id, id, started_at, duration_ms, result,\n flow_status, workflow_as_code_status, status, worker)\n SELECT\n q.workspace_id, q.id, q.started_at,\n (EXTRACT('epoch' FROM now()) - EXTRACT('epoch' FROM COALESCE(q.started_at, now()))) * 1000,\n $3::text::jsonb,\n CASE WHEN si.idx IS NOT NULL\n THEN jsonb_set(\n s.flow_status,\n ARRAY['modules', (si.idx::int - 1)::text],\n $6::jsonb\n )\n ELSE s.flow_status\n END,\n s.workflow_as_code_status,\n 'skipped'::job_status,\n q.worker\n FROM v2_job_queue q\n LEFT JOIN v2_job_status s ON s.id = q.id\n LEFT JOIN step_index si ON true\n WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = EXCLUDED.result\n RETURNING 1 AS x\n ), _deleted AS (\n DELETE FROM v2_job_queue WHERE id = $1\n ), _logged AS (\n INSERT INTO job_logs (logs, job_id, workspace_id)\n VALUES ($4, $1, $2)\n ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs)\n )\n SELECT x FROM completed\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "x",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Uuid",
"Varchar",
"Text",
"Text",
"Text",
"Jsonb"
]
},
"nullable": [
null
]
},
"hash": "4461fe84370e7f07b4423ea5e71b913dd6ee9c655f9ec7087cc0fec9b9c3099a"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT ig.instance_role FROM email_to_igroup eig\n JOIN instance_group ig ON ig.name = eig.igroup\n WHERE eig.email = $1 AND ig.instance_role IS NOT NULL",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "instance_role",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true
]
},
"hash": "449934711f09b700fca46be3e165d37d14d51d5c95776e2d5491b5c5ab3e25b7"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, parent_job)\n VALUES ($1, 'noop', 'deno', 'test-user', 'u/test-user', 'test@windmill.dev', $2, $3)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Varchar",
"Uuid"
]
},
"nullable": []
},
"hash": "4538bea4159677d8e653159d7d01649cae08e6ffef668a7cbac49b312bf30766"
}

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO debounce_key (job_id, key)\n VALUES ($1, $2)\n ON CONFLICT (key)\n DO UPDATE SET\n previous_job_id = debounce_key.job_id,\n job_id = EXCLUDED.job_id,\n debounced_times = debounce_key.debounced_times + 1\n RETURNING\n debounced_times,\n first_started_at,\n previous_job_id AS job_id_to_debounce\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "debounced_times",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "first_started_at",
"type_info": "Timestamptz"
},
{
"ordinal": 2,
"name": "job_id_to_debounce",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Uuid",
"Varchar"
]
},
"nullable": [
false,
false,
true
]
},
"hash": "45de6be332f4ec89482782e3b1640649ed1a6f6d4f1e1b636c71bdd36c31b618"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n path,\n summary,\n description\n FROM\n flow\n WHERE\n path ~ ANY($1) AND\n workspace_id = $2 AND\n archived is FALSE\n ",
"query": "SELECT\n path,\n summary,\n description\n FROM\n flow\n WHERE\n path ~ ANY($1) AND\n workspace_id = $2 AND\n archived is FALSE\n ",
"describe": {
"columns": [
{
@@ -31,5 +31,5 @@
false
]
},
"hash": "33367c42e87e78ae987c0966dc4d445c5eff75b2e2843ffd7a46b03cbaea9ae8"
"hash": "4615b37cb848f9589622426d291c721e532b230c527deb701e24605c7027e38b"
}

View File

@@ -0,0 +1,19 @@
{
"db_name": "PostgreSQL",
"query": "WITH update_lock AS (\n UPDATE script SET lock = $1, modules = COALESCE($6, modules) WHERE hash = $2 AND workspace_id = $3\n )\n INSERT INTO lock_hash (workspace_id, path, lockfile_hash)\n VALUES ($3, $4, $5)\n ON CONFLICT (workspace_id, path) DO UPDATE SET lockfile_hash = $5",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Int8",
"Text",
"Varchar",
"Int8",
"Jsonb"
]
},
"nullable": []
},
"hash": "49b18e987e2dfa3c7ab915757ff3b9c0e6e371136b565f9b0f5a3393ef8d8d57"
}

View File

@@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "SELECT super_admin, devops FROM password WHERE email = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "super_admin",
"type_info": "Bool"
},
{
"ordinal": 1,
"name": "devops",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false
]
},
"hash": "4afabac265755dd90c33193260eea8be1f62e4607fcd8f401701ab66f6d20cae"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM token WHERE token = $1",
"query": "DELETE FROM token WHERE token_hash = $1",
"describe": {
"columns": [],
"parameters": {
@@ -10,5 +10,5 @@
},
"nullable": []
},
"hash": "66e0968fe9f757755945a7010153821cf73ace9d6692750ccc4cca37701ed77a"
"hash": "4b76c4a387786bc5bb69e4c684c34b936c3ffff44ae58f0709d05ba3ff534f79"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE nats_trigger\n SET\n nats_resource_path = $1,\n subjects = $2,\n stream_name = $3,\n consumer_name = $4,\n use_jetstream = $5,\n script_path = $6,\n path = $7,\n is_flow = $8,\n edited_by = $9,\n email = $10,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $13,\n error_handler_args = $14,\n retry = $15\n WHERE\n workspace_id = $11 AND path = $12\n ",
"query": "\n UPDATE nats_trigger\n SET\n nats_resource_path = $1,\n subjects = $2,\n stream_name = $3,\n consumer_name = $4,\n use_jetstream = $5,\n script_path = $6,\n path = $7,\n is_flow = $8,\n edited_by = $9,\n permissioned_as = $10,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $13,\n error_handler_args = $14,\n retry = $15\n WHERE\n workspace_id = $11 AND path = $12\n ",
"describe": {
"columns": [],
"parameters": {
@@ -24,5 +24,5 @@
},
"nullable": []
},
"hash": "9f41ea5cbe4cffa74e4a283fe8f023c813e349956487f7b6599da452c068e9b9"
"hash": "4ba114f54ed88c27dd4aed05a273c91e98913397b310ff760c1dbb9077764e9b"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "PostgreSQL",
"query": "SELECT token_hash FROM token WHERE email = 'test@windmill.dev' AND label = 'test token'",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "token_hash",
"type_info": "Varchar"
}
],
"parameters": {
"Left": []
},
"nullable": [
false
]
},
"hash": "4bf2f3c6771ab4a15b94ba713ebaab2b35961f750600500e3736edcff1c191fe"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT label,\n concat(substring(token for 10)) AS token_prefix,\n expiration,\n created_at,\n last_used_at,\n scopes,\n email\n FROM token\n WHERE workspace_id = $1\n AND (\n scopes @> ARRAY['jobs:run:scripts:' || $2]::text[]\n OR scopes @> ARRAY['run:script/' || $2]::text[]\n )\n ",
"query": "\n SELECT label,\n token_prefix,\n expiration,\n created_at,\n last_used_at,\n scopes,\n email\n FROM token\n WHERE workspace_id = $1\n AND (\n scopes @> ARRAY['jobs:run:flows:' || $2]::text[]\n OR scopes @> ARRAY['run:flow/' || $2]::text[]\n )\n ",
"describe": {
"columns": [
{
@@ -11,7 +11,7 @@
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
"type_info": "Varchar"
},
{
"ordinal": 2,
@@ -47,7 +47,7 @@
},
"nullable": [
true,
null,
false,
true,
false,
false,
@@ -55,5 +55,5 @@
true
]
},
"hash": "29673d489fbf45fc249da04c1a2fd60e2364ba87263f962ed7d4329c916620a1"
"hash": "4c7231f24fd0bcc99004c5bd4065697cd321b397422a7c689b85216bbb1fd525"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, cache_ignore_s3_path, runnable_settings_handle) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38)",
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, cache_ignore_s3_path, runnable_settings_handle, modules) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39)",
"describe": {
"columns": [],
"parameters": {
@@ -78,7 +78,7 @@
"Int4",
"Varchar",
"Bool",
"Bool",
"Varchar",
"Varchar",
"Bool",
"Text",
@@ -87,10 +87,11 @@
"Varchar",
"Int4",
"Bool",
"Int8"
"Int8",
"Jsonb"
]
},
"nullable": []
},
"hash": "b4eb72b0274cbdce7490f63c36d0d16ee847294fadc138593a1baa417cbb3652"
"hash": "4d983f1e3e63a1a70edf5d867d9f23f2069a7a4ba1dcc1331ecccdf1c6a95cb8"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token (token_hash, token_prefix, email, label, super_admin, owner, workspace_id)\n VALUES ($1, $2, 'charlie@windmill.dev', 'Charlie new token', false, 'u/charlie', 'test-workspace')",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar"
]
},
"nullable": []
},
"hash": "4e88aec662ebc70e0425a48a1b4e2e60e3183fa81a411622891caea6dc03fa90"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO script (\n workspace_id, hash, path, parent_hashes, summary, description, content,\n created_by, created_at, archived, schema, deleted, is_template,\n extra_perms, lock, lock_error_logs, language, kind, tag, draft_only,\n envs, concurrent_limit, concurrency_time_window_s, cache_ttl,\n dedicated_worker, ws_error_handler_muted, priority, timeout,\n delete_after_use, restart_unless_cancelled, concurrency_key,\n visible_to_runner_only, no_main_func, codebase, has_preprocessor,\n on_behalf_of_email, assets\n )\n SELECT\n $1, hash, path, parent_hashes, summary, description, content,\n created_by, created_at, archived, schema, deleted, is_template,\n extra_perms, lock, lock_error_logs, language, kind, tag, draft_only,\n envs, concurrent_limit, concurrency_time_window_s, cache_ttl,\n dedicated_worker, ws_error_handler_muted, priority, timeout,\n delete_after_use, restart_unless_cancelled, concurrency_key,\n visible_to_runner_only, no_main_func, codebase, has_preprocessor,\n on_behalf_of_email, assets\n FROM script\n WHERE workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
},
"nullable": []
},
"hash": "4fcce9b5b039b73b2f19fa9d15294bbb30311749431f31e488c32d21b2337544"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval\n AND running = true AND (ping IS NULL OR ping < now() - ('60 seconds')::interval) AND same_worker = true AND worker IS NOT NULL GROUP BY worker",
"query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval\n AND running = true AND (ping IS NULL OR ping < now() - ('60 seconds')::interval) AND same_worker = true AND worker IS NOT NULL AND v2_job_queue.suspend_until IS NULL GROUP BY worker",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
null
]
},
"hash": "469f8b7f691e621cce78b83994b4a4625bb7fbd0974c69745c31c7562563944f"
"hash": "4fdb9dc38c0a8e882a1dee39e42664b4c85fd43edbd7ebd8fd5ad380e5a8e3cc"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "51f09f073842a6990535b887d8267fab305c21e4d7703bedbadf405b5c2d7582"
}

View File

@@ -1,11 +1,11 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM token WHERE workspace_id = $1 AND label IS DISTINCT FROM 'session' RETURNING token",
"query": "DELETE FROM token WHERE workspace_id = $1 AND label IS DISTINCT FROM 'session' RETURNING token_prefix",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "token",
"name": "token_prefix",
"type_info": "Varchar"
}
],
@@ -18,5 +18,5 @@
false
]
},
"hash": "2d6607b3c38fe72b5663c32de58dacbabed4c5ae28101e3ae2694f96fd055a91"
"hash": "52379713a1f7312127bcd13c9a8027a85270c25c5a0f0d4d7670bd602bd3cebf"
}

View File

@@ -1,18 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, $4, $5)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Timestamptz",
"Bool"
]
},
"nullable": []
},
"hash": "54756c6c39888feb2206b056df1c84c3bb44adc490309954359845c06b6e607c"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = COALESCE($24, email),\n edited_by = $25\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n permissioned_as = $26\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
"describe": {
"columns": [
{
@@ -65,96 +65,101 @@
},
{
"ordinal": 12,
"name": "permissioned_as",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 13,
"ordinal": 14,
"name": "on_failure",
"type_info": "Varchar"
},
{
"ordinal": 14,
"ordinal": 15,
"name": "on_failure_times",
"type_info": "Int4"
},
{
"ordinal": 15,
"ordinal": 16,
"name": "on_failure_exact",
"type_info": "Bool"
},
{
"ordinal": 16,
"ordinal": 17,
"name": "on_failure_extra_args: _",
"type_info": "Jsonb"
},
{
"ordinal": 17,
"ordinal": 18,
"name": "on_recovery",
"type_info": "Varchar"
},
{
"ordinal": 18,
"ordinal": 19,
"name": "on_recovery_times",
"type_info": "Int4"
},
{
"ordinal": 19,
"ordinal": 20,
"name": "on_recovery_extra_args: _",
"type_info": "Jsonb"
},
{
"ordinal": 20,
"ordinal": 21,
"name": "on_success",
"type_info": "Varchar"
},
{
"ordinal": 21,
"ordinal": 22,
"name": "on_success_extra_args: _",
"type_info": "Jsonb"
},
{
"ordinal": 22,
"ordinal": 23,
"name": "ws_error_handler_muted",
"type_info": "Bool"
},
{
"ordinal": 23,
"ordinal": 24,
"name": "retry",
"type_info": "Jsonb"
},
{
"ordinal": 24,
"ordinal": 25,
"name": "no_flow_overlap",
"type_info": "Bool"
},
{
"ordinal": 25,
"ordinal": 26,
"name": "summary",
"type_info": "Varchar"
},
{
"ordinal": 26,
"ordinal": 27,
"name": "description",
"type_info": "Text"
},
{
"ordinal": 27,
"ordinal": 28,
"name": "tag",
"type_info": "Varchar"
},
{
"ordinal": 28,
"ordinal": 29,
"name": "paused_until",
"type_info": "Timestamptz"
},
{
"ordinal": 29,
"ordinal": 30,
"name": "cron_version",
"type_info": "Text"
},
{
"ordinal": 30,
"ordinal": 31,
"name": "dynamic_skip",
"type_info": "Varchar"
}
@@ -185,6 +190,7 @@
"Text",
"Varchar",
"Varchar",
"Varchar",
"Varchar"
]
},
@@ -201,6 +207,7 @@
true,
false,
false,
false,
true,
true,
true,
@@ -222,5 +229,5 @@
true
]
},
"hash": "987d79f7c6d7bc148cc8aab67e47161cfca045966e995e28c7a7ad090cffeda0"
"hash": "54b4c762add9b1ebfdb2a6d5abd6d20e86dc0e6544f0bb22fa4ec68aa54a4dc8"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token_hash, token_prefix, token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, $4, $5, now() + ($6 || ' seconds')::interval, $7)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Varchar",
"Text",
"Bool"
]
},
"nullable": []
},
"hash": "54c0c20fe025d4fb45f04ff3389b25915f671e7c52426fc54b2fd533b90596e2"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT token FROM token WHERE token_hash = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "token",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true
]
},
"hash": "56031289603fbf9c60ff2c04750fa0e94550eb617612c2bba81b9ce150d355b5"
}

View File

@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n DELETE FROM token\n WHERE token LIKE concat($1::text, '%')\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "58dc872520beaa914fef8b7f30e578261fb9ebd92a81e1f2c8edaf93cece0819"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "5c056ad6cc8967393729288437205c605a24118021fdb2b21b6b61695dc4ff28"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM token WHERE token_hash = $1) AS exists",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
null
]
},
"hash": "5c09c2ffb28f6eee3d7e48bd6373c0bcddc0943346f02315b962db3b13590d30"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n mode = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n email = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ",
"query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n mode = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n permissioned_as = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ",
"describe": {
"columns": [],
"parameters": {
@@ -82,5 +82,5 @@
},
"nullable": []
},
"hash": "2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282"
"hash": "5efbf92ac7347e73769c66ffdc4037c7e56a5939d9ffcbee13e0264cbb2a6dfe"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT debounced_times FROM debounce_key WHERE key = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "debounced_times",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "6009cf60c43608bb1c7924dbd0a9cdc01986d787eb24f0fffe4550d939851e22"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "WITH active_users AS (SELECT distinct username as email FROM (SELECT username, timestamp, operation FROM audit_partitioned UNION ALL SELECT username, timestamp, operation FROM audit) AS a WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username, first_time_user\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC, devops DESC\n LIMIT $1 OFFSET $2",
"query": "WITH active_users AS (SELECT distinct username as email FROM (SELECT username, timestamp, operation FROM audit_partitioned UNION ALL SELECT username, timestamp, operation FROM audit) AS a WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username, first_time_user, role_source\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC, devops DESC\n LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
@@ -52,6 +52,11 @@
"ordinal": 9,
"name": "first_time_user",
"type_info": "Bool"
},
{
"ordinal": 10,
"name": "role_source",
"type_info": "Varchar"
}
],
"parameters": {
@@ -70,8 +75,9 @@
true,
true,
true,
false,
false
]
},
"hash": "9229d9a9ff389cf26e480b604b83900e2d362ee934ef27284ef39f4eed440e59"
"hash": "60118de85463098220b1c74f667b6fedb0f3f0040844c3774145e8f1f4c023ce"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email, login_type::TEXT, super_admin, devops, verified, name, company, username, NULL::bool as operator_only, first_time_user FROM password WHERE email = $1",
"query": "SELECT email, login_type::TEXT, super_admin, devops, verified, name, company, username, NULL::bool as operator_only, first_time_user, role_source FROM password WHERE email = $1",
"describe": {
"columns": [
{
@@ -52,6 +52,11 @@
"ordinal": 9,
"name": "first_time_user",
"type_info": "Bool"
},
{
"ordinal": 10,
"name": "role_source",
"type_info": "Varchar"
}
],
"parameters": {
@@ -69,8 +74,9 @@
true,
true,
null,
false,
false
]
},
"hash": "37e23397905e25bbbf5a7047c790967a97cc8f6948beef706b0c053621882330"
"hash": "65c59e224e460351c2f88261f8b1b1e7ce2bb160270b59c0f359b7952453b2b9"
}

Some files were not shown because too many files have changed in this diff Show More