windmill-cli 1.591.3 → 1.592.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/gen/core/OpenAPI.js +1 -1
- package/esm/src/commands/app/app_metadata.js +1 -1
- package/esm/src/commands/app/dev.js +1 -1
- package/esm/src/commands/app/raw_apps.js +1 -1
- package/esm/src/commands/gitsync-settings/gitsync-settings.js +28 -2
- package/esm/src/commands/init/init.js +14 -15
- package/esm/src/commands/sync/sync.js +1 -1
- package/esm/src/core/settings.js +1 -1
- package/esm/src/guidance/flow_guidance.js +10 -429
- package/esm/src/guidance/prompts.js +2620 -0
- package/esm/src/guidance/script_guidance.js +9 -435
- package/esm/src/main.js +3 -3
- package/esm/src/types.js +1 -1
- package/package.json +1 -1
- package/types/src/commands/app/{apps.d.ts → app.d.ts} +1 -1
- package/types/src/commands/app/app.d.ts.map +1 -0
- package/types/src/commands/gitsync-settings/gitsync-settings.d.ts +24 -2
- package/types/src/commands/gitsync-settings/gitsync-settings.d.ts.map +1 -1
- package/types/src/commands/init/init.d.ts.map +1 -1
- package/types/src/commands/worker-groups/{worker_groups.d.ts → worker-groups.d.ts} +1 -1
- package/types/src/commands/worker-groups/{worker_groups.d.ts.map → worker-groups.d.ts.map} +1 -1
- package/types/src/guidance/flow_guidance.d.ts +1 -1
- package/types/src/guidance/flow_guidance.d.ts.map +1 -1
- package/types/src/guidance/prompts.d.ts +4 -0
- package/types/src/guidance/prompts.d.ts.map +1 -0
- package/types/src/guidance/script_guidance.d.ts +1 -1
- package/types/src/guidance/script_guidance.d.ts.map +1 -1
- package/types/src/main.d.ts +2 -2
- package/types/src/main.d.ts.map +1 -1
- package/esm/src/commands/gitsync-settings/index.js +0 -28
- package/types/src/commands/app/apps.d.ts.map +0 -1
- package/types/src/commands/gitsync-settings/index.d.ts +0 -25
- package/types/src/commands/gitsync-settings/index.d.ts.map +0 -1
- /package/esm/src/commands/app/{apps.js → app.js} +0 -0
- /package/esm/src/commands/worker-groups/{worker_groups.js → worker-groups.js} +0 -0
package/esm/gen/core/OpenAPI.js
CHANGED
|
@@ -8,7 +8,7 @@ import { generateHash, getHeaders, writeIfChanged } from "../../utils/utils.js";
|
|
|
8
8
|
import { exts } from "../script/script.js";
|
|
9
9
|
import { FSFSElement, yamlOptions } from "../sync/sync.js";
|
|
10
10
|
import { loadRunnablesFromBackend, writeRunnableToBackend, } from "./raw_apps.js";
|
|
11
|
-
import { replaceInlineScripts } from "./
|
|
11
|
+
import { replaceInlineScripts } from "./app.js";
|
|
12
12
|
import { newPathAssigner, newRawAppPathAssigner, } from "../../../windmill-utils-internal/src/path-utils/path-assigner.js";
|
|
13
13
|
import { mergeConfigWithConfigFile } from "../../core/conf.js";
|
|
14
14
|
import { resolveWorkspace } from "../../core/context.js";
|
|
@@ -13,7 +13,7 @@ import * as wmill from "../../../gen/services.gen.js";
|
|
|
13
13
|
import { resolveWorkspace } from "../../core/context.js";
|
|
14
14
|
import { requireLogin } from "../../core/auth.js";
|
|
15
15
|
import { GLOBAL_CONFIG_OPT } from "../../core/conf.js";
|
|
16
|
-
import { replaceInlineScripts } from "./
|
|
16
|
+
import { replaceInlineScripts } from "./app.js";
|
|
17
17
|
import { APP_BACKEND_FOLDER, inferRunnableSchemaFromFile, } from "./app_metadata.js";
|
|
18
18
|
import { loadRunnablesFromBackend } from "./raw_apps.js";
|
|
19
19
|
const DEFAULT_PORT = 4000;
|
|
@@ -6,7 +6,7 @@ import { colors, log, SEP, windmillUtils, yamlParseFile, yamlStringify, } from "
|
|
|
6
6
|
import * as wmill from "../../../gen/services.gen.js";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { isSuperset } from "../../types.js";
|
|
9
|
-
import { replaceInlineScripts, repopulateFields } from "./
|
|
9
|
+
import { replaceInlineScripts, repopulateFields } from "./app.js";
|
|
10
10
|
import { createBundle, detectFrameworks } from "./bundle.js";
|
|
11
11
|
import { APP_BACKEND_FOLDER } from "./app_metadata.js";
|
|
12
12
|
import { writeIfChanged } from "../../utils/utils.js";
|
|
@@ -1,2 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Command } from "../../../deps.js";
|
|
2
|
+
import { pullGitSyncSettings } from "./pull.js";
|
|
3
|
+
import { pushGitSyncSettings } from "./push.js";
|
|
4
|
+
const command = new Command()
|
|
5
|
+
.description("Manage git-sync settings between local wmill.yaml and Windmill backend")
|
|
6
|
+
.command("pull")
|
|
7
|
+
.description("Pull git-sync settings from Windmill backend to local wmill.yaml")
|
|
8
|
+
.option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)")
|
|
9
|
+
.option("--default", "Write settings to top-level defaults instead of overrides")
|
|
10
|
+
.option("--replace", "Replace existing settings (non-interactive mode)")
|
|
11
|
+
.option("--override", "Add branch-specific override (non-interactive mode)")
|
|
12
|
+
.option("--diff", "Show differences without applying changes")
|
|
13
|
+
.option("--json-output", "Output in JSON format")
|
|
14
|
+
.option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)")
|
|
15
|
+
.option("--yes", "Skip interactive prompts and use default behavior")
|
|
16
|
+
.option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides")
|
|
17
|
+
.action(pullGitSyncSettings)
|
|
18
|
+
.command("push")
|
|
19
|
+
.description("Push git-sync settings from local wmill.yaml to Windmill backend")
|
|
20
|
+
.option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)")
|
|
21
|
+
.option("--diff", "Show what would be pushed without applying changes")
|
|
22
|
+
.option("--json-output", "Output in JSON format")
|
|
23
|
+
.option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)")
|
|
24
|
+
.option("--yes", "Skip interactive prompts and use default behavior")
|
|
25
|
+
.option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides")
|
|
26
|
+
.action(pushGitSyncSettings);
|
|
27
|
+
export { pullGitSyncSettings, pushGitSyncSettings };
|
|
28
|
+
export default command;
|
|
@@ -5,6 +5,7 @@ import { SCRIPT_GUIDANCE } from "../../guidance/script_guidance.js";
|
|
|
5
5
|
import { FLOW_GUIDANCE } from "../../guidance/flow_guidance.js";
|
|
6
6
|
import { getActiveWorkspaceOrFallback } from "../workspace/workspace.js";
|
|
7
7
|
import { generateRTNamespace } from "../resource-type/resource-type.js";
|
|
8
|
+
import { CLI_COMMANDS } from "../../guidance/prompts.js";
|
|
8
9
|
/**
|
|
9
10
|
* Bootstrap a windmill project with a wmill.yaml file
|
|
10
11
|
*/
|
|
@@ -168,20 +169,10 @@ async function initAction(opts) {
|
|
|
168
169
|
try {
|
|
169
170
|
const scriptGuidanceContent = SCRIPT_GUIDANCE;
|
|
170
171
|
const flowGuidanceContent = FLOW_GUIDANCE;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
await dntShim.Deno.writeTextFile(".cursor/rules/script.mdc", scriptGuidanceContent);
|
|
176
|
-
log.info(colors.green("Created .cursor/rules/script.mdc"));
|
|
177
|
-
}
|
|
178
|
-
if (!(await dntShim.Deno.stat(".cursor/rules/flow.mdc").catch(() => null))) {
|
|
179
|
-
await dntShim.Deno.writeTextFile(".cursor/rules/flow.mdc", flowGuidanceContent);
|
|
180
|
-
log.info(colors.green("Created .cursor/rules/flow.mdc"));
|
|
181
|
-
}
|
|
182
|
-
// Create CLAUDE.md file
|
|
183
|
-
if (!(await dntShim.Deno.stat("CLAUDE.md").catch(() => null))) {
|
|
184
|
-
await dntShim.Deno.writeTextFile("CLAUDE.md", `
|
|
172
|
+
const cliCommandsContent = CLI_COMMANDS;
|
|
173
|
+
// Create AGENTS.md file
|
|
174
|
+
if (!(await dntShim.Deno.stat("AGENTS.md").catch(() => null))) {
|
|
175
|
+
await dntShim.Deno.writeTextFile("AGENTS.md", `
|
|
185
176
|
You are a helpful assistant that can help with Windmill scripts and flows creation.
|
|
186
177
|
|
|
187
178
|
## Script Guidance
|
|
@@ -189,7 +180,15 @@ ${scriptGuidanceContent}
|
|
|
189
180
|
|
|
190
181
|
## Flow Guidance
|
|
191
182
|
${flowGuidanceContent}
|
|
192
|
-
|
|
183
|
+
|
|
184
|
+
## CLI Commands
|
|
185
|
+
${cliCommandsContent}
|
|
186
|
+
`);
|
|
187
|
+
log.info(colors.green("Created AGENTS.md"));
|
|
188
|
+
}
|
|
189
|
+
// Create CLAUDE.md file, referencing AGENTS.md
|
|
190
|
+
if (!(await dntShim.Deno.stat("CLAUDE.md").catch(() => null))) {
|
|
191
|
+
await dntShim.Deno.writeTextFile("CLAUDE.md", "Instructions are in @AGENTS.md");
|
|
193
192
|
log.info(colors.green("Created CLAUDE.md"));
|
|
194
193
|
}
|
|
195
194
|
}
|
|
@@ -18,7 +18,7 @@ import { pushResource } from "../resource/resource.js";
|
|
|
18
18
|
import { newPathAssigner, newRawAppPathAssigner, } from "../../../windmill-utils-internal/src/path-utils/path-assigner.js";
|
|
19
19
|
import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../windmill-utils-internal/src/inline-scripts/extractor.js";
|
|
20
20
|
import { generateFlowLockInternal } from "../flow/flow_metadata.js";
|
|
21
|
-
import { isExecutionModeAnonymous } from "../app/
|
|
21
|
+
import { isExecutionModeAnonymous } from "../app/app.js";
|
|
22
22
|
import { APP_BACKEND_FOLDER, generateAppLocksInternal, } from "../app/app_metadata.js";
|
|
23
23
|
// Merge CLI options with effective settings, preserving CLI flags as overrides
|
|
24
24
|
function mergeCliWithEffectiveOptions(cliOpts, effectiveOpts) {
|
package/esm/src/core/settings.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as wmill from "../../gen/services.gen.js";
|
|
|
5
5
|
import { compareInstanceObjects } from "../commands/instance/instance.js";
|
|
6
6
|
import { isSuperset } from "../types.js";
|
|
7
7
|
import { deepEqual } from "../utils/utils.js";
|
|
8
|
-
import { removeWorkerPrefix } from "../commands/worker-groups/
|
|
8
|
+
import { removeWorkerPrefix } from "../commands/worker-groups/worker-groups.js";
|
|
9
9
|
import { decrypt, encrypt } from "../utils/local_encryption.js";
|
|
10
10
|
const INSTANCE_SETTINGS_PATH = "instance_settings.yaml";
|
|
11
11
|
let instanceSettingsPath = INSTANCE_SETTINGS_PATH;
|
|
@@ -1,434 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# System Prompt: OpenFlow Workflow Generator
|
|
7
|
-
|
|
8
|
-
You are an expert at creating OpenFlow YAML specifications for Windmill workflows.
|
|
1
|
+
// CLI Flow Guidance - Uses centralized prompts from system_prompts/
|
|
2
|
+
import * as prompts from "./prompts.js";
|
|
3
|
+
// CLI-specific introduction
|
|
4
|
+
const CLI_INTRO = `You are an expert at creating OpenFlow YAML specifications for Windmill workflows.
|
|
9
5
|
OpenFlow is an open standard for defining workflows as directed acyclic graphs where each node represents a computation step.
|
|
10
|
-
When asked to create a flow, ask the user in which folder he wants to put it if not specified. Then create a new folder in the specified folder, that ends with \`.flow\`. It should contain a \`.yaml\` file that contains the flow definition.
|
|
6
|
+
When asked to create a flow, ask the user in which folder he wants to put it if not specified. Then create a new folder in the specified folder, that ends with \`.flow\`. It should contain a \`.yaml\` file that contains the flow definition.
|
|
11
7
|
For rawscript type module in the flow, the content key should start with "!inline" followed by the path of the script containing the code. It should be put in the same folder as the flow.
|
|
12
8
|
For script type module, path should be the path of the script in the whole repository (not constrained to the flow folder).
|
|
13
|
-
You do not need to create .lock and .yaml files manually. Instead, you should run \`wmill flow generate-locks --yes\` to create them
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Every OpenFlow workflow must follow this root structure:
|
|
19
|
-
|
|
20
|
-
\`\`\`yaml
|
|
21
|
-
summary: "Brief one-line description"
|
|
22
|
-
description: "Optional detailed description"
|
|
23
|
-
value:
|
|
24
|
-
modules: [] # Array of workflow steps
|
|
25
|
-
# Optional properties:
|
|
26
|
-
failure_module: {} # Error handler
|
|
27
|
-
preprocessor_module: {} # Runs before first step
|
|
28
|
-
same_worker: false # Force same worker execution
|
|
29
|
-
concurrent_limit: 0 # Limit concurrent executions
|
|
30
|
-
concurrency_key: "string" # Custom concurrency grouping
|
|
31
|
-
concurrency_time_window_s: 0
|
|
32
|
-
custom_debounce_key: "key"
|
|
33
|
-
debounce_delay_s: 0
|
|
34
|
-
skip_expr: "javascript_expression" # Skip workflow condition
|
|
35
|
-
cache_ttl: 0 # Cache results duration
|
|
36
|
-
priority: 0 # Execution priority
|
|
37
|
-
early_return: "javascript_expression" # Early termination condition
|
|
38
|
-
schema: # JSON Schema for workflow inputs
|
|
39
|
-
type: object
|
|
40
|
-
properties: {}
|
|
41
|
-
required: []
|
|
42
|
-
\`\`\`
|
|
43
|
-
|
|
44
|
-
## Module Types
|
|
45
|
-
|
|
46
|
-
### 1. RawScript (Inline Code)
|
|
47
|
-
\`\`\`yaml
|
|
48
|
-
id: unique_step_id
|
|
49
|
-
value:
|
|
50
|
-
type: rawscript
|
|
51
|
-
content: '!inline inline_script_1.inline_script.ts'
|
|
52
|
-
language: bun|deno|python3|go|bash|powershell|postgresql|mysql|bigquery|snowflake|mssql|oracledb|graphql|nativets|php
|
|
53
|
-
input_transforms:
|
|
54
|
-
param1:
|
|
55
|
-
type: javascript|static
|
|
56
|
-
expr: "flow_input.name" # or for static: value: "fixed_value"
|
|
57
|
-
# Optional properties:
|
|
58
|
-
path: "optional/path"
|
|
59
|
-
lock: "dependency_lock_content"
|
|
60
|
-
tag: "version_tag"
|
|
61
|
-
concurrent_limit: 0
|
|
62
|
-
concurrency_time_window_s: 0
|
|
63
|
-
custom_concurrency_key: "key"
|
|
64
|
-
custom_debounce_key: "key"
|
|
65
|
-
debounce_delay_s: 0
|
|
66
|
-
is_trigger: false
|
|
67
|
-
assets: []
|
|
68
|
-
\`\`\`
|
|
69
|
-
|
|
70
|
-
### 2. PathScript (Reference to Existing Script)
|
|
71
|
-
\`\`\`yaml
|
|
72
|
-
id: step_id
|
|
73
|
-
value:
|
|
74
|
-
type: script
|
|
75
|
-
path: "u/user/script_name" # or "f/folder/script_name" or "hub/script_path"
|
|
76
|
-
input_transforms:
|
|
77
|
-
param_name:
|
|
78
|
-
type: javascript
|
|
79
|
-
expr: "results.previous_step"
|
|
80
|
-
# Optional:
|
|
81
|
-
hash: "specific_version_hash"
|
|
82
|
-
tag_override: "version_tag"
|
|
83
|
-
is_trigger: false
|
|
84
|
-
\`\`\`
|
|
85
|
-
|
|
86
|
-
### 3. PathFlow (Sub-workflow)
|
|
87
|
-
\`\`\`yaml
|
|
88
|
-
id: step_id
|
|
89
|
-
value:
|
|
90
|
-
type: flow
|
|
91
|
-
path: "f/folder/flow_name"
|
|
92
|
-
input_transforms:
|
|
93
|
-
param_name:
|
|
94
|
-
type: static
|
|
95
|
-
value: "fixed_value"
|
|
96
|
-
\`\`\`
|
|
97
|
-
|
|
98
|
-
### 4. ForLoop
|
|
99
|
-
\`\`\`yaml
|
|
100
|
-
id: loop_step
|
|
101
|
-
value:
|
|
102
|
-
type: forloopflow
|
|
103
|
-
iterator:
|
|
104
|
-
type: javascript
|
|
105
|
-
expr: "flow_input.items" # Must evaluate to array
|
|
106
|
-
skip_failures: true|false
|
|
107
|
-
parallel: true|false # Run iterations in parallel
|
|
108
|
-
parallelism: 4 # Max parallel iterations (if parallel: true)
|
|
109
|
-
modules:
|
|
110
|
-
- id: loop_body_step
|
|
111
|
-
value:
|
|
112
|
-
type: rawscript
|
|
113
|
-
content: |
|
|
114
|
-
export async function main(iter: any) {
|
|
115
|
-
// iter.value contains current item
|
|
116
|
-
// iter.index contains current index
|
|
117
|
-
return iter.value;
|
|
118
|
-
}
|
|
119
|
-
language: bun
|
|
120
|
-
input_transforms:
|
|
121
|
-
iter:
|
|
122
|
-
type: javascript
|
|
123
|
-
expr: "flow_input.iter"
|
|
124
|
-
\`\`\`
|
|
125
|
-
|
|
126
|
-
### 5. WhileLoop
|
|
127
|
-
\`\`\`yaml
|
|
128
|
-
id: while_step
|
|
129
|
-
value:
|
|
130
|
-
type: whileloopflow
|
|
131
|
-
skip_failures: false
|
|
132
|
-
parallel: false
|
|
133
|
-
parallelism: 1
|
|
134
|
-
modules:
|
|
135
|
-
- id: condition_check
|
|
136
|
-
value:
|
|
137
|
-
type: rawscript
|
|
138
|
-
content: |
|
|
139
|
-
export async function main() {
|
|
140
|
-
return Math.random() > 0.5; // Continue condition
|
|
141
|
-
}
|
|
142
|
-
language: bun
|
|
143
|
-
input_transforms: {}
|
|
144
|
-
\`\`\`
|
|
145
|
-
|
|
146
|
-
### 6. Conditional Branch (BranchOne)
|
|
147
|
-
\`\`\`yaml
|
|
148
|
-
id: branch_step
|
|
149
|
-
value:
|
|
150
|
-
type: branchone
|
|
151
|
-
branches:
|
|
152
|
-
- summary: "Condition 1"
|
|
153
|
-
expr: "results.previous_step > 10"
|
|
154
|
-
modules:
|
|
155
|
-
- id: branch1_step
|
|
156
|
-
value:
|
|
157
|
-
type: rawscript
|
|
158
|
-
content: "export async function main() { return 'branch1'; }"
|
|
159
|
-
language: bun
|
|
160
|
-
input_transforms: {}
|
|
161
|
-
- summary: "Condition 2"
|
|
162
|
-
expr: "results.previous_step <= 10"
|
|
163
|
-
modules:
|
|
164
|
-
- id: branch2_step
|
|
165
|
-
value:
|
|
166
|
-
type: rawscript
|
|
167
|
-
content: "export async function main() { return 'branch2'; }"
|
|
168
|
-
language: bun
|
|
169
|
-
input_transforms: {}
|
|
170
|
-
default: # Runs if no branch condition matches
|
|
171
|
-
- id: default_step
|
|
172
|
-
value:
|
|
173
|
-
type: rawscript
|
|
174
|
-
content: "export async function main() { return 'default'; }"
|
|
175
|
-
language: bun
|
|
176
|
-
input_transforms: {}
|
|
177
|
-
\`\`\`
|
|
178
|
-
|
|
179
|
-
### 7. Parallel Branches (BranchAll)
|
|
180
|
-
\`\`\`yaml
|
|
181
|
-
id: parallel_step
|
|
182
|
-
value:
|
|
183
|
-
type: branchall
|
|
184
|
-
parallel: true # Run branches in parallel
|
|
185
|
-
branches:
|
|
186
|
-
- summary: "Branch A"
|
|
187
|
-
skip_failure: false # Continue if this branch fails
|
|
188
|
-
modules:
|
|
189
|
-
- id: branch_a_step
|
|
190
|
-
value:
|
|
191
|
-
type: rawscript
|
|
192
|
-
content: "export async function main() { return 'A'; }"
|
|
193
|
-
language: bun
|
|
194
|
-
input_transforms: {}
|
|
195
|
-
- summary: "Branch B"
|
|
196
|
-
skip_failure: true
|
|
197
|
-
modules:
|
|
198
|
-
- id: branch_b_step
|
|
199
|
-
value:
|
|
200
|
-
type: rawscript
|
|
201
|
-
content: "export async function main() { return 'B'; }"
|
|
202
|
-
language: bun
|
|
203
|
-
input_transforms: {}
|
|
204
|
-
\`\`\`
|
|
205
|
-
|
|
206
|
-
### 8. Identity (Pass-through)
|
|
207
|
-
\`\`\`yaml
|
|
208
|
-
id: identity_step
|
|
209
|
-
value:
|
|
210
|
-
type: identity
|
|
211
|
-
flow: false # Set to true if this represents a sub-flow
|
|
212
|
-
\`\`\`
|
|
213
|
-
|
|
214
|
-
## Input Transforms & Data Flow
|
|
215
|
-
|
|
216
|
-
### JavaScript Expressions
|
|
217
|
-
Reference data using these variables in \`expr\` fields:
|
|
218
|
-
- \`flow_input.property_name\` - Access workflow inputs
|
|
219
|
-
- \`results.step_id\` - Access outputs from previous steps
|
|
220
|
-
- \`results.step_id.property\` - Access specific properties
|
|
221
|
-
- \`flow_input.iter.value\` - Current iteration value (in loops)
|
|
222
|
-
- \`flow_input.iter.index\` - Current iteration index (in loops)
|
|
223
|
-
|
|
224
|
-
### Static Values
|
|
225
|
-
\`\`\`yaml
|
|
226
|
-
input_transforms:
|
|
227
|
-
param_name:
|
|
228
|
-
type: static
|
|
229
|
-
value: "fixed_string" # Can be string, number, boolean, object, array
|
|
230
|
-
\`\`\`
|
|
231
|
-
|
|
232
|
-
### Resource References
|
|
233
|
-
\`\`\`yaml
|
|
234
|
-
input_transforms:
|
|
235
|
-
database:
|
|
236
|
-
type: static
|
|
237
|
-
value: "$res:f/folder/my_database" # Reference to stored resource
|
|
238
|
-
\`\`\`
|
|
239
|
-
|
|
240
|
-
## Advanced Module Properties
|
|
241
|
-
|
|
242
|
-
### Error Handling & Control Flow
|
|
243
|
-
\`\`\`yaml
|
|
244
|
-
id: step_id
|
|
245
|
-
value: # ... module definition
|
|
246
|
-
# Control flow options:
|
|
247
|
-
stop_after_if:
|
|
248
|
-
expr: "results.step_id.should_stop"
|
|
249
|
-
skip_if_stopped: true
|
|
250
|
-
error_message: "Custom stop message"
|
|
251
|
-
stop_after_all_iters_if: # For loops only
|
|
252
|
-
expr: "results.step_id.should_stop_loop"
|
|
253
|
-
skip_if_stopped: false
|
|
254
|
-
skip_if:
|
|
255
|
-
expr: "results.step_id.should_skip"
|
|
256
|
-
sleep:
|
|
257
|
-
type: javascript
|
|
258
|
-
expr: "flow_input.delay_seconds"
|
|
259
|
-
continue_on_error: false # Continue workflow if this step fails
|
|
260
|
-
delete_after_use: false # Clean up results after use
|
|
261
|
-
|
|
262
|
-
# Execution control:
|
|
263
|
-
cache_ttl: 3600 # Cache results for 1 hour
|
|
264
|
-
timeout: 300 # Step timeout in seconds
|
|
265
|
-
priority: 0 # Higher numbers = higher priority
|
|
266
|
-
mock:
|
|
267
|
-
enabled: false
|
|
268
|
-
return_value: "mocked_result"
|
|
269
|
-
|
|
270
|
-
# Suspend/Approval:
|
|
271
|
-
suspend:
|
|
272
|
-
required_events: 1 # Number of resume events needed
|
|
273
|
-
timeout: 86400 # Timeout in seconds
|
|
274
|
-
resume_form:
|
|
275
|
-
schema:
|
|
276
|
-
type: object
|
|
277
|
-
properties:
|
|
278
|
-
approved:
|
|
279
|
-
type: boolean
|
|
280
|
-
user_auth_required: true
|
|
281
|
-
user_groups_required:
|
|
282
|
-
type: static
|
|
283
|
-
value: ["admin"]
|
|
284
|
-
self_approval_disabled: false
|
|
285
|
-
hide_cancel: false
|
|
286
|
-
continue_on_disapprove_timeout: false
|
|
287
|
-
|
|
288
|
-
# Retry configuration:
|
|
289
|
-
retry:
|
|
290
|
-
constant:
|
|
291
|
-
attempts: 3
|
|
292
|
-
seconds: 5
|
|
293
|
-
# OR exponential backoff:
|
|
294
|
-
# exponential:
|
|
295
|
-
# attempts: 3
|
|
296
|
-
# multiplier: 2
|
|
297
|
-
# seconds: 1
|
|
298
|
-
# random_factor: 10 # 0-100% jitter
|
|
299
|
-
\`\`\`
|
|
300
|
-
|
|
301
|
-
## Special Modules
|
|
302
|
-
|
|
303
|
-
### Failure Handler (Error Handler)
|
|
304
|
-
\`\`\`yaml
|
|
305
|
-
value:
|
|
306
|
-
failure_module:
|
|
307
|
-
id: failure
|
|
308
|
-
value:
|
|
309
|
-
type: rawscript
|
|
310
|
-
content: |
|
|
311
|
-
export async function main(error: any) {
|
|
312
|
-
// error.message, error.step_id, error.name, error.stack
|
|
313
|
-
console.log("Flow failed:", error.message);
|
|
314
|
-
return error;
|
|
315
|
-
}
|
|
316
|
-
language: bun
|
|
317
|
-
input_transforms: {}
|
|
318
|
-
\`\`\`
|
|
319
|
-
|
|
320
|
-
### Preprocessor
|
|
321
|
-
\`\`\`yaml
|
|
322
|
-
value:
|
|
323
|
-
preprocessor_module:
|
|
324
|
-
id: preprocessor
|
|
325
|
-
value:
|
|
326
|
-
type: rawscript
|
|
327
|
-
content: |
|
|
328
|
-
export async function main() {
|
|
329
|
-
console.log("Flow starting...");
|
|
330
|
-
return "preprocessed";
|
|
331
|
-
}
|
|
332
|
-
language: bun
|
|
333
|
-
input_transforms: {}
|
|
334
|
-
\`\`\`
|
|
335
|
-
|
|
336
|
-
## Schema Definition
|
|
337
|
-
\`\`\`yaml
|
|
338
|
-
schema:
|
|
339
|
-
$schema: "https://json-schema.org/draft/2020-12/schema"
|
|
340
|
-
type: object
|
|
341
|
-
properties:
|
|
342
|
-
name:
|
|
343
|
-
type: string
|
|
344
|
-
description: "User name"
|
|
345
|
-
default: ""
|
|
346
|
-
email:
|
|
347
|
-
type: string
|
|
348
|
-
format: email
|
|
349
|
-
count:
|
|
350
|
-
type: integer
|
|
351
|
-
minimum: 1
|
|
352
|
-
maximum: 100
|
|
353
|
-
database:
|
|
354
|
-
type: object
|
|
355
|
-
format: "resource-postgresql" # Resource type reference
|
|
356
|
-
items:
|
|
357
|
-
type: array
|
|
358
|
-
items:
|
|
359
|
-
type: string
|
|
360
|
-
required: ["name", "email"]
|
|
361
|
-
order: ["name", "email", "count"] # UI field order
|
|
362
|
-
\`\`\`
|
|
363
|
-
|
|
364
|
-
## Best Practices
|
|
365
|
-
|
|
366
|
-
1. **Step IDs**: Use descriptive, unique identifiers (alphanumeric + underscores)
|
|
367
|
-
2. **Data Flow**: Chain steps using \`results.step_id\` references
|
|
368
|
-
3. **Error Handling**: Add failure_module for critical workflows
|
|
369
|
-
4. **Languages**: Use \`bun\` for TypeScript (fastest), \`python3\` for Python
|
|
370
|
-
5. **Resources**: Store credentials/configs as resources, reference with \`$res:path\`
|
|
371
|
-
6. **Loops**: Prefer \`parallel: true\` for independent iterations
|
|
372
|
-
7. **Branching**: Use \`branchone\` for if/else logic, \`branchall\` for parallel processing
|
|
373
|
-
8. **Schemas**: Always define input schemas for better UX and validation
|
|
374
|
-
|
|
375
|
-
## Example Complete Workflow
|
|
376
|
-
\`\`\`yaml
|
|
377
|
-
summary: "Process user data"
|
|
378
|
-
description: "Validates user input, processes data, and sends notifications"
|
|
379
|
-
value:
|
|
380
|
-
modules:
|
|
381
|
-
- id: validate_input
|
|
382
|
-
value:
|
|
383
|
-
type: rawscript
|
|
384
|
-
content: '!inline inline_script_0.inline_script.ts'
|
|
385
|
-
# script at path inline_script_0.inline_script.ts will contain
|
|
386
|
-
# export async function main(email: string, name: string) {
|
|
387
|
-
# if (!email.includes('@')) throw new Error('Invalid email');
|
|
388
|
-
# return { email, name, valid: true };
|
|
389
|
-
# }
|
|
390
|
-
language: bun
|
|
391
|
-
input_transforms:
|
|
392
|
-
email:
|
|
393
|
-
type: javascript
|
|
394
|
-
expr: "flow_input.email"
|
|
395
|
-
name:
|
|
396
|
-
type: javascript
|
|
397
|
-
expr: "flow_input.name"
|
|
398
|
-
- id: process_data
|
|
399
|
-
value:
|
|
400
|
-
type: script
|
|
401
|
-
path: "f/shared/data_processor"
|
|
402
|
-
input_transforms:
|
|
403
|
-
user_data:
|
|
404
|
-
type: javascript
|
|
405
|
-
expr: "results.validate_input"
|
|
406
|
-
- id: send_notification
|
|
407
|
-
value:
|
|
408
|
-
type: rawscript
|
|
409
|
-
content: '!inline inline_script_1.inline_script.ts'
|
|
410
|
-
# script at path inline_script_1.inline_script.ts will contain
|
|
411
|
-
# export async function main(processed_data: any) {
|
|
412
|
-
# console.log("Sending notification for:", processed_data.name);
|
|
413
|
-
# return "notification_sent";
|
|
414
|
-
# }
|
|
415
|
-
language: bun
|
|
416
|
-
input_transforms:
|
|
417
|
-
processed_data:
|
|
418
|
-
type: javascript
|
|
419
|
-
expr: "results.process_data"
|
|
420
|
-
schema:
|
|
421
|
-
type: object
|
|
422
|
-
properties:
|
|
423
|
-
email:
|
|
424
|
-
type: string
|
|
425
|
-
format: email
|
|
426
|
-
description: "User email address"
|
|
427
|
-
name:
|
|
428
|
-
type: string
|
|
429
|
-
description: "User full name"
|
|
430
|
-
required: ["email", "name"]
|
|
431
|
-
\`\`\`
|
|
9
|
+
You do not need to create .lock and .yaml files manually. Instead, you should run \`wmill flow generate-locks --yes\` to create them.`;
|
|
10
|
+
// Assemble complete flow guidance
|
|
11
|
+
export const FLOW_GUIDANCE = `
|
|
12
|
+
${CLI_INTRO}
|
|
432
13
|
|
|
433
|
-
|
|
14
|
+
${prompts.FLOW_PROMPT}
|
|
434
15
|
`;
|