feat: guide the AI when no resource type fits a service

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-10-08 10:29:52 +02:00
co-authored by Claude Opus 5.5
parent 3f3b1c2c30
commit bdb5d43425
37 changed files with 382 additions and 36 deletions
+68
View File
@@ -11,6 +11,7 @@ import type {
ListableResource,
ListableVariable,
Resource,
ResourceType,
Script
} from '../../../frontend/src/lib/gen'
import type {
@@ -98,6 +99,51 @@ export interface BenchmarkWorkspaceResource {
description?: string
}
/** A resource type the instance holds. Only a seeded one has a schema to look up by name. */
export interface BenchmarkWorkspaceResourceType {
name: string
schema?: unknown
description?: string
}
/** Names a hub-synced instance holds, for cases that seed no resource types. Their schemas are
* not modelled, so a lookup by name misses them, as the similarity search does. */
const SYNCED_RESOURCE_TYPE_NAMES = [
'ai_instruction',
'ai_skill',
'airtable',
'asana',
'aws',
'azure_blob',
'clickup',
'discord_webhook',
'gcal',
'gcp_service_account',
'github',
'gitlab',
'gmail',
'gsheets',
'hubspot',
'jira',
'linear',
'mailchimp',
'mongodb',
'mysql',
'notion',
'openai',
'postgresql',
's3',
'salesforce',
'sendgrid',
'shopify',
'slack',
'smtp',
'stripe',
'supabase',
'twilio',
'zendesk'
]
export interface BenchmarkWorkspaceJob {
/** Stable id so a case prompt can reference a specific run (e.g. for get_run). */
id?: string
@@ -118,6 +164,8 @@ export interface BenchmarkWorkspaceRunnables {
variables?: BenchmarkWorkspaceVariable[]
aiProviders?: BenchmarkWorkspaceAiProvider[]
resources?: BenchmarkWorkspaceResource[]
/** Unset means an instance synced with the hub. */
resourceTypes?: BenchmarkWorkspaceResourceType[]
datatables?: BenchmarkDatatableSeed[]
/** DuckLake catalog names, as `list_ducklakes` reports them. */
ducklakes?: string[]
@@ -328,6 +376,26 @@ export function listBenchmarkPlainResources(workspace: string): ListableResource
}))
}
/** Resource type names of a benchmark workspace, as `ResourceService.listResourceTypeNames`
* returns them. Null when the workspace is not a benchmark one. */
export function listBenchmarkResourceTypeNames(workspace: string): string[] | null {
if (!benchmarkWorkspaces.has(workspace)) {
return null
}
const seeded = benchmarkWorkspaceRunnables.get(workspace)?.resourceTypes
return seeded ? seeded.map((rt) => rt.name) : SYNCED_RESOURCE_TYPE_NAMES
}
/** A seeded resource type, as `ResourceService.getResourceType` returns it. */
export function getBenchmarkResourceType(workspace: string, name: string): ResourceType | null {
const seed = benchmarkWorkspaceRunnables
.get(workspace)
?.resourceTypes?.find((rt) => rt.name === name && rt.schema !== undefined)
return seed
? { workspace_id: 'admins', name: seed.name, schema: seed.schema, description: seed.description }
: null
}
/** A seeded resource with its value, as `ResourceService.getResource` returns it. Covers both
* seed kinds, so it agrees with `existsResource` and `listResource` — both of those report AI
* providers too, and a case that lists resources and then reads one by path would otherwise get
@@ -104,8 +104,10 @@ vi.mock('$lib/gen', async () => {
getBenchmarkVariableByPath,
hasBenchmarkWorkspace,
getBenchmarkResource,
getBenchmarkResourceType,
listBenchmarkAiProviderResources,
listBenchmarkPlainResources,
listBenchmarkResourceTypeNames,
listBenchmarkApps,
listBenchmarkDatatables,
listBenchmarkDataMetrics,
@@ -463,7 +465,20 @@ vi.mock('$lib/gen', async () => {
return value
},
queryResourceTypes: async (data: { workspace: string }) =>
hasBenchmarkWorkspace(data.workspace) ? [] : actual.ResourceService.queryResourceTypes(data)
hasBenchmarkWorkspace(data.workspace) ? [] : actual.ResourceService.queryResourceTypes(data),
listResourceTypeNames: async (data: { workspace: string }) =>
listBenchmarkResourceTypeNames(data.workspace) ??
actual.ResourceService.listResourceTypeNames(data),
getResourceType: async (data: { workspace: string; path: string }) => {
if (!hasBenchmarkWorkspace(data.workspace)) {
return actual.ResourceService.getResourceType(data)
}
const resourceType = getBenchmarkResourceType(data.workspace, data.path)
if (!resourceType) {
throw new Error(`Resource type "${data.path}" not found in benchmark workspace`)
}
return resourceType
}
}),
VariableService: wrapService(actual.VariableService, {
existsVariable: async (data: { workspace: string; path: string }) =>
+52
View File
@@ -759,6 +759,58 @@
- keeps both drafts under a sensible eval/global Slack-related path
- does not create schedules, triggers, or deployed workspace changes
# The instance never synced the hub's resource types, so the one Confluence takes is missing:
# the script's connection picker stays empty until a superadmin syncs, which only the
# assistant's reply can tell the user.
- id: global-resource-type-hub-not-synced
prompt: |-
Write a script that creates a page in our Confluence space from a space key, a title and the page body.
initial: ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json
runtime:
maxTurns: 10
validate:
requiredDrafts:
- type: script
valueExcludes:
- getVariable
- get_variable
toolExpect:
forbiddenToolsUsed:
- write_variable
- deploy_workspace_item
assistantExpect:
requiredMentionsAnyOf:
- - sync
judgeChecklist:
- the script takes the Confluence connection as one resource-typed parameter rather than separate string credentials or a variable read inside the code
- the script creates a Confluence page from a space key, a title and a body
# An in-house API has no hub resource type, so its connection needs a type of the workspace's
# own: a token parameter or a variable read inside the code is the fallback the guidance rules out.
- id: global-resource-type-custom-api
prompt: |-
Write a script that fetches an invoice by id from our internal billing API at https://billing.acme.internal/api/v1. It authenticates with a bearer token.
runtime:
maxTurns: 10
userAnswers:
- Yes, use a resource type
validate:
requiredDrafts:
- type: script
valueExcludes:
- getVariable
- get_variable
toolExpect:
forbiddenToolsUsed:
- write_variable
- deploy_workspace_item
assistantExpect:
requiredMentionsAnyOf:
- - resource type
judgeChecklist:
- the script takes the billing API's base URL and token together as one resource-typed parameter, not a plain string token or a variable read inside the code
- the script fetches an invoice by id with a bearer token
- id: global-test19-datatable-not-configured-asks-to-set-up
prompt: |-
Here are two newsletter signups: alice@example.com and bob@example.com.
@@ -0,0 +1,5 @@
{
"workspace": {
"resourceTypes": [{ "name": "ai_skill" }, { "name": "ai_instruction" }]
}
}
@@ -91,6 +91,19 @@ async function push(opts: PushOptions, filePath: string, name: string) {
log.info(colors.bold.underline.green("Resource pushed"));
}
// The hub publishes hundreds of resource types and an instance seeds only a few of its own,
// so fewer than this means the instance never synced with the hub.
const UNSYNCED_RESOURCE_TYPE_COUNT = 20;
function warnIfNotSyncedWithHub(resourceTypes: ResourceType[]) {
const count = new Set(resourceTypes.map((rt) => rt.name)).size;
if (count < UNSYNCED_RESOURCE_TYPE_COUNT) {
log.warnAlways(
`Only ${count} resource types exist on this instance, so it has most likely never synced with the Windmill Hub, which has types for most services. A superadmin can sync resource types from the hub with "Sync resource types with Hub" in the Add resource dialog of the Resources page, or by running the u/admin/hub_sync script in the admins workspace.`
);
}
}
async function list(opts: GlobalOptions & { schema?: boolean; json?: boolean }) {
if (opts.json) log.setSilent(true);
const workspace = await resolveWorkspace(opts);
@@ -99,12 +112,9 @@ async function list(opts: GlobalOptions & { schema?: boolean; json?: boolean })
workspace: workspace.workspaceId,
});
warnIfNotSyncedWithHub(res);
if (opts.json) {
console.log(JSON.stringify(res));
} else if (res.length === 0) {
log.info("No custom resource types found in this workspace.");
log.info("Built-in types like 'postgresql', 'slack', 'mysql', etc. are available from the Windmill Hub.");
return;
} else if (opts.schema) {
new Table()
.header(["Workspace", "Name", "Schema"])
@@ -172,6 +182,7 @@ export async function generateRTNamespace(opts: GlobalOptions) {
const rts = await wmill.listResourceType({
workspace: workspace.workspaceId,
});
warnIfNotSyncedWithHub(rts);
let namespaceContent = "declare namespace RT {\n";
namespaceContent += rts
+28
View File
@@ -95,6 +95,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -259,6 +260,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -383,6 +385,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -500,6 +503,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -1323,6 +1327,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -2158,6 +2163,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -2274,6 +2280,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3099,6 +3106,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3249,6 +3257,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3382,6 +3391,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3502,6 +3512,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3615,6 +3626,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3731,6 +3743,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3848,6 +3861,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -3980,6 +3994,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -4095,6 +4110,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -4225,6 +4241,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -5234,6 +5251,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -5394,6 +5412,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -5544,6 +5563,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -7178,6 +7198,14 @@ The prefix must be on the string itself.
}
\`\`\`
## When No Resource Type Fits
The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service:
- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two.
- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields.
- Use a plain variable instead of a resource only when the user prefers it.
## Custom Resource Types
Create custom resource types with JSON Schema:
+1 -4
View File
@@ -139,10 +139,7 @@ describe("resource-type commands", () => {
);
expect(result.code).toEqual(0);
// When empty, shows helpful message; when populated, shows table with Name header
const hasTable = result.stdout.includes("Name");
const hasEmptyMessage = result.stdout.includes("No custom resource types");
expect(hasTable || hasEmptyMessage).toBe(true);
expect(result.stdout).toContain("Name");
});
});
@@ -104,7 +104,7 @@ import {
createInlineScriptSession,
findUnresolvedInlineScriptRefs
} from '../flow/inlineScriptsUtils'
import { searchNpmPackagesTool } from '../script/core'
import { searchNpmPackagesTool, searchResourceTypes } from '../script/core'
import type { McpServer } from './mcpTools'
import { logFeatureUsage } from '$lib/utils/featureUsage'
import { isSkillEnabled } from '../skills/enabledSkills'
@@ -4283,19 +4283,16 @@ export const globalTools: SessionTool<{}>[] = [
toolCallbacks.setToolStatus(toolId, {
content: `Searching resource types for "${parsed.query}"...`
})
const results = await ResourceService.queryResourceTypes({
const { resourceTypes, hubSyncNote } = await searchResourceTypes(
parsed.query,
workspace,
text: parsed.query,
limit: parsed.limit ?? 5
})
parsed.limit ?? 5
)
toolCallbacks.setToolStatus(toolId, {
content: `Found ${results.length} resource type(s) for "${parsed.query}"`
content: `Found ${resourceTypes.length} resource type(s) for "${parsed.query}"`
})
return JSON.stringify(
results.map((rt) => ({
name: rt.name,
schema: rt.schema
})),
hubSyncNote ? { resource_types: resourceTypes, note: hubSyncNote } : resourceTypes,
null,
2
)
@@ -14,7 +14,40 @@ vi.mock('@codingame/monaco-vscode-standalone-typescript-language-features', () =
vi.mock('@codingame/monaco-vscode-languages-service-override', () => ({ default: () => ({}) }))
vi.mock('$lib/components/vscode', () => ({}))
import { editCodeToolWithDiff } from './core'
import { ResourceService } from '$lib/gen'
import { editCodeToolWithDiff, searchResourceTypes } from './core'
describe('searchResourceTypes', () => {
const syncedNames = Array.from({ length: 30 }, (_, i) => `type_${i}`)
// The similarity index trails the table by up to a day, and builds without embeddings
// have none, so a type the query names must still be found.
it('finds a type the query names when the similarity search misses it', async () => {
vi.spyOn(ResourceService, 'queryResourceTypes').mockResolvedValue([])
vi.spyOn(ResourceService, 'listResourceTypeNames').mockResolvedValue([...syncedNames, 'stripe'])
vi.spyOn(ResourceService, 'getResourceType').mockResolvedValue({
name: 'stripe',
schema: { type: 'object' }
})
const { resourceTypes, hubSyncNote } = await searchResourceTypes('Stripe payments', 'ws', 5)
expect(resourceTypes.map((rt) => rt.name)).toEqual(['stripe'])
expect(hubSyncNote).toBeUndefined()
})
it('notes an instance that never synced with the hub', async () => {
vi.spyOn(ResourceService, 'queryResourceTypes').mockResolvedValue([])
vi.spyOn(ResourceService, 'listResourceTypeNames').mockResolvedValue([
'ai_skill',
'ai_instruction'
])
const { hubSyncNote } = await searchResourceTypes('stripe', 'ws', 5)
expect(hubSyncNote).toContain('never synced')
})
})
describe('editCodeToolWithDiff', () => {
it('records the complete script diff after applying replacements', async () => {
@@ -19,7 +19,8 @@ import {
type ScriptLintResult,
formatScriptLintResult,
createSearchWorkspaceTool,
createGetRunnableDetailsTool
createGetRunnableDetailsTool,
HUB_SYNC_INSTRUCTIONS
} from '../shared'
import { createWorkspaceMutationTools } from '../workspaceTools'
import { setupTypeAcquisition, type DepsToGet } from '$lib/ata'
@@ -41,7 +42,7 @@ const TYPES_CONTEXT_PERCENTAGE = 1
export const DIFF_BASED_EDIT_PROVIDERS: AIProvider[] = []
export function formatResourceTypes(
allResourceTypes: ResourceType[],
allResourceTypes: Pick<ResourceType, 'name' | 'schema'>[],
lang: 'python3' | 'php' | 'bun' | 'deno' | 'nativets' | 'bunnative'
) {
if (lang === 'python3') {
@@ -67,14 +68,52 @@ export function formatResourceTypes(
}
}
async function getResourceTypes(prompt: string, workspace: string) {
const resourceTypes = await ResourceService.queryResourceTypes({
workspace: workspace,
text: prompt,
limit: 5
})
// The hub publishes hundreds of resource types and an instance seeds only a few of its own,
// so fewer than this means the instance never synced with the hub.
const UNSYNCED_RESOURCE_TYPE_COUNT = 20
return resourceTypes
/**
* Similarity matches, led by any resource type the query names exactly: the similarity index
* is rebuilt from the table once a day, and builds without embeddings have no index at all.
*/
export async function searchResourceTypes(
query: string,
workspace: string,
limit: number
): Promise<{ resourceTypes: Pick<ResourceType, 'name' | 'schema'>[]; hubSyncNote?: string }> {
const [similar, names] = await Promise.all([
ResourceService.queryResourceTypes({ workspace, text: query, limit }).catch(() => []),
ResourceService.listResourceTypeNames({ workspace })
.then((listed) => new Set(listed))
.catch(() => undefined)
])
const normalized = query.trim().toLowerCase()
const candidates = new Set([
normalized.replace(/[\s-]+/g, '_'),
...normalized.split(/[^a-z0-9_]+/)
])
const exactNames = [...candidates].filter(
(name) => names?.has(name) && !similar.some((rt) => rt.name === name)
)
const exact = await Promise.all(
exactNames
.slice(0, limit)
.map((name) =>
ResourceService.getResourceType({ workspace, path: name }).catch(() => undefined)
)
)
const resourceTypes = [
...exact.flatMap((rt) => (rt ? [{ name: rt.name, schema: rt.schema }] : [])),
...similar
].slice(0, limit)
const hubSyncNote =
names && names.size < UNSYNCED_RESOURCE_TYPE_COUNT
? `Only ${names.size} resource types exist on this instance, so it has most likely never synced with the Windmill Hub, which has types for most services. ${HUB_SYNC_INSTRUCTIONS}`
: undefined
return { resourceTypes, hubSyncNote }
}
export const SUPPORTED_CHAT_SCRIPT_LANGUAGES = [
@@ -149,13 +188,14 @@ export async function getFormattedResourceTypes(
case 'bunnative':
case 'python3':
case 'php': {
const resourceTypes = await getResourceTypes(prompt, workspace)
const { resourceTypes, hubSyncNote } = await searchResourceTypes(prompt, workspace, 5)
const intro = `RESOURCE_TYPES:\n`
const found =
resourceTypes.length > 0
? `RESOURCE_TYPES:\n${formatResourceTypes(resourceTypes, lang)}`
: `No resource type matches "${prompt}".`
const resourceTypesText = formatResourceTypes(resourceTypes, lang)
return intro + resourceTypesText
return hubSyncNote ? `${found}\n\n${hubSyncNote}` : found
}
default:
return ''
@@ -57,7 +57,8 @@ vi.mock('$lib/gen', () => ({
EmailTriggerService: { createEmailTrigger: vi.fn() },
SettingService: { getGlobal: vi.fn() },
ResourceService: {
getResourceValue: vi.fn(async ({ path }: { path: string }) => ({ content: `body of ${path}` }))
getResourceValue: vi.fn(async ({ path }: { path: string }) => ({ content: `body of ${path}` })),
listResourceTypeNames: vi.fn(async () => ['confluence'])
}
}))
@@ -2270,6 +2271,29 @@ describe('getHubIntegrationTool', () => {
expect(!!parsed.scripts_note).toBe(expected)
})
// Without the note the model writes against a type the instance lacks, and the
// script's resource picker stays empty until someone syncs.
it.each([
[['confluence'], false],
[['ai_skill'], true]
])('flags hub resource types the instance lacks (%s)', async (present, flagged) => {
const { IntegrationService, ResourceService } = await import('$lib/gen')
Object.assign(IntegrationService, { getHubIntegrationMeta: vi.fn(async () => doc) })
vi.mocked(ResourceService.listResourceTypeNames).mockResolvedValueOnce(present)
const { getHubIntegrationTool } = await import('./shared')
const parsed = JSON.parse(
await getHubIntegrationTool.fn({
args: { integration: 'confluence' },
workspace: 'ws',
toolId: 't1',
toolCallbacks: { setToolStatus: vi.fn() }
} as any)
)
expect(parsed.resource_types_note?.includes('confluence') ?? false).toBe(flagged)
})
// A hub that times out has said nothing about whether the integration exists, and
// reporting it as absent would stick for the rest of the conversation.
it('does not report a transient hub failure as a missing integration', async () => {
@@ -49,6 +49,7 @@ import {
FlowService,
IntegrationService,
JobService,
ResourceService,
type Job,
type CompletedJob,
type FlowValue,
@@ -1710,12 +1711,31 @@ function parseResourceTypeSchema(schema: unknown): unknown {
}
}
export const HUB_SYNC_INSTRUCTIONS =
'A superadmin can sync resource types from the hub with "Sync resource types with Hub" in the Add resource dialog of the Resources page, or by running the u/admin/hub_sync script in the admins workspace.'
async function missingResourceTypesNote(
workspace: string,
names: string[]
): Promise<string | undefined> {
if (names.length === 0) {
return undefined
}
const present = await ResourceService.listResourceTypeNames({ workspace })
.then((listed) => new Set(listed))
.catch(() => undefined)
const missing = present ? names.filter((name) => !present.has(name)) : []
return missing.length > 0
? `This instance does not have the ${missing.join(', ')} resource type${missing.length > 1 ? 's' : ''} yet. ${HUB_SYNC_INSTRUCTIONS}`
: undefined
}
export const getHubIntegrationTool = {
requires: NONE,
def: getHubIntegrationToolDef,
// Reads one hub document over a GET and writes nothing, so planning may use it.
planModeSafe: true,
fn: async ({ args, toolId, toolCallbacks }) => {
fn: async ({ args, workspace, toolId, toolCallbacks }) => {
const { integration } = getHubIntegrationSchema.parse(args)
toolCallbacks.setToolStatus(toolId, { content: `Reading the ${integration} integration...` })
@@ -1745,6 +1765,10 @@ export const getHubIntegrationTool = {
// every section is read as optional: a hub that sends less should return less,
// not fail the call.
const derived = doc.derived
const resourceTypesNote = await missingResourceTypesNote(
workspace,
(doc.resource_types ?? []).map((rt) => rt.name)
)
return JSON.stringify({
integration: doc.app,
display_name: doc.display_name,
@@ -1778,6 +1802,7 @@ export const getHubIntegrationTool = {
...(rt.description ? { description: rt.description } : {}),
schema: parseResourceTypeSchema(rt.schema)
})),
...(resourceTypesNote ? { resource_types_note: resourceTypesNote } : {}),
example_scripts: (derived?.top_scripts ?? []).slice(0, MAX_INTEGRATION_EXAMPLES).map((s) => ({
path: s.path,
summary: s.summary,
+10
View File
@@ -5,6 +5,7 @@ export const SCRIPT_BASE = `# Windmill Script Writing Guide
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments
- Where the language has a Windmill client (\`wmill\`), use it to interact with the platform
@@ -827,6 +828,15 @@ The prefix must be on the string itself.
}
\`\`\`
## When No Resource Type Fits
The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service:
- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two.
- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields.
- You cannot create a resource type yourself: give the user the schema to add with **Add resource type** on the Resources page.
- Use a plain variable instead of a resource only when the user prefers it.
## Custom Resource Types
Create custom resource types with JSON Schema:
+1
View File
@@ -3,6 +3,7 @@
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -214,6 +214,14 @@ The prefix must be on the string itself.
}
```
## When No Resource Type Fits
The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service:
- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two.
- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields.
- Use a plain variable instead of a resource only when the user prefers it.
## Custom Resource Types
Create custom resource types with JSON Schema:
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
@@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types.
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform
+11
View File
@@ -213,6 +213,17 @@ The prefix must be on the string itself.
}
```
## When No Resource Type Fits
The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service:
- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two.
- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields.
<!-- chat-only -->
- You cannot create a resource type yourself: give the user the schema to add with **Add resource type** on the Resources page.
<!-- /chat-only -->
- Use a plain variable instead of a resource only when the user prefers it.
## Custom Resource Types
Create custom resource types with JSON Schema:
+1
View File
@@ -3,6 +3,7 @@
## General Principles
- A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters
- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records
- Libraries are installed automatically - do not show installation instructions
- In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments
- Where the language has a Windmill client (`wmill`), use it to interact with the platform