Commit Graph
14568 Commits
Author SHA1 Message Date
Diego Imbert eb95a95f48 feat(datatables): a permissions drawer on a table 2026-08-31 06:52:03 +02:00
Diego Imbert a8a8812839 feat(datatables): group grants that repeat over objects into one row 2026-08-31 06:45:37 +02:00
Diego Imbert 7b94a0fb4d feat(datatables): rename a schema from its row menu 2026-08-31 06:20:13 +02:00
Diego Imbert e989a4d9ae fix(datatables): report create privileges for the role the manager is on 2026-08-31 06:01:13 +02:00
Diego Imbert 6e76edb279 fix(datatables): tell the caller when roles have been saved 2026-08-31 06:01:13 +02:00
Diego Imbert 19035031b7 fix(datatables): stop offering to grant CONNECT on a database
Every role a data table creates is granted it at creation, so the statement
could only restate what is already true. The backend still accepts it, so a
CONNECT grant already in the catalog stays revocable.
2026-08-31 06:00:11 +02:00
Diego Imbert baaf7632a3 fix(datatables): hide schemas the connection cannot enter from the tree
The right join is what keeps an empty schema in the list, and it reads
pg_namespace, which every role can read in full — so a schema the role has no
privilege on came back too, as an empty one, since information_schema does
filter. An empty schema it can enter still shows.
2026-08-31 06:00:11 +02:00
Diego Imbert fc3732fb7f fix(datatables): repair an instance database's grant options where they are used
Handing a privilege to a role means granting it, and a privilege held without
the grant option cannot be passed on — Postgres answers such a statement with
a warning and no effect, so the roles silently receive nothing. Databases
provisioned before those options were part of the instance grants still hold
them plain.

The repair now runs on the two paths that need it, saving permissions and
applying an ACL change, rather than only on opting in to migrations: a data
table can use roles without ever touching a migration.
2026-08-31 05:55:50 +02:00
Diego Imbert 38023de582 feat(datatables): only offer create where the role may create 2026-08-31 05:52:42 +02:00
Diego Imbert 422cc98130 nit 2026-08-31 05:36:23 +02:00
Diego Imbert e1556004f6 feat(datatables): grant database privileges from the roles drawer 2026-08-31 05:19:51 +02:00
Diego Imbert 9ebca89e28 feat(datatables): badge the data table the manager is connected to
The tree lists every data table; only one of them answers the queries.
2026-08-31 05:17:23 +02:00
Diego Imbert 1eb314e8cb feat(datatables): fold removing a data table into its row menu
The menu takes the place the close button held at the end of the row, and
migrations and permissions grey out while the row has unsaved edits — they
act on the saved data table, which an unsaved one is not.
2026-08-31 05:15:16 +02:00
Diego Imbert 395eac68c7 fix(datatables): give custom_instance_user the grant option on instance databases 2026-08-31 05:07:01 +02:00
Diego Imbert ce89023cef fix(datatables): settle the role from the data table being switched to 2026-08-31 04:58:39 +02:00
Diego Imbert 924389a0ac fix(datatables): test the connection as the data table's own admin role 2026-08-31 04:39:56 +02:00
Diego Imbert 1244d8015d refactor(datatables): move migrations and permissions into the row menu 2026-08-31 04:39:56 +02:00
Diego Imbert 8429d909d4 fix(datatables): accept postgres 17's MAINTAIN table privilege 2026-08-31 04:33:22 +02:00
Diego Imbert bac89b0790 fix(datatables): allow deleting a migration that has already been applied 2026-08-31 04:33:22 +02:00
Diego Imbert 7effde8bbe fix(datatables): hide schemas the connection's role cannot reach 2026-08-31 04:33:22 +02:00
Diego Imbert 163ff3aad6 feat(datatables): drop a schema from its row menu 2026-08-31 04:15:21 +02:00
Diego Imbert 126204a11e fix(datatables): resolve pg_database_owner to the database's real owner 2026-08-31 04:15:21 +02:00
Diego Imbert 6774ddc81b nit 2026-08-31 04:03:42 +02:00
Diego Imbert 803e89319e feat(datatables): own and grant on a data table's schemas
A permissions drawer on a schema row: who owns it, and what each role may do
in it. Ownership moves the schema and everything already in it, and writes
the default privileges that keep the owner in reach of what the other roles
create later — an object belongs to whoever creates it, so that is the only
way to cover what does not exist yet.

Grants are built as statements — privileges, scope, role — and read back from
the catalog, per object and including default privileges, so what the page
shows is what the database has. Every change is confirmed against its own SQL
and runs in one transaction.

