* feat(cli): improve agent prompts/skills and workspace fork workflow
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): refuse fork --from-branch rename of a base branch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(cli): auto-detect fork branch workflow, drop rt.d.ts refresh and legacy-name warning
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(skills): reconcile raw-app generate-metadata stance (agent offers+runs)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(skills): agent runs all CLI commands, gated on intent not on user typing them
Extends #9467's safe-vs-destructive model: the agent runs consequential commands (sync push, generate-metadata) itself too, gated on explicit user intent rather than handed to the user to type. The explicit-intent rule is the safeguard; an approval prompt is treated as a possible backstop, not assumed (auto-approve/headless runs have none).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "docs(skills): agent runs all CLI commands, gated on intent not on user typing them"
Reverts 9225e1759b. That commit over-reached: #9467 already established the safe-vs-destructive split, and the targeted item-6 fix already removed the passive "tell the user they can run <safe next step>" phrasing. The blanket "agent runs everything" principle pushed deploys to be more eager and carried a wrong "permission layer prompts for approval" claim (untrue in auto-approve/headless mode). Keep deploys conservative.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(cli): default fork workspace name/id to the current branch when renaming it
When 'wmill workspace fork' converts the current working branch into the fork branch, default the fork's name and id to that branch (sanitized to a slug, since branch names can contain '/'). Interactive: the prompt is pre-filled (enter to accept); non-interactive (--yes): used automatically. Adds a unit test for the slug derivation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): address fork review — guard fork-branch rename, cap+validate fork id
Two P2s from review:
- --from-branch refused when the current branch is already a fork branch (would detach the existing fork by renaming its branch).
- fork id slug capped to 42 chars (backend max 50 incl. wm-fork- prefix); auto-derived id is slugged; full id validated client-side before existsWorkspace/datatable cloning so an invalid id fails fast instead of leaving cloned Postgres databases behind.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.5 KiB
TypeScript (Bun Native)
Native TypeScript execution. Native scripts are Bun scripts that run on the native worker — a lightweight V8 isolate that exposes fetch and the JavaScript standard library — and can be heavily parallelized. Every script MUST start with //native on its first line so Windmill routes it to the native worker; without it the exact same script runs on the regular Bun worker. You may import npm packages and other Windmill scripts (e.g. ./helper.ts) — imports are resolved and bundled just like a regular Bun script — as long as everything (your code and its dependencies) relies only on fetch and the standard library. Libraries that need Node/Bun runtime APIs (filesystem, node:* modules, child processes, native addons) will not work on the native worker; use the regular bun language for those.
Structure
Export a single async function called main:
//native
export async function main(param1: string, param2: number) {
// Your code here
return { result: param1, count: param2 };
}
Do not call the main function.
Resource Types
On Windmill, credentials and configuration are stored in resources and passed as parameters to main.
Use the RT namespace for resource types:
//native
export async function main(stripe: RT.Stripe) {
// stripe contains API key and config from the resource
}
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
The constraint is the runtime, not the import list. You may import npm packages and relative Windmill scripts; they are resolved and bundled exactly like a regular Bun script. But the native worker only provides fetch and the JavaScript standard library, so any imported code must work using only those. Anything requiring Node/Bun built-ins (node:fs, child_process, the Bun API, native modules) belongs in a regular bun script instead. Use the globally available fetch for HTTP:
//native
export async function main(url: string) {
const response = await fetch(url);
return await response.json();
}
Windmill Client
windmill-client works on the native worker (its calls go over fetch), so use it as the preferred way to talk to Windmill — reading resources/variables/states, running scripts and flows, and the S3 helpers below (loadS3File, loadS3FileStream, writeS3File, S3Object). It handles auth, the workspace, and the base URL for you. Reserve raw fetch for calling external HTTP APIs that aren't Windmill.
The full windmill-client API reference (every exported function and its signature) is included in this skill below — consult it for the exact method instead of hand-rolling a fetch against the Windmill API.
Preprocessor Scripts
For preprocessor scripts, the function should be named preprocessor and receives an event parameter:
//native
type Event = {
kind:
| "webhook"
| "http"
| "websocket"
| "kafka"
| "email"
| "nats"
| "postgres"
| "sqs"
| "mqtt"
| "gcp";
body: any;
headers: Record<string, string>;
query: Record<string, string>;
};
export async function preprocessor(event: Event) {
return {
param1: event.body.field1,
param2: event.query.id,
};
}
S3 Object Operations
Windmill provides built-in support for S3-compatible storage operations. The wmill.S3Object type covers both the s3://storage/key URI form (s3:///key for the workspace default storage) and the { s3, storage? } record form — always use it instead of redefining your own.
Receiving an S3Object as a script parameter
//native
import * as wmill from "windmill-client";
export async function main(file: wmill.S3Object) {
const content = await wmill.loadS3File(file);
// ...
}
S3 operations
//native
import * as wmill from "windmill-client";
// Load file content from S3
const content: Uint8Array = await wmill.loadS3File(s3object);
// Load file as stream
const blob: Blob = await wmill.loadS3FileStream(s3object);
// Write file to S3
const result: wmill.S3Object = await wmill.writeS3File(
s3object, // Target path (or undefined to auto-generate)
fileContent, // string or Blob
s3ResourcePath // Optional: specific S3 resource to use
);