feat: add wmill user list and document post-suspend approvers context (#11600)

* feat: add wmill user list and document post-suspend approvers context

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

* docs: approvers holds the resume url approver string when set

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

* chore: regenerate cli skills for approvers wording

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

* chore: update ee-repo-ref to acb254ac509aff244849c09e50a8d39aae185138

This commit updates the EE repository reference after PR #852 was merged in windmill-ee-private.

Previous ee-repo-ref: 9cc330f1a8e80bc075a6ad9839966d3b344f8ddd

New ee-repo-ref: acb254ac509aff244849c09e50a8d39aae185138

Automated by sync-ee-ref workflow.

* fix: drop name column from wmill user list, usr has no name

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

* docs: approvalPage resumes record the logged-in user as approver

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

---------

Co-authored-by: Claude Opus 5.5 <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>
This commit is contained in:
hugocasa
2026-10-08 08:57:26 +02:00
committed by GitHub
co-authored by Claude Opus 5.5 windmill-internal-app[bot] Ruben Fiszel
parent a2e51cdbaa
commit 1c2667cd55
8 changed files with 59 additions and 0 deletions
+31
View File
@@ -1,6 +1,8 @@
import { writeFile } from "node:fs/promises";
import { requireLogin } from "../../core/auth.ts";
import { resolveWorkspace } from "../../core/context.ts";
import { mergeConfigWithConfigFile } from "../../core/conf.ts";
import {
GlobalOptions,
isSuperset,
@@ -69,6 +71,32 @@ async function list(opts: GlobalOptions) {
.render();
}
async function listWorkspaceUsers(opts: GlobalOptions & { json?: boolean }) {
if (opts.json) log.setSilent(true);
opts = await mergeConfigWithConfigFile(opts);
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
const users = await wmill.listUsers({ workspace: workspace.workspaceId });
if (opts.json) {
console.log(JSON.stringify(users));
} else {
new Table()
.header(["username", "email", "role"])
.padding(2)
.border(true)
.body(
users.map((u) => [
u.username,
u.email,
u.is_admin ? "admin" : u.operator ? "operator" : "developer",
])
)
.render();
}
}
function rdString() {
return Math.random().toString(36).slice(2, 7);
}
@@ -518,6 +546,9 @@ export async function pushInstanceGroups(
const command = new Command()
.description("user related commands")
.action(list as any)
.command("list", "List the users of the workspace")
.option("--json", "Output as JSON (for piping to jq)")
.action(listWorkspaceUsers as any)
.command("add", "Create a user")
.arguments("<email:string> [password:string]")
.option("--superadmin", "Specify to make the new user superadmin.")
+6
View File
@@ -5969,6 +5969,10 @@ can call one, not an agent used as a tool.
- \`results.step_id.property\` - Access specific property from a previous step output only when that step result is in scope
- \`flow_input.iter.value\` - Current iteration value inside a \`forloopflow\`; in a \`whileloopflow\` it is just the iteration index (a plain number, same as \`flow_input.iter.index\`)
- \`flow_input.iter.index\` - Current loop index when inside a loop (\`forloopflow\` or \`whileloopflow\`)
- After a suspend step (approval), the next step's javascript expressions also get:
- \`resume\` - the last resume payload (the resume form values); with \`continue_on_disapprove_timeout\`, a disapproval or timeout yields \`{ error: { name: "SuspendedDisapproved" | "SuspendedTimedOut", message } }\` instead
- \`resumes\` - every resume payload, in arrival order
- \`approvers\` - a plain array of strings, one per resume: for a resume through the \`resume\`/\`cancel\` URL from \`getResumeUrls(approver)\`, the \`approver\` passed there if any; otherwise (including the \`approvalPage\` link) the resuming user's username, else \`"anonymous"\`, e.g. \`approvers[0]\`. It is NOT the \`{ resume_id, approver }\` objects stored in the flow status, so \`approvers[0].approver\` is always undefined
## Loop Structure Rules
@@ -8711,6 +8715,8 @@ user related commands
**Subcommands:**
- \`user list\` - List the users of the workspace
- \`--json\` - Output as JSON (for piping to jq)
- \`user add <email:string> [password:string]\` - Create a user
- \`--superadmin\` - Specify to make the new user superadmin.
- \`--company <company:string>\` - Specify to set the company of the new user.
@@ -727,6 +727,8 @@ user related commands
**Subcommands:**
- `user list` - List the users of the workspace
- `--json` - Output as JSON (for piping to jq)
- `user add <email:string> [password:string]` - Create a user
- `--superadmin` - Specify to make the new user superadmin.
- `--company <company:string>` - Specify to set the company of the new user.
+4
View File
@@ -284,6 +284,10 @@ can call one, not an agent used as a tool.
- `results.step_id.property` - Access specific property from a previous step output only when that step result is in scope
- `flow_input.iter.value` - Current iteration value inside a `forloopflow`; in a `whileloopflow` it is just the iteration index (a plain number, same as `flow_input.iter.index`)
- `flow_input.iter.index` - Current loop index when inside a loop (`forloopflow` or `whileloopflow`)
- After a suspend step (approval), the next step's javascript expressions also get:
- `resume` - the last resume payload (the resume form values); with `continue_on_disapprove_timeout`, a disapproval or timeout yields `{ error: { name: "SuspendedDisapproved" | "SuspendedTimedOut", message } }` instead
- `resumes` - every resume payload, in arrival order
- `approvers` - a plain array of strings, one per resume: for a resume through the `resume`/`cancel` URL from `getResumeUrls(approver)`, the `approver` passed there if any; otherwise (including the `approvalPage` link) the resuming user's username, else `"anonymous"`, e.g. `approvers[0]`. It is NOT the `{ resume_id, approver }` objects stored in the flow status, so `approvers[0].approver` is always undefined
## Loop Structure Rules
+6
View File
@@ -311,6 +311,10 @@ can call one, not an agent used as a tool.
- \`results.step_id.property\` - Access specific property from a previous step output only when that step result is in scope
- \`flow_input.iter.value\` - Current iteration value inside a \`forloopflow\`; in a \`whileloopflow\` it is just the iteration index (a plain number, same as \`flow_input.iter.index\`)
- \`flow_input.iter.index\` - Current loop index when inside a loop (\`forloopflow\` or \`whileloopflow\`)
- After a suspend step (approval), the next step's javascript expressions also get:
- \`resume\` - the last resume payload (the resume form values); with \`continue_on_disapprove_timeout\`, a disapproval or timeout yields \`{ error: { name: "SuspendedDisapproved" | "SuspendedTimedOut", message } }\` instead
- \`resumes\` - every resume payload, in arrival order
- \`approvers\` - a plain array of strings, one per resume: for a resume through the \`resume\`/\`cancel\` URL from \`getResumeUrls(approver)\`, the \`approver\` passed there if any; otherwise (including the \`approvalPage\` link) the resuming user's username, else \`"anonymous"\`, e.g. \`approvers[0]\`. It is NOT the \`{ resume_id, approver }\` objects stored in the flow status, so \`approvers[0].approver\` is always undefined
## Loop Structure Rules
@@ -4206,6 +4210,8 @@ user related commands
**Subcommands:**
- \`user list\` - List the users of the workspace
- \`--json\` - Output as JSON (for piping to jq)
- \`user add <email:string> [password:string]\` - Create a user
- \`--superadmin\` - Specify to make the new user superadmin.
- \`--company <company:string>\` - Specify to set the company of the new user.
@@ -732,6 +732,8 @@ user related commands
**Subcommands:**
- `user list` - List the users of the workspace
- `--json` - Output as JSON (for piping to jq)
- `user add <email:string> [password:string]` - Create a user
- `--superadmin` - Specify to make the new user superadmin.
- `--company <company:string>` - Specify to set the company of the new user.
@@ -371,6 +371,10 @@ can call one, not an agent used as a tool.
- `results.step_id.property` - Access specific property from a previous step output only when that step result is in scope
- `flow_input.iter.value` - Current iteration value inside a `forloopflow`; in a `whileloopflow` it is just the iteration index (a plain number, same as `flow_input.iter.index`)
- `flow_input.iter.index` - Current loop index when inside a loop (`forloopflow` or `whileloopflow`)
- After a suspend step (approval), the next step's javascript expressions also get:
- `resume` - the last resume payload (the resume form values); with `continue_on_disapprove_timeout`, a disapproval or timeout yields `{ error: { name: "SuspendedDisapproved" | "SuspendedTimedOut", message } }` instead
- `resumes` - every resume payload, in arrival order
- `approvers` - a plain array of strings, one per resume: for a resume through the `resume`/`cancel` URL from `getResumeUrls(approver)`, the `approver` passed there if any; otherwise (including the `approvalPage` link) the resuming user's username, else `"anonymous"`, e.g. `approvers[0]`. It is NOT the `{ resume_id, approver }` objects stored in the flow status, so `approvers[0].approver` is always undefined
## Loop Structure Rules
+4
View File
@@ -289,6 +289,10 @@ can call one, not an agent used as a tool.
- `results.step_id.property` - Access specific property from a previous step output only when that step result is in scope
- `flow_input.iter.value` - Current iteration value inside a `forloopflow`; in a `whileloopflow` it is just the iteration index (a plain number, same as `flow_input.iter.index`)
- `flow_input.iter.index` - Current loop index when inside a loop (`forloopflow` or `whileloopflow`)
- After a suspend step (approval), the next step's javascript expressions also get:
- `resume` - the last resume payload (the resume form values); with `continue_on_disapprove_timeout`, a disapproval or timeout yields `{ error: { name: "SuspendedDisapproved" | "SuspendedTimedOut", message } }` instead
- `resumes` - every resume payload, in arrival order
- `approvers` - a plain array of strings, one per resume: for a resume through the `resume`/`cancel` URL from `getResumeUrls(approver)`, the `approver` passed there if any; otherwise (including the `approvalPage` link) the resuming user's username, else `"anonymous"`, e.g. `approvers[0]`. It is NOT the `{ resume_id, approver }` objects stored in the flow status, so `approvers[0].approver` is always undefined
## Loop Structure Rules