mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 08:07:15 +00:00
e866b68cdf3fcd567e603aff09f4619fa8adc637
14455
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e866b68cdf |
feat: surface execution usage in the sidebar and explain what an execution is (#10760)
* feat: surface execution usage in the sidebar and explain what an execution is Users read "executions" as a job count and are surprised by the real number, which meters a second of compute. Every place the UI prints an execution count now says so, and the sidebar carries a usage meter for the quota that will bind first. Adds SidebarUsage at the bottom of both sidebar surfaces: a ring in the collapsed rail, a labelled bar when expanded, and a modal breaking down every quota. On the free tier it meters the per-user and per-workspace 1000-execution caps; on a paid plan it meters workspace usage against the executions the workspace's seats already include. Item.tooltip was inert on disabled dropdown rows: DropdownSubmenuItem rendered the info icon inside the disabled button, which swallows hover, and the row's own title attribute shadowed any wrapper title. Both renderers now fall back to a wrapper title the way DropdownV2Inner already intended. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the usage meter tied to the workspace it describes isPremiumStore held the previous workspace's tier across a switch, which no consumer noticed while it only gated affordances — the usage meter is the first surface to render a number from it, and would have shown a paid seat quota for a free workspace. It is now undefined until the active workspace's tier is known, and a superseded response no longer writes. The seat fetch had the same shape: a slow response for the workspace we left overwrote the current count and stayed wrong until the next switch. The usage wrapper also carried the padding the brand-mark row used to own, which shifted the sidebar bottom by 4px on every instance where the meter renders nothing. The component owns its own padding instead. Names the collapsed ring for assistive tech, which otherwise saw an unlabelled button whose only signal was the arc's color. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope the usage meter to the billing workspace and a known tier A fork's usage, tier and bill all resolve to its billing root, but its member list is deliberately a subset of the root's, so counting fork members metered root usage against a fork-sized cap and invented billed-seat overages. Seats now come from the billing root, and the paid meter stays hidden when that root is not visible from the fork. The tier was cleared only after the user-store round-trip, so the meter rendered the previous workspace's tier for the length of it — a free→paid switch showed the 1000-execution hard cap on a paid workspace, not a race but every time. The clear now happens before the first await. Workspace usage had neither guard: a superseded response overwrote the store permanently, and the meter is the first surface to print that number as its headline rather than bury it in a dropdown. The free-tier counters keyed off `!$isPremiumStore`, which reads an unknown tier as free and flashed the free-tier blocks during a paid-to-paid switch. They wait for a known tier instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: never render an unresolved execution count as zero The workspace-usage clear wrote 0, which is a real usage value: an in-flight or failed fetch rendered as a green "0/1,000" bar, and a rejection left it there for the session because loadUsage had no failure path. Usage is now undefined until it resolves, each endpoint is assigned on its own so one failing leaves the other's number intact, and a quota is listed only once its own usage, tier and cap are known. The legacy counters show an em dash rather than a fabricated 0. The fork gates read an unknown tier as not-premium, so clearing the tier on switch made the fork entry point disappear for the length of the fetch on a paid-to-paid switch. They hold while the tier is unknown. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read the usage endpoints as numbers, and fall back to the free tier Both usage endpoints serve text/plain, so the client hands back a string despite the generated `number` type. Interpolation and arithmetic coerced it, which is why nothing noticed before, but `toLocaleString` on a string returns it unchanged — a five-figure count rendered without its thousands separator against a formatted cap. A failed tier fetch left the tier unknown for the session, and consumers hold premium-only affordances through the unknown window so a free workspace kept offering them. It falls back to the free tier instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep an unknown tier unknown, and refresh the seat cap on demand Falling back to the free tier on a failed tier fetch fixed the affordance gates by lying to the meter: a paid workspace's real five-figure usage rendered against the 1000 hard cap, red, under "jobs stop running for the rest of the month". The tier stays unknown instead, and the two consumers get what each needs — the meter hides, while affordances read `maybePremium`, which holds through the pending window but fails closed once the fetch has failed. Membership changes elsewhere don't reach this component, so the seat cap could show an overage against a cap that had since grown. It re-resolves when the modal opens, which is when the number is read rather than glanced at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let anything showing executions re-read them The counters were written in one place, the root layout, on a workspace change only — so a tab left open all day showed the count from whenever the workspace was opened, and the modal-open refresh could only reach the seat cap, leaving a freshly computed denominator over a stale numerator. Moves the fetch to lib/usage.ts, next to the stores it writes, so the meter can refresh both numbers when its modal opens. Seats follow a membership signal that WorkspaceUserSettings bumps where it already refetches after every mutation, so the cap stops lagging a role change without either side owning the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: order concurrent usage and seat refreshes The workspace id doesn't order two requests for the same workspace, and both refreshes can now have two in flight: usage through A→B→A or a modal-open refresh landing on one already running, seats through a membership bump arriving mid-request. An older response could win and restore the count it replaced. Each refresh takes a generation and only writes if it is still the newest. The membership signal also fired on a plain read, so opening the users tab made every consumer re-fetch a list identical to the one it held. It bumps on an observed change to the member set instead, never on the first read. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: count only billable seats, and order the tier requests The cap counted every member row, while the backend bills `NOT disabled AND NOT is_service_account` — a workspace with service accounts got an inflated included quota, which hides a real overage rather than inventing one. The seat basis matches `count_paid_seats` now, and the membership signature carries both fields so enabling or disabling a member re-resolves the cap. The tier fetch was the one refresh still ordered by workspace id alone, so a late failure for a workspace could raise the failure flag over a tier a newer request had already resolved. It takes a generation like the other two. The membership signature is keyed by workspace: this page survives a workspace switch, and comparing one workspace's members against another's reported a membership change where only the workspace had changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: compare the member set only against the same workspace's Qualifying the signature with the workspace put the workspace inside the value being compared, so a switch made every comparison unequal and bumped the version unconditionally — the opposite of the intent, and worse than before the key. The workspace is the key now, not part of the payload: a different one has nothing to compare against and re-baselines silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: hold the usage and tier fetches in resources Every one of these values belongs to a workspace but lived in a bare store, so each writer and reader re-derived "does this still describe what I'm rendering?" by hand. Nine sites did, and the ones that forgot were most of this branch's review findings: three stale-workspace overwrites, three A→B→A races, and two placeholders (`0` executions, `false` tier) that read as real data because an in-band value was standing in for "not known". `resource` from runed — which frontend/AGENTS.md prescribes for async data, and which ~80 files here already use — supplies all three properties as behaviour rather than convention: a superseded fetch is discarded, the value resets when its key changes, and loading and error are states instead of magic values. The seat count keys on the billing root and the membership version, so both a workspace switch and an added member re-resolve it. That removes three generation counters, two workspace trackers, and the manual clear-and-compare around each fetch. What remains is one publish site that asserts the value still carries the active workspace before it reaches a store. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: order the resource responses that runed does not The refactor claimed `resource` discards a superseded fetch. It does not: its only ordering is an AbortController whose signal the generated client cannot consume, and `current = result` runs unconditionally once a fetch resolves. So a late answer for a workspace we had left still landed in `current`, and the publish site — which trusted `current` — cleared the value on screen for the workspace we were on. That reinstated the races the generation counters had covered. `loading` was standing in for the missing ordering, and it cannot: it is also true during a `refetch()`, when `current` is still the right value. Gating on it meant every re-read blanked the meter, and clicking it unmounted the modal that same click had opened, since both sit behind the quota it had just cleared. Values now carry the scope they describe and `scopedValue` keeps the newest one matching the active scope, so a superseded answer neither publishes nor erases, and a re-read leaves the display alone. The account-wide user counter keys on the account, so a workspace switch no longer clears it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: order responses within a scope, not just across scopes The tag carried what a value described but not when it was asked for, so two fetches for one scope — a refetch landing on an in-flight load, or a second membership invalidation — were indistinguishable and the older won if it landed last. That left the seat cap reading the pre-change number until the next bump or switch, which is the stale cap the generation counters had covered. Widening the tag to the resource key would have fixed it by blanking the bar on every membership change, so the issue order travels alongside the scope instead: `tagged` stamps each request as it is issued, and only a strictly newer answer for the current scope replaces the held one. The unit tests now cover the same-key case they missed; both new ones fail against the key-only guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: ignore a user list a newer read has overtaken `lastSeen` was written unconditionally after the await, so a response for a workspace already left overwrote the baseline for the workspace on screen. The next real membership change there then compared against a baseline that was never taken for it, re-baselined silently, and never bumped `workspaceMembershipVersion` — leaving the sidebar on the old seat cap. The `users` assignment had the same hole: an overtaken list could paint over a newer one. Both now go through a single check: a read whose issue order is behind the last applied one is dropped before it touches either. Also trims the two `scopedValue` docstrings and the membership rationale to the four lines AGENTS.md allows, and records there that a failed refresh keeps the last successful value rather than blanking. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: do not claim a plan before the tier resolves Widening `isPremiumStore` to `boolean | undefined` left `UserMenu`'s `{:else}` catching the unresolved state: with the tier still in flight, or after the request failed, a free workspace was told it was on the "Premium plan". Both branches under that block assert a plan, so the block now renders only once the tier is known — which also keeps the bordered divider from appearing empty while it resolves. Verified against the running instance with the tier stubbed slow: unresolved shows neither branch, `false` shows the free counters, `true` shows "Premium plan". Reverting the guard reproduces the wrong label at 300ms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin how a late answer orders against the read that replaced it Returning to a scope whose earlier read is still in flight is the one case the guard resolves by scope rather than by sequence, and the suite only covered it with nothing outstanding. It now covers the late answer itself: it stands while it is the only value describing the scope, the read issued on returning supersedes it, and it cannot come back afterwards. Also gives the meter the explicit `type="button"` the sibling sidebar rows use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: size the modal's plan button with unifiedSize `size` is deprecated on `Button`. `unifiedSize="sm"` renders the plan button at the same height and weight as the modal's own Close button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: match the plan button to the modal's own action button `unifiedSize="sm"` is `h-7`, and the Cancel button `Modal` renders beside it is `px-3 py-[7px]`, i.e. 32px — so the two sat 4px apart. `md` is the unified size that lands on 32px, which pairs them without putting a deprecated prop back. Measured both boxes rather than the new one alone: 32px and 32px, same top. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: instrument the execution meter, and bill-align PremiumInfo's seats The meter's only interaction is opening the modal, so that is what it counts: `usage_meter/opened`, keyed by the plan tier and the quota that was tightest — `free:user`, `free:workspace`, `paid:workspace`. The full set is a type next to the call site so the vocabulary stays readable in one place. The pair is registered in `FEATURE_USAGE_KINDS` (windmill-ee-private), without which the post is dropped with a 204 and records nothing. Verified both halves: the browser posts `{"feature":"usage_meter","kind":"opened","key":"free:user","value":1}`, the running EE image drops it because its registry predates the entry, and `is_recordable_event` accepts it once the entry is there. `PremiumInfo` computed its seats from an unfiltered user list, so the billing page counted disabled members and service accounts that `count_paid_seats` does not bill. Same filter as the sidebar's cap now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: point ee-repo-ref at the usage_meter registration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read the member list before the seat rows that depend on it `loadPremiumInfo` reads `users` after its own await and nothing recomputes the seat rows when the list lands, so whenever `premium_info` won the race the page rendered zero developers, zero operators and zero seats and kept them. The list is now fetched first, and a failure to read it no longer costs the rest of the page. Also refreshes the registered-action inventory in `docs/feature-telemetry.md`, which the new pair makes 21 across nine features. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: scope the seat comment to the counter it matches The comment claimed parity with the seats actually charged, which nothing in this repo computes: `count_paid_seats` documents itself as counting provisioned members rather than billing's active-user population, and the Stripe quantity is not derived here. What the filter buys is agreement with that counter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to c6902ec2c51dc0ce30962afbfab3e456c5d9b831 This commit updates the EE repository reference after PR #735 was merged in windmill-ee-private. Previous ee-repo-ref: bbc48fae6b73b6d72fe2e125e6003794a4ece167 New ee-repo-ref: c6902ec2c51dc0ce30962afbfab3e456c5d9b831 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
ee1f9814c2 |
fix: gate the chat's open_page on the operating workspace's role (#10779)
* fix: gate the chat's open_page on the operating workspace's role Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: don't describe an unresolved open_page role as a denial Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5099f405d4 |
feat: make the Git Repo Viewer work with GitHub App repositories (#10765)
* fix: resolve the head commit of GitHub App repos in the git repo viewer `get_git_commit_hash` ran `git ls-remote` against the raw resource URL. A GitHub-App-backed repository stores a tokenless URL, so the probe failed with "could not read Username" and the viewer never got past its first step. Resolve the head over the GitHub REST API with a server-side installation token instead, reusing the lookup the auto-pull poller already uses for app repos. Non-app repositories keep the ls-remote path. Also picks up the EE-side allowlist fix that lets the clone hub script request an installation token. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 63c67e2a2db198af26a0334f5be14af7d9987eb1 This commit updates the EE repository reference after PR #732 was merged in windmill-ee-private. Previous ee-repo-ref: 2a260961fa0a9bb5631c17e2f718cb8efb4f9aa2 New ee-repo-ref: 63c67e2a2db198af26a0334f5be14af7d9987eb1 Automated by sync-ee-ref workflow. * fix: honour the app-repo head lookup's not-app-backed result `get_app_repo_head_for_autopull` documents `Ok(None)` as "this repo is not app-backed, use the ls-remote path", which is what the other two callers do. Fall through to `ls-remote` on `None` instead of turning it into a 500, and drop the handler's own `is_github_app` read now that the callee's answer is honoured. Also bumps ee-repo-ref to pick up route-safe ref handling in that lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: serve GitHub App repositories as an archive instead of a token The viewer's clone script asked the server for an installation token and put it in the clone URL. That token is installation-wide and carries the App's full permissions, so minting one requires a workspace admin, and the viewer was therefore admin-only for app-backed repositories. The server now streams a tarball of the commit instead, authorized by read access to the git_repository resource, so no GitHub credential reaches the job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: run delegate_to_git_repo playbooks from GitHub App repositories An Ansible job's runnable_path is the user's own script, which no entry in the git-sync script allowlist can match, so `delegate_to_git_repo` could never obtain a token for an app-backed repo. It also gave up entirely on agent workers, whose connection has no database to mint one from. A playbook run only reads a working tree: the clone is followed by one rev-parse for a log line, and nothing after that touches git. So take the same archive route the viewer uses, extracting the commit's tarball into the job's repository directory. No GitHub credential reaches the worker, and agent workers work because the route is HTTP. Archive entries are joined onto the target by hand so a crafted archive cannot write outside the job directory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: drop the now-immutable secret_url binding Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: point the repo viewer at the archive-based clone script hub/28905 reads app-backed repositories through the server's archive route instead of minting an installation token, which the backend in this release no longer grants it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: stream repository archives to disk rather than into memory The archive download went through `AuthedClient::get`, whose client caps a request at 20 seconds and whose response was then buffered whole. A repository is arbitrarily large, so that cut off slow downloads and put every job on the worker at risk of running the process out of memory. Add `get_streaming`, the read counterpart to the streaming upload path, and write the response out chunk by chunk. Extraction now creates each entry's parent directory: a tar carries directory entries only by convention, and the traversal guard now has tests, one of which caught the missing parent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: require admin to read an app-backed repository A `git_repository` resource names the repository rather than holding a credential for it, so read access to one authorizes nothing: anyone who can write a resource path can point one at any repository the GitHub App installation reaches, then read their own resource. The head lookup now requires admin for app-backed repos, matching the archive route and the repository picker, which already limits itself to workspaces where the caller is an admin. Repos that aren't app-backed are untouched and stay open to any reader. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: describe the repo viewer's hub script as it stands The file read as a patch waiting to be applied, against a hub version two releases stale. Describe what the published script does, including the archive route app-backed repositories now take. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: run the archive fetch under the job poller, off the job directory Three defects in the delegate path's fetch: The download and extraction ran outside the job poller that the git clone paths go through, so a cancelled or timed-out run kept streaming and extracting an arbitrarily large repository while holding the worker. There is no wall-clock bound on the download itself, by design, which is exactly why it needs the poller. The archive was written to a fixed name inside the job directory, where `create_file_resources` has already laid down the run's own files at paths the playbook chooses. A run naming a file `repo_archive.tar.gz` had it truncated and then deleted. It goes to a per-job temp path now. Link entries were unpacked with their target unchecked. `Entry::unpack` writes the link verbatim, so a link out of the tree plus a later entry descending through it writes wherever it points. Targets now face the same containment check as entry paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep repo symlinks, refuse only writes that go through them The link check rejected any target containing `..`, which is ordinary in a repository — `docs/x -> ../README.md` resolves inside the tree, and a git checkout keeps it. Rejecting it failed the whole extraction for repositories the clone path handles, and app-backed repos have no clone path to fall back to. Targets are preserved as git preserves them. What would let one escape is a later entry written at or underneath the link, so that is what is refused. Extraction also polls an abort flag now: a `spawn_blocking` task outlives the join handle its caller drops, so a cancelled job left it unpacking in the background. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: refuse hard links in a repository archive Leaving link targets verbatim is right for symlinks — git checks them out that way, and an escape needs a second entry descending through the link, which is refused. A hard link is not like that: unpacking one creates it against a target resolved there and then, so an escaping target is useful on its own. No git tree can express a hard link, so an archive carrying one did not come from a repository. Refuse it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 21f79bbbd39ae89665d1a89738630978616aa309 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: update ee-repo-ref to 37695a769b25d16b34107eedc1076793a8b388c8 This commit updates the EE repository reference after PR #737 was merged in windmill-ee-private. Previous ee-repo-ref: 21f79bbbd39ae89665d1a89738630978616aa309 New ee-repo-ref: 37695a769b25d16b34107eedc1076793a8b388c8 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
dad8fed647 |
fix: refuse an MCP endpoint call whose required request body is empty (#10771)
* fix: refuse an MCP endpoint call whose required request body is empty Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: state the required-body rationale once Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1f59841a67 |
feat: add WM_ROOT_WORKSPACE, the closest dev or prod workspace of a job (#10776)
* feat: add WM_ROOT_WORKSPACE, the closest dev or prod workspace of a job Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HBMJwogo6jJ1P55uvB3YpF * fix: do not cache a failed root-workspace lookup, and sweep on fork create Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HBMJwogo6jJ1P55uvB3YpF * fix: shorten the agent-worker root-workspace TTL and pin the sweep wiring Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HBMJwogo6jJ1P55uvB3YpF * chore: update ee-repo-ref to a2fa58e5301d3865dd06ad73519e20ba7a5af0f0 This commit updates the EE repository reference after PR #736 was merged in windmill-ee-private. Previous ee-repo-ref: 07a9d26a79a403ae27c48abd508a6699f2c87c49 New ee-repo-ref: a2fa58e5301d3865dd06ad73519e20ba7a5af0f0 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
05abf6d5aa |
feat(cli): deduplicate identical script lockfiles (dedupeLockfiles) (#10769)
* feat(cli): deduplicate identical script lockfiles into one per language * test: pin shared lockfile path classification * fix(cli): never delete a lockfile the dedup plan also writes * fix(cli): plan lockfile dedup from the whole tree, not the sync scope * fix(cli): keep dedup out of dry runs and stop hiding scripts from its scan * test: pin which files the shared-lock scan counts as readers * fix(cli): validate shared-lock refs and let the majority keep its file * fix(cli): snapshot shared-lock ownership before regeneration moves it * fix(cli): address dedup review nits (dry-run push, json shape, scan scope) * refactor(cli): put shared lockfiles in a top-level locks/ directory * fix(cli): claim only the shared lock names windmill writes, and only when on * fix(cli): read the lock field itself, and count only scripts sync reads * fix(cli): parse metadata by its real format and lint from the sync root * fix(cli): never re-hash a script whose generation failed * fix(cli): share sync's walk exclusions and fail closed on unreadable dirs * fix(cli): keep a lockfile the metadata on disk still references * fix(cli): decide a lock is unread from the metadata field sync reads * refactor(cli): name shared lockfiles after the dependency file they resolve * fix(cli): carry shared lockfiles a narrowed sync cannot speak for * fix(cli): move a shared lockfile when its dependency file moved, not on a head count * fix(cli): let the many correct a shared lockfile a lone variant planted * fix(cli): read why a lock differs from the stamp the worker writes into it * fix(cli): let an agreeing majority speak whatever the stamps say * docs(cli): count the disjuncts the comment introduces * fix(cli): keep a private lock for any script the worker locks differently * fix(cli): match annotations by the worker's own names, not by shape * fix(cli): recognize the py: interpreter pin the macro does not cover * fix(cli): let the map speak for dependency-file deletions * perf(cli): group lock entries without rebuilding the group per insert * fix(cli): defer shared-lock deletions until the metadata has settled * fix(cli): decide shared-lock readers by the lock field, failing closed * fix(cli): read folded lock refs and keep locks read by unparseable metadata * refactor(cli): one shared-lock reader scan, shared by the pull and push paths * fix(cli): keep nested dependency set names out of shared lockfiles * fix(cli): drop a shared-lock scan gate that no real repo took |
||
|
|
de90e44650 |
docs: document DATABASE_URL_FILE in the env var help (#10773)
Claude-Session: https://claude.ai/code/session_0125f1Vwj7pR9oY8NCxLXwtW Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
574775d50c |
fix: teach the AI the raw-app job bindings, the SDK reference and the draft/deployed split (#10754)
* feat: teach the AI the raw-app job bindings and the draft/deployed split Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope the raw-app deploy advice to the referenced item, and stop kind-conversion from stranding fields The draft/deployed guidance added in the previous commit was read as "deploy the app too": the agent asked for both the flow and the app and routed a one-item dependency through the review-and-deploy page. Only the referenced flow or script has to exist deployed — the preview runs the app's draft — so the prompts, the `write_app_runnable` warning and the testing rule now say to offer that one deploy and leave the app a draft. `buildPersistedRunnable` spread the existing runnable when rewriting it, so converting a path runnable to inline left `runType`/`path` behind (and the reverse left `inlineScript`). `isRunnableByName` matches the inline branch first, so an app "wired to a flow" silently ran stale inline code. `test_run_app_runnable` now fills ctx-bound inputs with `$ctx:<prop>` the way RawAppBackgroundRunner does, so a ctx argument no longer arrives missing. The SDK-reference rationale claimed WM_TOKEN may be unset, that a missing base URL falls back to localhost, and that a job token is scoped enough to 403 a hand-rolled REST call. None of the three is true, and it shipped to every write-script prompt; the text now only says the client configures itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review round on the raw-app AI instructions The eval case could pass on the exact answer it exists to reject. Every `requiredMentionsAnyOf` alternative but one was flow-agnostic, so "the app must be deployed" satisfied "must be deployed". All alternatives now name the flow, and a unit test pins that the app-only phrasing fails. `instanceLine` asserted "self-hosted Community Edition" outside the browser, where `isCloudHosted()` reads false and the license store is unset — so every global eval was told that regardless of what it pointed at. It is now emitted only under BROWSER. `assistantExpect.forbiddenMentions` defaulted a missing `assistantText` to "", which passes every entry forever on a mode whose runner does not report it. It now fails with that as the reason. `buildPersistedRunnable` carried `schema` across a retarget, so a path runnable pointed at a new flow kept the previous item's schema and `genWmillTs` typed `backend.<key>(args)` from the wrong inputs. It survives only while kind and path both match. The SDK header claimed "a function that is not listed below does not exist". `windmill-client` also exports the generated services, and the Python client exposes `Windmill.get`/`.post`, so an endpoint without a helper had no legal move. Each language now names its own escape hatch. `getAppInstructions` said the attached reference carries the TypeScript SDK even when `language: "python3"` had swapped in the Python one — on the very sentence telling the model to make that call. The kind-conversion comment claimed a hybrid runnable "silently runs stale inline code". It does not: `isRunnableByName`, `isRunnableByPath`, `convertPersistedToBackendRunnable` and `rawAppPolicy.processRunnable` all dispatch on `type` alone. The leftovers contradict the runnable's kind rather than override it, which is what the comment now says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address round-2 review nits on the raw-app AI instructions `flow is deployed` was satisfied both by "once the flow is deployed, the button works" and by a hallucinated "done — the flow is deployed", which eval mode makes impossible and the drafts-only judge cannot see. Every alternative now states an outstanding obligation, and two more real phrasings ("will need to be deployed") are accepted so a correct answer is not failed on wording. Condenses the three comment blocks that ran past the four-line limit in AGENTS.md, and drops two claims inside them that no longer hold: the `testRunAppRunnable` doc said it runs a runnable the way the app's own frontend does (it is the editor preview, which a deployed app's stored policy does not match), and `undeployedRunnableTargets` described its argument as the write tool's raw input when the call site passes the persisted runnable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: report the real cause when a test run fails, and label the app-runnable card Driving `test_run_app_runnable` in a live session surfaced two defects the API-level check could not see. `executeTestRun` built its failure message from `error.message`, which the generated client leaves as the bare status text while the server's message sits in `body`. A path runnable aimed at an undeployed flow reported "Not Found" instead of "Not found: flow not found at name u/admin/current_time" — dropping the one diagnostic the run exists to produce. `formatToolError`, in the same file and written for exactly this, now does it. This also applies to test_run_script and test_run_flow, which had the same loss. The completion card read "Flow test completed successfully" for an app runnable, because `contextName` doubles as the jobs-tray kind and a path runnable pointing at a flow really does queue a flow job. A `completionName` override now names what ran without changing the kind. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: pin the deploy expectation against wrong answers, not just correct ones `deploying the flow` was satisfied by "done deploying the flow" — a deploy the agent only claims to have made, which eval mode makes impossible and the drafts-only judge cannot see. Replaced with the prospective forms, and dropped the same reading from the workflow variant. Three review rounds each found this same class of hole in the phrasing list, so the list is now exercised against the wrong answers themselves rather than eyeballed: naming the app as what needs deploying, claiming the deploy is already done, claiming to have deployed the flow, and saying nothing about deploying all have to fail, while four real correct phrasings have to pass. The test reads the case out of global.yaml, so a future edit to the alternatives is checked by it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: drop the tense-neutral deploy alternatives and cover completed claims A gerund after a preposition carries no tense, so `before`/`after`/`by deploying the flow` all match a deploy the agent only claims to have made ("after deploying the flow, I clicked the button and it returns the greeting") just as the bare gerund did. All three are gone rather than swapped for whichever reads least badly, and the two completed-deploy phrasings are now negative fixtures. The remaining alternatives are imperative or obligational, which a claim of having already deployed cannot satisfy. Condenses the two comments this list carries: the YAML block to four lines, and the test's rationale to the durable constraint about substring matching. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: encrypt sensitive inputs when test-running an app runnable `test_run_app_runnable` sent `force_viewer_static_fields` but not `force_viewer_sensitive_inputs`, which every other preview path derives from the runnable's `sensitive` user fields. That list is the only thing driving the encryption loop in apps.rs, so testing a runnable with a sensitive input wrote the real value into the job's args in plaintext, readable by anyone with run access to the workspace. Verified against a running EE instance. With the list, `api_key` is stored as `$encrypted:mvqtSRI9…` and the sentinel appears nowhere in the job record; without it, the sentinel is readable in run details. A non-sensitive field is left plaintext either way. The tool claims parity with the editor preview, so it uses that same filter (`type == 'user' && sensitive`) and omits the field entirely when empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
ac27d0200d |
feat(sessions): batch edit, filters and grouping in the session sidebar (#10772)
* feat(sessions): batch edit, filters and grouping in the session sidebar
Session list management from the sidebar:
- Edit mode (Settings menu > Edit sessions) puts a checkbox on every row with
select-all, shift-click range selection, and batch Archive/Unarchive and
Delete. Batch delete never removes fork workspaces, matching the single
delete's default.
- Last activity filter (Any time / 7 / 30 / 90 days) hiding sessions untouched
for longer than the cutoff. Sessions had no activity timestamp, so
`Session.lastActivityAt` is stamped at the two write funnels (persistTouched,
markSessionSeen) and falls back to createdAt for older records.
- Group by None / Date (Today, Yesterday, Last 7 days, Last 30 days, Older) /
Workspace fork, the last giving one group per workspace with a hover "+" that
starts a session there and a badge for dev workspaces.
- All of it reachable from the collapsed rail's Filter submenu too.
The sidebar header is now a full-width New session button with the settings
menu beside it; the "AI sessions" title stays only in the collapsible sidebar
section, where it doubles as the fold toggle. Entering edit mode reuses that
row and the status-dot slot, so no row moves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: guard batch unarchive and keep new group sessions transient
* style: match session options cog to the new session button size
* style: keep the session options cog neutral regardless of filters
* style: use an ellipsis for the session list options button
* style: narrow the session list options menu to fit the sidebar
* Revert "style: narrow the session list options menu to fit the sidebar"
This reverts commit
|
||
|
|
2b4369d7cb |
fix: reject invalid AI agent tool names when the chat writes a flow (#10756)
* fix: reject invalid AI agent tool names when the chat writes a flow Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review nits on agent tool name validation Share one AI-agent walk between the providerless-agent and invalid-tool-name collectors, drop the unused validateToolName, and list every reserved id in the tool naming rules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: describe an agent tool's summary as the name the agent calls it by The OpenFlow schema described `AgentTool.summary` as a short description of the tool, which is the same schema the flow write tools hand the model, so it pulled against the naming rules. Narrow those rules to flowmodule tools, since websearch and mcp tool names are never regex-checked, and let `kind` take either vocabulary its callers resolve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: name-check only the agent tools whose summary the agent calls An mcp tool exposes the MCP server's own tool names and a websearch tool's summary is a plain label, so neither reaches the worker's name check. Both default to an empty summary in the editor, which the chat then refused to write back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f6645af77e |
fix: explain the 6-field cron format when a schedule is rejected (#10768)
* fix: explain the 6-field cron format when a schedule is rejected Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mUJd8ZRXkzbhmHkZryYoE * fix: phrase the cron hint as a prepend, not an equivalent schedule Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mUJd8ZRXkzbhmHkZryYoE * fix: withhold the cron example where v1 shifts the weekday Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mUJd8ZRXkzbhmHkZryYoE * fix: withhold the cron example for any restricted weekday on v1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mUJd8ZRXkzbhmHkZryYoE --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c2deea13b7 |
fix(security): a WM_TOKEN job token can never be a global superadmin (GHSA-hfh4-cx4h-3fcr) (#10124)
* fix(security): a WM_TOKEN job token can never be a global superadmin (GHSA-hfh4-cx4h-3fcr)
Privilege escalation: an app/flow/schedule/trigger execution policy's `on_behalf_of`
(which a `wm_deployers` member can set) could point at a superadmin email. The
resulting job `WM_TOKEN` then passed the email-based superadmin checks, granting
instance superadmin. `forbid_superadmin_job_token` only guarded ~15 of ~75 routes.
Fix at the token layer: a WM_TOKEN must never satisfy a superadmin gate,
regardless of whose email it runs as (sentinel OR a real superadmin).
- `ApiAuthed` gains a `job_id` field, stamped once in `AuthCache::get_opt_job_authed`
from the resolved token's job_id (correct even on cache hits).
- `require_super_admin(db, email)` -> `require_super_admin(db, &ApiAuthed)`, rejects
`authed.job_id.is_some()`. `require_super_admin_email` kept for the few internal
callers without an ApiAuthed.
- `is_super_admin_authed(db, &ApiAuthed)` for the boolean `is_super_admin_email`
authorization branches on request handlers (workspace deletion, fork drops,
dev-workspace attach/archive, object-storage SSRF exemption, custom dbname, EE GHES
+ connected repositories, ...). Migrate ~75 sites (OSS + EE).
- CUSTOM_INSTANCE_DB reads the *authenticated* job_id, not the caller-supplied
`?job_id` query param. Worker-tag check takes a precomputed job-aware `is_super_admin`
on the request path.
Execution-time on-behalf checks (scheduled/flow worker-tag, Cloud enqueue quota,
is_devops_email) are hardened in a follow-up — see
docs/followup-onbehalf-execution-privilege-hardening.md.
Regression tests: a superadmin-email WM_TOKEN is rejected on `require_super_admin`
routes, on `DELETE /workspaces/delete/{w}` (403, workspace preserved), and on the
CUSTOM_INSTANCE_DB lookup with no `?job_id` (401); real superadmin tokens still succeed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: cap devops role at workspace admin and reject reserved on_behalf_of identities
Extends the job-token cap with three pieces:
- `require_devops_role` takes `&ApiAuthed` and rejects job tokens.
`is_devops_email` is true for superadmin emails, so every worker-management,
instance-config and service-log route was reachable by the same superadmin
`WM_TOKEN` that `require_super_admin` already rejects.
- A `job_id` claim that does not parse as a uuid rejects the token rather than
resolving to `None`, which would clear the job provenance and uncap it. Applies
to the internal JWT and the external `jwt_ext_` path.
- Defense in depth at store time: `validate_on_behalf_of` refuses the reserved
internal sentinels as an `on_behalf_of` on apps/flows/scripts/schedules/triggers,
and app execution refuses a policy carrying one — covering already-persisted and
forked-app rows that predate the cap. Deploying on behalf of a real user,
including a real superadmin, stays allowed; the cap handles that at execution.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(mcp): preserve job-token provenance when minting the proxy JWT
The MCP endpoint-tool proxy re-mints a JWT from the caller's ApiAuthed to
forward the proxied request, but passed job_id: None. A job's WM_TOKEN is
capped at workspace admin (GHSA-hfh4-cx4h-3fcr); dropping the job_id here
re-minted an uncapped token that satisfies require_super_admin /
require_devops_role on the proxied route (e.g. listWorkers exposing worker
IPs, job/workspace IDs, and sensitive tags).
Carry api_authed.job_id into create_jwt_token. Adds an in-module regression
that decodes the forwarded JWT and asserts the job_id is preserved for a job
caller and absent for a non-job caller.
Reported by Codex CI review (P1) on #10124.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: cap the admin-or-devops gate at workspace admin for job tokens
require_admin_or_devops (the EE critical-alerts endpoints) grants when the
caller is a workspace admin OR an instance devops. is_devops_email is true
for superadmins, so a WM_TOKEN running on-behalf of a superadmin who is not a
member of the target workspace could clear the devops branch and read/ack that
workspace's critical alerts (GHSA-hfh4-cx4h-3fcr). This gate takes a bare
email, not an ApiAuthed, so the token-layer cap could not see it.
Thread the caller's job-token provenance and reject the devops branch for job
tokens, matching require_devops_role. The workspace-admin branch stays allowed
— that is the cap ceiling. Adds an enterprise-gated regression proving the
bypass is closed and a real superadmin token still clears the gate.
Found while auditing the PR for bare-email gates the choke-point cap misses.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: cap instance-global is_admin gates at workspace admin for job tokens
Three instance-global routes gate on the caller's own `is_admin` claim, which
`ApiAuthed.is_admin` carries into a WM_TOKEN (it is a workspace-admin claim,
true for superadmins too). A job token is capped at workspace admin
(GHSA-hfh4-cx4h-3fcr), so its is_admin claim must not authorize instance
actions on a route with no workspace binding:
- `unarchive_workspace` — unarchive an arbitrary workspace by id
- `prune_concurrency_group` — delete a global concurrency group
- `list_worker_groups` — return unobfuscated `env_vars_static` (may hold secrets)
Add job-token-aware `is_instance_admin` / `require_instance_admin` helpers (the
same shape as `require_super_admin` / `require_devops_role`) and use them at
these three sites. Workspace-scoped `require_admin(authed.is_admin, ...)` gates
are intentionally left unchanged — a workspace-admin job token is within the
cap there. Regression added covering all three; verified it lets a WM_TOKEN
unarchive/leak without the fix and is blocked with it.
Reported by Codex CI review (P1) on #10124.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(mcp): drop orphaned path_field_renames from EndpointTool test helper
The merge with main adopted main's mcp path-substitution refactor (#10162),
which removed the `path_field_renames` field from `EndpointTool` and its
consumer (`substitute_path_params` no longer takes per-field path renames).
main's `runner.rs` `ep` test helper still constructed the struct with
`path_field_renames: None`, so the workspace test build (cargo test --all,
which compiles windmill-mcp's own #[cfg(test)] module under the `server`
feature) failed with E0560. A plain `cargo check` does not compile that test
module, so it only surfaced in CI's cargo_test.
Remove the orphaned field to match the struct.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test: describe the sentinel-rejection policy the forged-identity test asserts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: complete ApiAuthed initializers in feature-gated tests after merge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: stop job tokens minting credentials that shed their provenance
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: cap the MCP OAuth approval mint at the same elevated-job-token gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: cap the self-service password reset at the elevated-job-token gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: cap app embed/SDK mints and scope widening at the elevated-job-token gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep job tokens from destroying the account they run on behalf of
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: deny job tokens a foreign-workspace admin claim and workspace ejection
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: keep the follow-up inventory in the PR instead of the repo
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: make the session workspace status gate job-token aware
session_workspace_status derived its superadmin branch from a bare email
check, so a job token carrying a superadmin identity resolved the existence
of workspaces it has no relationship with rather than seeing them as
deleted. Switch to is_super_admin_authed, matching every other instance
gate reached from a request ApiAuthed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* revert: leave the global concurrency-group listing on the plain admin gate
The listing exposes concurrency keys across workspaces, which is metadata
rather than a capability, and it 401s rather than degrading. Keep the guard
on the prune route next to it, which is the destructive one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the instance-admin gate on the global concurrency listing
The listing spans every workspace's concurrency keys, and the gate rejects
only job tokens: the !is_admin branch is the pre-existing check, so
workspaced tokens and interactive admins are unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to d30af67d38954f9012f7bad08da23e347344b4c6
This commit updates the EE repository reference after PR #664 was merged in windmill-ee-private.
Previous ee-repo-ref: 7870573dbc3360f99bada143f094c67dce0d9e9c
New ee-repo-ref: d30af67d38954f9012f7bad08da23e347344b4c6
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: hugocasa <hugo@casademont.ch>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
|
||
|
|
ed2ff6c5e7 |
fix: scope git-sync concurrency key per repository (#10767)
* [ee] fix: scope git-sync concurrency key per repository Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: dedupe git repo resource helper, fail loudly on callback timeout Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reserve the workspace prefix in the git-sync concurrency key cap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: cover the concurrency-key prefix reservation and the pull lane Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to dff61d6da80d15f8327af99d322c00cc91f784ff This commit updates the EE repository reference after PR #734 was merged in windmill-ee-private. Previous ee-repo-ref: e50a7eca7d7f8771979485f654831b15de59ec25 New ee-repo-ref: dff61d6da80d15f8327af99d322c00cc91f784ff Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
5fb145c79f |
feat: guided setup wizard for data tables on Cloud (#10584)
* feat(frontend): guided setup wizard for data tables
On Cloud a data table cannot use the Windmill instance database, so a new
workspace hit a dead end: an alert telling the user to go find a PostgreSQL
resource somewhere else. Setting one up meant three disconnected places, and the
connection could only be tested after the config had already been saved.
Adds a three-step wizard (choose a database -> set it up -> name it) reached from
the data tables settings page:
- Supabase: signs in via the existing supabase_wizard OAuth client and creates
the project from inside Windmill. Because db_pass is an input to project
creation, Windmill sets the password and the user never visits a dashboard.
- Your own database: picks an existing postgresql resource, or adds one with a
connection string through the form that already supports it.
- Windmill database: hands back to the inline row editor, since instance
databases are provisioned by a superadmin.
Verifying access is no longer a step the user takes: Continue runs the check and
passing it is what advances the wizard, so a database that cannot create tables
never reaches the workspace config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin ee-repo-ref to the Supabase provisioning endpoints
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): do not claim the database is ready when its check failed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review findings on the data table wizard
- The Supabase create branch advanced on `provisioning === 4` without consulting
the check it had just run, so a role that cannot create tables could reach
Finish. It now blocks and offers Try again.
- Retrying no longer mints a fresh secret variable + resource each time: the
credentials are only re-created when the password actually changed.
- The generated password is captured before the create call rather than after,
since a throw there can still leave a project behind.
- On a failed provision the project list is refreshed, so the just-created
project can be picked up from the other tab instead of provisioning a second.
- Finish refuses a name that already belongs to another data table, which
previously repointed it at the new database.
- Secrets go to the acting user's namespace instead of a literal `u/admin/`.
- The progress list no longer ticks "Created on Supabase" before the request is
sent, and does not claim the database is ready when its check failed.
- The wizard's resume state is cleared when it closes, so reopening after an
abandoned OAuth round trip is not stuck on step 2.
- The OAuth callback shares the session-storage key rather than repeating it.
- SupabaseConnect uses the shared provisioning helpers instead of a fork.
- Restores the doc comment displaced onto TestDataTableResourceQuery.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): simplify Alert layout and balance its vertical padding
The body was rendered by two near-duplicate branches, each wrapping the text in an
extra div only to hang a margin on it, and the margins disagreed: the collapsible
branch spaced above with mt-2, the static one below with mb-2. Since isCollapsed
defaults to true, every non-collapsible alert took the static branch, so titled
alerts read as 24px of space below the text against 16px above -- visibly
off-centre -- with the title and body flush against each other.
Collapse both branches into one and drop the margins; the container's own padding
now sets top and bottom equally, with a small gap under the title row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): only offer Supabase when its OAuth client is configured
The wizard offered the Supabase card unconditionally, so on an instance whose
superadmin never configured a supabase_wizard client -- or whose backend is built
without the oauth2 feature, which compiles the whole /api/oauth router out -- the
card dead-ended at a 404. Gate it on listOauthConnects, the same check
ApiConnectForm already makes, fetched on open so configuring the client mid-session
does not require a reload.
Also drop the Supabase project ref from the existing-project cards: it is an opaque
identifier that means nothing outside Supabase's own dashboard URLs. Show the region
instead, plus a status word when the project is not healthy, since a paused project
is the one case where the connection check fails for a reason unrelated to the
password.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): run the Supabase OAuth leg in a popup
A full-page redirect unmounts the wizard, so anything the user does on Supabase's
side -- signing in, confirming an email, browsing their dashboard -- leaves them
with nothing pointing back at Windmill, and the wizard had to park its state in
sessionStorage to survive the trip.
Open the connect endpoint in a popup instead. The modal stays on screen throughout
and the callback hands the token back through postMessage rather than navigating.
The parked-state path stays as the fallback for browsers that block the popup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): scope the connection check to the choice that produced it
A failed check stayed on screen when the user switched Supabase mode or picked a
different provider, so a fresh tab opened showing an error about a database it had
nothing to do with. Clear the report and the error on both switches; re-clicking the
tab already selected leaves an error the user is reading in place.
Also polish the Supabase step: project cards get the provider-card treatment (icon,
p-3, flex column) instead of a hand-rolled variant whose block layout left more
padding above the name than below; form labels settle on text-emphasis; and the
signup link sits under the primary button for anyone who does not have an account
yet.
Drop the "free" badge and the "Free on Supabase" line -- every option in the wizard
is free, so neither told the user anything -- and say what the Supabase card
actually does now that connecting an existing project is the default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(frontend): one setup checklist and one Supabase step for every host
The data table wizard, the instance database modal and the resource drawer each had
their own version of the same two interactions, and they had already begun to drift:
the wizard's Supabase resource shape was rebuilt by hand in the drawer, and the
instance checks rendered with no notion of a step being in flight.
SetupChecklist replaces LoggedWizardResult, whose only consumer was the instance
modal. It adds the running state that component lacked, so a list driven by an
endpoint that reports nothing until it returns still shows where it is. Both the
instance checks and the Supabase provisioning stages render through it.
SupabaseProjectStep owns picking or creating a project, and useSupabaseOauth owns
the popup leg. Each host keeps only what is genuinely its own: the wizard saves a
variable and resource then verifies the connection, the resource drawer fills in its
own form. Both trigger authorization themselves, so a host can offer it a screen
earlier than the step does.
The lists load behind a spinner because which mode to open on depends on whether the
account has projects; deciding that after rendering flipped the toggle under the user.
Adds a kitchen_sink playground for the checklist so the animation and every failure
position can be exercised without a backend, a superadmin, or a Supabase account.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): tidy the resource drawer around the Supabase entry point
Connect Supabase was a hand-styled anchor carrying Supabase's brand hex values
rather than a Button, and it sat in a row whose other controls had settled on
unifiedSize md. Making it a Button meant SupabaseIcon had to satisfy IconType, so it
now takes `size` (deriving height/width from it) alongside the string props its other
callers pass.
The manual resource form spaced every field 32px apart and WhitelistIp added another
16px of its own, which read as a gap rather than a rhythm. One gap of 16px, with the
form itself given a little more separation from the description above it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): stop Supabase resources coming up modified when first opened
Resource forms fill in every unset property from the schema as soon as they render,
so a postgresql resource saved without region, root_certificate_pem and use_iam_auth
was dirty -- and had saved a draft -- the first time anyone looked at it. Write them
with the rest of the value.
SupabaseConnect also rebuilt the resource shape by hand instead of using the shared
helper, which is how the pooler host format ended up in two places.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(backend): record where a data table came from and whether setup finished
edit_datatable_config replaces the whole datatables map and DataTable does not deny
unknown fields, so anything the request omits is dropped without a word. origin and
setup_incomplete would have been erased by any unrelated save;
preserve_unmanaged_datatable_fields carries them -- and migrations_enabled, which had
the same problem inline -- forward for entries that already exist, following renames.
setup_incomplete is what lets a row be recorded before the resource it points at
exists, so the wizard can write nothing until the user finishes. There is deliberately
no intermediate state: the setup runs entirely in the browser, so nothing server-side
could advance one.
datatable_health probes every data table at once for the settings page and skips the
incomplete ones, whose resource_path resolves to nothing yet. set_datatable_setup
patches a single entry instead of resending the map. test_datatable_connection_value
checks a connection the caller has not saved anywhere, which the wizard needs before
it has written a resource.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): make destructive default and subtle buttons read red
Both variants were neutral until the pointer arrived, then filled solid red: nothing
marked the button as destructive until you were already on it. They now carry red text
at rest, with a faded red border on default and a light red wash on hover, which is
what the legacy red border style in the same file had always done.
Three call sites passed color="red" alongside a design-system variant. getStyleClass
returns before colour is read for accent, accent-secondary, default and subtle, so the
delete-migration control, its modal confirm and the import-database button had all been
rendering neutral. They pass destructive now.
The dropdown variant strips the button's own border, and matched border-border-light
literally -- a class the destructive style no longer contains.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(frontend): rebuild data table setup around a read-only row
The wizard gathers intent over two steps, reviews it on a third and writes nothing
until Finish, so a billable Supabase project is created only once the user has seen
what will happen. runSetup is also the retry: every step probes for its own result
before doing anything, so running it again on a half-finished data table resumes
instead of duplicating. Its steps are keyed rather than dispatched on their titles,
where rewording one changed what it did.
The settings row stops being an editable form with a dirty/save cycle. It carries the
name, where the database came from, a health dot and two actions; everything rare
moved into the gear panel, which also offers Finish setup for a data table whose
wizard never completed. Manage is ExploreAssetButton, the control the ducklake list
already uses, and the row and panel both link out to the underlying resource.
supabaseResourceValue no longer assembles the pooler host from the region.
aws-0-<region>.pooler.supabase.com is wrong for any project Supabase allocated
elsewhere, so the host, user and port come from the pooler config endpoint.
Two data tables sharing one database also share _wm_migrations, which is probed
unqualified, so the review step warns when the database being connected is already
behind another data table.
SupabaseConnect is deleted. The resource drawer uses the shared project step
restricted to existing projects: creating one is a billed action and belongs in the
wizard, which has somewhere to report what it did. The kitchen_sink checklist
playground goes with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): fall back to a direct Supabase connection when the pooler cannot be read
Reading a project's Supavisor config needs the database_pooling_config_read scope, which
an instance's Supabase OAuth app may never have been granted. No retry recovers from
that, and the wizard treated it as fatal: the user was left with an error and no way to
finish connecting a project that was otherwise fine.
resolveSupabaseConnection replaces the bare pooler read everywhere it happened. Asking
for session pooling and failing now yields a direct connection plus the reason, which
supabaseResourceValue already knew how to write. Nothing about the fallback is silent --
direct is IPv6-only, which is the whole reason session pooling is the default -- so the
wizard warns on its review step and the resource drawer says so in its toast.
The row is recorded before credentials are saved, so an origin claiming session pooling
has to be corrected once a direct host is what gets written; the run patches it through
set_datatable_setup rather than leaving the panel to report a mode nothing uses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(frontend): open the database behind a data table, and say when it cannot write
Every database in the list now opens the surface that owns its credentials. A postgres
one opens its resource in the editor drawer; a Windmill instance one opens the instance
modal, which is where its setup checks, password rotation and drop already lived. Both
are reachable from the row and from the panel's provenance list, and the provider icon
moved inside the button so the whole thing is one target.
CustomInstanceDbWizardModal targeted #content unconditionally, which put it underneath
the panel drawer that now opens it. It takes a target, and the panel portals it to the
body.
The status column gains a third state. The probe reports privileges but nothing gated
the dot on them, so a data table whose role cannot create tables showed as Connected and
only failed when someone ran a migration. It reads "Limited permissions" instead, and
opens the panel on the report carrying the GRANTs that fix it -- the settings page has
already probed, so the panel takes that report rather than asking the user to run Test
connection over work already done. fullyPrivileged is exported from the report component
so the dot and the report cannot disagree about what counts as healthy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* revert(frontend): keep the data tables settings table as it was
The settings table and the setup wizard are two changes that only shared a file. Splitting
them makes each reviewable: this branch keeps the wizard, and the read-only row, gear
panel, health probe and clickable databases move to their own branch.
The rows go back to the editable form with its pickers and save footer, still opening the
wizard from Add a database. DataTableSettingsPanel, dataTableHealth and dataTableOrigin
had no other consumers and go with them; the connection report stays, because the wizard
shows it too.
DataTableSettingsType keeps `origin`: the wizard writes it, and the review step reads it
back to warn when two data tables would share one database and therefore one
_wm_migrations table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): confirm before dismissing the data table wizard mid-setup
Closing was guarded while a run was in flight and unguarded before one, which is backwards:
a run leaves a row to resume from, whereas a backdrop click on the review step threw away
the project, the pasted password and the folder with nothing to recover them from.
Backdrop, Escape and the close button now go through one path that asks first. It only asks
when there is something to lose -- no provider chosen yet, or a run that already produced a
result, closes immediately -- so the dialog does not become something to click through.
Continue in the background still leaves in one click; that exit was always the deliberate
one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): stop the wizard claiming the resource folder controls who can use a data table
"Who can use this database" was wrong. Every path that resolves a datatable:// reference --
both executors and the agent-worker endpoint -- reads the resource unchecked, by workspace
and name. A resource in u/admin is usable by everyone's scripts. The folder governs who can
see and edit the connection, and who can reference the resource directly in a SQL step;
neither is who can use the data table. The wizard was contradicting the tab's own
description two screens later.
The folder select and name field become one Path picker, the same one the resource,
variable and script forms use, so the review step reads as a resource path rather than a
permission choice. Its initialPath is snapshotted when the step opens: Path seeds itself
from it, and a live value fights the typing. Finish now also gates on Path's error, so a
taken or malformed path stops the run before it writes anything.
The button that opens all this says "Add a data table" -- the data table is what you get;
the database is a detail chosen along the way.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* revert(frontend): move the destructive button restyle out of the wizard PR
This reverts
|
||
|
|
539ca6a4fe |
unbreak the scratch-dir permission guards on macOS (#10766)
* fix(agents): unbreak the scratch-dir guards on macOS Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): fold case in the scratch-guard exclusion list Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): match the MCP cache roots exactly, not by prefix Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(agents): pin the MCP cache class on the fileops guard Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c7ec33cc9f |
feat: rework the resource type list in the add-resource drawer (#10757)
* feat: rework the add-resource drawer list The resource type picker in the "Add a resource" drawer showed 273 types as bordered chips in three columns, labelled by their raw type name with the description hidden in search text only. Rows now carry the product name, the type name, and its description, and the list is searchable and keyboard-drivable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address local review nits on the resource list - keep DOM focus on the highlighted row when arrow keys move it from a focused row, so Enter never activates a different row than the lit one - ignore the `mouseenter` the browser fires when rows scroll under a stationary pointer, which dragged the highlight back mid-navigation - namespace the OAuth rows' aiId: a provider is listed in both sections, and triggerableByAI keys a single map by id - seed the custom-type set from the names call, so the section survives the full resource-type list 403ing on a public app domain - drop resourceTypeLabel, whose last caller now uses the display name - read a leading acronym as letters when picking a/an ("an S3 resource") - test resourceTypeDisplayName directly Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: scroll the resource list on its own, and report search results - the drawer no longer scrolls: step 1 is a full-height column with the search field and the sync button fixed, and only the rows scrolling. This drops the sticky search bar and the scroll-margin the rows needed to clear it - searching shows a per-section count, hides the sections it empties, and states plainly when nothing matched at all - section spacing moved onto the column's gap, so a section a search empties takes its spacing with it instead of leaving a hole Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review-round nits on the resource list - one definition of "a search is active": a whitespace-only query kept the browse ordering but still ranked, dropped the database grouping and highlighted row 0 - "a NATS resource": the acronym rule reads initials as letters, which is wrong for an all-caps name said as a word - give the lightweight picker's wrapper a height, so the step-1 list fills it the way it fills the drawer Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: drop the article from the add-resource title Whether a label takes "a" or "an" follows how it is said, and the spelling does not carry that: "an S3" but "a NATS", "an MCP" but "a REST", "a URL" but "an hour". Three review rounds each found another name the rule got wrong, so the title now names the type without an article. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
38a6178154 | scripts for npm token regen | ||
|
|
a7637aca31 |
chore(main): release 1.792.2 (#10753)
* chore(main): release 1.792.2 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.792.2 |
||
|
|
fa7fbd348d |
fix(security): validate ansible git repository URLs before invoking git (#10759)
The Ansible executor passed the user-controlled git repository `url` (from playbook YAML or a `git_repository` resource) straight into `git clone`, `git ls-remote` and `git remote add` on the worker host. A URL that git parses as an option — e.g. `--upload-pack=<cmd>` — turns `git ls-remote <url> HEAD` into arbitrary command execution on the host, outside any job sandbox. Non-http transports (`ext::`, `file://`, local paths) similarly run programs or read host files. Add `validate_git_repo_url` in windmill-common: reject a leading `-`, reject remote-helper `::` syntax, and allow only the `http(s)`, `ssh`, `git` and scp-like `[user@]host:path` transports. Also reject a `branch`/`commit` that starts with `-`. Validation runs at every ansible entry point that spawns git, covering both the inline-YAML and resource-provided URL paths. CWE-88 (argument injection) / CWE-78. Reported by Nitin Gavhane. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ef8a8e821c |
fix: check direct-deployment lock and superadmin in the deploy preflight (#10748)
* fix: check direct-deployment lock and superadmin in the deploy preflight `checkDeployPermission` mirrors the server's `check_deploy_rules` so the deploy UI can disable an action with a reason instead of letting the click come back 403. It modelled only `RestrictDeployToDeployers`, leaving two terms out: - `DisableDirectDeployment` was never evaluated. In a workspace carrying only that rule the preflight allowed the deploy and the request 403'd. - The server bypasses on `ApiAuthed.is_admin`, which is `usr.is_admin || super_admin`, while `whoami` reports the two separately. A superadmin who is a plain member of the workspace was refused a deploy the server allows. Evaluate `DisableDirectDeployment` first, as the server does, so the same message wins when both rules block, and add the superadmin term to the shared ruleset bypass helper. `wm_deployers` membership is an implicit pass on `RestrictDeployToDeployers` alone, so it no longer short-circuits the rules fetch the way admin does — a deployer is still bound by a direct-deployment lock, and a test pins that. The operator refusal stays above the admin/superadmin short-circuit: the server refuses operators in the item handlers whatever their global role, so a superadmin who is an operator in the workspace is still refused. Its doc no longer presents that term as part of the `check_deploy_rules` mirror, since the rule carries no operator term and refusing every kind here is deliberately stricter than the server. Callers no longer name which rules the preflight covers. That list rots at every site that repeats it, so it lives only at the preflight itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: apply the direct-deployment refusal only to the kinds the server gates `check_deploy_rules` runs from the item handlers, and only scripts, flows, apps, resources, resource types, variables and folders reach it. Schedules and triggers hit no gate at all: in a `DisableDirectDeployment` workspace the server returns 200 for a schedule and 403 for a script. The preflight answers per workspace, and that one answer disabled the deploy action for every kind, so adding the direct-deployment term would have blocked schedule and trigger deploys the server accepts. Tag each refusal with the term that produced it and let callers narrow a direct-deployment refusal to the kinds the server actually gates; a selection still blocks as soon as one gated kind is in it. The deployers-only term keeps applying to every kind. It over-reaches the same way, but narrowing it would loosen the UI beyond mirroring the new rule, so it stays as it is and no existing behaviour changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: mirror the superadmin bypass in the per-item deploy checks too `checkPathWritePermission` and `canPreserveOnBehalfOf` still tested `is_admin` alone. The server reads the merged `ApiAuthed.is_admin` in both places — `is_owner` for path ownership and `can_preserve_on_behalf_of` for the deploy identity — so a superadmin who is a plain member was refused a write the server accepts: creating a script in a folder owned by someone else returns 201 for them. Also drop the rule enumeration from the session deploy guard's comment, which named the operator and deployer rules for a preflight that now covers the direct-deployment lock and answers per kind. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the deploy refusal on an empty selection and match the advice to the fork lock * fix: mirror the superadmin bypass in the compare page's on-behalf-of gate * docs: name the variable that tracks the deploy direction --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0cba084a2e |
test(agents): make the tree-root rows follow the checkout kind (#10749)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f34b7fbcfa |
fix: make the listScripts parent_hash filter valid SQL (#10752)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9f517d5a40 |
chore(main): release 1.792.1 (#10750)
* chore(main): release 1.792.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.792.1 |
||
|
|
494e6f146e |
fix: route legacy AI entry points to sessions instead of the unmounted chat (#10705)
* fix: route legacy AI entry points to sessions instead of the unmounted chat Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep createSession's workspace choice and revert pipeline hand-off Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: guard in-session step generation and restore AI action labels Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep AI Fix usable in-session and stop silent no-op hand-offs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: neutral AI form assistant heading to match both branches Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the AI form assistant branch rationale once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: auto-send AI hand-offs and keep in-session step generation in global mode * fix: name the AI session in the entry point labels * fix: claim auto-send reactively and queue programmatic sends mid-turn * test: pin the auto-send claim going stale * fix: stop the script drawer hand-off from abandoning its unsaved script * fix: keep a stale hand-off prompt and close the pre-loading send window * fix: only blank the composer for an intent this wrapper can claim * fix: report composer edits only, never the mount-time draft --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> |
||
|
|
8efede55d6 |
chore(main): release 1.792.0 (#10745)
* chore(main): release 1.792.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.792.0 |
||
|
|
d40a446868 |
style: use subtle Button for raw app preview toolbar actions (#10747)
* style: use subtle Button for raw app preview toolbar actions * fix: expose inspector toggle pressed state via aria-pressed |
||
|
|
ef4dc46d4b |
fix(cli): keep script settings on push and repair the up-to-date check (#10741)
* fix(cli): keep script retention, debounce and cache settings on push Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(cli): surface the create response when the fixture fails Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(cli): drop debounce settings the CI build refuses to accept Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * repair the script push up-to-date comparison (#10743) * test: settle the backlog before the capped audit-export drain (#10737) * test: settle the backlog before the capped audit-export drain * chore: update ee-repo-ref to bd4de74eb37b32a2b6c7c69f6dedac031ef8436b This commit updates the EE repository reference after PR #730 was merged in windmill-ee-private. Previous ee-repo-ref: b5a5f9114df26088cfe976d91f10e55ba8bfcaa6 New ee-repo-ref: bd4de74eb37b32a2b6c7c69f6dedac031ef8436b Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * fix(cli): repair the script push up-to-date comparison Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(cli): drain dependency jobs and pin a non-1 priority skip Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(cli): describe the priority fixture without the old comparison Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(cli): read cache_ignore_s3_path off the typed response Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): stop redeploying bunnative scripts on every push Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
7b17e358b3 |
feat(frontend): record the outcome of every AI chat tool call (#10746)
* feat(frontend): record the outcome of every AI chat tool call The `ai_chat`/`tool` counter fired before execution, so nothing recorded whether a tool call succeeded, and the three paths that refuse a call before it runs recorded nothing at all. Log once per call on whichever path ends it, keyed `<tool_name>:<status>` over ok, error, declined, rejected and blocked_plan_mode. Per-tool totals now need `split_part(key, ':', 1)` downstream; rows keyed by the bare tool name coexist for up to 60 days. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(frontend): state what the tool-call telemetry statuses do not cover `ok` means the tool function resolved, which includes tools that report failure by returning an error string, and a call abandoned mid-execution logs nothing. Also pin that a hallucinated tool name reaches telemetry nowhere. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6749015fbf |
fix: audit the icon library against brand guidelines (#10722)
* feat: audit the icon library against brand guidelines
Every icon component checked against its brand's own published guidelines for
correct artwork, current colours, and readability on both app surfaces.
- 127 marks now carry a per-theme pair (text-[#light] dark:text-[#dark]), applied
only where the brand publishes a reversed or dark variant. twMerge where the
component exposes a class prop, so callers can still pass sizing.
- 296 of 304 brand icons record their source in a comment above the <svg>,
including the rule where the brand imposes one (Google forbids recolouring,
Cal.com is deliberately greyscale, Oracle reserves the MySQL dolphin).
- BRAND_COLORS.md is generated from the components, so the table cannot drift
from the code.
- Marks that were unreadable on a surface: 13 -> 1 on dark, 9 -> 4 on light.
The remainder are blocked by trademark terms, not unfixed.
- Wrong artwork replaced where a first-party or CC0 source existed: PayPal is
the real three-colour monogram, Stripe is the bare S rather than an app tile,
gcloud resolves to Google's mark instead of a generic hexagon.
- Concept icons (CACertificate, DbIcon, Webdav, Asset*, Bcrypt) inherit
currentColor instead of hardcoding a colour.
Fixes a cross-component CSS bug: ten icons embedded <style> inside their <svg>.
Svelte only scopes a component's top-level style block, so those were injected as
document-global rules under names like .st0 and .cls-2, which four icons each
defined differently. WindmillIcon renders from the root logged-in layout, putting
.st0 { fill:#ffffff } on every page. Class names are now namespaced per icon.
Adds /kitchen_sink/icons, a gallery rendering every icon on both surfaces at once.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: render brand icons in the surrounding text colour in control strips
A trigger picker mixing lucide glyphs (Webhook, Route, Database) with brand marks
(Kafka, GCP, AWS) read as two sets of controls once the marks became coloured.
Adds an .icon-mono utility that redirects descendant fills to currentColor, applied
by the container rather than passed to the icon. That is what makes it work on every
icon: GoogleCloudIcon has four hardcoded fills, no currentColor and no class prop, so
nothing passed to it could change its colour, and gradient-based marks cannot express
a monochrome variant at all without being redrawn.
- ToggleButton takes a monochromeIcon prop, opt-in since it is used app-wide.
- TriggersBadge, SidebarContent and QuickMenuItem (which backs GlobalSearchModal)
apply it unconditionally: these are uniform lists where one coloured entry among
grey ones reads as an error.
- DropdownV2 gains menuClass, because it portals its menu and a wrapper around the
component cannot style it. CaptureButton passes icon-mono through it.
!important is required because a handful of icons paint through style="fill:…", which
no selector outranks. Stroke is redirected only where one is declared, so shapes
carrying stroke="none" do not sprout outlines. Wrappers use display:contents, so no
layout box is added.
RowIcon is deliberately untouched — table rows keep showing brand colour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: close out the icon provenance gaps
Sources the 8 icons that had none and settles the 54 records whose author rated
itself below "high" and which no verifier ever reached — the earlier run's
verification stage was killed by a session limit.
46 confirmed as already correct, 11 citations corrected, 3 colours corrected.
Two changes were refuted and reverted by the adversarial pass:
- Mysql: the comment had the colour-to-shape mapping inverted. Rasterising the
first-party asset shows #00758F paints the dolphin and "My" while #F29111 paints
"SQL", not the reverse. The mark renders monochrome here, so nothing on screen
was ever wrong — only the note. Also rescoped the trademark sentence to what the
page literally says.
- AdobeAcrobatSignIcon: a "corrected" citation was rejected on evidence. The agent
claimed the original URL 404s; three fetches returned HTTP 200 with a genuine
Adobe SVG whose stylesheet is .a{fill:#584ccc}. Reverted to the original comment,
which also resolves the one unverified colour change on this branch — #584CCC is
current and first-party confirmed.
AmqpIcon is deliberately left with no brand colour: AMQP is an OASIS protocol, not
a vendor, and amqp.org publishes no palette.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: add icons for 11 resource types that had none
19 hub resource types fell back to a generic Boxes glyph. One agent per brand went
looking for a square vector logomark from a first-party source, with an adversarial
check on everything it produced; 11 landed and 8 correctly came back empty.
Added: beamer, campayn, codat, comapeo_server, klaviyo, matteroom, mollie, motimate,
paychex, terra, vectara. Each records its source, and the components follow the
library's conventions — no <style> block (Svelte does not scope those, which is what
made .st0 leak document-wide), gradient ids prefixed with the component name.
The other 8 keep the fallback, which is the right outcome rather than a failure:
- actimo, adrapid, aero_workflow, matteroom-adjacent niche products publish their mark
only as raster. Upscaled PNGs would look soft beside 300+ vector marks.
- gfw redirects to Global Nature Watch and publishes a wordmark, not a mark.
- leonardoai, localcontexts, weatherapi, webscrapingai serve nothing usable.
No hand-tracing: approximating a mark from a screenshot is invention, not sourcing,
and a wrong logo is worse than the tidy fallback glyph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: lettermark fallback for reserved marks, and fix the resources table rows
Icons
- Brands that reserve their logo for licensees no longer ship it. BrandLetterIcon draws
the initial in the brand's own colour instead: recognisable, not their mark, and not
invented artwork. Adobe Acrobat Sign and MySQL use it, plus the eight resource types
whose brands publish no vector mark at all.
Adobe: "does not allow the use of its product icons by third parties in their products
or related materials of any kind, except through an Adobe partnership agreement".
On dark the letter inverts to a filled rounded square, because a mid-tone brand colour
chosen to read on white goes dim as a foreground on #2e3441. Where white-on-tile is
also dim, the tile takes a near-black letter instead; light-mode letters are darkened
along their own hue until they clear 3:1. Every pair was measured, not eyeballed.
- Google Docs was drawing a generic monochrome document glyph while carrying a comment
claiming Google's colours. Replaced with Google's own 192px product icon.
- Azure was drawn monochrome, justified by a comment citing Microsoft's rule against
distorting the mark — which drawing it monochrome is. Replaced with Microsoft's own
logo_azure.svg. Their terms say to use the icons "as they would appear within Azure";
permitted use is diagrams, training and documentation, which is recorded in the file.
- Adobe Acrobat Sign's artwork was a geometric "A" plus a squiggle, not Adobe's ribbon
swirl. Moot now that it is a lettermark, but the mark was wrong.
- Gradient, mask and clip ids in the new artwork are namespaced per icon; ids are
document-global and collide the same way the .st0 class names did.
Resources tables
- Description cells are a fixed two lines: min-h floors short ones, line-clamp ceilings
long ones, so every row is the same height. Full text on hover via title.
- Widened to 30rem (84 chars/line) and vertically centred. The clamp needs
display:-webkit-box, which stacks lines from the top, so the span sits in a
flex items-center wrapper rather than carrying the height itself.
- w-full min-w-0 max-w-[30rem] instead of a fixed w-96, so a narrow viewport shrinks the
column and truncates rather than forcing the page to scroll sideways.
- The actions column loses its border-l separator and right-aligns the "Shared globally"
badge, matching the rows that show buttons.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: icon-mono filled lucide outlines and missed currentColor brand marks
Two bugs in the monochrome utility, both from the fill rule being too blunt.
- Lucide icons are outlines: fill="none" with stroke="currentColor" and no fills on
their children. Forcing fill on every descendant overrode that none and turned each
glyph into a solid blob. The filled case is now scoped to svgs that do not declare
fill="none", and svgs that do only get children redirected if they declare a real
fill of their own — so a brand mark drawn as an outline still works.
- Brand marks that paint with currentColor carry their own text-[#hex] class, so
redirecting fills left them branded: MQTT stayed #660066, NATS #375C93. The svg now
inherits the container's colour, which is what actually makes them monochrome.
Also wires the sidebar's trigger section, which was never covered: those links render
through MenuLink, not the sub-item block that had the class.
Verified in the browser across all five shapes an icon can take — lucide outline,
hardcoded fill, currentColor plus brand class, outline root with filled children, and
inline style="fill:#..". Lucide keeps fill:none and a grey stroke; the rest follow the
container.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: dedicated monochrome trigger icons instead of a CSS override
Reverts the trigger surfaces to the icons that were there before the brand-colour
audit, as ./icons/triggers/ variants. A trigger picker lists brand marks beside lucide
glyphs (Webhook, Route, Database), so a coloured mark reads as a different kind of
thing rather than a peer.
Ten variants, restored from main where they were already monochrome: Kafka, NATS, MQTT,
AMQP, AWS, Azure, Nextcloud, Google, GitHub. Google Cloud is the exception — main's copy
is a greyscale rendition rather than currentColor, so it is rebuilt from the current
four-colour artwork with the fills dropped.
Separate files rather than the CSS override that was there, because coercion cannot work
in general: forcing fills to currentColor breaks lucide's outline icons, which are
fill="none" with a stroke, and marks that set their own text-[#hex] class ignore a fill
rule entirely. Both bugs were live. The .icon-mono utility, ToggleButton's monochromeIcon
prop and the DropdownV2 menuClass pass-through are gone with it.
index.ts documents which folder to use where: ./triggers/ for trigger surfaces, the
full-colour mark for the resource picker, AppConnect and docs, and keep the two in sync.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: trigger pages and global search still used the colour brand marks
The ToggleButtonGroup on each trigger page pairs a brand icon with a lucide Code
glyph, so GCP Pub/Sub rendered Google's four-colour mark next to a monochrome one.
Kafka, NATS, MQTT and the rest had the same wiring; they were just less obvious
because their marks are near-monochrome already.
Repoints all seven trigger pages and the global search nav entries at the
./icons/triggers/ variants. RowIcon is left on the full-colour marks: table rows
show brand colour by design.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: restore the greyscale GCP trigger icon, and show variants in the gallery
The trigger variant had been flattened to currentColor, which collapses Google's cloud
into one flat silhouette and loses the tonal steps that give it shape. The pre-audit
icon was greyscale, not monochrome — #B0B0B0 / #D0D0D0 / #E0E0E0 / #FFFFFF — so it is
restored verbatim from main.
Also globs icons/**/*.svelte in /kitchen_sink/icons so trigger variants render next to
the full-colour marks they shadow, labelled by folder. Comparing the two is the thing
this page was missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: flow trigger dropdown rows use the desaturated marks too
The flow-graph badge menu still rendered the full-colour brand marks next to
lucide glyphs. Route both dropdowns through triggerIconMapMono: the badge
itself keeps the colour mark, only the rows it opens change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: rank resource-type search results by best match
Searching the description is what makes `gdrive` findable as "google", but it
also means "google" matches a dozen types that only mention the product in
passing. Rank a match on the type's own name above any description match, and
break ties on where the match starts, so `googleai` leads and a description
opening with "Google OAuth token..." beats one mentioning Google halfway
through.
Applied to all three resource-type searches: the Resource Types tab (whose bare
term also only searched the name until now), the add-resource drawer, and the
schema-narrowing picker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: trigger pages and global search show the full-colour marks
The desaturated variants belong to the two dense lists that sit beside lucide
glyphs -- the sidebar trigger list and the capture dropdown. Everywhere else a
brand mark stands on its own and should be the real one: the per-kind trigger
pages, the command palette, the capture table and the chat tool cards. Records
the rule in icons/index.ts so the next caller picks the right folder.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review round on the icon and resource-type work
- AppConnectInner went back to listResourceTypeNames for the list: /resources/type/list
is not on the public app domain's route allow-list, so a published app's resource
picker 403'd and, because the throw left connectsManual unset, stayed empty on every
retry. Descriptions now load best-effort behind it.
- Dropped DropdownV2's menuClass: nothing passes it; the flow-graph badge menu styles
melt's Menu, which has its own.
- icons/index.ts named two surfaces for the desaturated variants; there are four, and
the flow-graph badge and the menu it opens differ. Dropped the stale GCloudIcon note.
- GoogleCloudIcon takes width/height again: generic call sites resolve it through
APP_TO_ICON_COMPONENT and pass no size, so gcloud rendered at 16px after the remap.
- The path explainer is one ResourcePathHint component instead of the same copy twice.
- BRAND_COLORS.md recorded Ansible, Datadog, Deno, DeepL and Toggl as fixed; each
publishes a second artwork swapped in by class, so their dark hex and ratio were
wrong. Header no longer claims a generator that isn't in the repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: drop the duplicate gcloud icon and unblock the connect list
GCloudIcon.svelte was rewritten into the same four-colour mark as
GoogleCloudIcon.svelte and nothing pointed at it any more, so it was two files
drawing one logo waiting to drift apart.
The description fetch also sat on the critical path: the "Others" list showed
skeletons until a request for every type's full schema returned -- one that a
published app is guaranteed to get a 403 on. It now runs unawaited, and search
re-ranks when the descriptions land.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: AwsIcon and GoogleIcon take size again
The audit narrowed both to width/height with a 24px default, but every dynamic
call site passes size — RowIcon, the flow trigger badges, ToggleButton, global
search, the chat tool cards, the native-trigger page — so the SQS and Google
marks rendered at 24px wherever a smaller size was asked for. Both take size
again, keep width/height for the call sites that use those, and accept a class
so RowIcon's grey still applies.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: row-strip brand marks keep their colour
RowIcon greyed five of its seven brand marks with text-gray-400 while gcp and
azure rendered in colour. Now that AwsIcon accepts a class, the grey took its
wordmark but not its hardcoded #FF9900 smile, so the SQS row came out half
grey, half orange.
The rule this branch settled on is that only the four trigger menus desaturate;
a table is not one of them. Dropping the class from all five makes the strip
agree with the gcp and azure rows beside them, and with the lucide glyphs
staying grey since they carry no brand colour to keep.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
8492b4b4ba |
chore: prove scratch file ops per command segment (#10744)
* fix(agents): prove scratch file ops per command segment Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: describe the checkout root in the scratch guidance Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): close two auto-allow holes in the scratch guards Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): keep redirects and chained writes off the allow path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): never prove a command carrying a substitution or relative cd Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): treat sibling checkouts as separate roots Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): prove where a directory-form cp or mv actually lands Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): leave directory-form cp and mv unproved Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state the one-write-per-line rule in the scratch guidance Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: prefer Edit/Write over shell edits in agent guidance Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(agents): stop a failed cd from hiding the directory form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(agents): state the glob and cd rationale once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6783a396b1 |
fix(api): document cache_ignore_s3_path on the Script read schema (#10742)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1fa3bf3b29 |
fix: show runtime-detected assets in a run's Assets tab (#10738)
* fix: show runtime-detected assets in a run's Assets tab * fix: address review nits on run assets tab * fix: cap the run assets list and report when it is cut * fix: cap run assets by asset, not by row |
||
|
|
5d7881beb8 |
test: settle the backlog before the capped audit-export drain (#10737)
* test: settle the backlog before the capped audit-export drain * chore: update ee-repo-ref to bd4de74eb37b32a2b6c7c69f6dedac031ef8436b This commit updates the EE repository reference after PR #730 was merged in windmill-ee-private. Previous ee-repo-ref: b5a5f9114df26088cfe976d91f10e55ba8bfcaa6 New ee-repo-ref: bd4de74eb37b32a2b6c7c69f6dedac031ef8436b Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
ce71756c89 |
chore(main): release 1.791.0 (#10718)
* chore(main): release 1.791.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.791.0 |
||
|
|
343ce6e143 |
fix: derive a raw app's policy on deploy, and default an omitted execution_mode (#10733)
* fix: default an omitted app policy execution_mode to publisher Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: drop stale comments claiming execution_mode is required Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: derive a raw app's policy on deploy instead of trusting the caller's Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: pin the ee ref to the companion branch Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: vendor the raw-app policy derivation into the bundle job Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: note the vendored raw-app policy bundle Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: derive the policy on a value-only raw-source update too Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reject raw-app runnables whose shape yields an unusable grant Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: cache the new policy query and tighten raw-app runnable validation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: let the policy bundle drift guard survive a CRLF checkout Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 23431f5cf1d627051ded89111bbf2e301e9db456 This commit updates the EE repository reference after PR #729 was merged in windmill-ee-private. Previous ee-repo-ref: 0bdf8818fa115ad6b0d14f3117a18e8a580cce4d New ee-repo-ref: 23431f5cf1d627051ded89111bbf2e301e9db456 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
5972a1ca06 |
chore(cli): pick up shared-utils 1.0.13 (#10736)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
39f0542b2f |
refactor(frontend): keep the shared-utils bundle free of UI code (#10735)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f4f2dd5ece |
chore(frontend): unbreak the shared-utils library build (#10734)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b17fdab8ff |
fix: compile resource types with no properties instead of throwing (#10730)
* fix: compile resource types with no properties instead of throwing * fix: keep property-less resource types in the editor RT namespace |
||
|
|
6b5b9f72d4 |
fix: type s3-streamed columns that are all-null in the inference sample (#10728)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fd9295a58e |
feat(copilot): let plan mode draw, but never write the plan (#10725)
* fix(copilot): validate the version an approval stamps * feat(copilot): let plan mode write artifacts, but never the plan * feat(copilot): tell plan mode it may keep notes, not rewrite the plan |
||
|
|
66bffaa60d |
feat: add empty state cards to list pages (#10726)
* feat: add empty state cards to list pages
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: animate trigger drawers on first open
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: distinguish filtered-empty schedules, reuse the rAF helper
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: hide the header create button while the empty state offers it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Revert "fix: hide the header create button while the empty state offers it"
This reverts commit
|
||
|
|
05eba6c9ab | fix: include delete_after_secs in script deploy payload (#10731) | ||
|
|
66e3790da4 |
docs: announce we are not seeking outside contribution (#10724)
* docs: announce we are not seeking outside contribution Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: point big ideas at the feature request template Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab3c0206d7 |
fix: support @typechecked decorator in Python relative imports (#8495)
WindmillFinder's ModuleSpec lacked origin, so __file__ was never set on loaded modules. inspect.getfile() then raised "is a built-in module", breaking typeguard's @typechecked and anything else that introspects module source. Use spec_from_file_location() which sets origin correctly. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: hugocasa <hugo@casademont.ch> |
||
|
|
010d67e07f |
chore(main): release 1.790.1 (#10712)
* chore(main): release 1.790.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.790.1 |
||
|
|
64d78b4db1 |
fix: fall back to polling when a proxy mutes the job SSE stream (#10716)
* fix: fall back to polling when a proxy mutes the job SSE stream * fix: do not charge deliberate no-logs sse restarts to the retry budget |
||
|
|
529e960629 |
perf: cap resource content sent to the search modal (#10714)
* perf: cap resource content sent to the search modal Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: address review — fence the LATERAL, flag partial search, add cap test Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: pluralize the truncation notice and link the cap to its openapi doc Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0258f3f81b |
perf: unblock workers before the API router is built (#10711)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
944ad1083a |
chore(main): release 1.790.0 (#10699)
* chore(main): release 1.790.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.790.0 |