mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-23 08:03:33 +00:00
* feat: restricted job tokens (job_token_scopes on scripts and flows) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: admit flow-run reads, skip dedicated workers, gate on worker version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off flow runners, preserve scopes on rename and promotion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep restricted jobs off every dedicated handoff, confine progress flow id Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: exclude restricted runnables from dedicated worker startup Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: gate restrictions on the release after 1.821.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: store per-job scopes on job_perms instead of v2_job, pin inline runs to the checked version Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: step-level job_token_scopes for flow steps and agent tools Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: fail closed on perms read errors, refuse restricted queue imports, gate step scopes in previews Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a job's scopes on its completion so a re-run keeps the caller's cap Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: carry a zombie job's scopes into its completion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: leave a zombie for the next sweep when its scopes cannot be read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * docs: correct the QueuedJobV2 completion comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: validate step scopes in batch flows, fail closed on unvalidated step scopes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: refuse flows with step or tool restrictions at push while an older worker is live Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: apply the step-scope worker gate to flow restarts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: list the job token toggle with the other step and flow settings Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: pin the EE companion merged with EE main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * perf: skip scope lookups for unrestricted jobs; list job token setting last Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * style: rustfmt scopes tests Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * fix: confine restricted job tokens to their own run lineage; drop remaining extra lookups Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FHGE3ynoAu6yrgeg4kLwL * chore: update ee-repo-ref to 259ad3bfeef5285ba80eedc86309b11dca001220 This commit updates the EE repository reference after PR #843 was merged in windmill-ee-private. Previous ee-repo-ref: 2b77c0225dca441235daf7bf0a06ba968df0c927 New ee-repo-ref: 259ad3bfeef5285ba80eedc86309b11dca001220 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
135 lines
4.4 KiB
Svelte
135 lines
4.4 KiB
Svelte
<script lang="ts">
|
|
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
|
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
|
import { getContext } from 'svelte'
|
|
import type { FlowEditorContext } from '../types'
|
|
import { filteredContentForExport } from '../utils.svelte'
|
|
import YAML from 'yaml'
|
|
|
|
import { Button } from '$lib/components/common'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { Loader2 } from 'lucide-svelte'
|
|
import { refreshStateStore } from '$lib/svelte5Utils.svelte'
|
|
import SimpleEditor from '../../SimpleEditor.svelte'
|
|
|
|
const { flowStore } = $state(getContext<FlowEditorContext>('FlowEditorContext'))
|
|
|
|
interface Props {
|
|
drawer: Drawer | undefined
|
|
}
|
|
|
|
let { drawer = $bindable() }: Props = $props()
|
|
|
|
let code = $state('')
|
|
let initialCode = $state('')
|
|
let editor = $state(undefined) as SimpleEditor | undefined
|
|
let hasChanges = $derived(code !== initialCode)
|
|
|
|
function reload() {
|
|
code = YAML.stringify(filteredContentForExport(flowStore.val))
|
|
initialCode = code
|
|
editor?.setCode(code)
|
|
}
|
|
|
|
/** `value` is assigned unconditionally below and `FlowEditor` dereferences
|
|
* `flowStore.val.value.modules`, so an OpenFlow document missing it takes the
|
|
* whole editor down mid-render — after the toast has already claimed success.
|
|
* Reject it up front instead. */
|
|
function validateShape(parsed: unknown) {
|
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
throw new Error('the document must be a mapping (key: value)')
|
|
}
|
|
const value = (parsed as Record<string, unknown>).value
|
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
throw new Error("missing 'value' - paste a whole OpenFlow document, not just its value")
|
|
}
|
|
if (!Array.isArray((value as Record<string, unknown>).modules)) {
|
|
throw new Error("'value.modules' must be a list")
|
|
}
|
|
}
|
|
|
|
function validateGroups(groups: { start_id: string; end_id: string }[] | undefined) {
|
|
if (!groups) return
|
|
const seen = new Set<string>()
|
|
for (const g of groups) {
|
|
const key = `${g.start_id}:${g.end_id}`
|
|
if (seen.has(key)) {
|
|
throw new Error(`Duplicate group: '${g.start_id}' → '${g.end_id}'`)
|
|
}
|
|
seen.add(key)
|
|
}
|
|
}
|
|
|
|
function apply() {
|
|
try {
|
|
const parsed = YAML.parse(code)
|
|
validateShape(parsed)
|
|
validateGroups(parsed.value?.groups)
|
|
if (parsed.summary && typeof parsed.summary === 'string') {
|
|
flowStore.val.summary = parsed.summary
|
|
}
|
|
if (parsed.description && typeof parsed.description === 'string') {
|
|
flowStore.val.description = parsed.description
|
|
}
|
|
if (parsed['ws_error_handler_muted'] !== undefined) {
|
|
flowStore.val.ws_error_handler_muted = parsed['ws_error_handler_muted']
|
|
}
|
|
if (parsed['dedicated_worker'] !== undefined) {
|
|
flowStore.val.dedicated_worker = parsed['dedicated_worker']
|
|
}
|
|
if (parsed['visible_to_runner_only'] !== undefined) {
|
|
flowStore.val.visible_to_runner_only = parsed['visible_to_runner_only']
|
|
}
|
|
if (parsed['on_behalf_of_email'] !== undefined) {
|
|
flowStore.val.on_behalf_of_email = parsed['on_behalf_of_email']
|
|
}
|
|
if (parsed['on_behalf_of'] !== undefined) {
|
|
flowStore.val.on_behalf_of = parsed['on_behalf_of']
|
|
}
|
|
if (parsed['job_token_scopes'] !== undefined) {
|
|
flowStore.val.job_token_scopes = parsed['job_token_scopes']
|
|
}
|
|
flowStore.val.value = parsed.value
|
|
flowStore.val.schema = parsed.schema
|
|
flowStore.val.tag = parsed.tag
|
|
refreshStateStore(flowStore)
|
|
initialCode = code
|
|
sendUserToast('Changes applied')
|
|
} catch (e) {
|
|
sendUserToast('Error parsing yaml: ' + e, true)
|
|
}
|
|
}
|
|
|
|
let editorHeight = $state(0)
|
|
</script>
|
|
|
|
<Drawer on:open={reload} bind:this={drawer} size="800px">
|
|
<DrawerContent title="OpenFlow" on:close={() => drawer?.toggleDrawer()}>
|
|
{#snippet actions()}
|
|
<Button variant="default" unifiedSize="md" disabled={!hasChanges} on:click={reload}
|
|
>Reset code</Button
|
|
>
|
|
<Button variant="accent" unifiedSize="md" disabled={!hasChanges} on:click={apply}
|
|
>Apply changes</Button
|
|
>
|
|
{/snippet}
|
|
|
|
{#if flowStore.val}
|
|
{#await import('../../SimpleEditor.svelte')}
|
|
<Loader2 class="animate-spin" />
|
|
{:then Module}
|
|
<div class="h-full w-full overflow-hidden" bind:clientHeight={editorHeight}>
|
|
<Module.default
|
|
bind:this={editor}
|
|
autoHeight
|
|
minHeight={editorHeight}
|
|
bind:code
|
|
lang="yaml"
|
|
leadingChangeSync
|
|
/>
|
|
</div>
|
|
{/await}
|
|
{/if}
|
|
</DrawerContent>
|
|
</Drawer>
|