@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.
Files changed (3) hide show
  1. package/README.md +13 -12
  2. package/dist/index.js +141 -86
  3. 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
- Flows 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.
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 flow from a JSON definition. Flows are saved to `.one/flows/<key>.flow.json`.
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>` | Flow definition as a JSON string |
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 flow by key or file path. Pass inputs with repeatable `-i` flags.
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 flow definition are auto-resolved when the user has exactly one connection for that platform.
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 flows saved in `.one/flows/`.
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 flow JSON file against the schema.
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 flow run from where it left off.
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 flow runs, optionally filtered by flow key.
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 flow
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, use **flows**:
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
- Flows support conditions, loops, parallel execution, transforms, code steps, and file I/O. Run `one guide flows` for the full schema reference and examples.
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}/connections?#open=${platform}`;
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
- console.log();
1878
- console.log(pc6.bold("Response:"));
1879
- console.log(JSON.stringify(result.responseData, null, 2));
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 results = [];
2378
+ const bounded = items.slice(0, maxIterations);
2359
2379
  const savedLoop = { ...context.loop };
2360
- for (let i = 0; i < Math.min(items.length, maxIterations); i++) {
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]: items[i],
2363
- item: items[i],
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 Flow ")));
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 flow creation not yet supported. Use --definition <json> or pipe JSON via stdin.");
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(`Flow "${flow2.name}" saved to ${flowPath}`, "Created");
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 Flow ")));
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 flow "${keyOrPath}"...`);
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("Flow not found");
2998
+ spinner5.stop("Workflow not found");
2949
2999
  error(err instanceof Error ? err.message : String(err));
2950
3000
  return;
2951
3001
  }
2952
- spinner5.stop(`Flow: ${flow2.name} (${flow2.steps.length} steps)`);
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 flow...");
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("Flow completed");
3043
+ execSpinner.stop("Workflow completed");
2994
3044
  }
2995
3045
  if (isAgentMode()) {
2996
3046
  json({
2997
- event: "flow:result",
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("Flow failed");
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: "flow:result",
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(`Flow failed: ${errorMsg}`);
3084
+ error(`Workflow failed: ${errorMsg}`);
3035
3085
  }
3036
3086
  }
3037
3087
  async function flowListCommand() {
3038
- intro2(pc7.bgCyan(pc7.black(" One Flow ")));
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 flows found in .one/flows/\n\nCreate one with: one flow create", "Flows");
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 Flow ")));
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 flow: ${err instanceof Error ? err.message : String(err)}`);
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("Flow is valid");
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(`Flow "${flowData.key}" passed all validation checks`, "Valid");
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 Flow ")));
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 flow "${state.flowKey}": ${err instanceof Error ? err.message : String(err)}`);
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("Flow completed");
3179
+ spinner5.stop("Workflow completed");
3130
3180
  if (isAgentMode()) {
3131
3181
  json({
3132
- event: "flow:result",
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: "flow:result", runId, status: "failed", error: errorMsg });
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 Flow ")));
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 flow "${flowKey}"` : "No flow runs found", "Runs");
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: "Flow" },
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
- - **flows** \u2014 Building and executing multi-step API workflows (JSON-based).
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, use flows:
3294
+ For multi-step workflows:
3245
3295
  1. Discover actions with the workflow above
3246
- 2. Build a flow JSON definition
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 Flow \u2014 Multi-Step API Workflows
3437
+ var GUIDE_FLOWS = `# One Workflows \u2014 Multi-Step API Workflows
3387
3438
 
3388
- You have access to the One CLI's flow engine, which lets you create and execute multi-step API workflows as JSON files. Flows chain actions across platforms \u2014 e.g., look up a Stripe customer, then send them a welcome email via Gmail.
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
- - Flows are JSON files stored at \`.one/flows/<key>.flow.json\`
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 flow has a unique **key** used to reference and execute it
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 Flow \u2014 Step-by-Step Process
3448
+ ## 2. Building a Workflow \u2014 Step-by-Step Process
3398
3449
 
3399
- **You MUST follow this process to build a correct flow:**
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 flow
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 flow. The knowledge output tells you the exact request body structure, required fields, path variables, and query parameters. Without this, your flow JSON will have incorrect data shapes.
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 flow JSON
3472
+ ### Step 3: Construct the workflow JSON
3422
3473
 
3423
- Using the knowledge gathered, build the flow JSON with:
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 flow file
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. Flow JSON Schema Reference
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 Flows
3761
+ ## 7. Updating Existing Workflows
3708
3762
 
3709
- To modify an existing flow:
3763
+ To modify an existing workflow:
3710
3764
 
3711
- 1. Read the flow JSON file at \`.one/flows/<key>.flow.json\`
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 flow file
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 flow
3988
+ # Create a workflow
3935
3989
  one --agent flow create <key> --definition '<json>'
3936
3990
 
3937
- # List all flows
3991
+ # List all workflows
3938
3992
  one --agent flow list
3939
3993
 
3940
- # Validate a flow
3994
+ # Validate a workflow
3941
3995
  one --agent flow validate <key>
3942
3996
 
3943
- # Execute a flow
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 flow runs
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 flow
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 flows portable and shareable
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 API workflows" },
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: Flows", content: GUIDE_FLOWS };
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, flows, all)
4089
+ one guide [topic] Full CLI guide (topics: overview, actions, workflows, all)
4036
4090
 
4037
- Flows (multi-step workflows):
4038
- one flow list List saved flows
4039
- one flow create [key] Create a flow from JSON
4040
- one flow execute <key> Execute a flow
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 API workflows");
4098
- flow.command("create [key]").description("Create a new flow from JSON definition").option("--definition <json>", "Flow definition as JSON string").option("-o, --output <path>", "Custom output path (default .one/flows/<key>.flow.json)").action(async (key, options) => {
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 flow 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) => {
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 flows in .one/flows/").action(async () => {
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 flow JSON file").action(async (keyOrPath) => {
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 flow run").action(async (runId) => {
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 flow runs (optionally filtered by flow key)").action(async (flowKey) => {
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picahq/cli",
3
- "version": "1.11.1",
3
+ "version": "1.12.1",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [