@withone/cli 1.12.2 → 1.12.3

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.
@@ -0,0 +1,603 @@
1
+ ---
2
+ name: one-flow
3
+ description: |
4
+ Build and execute multi-step API workflows that chain actions across platforms — like n8n/Zapier but file-based. Workflows are JSON files stored at `.one/flows/<key>.flow.json`.
5
+
6
+ TRIGGER when the user wants to:
7
+ - Create a multi-step workflow or automation (e.g., "create a workflow that looks up a customer in Stripe and sends them an email")
8
+ - Chain multiple API actions together across platforms
9
+ - Build a pipeline or sequence of API calls
10
+ - Execute, validate, or manage existing workflows
11
+ - Automate a process involving multiple connected platforms
12
+ - Schedule or orchestrate a series of actions
13
+
14
+ DO NOT TRIGGER for:
15
+ - Single action execution (use one-actions skill instead)
16
+ - Setting up One or installing MCP (that's `one init`)
17
+ - Adding new connections (that's `one connection add`)
18
+ ---
19
+
20
+ # One Workflows — Multi-Step API Workflows
21
+
22
+ 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 — e.g., look up a Stripe customer, then send them a welcome email via Gmail.
23
+
24
+ ## 1. Overview
25
+
26
+ - Workflows are JSON files stored at `.one/flows/<key>.flow.json`
27
+ - All dynamic values (including connection keys) are declared as **inputs**
28
+ - Each workflow has a unique **key** used to reference and execute it
29
+ - Executed via `one --agent flow execute <key> -i name=value`
30
+
31
+ ## 2. Building a Workflow — Step-by-Step Process
32
+
33
+ **You MUST follow this process to build a correct workflow:**
34
+
35
+ ### Step 1: Discover connections
36
+
37
+ ```bash
38
+ one --agent connection list
39
+ ```
40
+
41
+ Find out which platforms are connected and get their connection keys.
42
+
43
+ ### Step 2: For EACH API action needed, get the knowledge
44
+
45
+ ```bash
46
+ # Find the action ID
47
+ one --agent actions search <platform> "<query>" -t execute
48
+
49
+ # Read the full docs — REQUIRED before adding to a workflow
50
+ one --agent actions knowledge <platform> <actionId>
51
+ ```
52
+
53
+ **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.
54
+
55
+ ### Step 3: Construct the workflow JSON
56
+
57
+ Using the knowledge gathered, build the workflow JSON with:
58
+ - All inputs declared (connection keys + user parameters)
59
+ - Each step with the correct actionId, platform, and data structure (from knowledge)
60
+ - Data wired between steps using `$.input.*` and `$.steps.*` selectors
61
+
62
+ ### Step 4: Write the workflow file
63
+
64
+ ```bash
65
+ one --agent flow create <key> --definition '<json>'
66
+ ```
67
+
68
+ Or write directly to `.one/flows/<key>.flow.json`.
69
+
70
+ ### Step 5: Validate
71
+
72
+ ```bash
73
+ one --agent flow validate <key>
74
+ ```
75
+
76
+ ### Step 6: Execute
77
+
78
+ ```bash
79
+ one --agent flow execute <key> -i connectionKey=xxx -i param=value
80
+ ```
81
+
82
+ ## 3. Workflow JSON Schema Reference
83
+
84
+ ```json
85
+ {
86
+ "key": "welcome-customer",
87
+ "name": "Welcome New Customer",
88
+ "description": "Look up a Stripe customer and send them a welcome email via Gmail",
89
+ "version": "1",
90
+ "inputs": {
91
+ "stripeConnectionKey": {
92
+ "type": "string",
93
+ "required": true,
94
+ "description": "Stripe connection key from one connection list",
95
+ "connection": { "platform": "stripe" }
96
+ },
97
+ "gmailConnectionKey": {
98
+ "type": "string",
99
+ "required": true,
100
+ "description": "Gmail connection key from one connection list",
101
+ "connection": { "platform": "gmail" }
102
+ },
103
+ "customerEmail": {
104
+ "type": "string",
105
+ "required": true,
106
+ "description": "Customer email to look up"
107
+ }
108
+ },
109
+ "steps": [
110
+ {
111
+ "id": "stepId",
112
+ "name": "Human-readable label",
113
+ "type": "action",
114
+ "action": {
115
+ "platform": "stripe",
116
+ "actionId": "the-action-id-from-search",
117
+ "connectionKey": "$.input.stripeConnectionKey",
118
+ "data": {},
119
+ "pathVars": {},
120
+ "queryParams": {},
121
+ "headers": {}
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ ### Input declarations
129
+
130
+ | Field | Type | Description |
131
+ |---|---|---|
132
+ | `type` | string | `string`, `number`, `boolean`, `object`, `array` |
133
+ | `required` | boolean | Whether this input must be provided (default: true) |
134
+ | `default` | any | Default value if not provided |
135
+ | `description` | string | Human-readable description |
136
+ | `connection` | object | Connection metadata: `{ "platform": "gmail" }` — enables auto-resolution |
137
+
138
+ **Connection inputs** have a `connection` field. If the user has exactly one connection for that platform, the workflow engine auto-resolves it.
139
+
140
+ ## 4. Selector Syntax Reference
141
+
142
+ | Pattern | Resolves To |
143
+ |---|---|
144
+ | `$.input.gmailConnectionKey` | Input value (including connection keys) |
145
+ | `$.input.customerEmail` | Any input parameter |
146
+ | `$.steps.stepId.response` | Full API response from a step |
147
+ | `$.steps.stepId.response.data[0].email` | Nested field with array index |
148
+ | `$.steps.stepId.response.data[*].id` | Wildcard — maps array to field |
149
+ | `$.env.MY_VAR` | Environment variable |
150
+ | `$.loop.item` | Current loop item |
151
+ | `$.loop.i` | Current loop index |
152
+ | `"Hello {{$.steps.getUser.response.data.name}}"` | String interpolation |
153
+
154
+ **Rules:**
155
+ - A value that is purely `$.xxx` resolves to the raw type (object, array, number)
156
+ - A string containing `{{$.xxx}}` does string interpolation (stringifies objects)
157
+ - Selectors inside objects/arrays are resolved recursively
158
+
159
+ ## 5. Step Types Reference
160
+
161
+ ### `action` — Execute a One API action
162
+
163
+ ```json
164
+ {
165
+ "id": "findCustomer",
166
+ "name": "Search Stripe customers",
167
+ "type": "action",
168
+ "action": {
169
+ "platform": "stripe",
170
+ "actionId": "conn_mod_def::xxx::yyy",
171
+ "connectionKey": "$.input.stripeConnectionKey",
172
+ "data": {
173
+ "query": "email:'{{$.input.customerEmail}}'"
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### `transform` — Transform data with a JS expression
180
+
181
+ ```json
182
+ {
183
+ "id": "extractNames",
184
+ "name": "Extract customer names",
185
+ "type": "transform",
186
+ "transform": {
187
+ "expression": "$.steps.findCustomer.response.data.map(c => c.name)"
188
+ }
189
+ }
190
+ ```
191
+
192
+ The expression is evaluated with the full flow context as `$`.
193
+
194
+ ### `code` — Run multi-line JavaScript
195
+
196
+ Unlike `transform` (single expression, implicit return), `code` runs a full function body with explicit `return`. Use it when you need variables, loops, try/catch, or `await`.
197
+
198
+ ```json
199
+ {
200
+ "id": "processData",
201
+ "name": "Process and enrich data",
202
+ "type": "code",
203
+ "code": {
204
+ "source": "const customers = $.steps.listCustomers.response.data;\nconst enriched = customers.map(c => ({\n ...c,\n tier: c.spend > 1000 ? 'gold' : 'silver'\n}));\nreturn enriched;"
205
+ }
206
+ }
207
+ ```
208
+
209
+ The `source` field contains a JS function body. The flow context is available as `$`. The function is async, so you can use `await`. The return value is stored as the step result.
210
+
211
+ ### `condition` — If/then/else branching
212
+
213
+ ```json
214
+ {
215
+ "id": "checkFound",
216
+ "name": "Check if customer was found",
217
+ "type": "condition",
218
+ "condition": {
219
+ "expression": "$.steps.findCustomer.response.data.length > 0",
220
+ "then": [
221
+ { "id": "sendEmail", "name": "Send welcome email", "type": "action", "action": { "..." : "..." } }
222
+ ],
223
+ "else": [
224
+ { "id": "logNotFound", "name": "Log not found", "type": "transform", "transform": { "expression": "'Customer not found'" } }
225
+ ]
226
+ }
227
+ }
228
+ ```
229
+
230
+ ### `loop` — Iterate over an array
231
+
232
+ ```json
233
+ {
234
+ "id": "processOrders",
235
+ "name": "Process each order",
236
+ "type": "loop",
237
+ "loop": {
238
+ "over": "$.steps.listOrders.response.data",
239
+ "as": "order",
240
+ "indexAs": "i",
241
+ "maxIterations": 1000,
242
+ "maxConcurrency": 5,
243
+ "steps": [
244
+ {
245
+ "id": "createInvoice",
246
+ "name": "Create invoice for order",
247
+ "type": "action",
248
+ "action": {
249
+ "platform": "quickbooks",
250
+ "actionId": "...",
251
+ "connectionKey": "$.input.qbConnectionKey",
252
+ "data": { "amount": "$.loop.order.total" }
253
+ }
254
+ }
255
+ ]
256
+ }
257
+ }
258
+ ```
259
+
260
+ - `maxConcurrency` (optional): When set > 1, loop iterations run in parallel batches of that size. Default is sequential (1).
261
+
262
+ ### `parallel` — Run steps concurrently
263
+
264
+ ```json
265
+ {
266
+ "id": "parallelLookups",
267
+ "name": "Look up in parallel",
268
+ "type": "parallel",
269
+ "parallel": {
270
+ "maxConcurrency": 5,
271
+ "steps": [
272
+ { "id": "getStripe", "name": "Get Stripe data", "type": "action", "action": { "...": "..." } },
273
+ { "id": "getHubspot", "name": "Get HubSpot data", "type": "action", "action": { "...": "..." } }
274
+ ]
275
+ }
276
+ }
277
+ ```
278
+
279
+ ### `file-read` — Read from filesystem
280
+
281
+ ```json
282
+ {
283
+ "id": "readConfig",
284
+ "name": "Read config file",
285
+ "type": "file-read",
286
+ "fileRead": { "path": "./data/config.json", "parseJson": true }
287
+ }
288
+ ```
289
+
290
+ ### `file-write` — Write to filesystem
291
+
292
+ ```json
293
+ {
294
+ "id": "writeResults",
295
+ "name": "Save results",
296
+ "type": "file-write",
297
+ "fileWrite": {
298
+ "path": "./output/results.json",
299
+ "content": "$.steps.transform.output",
300
+ "append": false
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## 6. Error Handling
306
+
307
+ ### `onError` strategies
308
+
309
+ ```json
310
+ {
311
+ "id": "riskyStep",
312
+ "name": "Might fail",
313
+ "type": "action",
314
+ "onError": {
315
+ "strategy": "retry",
316
+ "retries": 3,
317
+ "retryDelayMs": 1000
318
+ },
319
+ "action": { "...": "..." }
320
+ }
321
+ ```
322
+
323
+ | Strategy | Behavior |
324
+ |---|---|
325
+ | `fail` | Stop the flow immediately (default) |
326
+ | `continue` | Mark step as failed, continue to next step |
327
+ | `retry` | Retry up to N times with delay |
328
+ | `fallback` | On failure, execute a different step |
329
+
330
+ ### Conditional execution
331
+
332
+ Skip a step based on previous results:
333
+
334
+ ```json
335
+ {
336
+ "id": "sendEmail",
337
+ "name": "Send email only if customer found",
338
+ "type": "action",
339
+ "if": "$.steps.findCustomer.response.data.length > 0",
340
+ "action": { "...": "..." }
341
+ }
342
+ ```
343
+
344
+ ## 7. Updating Existing Workflows
345
+
346
+ To modify an existing workflow:
347
+
348
+ 1. Read the workflow JSON file at `.one/flows/<key>.flow.json`
349
+ 2. Understand its current structure
350
+ 3. Use `one --agent actions knowledge <platform> <actionId>` for any new actions
351
+ 4. Modify the JSON (add/remove/update steps, change data mappings, add inputs)
352
+ 5. Write back the updated workflow file
353
+ 6. Validate: `one --agent flow validate <key>`
354
+
355
+ ## 8. Complete Examples
356
+
357
+ ### Example 1: Simple 2-step — Search Stripe customer, send Gmail email
358
+
359
+ ```json
360
+ {
361
+ "key": "welcome-customer",
362
+ "name": "Welcome New Customer",
363
+ "description": "Look up a Stripe customer and send them a welcome email",
364
+ "version": "1",
365
+ "inputs": {
366
+ "stripeConnectionKey": {
367
+ "type": "string",
368
+ "required": true,
369
+ "description": "Stripe connection key",
370
+ "connection": { "platform": "stripe" }
371
+ },
372
+ "gmailConnectionKey": {
373
+ "type": "string",
374
+ "required": true,
375
+ "description": "Gmail connection key",
376
+ "connection": { "platform": "gmail" }
377
+ },
378
+ "customerEmail": {
379
+ "type": "string",
380
+ "required": true,
381
+ "description": "Customer email to look up"
382
+ }
383
+ },
384
+ "steps": [
385
+ {
386
+ "id": "findCustomer",
387
+ "name": "Search for customer in Stripe",
388
+ "type": "action",
389
+ "action": {
390
+ "platform": "stripe",
391
+ "actionId": "STRIPE_SEARCH_CUSTOMERS_ACTION_ID",
392
+ "connectionKey": "$.input.stripeConnectionKey",
393
+ "data": {
394
+ "query": "email:'{{$.input.customerEmail}}'"
395
+ }
396
+ }
397
+ },
398
+ {
399
+ "id": "sendWelcome",
400
+ "name": "Send welcome email via Gmail",
401
+ "type": "action",
402
+ "if": "$.steps.findCustomer.response.data && $.steps.findCustomer.response.data.length > 0",
403
+ "action": {
404
+ "platform": "gmail",
405
+ "actionId": "GMAIL_SEND_EMAIL_ACTION_ID",
406
+ "connectionKey": "$.input.gmailConnectionKey",
407
+ "data": {
408
+ "to": "{{$.input.customerEmail}}",
409
+ "subject": "Welcome, {{$.steps.findCustomer.response.data[0].name}}!",
410
+ "body": "Thank you for being a customer. We're glad to have you!"
411
+ }
412
+ }
413
+ }
414
+ ]
415
+ }
416
+ ```
417
+
418
+ ### Example 2: Conditional — Check if HubSpot contact exists, create or update
419
+
420
+ ```json
421
+ {
422
+ "key": "sync-hubspot-contact",
423
+ "name": "Sync Contact to HubSpot",
424
+ "description": "Check if a contact exists in HubSpot, create if new or update if existing",
425
+ "version": "1",
426
+ "inputs": {
427
+ "hubspotConnectionKey": {
428
+ "type": "string",
429
+ "required": true,
430
+ "connection": { "platform": "hub-spot" }
431
+ },
432
+ "email": { "type": "string", "required": true },
433
+ "firstName": { "type": "string", "required": true },
434
+ "lastName": { "type": "string", "required": true }
435
+ },
436
+ "steps": [
437
+ {
438
+ "id": "searchContact",
439
+ "name": "Search for existing contact",
440
+ "type": "action",
441
+ "action": {
442
+ "platform": "hub-spot",
443
+ "actionId": "HUBSPOT_SEARCH_CONTACTS_ACTION_ID",
444
+ "connectionKey": "$.input.hubspotConnectionKey",
445
+ "data": {
446
+ "filterGroups": [{ "filters": [{ "propertyName": "email", "operator": "EQ", "value": "$.input.email" }] }]
447
+ }
448
+ }
449
+ },
450
+ {
451
+ "id": "createOrUpdate",
452
+ "name": "Create or update contact",
453
+ "type": "condition",
454
+ "condition": {
455
+ "expression": "$.steps.searchContact.response.total > 0",
456
+ "then": [
457
+ {
458
+ "id": "updateContact",
459
+ "name": "Update existing contact",
460
+ "type": "action",
461
+ "action": {
462
+ "platform": "hub-spot",
463
+ "actionId": "HUBSPOT_UPDATE_CONTACT_ACTION_ID",
464
+ "connectionKey": "$.input.hubspotConnectionKey",
465
+ "pathVars": { "contactId": "$.steps.searchContact.response.results[0].id" },
466
+ "data": {
467
+ "properties": { "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
468
+ }
469
+ }
470
+ }
471
+ ],
472
+ "else": [
473
+ {
474
+ "id": "createContact",
475
+ "name": "Create new contact",
476
+ "type": "action",
477
+ "action": {
478
+ "platform": "hub-spot",
479
+ "actionId": "HUBSPOT_CREATE_CONTACT_ACTION_ID",
480
+ "connectionKey": "$.input.hubspotConnectionKey",
481
+ "data": {
482
+ "properties": { "email": "$.input.email", "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
483
+ }
484
+ }
485
+ }
486
+ ]
487
+ }
488
+ }
489
+ ]
490
+ }
491
+ ```
492
+
493
+ ### Example 3: Loop — Iterate over Shopify orders, create invoices
494
+
495
+ ```json
496
+ {
497
+ "key": "shopify-to-invoices",
498
+ "name": "Shopify Orders to Invoices",
499
+ "description": "Fetch recent Shopify orders and create an invoice for each",
500
+ "version": "1",
501
+ "inputs": {
502
+ "shopifyConnectionKey": {
503
+ "type": "string",
504
+ "required": true,
505
+ "connection": { "platform": "shopify" }
506
+ },
507
+ "qbConnectionKey": {
508
+ "type": "string",
509
+ "required": true,
510
+ "connection": { "platform": "quick-books" }
511
+ }
512
+ },
513
+ "steps": [
514
+ {
515
+ "id": "listOrders",
516
+ "name": "List recent Shopify orders",
517
+ "type": "action",
518
+ "action": {
519
+ "platform": "shopify",
520
+ "actionId": "SHOPIFY_LIST_ORDERS_ACTION_ID",
521
+ "connectionKey": "$.input.shopifyConnectionKey",
522
+ "queryParams": { "status": "any", "limit": "50" }
523
+ }
524
+ },
525
+ {
526
+ "id": "createInvoices",
527
+ "name": "Create invoice for each order",
528
+ "type": "loop",
529
+ "loop": {
530
+ "over": "$.steps.listOrders.response.orders",
531
+ "as": "order",
532
+ "indexAs": "i",
533
+ "steps": [
534
+ {
535
+ "id": "createInvoice",
536
+ "name": "Create QuickBooks invoice",
537
+ "type": "action",
538
+ "onError": { "strategy": "continue" },
539
+ "action": {
540
+ "platform": "quick-books",
541
+ "actionId": "QB_CREATE_INVOICE_ACTION_ID",
542
+ "connectionKey": "$.input.qbConnectionKey",
543
+ "data": {
544
+ "Line": [
545
+ {
546
+ "Amount": "$.loop.order.total_price",
547
+ "Description": "Shopify Order #{{$.loop.order.order_number}}"
548
+ }
549
+ ]
550
+ }
551
+ }
552
+ }
553
+ ]
554
+ }
555
+ },
556
+ {
557
+ "id": "summary",
558
+ "name": "Generate summary",
559
+ "type": "transform",
560
+ "transform": {
561
+ "expression": "({ totalOrders: $.steps.listOrders.response.orders.length, processed: $.steps.createInvoices.output.length })"
562
+ }
563
+ }
564
+ ]
565
+ }
566
+ ```
567
+
568
+ ## CLI Commands Reference
569
+
570
+ ```bash
571
+ # Create a workflow
572
+ one --agent flow create <key> --definition '<json>'
573
+
574
+ # List all workflows
575
+ one --agent flow list
576
+
577
+ # Validate a workflow
578
+ one --agent flow validate <key>
579
+
580
+ # Execute a workflow
581
+ one --agent flow execute <key> -i connectionKey=value -i param=value
582
+
583
+ # Execute with dry run (validate only)
584
+ one --agent flow execute <key> --dry-run -i connectionKey=value
585
+
586
+ # Execute with verbose output
587
+ one --agent flow execute <key> -v -i connectionKey=value
588
+
589
+ # List workflow runs
590
+ one --agent flow runs [flowKey]
591
+
592
+ # Resume a paused/failed run
593
+ one --agent flow resume <runId>
594
+ ```
595
+
596
+ ## Important Notes
597
+
598
+ - **Always use `--agent` flag** for structured JSON output
599
+ - **Always call `one actions knowledge`** before adding an action step to a workflow
600
+ - Platform names are **kebab-case** (e.g., `hub-spot`, not `HubSpot`)
601
+ - Connection keys are **inputs**, not hardcoded — makes workflows portable and shareable
602
+ - Use `$.input.*` for input values, `$.steps.*` for step results
603
+ - Action IDs in examples (like `STRIPE_SEARCH_CUSTOMERS_ACTION_ID`) are placeholders — always use `one actions search` to find the real IDs