@withone/cli 1.13.9 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  loadFlow,
12
12
  resolveFlowPath,
13
13
  saveFlow
14
- } from "./chunk-GEQRZGKM.js";
14
+ } from "./chunk-SX2Y4HDW.js";
15
15
 
16
16
  // src/index.ts
17
17
  import { createRequire as createRequire2 } from "module";
@@ -1316,7 +1316,9 @@ async function connectionListCommand(options) {
1316
1316
  connections: limited.map((conn) => ({
1317
1317
  platform: conn.platform,
1318
1318
  state: conn.state,
1319
- key: conn.key
1319
+ key: conn.key,
1320
+ ...conn.name && { name: conn.name },
1321
+ ...conn.tags?.length && { tags: conn.tags }
1320
1322
  })),
1321
1323
  ...limited.length < filtered.length && {
1322
1324
  hint: `Showing ${limited.length} of ${filtered.length} connections. Use --search <query> to filter by platform or --limit <n> to see more.`
@@ -2495,8 +2497,345 @@ function colorStatus(status) {
2495
2497
  }
2496
2498
  }
2497
2499
 
2498
- // src/commands/guide.ts
2500
+ // src/commands/relay.ts
2499
2501
  import pc8 from "picocolors";
2502
+ function getConfig3() {
2503
+ const apiKey = getApiKey();
2504
+ if (!apiKey) {
2505
+ error("Not configured. Run `one init` first.");
2506
+ }
2507
+ const ac = getAccessControlFromAllSources();
2508
+ const connectionKeys = ac.connectionKeys || ["*"];
2509
+ return { apiKey, connectionKeys };
2510
+ }
2511
+ function parseJsonArg2(value, argName) {
2512
+ try {
2513
+ return JSON.parse(value);
2514
+ } catch {
2515
+ error(`Invalid JSON for ${argName}: ${value}`);
2516
+ }
2517
+ }
2518
+ async function relayCreateCommand(options) {
2519
+ const { apiKey, connectionKeys } = getConfig3();
2520
+ if (!connectionKeys.includes("*") && !connectionKeys.includes(options.connectionKey)) {
2521
+ error(`Connection key "${options.connectionKey}" is not allowed.`);
2522
+ }
2523
+ const api = new OneApi(apiKey);
2524
+ const spinner5 = createSpinner();
2525
+ spinner5.start("Creating relay endpoint...");
2526
+ try {
2527
+ const body = {
2528
+ connectionKey: options.connectionKey
2529
+ };
2530
+ if (options.description) body.description = options.description;
2531
+ if (options.eventFilters) body.eventFilters = parseJsonArg2(options.eventFilters, "--event-filters");
2532
+ if (options.tags) body.tags = parseJsonArg2(options.tags, "--tags");
2533
+ if (options.createWebhook) body.createWebhook = true;
2534
+ const result = await api.createRelayEndpoint(body);
2535
+ if (isAgentMode()) {
2536
+ json(result);
2537
+ return;
2538
+ }
2539
+ spinner5.stop("Relay endpoint created");
2540
+ console.log();
2541
+ console.log(` ${pc8.dim("ID:")} ${result.id}`);
2542
+ console.log(` ${pc8.dim("URL:")} ${result.url}`);
2543
+ console.log(` ${pc8.dim("Active:")} ${result.active}`);
2544
+ if (result.description) console.log(` ${pc8.dim("Description:")} ${result.description}`);
2545
+ if (result.eventFilters?.length) console.log(` ${pc8.dim("Events:")} ${result.eventFilters.join(", ")}`);
2546
+ if (result.webhookPayload?.id) console.log(` ${pc8.dim("Webhook ID:")} ${result.webhookPayload.id}`);
2547
+ console.log();
2548
+ } catch (error2) {
2549
+ spinner5.stop("Failed to create relay endpoint");
2550
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2551
+ }
2552
+ }
2553
+ async function relayListCommand(options) {
2554
+ const { apiKey } = getConfig3();
2555
+ const api = new OneApi(apiKey);
2556
+ const spinner5 = createSpinner();
2557
+ spinner5.start("Loading relay endpoints...");
2558
+ try {
2559
+ const query = {};
2560
+ if (options.limit) query.limit = options.limit;
2561
+ if (options.page) query.page = options.page;
2562
+ const result = await api.listRelayEndpoints(query);
2563
+ const endpoints = result.rows || [];
2564
+ if (isAgentMode()) {
2565
+ json({
2566
+ total: result.total,
2567
+ showing: endpoints.length,
2568
+ endpoints: endpoints.map((e) => ({
2569
+ id: e.id,
2570
+ active: e.active,
2571
+ description: e.description,
2572
+ eventFilters: e.eventFilters,
2573
+ actionsCount: e.actions?.length || 0,
2574
+ url: e.url,
2575
+ createdAt: e.createdAt
2576
+ }))
2577
+ });
2578
+ return;
2579
+ }
2580
+ spinner5.stop(`${endpoints.length} relay endpoint${endpoints.length === 1 ? "" : "s"} found`);
2581
+ if (endpoints.length === 0) {
2582
+ console.log("\n No relay endpoints yet.\n");
2583
+ return;
2584
+ }
2585
+ printTable(
2586
+ ["Status", "Description", "Events", "Actions", "ID"],
2587
+ endpoints.map((e) => [
2588
+ e.active ? pc8.green("\u25CF") : pc8.dim("\u25CB"),
2589
+ e.description || pc8.dim("(none)"),
2590
+ e.eventFilters?.join(", ") || pc8.dim("all"),
2591
+ String(e.actions?.length || 0),
2592
+ pc8.dim(e.id.slice(0, 8))
2593
+ ])
2594
+ );
2595
+ } catch (error2) {
2596
+ spinner5.stop("Failed to list relay endpoints");
2597
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2598
+ }
2599
+ }
2600
+ async function relayGetCommand(id) {
2601
+ const { apiKey } = getConfig3();
2602
+ const api = new OneApi(apiKey);
2603
+ const spinner5 = createSpinner();
2604
+ spinner5.start("Loading relay endpoint...");
2605
+ try {
2606
+ const result = await api.getRelayEndpoint(id);
2607
+ if (isAgentMode()) {
2608
+ json(result);
2609
+ return;
2610
+ }
2611
+ spinner5.stop("Relay endpoint loaded");
2612
+ console.log();
2613
+ console.log(` ${pc8.dim("ID:")} ${result.id}`);
2614
+ console.log(` ${pc8.dim("URL:")} ${result.url}`);
2615
+ console.log(` ${pc8.dim("Active:")} ${result.active}`);
2616
+ if (result.description) console.log(` ${pc8.dim("Description:")} ${result.description}`);
2617
+ if (result.eventFilters?.length) console.log(` ${pc8.dim("Events:")} ${result.eventFilters.join(", ")}`);
2618
+ console.log(` ${pc8.dim("Actions:")} ${result.actions?.length || 0}`);
2619
+ if (result.actions?.length) {
2620
+ for (const [i, action] of result.actions.entries()) {
2621
+ console.log(` ${pc8.dim(`[${i}]`)} type=${action.type}${action.actionId ? ` actionId=${action.actionId}` : ""}${action.url ? ` url=${action.url}` : ""}`);
2622
+ }
2623
+ }
2624
+ console.log(` ${pc8.dim("Created:")} ${result.createdAt}`);
2625
+ console.log();
2626
+ } catch (error2) {
2627
+ spinner5.stop("Failed to load relay endpoint");
2628
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2629
+ }
2630
+ }
2631
+ async function relayUpdateCommand(id, options) {
2632
+ const { apiKey } = getConfig3();
2633
+ const api = new OneApi(apiKey);
2634
+ const spinner5 = createSpinner();
2635
+ spinner5.start("Updating relay endpoint...");
2636
+ try {
2637
+ const body = {};
2638
+ if (options.description !== void 0) body.description = options.description;
2639
+ if (options.active !== void 0) body.active = options.active;
2640
+ if (options.eventFilters) body.eventFilters = parseJsonArg2(options.eventFilters, "--event-filters");
2641
+ if (options.tags) body.tags = parseJsonArg2(options.tags, "--tags");
2642
+ if (options.actions) body.actions = parseJsonArg2(options.actions, "--actions");
2643
+ const result = await api.updateRelayEndpoint(id, body);
2644
+ if (isAgentMode()) {
2645
+ json(result);
2646
+ return;
2647
+ }
2648
+ spinner5.stop("Relay endpoint updated");
2649
+ console.log(` ${pc8.dim("ID:")} ${result.id}`);
2650
+ console.log(` ${pc8.dim("Active:")} ${result.active}`);
2651
+ console.log(` ${pc8.dim("Actions:")} ${result.actions?.length || 0}`);
2652
+ console.log();
2653
+ } catch (error2) {
2654
+ spinner5.stop("Failed to update relay endpoint");
2655
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2656
+ }
2657
+ }
2658
+ async function relayDeleteCommand(id) {
2659
+ const { apiKey } = getConfig3();
2660
+ const api = new OneApi(apiKey);
2661
+ const spinner5 = createSpinner();
2662
+ spinner5.start("Deleting relay endpoint...");
2663
+ try {
2664
+ const result = await api.deleteRelayEndpoint(id);
2665
+ if (isAgentMode()) {
2666
+ json({ deleted: true, id: result.id });
2667
+ return;
2668
+ }
2669
+ spinner5.stop("Relay endpoint deleted");
2670
+ console.log(` Deleted: ${result.id}`);
2671
+ console.log();
2672
+ } catch (error2) {
2673
+ spinner5.stop("Failed to delete relay endpoint");
2674
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2675
+ }
2676
+ }
2677
+ async function relayActivateCommand(id, options) {
2678
+ const { apiKey } = getConfig3();
2679
+ const api = new OneApi(apiKey);
2680
+ const spinner5 = createSpinner();
2681
+ spinner5.start("Activating relay endpoint...");
2682
+ try {
2683
+ const actions2 = parseJsonArg2(options.actions, "--actions");
2684
+ const body = { actions: actions2 };
2685
+ if (options.webhookSecret) body.webhookSecret = options.webhookSecret;
2686
+ const result = await api.activateRelayEndpoint(id, body);
2687
+ if (isAgentMode()) {
2688
+ json(result);
2689
+ return;
2690
+ }
2691
+ spinner5.stop("Relay endpoint activated");
2692
+ console.log(` ${pc8.dim("ID:")} ${result.id}`);
2693
+ console.log(` ${pc8.dim("Active:")} ${result.active}`);
2694
+ console.log(` ${pc8.dim("Actions:")} ${result.actions?.length || 0}`);
2695
+ console.log();
2696
+ } catch (error2) {
2697
+ spinner5.stop("Failed to activate relay endpoint");
2698
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2699
+ }
2700
+ }
2701
+ async function relayEventsCommand(options) {
2702
+ const { apiKey } = getConfig3();
2703
+ const api = new OneApi(apiKey);
2704
+ const spinner5 = createSpinner();
2705
+ spinner5.start("Loading relay events...");
2706
+ try {
2707
+ const query = {};
2708
+ if (options.limit) query.limit = options.limit;
2709
+ if (options.page) query.page = options.page;
2710
+ if (options.platform) query.platform = options.platform;
2711
+ if (options.eventType) query.eventType = options.eventType;
2712
+ if (options.after) query.after = options.after;
2713
+ if (options.before) query.before = options.before;
2714
+ const result = await api.listRelayEvents(query);
2715
+ const events = result.rows || [];
2716
+ if (isAgentMode()) {
2717
+ json({
2718
+ total: result.total,
2719
+ showing: events.length,
2720
+ events: events.map((e) => ({
2721
+ id: e.id,
2722
+ platform: e.platform,
2723
+ eventType: e.eventType,
2724
+ timestamp: e.timestamp || e.createdAt
2725
+ }))
2726
+ });
2727
+ return;
2728
+ }
2729
+ spinner5.stop(`${events.length} event${events.length === 1 ? "" : "s"} found`);
2730
+ if (events.length === 0) {
2731
+ console.log("\n No events found.\n");
2732
+ return;
2733
+ }
2734
+ printTable(
2735
+ ["Platform", "Event Type", "Timestamp", "ID"],
2736
+ events.map((e) => [
2737
+ e.platform,
2738
+ e.eventType || pc8.dim("unknown"),
2739
+ e.timestamp || e.createdAt,
2740
+ pc8.dim(e.id.slice(0, 8))
2741
+ ])
2742
+ );
2743
+ } catch (error2) {
2744
+ spinner5.stop("Failed to list relay events");
2745
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2746
+ }
2747
+ }
2748
+ async function relayEventGetCommand(id) {
2749
+ const { apiKey } = getConfig3();
2750
+ const api = new OneApi(apiKey);
2751
+ const spinner5 = createSpinner();
2752
+ spinner5.start("Loading relay event...");
2753
+ try {
2754
+ const result = await api.getRelayEvent(id);
2755
+ if (isAgentMode()) {
2756
+ json(result);
2757
+ return;
2758
+ }
2759
+ spinner5.stop("Relay event loaded");
2760
+ console.log();
2761
+ console.log(` ${pc8.dim("ID:")} ${result.id}`);
2762
+ console.log(` ${pc8.dim("Platform:")} ${result.platform}`);
2763
+ console.log(` ${pc8.dim("Event:")} ${result.eventType}`);
2764
+ console.log(` ${pc8.dim("Timestamp:")} ${result.timestamp || result.createdAt}`);
2765
+ console.log(` ${pc8.dim("Payload:")}`);
2766
+ console.log(JSON.stringify(result.payload, null, 2));
2767
+ console.log();
2768
+ } catch (error2) {
2769
+ spinner5.stop("Failed to load relay event");
2770
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2771
+ }
2772
+ }
2773
+ async function relayDeliveriesCommand(options) {
2774
+ if (!options.endpointId && !options.eventId) {
2775
+ error("Provide either --endpoint-id or --event-id");
2776
+ }
2777
+ const { apiKey } = getConfig3();
2778
+ const api = new OneApi(apiKey);
2779
+ const spinner5 = createSpinner();
2780
+ spinner5.start("Loading deliveries...");
2781
+ try {
2782
+ const deliveries = options.endpointId ? await api.listRelayEndpointDeliveries(options.endpointId) : await api.listRelayEventDeliveries(options.eventId);
2783
+ const items = Array.isArray(deliveries) ? deliveries : deliveries.rows || [];
2784
+ if (isAgentMode()) {
2785
+ json({ deliveries: items });
2786
+ return;
2787
+ }
2788
+ spinner5.stop(`${items.length} deliver${items.length === 1 ? "y" : "ies"} found`);
2789
+ if (items.length === 0) {
2790
+ console.log("\n No deliveries found.\n");
2791
+ return;
2792
+ }
2793
+ printTable(
2794
+ ["Status", "Code", "Attempt", "Delivered At", "Error"],
2795
+ items.map((d) => [
2796
+ d.status === "success" ? pc8.green(d.status) : pc8.red(d.status),
2797
+ d.statusCode != null ? String(d.statusCode) : pc8.dim("-"),
2798
+ String(d.attempt),
2799
+ d.deliveredAt || pc8.dim("-"),
2800
+ d.error ? pc8.red(d.error.slice(0, 50)) : pc8.dim("-")
2801
+ ])
2802
+ );
2803
+ } catch (error2) {
2804
+ spinner5.stop("Failed to load deliveries");
2805
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2806
+ }
2807
+ }
2808
+ async function relayEventTypesCommand(platform) {
2809
+ const { apiKey } = getConfig3();
2810
+ const api = new OneApi(apiKey);
2811
+ const spinner5 = createSpinner();
2812
+ spinner5.start(`Loading event types for ${pc8.cyan(platform)}...`);
2813
+ try {
2814
+ const eventTypes = await api.listRelayEventTypes(platform);
2815
+ if (isAgentMode()) {
2816
+ json({ platform, eventTypes });
2817
+ return;
2818
+ }
2819
+ spinner5.stop(`${eventTypes.length} event type${eventTypes.length === 1 ? "" : "s"} found`);
2820
+ if (eventTypes.length === 0) {
2821
+ console.log(`
2822
+ No event types found for ${platform}.
2823
+ `);
2824
+ return;
2825
+ }
2826
+ console.log();
2827
+ for (const type of eventTypes) {
2828
+ console.log(` ${type}`);
2829
+ }
2830
+ console.log();
2831
+ } catch (error2) {
2832
+ spinner5.stop("Failed to load event types");
2833
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
2834
+ }
2835
+ }
2836
+
2837
+ // src/commands/guide.ts
2838
+ import pc9 from "picocolors";
2500
2839
 
2501
2840
  // src/lib/guide-content.ts
2502
2841
  var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
@@ -2517,886 +2856,316 @@ one --agent <command>
2517
2856
 
2518
2857
  All commands return JSON. If an \`error\` key is present, the command failed.
2519
2858
 
2520
- ## Topics
2521
-
2522
- This guide has three sections you can request individually:
2523
-
2524
- - **overview** \u2014 This section. Setup, flag usage, and discovery workflow.
2525
- - **actions** \u2014 Full workflow for searching, reading docs, and executing platform actions.
2526
- - **workflows** \u2014 Building and executing multi-step API workflows (JSON-based).
2527
-
2528
- ## Discovery Workflow
2529
-
2530
- 1. \`one --agent connection list\` \u2014 See connected platforms and connection keys
2531
- 2. \`one --agent actions search <platform> <query>\` \u2014 Find actions
2532
- 3. \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read full docs (REQUIRED before execute)
2533
- 4. \`one --agent actions execute <platform> <actionId> <connectionKey>\` \u2014 Execute the action
2534
-
2535
- For multi-step workflows:
2536
- 1. Discover actions with the workflow above
2537
- 2. Build a workflow JSON definition
2538
- 3. \`one --agent flow create <key> --definition '<json>'\`
2539
- 4. \`one --agent flow execute <key> -i param=value\`
2540
-
2541
- Platform names are always kebab-case (e.g., \`hub-spot\`, \`google-calendar\`).
2542
- Run \`one platforms\` to browse all 200+ available platforms.
2543
- `;
2544
- var GUIDE_ACTIONS = `# One Actions CLI Workflow
2545
-
2546
- You have access to the One CLI which lets you interact with 200+ third-party platforms through their APIs. The CLI handles authentication, request building, and execution through One's passthrough proxy.
2547
-
2548
- ## The Workflow
2549
-
2550
- Always follow this sequence \u2014 each step builds on the previous one:
2551
-
2552
- 1. **List connections** to see what platforms the user has connected
2553
- 2. **Search actions** to find the right API action for what the user wants to do
2554
- 3. **Get knowledge** to understand the action's parameters, requirements, and structure
2555
- 4. **Execute** the action with the correct parameters
2556
-
2557
- Never skip the knowledge step before executing \u2014 it contains critical information about required parameters, validation rules, and request structure that you need to build a correct request.
2558
-
2559
- ## Commands
2560
-
2561
- ### 1. List Connections
2562
-
2563
- \`\`\`bash
2564
- one --agent connection list
2565
- \`\`\`
2566
-
2567
- Returns JSON with all connected platforms, their status, and connection keys. You need the **connection key** for executing actions, and the **platform name** (kebab-case) for searching actions.
2568
-
2569
- Output format:
2570
- \`\`\`json
2571
- {"connections": [{"platform": "gmail", "state": "active", "key": "conn_abc123"}, ...]}
2572
- \`\`\`
2573
-
2574
- ### 2. Search Actions
2859
+ ## IMPORTANT: Learn before you use
2575
2860
 
2576
- \`\`\`bash
2577
- one --agent actions search <platform> <query>
2578
- \`\`\`
2579
-
2580
- Search for actions on a specific platform using natural language. Returns JSON with up to 5 matching actions including their action IDs, HTTP methods, and paths.
2861
+ Before using any feature, you MUST read the corresponding skill documentation first. The skills are bundled with the CLI and teach you the correct workflow, required steps, template syntax, and common mistakes. Never guess \u2014 read the skill, then act.
2581
2862
 
2582
- - \`<platform>\` \u2014 Platform name in kebab-case exactly as shown in the connections list (e.g., \`gmail\`, \`shopify\`, \`hub-spot\`)
2583
- - \`<query>\` \u2014 Natural language description of what you want to do (e.g., \`"send email"\`, \`"list contacts"\`, \`"create order"\`)
2863
+ ## Features
2584
2864
 
2585
- Options:
2586
- - \`-t, --type <execute|knowledge>\` \u2014 Use \`execute\` when the user wants to perform an action, \`knowledge\` when they want documentation or want to write code. Defaults to \`knowledge\`.
2865
+ ### 1. Actions \u2014 Execute API calls on 200+ platforms
2866
+ Search for actions, read their docs, and execute them. This is the core workflow.
2587
2867
 
2588
- Example:
2868
+ **Quick start:**
2589
2869
  \`\`\`bash
2590
- one --agent actions search gmail "send email" -t execute
2870
+ one --agent connection list # See connected platforms
2871
+ one --agent actions search <platform> "<query>" -t execute # Find an action
2872
+ one --agent actions knowledge <platform> <actionId> # Read docs (REQUIRED)
2873
+ one --agent actions execute <platform> <actionId> <key> -d '{}' # Execute it
2591
2874
  \`\`\`
2592
2875
 
2593
- Output format:
2594
- \`\`\`json
2595
- {"actions": [{"_id": "abc123", "title": "Send Email", "tags": [...], "method": "POST", "path": "/messages/send"}, ...]}
2596
- \`\`\`
2597
-
2598
- ### 3. Get Action Knowledge
2599
-
2600
- \`\`\`bash
2601
- one --agent actions knowledge <platform> <actionId>
2602
- \`\`\`
2876
+ **Parameter flags:**
2877
+ - \`-d <json>\` \u2014 Request body (POST/PUT/PATCH)
2878
+ - \`--path-vars <json>\` \u2014 URL path variables (e.g., \`{"userId": "me"}\`)
2879
+ - \`--query-params <json>\` \u2014 Query parameters (arrays expand to repeated params)
2880
+ - \`--form-url-encoded\` \u2014 Send as form data instead of JSON
2881
+ - \`--dry-run\` \u2014 Preview request without executing
2603
2882
 
2604
- Get comprehensive documentation for an action including parameters, requirements, validation rules, request/response structure, and examples. Returns JSON with the full API knowledge and HTTP method.
2883
+ Do NOT pass path or query parameters in \`-d\` \u2014 use the correct flags.
2605
2884
 
2606
- Always call this before executing \u2014 it tells you exactly what parameters are required and how to structure the request.
2885
+ ### 2. Flows \u2014 Multi-step API workflows
2886
+ Chain actions across platforms as JSON workflow files with conditions, loops, parallel execution, transforms, and AI analysis via bash steps.
2607
2887
 
2608
- Example:
2888
+ **Quick start:**
2609
2889
  \`\`\`bash
2610
- one --agent actions knowledge gmail 67890abcdef
2890
+ one --agent flow create <key> --definition '<json>' # Create a workflow
2891
+ one --agent flow validate <key> # Validate it
2892
+ one --agent flow execute <key> -i param=value # Execute it
2893
+ one --agent flow list # List all workflows
2611
2894
  \`\`\`
2612
2895
 
2613
- Output format:
2614
- \`\`\`json
2615
- {"knowledge": "...full API documentation and guidance...", "method": "POST"}
2616
- \`\`\`
2617
-
2618
- ### 4. Execute Action
2619
-
2620
- \`\`\`bash
2621
- one --agent actions execute <platform> <actionId> <connectionKey> [options]
2622
- \`\`\`
2623
-
2624
- Execute an action on a connected platform. Returns JSON with the request details and response data. You must have retrieved the knowledge for this action first.
2625
-
2626
- - \`<platform>\` \u2014 Platform name in kebab-case
2627
- - \`<actionId>\` \u2014 Action ID from the search results
2628
- - \`<connectionKey>\` \u2014 Connection key from \`one connection list\`
2896
+ **Key concepts:**
2897
+ - Workflows are JSON files at \`.one/flows/<key>.flow.json\`
2898
+ - 12 step types: action, transform, code, condition, loop, parallel, file-read, file-write, while, flow, paginate, bash
2899
+ - Data wiring via selectors: \`$.input.param\`, \`$.steps.stepId.response\`, \`$.loop.item\`
2900
+ - AI analysis via bash steps: \`claude --print\` with \`parseJson: true\`
2901
+ - Use \`--allow-bash\` to enable bash steps, \`--mock\` for dry-run with mock responses
2629
2902
 
2630
- Options:
2631
- - \`-d, --data <json>\` \u2014 Request body as JSON string (for POST, PUT, PATCH)
2632
- - \`--path-vars <json>\` \u2014 Path variables as JSON (for URLs with \`{id}\` placeholders)
2633
- - \`--query-params <json>\` \u2014 Query parameters as JSON
2634
- - \`--headers <json>\` \u2014 Additional headers as JSON
2635
- - \`--form-data\` \u2014 Send as multipart/form-data instead of JSON
2636
- - \`--form-url-encoded\` \u2014 Send as application/x-www-form-urlencoded
2637
- - \`--dry-run\` \u2014 Show the request that would be sent without executing it
2903
+ ### 3. Relay \u2014 Webhook event forwarding between platforms
2904
+ Receive webhooks from platforms (Stripe, GitHub, Airtable, Attio, Google Calendar) and forward event data to any connected platform using passthrough actions with Handlebars templates. No middleware, no code.
2638
2905
 
2639
- Examples:
2906
+ **Quick start:**
2640
2907
  \`\`\`bash
2641
- # Simple GET request
2642
- one --agent actions execute shopify <actionId> <connectionKey>
2643
-
2644
- # POST with data
2645
- one --agent actions execute hub-spot <actionId> <connectionKey> \\
2646
- -d '{"properties": {"email": "jane@example.com", "firstname": "Jane"}}'
2647
-
2648
- # With path variables and query params
2649
- one --agent actions execute shopify <actionId> <connectionKey> \\
2650
- --path-vars '{"order_id": "12345"}' \\
2651
- --query-params '{"limit": "10"}'
2652
- \`\`\`
2653
-
2654
- Output format:
2655
- \`\`\`json
2656
- {"request": {"method": "POST", "url": "https://..."}, "response": {...}}
2908
+ one --agent relay event-types <platform> # See available events
2909
+ one --agent relay create --connection-key <key> --create-webhook --event-filters '["event.type"]'
2910
+ one --agent relay activate <id> --actions '[{"type":"passthrough","actionId":"...","connectionKey":"...","body":{...}}]'
2911
+ one --agent relay list # List endpoints
2912
+ one --agent relay events --platform <p> # List received events
2913
+ one --agent relay deliveries --endpoint-id <id> # Check delivery status
2657
2914
  \`\`\`
2658
2915
 
2659
- ## Error Handling
2916
+ **Key concepts:**
2917
+ - \`passthrough\` actions map webhook fields to another platform's API using Handlebars: \`{{payload.data.object.email}}\`
2918
+ - Template context: \`{{relayEventId}}\`, \`{{platform}}\`, \`{{eventType}}\`, \`{{payload}}\`, \`{{timestamp}}\`, \`{{connectionId}}\`
2919
+ - \`--create-webhook\` auto-registers the webhook URL with the source platform
2920
+ - Use \`actions knowledge\` to learn both the incoming payload shape AND the destination API shape before building templates
2660
2921
 
2661
- All errors return JSON in agent mode:
2662
- \`\`\`json
2663
- {"error": "Error message here"}
2664
- \`\`\`
2922
+ ## Topics
2665
2923
 
2666
- Parse the output as JSON. If the \`error\` key is present, the command failed \u2014 report the error message to the user.
2924
+ Request specific sections:
2925
+ - \`one guide overview\` \u2014 This section
2926
+ - \`one guide actions\` \u2014 Actions reference (search, knowledge, execute)
2927
+ - \`one guide flows\` \u2014 Workflow engine reference (step types, selectors, examples)
2928
+ - \`one guide relay\` \u2014 Webhook relay reference (templates, passthrough actions)
2929
+ - \`one guide all\` \u2014 Everything
2667
2930
 
2668
2931
  ## Important Notes
2669
2932
 
2670
- - **Always use \`--agent\` flag** \u2014 it produces structured JSON output without spinners, colors, or interactive prompts
2671
- - Platform names are always **kebab-case** (e.g., \`hub-spot\` not \`HubSpot\`, \`ship-station\` not \`ShipStation\`)
2672
- - Always use the **exact action ID** from search results \u2014 don't guess or construct them
2673
- - Always read the knowledge output carefully \u2014 it tells you which parameters are required vs optional, what format they need to be in, and any caveats specific to that API
2674
- - JSON values passed to \`-d\`, \`--path-vars\`, \`--query-params\`, and \`--headers\` must be valid JSON strings (use single quotes around the JSON to avoid shell escaping issues)
2675
- - If search returns no results, try broader queries (e.g., \`"list"\` instead of \`"list active premium customers"\`)
2676
- - The execute command respects access control settings configured via \`one config\` \u2014 if execution is blocked, the user may need to adjust their permissions
2933
+ - **Always use \`--agent\` flag** for structured JSON output
2934
+ - Platform names are always **kebab-case** (e.g., \`hub-spot\`, \`google-calendar\`)
2935
+ - Always use the **exact action ID** from search results \u2014 don't guess
2936
+ - Always read **knowledge** before executing any action
2937
+ - Connection keys come from \`one connection list\` \u2014 don't hardcode them
2677
2938
  `;
2678
- var GUIDE_FLOWS = `# One Workflows \u2014 Multi-Step API Workflows
2939
+ var GUIDE_ACTIONS = `# One Actions \u2014 Reference
2679
2940
 
2680
- 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.
2941
+ ## Workflow: search \u2192 knowledge \u2192 execute
2681
2942
 
2682
- ## 1. Overview
2943
+ Always follow this sequence. Never skip the knowledge step.
2683
2944
 
2684
- - Workflows are JSON files stored at \`.one/flows/<key>.flow.json\`
2685
- - All dynamic values (including connection keys) are declared as **inputs**
2686
- - Each workflow has a unique **key** used to reference and execute it
2687
- - Executed via \`one --agent flow execute <key> -i name=value\`
2688
-
2689
- ## 2. Building a Workflow \u2014 Step-by-Step Process
2690
-
2691
- **You MUST follow this process to build a correct workflow:**
2692
-
2693
- ### Step 1: Discover connections
2945
+ ### 1. List Connections
2694
2946
 
2695
2947
  \`\`\`bash
2696
2948
  one --agent connection list
2697
2949
  \`\`\`
2698
2950
 
2699
- Find out which platforms are connected and get their connection keys.
2951
+ Returns platforms, status, connection keys, and tags.
2700
2952
 
2701
- ### Step 2: For EACH API action needed, get the knowledge
2953
+ ### 2. Search Actions
2702
2954
 
2703
2955
  \`\`\`bash
2704
- # Find the action ID
2705
2956
  one --agent actions search <platform> "<query>" -t execute
2706
-
2707
- # Read the full docs \u2014 REQUIRED before adding to a workflow
2708
- one --agent actions knowledge <platform> <actionId>
2709
2957
  \`\`\`
2710
2958
 
2711
- **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.
2712
-
2713
- ### Step 3: Construct the workflow JSON
2959
+ - Use \`-t execute\` when the user wants to perform an action
2960
+ - Use \`-t knowledge\` (default) for documentation/code generation
2961
+ - Returns up to 5 matching actions with IDs, methods, and paths
2714
2962
 
2715
- Using the knowledge gathered, build the workflow JSON with:
2716
- - All inputs declared (connection keys + user parameters)
2717
- - Each step with the correct actionId, platform, and data structure (from knowledge)
2718
- - Data wired between steps using \`$.input.*\` and \`$.steps.*\` selectors
2719
-
2720
- ### Step 4: Write the workflow file
2963
+ ### 3. Get Knowledge
2721
2964
 
2722
2965
  \`\`\`bash
2723
- one --agent flow create <key> --definition '<json>'
2966
+ one --agent actions knowledge <platform> <actionId>
2724
2967
  \`\`\`
2725
2968
 
2726
- Or write directly to \`.one/flows/<key>.flow.json\`.
2969
+ Returns full API docs: required fields, validation rules, request structure. **REQUIRED before execute.** The output includes a CLI PARAMETER MAPPING section showing which flags to use.
2727
2970
 
2728
- ### Step 5: Validate
2971
+ ### 4. Execute
2729
2972
 
2730
2973
  \`\`\`bash
2731
- one --agent flow validate <key>
2732
- \`\`\`
2733
-
2734
- ### Step 6: Execute
2735
-
2736
- \`\`\`bash
2737
- one --agent flow execute <key> -i connectionKey=xxx -i param=value
2738
- \`\`\`
2739
-
2740
- ## 3. Workflow JSON Schema Reference
2741
-
2742
- \`\`\`json
2743
- {
2744
- "key": "welcome-customer",
2745
- "name": "Welcome New Customer",
2746
- "description": "Look up a Stripe customer and send them a welcome email via Gmail",
2747
- "version": "1",
2748
- "inputs": {
2749
- "stripeConnectionKey": {
2750
- "type": "string",
2751
- "required": true,
2752
- "description": "Stripe connection key from one connection list",
2753
- "connection": { "platform": "stripe" }
2754
- },
2755
- "gmailConnectionKey": {
2756
- "type": "string",
2757
- "required": true,
2758
- "description": "Gmail connection key from one connection list",
2759
- "connection": { "platform": "gmail" }
2760
- },
2761
- "customerEmail": {
2762
- "type": "string",
2763
- "required": true,
2764
- "description": "Customer email to look up"
2765
- }
2766
- },
2767
- "steps": [
2768
- {
2769
- "id": "stepId",
2770
- "name": "Human-readable label",
2771
- "type": "action",
2772
- "action": {
2773
- "platform": "stripe",
2774
- "actionId": "the-action-id-from-search",
2775
- "connectionKey": "$.input.stripeConnectionKey",
2776
- "data": {},
2777
- "pathVars": {},
2778
- "queryParams": {},
2779
- "headers": {}
2780
- }
2781
- }
2782
- ]
2783
- }
2784
- \`\`\`
2785
-
2786
- ### Input declarations
2787
-
2788
- | Field | Type | Description |
2789
- |---|---|---|
2790
- | \`type\` | string | \`string\`, \`number\`, \`boolean\`, \`object\`, \`array\` |
2791
- | \`required\` | boolean | Whether this input must be provided (default: true) |
2792
- | \`default\` | any | Default value if not provided |
2793
- | \`description\` | string | Human-readable description |
2794
- | \`connection\` | object | Connection metadata: \`{ "platform": "gmail" }\` \u2014 enables auto-resolution |
2795
-
2796
- **Connection inputs** have a \`connection\` field. If the user has exactly one connection for that platform, the workflow engine auto-resolves it.
2797
-
2798
- ## 4. Selector Syntax Reference
2799
-
2800
- | Pattern | Resolves To |
2801
- |---|---|
2802
- | \`$.input.gmailConnectionKey\` | Input value (including connection keys) |
2803
- | \`$.input.customerEmail\` | Any input parameter |
2804
- | \`$.steps.stepId.response\` | Full API response from a step |
2805
- | \`$.steps.stepId.response.data[0].email\` | Nested field with array index |
2806
- | \`$.steps.stepId.response.data[*].id\` | Wildcard \u2014 maps array to field |
2807
- | \`$.env.MY_VAR\` | Environment variable |
2808
- | \`$.loop.item\` | Current loop item |
2809
- | \`$.loop.i\` | Current loop index |
2810
- | \`"Hello {{$.steps.getUser.response.data.name}}"\` | String interpolation |
2811
-
2812
- **Rules:**
2813
- - A value that is purely \`$.xxx\` resolves to the raw type (object, array, number)
2814
- - A string containing \`{{$.xxx}}\` does string interpolation (stringifies objects)
2815
- - Selectors inside objects/arrays are resolved recursively
2816
-
2817
- ## 5. Step Types Reference
2818
-
2819
- ### \`action\` \u2014 Execute a One API action
2820
-
2821
- \`\`\`json
2822
- {
2823
- "id": "findCustomer",
2824
- "name": "Search Stripe customers",
2825
- "type": "action",
2826
- "action": {
2827
- "platform": "stripe",
2828
- "actionId": "conn_mod_def::xxx::yyy",
2829
- "connectionKey": "$.input.stripeConnectionKey",
2830
- "data": {
2831
- "query": "email:'{{$.input.customerEmail}}'"
2832
- }
2833
- }
2834
- }
2835
- \`\`\`
2836
-
2837
- ### \`transform\` \u2014 Transform data with a JS expression
2838
-
2839
- \`\`\`json
2840
- {
2841
- "id": "extractNames",
2842
- "name": "Extract customer names",
2843
- "type": "transform",
2844
- "transform": {
2845
- "expression": "$.steps.findCustomer.response.data.map(c => c.name)"
2846
- }
2847
- }
2848
- \`\`\`
2849
-
2850
- The expression is evaluated with the full flow context as \`$\`.
2851
-
2852
- ### \`code\` \u2014 Run multi-line JavaScript
2853
-
2854
- 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\`.
2855
-
2856
- \`\`\`json
2857
- {
2858
- "id": "processData",
2859
- "name": "Process and enrich data",
2860
- "type": "code",
2861
- "code": {
2862
- "source": "const customers = $.steps.listCustomers.response.data;\\nconst enriched = customers.map(c => ({\\n ...c,\\n tier: c.spend > 1000 ? 'gold' : 'silver'\\n}));\\nreturn enriched;"
2863
- }
2864
- }
2865
- \`\`\`
2866
-
2867
- 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.
2868
-
2869
- ### \`condition\` \u2014 If/then/else branching
2870
-
2871
- \`\`\`json
2872
- {
2873
- "id": "checkFound",
2874
- "name": "Check if customer was found",
2875
- "type": "condition",
2876
- "condition": {
2877
- "expression": "$.steps.findCustomer.response.data.length > 0",
2878
- "then": [
2879
- { "id": "sendEmail", "name": "Send welcome email", "type": "action", "action": { "..." : "..." } }
2880
- ],
2881
- "else": [
2882
- { "id": "logNotFound", "name": "Log not found", "type": "transform", "transform": { "expression": "'Customer not found'" } }
2883
- ]
2884
- }
2885
- }
2886
- \`\`\`
2887
-
2888
- ### \`loop\` \u2014 Iterate over an array
2889
-
2890
- \`\`\`json
2891
- {
2892
- "id": "processOrders",
2893
- "name": "Process each order",
2894
- "type": "loop",
2895
- "loop": {
2896
- "over": "$.steps.listOrders.response.data",
2897
- "as": "order",
2898
- "indexAs": "i",
2899
- "maxIterations": 1000,
2900
- "maxConcurrency": 5,
2901
- "steps": [
2902
- {
2903
- "id": "createInvoice",
2904
- "name": "Create invoice for order",
2905
- "type": "action",
2906
- "action": {
2907
- "platform": "quickbooks",
2908
- "actionId": "...",
2909
- "connectionKey": "$.input.qbConnectionKey",
2910
- "data": { "amount": "$.loop.order.total" }
2911
- }
2912
- }
2913
- ]
2914
- }
2915
- }
2974
+ one --agent actions execute <platform> <actionId> <connectionKey> [options]
2916
2975
  \`\`\`
2917
2976
 
2918
- - \`maxConcurrency\` (optional): When set > 1, loop iterations run in parallel batches of that size. Default is sequential (1).
2977
+ **Flags:**
2978
+ - \`-d, --data <json>\` \u2014 Request body (POST/PUT/PATCH)
2979
+ - \`--path-vars <json>\` \u2014 Path variables: \`{"userId": "me"}\`
2980
+ - \`--query-params <json>\` \u2014 Query params: \`{"limit": "10"}\`
2981
+ - Arrays expand to repeated params: \`{"metadataHeaders": ["From", "Subject"]}\`
2982
+ - \`--headers <json>\` \u2014 Additional headers
2983
+ - \`--form-data\` / \`--form-url-encoded\` \u2014 Alternative content types
2984
+ - \`--dry-run\` \u2014 Preview without executing
2919
2985
 
2920
- ### \`parallel\` \u2014 Run steps concurrently
2986
+ **Do NOT** pass path or query parameters in \`-d\`. Use the correct flags.
2921
2987
 
2922
- \`\`\`json
2923
- {
2924
- "id": "parallelLookups",
2925
- "name": "Look up in parallel",
2926
- "type": "parallel",
2927
- "parallel": {
2928
- "maxConcurrency": 5,
2929
- "steps": [
2930
- { "id": "getStripe", "name": "Get Stripe data", "type": "action", "action": { "...": "..." } },
2931
- { "id": "getHubspot", "name": "Get HubSpot data", "type": "action", "action": { "...": "..." } }
2932
- ]
2933
- }
2934
- }
2935
- \`\`\`
2988
+ ## Error Handling
2936
2989
 
2937
- ### \`file-read\` \u2014 Read from filesystem
2990
+ All errors return JSON: \`{"error": "message"}\`. Check the \`error\` key.
2938
2991
 
2939
- \`\`\`json
2940
- {
2941
- "id": "readConfig",
2942
- "name": "Read config file",
2943
- "type": "file-read",
2944
- "fileRead": { "path": "./data/config.json", "parseJson": true }
2945
- }
2946
- \`\`\`
2992
+ ## Notes
2947
2993
 
2948
- ### \`file-write\` \u2014 Write to filesystem
2994
+ - Platform names are **kebab-case** (e.g., \`hub-spot\`)
2995
+ - JSON flags use single quotes around the JSON to avoid shell escaping
2996
+ - If search returns no results, try broader queries
2997
+ - Access control settings from \`one config\` may restrict execution
2998
+ `;
2999
+ var GUIDE_FLOWS = `# One Flows \u2014 Reference
2949
3000
 
2950
- \`\`\`json
2951
- {
2952
- "id": "writeResults",
2953
- "name": "Save results",
2954
- "type": "file-write",
2955
- "fileWrite": {
2956
- "path": "./output/results.json",
2957
- "content": "$.steps.transform.output",
2958
- "append": false
2959
- }
2960
- }
2961
- \`\`\`
3001
+ ## Overview
2962
3002
 
2963
- ### \`while\` \u2014 Condition-driven loop (do-while)
3003
+ Workflows are JSON files at \`.one/flows/<key>.flow.json\` that chain actions across platforms.
2964
3004
 
2965
- Iterates until a condition becomes falsy. The first iteration always runs (do-while semantics), then the condition is checked before each subsequent iteration. Useful for pagination.
3005
+ ## Commands
2966
3006
 
2967
- \`\`\`json
2968
- {
2969
- "id": "paginate",
2970
- "name": "Paginate through all pages",
2971
- "type": "while",
2972
- "while": {
2973
- "condition": "$.steps.paginate.output.lastResult.nextPageToken != null",
2974
- "maxIterations": 50,
2975
- "steps": [
2976
- {
2977
- "id": "fetchPage",
2978
- "name": "Fetch next page",
2979
- "type": "action",
2980
- "action": {
2981
- "platform": "gmail",
2982
- "actionId": "GMAIL_LIST_MESSAGES_ACTION_ID",
2983
- "connectionKey": "$.input.gmailKey",
2984
- "queryParams": {
2985
- "pageToken": "$.steps.paginate.output.lastResult.nextPageToken"
2986
- }
2987
- }
2988
- }
2989
- ]
2990
- }
2991
- }
3007
+ \`\`\`bash
3008
+ one --agent flow create <key> --definition '<json>' # Create
3009
+ one --agent flow list # List
3010
+ one --agent flow validate <key> # Validate
3011
+ one --agent flow execute <key> -i name=value # Execute
3012
+ one --agent flow execute <key> --dry-run --mock # Test with mock data
3013
+ one --agent flow execute <key> --allow-bash # Enable bash steps
3014
+ one --agent flow runs [flowKey] # List past runs
3015
+ one --agent flow resume <runId> # Resume failed run
2992
3016
  \`\`\`
2993
3017
 
2994
- | Field | Type | Description |
2995
- |---|---|---|
2996
- | \`condition\` | string | JS expression evaluated before each iteration (after iteration 0) |
2997
- | \`maxIterations\` | number | Safety cap, default: 100 |
2998
- | \`steps\` | FlowStep[] | Steps to execute each iteration |
3018
+ ## Building a Workflow
3019
+
3020
+ 1. **Design first** \u2014 clarify the end goal, map the full value chain, identify where AI analysis is needed
3021
+ 2. **Discover connections** \u2014 \`one --agent connection list\`
3022
+ 3. **Get knowledge** for every action \u2014 \`one --agent actions knowledge <platform> <actionId>\`
3023
+ 4. **Construct JSON** \u2014 declare inputs, wire steps with selectors
3024
+ 5. **Validate** \u2014 \`one --agent flow validate <key>\`
3025
+ 6. **Execute** \u2014 \`one --agent flow execute <key> -i param=value\`
3026
+
3027
+ ## Step Types
3028
+
3029
+ | Type | Purpose |
3030
+ |------|---------|
3031
+ | \`action\` | Execute a platform API action |
3032
+ | \`transform\` | Single JS expression (implicit return) |
3033
+ | \`code\` | Multi-line async JS (explicit return) |
3034
+ | \`condition\` | If/then/else branching |
3035
+ | \`loop\` | Iterate over array with optional concurrency |
3036
+ | \`parallel\` | Run steps concurrently |
3037
+ | \`file-read\` | Read file (optional JSON parse) |
3038
+ | \`file-write\` | Write/append to file |
3039
+ | \`while\` | Do-while loop with condition |
3040
+ | \`flow\` | Execute a sub-flow |
3041
+ | \`paginate\` | Auto-paginate API results |
3042
+ | \`bash\` | Shell command (requires \`--allow-bash\`) |
3043
+
3044
+ ## Selectors
2999
3045
 
3000
- The step output contains \`lastResult\` (last step's output from most recent iteration), \`iteration\` (count), and \`results\` (array of all iteration outputs). Reference via \`$.steps.<id>.output.lastResult\`.
3001
-
3002
- ### \`flow\` \u2014 Execute a sub-flow
3046
+ | Pattern | Resolves To |
3047
+ |---------|-------------|
3048
+ | \`$.input.paramName\` | Input value |
3049
+ | \`$.steps.stepId.response\` | Full API response |
3050
+ | \`$.steps.stepId.response.data[0].email\` | Nested field |
3051
+ | \`$.steps.stepId.response.data[*].id\` | Wildcard array map |
3052
+ | \`$.env.MY_VAR\` | Environment variable |
3053
+ | \`$.loop.item\` / \`$.loop.i\` | Loop iteration |
3054
+ | \`"Hello {{$.steps.getUser.response.name}}"\` | String interpolation |
3003
3055
 
3004
- Loads and executes another saved flow, enabling flow composition. Circular flows are detected and blocked.
3056
+ ## Error Handling
3005
3057
 
3006
3058
  \`\`\`json
3007
- {
3008
- "id": "processCustomer",
3009
- "name": "Run customer enrichment flow",
3010
- "type": "flow",
3011
- "flow": {
3012
- "key": "enrich-customer",
3013
- "inputs": {
3014
- "email": "$.steps.getCustomer.response.email",
3015
- "connectionKey": "$.input.hubspotConnectionKey"
3016
- }
3017
- }
3018
- }
3059
+ {"onError": {"strategy": "retry", "retries": 3, "retryDelayMs": 1000}}
3019
3060
  \`\`\`
3020
3061
 
3021
- | Field | Type | Description |
3022
- |---|---|---|
3023
- | \`key\` | string | Flow key or path (supports selectors) |
3024
- | \`inputs\` | object | Input values mapped to the sub-flow's declared inputs (supports selectors) |
3062
+ Strategies: \`fail\` (default), \`continue\`, \`retry\`, \`fallback\`
3025
3063
 
3026
- ### \`paginate\` \u2014 Auto-collect paginated API results
3064
+ Conditional execution: \`"if": "$.steps.prev.response.data.length > 0"\`
3027
3065
 
3028
- Automatically pages through a paginated API, collecting all results into a single array.
3066
+ ## AI-Augmented Pattern
3029
3067
 
3030
- \`\`\`json
3031
- {
3032
- "id": "allMessages",
3033
- "name": "Fetch all Gmail messages",
3034
- "type": "paginate",
3035
- "paginate": {
3036
- "action": {
3037
- "platform": "gmail",
3038
- "actionId": "GMAIL_LIST_MESSAGES_ACTION_ID",
3039
- "connectionKey": "$.input.gmailKey",
3040
- "queryParams": { "maxResults": 100 }
3041
- },
3042
- "pageTokenField": "nextPageToken",
3043
- "resultsField": "messages",
3044
- "inputTokenParam": "queryParams.pageToken",
3045
- "maxPages": 10
3046
- }
3047
- }
3048
- \`\`\`
3068
+ For workflows that need analysis/summarization, use the file-write \u2192 bash \u2192 code pattern:
3049
3069
 
3050
- | Field | Type | Description |
3051
- |---|---|---|
3052
- | \`action\` | FlowActionConfig | The API action to call (same format as action steps) |
3053
- | \`pageTokenField\` | string | Dot-path in the API response to the next page token |
3054
- | \`resultsField\` | string | Dot-path in the API response to the results array |
3055
- | \`inputTokenParam\` | string | Dot-path in the action config where the page token is injected |
3056
- | \`maxPages\` | number | Maximum pages to fetch, default: 10 |
3070
+ 1. \`file-write\` \u2014 save data to temp file
3071
+ 2. \`bash\` \u2014 \`claude --print\` analyzes it (\`parseJson: true\`, \`timeout: 180000\`)
3072
+ 3. \`code\` \u2014 parse and structure the output
3057
3073
 
3058
- ### \`bash\` \u2014 Execute shell commands
3074
+ Set timeout to at least 180000ms (3 min). Run Claude-heavy flows sequentially, not in parallel.
3059
3075
 
3060
- Runs a shell command. **Requires \`--allow-bash\` flag** for security.
3076
+ ## Notes
3061
3077
 
3062
- \`\`\`json
3063
- {
3064
- "id": "analyzeData",
3065
- "name": "Analyze data with Claude",
3066
- "type": "bash",
3067
- "bash": {
3068
- "command": "claude --print 'Analyze: {{$.steps.fetchData.response}}' --output-format json",
3069
- "timeout": 120000,
3070
- "parseJson": true
3071
- }
3072
- }
3073
- \`\`\`
3074
-
3075
- | Field | Type | Description |
3076
- |---|---|---|
3077
- | \`command\` | string | Shell command to execute (supports selectors and interpolation) |
3078
- | \`timeout\` | number | Timeout in ms, default: 30000 |
3079
- | \`parseJson\` | boolean | Parse stdout as JSON, default: false |
3080
- | \`cwd\` | string | Working directory (supports selectors) |
3081
- | \`env\` | object | Additional environment variables |
3078
+ - Connection keys are **inputs**, not hardcoded
3079
+ - Action IDs in examples are placeholders \u2014 always use \`actions search\`
3080
+ - Code steps allow \`crypto\`, \`buffer\`, \`url\`, \`path\` \u2014 \`fs\`, \`http\`, \`child_process\` are blocked
3081
+ - Bash steps require \`--allow-bash\` flag
3082
+ - State is persisted after every step \u2014 resume picks up where it left off
3083
+ `;
3084
+ var GUIDE_RELAY = `# One Relay \u2014 Reference
3082
3085
 
3083
- **Security:** Bash steps are blocked by default. Pass \`--allow-bash\` to \`one flow execute\` to enable them.
3086
+ ## Overview
3084
3087
 
3085
- ## 6. Error Handling
3088
+ Webhook relay receives events from platforms (Stripe, GitHub, Airtable, Attio, Google Calendar) and forwards them to any connected platform using passthrough actions with Handlebars templates.
3086
3089
 
3087
- ### \`onError\` strategies
3090
+ ## Commands
3088
3091
 
3089
- \`\`\`json
3090
- {
3091
- "id": "riskyStep",
3092
- "name": "Might fail",
3093
- "type": "action",
3094
- "onError": {
3095
- "strategy": "retry",
3096
- "retries": 3,
3097
- "retryDelayMs": 1000
3098
- },
3099
- "action": { "...": "..." }
3100
- }
3092
+ \`\`\`bash
3093
+ one --agent relay event-types <platform> # List available events
3094
+ one --agent relay create --connection-key <key> --create-webhook --event-filters '["event.type"]'
3095
+ one --agent relay activate <id> --actions '<json>' # Add forwarding actions
3096
+ one --agent relay list # List endpoints
3097
+ one --agent relay get <id> # Get endpoint details
3098
+ one --agent relay update <id> --actions '<json>' # Update endpoint
3099
+ one --agent relay delete <id> # Delete endpoint
3100
+ one --agent relay events --platform <p> # List received events
3101
+ one --agent relay event <id> # Get event with payload
3102
+ one --agent relay deliveries --endpoint-id <id> # Check delivery status
3101
3103
  \`\`\`
3102
3104
 
3103
- | Strategy | Behavior |
3104
- |---|---|
3105
- | \`fail\` | Stop the flow immediately (default) |
3106
- | \`continue\` | Mark step as failed, continue to next step |
3107
- | \`retry\` | Retry up to N times with delay |
3108
- | \`fallback\` | On failure, execute a different step |
3105
+ ## Building a Relay
3109
3106
 
3110
- ### Conditional execution
3107
+ 1. **Discover connections** \u2014 identify source and destination platforms
3108
+ 2. **Get event types** \u2014 \`one --agent relay event-types <platform>\`
3109
+ 3. **Get source knowledge** \u2014 understand the incoming webhook payload shape (\`{{payload.*}}\` paths)
3110
+ 4. **Get destination knowledge** \u2014 understand the outgoing API body shape
3111
+ 5. **Create endpoint** \u2014 with \`--create-webhook\` and \`--event-filters\`
3112
+ 6. **Activate** \u2014 with passthrough action mapping source fields to destination fields
3111
3113
 
3112
- Skip a step based on previous results:
3114
+ ## Template Context
3113
3115
 
3114
- \`\`\`json
3115
- {
3116
- "id": "sendEmail",
3117
- "name": "Send email only if customer found",
3118
- "type": "action",
3119
- "if": "$.steps.findCustomer.response.data.length > 0",
3120
- "action": { "...": "..." }
3121
- }
3122
- \`\`\`
3123
-
3124
- ## 7. Updating Existing Workflows
3125
-
3126
- To modify an existing workflow:
3116
+ | Variable | Description |
3117
+ |----------|-------------|
3118
+ | \`{{relayEventId}}\` | Unique event UUID |
3119
+ | \`{{platform}}\` | Source platform (e.g., \`stripe\`) |
3120
+ | \`{{eventType}}\` | Event type (e.g., \`customer.created\`) |
3121
+ | \`{{payload}}\` | Full incoming webhook body |
3122
+ | \`{{timestamp}}\` | When the event was received |
3123
+ | \`{{connectionId}}\` | Source connection UUID |
3124
+ | \`{{json payload}}\` | Embed full payload as JSON string |
3127
3125
 
3128
- 1. Read the workflow JSON file at \`.one/flows/<key>.flow.json\`
3129
- 2. Understand its current structure
3130
- 3. Use \`one --agent actions knowledge <platform> <actionId>\` for any new actions
3131
- 4. Modify the JSON (add/remove/update steps, change data mappings, add inputs)
3132
- 5. Write back the updated workflow file
3133
- 6. Validate: \`one --agent flow validate <key>\`
3126
+ Access nested fields: \`{{payload.data.object.email}}\`
3134
3127
 
3135
- ## 8. Complete Examples
3136
-
3137
- ### Example 1: Simple 2-step workflow \u2014 Search Stripe customer, send Gmail email
3128
+ ## Action Types
3138
3129
 
3130
+ **passthrough** (primary) \u2014 forward to another platform's API:
3139
3131
  \`\`\`json
3140
3132
  {
3141
- "key": "welcome-customer",
3142
- "name": "Welcome New Customer",
3143
- "description": "Look up a Stripe customer and send them a welcome email",
3144
- "version": "1",
3145
- "inputs": {
3146
- "stripeConnectionKey": {
3147
- "type": "string",
3148
- "required": true,
3149
- "description": "Stripe connection key",
3150
- "connection": { "platform": "stripe" }
3151
- },
3152
- "gmailConnectionKey": {
3153
- "type": "string",
3154
- "required": true,
3155
- "description": "Gmail connection key",
3156
- "connection": { "platform": "gmail" }
3157
- },
3158
- "customerEmail": {
3159
- "type": "string",
3160
- "required": true,
3161
- "description": "Customer email to look up"
3162
- }
3163
- },
3164
- "steps": [
3165
- {
3166
- "id": "findCustomer",
3167
- "name": "Search for customer in Stripe",
3168
- "type": "action",
3169
- "action": {
3170
- "platform": "stripe",
3171
- "actionId": "STRIPE_SEARCH_CUSTOMERS_ACTION_ID",
3172
- "connectionKey": "$.input.stripeConnectionKey",
3173
- "data": {
3174
- "query": "email:'{{$.input.customerEmail}}'"
3175
- }
3176
- }
3177
- },
3178
- {
3179
- "id": "sendWelcome",
3180
- "name": "Send welcome email via Gmail",
3181
- "type": "action",
3182
- "if": "$.steps.findCustomer.response.data && $.steps.findCustomer.response.data.length > 0",
3183
- "action": {
3184
- "platform": "gmail",
3185
- "actionId": "GMAIL_SEND_EMAIL_ACTION_ID",
3186
- "connectionKey": "$.input.gmailConnectionKey",
3187
- "data": {
3188
- "to": "{{$.input.customerEmail}}",
3189
- "subject": "Welcome, {{$.steps.findCustomer.response.data[0].name}}!",
3190
- "body": "Thank you for being a customer. We're glad to have you!"
3191
- }
3192
- }
3193
- }
3194
- ]
3133
+ "type": "passthrough",
3134
+ "actionId": "<action-id>",
3135
+ "connectionKey": "<dest-connection-key>",
3136
+ "body": { "channel": "#alerts", "text": "New customer: {{payload.data.object.name}}" },
3137
+ "eventFilters": ["customer.created"]
3195
3138
  }
3196
3139
  \`\`\`
3197
3140
 
3198
- ### Example 2: Conditional \u2014 Check if HubSpot contact exists, create or update
3199
-
3141
+ **url** \u2014 forward raw event to a URL:
3200
3142
  \`\`\`json
3201
- {
3202
- "key": "sync-hubspot-contact",
3203
- "name": "Sync Contact to HubSpot",
3204
- "description": "Check if a contact exists in HubSpot, create if new or update if existing",
3205
- "version": "1",
3206
- "inputs": {
3207
- "hubspotConnectionKey": {
3208
- "type": "string",
3209
- "required": true,
3210
- "connection": { "platform": "hub-spot" }
3211
- },
3212
- "email": { "type": "string", "required": true },
3213
- "firstName": { "type": "string", "required": true },
3214
- "lastName": { "type": "string", "required": true }
3215
- },
3216
- "steps": [
3217
- {
3218
- "id": "searchContact",
3219
- "name": "Search for existing contact",
3220
- "type": "action",
3221
- "action": {
3222
- "platform": "hub-spot",
3223
- "actionId": "HUBSPOT_SEARCH_CONTACTS_ACTION_ID",
3224
- "connectionKey": "$.input.hubspotConnectionKey",
3225
- "data": {
3226
- "filterGroups": [{ "filters": [{ "propertyName": "email", "operator": "EQ", "value": "$.input.email" }] }]
3227
- }
3228
- }
3229
- },
3230
- {
3231
- "id": "createOrUpdate",
3232
- "name": "Create or update contact",
3233
- "type": "condition",
3234
- "condition": {
3235
- "expression": "$.steps.searchContact.response.total > 0",
3236
- "then": [
3237
- {
3238
- "id": "updateContact",
3239
- "name": "Update existing contact",
3240
- "type": "action",
3241
- "action": {
3242
- "platform": "hub-spot",
3243
- "actionId": "HUBSPOT_UPDATE_CONTACT_ACTION_ID",
3244
- "connectionKey": "$.input.hubspotConnectionKey",
3245
- "pathVars": { "contactId": "$.steps.searchContact.response.results[0].id" },
3246
- "data": {
3247
- "properties": { "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
3248
- }
3249
- }
3250
- }
3251
- ],
3252
- "else": [
3253
- {
3254
- "id": "createContact",
3255
- "name": "Create new contact",
3256
- "type": "action",
3257
- "action": {
3258
- "platform": "hub-spot",
3259
- "actionId": "HUBSPOT_CREATE_CONTACT_ACTION_ID",
3260
- "connectionKey": "$.input.hubspotConnectionKey",
3261
- "data": {
3262
- "properties": { "email": "$.input.email", "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
3263
- }
3264
- }
3265
- }
3266
- ]
3267
- }
3268
- }
3269
- ]
3270
- }
3143
+ {"type": "url", "url": "https://your-app.com/webhook", "eventFilters": ["customer.created"]}
3271
3144
  \`\`\`
3272
3145
 
3273
- ### Example 3: Loop workflow \u2014 Iterate over Shopify orders, create invoices
3274
-
3146
+ **agent** \u2014 send to an agent:
3275
3147
  \`\`\`json
3276
- {
3277
- "key": "shopify-to-invoices",
3278
- "name": "Shopify Orders to Invoices",
3279
- "description": "Fetch recent Shopify orders and create an invoice for each",
3280
- "version": "1",
3281
- "inputs": {
3282
- "shopifyConnectionKey": {
3283
- "type": "string",
3284
- "required": true,
3285
- "connection": { "platform": "shopify" }
3286
- },
3287
- "qbConnectionKey": {
3288
- "type": "string",
3289
- "required": true,
3290
- "connection": { "platform": "quick-books" }
3291
- }
3292
- },
3293
- "steps": [
3294
- {
3295
- "id": "listOrders",
3296
- "name": "List recent Shopify orders",
3297
- "type": "action",
3298
- "action": {
3299
- "platform": "shopify",
3300
- "actionId": "SHOPIFY_LIST_ORDERS_ACTION_ID",
3301
- "connectionKey": "$.input.shopifyConnectionKey",
3302
- "queryParams": { "status": "any", "limit": "50" }
3303
- }
3304
- },
3305
- {
3306
- "id": "createInvoices",
3307
- "name": "Create invoice for each order",
3308
- "type": "loop",
3309
- "loop": {
3310
- "over": "$.steps.listOrders.response.orders",
3311
- "as": "order",
3312
- "indexAs": "i",
3313
- "steps": [
3314
- {
3315
- "id": "createInvoice",
3316
- "name": "Create QuickBooks invoice",
3317
- "type": "action",
3318
- "onError": { "strategy": "continue" },
3319
- "action": {
3320
- "platform": "quick-books",
3321
- "actionId": "QB_CREATE_INVOICE_ACTION_ID",
3322
- "connectionKey": "$.input.qbConnectionKey",
3323
- "data": {
3324
- "Line": [
3325
- {
3326
- "Amount": "$.loop.order.total_price",
3327
- "Description": "Shopify Order #{{$.loop.order.order_number}}"
3328
- }
3329
- ]
3330
- }
3331
- }
3332
- }
3333
- ]
3334
- }
3335
- },
3336
- {
3337
- "id": "summary",
3338
- "name": "Generate summary",
3339
- "type": "transform",
3340
- "transform": {
3341
- "expression": "({ totalOrders: $.steps.listOrders.response.orders.length, processed: $.steps.createInvoices.output.length })"
3342
- }
3343
- }
3344
- ]
3345
- }
3148
+ {"type": "agent", "agentId": "<uuid>", "eventFilters": ["customer.created"]}
3346
3149
  \`\`\`
3347
3150
 
3348
- ## CLI Commands Reference
3349
-
3350
- \`\`\`bash
3351
- # Create a workflow
3352
- one --agent flow create <key> --definition '<json>'
3353
-
3354
- # List all workflows
3355
- one --agent flow list
3356
-
3357
- # Validate a workflow
3358
- one --agent flow validate <key>
3359
-
3360
- # Execute a workflow
3361
- one --agent flow execute <key> -i connectionKey=value -i param=value
3362
-
3363
- # Execute with dry run (validate only)
3364
- one --agent flow execute <key> --dry-run -i connectionKey=value
3151
+ ## Supported Source Platforms
3365
3152
 
3366
- # Execute with mock mode (dry-run + mock API responses, runs transforms/code normally)
3367
- one --agent flow execute <key> --dry-run --mock -i connectionKey=value
3153
+ Airtable, Attio, GitHub, Google Calendar, Stripe
3368
3154
 
3369
- # Execute with bash steps enabled
3370
- one --agent flow execute <key> --allow-bash -i connectionKey=value
3155
+ Any connected platform can be a destination via passthrough actions.
3371
3156
 
3372
- # Execute with verbose output
3373
- one --agent flow execute <key> -v -i connectionKey=value
3374
-
3375
- # List workflow runs
3376
- one --agent flow runs [flowKey]
3377
-
3378
- # Resume a paused/failed run
3379
- one --agent flow resume <runId>
3380
- \`\`\`
3157
+ ## Debugging
3381
3158
 
3382
- ## Important Notes
3383
-
3384
- - **Always use \`--agent\` flag** for structured JSON output
3385
- - **Always call \`one actions knowledge\`** before adding an action step to a workflow
3386
- - Platform names are **kebab-case** (e.g., \`hub-spot\`, not \`HubSpot\`)
3387
- - Connection keys are **inputs**, not hardcoded \u2014 makes workflows portable and shareable
3388
- - Use \`$.input.*\` for input values, \`$.steps.*\` for step results
3389
- - Action IDs in examples (like \`STRIPE_SEARCH_CUSTOMERS_ACTION_ID\`) are placeholders \u2014 always use \`one actions search\` to find the real IDs
3390
- - **Parallel step outputs** are accessible both by index (\`$.steps.parallelStep.output[0]\`) and by substep ID (\`$.steps.substepId.response\`)
3391
- - **Loop step outputs** include iteration details via \`$.steps.myLoop.response.iterations[0].innerStepId.response\`
3392
- - **Code steps** support \`await require('crypto')\`, \`await require('buffer')\`, \`await require('url')\`, \`await require('path')\` \u2014 \`fs\`, \`http\`, \`child_process\`, etc. are blocked
3393
- - **Bash steps** require \`--allow-bash\` flag for security
3394
- - **State is persisted** after every step completion \u2014 resume picks up where it left off
3159
+ 1. \`relay get <id>\` \u2014 verify endpoint is active with actions configured
3160
+ 2. \`relay events --platform <p>\` \u2014 check events are arriving
3161
+ 3. \`relay deliveries --event-id <id>\` \u2014 check delivery status and errors
3162
+ 4. \`relay event <id>\` \u2014 inspect full payload to verify template paths
3395
3163
  `;
3396
3164
  var TOPICS = [
3397
- { topic: "overview", description: "Setup, --agent flag, discovery workflow" },
3165
+ { topic: "overview", description: "Setup, features, and quick start for each" },
3398
3166
  { topic: "actions", description: "Search, read docs, and execute platform actions" },
3399
3167
  { topic: "flows", description: "Build and execute multi-step workflows" },
3168
+ { topic: "relay", description: "Receive webhooks and forward to other platforms" },
3400
3169
  { topic: "all", description: "Complete guide (all topics combined)" }
3401
3170
  ];
3402
3171
  function getGuideContent(topic) {
@@ -3407,10 +3176,12 @@ function getGuideContent(topic) {
3407
3176
  return { title: "One CLI \u2014 Agent Guide: Actions", content: GUIDE_ACTIONS };
3408
3177
  case "flows":
3409
3178
  return { title: "One CLI \u2014 Agent Guide: Workflows", content: GUIDE_FLOWS };
3179
+ case "relay":
3180
+ return { title: "One CLI \u2014 Agent Guide: Relay", content: GUIDE_RELAY };
3410
3181
  case "all":
3411
3182
  return {
3412
3183
  title: "One CLI \u2014 Agent Guide: Complete",
3413
- content: [GUIDE_OVERVIEW, GUIDE_ACTIONS, GUIDE_FLOWS].join("\n---\n\n")
3184
+ content: [GUIDE_OVERVIEW, GUIDE_ACTIONS, GUIDE_FLOWS, GUIDE_RELAY].join("\n---\n\n")
3414
3185
  };
3415
3186
  }
3416
3187
  }
@@ -3419,7 +3190,7 @@ function getAvailableTopics() {
3419
3190
  }
3420
3191
 
3421
3192
  // src/commands/guide.ts
3422
- var VALID_TOPICS = ["overview", "actions", "flows", "all"];
3193
+ var VALID_TOPICS = ["overview", "actions", "flows", "relay", "all"];
3423
3194
  async function guideCommand(topic = "all") {
3424
3195
  if (!VALID_TOPICS.includes(topic)) {
3425
3196
  error(
@@ -3432,14 +3203,14 @@ async function guideCommand(topic = "all") {
3432
3203
  json({ topic, title, content, availableTopics });
3433
3204
  return;
3434
3205
  }
3435
- intro2(pc8.bgCyan(pc8.black(" One Guide ")));
3206
+ intro2(pc9.bgCyan(pc9.black(" One Guide ")));
3436
3207
  console.log();
3437
3208
  console.log(content);
3438
- console.log(pc8.dim("\u2500".repeat(60)));
3209
+ console.log(pc9.dim("\u2500".repeat(60)));
3439
3210
  console.log(
3440
- pc8.dim("Available topics: ") + availableTopics.map((t) => pc8.cyan(t.topic)).join(", ")
3211
+ pc9.dim("Available topics: ") + availableTopics.map((t) => pc9.cyan(t.topic)).join(", ")
3441
3212
  );
3442
- console.log(pc8.dim(`Run ${pc8.cyan("one guide <topic>")} for a specific section.`));
3213
+ console.log(pc9.dim(`Run ${pc9.cyan("one guide <topic>")} for a specific section.`));
3443
3214
  }
3444
3215
 
3445
3216
  // src/lib/platform-meta.ts
@@ -3587,18 +3358,30 @@ The \`--agent\` flag gives structured JSON output. Always include it right
3587
3358
  after \`one\`:
3588
3359
  one --agent <command>
3589
3360
 
3361
+ ### IMPORTANT: Learn before you use
3362
+ Before using any feature (actions, flows, relay), you MUST read the
3363
+ corresponding skill documentation first. The skills are bundled with
3364
+ the CLI and teach you the correct workflow, required steps, and
3365
+ common mistakes to avoid. Never guess \u2014 read the skill, then act.
3366
+
3590
3367
  ### Quick reference:
3591
3368
  - \`one --agent list\` \u2014 See connected platforms and connection keys
3592
3369
  - \`one --agent actions search <platform> "<query>"\` \u2014 Find actions
3593
3370
  - \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read docs (REQUIRED before execute)
3594
3371
  - \`one --agent actions execute <platform> <actionId> <connectionKey>\` \u2014 Execute action
3595
3372
  - \`one --agent flow create\` \u2014 Build multi-step workflows
3373
+ - \`one --agent relay create\` \u2014 Set up webhook relay (receive events, forward to other platforms)
3596
3374
  - \`one --agent guide\` \u2014 Full documentation
3597
3375
  - \`one add <platform>\` \u2014 Connect a new platform (interactive, no --agent)
3598
3376
 
3599
3377
  ### Workflow: search -> knowledge -> execute
3600
3378
  Always read the knowledge before executing. It tells you required parameters,
3601
3379
  validation rules, and platform-specific details.
3380
+
3381
+ ### Webhook Relay
3382
+ Use \`one relay\` to receive webhooks from platforms (Stripe, GitHub, etc.)
3383
+ and forward event data to other platforms using passthrough actions with
3384
+ Handlebars templates. No middleware needed.
3602
3385
  \`\`\``);
3603
3386
  sections.push(buildCurrentState(connections));
3604
3387
  sections.push(`## How To Use the CLI
@@ -3624,6 +3407,17 @@ The \`--agent\` flag goes right after \`one\`, before the subcommand.
3624
3407
  Use \`one flow create\` to build JSON workflows that chain actions across
3625
3408
  platforms with conditions, loops, parallel execution, and transforms.
3626
3409
 
3410
+ ### Webhook Relay:
3411
+ Use \`one relay create\` to receive webhooks from platforms (Stripe, GitHub,
3412
+ Airtable, Attio, Google Calendar) and forward event data to any connected
3413
+ platform using passthrough actions with Handlebars templates.
3414
+
3415
+ ### IMPORTANT: Learn before you use
3416
+ Before using flows or relay, you MUST read the corresponding skill
3417
+ documentation first. The skills teach you the correct workflow, template
3418
+ syntax, required steps, and common mistakes. Never guess \u2014 read the
3419
+ skill, then act.
3420
+
3627
3421
  Run \`one --agent guide\` for the complete reference documentation with examples.`);
3628
3422
  sections.push(`## What to tell the user
3629
3423
 
@@ -3849,6 +3643,14 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
3849
3643
  one flow execute <key> Execute a workflow
3850
3644
  one flow validate <key> Validate a flow
3851
3645
 
3646
+ Webhook Relay:
3647
+ one relay create Create a relay endpoint for a connection
3648
+ one relay list List relay endpoints
3649
+ one relay activate <id> Activate with passthrough actions
3650
+ one relay event-types <platform> List supported event types
3651
+ one relay events List received webhook events
3652
+ one relay deliveries List delivery attempts
3653
+
3852
3654
  Example \u2014 send an email through Gmail:
3853
3655
  $ one list
3854
3656
  # Find: gmail operational live::gmail::default::abc123
@@ -3943,7 +3745,38 @@ flow.command("resume <runId>").description("Resume a paused or failed workflow r
3943
3745
  flow.command("runs [flowKey]").description("List workflow runs (optionally filtered by flow key)").action(async (flowKey) => {
3944
3746
  await flowRunsCommand(flowKey);
3945
3747
  });
3946
- program.command("guide [topic]").description("Full CLI usage guide for agents (topics: overview, actions, flows, all)").action(async (topic) => {
3748
+ var relay = program.command("relay").alias("r").description("Receive webhooks from platforms and relay them via passthrough actions");
3749
+ relay.command("create").description("Create a new relay endpoint for a connection").requiredOption("--connection-key <key>", "Connection key for the source platform").option("--description <desc>", "Description of the relay endpoint").option("--event-filters <json>", `JSON array of event types to filter (e.g. '["customer.created"]')`).option("--tags <json>", "JSON array of tags").option("--create-webhook", "Automatically register the webhook with the source platform").action(async (options) => {
3750
+ await relayCreateCommand(options);
3751
+ });
3752
+ relay.command("list").alias("ls").description("List all relay endpoints").option("--limit <n>", "Max results per page").option("--page <n>", "Page number").action(async (options) => {
3753
+ await relayListCommand(options);
3754
+ });
3755
+ relay.command("get <id>").description("Get details of a relay endpoint").action(async (id) => {
3756
+ await relayGetCommand(id);
3757
+ });
3758
+ relay.command("update <id>").description("Update a relay endpoint").option("--description <desc>", "Update description").option("--active", "Set active").option("--no-active", "Set inactive").option("--event-filters <json>", "JSON array of event types").option("--tags <json>", "JSON array of tags").option("--actions <json>", "JSON array of actions (url, passthrough, or agent)").action(async (id, options) => {
3759
+ await relayUpdateCommand(id, options);
3760
+ });
3761
+ relay.command("delete <id>").description("Delete a relay endpoint").action(async (id) => {
3762
+ await relayDeleteCommand(id);
3763
+ });
3764
+ relay.command("activate <id>").description("Activate a relay endpoint with forwarding actions").requiredOption("--actions <json>", "JSON array of actions (url, passthrough, or agent)").option("--webhook-secret <secret>", "Webhook signing secret for signature verification").action(async (id, options) => {
3765
+ await relayActivateCommand(id, options);
3766
+ });
3767
+ relay.command("events").description("List received webhook events").option("--limit <n>", "Max results per page").option("--page <n>", "Page number").option("--platform <platform>", "Filter by platform").option("--event-type <type>", "Filter by event type").option("--after <iso>", "Events after this timestamp").option("--before <iso>", "Events before this timestamp").action(async (options) => {
3768
+ await relayEventsCommand(options);
3769
+ });
3770
+ relay.command("event <id>").description("Get details of a specific webhook event").action(async (id) => {
3771
+ await relayEventGetCommand(id);
3772
+ });
3773
+ relay.command("deliveries").description("List delivery attempts for an endpoint or event").option("--endpoint-id <id>", "Relay endpoint ID").option("--event-id <id>", "Relay event ID").action(async (options) => {
3774
+ await relayDeliveriesCommand(options);
3775
+ });
3776
+ relay.command("event-types <platform>").description("List supported webhook event types for a platform").action(async (platform) => {
3777
+ await relayEventTypesCommand(platform);
3778
+ });
3779
+ program.command("guide [topic]").description("Full CLI usage guide for agents (topics: overview, actions, flows, relay, all)").action(async (topic) => {
3947
3780
  await guideCommand(topic);
3948
3781
  });
3949
3782
  program.command("onboard").description("Agent onboarding \u2014 teaches your agent what the One CLI can do").option("--step <number>", "Run a specific onboarding step (1, 2, or 3)").action(async (options) => {