mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-27 08:02:39 +00:00
* feat: read-only ai sessions on protected workspaces with a use, plan and build picker Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: address local review findings on the use, plan and build picker Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: address CI review findings on use mode and protected sessions Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: hold test-run forms and pending protection lookups to use mode Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: fetch rules once per send and dedupe helpers and prompt text Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: send role-locked operators to an admin and cover it with an eval Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: count the work mode each session message runs in Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore: regenerate the mcp session definitions after merging main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: rename build to use & build and let the chat ask to switch modes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: word a declined mode switch by the switch, not the mode Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 55445977dd05c25791388070182c77f4dbc20c0e This commit updates the EE repository reference after PR #915 was merged in windmill-ee-private. Previous ee-repo-ref: 7f7bf7003e356346ce74395af02c5bdf14f38781 New ee-repo-ref: 55445977dd05c25791388070182c77f4dbc20c0e 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>
88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
import type { OperatorSettings, RoleCapabilities, RoleVisibility } from '$lib/gen'
|
|
import type { UserExt, UserWorkspace } from '$lib/stores'
|
|
import { FULL_CAPABILITIES, VISIBILITY_KEYS } from '$lib/workspaceRoles'
|
|
|
|
/**
|
|
* What a member's workspace role lets them author, manage and see: the frontend's copy of
|
|
* `windmill_common::roles`, see `docs/workspace-roles.md`. Gate on these, never on
|
|
* `UserExt.operator`, which only says "this role cannot write code" and so lets through a role
|
|
* that may code but not, say, build apps.
|
|
*/
|
|
|
|
export type VisibilityKey = keyof RoleVisibility
|
|
|
|
export { FULL_CAPABILITIES }
|
|
|
|
const VISIBILITY = VISIBILITY_KEYS.map((v) => v.key)
|
|
|
|
/**
|
|
* The operator role as a server that predates roles describes it, through `operator_settings`:
|
|
* builder rights are `compose`, the manage rights are granted unless withdrawn, and a page is
|
|
* visible only when its flag is `true` (absent settings hide every page, as they always did).
|
|
*/
|
|
export function legacyOperatorCapabilities(
|
|
settings: OperatorSettings | null | undefined
|
|
): RoleCapabilities {
|
|
const visibility = Object.fromEntries(
|
|
VISIBILITY.map((k) => [k, settings?.[k] === true])
|
|
) as RoleVisibility
|
|
return {
|
|
code: false,
|
|
flows: settings?.builder_flows === true ? 'compose' : 'none',
|
|
apps: settings?.builder_apps === true ? 'compose' : 'none',
|
|
manage_schedules: settings?.manage_schedules !== false,
|
|
manage_triggers: settings?.manage_triggers !== false,
|
|
visibility
|
|
}
|
|
}
|
|
|
|
export type CapabilitiesUser = Partial<Pick<UserExt, 'is_admin' | 'is_super_admin' | 'operator'>>
|
|
export type CapabilitiesWorkspace = Pick<UserWorkspace, 'capabilities' | 'operator_settings'>
|
|
|
|
/**
|
|
* The capabilities of `user` in `workspace` (its entry of `$userWorkspaces`). An admin holds
|
|
* everything. The server's `capabilities` win; an older server sends none, and the role is then
|
|
* derived from `operator` and `operator_settings` the way it read them. Without a user, a
|
|
* non-null `operator_settings` is what marks an operator, as the server sends it to them only.
|
|
*/
|
|
export function capabilitiesIn(
|
|
user: CapabilitiesUser | undefined,
|
|
workspace: CapabilitiesWorkspace | undefined
|
|
): RoleCapabilities {
|
|
if (user?.is_admin || user?.is_super_admin) return FULL_CAPABILITIES
|
|
if (workspace?.capabilities) return workspace.capabilities
|
|
const operator = user?.operator ?? workspace?.operator_settings != null
|
|
return operator ? legacyOperatorCapabilities(workspace?.operator_settings) : FULL_CAPABILITIES
|
|
}
|
|
|
|
/** Whether the role is gated at all, as `RoleCapabilities::is_restricted`: visibility is left out,
|
|
* since hiding a page must not cost a full author what restricted roles are refused, such as
|
|
* forking. */
|
|
export function isRestricted(caps: RoleCapabilities): boolean {
|
|
return (
|
|
!caps.code ||
|
|
caps.flows !== 'full' ||
|
|
caps.apps !== 'full' ||
|
|
!caps.manage_schedules ||
|
|
!caps.manage_triggers
|
|
)
|
|
}
|
|
|
|
/** The role with nothing left to build and the same pages in view: what a session in Use mode
|
|
* acts as. */
|
|
export function withoutBuilding(caps: RoleCapabilities): RoleCapabilities {
|
|
return {
|
|
...caps,
|
|
code: false,
|
|
flows: 'none',
|
|
apps: 'none',
|
|
manage_schedules: false,
|
|
manage_triggers: false
|
|
}
|
|
}
|
|
|
|
/** Whether the role hides any page. */
|
|
export function hidesPages(caps: RoleCapabilities): boolean {
|
|
return VISIBILITY.some((k) => !caps.visibility[k])
|
|
}
|