The editor takes a target rather than a schema: a table is the same call with
one more identifier, for when the table and role sections want it.
2026-08-31 04:03:42 +02:00
Diego Imbert c4e350e85c feat(datatables): lead tree rows with their chevron, keep the row menu visible
The favourite star follows the table name, showing on hover unless the table
is one — where the star is saying something, it stays.
2026-08-31 04:03:42 +02:00
Diego Imbert 7e00022f24 fix(datatables): put the row menu before the chevron, not over the row icon 2026-08-31 04:03:42 +02:00
Diego Imbert 7570349f2e feat(datatables): switch a data table's role from its row badge
The badge is now a button with a chevron, opening the select dropdown over
the roles the caller may use — the dropdown alone, not Select's own input.
Picking one reconnects the manager as that role, switching data table first
when the badge belongs to another one.
2026-08-31 04:03:42 +02:00
Diego Imbert bebd8bef8b feat(datatables): row menus everywhere, in place of the row icon
The menu now replaces a row's own icon on hover rather than its chevron, and
a table row keeps its icon with the favourite star moved to the far right.

Table menus are no longer limited to the data table the manager is connected
to: alter and delete route through the same switch the create rows use, so
they run against the connection of the data table the row belongs to.
2026-08-31 04:03:42 +02:00
Diego Imbert ae162b6d3e Merge branch 'main' into datatable-perms-3 2026-08-26 10:24:13 +02:00
hugocasaandClaude Opus 5 ffdf17ef8d fix: force HTTP router rebuild on trigger-change notification (#10849)
* fix: force HTTP router rebuild on trigger-change notification

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

* fix: coalesce http trigger change events into one forced rebuild

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

* fix: retry the coalesced http router rebuild when it fails

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

* fix: mark http routers stale when a forced rebuild fails

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

* fix: keep the router invalidation across an in-flight rebuild

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:23:25 +02:00
GuilhemandClaude Opus 5 72763c9ba5 tighten spacing between login email and password fields (#10811)
Claude-Session: https://claude.ai/code/session_01BmEVHF8afJmgv6saBRYN6w

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:49:48 +02:00
hugocasaandClaude Opus 5 46c363ffa4 fix: require admin on workspace tarball settings export (#10817)
* fix: require admin on workspace tarball settings export

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

* fix: name the refused flag in the settings export error

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 00:49:13 +02:00
GuilhemandClaude Opus 5 665f83e1f4 fix(frontend): operator menu opens on hover, pins on click (#10824)
* fix(frontend): operator menu opens on hover, pins on click

The operator hamburger synthesized a trigger click on every mouseenter, so
melt toggled the menu: re-entering an open menu closed it, and a real click
after a hover-open closed it too.

Hover now opens the menu only when closed and closes it 150ms after the
pointer leaves; the portaled content carries the same handlers so moving
between button and list keeps it open. A click is intercepted in the capture
phase: when hover already opened the menu the click is swallowed (melt would
otherwise toggle it shut) and pins it instead, so it stays open until a click
outside or on the trigger.

Opening and closing both go through a synthetic click on the trigger because
melt's menubar renders content only when rootActiveTrigger is set, which only
the trigger's own click handler does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* refactor(frontend): move hover-open/pin into Menu's openOnHover prop

The hover machinery duplicated what meltComponents/Popover.svelte already
offers as openOnHover. Menu.svelte owns both the trigger wrapper and the
content div, so the grace timeout, the pin flag and the synthetic trigger
click belong there rather than in the consumer.

OperatorMenu is back to its original markup plus `openOnHover`, and the other
Menubar users can opt in. Popover keeps its own implementation: it is built on
createPopover, not the menubar, and does not need the trigger-click detour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* fix(frontend): show the keyboard highlight on operator menu rows

sidebarClasses.hoverBg only reacts to the pointer, so rows styled with it
alone stayed transparent while melt moved data-highlighted through them:
arrow keys walked the menu invisibly. Affected Home, Runs, Schedules and
Tutorials (MenuLink), plus Account settings, Switch theme and All workspaces.

MenuLink adds the highlight only when it is rendered as a menu item; the
sidebar and settings-menu call sites pass no `item`, so nothing changes there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* fix(frontend): one highlight state per menu row, accent for the selected one

Menu rows carried a hover rule and a data-highlighted rule at once. Melt moves
data-highlighted with the pointer as well as the keyboard, so the hover rule
was a second, independent state: the row under the pointer and the row the
arrow keys had reached both lit up. Menu rows now style data-highlighted only.
"More triggers" keeps its hover rule — it is a plain div, not a melt item, so
it never receives data-highlighted.

The selected row also painted bg-surface-hover, making the current page
indistinguishable from a highlight. It now uses the accent pair the rest of
the app uses for selection, bg-surface-accent-selected + text-accent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* fix(frontend): make "More triggers" a real menu item

It was a hand-rolled <div role="button" tabindex="0">. Melt collects the rows
arrow keys walk with querySelectorAll('[data-melt-menu-id="<menuId>"]'), an
attribute only the item builder stamps on, so the row was skipped — and its
tabindex was no help either, since Tab inside an open menu is intercepted to
close it.

It is now a MenuItem. Melt closes the menu on item click unless the click is
defaultPrevented, and Svelte delegates onclick to the root, which runs after
melt's own listener, so the toggle sits in a capture handler on a wrapper
where it reaches the event first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* Revert "selected sidebar row on the accent tokens"

sidebarClasses drives the whole sidebar and SessionPicker, not just the
operator menu; restore selectedBg/selectedText to their previous values.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* feat(frontend): open the operator menu below the hamburger

Menu defaults to right-start, which put the operator menu alongside the
trigger and over the page header. bottom-start drops it under the hamburger,
left-aligned. Set on this menu only; the shared default is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* feat(frontend): show the operator menu trigger as selected while pinned

Nothing distinguished a pinned menu from one that is merely following the
pointer, so a click gave no feedback. Menu hands `pinned` to the triggr
snippet, and the operator hamburger keeps sidebarClasses.selectedBg while it
holds — the tint that hover gives it, now persisting after the pointer leaves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

* refactor(frontend): address Codex/Claude review on the menu hover changes

Reuse debounce from $lib/utils for the hover grace period instead of a
hand-rolled timer, matching how Popover implements the same delay.

Give the "More triggers" capture wrapper role="none" so it doesn't sit
between role="menu" and role="menuitem" as an unlabelled node, and spell out
in the comment why the listener has to be on an ancestor rather than on the
item itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwLAoY7s9iPkYPGcmnw6UC

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:48:51 +02:00
Diego Imbert 07c77ead74 feat(frontend): flag the fork-compare datatable schema diff as legacy (#10829)
* feat(frontend): flag the fork-compare datatable schema diff as legacy

* fix(frontend): make the legacy datatable diff alert copy match the opt-in flag
2026-08-26 00:48:28 +02:00
hugocasaandClaude Opus 5 78331fda8b fix(frontend): key the GitHub App installation selector on installation_id (#10831)
* fix(frontend): key the GitHub App installation selector on installation_id

The GitHub Account ID dropdown used `account_id` as both the option value
and the lookup key. A workspace can hold several installations for the same
org (re-installed, or added from another workspace), so `.find()` resolved to
whichever came first: picking the live installation could hand back a stale,
token-errored one whose `repositories` are empty, leaving the repository
dropdown blank. `RepositorySelector`'s pagination matched the same way and
appended the wrong installation's page.

Both now key on `installation_id`, and the dropdown appends the installation
id to the label only for orgs that appear more than once. Switching
installation remounts `RepositorySelector` and clears the selected
repository, so its loaded pages no longer carry over.

Fixes WIN-2448

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

* fix(frontend): shorten the duplicate-org installation label

Drop the "installation" word from the disambiguating suffix: the id alone
already tells the two entries apart, and it keeps the errored variant short
enough to read at a glance.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 00:48:12 +02:00
AlexRV12andClaude Opus 5 6b73145e72 fix(frontend): follow the operating workspace in step input forms (#10834)
* fix(frontend): follow the operating workspace in step input forms

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

* fix(frontend): rethrow auth errors and wire remaining variable pickers

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

* refactor(frontend): use runed watch for picker workspace reloads

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:47:46 +02:00
Diego ImbertandClaude Opus 5 8a6dc27236 feat: configurable expiry for presigned s3 public url signatures (#10835)
* feat: configurable expiry for presigned s3 public url signatures

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015JdZFeMXLGfeFNiQgx9QvA

* fix: describe expiry_secs clamping in the spec and pin the bounds in a test

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015JdZFeMXLGfeFNiQgx9QvA

* fix: omit null expiry_secs from the python sdk sign request

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015JdZFeMXLGfeFNiQgx9QvA

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:47:24 +02:00
hugocasaandClaude Opus 5 9fa8159ad1 fix: migrate slack resource-connect oauth to v2 (#10836)
* fix: migrate slack resource-connect oauth to v2

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

* fix: keep slack scopes one per entry, as every other provider does

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 00:41:15 +02:00
Ruben FiszelandClaude Opus 5 4658224592 fix(debugger): parse bun 1.4's UUID inspector token (#10828)
* fix(debugger): parse bun 1.4's UUID inspector token

Bun 1.4 changed the inspector URL's token to a hyphenated UUID. The stderr
scraper matched `[a-z0-9]+`, so it stopped at the first hyphen and connected to
a truncated path, which the inspector answers with 404. Every TypeScript debug
session has failed to attach since the 1.4.0 bump, taking the windmill-extra
integration tests with it.

Match the whole path, and only once its line is newline-terminated: a stderr
chunk can end mid-URL and would otherwise be read as a complete, truncated URL.

A close before the handshake completes is now reported as the connection
failure it is, rather than as a finished script, and the debuggee is reaped -
--inspect-wait blocks until a debugger attaches, so a failed attach leaked a bun
process per session.

On the test client, queue events that arrive before their waiter registers: the
server sends 'initialized' immediately behind the 'initialize' response, which
the client could drop and then time out waiting for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XMizaQRcnWRd79t5wWhjBN

* fix(debugger): keep the first terminated event's result on launch failure

A socket that drops after the handshake opens but mid-command-sequence reports
the termination from onclose, carrying the script result, and then fails the
launch. Sending a second terminated from the failure path overwrote that result
with an error-only event. Guard the send the way every other emit site in the
file does, leaving the reaping unconditional.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XMizaQRcnWRd79t5wWhjBN

* fix(debugger): report an inspector drop during setup as the failure it is

The setup commands run over an open socket and none of them reject when it
drops - sendInspectorCommand only has its own timer - so a drop between the
upgrade and Inspector.initialized was reported as a clean termination, and the
error surfaced up to 10s later or, once the duplicate was guarded, not at all.
Draw the line at execution actually starting rather than at the socket opening,
so those failures terminate with the connection error, immediately and once.

Pair the "Failed to start Bun" output with the terminated event it explains,
so a run that already reported its result cannot also be told it failed to
launch.

Prove the inspector URL complete with whitespace rather than an end-of-line:
trailing text on the banner line would otherwise stall the parse for 10s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XMizaQRcnWRd79t5wWhjBN

* fix(debugger): mark execution started only once the start command is answered

Inspector.initialized is what starts the script, so setting the flag before
awaiting its reply left a drop during that round trip looking like a clean
termination - the same silent failure, narrowed to one command. Its reply
precedes any close on the socket, so the continuation still runs before onclose
and a real run is not misread as a failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XMizaQRcnWRd79t5wWhjBN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:38:09 +02:00
Ruben FiszelandClaude Opus 5 5dc43c400a report the EE gate instead of a 500 on restart flow at step (#10846)
Claude-Session: https://claude.ai/code/session_01CbayDTXcGCTYuE9m56BRag

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:31:21 +02:00
Ruben FiszelandClaude Opus 5 41d111bf38 chore: emit only line tables for workspace crates in dev builds (#10845)
* chore: emit only line tables for workspace crates in dev builds

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQiM8pqagY19bnWiMebwGa

* docs: correct the CI comments that pinned profile.dev at debug = 2

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQiM8pqagY19bnWiMebwGa

* docs: scope the windows debuginfo comment to the crates that job builds

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQiM8pqagY19bnWiMebwGa

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 00:06:51 +02:00
Diego Imbert 7e5ac78447 feat(datatables): badge each data table row with the role it is reached through
Serve the caller's usable roles and the default one alongside the tree, so
the badge costs no extra round trip per data table. Shown only where naming
the role says something: a data table without permissions, or one whose only
usable role is the implicit admin, gets none.

The left-pane filter also matches schema names now.
2026-08-25 21:00:26 +02:00
Diego Imbert 983b514a5d enableFlyTransition 2026-08-25 20:47:46 +02:00
Diego Imbert ba69fee329 fix(datatables): show a row's menu on hover only, never because it is current 2026-08-25 20:42:06 +02:00
Diego Imbert 77965f1458 feat(datatables): create tables and schemas from any data table in the tree
The New table / New schema rows now show under every data table, not only
the one the manager is pointed at. Creating outside it switches to that data
table first, and the request rides through the parent because the switch
re-mounts the manager.

Tree rows also read uniformly now — the background alone marks the current
one — with the create rows set apart in secondary.
2026-08-25 19:47:50 +02:00
Diego Imbert 48294239e5 fix(datatables): key a table's favourite star on its own data table 2026-08-25 19:31:39 +02:00
Diego Imbert 3f92ab39ad fix(datatables): tick a picked table only from its checkbox 2026-08-25 19:28:48 +02:00
Diego Imbert 18d6302337 fix(datatables): put the picker checkboxes on the left, keep the chevrons right
Picking a table now also makes it the current one, previewed on the right —
across data tables too, which re-mounts the manager, so the picked
schema/table travel through the input the drawer keeps.
2026-08-25 18:42:59 +02:00
Diego Imbert 44314d6e7a fix(datatables): keep the new table/schema rows in the table picker 2026-08-25 18:34:52 +02:00
Diego Imbert b15bac1916 refactor(datatables): make multi-select a mode of the DB manager tree
The table picker for raw apps had its own checkbox tree next to the
manager's navigator. Now that the navigator is itself a data table ->
schema -> table tree, the picker is the same tree with a checkbox in
place of the chevron and row menu, so a pick can span data tables.

Also aligns the indent step between tree levels at 16px.
2026-08-25 18:27:32 +02:00