@picahq/cli 1.11.1 → 1.12.1
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/README.md +13 -12
- package/dist/index.js +141 -86
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ one flow validate welcome-customer
|
|
|
76
76
|
one flow execute welcome-customer -i email=jane@example.com
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
Workflows are stored as JSON at `.one/flows/<key>.flow.json` and support conditions, loops, parallel steps, transforms, and more. Run `one guide flows` for the full reference.
|
|
80
80
|
|
|
81
81
|
## How it works
|
|
82
82
|
|
|
@@ -201,6 +201,7 @@ one actions execute stripe <actionId> <connectionKey> \
|
|
|
201
201
|
| `--headers <json>` | Additional request headers |
|
|
202
202
|
| `--form-data` | Send as multipart/form-data |
|
|
203
203
|
| `--form-url-encoded` | Send as application/x-www-form-urlencoded |
|
|
204
|
+
| `--dry-run` | Show the request without executing it |
|
|
204
205
|
|
|
205
206
|
### `one guide [topic]`
|
|
206
207
|
|
|
@@ -222,7 +223,7 @@ In agent mode (`--agent`), the JSON response includes the guide content and an `
|
|
|
222
223
|
|
|
223
224
|
### `one flow create [key]`
|
|
224
225
|
|
|
225
|
-
Create a
|
|
226
|
+
Create a workflow from a JSON definition. Workflows are saved to `.one/flows/<key>.flow.json`.
|
|
226
227
|
|
|
227
228
|
```bash
|
|
228
229
|
# From a --definition flag
|
|
@@ -237,12 +238,12 @@ one flow create my-flow --definition '...' -o ./custom/path.json
|
|
|
237
238
|
|
|
238
239
|
| Option | What it does |
|
|
239
240
|
|--------|-------------|
|
|
240
|
-
| `--definition <json>` |
|
|
241
|
+
| `--definition <json>` | Workflow definition as a JSON string |
|
|
241
242
|
| `-o, --output <path>` | Custom output path (default: `.one/flows/<key>.flow.json`) |
|
|
242
243
|
|
|
243
244
|
### `one flow execute <key>`
|
|
244
245
|
|
|
245
|
-
Execute a
|
|
246
|
+
Execute a workflow by key or file path. Pass inputs with repeatable `-i` flags.
|
|
246
247
|
|
|
247
248
|
```bash
|
|
248
249
|
# Execute with inputs
|
|
@@ -256,7 +257,7 @@ one flow execute welcome-customer --dry-run -i customerEmail=jane@example.com
|
|
|
256
257
|
one flow execute welcome-customer -v -i customerEmail=jane@example.com
|
|
257
258
|
```
|
|
258
259
|
|
|
259
|
-
Connection inputs with a `connection` field in the
|
|
260
|
+
Connection inputs with a `connection` field in the workflow definition are auto-resolved when the user has exactly one connection for that platform.
|
|
260
261
|
|
|
261
262
|
Press Ctrl+C during execution to pause — the run can be resumed later with `one flow resume <runId>`.
|
|
262
263
|
|
|
@@ -268,7 +269,7 @@ Press Ctrl+C during execution to pause — the run can be resumed later with `on
|
|
|
268
269
|
|
|
269
270
|
### `one flow list`
|
|
270
271
|
|
|
271
|
-
List all
|
|
272
|
+
List all workflows saved in `.one/flows/`.
|
|
272
273
|
|
|
273
274
|
```bash
|
|
274
275
|
one flow list
|
|
@@ -276,7 +277,7 @@ one flow list
|
|
|
276
277
|
|
|
277
278
|
### `one flow validate <key>`
|
|
278
279
|
|
|
279
|
-
Validate a
|
|
280
|
+
Validate a workflow JSON file against the schema.
|
|
280
281
|
|
|
281
282
|
```bash
|
|
282
283
|
one flow validate welcome-customer
|
|
@@ -284,7 +285,7 @@ one flow validate welcome-customer
|
|
|
284
285
|
|
|
285
286
|
### `one flow resume <runId>`
|
|
286
287
|
|
|
287
|
-
Resume a paused or failed
|
|
288
|
+
Resume a paused or failed workflow run from where it left off.
|
|
288
289
|
|
|
289
290
|
```bash
|
|
290
291
|
one flow resume abc123
|
|
@@ -292,11 +293,11 @@ one flow resume abc123
|
|
|
292
293
|
|
|
293
294
|
### `one flow runs [flowKey]`
|
|
294
295
|
|
|
295
|
-
List
|
|
296
|
+
List workflow runs, optionally filtered by workflow key.
|
|
296
297
|
|
|
297
298
|
```bash
|
|
298
299
|
one flow runs # all runs
|
|
299
|
-
one flow runs welcome-customer # runs for a specific
|
|
300
|
+
one flow runs welcome-customer # runs for a specific workflow
|
|
300
301
|
```
|
|
301
302
|
|
|
302
303
|
### `one config`
|
|
@@ -329,7 +330,7 @@ one actions execute → Do it.
|
|
|
329
330
|
|
|
330
331
|
This is the same workflow whether you're sending emails, creating CRM contacts, processing payments, managing inventory, or posting to Slack. One pattern, any platform.
|
|
331
332
|
|
|
332
|
-
For multi-step workflows that chain actions across platforms
|
|
333
|
+
For multi-step workflows that chain actions across platforms:
|
|
333
334
|
|
|
334
335
|
```
|
|
335
336
|
one actions knowledge → Learn each action's schema
|
|
@@ -338,7 +339,7 @@ one flow validate → Check it
|
|
|
338
339
|
one flow execute → Run it
|
|
339
340
|
```
|
|
340
341
|
|
|
341
|
-
|
|
342
|
+
Workflows support conditions, loops, parallel execution, transforms, code steps, and file I/O. Run `one guide flows` for the full schema reference and examples.
|
|
342
343
|
|
|
343
344
|
## For AI agents
|
|
344
345
|
|
package/dist/index.js
CHANGED
|
@@ -483,6 +483,12 @@ var OneApi = class {
|
|
|
483
483
|
) : void 0,
|
|
484
484
|
data: requestData
|
|
485
485
|
};
|
|
486
|
+
if (args.dryRun) {
|
|
487
|
+
return {
|
|
488
|
+
requestConfig: sanitizedConfig,
|
|
489
|
+
responseData: null
|
|
490
|
+
};
|
|
491
|
+
}
|
|
486
492
|
const response = await fetch(fullUrl, fetchOpts);
|
|
487
493
|
if (!response.ok) {
|
|
488
494
|
const text4 = await response.text();
|
|
@@ -593,7 +599,7 @@ QUERY PARAMS: {{QUERY_PARAMS}}`;
|
|
|
593
599
|
import open from "open";
|
|
594
600
|
var ONE_APP_URL = "https://app.withone.ai";
|
|
595
601
|
function getConnectionUrl(platform) {
|
|
596
|
-
return `${ONE_APP_URL}
|
|
602
|
+
return `${ONE_APP_URL}/?#open=${platform}`;
|
|
597
603
|
}
|
|
598
604
|
function getApiKeyUrl() {
|
|
599
605
|
return `${ONE_APP_URL}/settings/api-keys`;
|
|
@@ -1841,7 +1847,7 @@ async function actionsExecuteCommand(platform, actionId, connectionKey, options)
|
|
|
1841
1847
|
const queryParams = options.queryParams ? parseJsonArg(options.queryParams, "--query-params") : void 0;
|
|
1842
1848
|
const headers = options.headers ? parseJsonArg(options.headers, "--headers") : void 0;
|
|
1843
1849
|
const execSpinner = createSpinner();
|
|
1844
|
-
execSpinner.start("Executing action...");
|
|
1850
|
+
execSpinner.start(options.dryRun ? "Building request..." : "Executing action...");
|
|
1845
1851
|
const result = await api.executePassthroughRequest(
|
|
1846
1852
|
{
|
|
1847
1853
|
platform,
|
|
@@ -1852,18 +1858,22 @@ async function actionsExecuteCommand(platform, actionId, connectionKey, options)
|
|
|
1852
1858
|
queryParams,
|
|
1853
1859
|
headers,
|
|
1854
1860
|
isFormData: options.formData,
|
|
1855
|
-
isFormUrlEncoded: options.formUrlEncoded
|
|
1861
|
+
isFormUrlEncoded: options.formUrlEncoded,
|
|
1862
|
+
dryRun: options.dryRun
|
|
1856
1863
|
},
|
|
1857
1864
|
actionDetails
|
|
1858
1865
|
);
|
|
1859
|
-
execSpinner.stop("Action executed successfully");
|
|
1866
|
+
execSpinner.stop(options.dryRun ? "Dry run \u2014 request not sent" : "Action executed successfully");
|
|
1860
1867
|
if (isAgentMode()) {
|
|
1861
1868
|
json({
|
|
1869
|
+
dryRun: options.dryRun || false,
|
|
1862
1870
|
request: {
|
|
1863
1871
|
method: result.requestConfig.method,
|
|
1864
|
-
url: result.requestConfig.url
|
|
1872
|
+
url: result.requestConfig.url,
|
|
1873
|
+
headers: options.dryRun ? result.requestConfig.headers : void 0,
|
|
1874
|
+
data: options.dryRun ? result.requestConfig.data : void 0
|
|
1865
1875
|
},
|
|
1866
|
-
response: result.responseData
|
|
1876
|
+
response: options.dryRun ? void 0 : result.responseData
|
|
1867
1877
|
});
|
|
1868
1878
|
return;
|
|
1869
1879
|
}
|
|
@@ -1874,9 +1884,19 @@ async function actionsExecuteCommand(platform, actionId, connectionKey, options)
|
|
|
1874
1884
|
` ${result.requestConfig.method} ${result.requestConfig.url}`
|
|
1875
1885
|
)
|
|
1876
1886
|
);
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1887
|
+
if (options.dryRun) {
|
|
1888
|
+
if (result.requestConfig.data) {
|
|
1889
|
+
console.log();
|
|
1890
|
+
console.log(pc6.dim("Body:"));
|
|
1891
|
+
console.log(pc6.dim(JSON.stringify(result.requestConfig.data, null, 2)));
|
|
1892
|
+
}
|
|
1893
|
+
console.log();
|
|
1894
|
+
note2("Dry run \u2014 request was not sent", "Dry Run");
|
|
1895
|
+
} else {
|
|
1896
|
+
console.log();
|
|
1897
|
+
console.log(pc6.bold("Response:"));
|
|
1898
|
+
console.log(JSON.stringify(result.responseData, null, 2));
|
|
1899
|
+
}
|
|
1880
1900
|
} catch (error2) {
|
|
1881
1901
|
spinner5.stop("Execution failed");
|
|
1882
1902
|
error(
|
|
@@ -2355,12 +2375,42 @@ async function executeLoopStep(step, context, api, permissions, allowedActionIds
|
|
|
2355
2375
|
throw new Error(`Loop "over" must resolve to an array, got ${typeof items}`);
|
|
2356
2376
|
}
|
|
2357
2377
|
const maxIterations = loop.maxIterations || 1e3;
|
|
2358
|
-
const
|
|
2378
|
+
const bounded = items.slice(0, maxIterations);
|
|
2359
2379
|
const savedLoop = { ...context.loop };
|
|
2360
|
-
|
|
2380
|
+
if (loop.maxConcurrency && loop.maxConcurrency > 1) {
|
|
2381
|
+
const results2 = new Array(bounded.length);
|
|
2382
|
+
for (let batchStart = 0; batchStart < bounded.length; batchStart += loop.maxConcurrency) {
|
|
2383
|
+
const batch = bounded.slice(batchStart, batchStart + loop.maxConcurrency);
|
|
2384
|
+
const batchResults = await Promise.all(
|
|
2385
|
+
batch.map(async (item, batchIdx) => {
|
|
2386
|
+
const i = batchStart + batchIdx;
|
|
2387
|
+
const iterContext = {
|
|
2388
|
+
...context,
|
|
2389
|
+
loop: {
|
|
2390
|
+
[loop.as]: item,
|
|
2391
|
+
item,
|
|
2392
|
+
i,
|
|
2393
|
+
...loop.indexAs ? { [loop.indexAs]: i } : {}
|
|
2394
|
+
},
|
|
2395
|
+
steps: { ...context.steps }
|
|
2396
|
+
};
|
|
2397
|
+
await executeSteps(loop.steps, iterContext, api, permissions, allowedActionIds, options);
|
|
2398
|
+
Object.assign(context.steps, iterContext.steps);
|
|
2399
|
+
return iterContext.loop[loop.as];
|
|
2400
|
+
})
|
|
2401
|
+
);
|
|
2402
|
+
for (let j = 0; j < batchResults.length; j++) {
|
|
2403
|
+
results2[batchStart + j] = batchResults[j];
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
context.loop = savedLoop;
|
|
2407
|
+
return { status: "success", output: results2, response: results2 };
|
|
2408
|
+
}
|
|
2409
|
+
const results = [];
|
|
2410
|
+
for (let i = 0; i < bounded.length; i++) {
|
|
2361
2411
|
context.loop = {
|
|
2362
|
-
[loop.as]:
|
|
2363
|
-
item:
|
|
2412
|
+
[loop.as]: bounded[i],
|
|
2413
|
+
item: bounded[i],
|
|
2364
2414
|
i
|
|
2365
2415
|
};
|
|
2366
2416
|
if (loop.indexAs) {
|
|
@@ -2892,7 +2942,7 @@ async function autoResolveConnectionInputs(flow2, inputs, api) {
|
|
|
2892
2942
|
return resolved;
|
|
2893
2943
|
}
|
|
2894
2944
|
async function flowCreateCommand(key, options) {
|
|
2895
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
2945
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
2896
2946
|
let flow2;
|
|
2897
2947
|
if (options.definition) {
|
|
2898
2948
|
try {
|
|
@@ -2912,7 +2962,7 @@ async function flowCreateCommand(key, options) {
|
|
|
2912
2962
|
error("Invalid JSON from stdin");
|
|
2913
2963
|
}
|
|
2914
2964
|
} else {
|
|
2915
|
-
error("Interactive
|
|
2965
|
+
error("Interactive workflow creation not yet supported. Use --definition <json> or pipe JSON via stdin.");
|
|
2916
2966
|
}
|
|
2917
2967
|
if (key) {
|
|
2918
2968
|
flow2.key = key;
|
|
@@ -2931,25 +2981,25 @@ ${errors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}`);
|
|
|
2931
2981
|
json({ created: true, key: flow2.key, path: flowPath });
|
|
2932
2982
|
return;
|
|
2933
2983
|
}
|
|
2934
|
-
note2(`
|
|
2984
|
+
note2(`Workflow "${flow2.name}" saved to ${flowPath}`, "Created");
|
|
2935
2985
|
outro2(`Validate: ${pc7.cyan(`one flow validate ${flow2.key}`)}
|
|
2936
2986
|
Execute: ${pc7.cyan(`one flow execute ${flow2.key}`)}`);
|
|
2937
2987
|
}
|
|
2938
2988
|
async function flowExecuteCommand(keyOrPath, options) {
|
|
2939
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
2989
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
2940
2990
|
const { apiKey, permissions, actionIds } = getConfig2();
|
|
2941
2991
|
const api = new OneApi(apiKey);
|
|
2942
2992
|
const spinner5 = createSpinner();
|
|
2943
|
-
spinner5.start(`Loading
|
|
2993
|
+
spinner5.start(`Loading workflow "${keyOrPath}"...`);
|
|
2944
2994
|
let flow2;
|
|
2945
2995
|
try {
|
|
2946
2996
|
flow2 = loadFlow(keyOrPath);
|
|
2947
2997
|
} catch (err) {
|
|
2948
|
-
spinner5.stop("
|
|
2998
|
+
spinner5.stop("Workflow not found");
|
|
2949
2999
|
error(err instanceof Error ? err.message : String(err));
|
|
2950
3000
|
return;
|
|
2951
3001
|
}
|
|
2952
|
-
spinner5.stop(`
|
|
3002
|
+
spinner5.stop(`Workflow: ${flow2.name} (${flow2.steps.length} steps)`);
|
|
2953
3003
|
const inputs = parseInputs(options.input || []);
|
|
2954
3004
|
const resolvedInputs = await autoResolveConnectionInputs(flow2, inputs, api);
|
|
2955
3005
|
const runner = new FlowRunner(flow2, resolvedInputs);
|
|
@@ -2980,7 +3030,7 @@ ${pc7.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
|
|
|
2980
3030
|
};
|
|
2981
3031
|
const execSpinner = createSpinner();
|
|
2982
3032
|
if (!options.verbose && !isAgentMode()) {
|
|
2983
|
-
execSpinner.start("Executing
|
|
3033
|
+
execSpinner.start("Executing workflow...");
|
|
2984
3034
|
}
|
|
2985
3035
|
try {
|
|
2986
3036
|
const context = await runner.execute(flow2, api, permissions, actionIds, {
|
|
@@ -2990,11 +3040,11 @@ ${pc7.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
|
|
|
2990
3040
|
});
|
|
2991
3041
|
process.off("SIGINT", sigintHandler);
|
|
2992
3042
|
if (!options.verbose && !isAgentMode()) {
|
|
2993
|
-
execSpinner.stop("
|
|
3043
|
+
execSpinner.stop("Workflow completed");
|
|
2994
3044
|
}
|
|
2995
3045
|
if (isAgentMode()) {
|
|
2996
3046
|
json({
|
|
2997
|
-
event: "
|
|
3047
|
+
event: "workflow:result",
|
|
2998
3048
|
runId,
|
|
2999
3049
|
logFile: logPath,
|
|
3000
3050
|
status: "success",
|
|
@@ -3016,12 +3066,12 @@ ${pc7.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
|
|
|
3016
3066
|
} catch (error2) {
|
|
3017
3067
|
process.off("SIGINT", sigintHandler);
|
|
3018
3068
|
if (!options.verbose && !isAgentMode()) {
|
|
3019
|
-
execSpinner.stop("
|
|
3069
|
+
execSpinner.stop("Workflow failed");
|
|
3020
3070
|
}
|
|
3021
3071
|
const errorMsg = error2 instanceof Error ? error2.message : String(error2);
|
|
3022
3072
|
if (isAgentMode()) {
|
|
3023
3073
|
json({
|
|
3024
|
-
event: "
|
|
3074
|
+
event: "workflow:result",
|
|
3025
3075
|
runId,
|
|
3026
3076
|
logFile: logPath,
|
|
3027
3077
|
status: "failed",
|
|
@@ -3031,18 +3081,18 @@ ${pc7.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
|
|
|
3031
3081
|
}
|
|
3032
3082
|
console.log(` ${pc7.dim(`Run ID: ${runId}`)}`);
|
|
3033
3083
|
console.log(` ${pc7.dim(`Log: ${logPath}`)}`);
|
|
3034
|
-
error(`
|
|
3084
|
+
error(`Workflow failed: ${errorMsg}`);
|
|
3035
3085
|
}
|
|
3036
3086
|
}
|
|
3037
3087
|
async function flowListCommand() {
|
|
3038
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
3088
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
3039
3089
|
const flows = listFlows();
|
|
3040
3090
|
if (isAgentMode()) {
|
|
3041
|
-
json({ flows });
|
|
3091
|
+
json({ workflows: flows });
|
|
3042
3092
|
return;
|
|
3043
3093
|
}
|
|
3044
3094
|
if (flows.length === 0) {
|
|
3045
|
-
note2("No
|
|
3095
|
+
note2("No workflows found in .one/flows/\n\nCreate one with: one flow create", "Workflows");
|
|
3046
3096
|
return;
|
|
3047
3097
|
}
|
|
3048
3098
|
console.log();
|
|
@@ -3065,7 +3115,7 @@ async function flowListCommand() {
|
|
|
3065
3115
|
console.log();
|
|
3066
3116
|
}
|
|
3067
3117
|
async function flowValidateCommand(keyOrPath) {
|
|
3068
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
3118
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
3069
3119
|
const spinner5 = createSpinner();
|
|
3070
3120
|
spinner5.start(`Validating "${keyOrPath}"...`);
|
|
3071
3121
|
let flowData;
|
|
@@ -3075,7 +3125,7 @@ async function flowValidateCommand(keyOrPath) {
|
|
|
3075
3125
|
flowData = JSON.parse(content);
|
|
3076
3126
|
} catch (err) {
|
|
3077
3127
|
spinner5.stop("Validation failed");
|
|
3078
|
-
error(`Could not read
|
|
3128
|
+
error(`Could not read workflow: ${err instanceof Error ? err.message : String(err)}`);
|
|
3079
3129
|
}
|
|
3080
3130
|
const errors = validateFlow(flowData);
|
|
3081
3131
|
if (errors.length > 0) {
|
|
@@ -3091,15 +3141,15 @@ async function flowValidateCommand(keyOrPath) {
|
|
|
3091
3141
|
console.log();
|
|
3092
3142
|
error(`${errors.length} validation error(s) found`);
|
|
3093
3143
|
}
|
|
3094
|
-
spinner5.stop("
|
|
3144
|
+
spinner5.stop("Workflow is valid");
|
|
3095
3145
|
if (isAgentMode()) {
|
|
3096
3146
|
json({ valid: true, key: flowData.key });
|
|
3097
3147
|
return;
|
|
3098
3148
|
}
|
|
3099
|
-
note2(`
|
|
3149
|
+
note2(`Workflow "${flowData.key}" passed all validation checks`, "Valid");
|
|
3100
3150
|
}
|
|
3101
3151
|
async function flowResumeCommand(runId) {
|
|
3102
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
3152
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
3103
3153
|
const state = FlowRunner.loadRunState(runId);
|
|
3104
3154
|
if (!state) {
|
|
3105
3155
|
error(`Run "${runId}" not found`);
|
|
@@ -3113,7 +3163,7 @@ async function flowResumeCommand(runId) {
|
|
|
3113
3163
|
try {
|
|
3114
3164
|
flow2 = loadFlow(state.flowKey);
|
|
3115
3165
|
} catch (err) {
|
|
3116
|
-
error(`Could not load
|
|
3166
|
+
error(`Could not load workflow "${state.flowKey}": ${err instanceof Error ? err.message : String(err)}`);
|
|
3117
3167
|
return;
|
|
3118
3168
|
}
|
|
3119
3169
|
const runner = FlowRunner.fromRunState(state);
|
|
@@ -3126,10 +3176,10 @@ async function flowResumeCommand(runId) {
|
|
|
3126
3176
|
spinner5.start(`Resuming run ${runId} (${state.completedSteps.length} steps already completed)...`);
|
|
3127
3177
|
try {
|
|
3128
3178
|
const context = await runner.resume(flow2, api, permissions, actionIds, { onEvent });
|
|
3129
|
-
spinner5.stop("
|
|
3179
|
+
spinner5.stop("Workflow completed");
|
|
3130
3180
|
if (isAgentMode()) {
|
|
3131
3181
|
json({
|
|
3132
|
-
event: "
|
|
3182
|
+
event: "workflow:result",
|
|
3133
3183
|
runId,
|
|
3134
3184
|
logFile: runner.getLogPath(),
|
|
3135
3185
|
status: "success",
|
|
@@ -3143,14 +3193,14 @@ async function flowResumeCommand(runId) {
|
|
|
3143
3193
|
spinner5.stop("Resume failed");
|
|
3144
3194
|
const errorMsg = error2 instanceof Error ? error2.message : String(error2);
|
|
3145
3195
|
if (isAgentMode()) {
|
|
3146
|
-
json({ event: "
|
|
3196
|
+
json({ event: "workflow:result", runId, status: "failed", error: errorMsg });
|
|
3147
3197
|
process.exit(1);
|
|
3148
3198
|
}
|
|
3149
3199
|
error(`Resume failed: ${errorMsg}`);
|
|
3150
3200
|
}
|
|
3151
3201
|
}
|
|
3152
3202
|
async function flowRunsCommand(flowKey) {
|
|
3153
|
-
intro2(pc7.bgCyan(pc7.black(" One
|
|
3203
|
+
intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
|
|
3154
3204
|
const runs = FlowRunner.listRuns(flowKey);
|
|
3155
3205
|
if (isAgentMode()) {
|
|
3156
3206
|
json({
|
|
@@ -3167,14 +3217,14 @@ async function flowRunsCommand(flowKey) {
|
|
|
3167
3217
|
return;
|
|
3168
3218
|
}
|
|
3169
3219
|
if (runs.length === 0) {
|
|
3170
|
-
note2(flowKey ? `No runs found for
|
|
3220
|
+
note2(flowKey ? `No runs found for workflow "${flowKey}"` : "No workflow runs found", "Runs");
|
|
3171
3221
|
return;
|
|
3172
3222
|
}
|
|
3173
3223
|
console.log();
|
|
3174
3224
|
printTable(
|
|
3175
3225
|
[
|
|
3176
3226
|
{ key: "runId", label: "Run ID" },
|
|
3177
|
-
{ key: "flowKey", label: "
|
|
3227
|
+
{ key: "flowKey", label: "Workflow" },
|
|
3178
3228
|
{ key: "status", label: "Status" },
|
|
3179
3229
|
{ key: "startedAt", label: "Started" },
|
|
3180
3230
|
{ key: "steps", label: "Steps Done" }
|
|
@@ -3232,7 +3282,7 @@ This guide has three sections you can request individually:
|
|
|
3232
3282
|
|
|
3233
3283
|
- **overview** \u2014 This section. Setup, flag usage, and discovery workflow.
|
|
3234
3284
|
- **actions** \u2014 Full workflow for searching, reading docs, and executing platform actions.
|
|
3235
|
-
- **
|
|
3285
|
+
- **workflows** \u2014 Building and executing multi-step API workflows (JSON-based).
|
|
3236
3286
|
|
|
3237
3287
|
## Discovery Workflow
|
|
3238
3288
|
|
|
@@ -3241,9 +3291,9 @@ This guide has three sections you can request individually:
|
|
|
3241
3291
|
3. \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read full docs (REQUIRED before execute)
|
|
3242
3292
|
4. \`one --agent actions execute <platform> <actionId> <connectionKey>\` \u2014 Execute the action
|
|
3243
3293
|
|
|
3244
|
-
For multi-step workflows
|
|
3294
|
+
For multi-step workflows:
|
|
3245
3295
|
1. Discover actions with the workflow above
|
|
3246
|
-
2. Build a
|
|
3296
|
+
2. Build a workflow JSON definition
|
|
3247
3297
|
3. \`one --agent flow create <key> --definition '<json>'\`
|
|
3248
3298
|
4. \`one --agent flow execute <key> -i param=value\`
|
|
3249
3299
|
|
|
@@ -3343,6 +3393,7 @@ Options:
|
|
|
3343
3393
|
- \`--headers <json>\` \u2014 Additional headers as JSON
|
|
3344
3394
|
- \`--form-data\` \u2014 Send as multipart/form-data instead of JSON
|
|
3345
3395
|
- \`--form-url-encoded\` \u2014 Send as application/x-www-form-urlencoded
|
|
3396
|
+
- \`--dry-run\` \u2014 Show the request that would be sent without executing it
|
|
3346
3397
|
|
|
3347
3398
|
Examples:
|
|
3348
3399
|
\`\`\`bash
|
|
@@ -3383,20 +3434,20 @@ Parse the output as JSON. If the \`error\` key is present, the command failed \u
|
|
|
3383
3434
|
- If search returns no results, try broader queries (e.g., \`"list"\` instead of \`"list active premium customers"\`)
|
|
3384
3435
|
- The execute command respects access control settings configured via \`one config\` \u2014 if execution is blocked, the user may need to adjust their permissions
|
|
3385
3436
|
`;
|
|
3386
|
-
var GUIDE_FLOWS = `# One
|
|
3437
|
+
var GUIDE_FLOWS = `# One Workflows \u2014 Multi-Step API Workflows
|
|
3387
3438
|
|
|
3388
|
-
You have access to the One CLI's
|
|
3439
|
+
You have access to the One CLI's workflow engine, which lets you create and execute multi-step API workflows as JSON files. Workflows chain actions across platforms \u2014 e.g., look up a Stripe customer, then send them a welcome email via Gmail.
|
|
3389
3440
|
|
|
3390
3441
|
## 1. Overview
|
|
3391
3442
|
|
|
3392
|
-
-
|
|
3443
|
+
- Workflows are JSON files stored at \`.one/flows/<key>.flow.json\`
|
|
3393
3444
|
- All dynamic values (including connection keys) are declared as **inputs**
|
|
3394
|
-
- Each
|
|
3445
|
+
- Each workflow has a unique **key** used to reference and execute it
|
|
3395
3446
|
- Executed via \`one --agent flow execute <key> -i name=value\`
|
|
3396
3447
|
|
|
3397
|
-
## 2. Building a
|
|
3448
|
+
## 2. Building a Workflow \u2014 Step-by-Step Process
|
|
3398
3449
|
|
|
3399
|
-
**You MUST follow this process to build a correct
|
|
3450
|
+
**You MUST follow this process to build a correct workflow:**
|
|
3400
3451
|
|
|
3401
3452
|
### Step 1: Discover connections
|
|
3402
3453
|
|
|
@@ -3412,20 +3463,20 @@ Find out which platforms are connected and get their connection keys.
|
|
|
3412
3463
|
# Find the action ID
|
|
3413
3464
|
one --agent actions search <platform> "<query>" -t execute
|
|
3414
3465
|
|
|
3415
|
-
# Read the full docs \u2014 REQUIRED before adding to a
|
|
3466
|
+
# Read the full docs \u2014 REQUIRED before adding to a workflow
|
|
3416
3467
|
one --agent actions knowledge <platform> <actionId>
|
|
3417
3468
|
\`\`\`
|
|
3418
3469
|
|
|
3419
|
-
**CRITICAL:** You MUST call \`one actions knowledge\` for every action you include in the
|
|
3470
|
+
**CRITICAL:** You MUST call \`one actions knowledge\` for every action you include in the workflow. The knowledge output tells you the exact request body structure, required fields, path variables, and query parameters. Without this, your workflow JSON will have incorrect data shapes.
|
|
3420
3471
|
|
|
3421
|
-
### Step 3: Construct the
|
|
3472
|
+
### Step 3: Construct the workflow JSON
|
|
3422
3473
|
|
|
3423
|
-
Using the knowledge gathered, build the
|
|
3474
|
+
Using the knowledge gathered, build the workflow JSON with:
|
|
3424
3475
|
- All inputs declared (connection keys + user parameters)
|
|
3425
3476
|
- Each step with the correct actionId, platform, and data structure (from knowledge)
|
|
3426
3477
|
- Data wired between steps using \`$.input.*\` and \`$.steps.*\` selectors
|
|
3427
3478
|
|
|
3428
|
-
### Step 4: Write the
|
|
3479
|
+
### Step 4: Write the workflow file
|
|
3429
3480
|
|
|
3430
3481
|
\`\`\`bash
|
|
3431
3482
|
one --agent flow create <key> --definition '<json>'
|
|
@@ -3445,7 +3496,7 @@ one --agent flow validate <key>
|
|
|
3445
3496
|
one --agent flow execute <key> -i connectionKey=xxx -i param=value
|
|
3446
3497
|
\`\`\`
|
|
3447
3498
|
|
|
3448
|
-
## 3.
|
|
3499
|
+
## 3. Workflow JSON Schema Reference
|
|
3449
3500
|
|
|
3450
3501
|
\`\`\`json
|
|
3451
3502
|
{
|
|
@@ -3501,7 +3552,7 @@ one --agent flow execute <key> -i connectionKey=xxx -i param=value
|
|
|
3501
3552
|
| \`description\` | string | Human-readable description |
|
|
3502
3553
|
| \`connection\` | object | Connection metadata: \`{ "platform": "gmail" }\` \u2014 enables auto-resolution |
|
|
3503
3554
|
|
|
3504
|
-
**Connection inputs** have a \`connection\` field. If the user has exactly one connection for that platform, the engine auto-resolves it.
|
|
3555
|
+
**Connection inputs** have a \`connection\` field. If the user has exactly one connection for that platform, the workflow engine auto-resolves it.
|
|
3505
3556
|
|
|
3506
3557
|
## 4. Selector Syntax Reference
|
|
3507
3558
|
|
|
@@ -3605,6 +3656,7 @@ The \`source\` field contains a JS function body. The flow context is available
|
|
|
3605
3656
|
"as": "order",
|
|
3606
3657
|
"indexAs": "i",
|
|
3607
3658
|
"maxIterations": 1000,
|
|
3659
|
+
"maxConcurrency": 5,
|
|
3608
3660
|
"steps": [
|
|
3609
3661
|
{
|
|
3610
3662
|
"id": "createInvoice",
|
|
@@ -3622,6 +3674,8 @@ The \`source\` field contains a JS function body. The flow context is available
|
|
|
3622
3674
|
}
|
|
3623
3675
|
\`\`\`
|
|
3624
3676
|
|
|
3677
|
+
- \`maxConcurrency\` (optional): When set > 1, loop iterations run in parallel batches of that size. Default is sequential (1).
|
|
3678
|
+
|
|
3625
3679
|
### \`parallel\` \u2014 Run steps concurrently
|
|
3626
3680
|
|
|
3627
3681
|
\`\`\`json
|
|
@@ -3704,20 +3758,20 @@ Skip a step based on previous results:
|
|
|
3704
3758
|
}
|
|
3705
3759
|
\`\`\`
|
|
3706
3760
|
|
|
3707
|
-
## 7. Updating Existing
|
|
3761
|
+
## 7. Updating Existing Workflows
|
|
3708
3762
|
|
|
3709
|
-
To modify an existing
|
|
3763
|
+
To modify an existing workflow:
|
|
3710
3764
|
|
|
3711
|
-
1. Read the
|
|
3765
|
+
1. Read the workflow JSON file at \`.one/flows/<key>.flow.json\`
|
|
3712
3766
|
2. Understand its current structure
|
|
3713
3767
|
3. Use \`one --agent actions knowledge <platform> <actionId>\` for any new actions
|
|
3714
3768
|
4. Modify the JSON (add/remove/update steps, change data mappings, add inputs)
|
|
3715
|
-
5. Write back the updated
|
|
3769
|
+
5. Write back the updated workflow file
|
|
3716
3770
|
6. Validate: \`one --agent flow validate <key>\`
|
|
3717
3771
|
|
|
3718
3772
|
## 8. Complete Examples
|
|
3719
3773
|
|
|
3720
|
-
### Example 1: Simple 2-step \u2014 Search Stripe customer, send Gmail email
|
|
3774
|
+
### Example 1: Simple 2-step workflow \u2014 Search Stripe customer, send Gmail email
|
|
3721
3775
|
|
|
3722
3776
|
\`\`\`json
|
|
3723
3777
|
{
|
|
@@ -3853,7 +3907,7 @@ To modify an existing flow:
|
|
|
3853
3907
|
}
|
|
3854
3908
|
\`\`\`
|
|
3855
3909
|
|
|
3856
|
-
### Example 3: Loop \u2014 Iterate over Shopify orders, create invoices
|
|
3910
|
+
### Example 3: Loop workflow \u2014 Iterate over Shopify orders, create invoices
|
|
3857
3911
|
|
|
3858
3912
|
\`\`\`json
|
|
3859
3913
|
{
|
|
@@ -3931,16 +3985,16 @@ To modify an existing flow:
|
|
|
3931
3985
|
## CLI Commands Reference
|
|
3932
3986
|
|
|
3933
3987
|
\`\`\`bash
|
|
3934
|
-
# Create a
|
|
3988
|
+
# Create a workflow
|
|
3935
3989
|
one --agent flow create <key> --definition '<json>'
|
|
3936
3990
|
|
|
3937
|
-
# List all
|
|
3991
|
+
# List all workflows
|
|
3938
3992
|
one --agent flow list
|
|
3939
3993
|
|
|
3940
|
-
# Validate a
|
|
3994
|
+
# Validate a workflow
|
|
3941
3995
|
one --agent flow validate <key>
|
|
3942
3996
|
|
|
3943
|
-
# Execute a
|
|
3997
|
+
# Execute a workflow
|
|
3944
3998
|
one --agent flow execute <key> -i connectionKey=value -i param=value
|
|
3945
3999
|
|
|
3946
4000
|
# Execute with dry run (validate only)
|
|
@@ -3949,7 +4003,7 @@ one --agent flow execute <key> --dry-run -i connectionKey=value
|
|
|
3949
4003
|
# Execute with verbose output
|
|
3950
4004
|
one --agent flow execute <key> -v -i connectionKey=value
|
|
3951
4005
|
|
|
3952
|
-
# List
|
|
4006
|
+
# List workflow runs
|
|
3953
4007
|
one --agent flow runs [flowKey]
|
|
3954
4008
|
|
|
3955
4009
|
# Resume a paused/failed run
|
|
@@ -3959,16 +4013,16 @@ one --agent flow resume <runId>
|
|
|
3959
4013
|
## Important Notes
|
|
3960
4014
|
|
|
3961
4015
|
- **Always use \`--agent\` flag** for structured JSON output
|
|
3962
|
-
- **Always call \`one actions knowledge\`** before adding an action step to a
|
|
4016
|
+
- **Always call \`one actions knowledge\`** before adding an action step to a workflow
|
|
3963
4017
|
- Platform names are **kebab-case** (e.g., \`hub-spot\`, not \`HubSpot\`)
|
|
3964
|
-
- Connection keys are **inputs**, not hardcoded \u2014 makes
|
|
4018
|
+
- Connection keys are **inputs**, not hardcoded \u2014 makes workflows portable and shareable
|
|
3965
4019
|
- Use \`$.input.*\` for input values, \`$.steps.*\` for step results
|
|
3966
4020
|
- Action IDs in examples (like \`STRIPE_SEARCH_CUSTOMERS_ACTION_ID\`) are placeholders \u2014 always use \`one actions search\` to find the real IDs
|
|
3967
4021
|
`;
|
|
3968
4022
|
var TOPICS = [
|
|
3969
4023
|
{ topic: "overview", description: "Setup, --agent flag, discovery workflow" },
|
|
3970
4024
|
{ topic: "actions", description: "Search, read docs, and execute platform actions" },
|
|
3971
|
-
{ topic: "flows", description: "Build and execute multi-step
|
|
4025
|
+
{ topic: "flows", description: "Build and execute multi-step workflows" },
|
|
3972
4026
|
{ topic: "all", description: "Complete guide (all topics combined)" }
|
|
3973
4027
|
];
|
|
3974
4028
|
function getGuideContent(topic) {
|
|
@@ -3978,7 +4032,7 @@ function getGuideContent(topic) {
|
|
|
3978
4032
|
case "actions":
|
|
3979
4033
|
return { title: "One CLI \u2014 Agent Guide: Actions", content: GUIDE_ACTIONS };
|
|
3980
4034
|
case "flows":
|
|
3981
|
-
return { title: "One CLI \u2014 Agent Guide:
|
|
4035
|
+
return { title: "One CLI \u2014 Agent Guide: Workflows", content: GUIDE_FLOWS };
|
|
3982
4036
|
case "all":
|
|
3983
4037
|
return {
|
|
3984
4038
|
title: "One CLI \u2014 Agent Guide: Complete",
|
|
@@ -4032,12 +4086,12 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
|
|
|
4032
4086
|
4. one actions execute <p> <id> <key> Execute the action
|
|
4033
4087
|
|
|
4034
4088
|
Guide:
|
|
4035
|
-
one guide [topic] Full CLI guide (topics: overview, actions,
|
|
4089
|
+
one guide [topic] Full CLI guide (topics: overview, actions, workflows, all)
|
|
4036
4090
|
|
|
4037
|
-
|
|
4038
|
-
one flow list List saved
|
|
4039
|
-
one flow create [key] Create a
|
|
4040
|
-
one flow execute <key> Execute a
|
|
4091
|
+
Workflows (multi-step):
|
|
4092
|
+
one flow list List saved workflows
|
|
4093
|
+
one flow create [key] Create a workflow from JSON
|
|
4094
|
+
one flow execute <key> Execute a workflow
|
|
4041
4095
|
one flow validate <key> Validate a flow
|
|
4042
4096
|
|
|
4043
4097
|
Example \u2014 send an email through Gmail:
|
|
@@ -4084,33 +4138,34 @@ actions.command("search <platform> <query>").description('Search for actions on
|
|
|
4084
4138
|
actions.command("knowledge <platform> <actionId>").alias("k").description("Get full docs for an action \u2014 MUST call before execute to know required params").action(async (platform, actionId) => {
|
|
4085
4139
|
await actionsKnowledgeCommand(platform, actionId);
|
|
4086
4140
|
});
|
|
4087
|
-
actions.command("execute <platform> <actionId> <connectionKey>").alias("x").description('Execute an action \u2014 pass connectionKey from "one list", actionId from "actions search"').option("-d, --data <json>", "Request body as JSON").option("--path-vars <json>", "Path variables as JSON").option("--query-params <json>", "Query parameters as JSON").option("--headers <json>", "Additional headers as JSON").option("--form-data", "Send as multipart/form-data").option("--form-url-encoded", "Send as application/x-www-form-urlencoded").action(async (platform, actionId, connectionKey, options) => {
|
|
4141
|
+
actions.command("execute <platform> <actionId> <connectionKey>").alias("x").description('Execute an action \u2014 pass connectionKey from "one list", actionId from "actions search"').option("-d, --data <json>", "Request body as JSON").option("--path-vars <json>", "Path variables as JSON").option("--query-params <json>", "Query parameters as JSON").option("--headers <json>", "Additional headers as JSON").option("--form-data", "Send as multipart/form-data").option("--form-url-encoded", "Send as application/x-www-form-urlencoded").option("--dry-run", "Show request that would be sent without executing").action(async (platform, actionId, connectionKey, options) => {
|
|
4088
4142
|
await actionsExecuteCommand(platform, actionId, connectionKey, {
|
|
4089
4143
|
data: options.data,
|
|
4090
4144
|
pathVars: options.pathVars,
|
|
4091
4145
|
queryParams: options.queryParams,
|
|
4092
4146
|
headers: options.headers,
|
|
4093
4147
|
formData: options.formData,
|
|
4094
|
-
formUrlEncoded: options.formUrlEncoded
|
|
4148
|
+
formUrlEncoded: options.formUrlEncoded,
|
|
4149
|
+
dryRun: options.dryRun
|
|
4095
4150
|
});
|
|
4096
4151
|
});
|
|
4097
|
-
var flow = program.command("flow").alias("f").description("Create, execute, and manage multi-step
|
|
4098
|
-
flow.command("create [key]").description("Create a new
|
|
4152
|
+
var flow = program.command("flow").alias("f").description("Create, execute, and manage multi-step workflows");
|
|
4153
|
+
flow.command("create [key]").description("Create a new workflow from JSON definition").option("--definition <json>", "Workflow definition as JSON string").option("-o, --output <path>", "Custom output path (default .one/flows/<key>.flow.json)").action(async (key, options) => {
|
|
4099
4154
|
await flowCreateCommand(key, options);
|
|
4100
4155
|
});
|
|
4101
|
-
flow.command("execute <keyOrPath>").alias("x").description("Execute a
|
|
4156
|
+
flow.command("execute <keyOrPath>").alias("x").description("Execute a workflow by key or file path").option("-i, --input <name=value>", "Input parameter (repeatable)", collect, []).option("--dry-run", "Validate and show execution plan without running").option("-v, --verbose", "Show full request/response for each step").action(async (keyOrPath, options) => {
|
|
4102
4157
|
await flowExecuteCommand(keyOrPath, options);
|
|
4103
4158
|
});
|
|
4104
|
-
flow.command("list").alias("ls").description("List all
|
|
4159
|
+
flow.command("list").alias("ls").description("List all workflows in .one/flows/").action(async () => {
|
|
4105
4160
|
await flowListCommand();
|
|
4106
4161
|
});
|
|
4107
|
-
flow.command("validate <keyOrPath>").description("Validate a
|
|
4162
|
+
flow.command("validate <keyOrPath>").description("Validate a workflow JSON file").action(async (keyOrPath) => {
|
|
4108
4163
|
await flowValidateCommand(keyOrPath);
|
|
4109
4164
|
});
|
|
4110
|
-
flow.command("resume <runId>").description("Resume a paused or failed
|
|
4165
|
+
flow.command("resume <runId>").description("Resume a paused or failed workflow run").action(async (runId) => {
|
|
4111
4166
|
await flowResumeCommand(runId);
|
|
4112
4167
|
});
|
|
4113
|
-
flow.command("runs [flowKey]").description("List
|
|
4168
|
+
flow.command("runs [flowKey]").description("List workflow runs (optionally filtered by flow key)").action(async (flowKey) => {
|
|
4114
4169
|
await flowRunsCommand(flowKey);
|
|
4115
4170
|
});
|
|
4116
4171
|
program.command("guide [topic]").description("Full CLI usage guide for agents (topics: overview, actions, flows, all)").action(async (topic) => {
|