@withone/cli 1.33.0 → 1.35.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/README.md
CHANGED
|
@@ -39,7 +39,14 @@ npm install -g @withone/cli
|
|
|
39
39
|
one init
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
`one init` walks you through setup: enter your [API key](https://app.withone.ai/settings/api-keys), pick your AI agents, and you're done. The MCP server gets installed automatically.
|
|
42
|
+
`one init` walks you through setup: authenticate via browser or enter your [API key](https://app.withone.ai/settings/api-keys), pick your AI agents, and you're done. The MCP server gets installed automatically.
|
|
43
|
+
|
|
44
|
+
Or authenticate directly:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
one login # Opens browser for authentication (global or per-directory)
|
|
48
|
+
one logout # Clear credentials (with scope picker and confirmation)
|
|
49
|
+
```
|
|
43
50
|
|
|
44
51
|
Requires Node.js 18+.
|
|
45
52
|
|
|
@@ -98,7 +105,7 @@ one flow validate welcome-customer
|
|
|
98
105
|
one flow execute welcome-customer -i email=jane@example.com
|
|
99
106
|
```
|
|
100
107
|
|
|
101
|
-
Workflows live under `.one/flows/<key>/flow.json` with an optional `lib/` subfolder for `.mjs` code modules — create new flows in this folder layout. (The legacy single-file layout `.one/flows/<key>.flow.json` is deprecated but still loads for backward compatibility.) Flows support conditions, loops, while loops, parallel steps, transforms, sub-flows, pagination, bash steps, and external `.mjs` code modules. Run `one guide flows` for the full reference.
|
|
108
|
+
Workflows live under `.one/flows/<key>/flow.json` with an optional `lib/` subfolder for `.mjs` code modules — create new flows in this folder layout. Flows can be organized into subdirectory groups: `.one/flows/<group>/<key>/flow.json`. Reference them as `group/key` or just the bare key if unique. (The legacy single-file layout `.one/flows/<key>.flow.json` is deprecated but still loads for backward compatibility.) Flows support conditions, loops, while loops, parallel steps, transforms, sub-flows, pagination, bash steps, and external `.mjs` code modules. Run `one guide flows` for the full reference.
|
|
102
109
|
|
|
103
110
|
## How it works
|
|
104
111
|
|
|
@@ -357,12 +364,15 @@ In agent mode (`--agent`), the JSON response includes the guide content and an `
|
|
|
357
364
|
|
|
358
365
|
### `one flow create [key]`
|
|
359
366
|
|
|
360
|
-
Create a workflow from a JSON definition. New workflows are always saved to the folder layout at `.one/flows/<key>/flow.json` (with a `lib/` subfolder scaffolded for code modules). The legacy `.one/flows/<key>.flow.json` single-file layout is deprecated; existing legacy files continue to load and run unchanged for backward compatibility.
|
|
367
|
+
Create a workflow from a JSON definition. New workflows are always saved to the folder layout at `.one/flows/<key>/flow.json` (with a `lib/` subfolder scaffolded for code modules). Use `group/key` to place flows in a subdirectory group (e.g. `.one/flows/research/company-research/flow.json`). The legacy `.one/flows/<key>.flow.json` single-file layout is deprecated; existing legacy files continue to load and run unchanged for backward compatibility.
|
|
361
368
|
|
|
362
369
|
```bash
|
|
363
370
|
# From a --definition flag
|
|
364
371
|
one flow create welcome-customer --definition '{"key":"welcome-customer","name":"Welcome","version":"1","inputs":{},"steps":[]}'
|
|
365
372
|
|
|
373
|
+
# Create in a subdirectory group
|
|
374
|
+
one flow create research/company-research --definition @flow.json
|
|
375
|
+
|
|
366
376
|
# From stdin
|
|
367
377
|
cat flow.json | one flow create
|
|
368
378
|
|
|
@@ -1042,7 +1042,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
1042
1042
|
if (flowStack.includes(resolvedKey)) {
|
|
1043
1043
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
1044
1044
|
}
|
|
1045
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
1045
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-TNI7YWVY.js");
|
|
1046
1046
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
1047
1047
|
const subContext = await executeFlow(
|
|
1048
1048
|
subFlow,
|
|
@@ -1847,7 +1847,18 @@ Workflows live in \`.one/flows/\` (relative to your current working directory \u
|
|
|
1847
1847
|
- **Folder layout (REQUIRED for new flows)** \u2014 \`.one/flows/<key>/flow.json\`, with an optional \`lib/\` subfolder for JavaScript modules. This is like a skill: the folder groups the JSON spec with any JavaScript modules it needs, so the whole flow is shareable. **Always create new flows in this layout.**
|
|
1848
1848
|
- **Single-file layout (DEPRECATED)** \u2014 \`.one/flows/<key>.flow.json\`. Still loads and runs for backward compatibility, but is deprecated. Do not create new flows in this layout. When editing an existing single-file flow, migrate it to the folder layout: move \`<key>.flow.json\` to \`<key>/flow.json\` and extract any non-trivial \`code.source\` blocks into \`<key>/lib/*.mjs\` modules.
|
|
1849
1849
|
|
|
1850
|
-
|
|
1850
|
+
**Subdirectory groups** \u2014 Flows can be organized into subdirectories: \`.one/flows/<group>/<key>/flow.json\`. For example:
|
|
1851
|
+
\`\`\`
|
|
1852
|
+
.one/flows/
|
|
1853
|
+
research/
|
|
1854
|
+
company-research/flow.json
|
|
1855
|
+
competitor-research/flow.json
|
|
1856
|
+
deal-ops/
|
|
1857
|
+
deal-log/flow.json
|
|
1858
|
+
\`\`\`
|
|
1859
|
+
Reference grouped flows with \`group/key\` (e.g. \`one flow execute research/company-research\`) or just the bare key if it's unique (e.g. \`one flow execute company-research\`). Create grouped flows with \`one flow create research/company-research --definition ...\`. \`flow list\` shows the group prefix.
|
|
1860
|
+
|
|
1861
|
+
When resolving a flow by key, the CLI checks the folder layout first, then the deprecated legacy file, then scans group subdirectories. The \`loadFlow\` helper in agent integrations behaves the same.
|
|
1851
1862
|
|
|
1852
1863
|
## Before you execute a flow you did NOT author \u2014 READ THIS
|
|
1853
1864
|
|
|
@@ -1872,19 +1883,21 @@ A good description is one paragraph. If a flow's description doesn't tell you ho
|
|
|
1872
1883
|
## Commands
|
|
1873
1884
|
|
|
1874
1885
|
\`\`\`bash
|
|
1875
|
-
one --agent flow create <key> --definition '<json>'
|
|
1876
|
-
one --agent flow create <key> --definition @flow.json
|
|
1877
|
-
one --agent flow
|
|
1878
|
-
one --agent flow
|
|
1879
|
-
one --agent flow
|
|
1880
|
-
one --agent flow execute <key>
|
|
1881
|
-
one --agent flow execute <key>
|
|
1882
|
-
one --agent flow
|
|
1883
|
-
one --agent flow
|
|
1884
|
-
one --agent flow
|
|
1886
|
+
one --agent flow create <key> --definition '<json>' # Create (or --definition @file.json)
|
|
1887
|
+
one --agent flow create <key> --definition @flow.json # Create from file
|
|
1888
|
+
one --agent flow create <group/key> --definition '<json>' # Create in a subdirectory group
|
|
1889
|
+
one --agent flow list # List (shows group prefixes)
|
|
1890
|
+
one --agent flow validate <key> # Validate
|
|
1891
|
+
one --agent flow execute <key> -i name=value # Execute (bare key)
|
|
1892
|
+
one --agent flow execute <group/key> -i name=value # Execute (namespaced key)
|
|
1893
|
+
one --agent flow execute <key> --dry-run --mock # Test with mock data
|
|
1894
|
+
one --agent flow execute <key> --allow-bash # Enable bash steps
|
|
1895
|
+
one --agent flow runs [flowKey] # List past runs
|
|
1896
|
+
one --agent flow resume <runId> # Resume failed run
|
|
1897
|
+
one --agent flow scaffold [template] # Generate a starter template
|
|
1885
1898
|
\`\`\`
|
|
1886
1899
|
|
|
1887
|
-
You can also write the JSON file directly to \`.one/flows/<key>/flow.json\` \u2014 often easier than passing large JSON via --definition. (The legacy \`.one/flows/<key>.flow.json\` single-file location is deprecated; don't use it for new flows.)
|
|
1900
|
+
You can also write the JSON file directly to \`.one/flows/<key>/flow.json\` (or \`.one/flows/<group>/<key>/flow.json\` for grouped flows) \u2014 often easier than passing large JSON via --definition. (The legacy \`.one/flows/<key>.flow.json\` single-file location is deprecated; don't use it for new flows.)
|
|
1888
1901
|
|
|
1889
1902
|
## Code modules (flow \`lib/\` folder)
|
|
1890
1903
|
|
|
@@ -2501,13 +2514,31 @@ var FlowRunner = class _FlowRunner {
|
|
|
2501
2514
|
}
|
|
2502
2515
|
};
|
|
2503
2516
|
function resolveFlowPath(keyOrPath) {
|
|
2504
|
-
if (keyOrPath.
|
|
2517
|
+
if (keyOrPath.endsWith(".json")) {
|
|
2518
|
+
return path2.resolve(keyOrPath);
|
|
2519
|
+
}
|
|
2520
|
+
if (keyOrPath.includes("\\")) {
|
|
2505
2521
|
return path2.resolve(keyOrPath);
|
|
2506
2522
|
}
|
|
2523
|
+
if (keyOrPath.includes("/")) {
|
|
2524
|
+
const nestedFolder = path2.resolve(FLOWS_DIR, keyOrPath, "flow.json");
|
|
2525
|
+
if (fs2.existsSync(nestedFolder)) return nestedFolder;
|
|
2526
|
+
const literal = path2.resolve(keyOrPath);
|
|
2527
|
+
if (fs2.existsSync(literal)) return literal;
|
|
2528
|
+
return nestedFolder;
|
|
2529
|
+
}
|
|
2507
2530
|
const folderPath = path2.resolve(FLOWS_DIR, keyOrPath, "flow.json");
|
|
2508
|
-
const legacyPath = path2.resolve(FLOWS_DIR, `${keyOrPath}.flow.json`);
|
|
2509
2531
|
if (fs2.existsSync(folderPath)) return folderPath;
|
|
2532
|
+
const legacyPath = path2.resolve(FLOWS_DIR, `${keyOrPath}.flow.json`);
|
|
2510
2533
|
if (fs2.existsSync(legacyPath)) return legacyPath;
|
|
2534
|
+
const flowsDir = path2.resolve(FLOWS_DIR);
|
|
2535
|
+
if (fs2.existsSync(flowsDir)) {
|
|
2536
|
+
for (const entry of fs2.readdirSync(flowsDir, { withFileTypes: true })) {
|
|
2537
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
2538
|
+
const nested = path2.join(flowsDir, entry.name, keyOrPath, "flow.json");
|
|
2539
|
+
if (fs2.existsSync(nested)) return nested;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2511
2542
|
return folderPath;
|
|
2512
2543
|
}
|
|
2513
2544
|
function getFlowRootDir(flowFilePath) {
|
|
@@ -2572,12 +2603,13 @@ function listFlows() {
|
|
|
2572
2603
|
if (!fs2.existsSync(flowsDir)) return [];
|
|
2573
2604
|
const flows = [];
|
|
2574
2605
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
2575
|
-
const readFlowFile = (filePath) => {
|
|
2606
|
+
const readFlowFile = (filePath, group) => {
|
|
2576
2607
|
try {
|
|
2577
2608
|
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2578
2609
|
const flow = JSON.parse(content);
|
|
2579
|
-
|
|
2580
|
-
seenKeys.
|
|
2610
|
+
const nsKey = group ? `${group}/${flow.key}` : flow.key;
|
|
2611
|
+
if (seenKeys.has(nsKey)) return;
|
|
2612
|
+
seenKeys.add(nsKey);
|
|
2581
2613
|
flows.push({
|
|
2582
2614
|
key: flow.key,
|
|
2583
2615
|
name: flow.name,
|
|
@@ -2586,6 +2618,7 @@ function listFlows() {
|
|
|
2586
2618
|
stepCount: flow.steps.length,
|
|
2587
2619
|
path: filePath,
|
|
2588
2620
|
layout: path2.basename(filePath) === "flow.json" ? "folder" : "legacy",
|
|
2621
|
+
group,
|
|
2589
2622
|
stepTypes: collectStepTypes(flow),
|
|
2590
2623
|
requiresBash: flowRequiresBash(flow),
|
|
2591
2624
|
usesCodeModules: flowUsesCodeModules(flow),
|
|
@@ -2594,26 +2627,44 @@ function listFlows() {
|
|
|
2594
2627
|
} catch {
|
|
2595
2628
|
}
|
|
2596
2629
|
};
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2630
|
+
const scanDir = (dir, group) => {
|
|
2631
|
+
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
2632
|
+
if (entry.name.startsWith(".")) continue;
|
|
2633
|
+
const full = path2.join(dir, entry.name);
|
|
2634
|
+
if (entry.isDirectory()) {
|
|
2635
|
+
const flowJson = path2.join(full, "flow.json");
|
|
2636
|
+
if (fs2.existsSync(flowJson)) {
|
|
2637
|
+
readFlowFile(flowJson, group);
|
|
2638
|
+
} else {
|
|
2639
|
+
if (!group) {
|
|
2640
|
+
scanDir(full, entry.name);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
} else if (entry.isFile() && entry.name.endsWith(".flow.json")) {
|
|
2644
|
+
readFlowFile(full, group);
|
|
2645
|
+
}
|
|
2605
2646
|
}
|
|
2606
|
-
}
|
|
2647
|
+
};
|
|
2648
|
+
scanDir(flowsDir);
|
|
2607
2649
|
return flows;
|
|
2608
2650
|
}
|
|
2609
|
-
function saveFlow(flow, outputPath) {
|
|
2651
|
+
function saveFlow(flow, outputPath, group) {
|
|
2610
2652
|
let flowPath;
|
|
2611
2653
|
if (outputPath) {
|
|
2612
2654
|
flowPath = path2.resolve(outputPath);
|
|
2613
2655
|
} else {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
if (
|
|
2656
|
+
let bareKey = flow.key;
|
|
2657
|
+
let resolvedGroup = group;
|
|
2658
|
+
if (flow.key.includes("/")) {
|
|
2659
|
+
const parts = flow.key.split("/");
|
|
2660
|
+
bareKey = parts.pop();
|
|
2661
|
+
resolvedGroup = resolvedGroup || parts.join("/");
|
|
2662
|
+
flow.key = bareKey;
|
|
2663
|
+
}
|
|
2664
|
+
const basePath = resolvedGroup ? path2.resolve(FLOWS_DIR, resolvedGroup, bareKey) : path2.resolve(FLOWS_DIR, bareKey);
|
|
2665
|
+
const legacyPath = path2.resolve(FLOWS_DIR, `${bareKey}.flow.json`);
|
|
2666
|
+
const folderPath = path2.join(basePath, "flow.json");
|
|
2667
|
+
if (!resolvedGroup && fs2.existsSync(legacyPath) && !fs2.existsSync(folderPath)) {
|
|
2617
2668
|
flowPath = legacyPath;
|
|
2618
2669
|
} else {
|
|
2619
2670
|
flowPath = folderPath;
|