fix: optimize flow lock generation and add rt.d.ts guidance for TS resource types (#8295)

Instruct AI to pass specific flow folder path to `wmill flow generate-locks`
instead of running it on all flows. Also add guidance for TypeScript language
files to check `rt.d.ts` for available resource types before using them.
Re-ran generate.py to propagate changes to all auto-generated files.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
centdix
2026-03-10 14:03:21 +01:00
committed by GitHub
parent cbac81e3a1
commit b40cf80fdd
20 changed files with 4723 additions and 3477 deletions
+2836 -2503
View File
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
The Windmill CLI (`wmill`) provides commands for managing scripts, flows, apps, and other resources.
Current version: 1.642.0
Current version: 1.651.1
## Global Options
@@ -60,6 +60,15 @@ Launch a dev server that will spawn a webserver with HMR
**Options:**
- `--includes <pattern...:string>` - Filter paths givena glob pattern or path
### docs
Search Windmill documentation. Requires Enterprise Edition.
**Arguments:** `<query:string>`
**Options:**
- `--json` - Output results as JSON.
### flow
flow related commands
@@ -108,7 +117,7 @@ folder related commands
- `--json` - Output as JSON (for piping to jq)
- `folder new <name:string>` - create a new folder locally
- `--summary <summary:string>` - folder summary
- `folder push <name:string>` - push a local folder to the remote by name. This overrides any remote versions.
- `folder push <name:string>` - push a local folder to the remote by name. This overrides any remote versions.
- `folder add-missing` - create default folder.meta.yaml for all subdirectories of f/ that are missing one
- `-y, --yes` - skip confirmation prompt
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -17,6 +17,14 @@ properties:
items:
type: string
description: Array of Kafka topic names to subscribe to
filters:
type: array
items:
type: object
properties:
key:
type: string
value: {}
error_handler_path:
type: string
description: Path to a script or flow to run when the triggered job fails
@@ -64,3 +72,4 @@ required:
- kafka_resource_path
- group_id
- topics
- filters
+198 -45
View File
@@ -122,6 +122,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
@@ -234,6 +236,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function:
@@ -387,6 +391,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
@@ -729,6 +735,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function:
@@ -1118,6 +1126,36 @@ SELECT * FROM users WHERE name = ? AND age > ?;
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -1212,13 +1250,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -1490,6 +1521,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -1505,12 +1538,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -1554,35 +1589,65 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
# Python SDK (wmill)
@@ -1875,6 +1940,16 @@ def load_s3_file_reader(s3object: S3Object | str, s3_resource_path: str | None)
# '''
def write_s3_file(s3object: S3Object | str | None, file_content: BufferedReader | bytes, s3_resource_path: str | None, content_type: str | None = None, content_disposition: str | None = None) -> S3Object
# Permanently delete a file from the workspace S3 bucket.
#
# '''python
# from wmill import S3Object
#
# s3_obj = S3Object(s3="/path/to/my_file.txt")
# client.delete_s3_object(s3_obj)
# '''
def delete_s3_object(s3object: S3Object | str, s3_resource_path: str | None = None) -> None
# Sign S3 objects for use by anonymous users in public apps.
#
# Args:
@@ -2093,18 +2168,6 @@ def boto3_connection_settings(s3_resource_path: str = '') -> Boto3ConnectionSett
# State path string
def get_state_path() -> str
# Decorator to mark a function as a workflow task.
#
# When executed inside a Windmill job, the decorated function runs as a
# separate workflow step. Outside Windmill, it executes normally.
#
# Args:
# tag: Optional worker tag for execution
#
# Returns:
# Decorated function
def task(*args, **kwargs)
# Parse resource syntax from string.
def parse_resource_syntax(s: str) -> Optional[str]
@@ -2168,3 +2231,93 @@ def infer_sql_type(value) -> str
def parse_sql_client_name(name: str) -> tuple[str, Optional[str]]
# Decorator that marks a function as a workflow task.
#
# Works in both WAC v1 (sync, HTTP-based dispatch) and WAC v2
# (async, checkpoint/replay) modes:
#
# - **v2 (inside @workflow)**: dispatches as a checkpoint step.
# - **v1 (WM_JOB_ID set, no @workflow)**: dispatches via HTTP API.
# - **Standalone**: executes the function body directly.
#
# Usage::
#
# @task
# async def extract_data(url: str): ...
#
# @task(path="f/external_script", timeout=600, tag="gpu")
# async def run_external(x: int): ...
def task(_func = None, path: Optional[str] = None, tag: Optional[str] = None, timeout: Optional[int] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill script.
#
# Usage::
#
# extract = task_script("f/data/extract", timeout=600)
#
# @workflow
# async def main():
# data = await extract(url="https://...")
def task_script(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill flow.
#
# Usage::
#
# pipeline = task_flow("f/etl/pipeline", priority=10)
#
# @workflow
# async def main():
# result = await pipeline(input=data)
def task_flow(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Decorator marking an async function as a workflow-as-code entry point.
#
# The function must be **deterministic**: given the same inputs it must call
# tasks in the same order on every replay. Branching on task results is fine
# (results are replayed from checkpoint), but branching on external state
# (current time, random values, external API calls) must use ``step()`` to
# checkpoint the value so replays see the same result.
def workflow(func)
# Execute ``fn`` inline and checkpoint the result.
#
# On replay the cached value is returned without re-executing ``fn``.
# Use for lightweight deterministic operations (timestamps, random IDs,
# config reads) that should not incur the overhead of a child job.
async def step(name: str, fn)
# Server-side sleep — suspend the workflow for the given duration without holding a worker.
#
# Inside a @workflow, the parent job suspends and auto-resumes after ``seconds``.
# Outside a workflow, falls back to ``asyncio.sleep``.
async def sleep(seconds: int)
# Suspend the workflow and wait for an external approval.
#
# Use ``get_resume_urls()`` (wrapped in ``step()``) to obtain
# resume/cancel/approval URLs before calling this function.
#
# Returns a dict with ``value`` (form data), ``approver``, and ``approved``.
#
# Example::
#
# urls = await step("urls", lambda: get_resume_urls())
# await step("notify", lambda: send_email(urls["approvalPage"]))
# result = await wait_for_approval(timeout=3600)
async def wait_for_approval(timeout: int = 1800, form: dict | None = None) -> dict
# Process items in parallel with optional concurrency control.
#
# Each item is processed by calling ``fn(item)``, which should be a @task.
# Items are dispatched in batches of ``concurrency`` (default: all at once).
#
# Example::
#
# @task
# async def process(item: str):
# ...
#
# results = await parallel(items, process, concurrency=5)
async def parallel(items, fn, concurrency: Optional[int] = None)
+100 -12
View File
@@ -288,6 +288,16 @@ def load_s3_file_reader(s3object: S3Object | str, s3_resource_path: str | None)
# '''
def write_s3_file(s3object: S3Object | str | None, file_content: BufferedReader | bytes, s3_resource_path: str | None, content_type: str | None = None, content_disposition: str | None = None) -> S3Object
# Permanently delete a file from the workspace S3 bucket.
#
# '''python
# from wmill import S3Object
#
# s3_obj = S3Object(s3="/path/to/my_file.txt")
# client.delete_s3_object(s3_obj)
# '''
def delete_s3_object(s3object: S3Object | str, s3_resource_path: str | None = None) -> None
# Sign S3 objects for use by anonymous users in public apps.
#
# Args:
@@ -506,18 +516,6 @@ def boto3_connection_settings(s3_resource_path: str = '') -> Boto3ConnectionSett
# State path string
def get_state_path() -> str
# Decorator to mark a function as a workflow task.
#
# When executed inside a Windmill job, the decorated function runs as a
# separate workflow step. Outside Windmill, it executes normally.
#
# Args:
# tag: Optional worker tag for execution
#
# Returns:
# Decorated function
def task(*args, **kwargs)
# Parse resource syntax from string.
def parse_resource_syntax(s: str) -> Optional[str]
@@ -581,3 +579,93 @@ def infer_sql_type(value) -> str
def parse_sql_client_name(name: str) -> tuple[str, Optional[str]]
# Decorator that marks a function as a workflow task.
#
# Works in both WAC v1 (sync, HTTP-based dispatch) and WAC v2
# (async, checkpoint/replay) modes:
#
# - **v2 (inside @workflow)**: dispatches as a checkpoint step.
# - **v1 (WM_JOB_ID set, no @workflow)**: dispatches via HTTP API.
# - **Standalone**: executes the function body directly.
#
# Usage::
#
# @task
# async def extract_data(url: str): ...
#
# @task(path="f/external_script", timeout=600, tag="gpu")
# async def run_external(x: int): ...
def task(_func = None, path: Optional[str] = None, tag: Optional[str] = None, timeout: Optional[int] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill script.
#
# Usage::
#
# extract = task_script("f/data/extract", timeout=600)
#
# @workflow
# async def main():
# data = await extract(url="https://...")
def task_script(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill flow.
#
# Usage::
#
# pipeline = task_flow("f/etl/pipeline", priority=10)
#
# @workflow
# async def main():
# result = await pipeline(input=data)
def task_flow(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Decorator marking an async function as a workflow-as-code entry point.
#
# The function must be **deterministic**: given the same inputs it must call
# tasks in the same order on every replay. Branching on task results is fine
# (results are replayed from checkpoint), but branching on external state
# (current time, random values, external API calls) must use ``step()`` to
# checkpoint the value so replays see the same result.
def workflow(func)
# Execute ``fn`` inline and checkpoint the result.
#
# On replay the cached value is returned without re-executing ``fn``.
# Use for lightweight deterministic operations (timestamps, random IDs,
# config reads) that should not incur the overhead of a child job.
async def step(name: str, fn)
# Server-side sleep — suspend the workflow for the given duration without holding a worker.
#
# Inside a @workflow, the parent job suspends and auto-resumes after ``seconds``.
# Outside a workflow, falls back to ``asyncio.sleep``.
async def sleep(seconds: int)
# Suspend the workflow and wait for an external approval.
#
# Use ``get_resume_urls()`` (wrapped in ``step()``) to obtain
# resume/cancel/approval URLs before calling this function.
#
# Returns a dict with ``value`` (form data), ``approver``, and ``approved``.
#
# Example::
#
# urls = await step("urls", lambda: get_resume_urls())
# await step("notify", lambda: send_email(urls["approvalPage"]))
# result = await wait_for_approval(timeout=3600)
async def wait_for_approval(timeout: int = 1800, form: dict | None = None) -> dict
# Process items in parallel with optional concurrency control.
#
# Each item is processed by calling ``fn(item)``, which should be a @task.
# Items are dispatched in batches of ``concurrency`` (default: all at once).
#
# Example::
#
# @task
# async def process(item: str):
# ...
#
# results = await parallel(items, process, concurrency=5)
async def parallel(items, fn, concurrency: Optional[int] = None)
@@ -2,6 +2,36 @@
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -96,13 +126,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -374,6 +397,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -389,12 +414,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -438,32 +465,62 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
@@ -7,7 +7,7 @@ description: MUST use when using the CLI.
The Windmill CLI (`wmill`) provides commands for managing scripts, flows, apps, and other resources.
Current version: 1.642.0
Current version: 1.651.1
## Global Options
@@ -65,6 +65,15 @@ Launch a dev server that will spawn a webserver with HMR
**Options:**
- `--includes <pattern...:string>` - Filter paths givena glob pattern or path
### docs
Search Windmill documentation. Requires Enterprise Edition.
**Arguments:** `<query:string>`
**Options:**
- `--json` - Output results as JSON.
### flow
flow related commands
@@ -113,7 +122,7 @@ folder related commands
- `--json` - Output as JSON (for piping to jq)
- `folder new <name:string>` - create a new folder locally
- `--summary <summary:string>` - folder summary
- `folder push <name:string>` - push a local folder to the remote by name. This overrides any remote versions.
- `folder push <name:string>` - push a local folder to the remote by name. This overrides any remote versions.
- `folder add-missing` - create default folder.meta.yaml for all subdirectories of f/ that are missing one
- `-y, --yes` - skip confirmation prompt
File diff suppressed because one or more lines are too long
@@ -42,6 +42,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
@@ -127,6 +129,36 @@ const result: S3Object = await wmill.writeS3File(
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -221,13 +253,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -499,6 +524,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -514,12 +541,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -563,32 +592,62 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
@@ -42,6 +42,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function:
@@ -125,6 +127,36 @@ const result: S3Object = await wmill.writeS3File(
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -219,13 +251,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -497,6 +522,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -512,12 +539,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -561,32 +590,62 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
@@ -42,6 +42,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
@@ -131,6 +133,36 @@ const result: S3Object = await wmill.writeS3File(
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -225,13 +257,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -503,6 +528,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -518,12 +545,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -567,32 +596,62 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
@@ -42,6 +42,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function:
@@ -92,6 +94,36 @@ export async function preprocessor(event: Event) {
Import: import * as wmill from 'windmill-client'
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
*/
ducklake(name: string = "main"): SqlTemplateFunction
/**
* Initialize the Windmill client with authentication token and base URL
* @param token - Authentication token (defaults to WM_TOKEN env variable)
@@ -186,13 +218,6 @@ async getResult(jobId: string): Promise<any>
*/
async getResultMaybe(jobId: string): Promise<any>
/**
* Wrap a function to execute as a Windmill task within a flow context
* @param f - Function to wrap as a task
* @returns Async wrapper function that executes as a Windmill job
*/
task<P, T>(f: (_: P) => T): (_: P) => Promise<T>
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
@@ -464,6 +489,8 @@ async usernameToEmail(username: string): Promise<string>
* @param {string} [options.approver] - Optional user ID or name of the approver for the request.
* @param {DefaultArgs} [options.defaultArgsJson] - Optional object defining or overriding the default arguments to a form field.
* @param {Enums} [options.dynamicEnumsJson] - Optional object overriding the enum default values of an enum form field.
* @param {string} [options.resumeButtonText] - Optional text for the resume button.
* @param {string} [options.cancelButtonText] - Optional text for the cancel button.
*
* @returns {Promise<void>} Resolves when the Slack approval request is successfully sent.
*
@@ -479,12 +506,14 @@ async usernameToEmail(username: string): Promise<string>
* approver: "approver123",
* defaultArgsJson: { key1: "value1", key2: 42 },
* dynamicEnumsJson: { foo: ["choice1", "choice2"], bar: ["optionA", "optionB"] },
* resumeButtonText: "Resume",
* cancelButtonText: "Cancel",
* });
* ```
*
* **Note:** This function requires execution within a Windmill flow or flow preview.
*/
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, }: SlackApprovalOptions): Promise<void>
async requestInteractiveSlackApproval({ slackResourcePath, channelId, message, approver, defaultArgsJson, dynamicEnumsJson, resumeButtonText, cancelButtonText, }: SlackApprovalOptions): Promise<void>
/**
* Sends an interactive approval request via Teams, allowing optional customization of the message, approver, and form fields.
@@ -528,32 +557,62 @@ async requestInteractiveTeamsApproval({ teamName, channelName, message, approver
*/
parseS3Object(s3Object: S3Object): S3ObjectRecord
/**
* Create a SQL template function for PostgreSQL/datatable queries
* @param name - Database/datatable name (default: "main")
* @returns SQL template function for building parameterized queries
* @example
* let sql = wmill.datatable()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}::int
* `.fetch()
*/
datatable(name: string = "main"): DatatableSqlTemplateFunction
setWorkflowCtx(ctx: WorkflowCtx | null): void
async sleep(seconds: number): Promise<void>
async step<T>(name: string, fn: () => T | Promise<T>): Promise<T>
/**
* Create a SQL template function for DuckDB/ducklake queries
* @param name - DuckDB database name (default: "main")
* @returns SQL template function for building parameterized queries
* Create a task that dispatches to a separate Windmill script.
*
* @example
* let sql = wmill.ducklake()
* let name = 'Robin'
* let age = 21
* await sql`
* SELECT * FROM friends
* WHERE name = ${name} AND age = ${age}
* `.fetch()
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
ducklake(name: string = "main"): SqlTemplateFunction
taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any>
/**
* Mark an async function as a workflow-as-code entry point.
*
* The function must be **deterministic**: given the same inputs it must call
* tasks in the same order on every replay. Branching on task results is fine
* (results are replayed from checkpoint), but branching on external state
* (current time, random values, external API calls) must use `step()` to
* checkpoint the value so replays see the same result.
*/
workflow<T>(fn: (...args: any[]) => Promise<T>): void
/**
* Suspend the workflow and wait for an external approval.
*
* Use `getResumeUrls()` (wrapped in `step()`) to obtain resume/cancel/approvalPage
* URLs before calling this function.
*
* @example
* const urls = await step("urls", () => getResumeUrls());
* await step("notify", () => sendEmail(urls.approvalPage));
* const { value, approver } = await waitForApproval({ timeout: 3600 });
*/
waitForApproval(options?: { timeout?: number; form?: object; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
/**
* Process items in parallel with optional concurrency control.
*
* Each item is processed by calling `fn(item)`, which should be a task().
* Items are dispatched in batches of `concurrency` (default: all at once).
*
* @example
* const process = task(async (item: string) => { ... });
* const results = await parallel(items, process, { concurrency: 5 });
*/
async parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R> | R, options?: { concurrency?: number },): Promise<R[]>
@@ -421,6 +421,16 @@ def load_s3_file_reader(s3object: S3Object | str, s3_resource_path: str | None)
# '''
def write_s3_file(s3object: S3Object | str | None, file_content: BufferedReader | bytes, s3_resource_path: str | None, content_type: str | None = None, content_disposition: str | None = None) -> S3Object
# Permanently delete a file from the workspace S3 bucket.
#
# '''python
# from wmill import S3Object
#
# s3_obj = S3Object(s3="/path/to/my_file.txt")
# client.delete_s3_object(s3_obj)
# '''
def delete_s3_object(s3object: S3Object | str, s3_resource_path: str | None = None) -> None
# Sign S3 objects for use by anonymous users in public apps.
#
# Args:
@@ -639,18 +649,6 @@ def boto3_connection_settings(s3_resource_path: str = '') -> Boto3ConnectionSett
# State path string
def get_state_path() -> str
# Decorator to mark a function as a workflow task.
#
# When executed inside a Windmill job, the decorated function runs as a
# separate workflow step. Outside Windmill, it executes normally.
#
# Args:
# tag: Optional worker tag for execution
#
# Returns:
# Decorated function
def task(*args, **kwargs)
# Parse resource syntax from string.
def parse_resource_syntax(s: str) -> Optional[str]
@@ -714,3 +712,93 @@ def infer_sql_type(value) -> str
def parse_sql_client_name(name: str) -> tuple[str, Optional[str]]
# Decorator that marks a function as a workflow task.
#
# Works in both WAC v1 (sync, HTTP-based dispatch) and WAC v2
# (async, checkpoint/replay) modes:
#
# - **v2 (inside @workflow)**: dispatches as a checkpoint step.
# - **v1 (WM_JOB_ID set, no @workflow)**: dispatches via HTTP API.
# - **Standalone**: executes the function body directly.
#
# Usage::
#
# @task
# async def extract_data(url: str): ...
#
# @task(path="f/external_script", timeout=600, tag="gpu")
# async def run_external(x: int): ...
def task(_func = None, path: Optional[str] = None, tag: Optional[str] = None, timeout: Optional[int] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill script.
#
# Usage::
#
# extract = task_script("f/data/extract", timeout=600)
#
# @workflow
# async def main():
# data = await extract(url="https://...")
def task_script(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Create a task that dispatches to a separate Windmill flow.
#
# Usage::
#
# pipeline = task_flow("f/etl/pipeline", priority=10)
#
# @workflow
# async def main():
# result = await pipeline(input=data)
def task_flow(path: str, timeout: Optional[int] = None, tag: Optional[str] = None, cache_ttl: Optional[int] = None, priority: Optional[int] = None, concurrency_limit: Optional[int] = None, concurrency_key: Optional[str] = None, concurrency_time_window_s: Optional[int] = None)
# Decorator marking an async function as a workflow-as-code entry point.
#
# The function must be **deterministic**: given the same inputs it must call
# tasks in the same order on every replay. Branching on task results is fine
# (results are replayed from checkpoint), but branching on external state
# (current time, random values, external API calls) must use ``step()`` to
# checkpoint the value so replays see the same result.
def workflow(func)
# Execute ``fn`` inline and checkpoint the result.
#
# On replay the cached value is returned without re-executing ``fn``.
# Use for lightweight deterministic operations (timestamps, random IDs,
# config reads) that should not incur the overhead of a child job.
async def step(name: str, fn)
# Server-side sleep — suspend the workflow for the given duration without holding a worker.
#
# Inside a @workflow, the parent job suspends and auto-resumes after ``seconds``.
# Outside a workflow, falls back to ``asyncio.sleep``.
async def sleep(seconds: int)
# Suspend the workflow and wait for an external approval.
#
# Use ``get_resume_urls()`` (wrapped in ``step()``) to obtain
# resume/cancel/approval URLs before calling this function.
#
# Returns a dict with ``value`` (form data), ``approver``, and ``approved``.
#
# Example::
#
# urls = await step("urls", lambda: get_resume_urls())
# await step("notify", lambda: send_email(urls["approvalPage"]))
# result = await wait_for_approval(timeout=3600)
async def wait_for_approval(timeout: int = 1800, form: dict | None = None) -> dict
# Process items in parallel with optional concurrency control.
#
# Each item is processed by calling ``fn(item)``, which should be a @task.
# Items are dispatched in batches of ``concurrency`` (default: all at once).
#
# Example::
#
# @task
# async def process(item: str):
# ...
#
# results = await parallel(items, process, concurrency=5)
async def parallel(items, fn, concurrency: Optional[int] = None)
+1 -1
View File
@@ -5,7 +5,7 @@
Create a folder ending with `.flow` and add a YAML file with the flow definition.
For rawscript modules, use `!inline path/to/script.ts` for the content key.
After writing:
- `wmill flow generate-locks --yes` - Generate lock files
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
- `wmill sync push` - Deploy to Windmill
## OpenFlow Schema
+2
View File
@@ -29,6 +29,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
+2
View File
@@ -29,6 +29,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function:
+2
View File
@@ -29,6 +29,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
```typescript
+2
View File
@@ -29,6 +29,8 @@ export async function main(stripe: RT.Stripe) {
Only use resource types if you need them to satisfy the instructions. Always use the RT namespace.
Before using a resource type, check the `rt.d.ts` file in the project root to see all available resource types and their fields. This file is generated by `wmill resource-type generate-namespace`.
## Imports
**No imports allowed.** Use the globally available `fetch` function: