* fix: bind /etc resolver files read-only in nsjail sandboxes
* docs(nsjail): explain why per-file /etc resolver binds are load-bearing
The explicit /etc/hosts, /etc/resolv.conf and /etc/hostname binds look
like removable duplication of the read-only /etc bind above them. They
are not: on Kubernetes those files are separate kubelet bind-mounts on
top of /etc and nsjail's read-only remount is non-recursive, so without
these shadow binds they stay writable and a job can persist cross-tenant
DNS poisoning for the pod lifetime. Comment guards against a future
"dedup cleanup" silently reintroducing the vulnerability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(nsjail): shorten the load-bearing-bind comment to 3 lines
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: sidebar menu rendering expanded-but-empty near 768px width
The desktop sidebar branch is gated by JS (`innerWidth < 768`), but its
width was set only via Tailwind `md:` classes (`@media (min-width:768px)`).
`window.innerWidth` rounds fractional viewport widths, so at e.g. 767.8px
JS rounds to 768 and renders the desktop sidebar, while the CSS media
query does not match and no width class applies — leaving the sidebar
shell expanded with no width/content. Drop the now-redundant `md:`
prefix so width tracks the JS branch decision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: align content offset breakpoint with sidebar JS gate
The sidebar width now follows the JS innerWidth gate, but the main
content left-offset in AiChatLayout still used the `md:` CSS media
query, leaving the two breakpoints out of sync in the same sub-pixel
band. Pass an `isMobile` flag from the layout (mirroring the sidebar's
`innerWidth < 768` condition) and gate the content padding on it with
unprefixed classes so sidebar width and content offset always flip
together.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: reset parent_hash in auto_parent when all versions at path are archived
* test: regression test for auto_parent with all versions archived
---------
Co-authored-by: windmill-internal-app[bot] <1429786+windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix: don't show ALLOW_PRIVATE_AI_BASE_URLS hint for malformed AI base URLs
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: impl std::error::Error for SsrfValidationError for anyhow callers
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: atomic bundle cache writes to prevent parallel cold-load race
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: trust-but-replace in atomic_publish_dir to never trust a stale partial cache dir
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: simplify atomic_publish_dir and add content-addressed rename-failure fallback
Revert the destroy-then-recreate dir swap (introduced concurrent-publisher
edge cases: spurious Err under a real herd, EACCES masking a stale partial)
back to a single atomic rename. Add the content-addressed exists-fallback to
atomic_write_file_bytes/atomic_copy_file so the loser of a publish race (and
Windows, where rename cannot replace an open/existing destination) treats the
already-published identical cache as success instead of failing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
windmill-parser-ts-asset only recognized writeS3File/loadS3File when
the first arg was a bare 's3://...' string literal. The actual SDK
signature takes an S3Object ({ s3, storage? }) or 's3://bucket/key'
string, which every real script uses, so object-form writes/reads were
never detected as assets. Resolve the S3Object arg the same way the
runtime parseS3Object does, mapping { s3, storage } to s3://<storage>/
<key> and feeding it through parse_asset_syntax so the path matches the
// on s3:///… trigger form. Adds regression tests.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: aggregate wait time should target the true root job, not flow_innermost_root_job
* refactor: reuse get_root_job_id helper for wait-time aggregation
Instead of duplicating the root_job → flow_innermost_root_job →
parent_job fallback chain inline, call the existing get_root_job_id()
helper (the same one used by push_next_flow_job) and filter out the
self-id case so standalone scripts still skip aggregate insertion.
Behaviorally identical, single source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: windmill-internal-app[bot] <1429786+windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [ee] fix(nativets): pass tracing-enabled OtelConfig to deno_telemetry::init
* chore: update ee-repo-ref to 19a76a09ffb43649ee19e62d07e8b8a42d78757b
This commit updates the EE repository reference after PR #573 was merged in windmill-ee-private.
Previous ee-repo-ref: daf7ce31f192d64ca6b5602af626ab6453d2aff4
New ee-repo-ref: 19a76a09ffb43649ee19e62d07e8b8a42d78757b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <1429786+windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: include service accounts in instance settings users list
Service accounts (workspace-scoped, no password row) now appear in the
superadmin users list with a Bot icon, workspace badge, and a link to
manage them in the workspace settings. Role is locked to Operator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update sqlx offline cache
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: use composite key for users each block
Service accounts can share emails across workspaces, so key by email + workspace_id to avoid Svelte each_key_duplicate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(python): preserve strings containing Infinity/NaN in result JSON
* test(python): add sanity checks for Infinity/NaN in results
* refactor(python): use string-aware regex callback for single-pass cleanup
* refactor(python): compact regex + handle backslash-escape parity
* perf(python): short-circuit cleanup when no Infinity/NaN/NUL in result
* feat: read-only flag on API tokens, orthogonal to scopes
Add a per-token `read_only` boolean set at creation time. When true, the
token can only call HTTP methods classified as Read (GET/HEAD/OPTIONS).
Mutating methods and job-run actions are rejected with 403, regardless of
which scopes are attached. Surfaced as a prominent toggle in the standard
token-creation flow and a discreet `2xs` toggle in MCP mode (where users
often want write access, so we don't bias them toward enabling it).
MCP enforcement: read-only tokens hide all script/flow/hub tools from
`list_tools` and only see endpoint tools whose method is GET, and the
runner rejects `call_tool` on anything mutating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: review fixes for read-only token flag
- Exempt /api/mcp/* and /mcp/* paths from the read-only middleware check.
MCP transport runs over POST (streamable HTTP / SSE), so otherwise the
middleware would 403 every MCP request before the runner could enforce
read-only at the tool-call level.
- Tighten is_endpoint_read_only to GET only, matching the read_only_hint
that create_endpoint_annotations actually emits.
- Add unit test for check_read_only_for_route covering GET/HEAD/OPTIONS,
mutating methods, and run paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref to read-only-trigger-toggle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): make read-only toggle discreet in both modes
Match the MCP-mode treatment in standard mode: text-tertiary, 2xs, shared
"Read-only" label. The tooltip switches per mode so the explanation still
fits the context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): gate read-only toggle behind Limit token permissions
The read-only toggle now only shows when the user has limited the token's
scopes (standard mode) or in MCP mode (which always picks an MCP scope).
Turning the limit off also resets read-only so it doesn't silently stick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(frontend): hide incompatible MCP tools when read-only is on
When the read-only toggle is on in MCP mode:
- Endpoint badges and the custom-mode endpoint MultiSelect filter to GET.
- Already-selected non-GET endpoints are pruned from the scope.
- The scripts/flows preview is replaced with a note explaining they're
hidden (the runner already rejects script/flow runs for read-only).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): place read-only toggle at top of limited scope area
The previous gate required at least one scope to be picked before the
read-only toggle appeared, which made it look missing while the user was
still building their scope list. Move the toggle inside ScopesPicker:
- Standard mode: sits directly under the "Limit token permissions" toggle
whenever Limit is on, before the scope selector.
- MCP mode: sits at the top of the MCP scope block.
readOnly is now $bindable on ScopesPicker so CreateToken still owns the
value. The auto-reset on un-limit moves into ScopesPicker too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): nest read-only toggle inside the scope list card
Place the read-only toggle at the top of the scope list (between the
Selected Scopes summary and the bordered domain list) via a new optional
topSlot snippet on ScopeSelector. Keeps ScopeSelector decoupled from
read-only specifics; ScopesPicker fills the slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
This commit updates the EE repository reference after PR #571 was merged in windmill-ee-private.
Previous ee-repo-ref: f53d26e6685dfd60bfa67686fbd7358169cfd130
New ee-repo-ref: 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
Automated by sync-ee-ref workflow.
* fix: address CI review for read-only token flag
- P1 (Codex): narrow the MCP middleware exemption from "any /api/mcp/*"
to just the streamable HTTP transport endpoints
(/api/mcp/gateway, /api/mcp/w/{ws}/{mcp,sse,list_tools}). Without this,
a read-only token could POST /api/mcp/gateway/oauth/server/approve and
mint a follow-on non-read-only MCP token via the OAuth code/token
exchange.
- P2 (Claude/cubic): fix test comment/assertion mismatch — the run-path
assertion now exercises GET (which is what the RUN_PATH_ACTIONS
elevation comment describes) in addition to POST. Add a regression
assertion for /api/mcp/gateway/oauth/server/approve.
- P2 (cubic): short-circuit script/flow/hub-script/resource fetches in
MCP list_tools when read_only is on — they would only be discarded
below, so skipping the DB and resource fan-out is pure win.
- P2 (cubic): when scopes are pre-supplied via the CreateToken prop, the
ScopesPicker isn't rendered, which previously hid the read-only
toggle entirely. Render it next to the pre-supplied scopes display.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
- exists_script_by_path now filters archived = false, matching the
conflict check in create_script_internal. Previously the frontend
blocked creating a new script at a path occupied only by archived
scripts, even though renaming to that same path was allowed.
- Hide the Delete entry in the script details "..." menu unless the
user is admin. The backend delete_script_by_hash already requires
admin, so non-admins would always see an error after clicking.
* perf(dynselect): only retrigger when helper-script args actually change
Parse the inline helper's signature with the existing WASM parser and
restrict the form-arg diff to keys the helper actually consumes. Typing
into unrelated fields no longer queues a dynselect job every second.
Falls back to the previous full-args comparison when the helper is
deployed or parsing fails.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(dynselect): avoid double helper-script fetch on mount
usePromise defaults to loadInit=true, so refresh() ran before the
JobLoader child was bound (firing a no-op pending promise) and the
$effect then fired a second refresh once the bind:this resolved.
Disable loadInit so the effect owns the single first call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(dynselect): use parser directly instead of inferArgs
inferArgs mutates a Schema object we never use and goes through a
shared cache; when fed an empty schema for non-main entrypoints the
caller cannot reliably read back the resulting properties. Add
parseEntrypointArgs that just runs the parser and returns the
parameter name Set (or undefined when unknown / unsupported / has
rest args / function not found). DynamicInput uses that and keeps
the previous params in flight while the next parse is computing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(dynselect): support deployed helpers in smart retrigger
Add getHelperEntrypointArgs which dispatches on HelperScript.source:
inline parses immediately; deployed fetches the script (or the flow's
inline dyn-select code) once and caches per (workspace, kind, path,
entrypoint). Without this the /scripts/get/* run view fell back to
the full-args comparison and still retriggered on unrelated fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(dynselect): zero-arg helpers report empty deps, not unknown
Codex review flagged that a valid zero-parameter entrypoint was being
treated as "couldn't determine signature" and falling back to the
full-args comparison. Distinguish "function found with no params" from
"function not found" via the parser's auto_kind field — only the
latter sets it, so empty args + auto_kind=null means a real zero-arg
helper and we return an empty Set (no retrigger on unrelated fields).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(bun): pass --preserve-symlinks on unbundled execution
Bun 1.2/1.3 moved its global package cache to a content-addressed
layout and the installer now creates a single directory symlink from
node_modules/<pkg> to the cache entry. Without --preserve-symlinks,
Bun resolves modules from each file's realpath, so any require/import
inside an installed package walks up from cache_nomount/bun/... and
never finds the sibling deps living under <job_dir>/node_modules/.
This manifested as e.g.
ENOENT while resolving package 'zod/v3' from
'/tmp/windmill/cache_nomount/bun/@langchain/core@1.1.44@@@1/dist/...'
on //nobundling scripts that pull @langchain/core, even though zod is
correctly installed alongside it in node_modules.
The bundled execution path already had --preserve-symlinks since #4132
(needed because we symlink the cached bundle file into the job dir).
The unbundled path didn't, because at the time Bun installed via per-
file hardlinks and the realpath of node_modules entries was the job
dir itself. The Bun installer's layout change made the flag necessary
on the unbundled path as well.
Add the flag to all three unbundled `bun run` invocations:
- nsjail unbundled path
- non-nsjail unbundled path
- dedicated worker (always unbundled)
This also fixes a latent bug on the first run of any bun script that
imports a package whose internals reference siblings (the build_cache
path runs unbundled this round while it builds the bundle for next
time).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(bun): regression test for nobundling + transitive require resolution
Adds an integration test that mirrors the original failure: a //nobundling
script importing @langchain/core, which (in its CJS internals) does
require('zod/v3'). Before --preserve-symlinks was added to the unbundled
bun run invocations, this failed with:
ENOENT while resolving package 'zod/v3' from
'.../cache_nomount/bun/@langchain/core@<ver>@@@1/dist/runnables/base.js'
The test covers the non-nsjail unbundled path. Reproducibility of the
pre-fix failure depends on Bun's installer choosing the directory-symlink
layout for the node_modules entry (the default on Bun 1.2/1.3+ with the
new content-addressed global cache that produced the user's error).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(git-sync): regression tests for secondary promotion repos
Adds two integration tests that reproduce the bug where a second
promotion-mode repo's deployment callback was silently dropped via
debounce-key collision, plus the EE ref bump that includes the fix.
Updates the two existing promotion-mode debounce-key tests to expect
the new repo-namespaced key shape.
* test(git-sync): drop redundant distinct-debounce-keys test
The behavior test (`test_two_promotion_repos_both_enqueue_callback`)
already covers the same regression one layer up: if the debounce keys
collide, one callback gets marked skipped, which the behavior test
catches.
* chore: update ee-repo-ref to 7a32388adaa37eb1dd1820b40e140ff1877110f2
This commit updates the EE repository reference after PR #572 was merged in windmill-ee-private.
Previous ee-repo-ref: dbf26f5e4c01c0de536f606679be46eb316aaf31
New ee-repo-ref: 7a32388adaa37eb1dd1820b40e140ff1877110f2
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: publish CLI skills + AGENTS.md to windmill-cli-docs for context7
Auto-generates a public docs snapshot (AGENTS.md, full CLI reference,
all rendered skills) and pushes it to windmill-labs/windmill-cli-docs on
every release tag, so context7 can index Windmill CLI docs.
- generate.py: new --context7-dir flag rendering fully-resolved skills
+ AGENTS.md (extracted from cli/src/guidance/core.ts to avoid drift)
+ cli-commands.md + README.md + manifest.json into a docs-repo checkout.
Preserves .git, .github, LICENSE, context7.json across regenerations.
- publish-cli-docs.yml: GitHub Action on v* tag and workflow_dispatch
that regenerates the docs repo and pushes via the CLI_DOCS_DEPLOY_KEY
SSH deploy key.
* fix: skip tag mirror on workflow_dispatch from non-tag ref
* docs: turn windmill-cli-docs README into a CLI quickstart
* fix: address PR review (target safety, regex anchor, concurrency, tag mirror)
- Refuse to wipe --context7-dir unless empty, has a context7 marker, or
points at the windmill-cli-docs remote (P1, prevents typo blast).
- Anchor AGENTS.md template regex on `generateAgentsMdContent` so adding
other template-returning functions to core.ts can't silently retarget it.
- Decode TS escapes in one pass to avoid order-sensitive mangling.
- Include Windmill version (from version.txt) in manifest.json so each
snapshot is self-describing.
- Add concurrency group on the publish workflow.
- Always mirror version tag on tag pushes, even when content is unchanged,
so the docs repo has a tag for every Windmill release.
- Expand preserve list with .gitignore, .gitattributes, CODEOWNERS.
* fix: validate manifest.json content, not just presence, before wipe
* feat: add EditableInput component
* feat: add EditorHeader for flow editor with file picker entry point
* feat: WorkspaceItemPicker for editor header navigation
* feat: clickable breadcrumb in EditorHeader, scoped picker keyboard nav
* fix: reload flow on URL change and reset highlight in search mode
* feat: editor header layout polish and trigger removal
* feat: apply EditorHeader to script, app, and raw app editors
* fix: show generated initial path for new apps in EditorHeader
* fix: align EditorHeader new-app path with draft drawer's Path component
* fix: read page.params.path in loadApp to reload on URL change
* fix: remount AppEditor when navigating between apps
* fix: clear app/files on URL change so editor remounts with fresh data
* fix: route picker selections to /apps/edit or /apps_raw/edit based on raw_app
* fix: drop Save button from path popover; defer rename to deploy
* refactor: drop pathPopoverContent snippet, bind newEditedPath directly
* refactor: dedupe editor header plumbing (editPathFor, userPathPrefix, breadcrumb snippet)
* fix: freeze breadcrumb during path edit so popover doesn't drift
* fix: drop spinner from path dependency-check; render nothing when no usages
* fix: swallow 404 in checkFlowOnBehalfOf so renaming a flow doesn't toast
* Revert "fix: swallow 404 in checkFlowOnBehalfOf so renaming a flow doesn't toast"
This reverts commit 82dec462ae.
* refactor: drop moveRenameManager dep from EditorHeader; pass onBehalfOfEmail as prop
* refactor: replace breadcrumb-snapshot effect with open/close setter
* refactor: drop unused dirtyPath state from EditorHeader
* fix: surface Path validation error in pen popover
* fix: decouple Path validation error from hideFullPath toggle
* refactor: use InputError for path validation message (slide transition)
* fix: re-derive Path meta from external path changes (sibling sync)
* docs: note Path's meta could be replaced with function-form bindings
* fix: 'Exit & see details' uses deployed path, not live store
* fix: undo/redo shortcut uses shiftKey instead of fragile case-match
* fix: type errors and keep edit pen visible while popover is open
* chore: remove unused meltComponents/Accordion wrapper
* fix: focus search input on picker open via popover openFocus selector
* fix: refocus picker search input on every popover open
* fix: pre-seed picker loaded state from cache so accordion opens at the right place
* fix: breadcrumb and picker track savedPath, not draft-renamed live path
* fix: inject current draft item into picker so breadcrumb scope isn't empty
* docs: add component-level and prop-level docs to EditableInput
* feat: warn that a deploy is needed when path is edited on a saved item
* feat: show same path-change-needs-deploy message in flow/script settings
* fix(flows): persist draft-renamed path through reload and dirty check
* fix: include path in unsaved-changes diff so renames trigger the modal
* feat: nested folders in picker tree and breadcrumb
* fix: per-segment popover state so switching breadcrumbs closes the previous one
* refactor: replace accordion picker with drill-through picker
* refactor(picker): review fixes, drill polish, and breadcrumb collapse
* fix(picker): review fixes — banned bindable, drop sibling-sync, load races, mouse highlight
* fix(picker): smooth-scroll highlighted row into view on open
* fix(picker): second-pass review fixes — load races, cache invalidation, breadcrumb/picker a11y, raw_app routing
* fix(RowIcon): apply size prop to resource_type and fallback divs
* feat(picker): add cross-kind 'All' root; deeper breadcrumb segments open there
* fix(picker,editor): third-pass review — search loading state, pen autofocus, allow empty summary, a11y
* fix(picker,editor): fourth-pass review — own check, flowbuilder arrows, editableinput double-save, customui.path gate, invalidate races
* fix(editor,picker): codex review — fresh URL state per load, granular whitelabel topBar gates
* fix(flows): clear localStorage in auto-reload to break URL-state loop
Setting a typed instance setting (e.g. `object_store_cache_config`) to
`null` via `PUT /api/settings/instance_config` was silently ignored:
`Json<InstanceConfig>` deserialized null to `None`, and `to_settings_map`
then dropped it via `skip_serializing_if = "Option::is_none"`, so
`diff_global_settings` (Merge mode) never saw the deletion.
Add `#[serde(remote = "Self")]` to `GlobalSettings` plus a manual
`Deserialize` impl that captures top-level explicit nulls into `extra`,
which already round-trips correctly via flatten. The handler stays
fully typed.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): branch download UI on shouldDownloadViaClient instead of intercepting in onclick
When OpenAPI.TOKEN is set, several download links rendered an `<a href>`
to the API and relied on an `onclick` handler to call `e.preventDefault()`
and route the request through `downloadViaClient`. This is fragile in
embedded contexts (e.g. the whitelabel React SDK) where Svelte's hydrated
event listener may not intercept the click in time, so the browser
follows the unauthenticated `href` straight to the API.
Mirror the drawer pattern already used in `LogViewer` and
`FlowStatusViewerInner`: render a `<button>` calling `downloadViaClient`
when `shouldDownloadViaClient()` is true, and fall back to the plain
`<a href download>` otherwise. Affects the LogViewer top bar, the large-
result download in DisplayResult, the inline S3 link in ObjectViewer,
the CSV link in ParqetCsvTableRenderer, and FileDownload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): treat custom HEADERS / basic auth as token equivalents in shouldDownloadViaClient
Customers wiring the SDK with cookie-bypass auth via custom request
headers (e.g. `OpenAPI.HEADERS = getAuthHeaders()` returning a Bearer
header) had `shouldDownloadViaClient()` return false because it only
checked `OpenAPI.TOKEN`. The plain `<a href download>` branch then
followed the link without those headers, so authenticated downloads
silently degraded to the cookie path (or failed when there is no cookie).
Widen the check to any non-cookie auth: TOKEN, HEADERS, or USERNAME
(basic auth). Route `downloadViaClient` through the generated client's
`getHeaders` so all configured auth schemes are applied consistently
instead of hand-building an Authorization header for TOKEN only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: add global ai mode plan
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add global ai draft mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: scope global ai mode to scripts and flows
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: simplify global ai workspace item shape
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: split global ai write tool into per-type tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add global ai schedule and trigger workspace item tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add dev-only /global_drafts route to inspect ai draft store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add edit_script and patch_flow_json global ai tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add deploy_workspace_item global ai tool with confirmation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: emit open-resource action card after deploy_workspace_item
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add delete_workspace_item global ai tool with confirmation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore(system_prompts): emit RESOURCES_BASE and resource/variable zod schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add global ai resource and variable workspace item tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: search_resource_types uses listResourceType to avoid embedding feature dep
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Revert "fix: search_resource_types uses listResourceType to avoid embedding feature dep"
This reverts commit 6d1d19514a.
* feat: emit open-resource action card for variable and resource deploys
* feat: add global ai raw app workspace item tools
* feat: split raw-app prompt into chat-only authoring and cli prefix
* feat: add init_app global ai tool to scaffold raw apps from templates
* fix: pass write_flow value as JSON string for gemini compat
* refactor: hoist countExactMatches and applyExactReplace to chat/shared
* refactor: extract editableFlowJson module shared with global mode
* fix(global): preserve flow schema and groups across draft and deploy
* feat: extract inline scripts from flow reads and patches in global mode
* refactor: add findAndReplace helper for match-validated text patches
* refactor: extract getInlineRunnableContent helper for app file tools
* refactor: extract assertNotGeneratedAppFile guard for /wmill.d.ts
* feat: gate global ai mode behind localStorage flag for dev rollout
* chore: bump svelte to ^5.55.5 in raw app template (sync with main)
* fix: isolate global ai draft rollout
* fix: preserve global ai deploy metadata
* fix: harden global ai draft tools
* chore: remove global ai plan doc
* fix: align raw app prompt guidance
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>