foldrun 0.6.0 → 0.7.2
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 +97 -15
- package/bin/foldrun.mjs +323 -45
- package/docs/agents.md +483 -0
- package/docs/api-usage.md +156 -0
- package/docs/api.md +435 -0
- package/docs/backups-and-restore.md +171 -0
- package/docs/budgets-and-billing.md +362 -0
- package/docs/cli.md +428 -0
- package/docs/coding-agents.md +224 -0
- package/docs/deploying.md +77 -0
- package/docs/evals.md +90 -0
- package/docs/faq.md +53 -0
- package/docs/flows.md +670 -0
- package/docs/getting-started.md +141 -0
- package/docs/glossary.md +78 -0
- package/docs/harness.md +128 -0
- package/docs/how-it-works.md +255 -0
- package/docs/introduction.md +47 -0
- package/docs/knowledge-and-memory.md +85 -0
- package/docs/notifications.md +320 -0
- package/docs/providers.md +241 -0
- package/docs/runs.md +238 -0
- package/docs/scheduling.md +103 -0
- package/docs/secrets.md +163 -0
- package/docs/security.md +167 -0
- package/docs/self-hosting.md +65 -0
- package/docs/status.md +73 -0
- package/docs/storage-and-sharing.md +118 -0
- package/docs/team-and-access.md +146 -0
- package/docs/tools.md +1810 -0
- package/docs/troubleshooting.md +155 -0
- package/docs/versions.md +79 -0
- package/docs/workspaces.md +160 -0
- package/package.json +9 -5
- package/src/commands.mjs +6426 -248
- package/src/guide.mjs +172 -0
package/bin/foldrun.mjs
CHANGED
|
@@ -6,27 +6,58 @@
|
|
|
6
6
|
// point. A framework whose free version is a crippled demo gets no adoption,
|
|
7
7
|
// and adoption is the only reason a hosted version has customers.
|
|
8
8
|
//
|
|
9
|
-
// foldrun init [dir] scaffold a
|
|
9
|
+
// foldrun init [dir] scaffold an account: a library, and a workspace in it
|
|
10
10
|
// foldrun check [dir] validate it — no model calls, no cost
|
|
11
11
|
// foldrun run <target> run an agent or a flow
|
|
12
12
|
// foldrun eval [name] run evals
|
|
13
13
|
// foldrun probe <model> can this model hold a tool loop? (live check)
|
|
14
14
|
// foldrun logs [run-id] recent runs, or one run's full event trail
|
|
15
|
+
// foldrun runs what has run lately, across the account
|
|
16
|
+
// foldrun report <run-id> one run, whole: every step, what it cost, what it wrote
|
|
17
|
+
// foldrun approvals what is waiting for a person, anywhere in the account
|
|
18
|
+
// foldrun approve <run-id> release a waiting gate (asks first) — reject refuses it
|
|
19
|
+
// foldrun stop <run-id> kill a run in flight
|
|
20
|
+
// foldrun account the account's own defaults — timezone, notify, budget, concurrency
|
|
21
|
+
// foldrun schedule every flow in the account that fires on a clock, and when it fires next
|
|
22
|
+
// foldrun triggers why nothing ran: fired vs started per flow, and each reason
|
|
23
|
+
// foldrun webhooks <verb> deliveries / redeliver <id> — the notify: webhooks a workspace sent, and each attempt
|
|
24
|
+
// foldrun notifications what mail you get, per category — set <category> on|off
|
|
25
|
+
// foldrun backups how the account is backed up, its snapshots — request a restore from one
|
|
26
|
+
// foldrun restore <ws> a workspace's source back to a point in its history (shows the change, asks)
|
|
27
|
+
// foldrun onboarding the account's getting-started steps, the next one first
|
|
28
|
+
// foldrun billing the balance, and what the money went on
|
|
29
|
+
// foldrun gallery the tools the platform ships to every account — list, pull, upgrade <tool>
|
|
30
|
+
// foldrun storage <verb> ls / cat / get / put / rm / share / shares / unshare — what the agents produced, and public links to it
|
|
31
|
+
// foldrun runtimes the environments a workspace's agents need, and whether each is built
|
|
15
32
|
// foldrun secrets <verb> set / ls / rm — the vault, from the terminal
|
|
16
|
-
// foldrun
|
|
33
|
+
// foldrun new <name> another workspace in this account
|
|
34
|
+
// foldrun agent new <name> one more agent in this workspace — also flow new, tool new
|
|
35
|
+
// foldrun agent run <name> run one agent once on a platform, no flow
|
|
36
|
+
// foldrun tool test <name> exercise one tool alone — no model, no run
|
|
37
|
+
// foldrun deploy [dir] push this account — or one workspace — into an installation
|
|
38
|
+
// foldrun pull bring the platform's account down into this folder
|
|
39
|
+
// foldrun status what differs here from what is deployed
|
|
40
|
+
// foldrun workspaces what exists here, and there
|
|
17
41
|
// foldrun invoke <flow> start a flow on a running platform
|
|
18
42
|
// foldrun source <verb> ls / cat / put / mv / rm one workspace file on a platform
|
|
19
43
|
// foldrun open [page] the dashboard for this workspace
|
|
20
44
|
// foldrun login sign this machine in from the browser
|
|
45
|
+
// foldrun login <site> a browser window to sign in to a site by hand; the session
|
|
46
|
+
// (cookies, storage, identity) is stored for an agent to wear
|
|
21
47
|
// foldrun whoami who the platform thinks this terminal is
|
|
22
48
|
// foldrun doctor what is between this terminal and the platform, checked
|
|
23
49
|
// foldrun accounts every account signed in here; `use <name>` switches
|
|
24
50
|
// foldrun keys <verb> ls / create / revoke — the account's API keys
|
|
51
|
+
// foldrun library <verb> ls / cat / put / rm — the account's shared shelf on the platform
|
|
52
|
+
// foldrun find <words> search the account the way the dashboard's ⌘K does
|
|
53
|
+
// foldrun changelog what each release of the platform changed
|
|
54
|
+
// foldrun preferences your own settings on the platform — the theme
|
|
25
55
|
//
|
|
26
56
|
// `check` is the one to run in CI: it catches the mistakes that otherwise only
|
|
27
57
|
// show up as a confidently wrong answer at 3am.
|
|
28
58
|
|
|
29
59
|
import { fileURLToPath } from "node:url";
|
|
60
|
+
import fs from "node:fs";
|
|
30
61
|
import path from "node:path";
|
|
31
62
|
import process from "node:process";
|
|
32
63
|
|
|
@@ -39,47 +70,199 @@ process.on("warning", () => {});
|
|
|
39
70
|
|
|
40
71
|
const [, , command, ...rest] = process.argv;
|
|
41
72
|
|
|
73
|
+
// `foldrun --version`: the package's own number and nothing else — no core,
|
|
74
|
+
// no network, no account. Before every import below, so it stays instant.
|
|
75
|
+
// `foldrun version` is the long form that also asks the platform.
|
|
76
|
+
if (command === "--version" || command === "-v") {
|
|
77
|
+
try {
|
|
78
|
+
console.log(`foldrun ${JSON.parse(fs.readFileSync(path.join(HERE, "../package.json"), "utf8")).version}`);
|
|
79
|
+
} catch {
|
|
80
|
+
console.log("foldrun (unknown version)");
|
|
81
|
+
}
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
|
|
42
85
|
const HELP = `foldrun — agents are just folders
|
|
43
86
|
|
|
44
|
-
foldrun init [dir] create
|
|
45
|
-
foldrun
|
|
87
|
+
foldrun init [dir] create an account folder: AGENTS.md, library/ and workspaces/<name>
|
|
88
|
+
foldrun new <name> another workspace in this account
|
|
89
|
+
foldrun agent new <name> one more agent in this workspace (--to <workspace> in an account)
|
|
90
|
+
foldrun flow new <name> one more flow — its first step names an agent you already have
|
|
91
|
+
foldrun tool new <name> one more tool: a folder with its program beside it (--transport, --language)
|
|
92
|
+
foldrun check [dir] validate every workspace here, and the shared library
|
|
93
|
+
foldrun check --to <ws> validate the DEPLOYED copy instead — fetched from the platform, checked here
|
|
94
|
+
foldrun agent run <name> run one agent once on the platform (--task "…", --to, --wait, --test)
|
|
95
|
+
foldrun agent link <agent> add to its team, in its frontmatter: --subagent <worker>, --consult <agent> or --can-ask (--description for a worker with none)
|
|
96
|
+
foldrun agent unlink <agent> take one away: --subagent <name>, --consult <name> or --can-ask
|
|
97
|
+
foldrun flow add <flow> <pattern> one canvas block as a markdown edit: chain, parallel, router, fan-out, loop, approval, ask, wait, rescue, subflow — diff shown, checked, --dry-run
|
|
98
|
+
foldrun flow rm-step <flow> --step <n|agent> delete one step (its options too); groups renumber, agent file untouched — says what else changes, diff, checked, asks (--yes), --dry-run
|
|
99
|
+
foldrun flow dup-step <flow> --step <n|agent> copy one step (marker and options) directly under it, in parallel with it — diff, checked, --dry-run
|
|
100
|
+
foldrun flow show <flow> the flow as the canvas draws it: trigger, groups, step chips, each agent's team, check problems in place
|
|
101
|
+
foldrun flow run <flow> start a flow on the platform — the same command as invoke
|
|
102
|
+
foldrun flow rotate-hook <flow> a new webhook URL for a flow; the old one stops at once — asks first, --yes means it
|
|
103
|
+
foldrun flow set <flow> --step <n|agent> key=value … the step editor: model, effort, retry, timeout, verify, when, case, else, loop, until, each, max, limits, instruction (key= clears) — diff, checked, --dry-run
|
|
104
|
+
foldrun flow trigger <flow> manual|webhook|schedule the trigger picker (--schedule "<cron>", --timezone <zone>) — diff, checked, --dry-run
|
|
105
|
+
foldrun flow move-step <flow> --step <n|agent> drag a card: --group <g> runs it beside group g, --after <g> gives it its own group after g (0 = first) — diff, checked, --dry-run
|
|
106
|
+
foldrun flow copy-step <flow> --step <n|agent>[,<n>…] the steps' markdown on stdout, as the canvas's Copy
|
|
107
|
+
foldrun flow paste <flow> step markdown from stdin or --file in as new groups (--after <g>, default last) or beside one (--group <g>) — diff, checked, --dry-run
|
|
108
|
+
foldrun flow draft "<what it should do>" Draft with AI on the platform (--to; --flow <name> redrafts one): shows the files and the check, saves on yes (--yes, --dry-run)
|
|
109
|
+
foldrun tool test <name> exercise one tool alone, no model and no run (key=value args, --to)
|
|
46
110
|
foldrun extract [dir] move single-file script tools into folders (tool.md + run.*)
|
|
47
|
-
foldrun run <target> run an agent or flow (target: name, or flow:name)
|
|
48
|
-
foldrun eval [name] run one eval, or all of them
|
|
111
|
+
foldrun run <target> run an agent or flow HERE (target: name, or flow:name) — on the platform: flow run / invoke a flow, agent run one agent
|
|
112
|
+
foldrun eval [name] run one eval, or all of them (--to <workspace>: the deployed evals, run on the platform)
|
|
113
|
+
foldrun promote <run-id> keep a finished run as a regression case in evals/ (--eval <name>, --expect "contains: …" repeatable, --case <name>)
|
|
49
114
|
foldrun probe <model> live check: can this model hold a tool loop here?
|
|
50
|
-
foldrun logs [run-id] recent runs, or one run's full event trail
|
|
51
|
-
foldrun
|
|
52
|
-
foldrun
|
|
53
|
-
foldrun
|
|
54
|
-
foldrun
|
|
115
|
+
foldrun logs [run-id] recent runs in one workspace, or one run's full event trail (--local: this machine's)
|
|
116
|
+
foldrun runs what has run lately, across the account — --status, --verdict, --since, --to, --limit (--local prints exactly what logs --local prints)
|
|
117
|
+
foldrun runs rm <run-id> delete a run's record and archived outputs; a live one is stopped first — asks first, --yes means it
|
|
118
|
+
foldrun report <run-id> one run, whole: header, every step, what it wrote and what is still waiting
|
|
119
|
+
foldrun report <run-id> get <agent>/<path> download a file the run archived from that agent's outputs/ (--file <path>|-, --force)
|
|
120
|
+
foldrun report <run-id> live the live browser view: which agents have a frame; --agent <name> saves the newest as a JPEG (--file <path>|-)
|
|
121
|
+
foldrun approvals every gate waiting on a person, with its question (--to <workspace> for one)
|
|
122
|
+
foldrun approve <run-id> release a waiting gate — asks first, --yes means it, --note "…" steers the step
|
|
123
|
+
foldrun reject <run-id> refuse one, with --note as the reason
|
|
124
|
+
foldrun stop <run-id> kill a run in flight — asks first, --yes means it
|
|
125
|
+
foldrun stop --status running [--since 2h] [--flow f] every run a filter matches (runs/bulk): lists them, asks, stops only those (--dry-run, --to)
|
|
126
|
+
foldrun answer <run-id> "…" answer the question an agent is asking mid-step (--option <n> picks a choice)
|
|
127
|
+
foldrun message <run-id> "…" say something to a running agent — it hears it after its next tool call
|
|
128
|
+
foldrun rerun <run-id> run it again from a step (--from <n>) or from an agent's step (--agent <name>); --wait
|
|
129
|
+
foldrun rerun --status failed [--since 24h] [--flow f] rerun every run a filter matches, from --from <n> (default 1) — lists, asks (--dry-run, --to)
|
|
130
|
+
foldrun observe where the account fails, retries and spends, per workspace (--to <workspace> in full, --since <days>, --json)
|
|
131
|
+
foldrun usage what the account consumed, and what it was charged week by week (--days <n>, --json)
|
|
132
|
+
foldrun schedule every flow that fires on a clock, its cron line and the next times (--to <workspace>)
|
|
133
|
+
foldrun schedule tick fire whatever is due now — Settings' "Run scheduler now"
|
|
134
|
+
foldrun guide write the coding-agent rules: a block in AGENTS.md and CLAUDE.md importing it (--check, --print)
|
|
135
|
+
foldrun docs [page] foldrun's docs, from the copy this CLI ships — what a coding agent should read (--path)
|
|
136
|
+
foldrun triggers why nothing ran: per flow, fired vs started and every reason (--since <days>, --to <workspace>)
|
|
137
|
+
foldrun storage <verb> what a workspace produced: ls [prefix], cat <path> (--preview: a PDF, sheet, deck or zip as text), get <path>, put <file|folder>... (--into <folder/>, --as <path> for one file), rm <path> — and share <path> (--ttl <days>, --forever), shares (--all), unshare <token>
|
|
138
|
+
foldrun runtimes the environments a deployed workspace's agents need (python, pip and npm packages) and whether each is built — failed ones with the installer's error (--wait holds on while any is building)
|
|
139
|
+
foldrun billing the account's balance and its recent ledger entries (--limit <n>)
|
|
140
|
+
foldrun billing statement [YYYY-MM] a month of the ledger (--csv for the file, --file <path> to save it)
|
|
141
|
+
foldrun billing wallet burn, runway, auto top-up — also set auto-top-up --threshold <usd> --amount <usd> | off, set email <address>
|
|
142
|
+
foldrun billing details who invoices are made out to — also set --name "…" --abn … --address "line1, city, state, postcode, AU" --email …
|
|
143
|
+
foldrun billing portal a link to Stripe's billing portal (owner only)
|
|
144
|
+
foldrun billing plans the plans, which one the account is on and this cycle's credits — also plan <id>, plan cancel, plan resume (asks first)
|
|
145
|
+
foldrun billing top-up <usd> Stripe's checkout page for adding credit (owner only; --no-browser prints it)
|
|
146
|
+
foldrun billing card Stripe's page for saving a card for auto top-up (owner only)
|
|
147
|
+
foldrun gallery the platform's built-in tools (web) and whether you keep your own copy — also pull (a copy for offline runs), upgrade <tool>
|
|
148
|
+
foldrun account the account's defaults — also set <key> <value>, clear <key>, providers (--check) (singular; accounts lists logins)
|
|
149
|
+
foldrun account export everything the platform holds about the account as one JSON file (owner only; --file <path>, --force)
|
|
150
|
+
foldrun workspace one deployed workspace's settings (--to) — also set <name|description|timezone|budget|notify> <value>, clear <key>
|
|
151
|
+
foldrun workspace vocabulary the names the dashboard's editor completes in a workspace: agents, flows, skills, tools, secrets, scripts, types, documents (--to, --json)
|
|
152
|
+
foldrun notify test send one test notification from a workspace and say what happened (--to <workspace>)
|
|
153
|
+
foldrun webhooks deliveries the notify: webhooks a workspace sent, newest first — status, attempts, last answer (--failed, --status <s>, --event <e>, --limit, --offset, --to)
|
|
154
|
+
foldrun webhooks redeliver <id> send one delivery again now, same X-Foldrun-Delivery id; exits 1 when refused (--to)
|
|
155
|
+
foldrun notifications what mail you get, category by category, and which are always sent (--json)
|
|
156
|
+
foldrun notifications set <category> on|off turn one off or back on — account-wide, or --to <workspace> for run-alerts and approvals
|
|
157
|
+
foldrun history [path] every change to a deployed workspace, newest first (--id <revision> for its diff, --limit, --to)
|
|
158
|
+
foldrun history restore <path> --id <revision> put one file back as that revision left it, as a new revision — diff, asks (--yes, --dry-run, --to)
|
|
159
|
+
foldrun repo ls a workspace's branches and tags — also diff <branch>, deploy <ref>, merge <branch> (both ask first), rm-branch <branch>, mirror <git-url|off>, mirror-now (--to)
|
|
160
|
+
foldrun restore <ws> --to <commit|time|3d> put a workspace's source back as it was then — shows the change, asks for the name typed back (--dry-run: the diff only, --yes)
|
|
161
|
+
foldrun backups how the account is backed up (encrypted?), when it last was, the snapshots it is in — also request --what runs|state|storage|everything --at "<when>" [--to <ws>] [--note]
|
|
162
|
+
foldrun secrets set NAME store a secret (prompted, never echoed) — also ls, rm, status; --kind file|ssh|api|service-account|m2m for the other shapes
|
|
163
|
+
foldrun secrets clients the OAuth clients saved on the platform — also add <name> (--provider or --authorize-url/--token-url, --client-id, --scopes), rm <name>
|
|
164
|
+
foldrun connect NAME OAuth sign-in from the terminal, stored as an auto-refreshing secret — --client <saved> runs it from a saved client through the platform
|
|
165
|
+
foldrun deploy [dir] push the whole account, or deploy <workspace> for one of them
|
|
166
|
+
foldrun pull [workspace] bring the platform's account down here (refuses to clobber; --force overrides)
|
|
167
|
+
foldrun status [workspace] per workspace: what is added, changed or gone since the last deploy
|
|
168
|
+
foldrun status --platform is the platform up: each component, and any incident or maintenance posted (--json)
|
|
169
|
+
foldrun workspaces what exists here and on the platform — also rm <name> (--platform --yes)
|
|
170
|
+
foldrun workspaces demo make the demo pipeline workspace on the platform — "Try the demo pipeline"
|
|
171
|
+
foldrun library the account's shared library on the platform: ls [skills|tools|scripts|knowledge], cat <kind>/<path>, put <kind>/<path> (--file, else stdin), rm <kind>/<path> (asks)
|
|
172
|
+
foldrun find <words> search the account as the dashboard's ⌘K does: workspaces, agents, flows, tools, skills, knowledge, memory, runs (--json)
|
|
173
|
+
foldrun invoke <flow> start a flow on a running platform (--to <workspace>; --once <key> so a retry never starts a second run; --tag <t> repeatable)
|
|
55
174
|
foldrun source <verb> the files on a platform, one at a time: ls, cat <path>, put <path>, mv, rm (--to <workspace>)
|
|
56
175
|
foldrun open [page] the dashboard for this workspace, in the browser
|
|
176
|
+
foldrun api spec the platform's OpenAPI 3.1 document (GET /api/openapi.json) — stdout, or --out <file>
|
|
177
|
+
foldrun api version the API version this account's requests get, the versions served, the rate limits — also pin <YYYY-MM-DD>, unpin (admin; --json)
|
|
178
|
+
foldrun changelog the platform's release notes, newest first: features, fixes, docs (--limit <n>, --json)
|
|
179
|
+
foldrun version this CLI's version, its core's, and the platform's: release, API version, component shas — warns when the platform ships a newer CLI (--json)
|
|
57
180
|
|
|
58
181
|
Signing in
|
|
59
182
|
foldrun login sign this machine in from the browser (--token <key> to skip it)
|
|
183
|
+
foldrun login <site> --url <address>
|
|
184
|
+
open a browser, sign in to that site by hand, and store the
|
|
185
|
+
session: cookies, storage and the browser identity it needs
|
|
60
186
|
foldrun logout forget this machine's key, and revoke it where allowed
|
|
61
187
|
foldrun whoami who you are on the platform: account, role, workspaces
|
|
188
|
+
foldrun onboarding the account's getting-started steps, done or not, the next one first (--json)
|
|
189
|
+
foldrun preferences your own settings on the platform — the theme; set theme system|light|dark (a key acts for whoever minted it)
|
|
62
190
|
foldrun doctor check the path to the platform: node, CLI, account, DNS, a timed /api/healthz
|
|
63
191
|
foldrun accounts every account signed in on this machine, and which one is active
|
|
64
192
|
foldrun use <name> act as one of them from here on
|
|
65
|
-
foldrun keys ls the account's API keys — also create <label
|
|
66
|
-
foldrun --
|
|
193
|
+
foldrun keys ls the account's API keys: created, last used, expires, scope — also create <label> [--expires 90d], rotate <id> [--grace 1h], revoke <id>
|
|
194
|
+
foldrun audit the account's audit log, newest first: sign-ins, keys, invites, secrets by name, deploys, support views (--since 7d, --action, --actor, --to, --all, --csv)
|
|
195
|
+
foldrun --version this CLI's version, offline (\`foldrun version\` also asks the platform)
|
|
196
|
+
foldrun --help everything; \`foldrun <command> --help\`, \`-h\` or \`foldrun help <command>\` for one (never runs it)
|
|
67
197
|
|
|
68
198
|
Options
|
|
69
|
-
--workspace <dir> the workspace folder (default: .)
|
|
70
|
-
--
|
|
199
|
+
--workspace <dir> the workspace folder (default: .) — on init, the first workspace's name
|
|
200
|
+
--flat init: the old single-folder shape, no account around it
|
|
201
|
+
--from <template> init: start from a shipped template, e.g. templates/hello
|
|
202
|
+
--transport <k> tool new: script (default), http or mcp
|
|
203
|
+
--language <l> tool new: the script's language — javascript, python, bash
|
|
71
204
|
--task "<text>" the instruction for a manual run
|
|
72
205
|
--test run, invoke: a test run — nothing outward, state/ untouched, receipts on the run page
|
|
73
206
|
--follow logs: keep tailing a live run (with --url: on the platform)
|
|
207
|
+
--status <s> runs, stop, rerun: only these statuses, comma-separated (failed, completed, awaiting-approval…)
|
|
208
|
+
--since <span> runs, stop, rerun: only runs started within 24h, 7d, 90m, 2w
|
|
209
|
+
--since <span> audit: entries from 24h, 7d, 30d ago, or a date (default 7d); --until <date> ends the window
|
|
210
|
+
--action <a> audit: one action (apikey.created) or a family (apikey, secret, member, support)
|
|
211
|
+
--actor <who> audit: one person or key — an email, key:<label>, support:<email>
|
|
212
|
+
--since <days> observe, triggers: the window in days (observe default 30, triggers 7)
|
|
213
|
+
--verdict <v> runs: only completed runs whose summary leads with it — good, bad, quiet, blocked (comma-separated)
|
|
214
|
+
--flow <name> stop, rerun: only that flow's runs; flow add: the other flow a subflow step runs; flow draft: the existing flow to redraft
|
|
215
|
+
--agent <name> flow add: the agent a chain, parallel or router step runs; rerun: from the first step that agent runs; report live: whose browser frame
|
|
216
|
+
--instruction "<text>" flow add: what a new step (chain, parallel, router) is told to do
|
|
217
|
+
--after <n> flow add, flow move-step, flow paste: put the new group after the nth (0 = first); --before <n> before it (flow add); default last
|
|
218
|
+
--group <n> flow add, flow move-step, flow paste: the group a step joins, in parallel
|
|
219
|
+
--step <n|agent> flow add, flow rm-step, flow dup-step, flow set, flow move-step, flow copy-step: the step a pattern goes on (or is deleted, copied, edited, moved) — its place in the file (1 = the first) or the agent it runs
|
|
220
|
+
--cases <V=agent,…> flow add: a router's branches, e.g. BUG=bugs,DOCS=docs; --else <agent> when none matches
|
|
221
|
+
--each <e> flow add: lines, items or "rows of <path>"; --max <n> caps it (1-20)
|
|
222
|
+
--loop <n> flow add: extra cycles, 1-5 (default 3); --until <MARKER> ends it (default APPROVED); --judge "<claim>" adds verify: judge:
|
|
223
|
+
--question "<text>" flow add: the ask a person gets before the step; answer: which question of several (its id)
|
|
224
|
+
--wait <span|event> flow add: 90s, 30m, 4h, 3d or event
|
|
225
|
+
--on-fail <agent> flow add: who takes the step over when it fails
|
|
226
|
+
--off flow add: take the pattern off the step instead (fan-out, loop, approval, ask, wait, rescue)
|
|
227
|
+
--schedule "<cron>" flow trigger: the cron line a schedule fires on (5 fields, or @daily); --timezone <zone> the zone it is read in
|
|
228
|
+
--kind <k> secrets set: file (--file <path>), ssh (--host, --user, --port, --key-file or a prompted password), api (--base-url, --header "Name: value" repeatable), service-account (--file <key.json>, --scopes), m2m (--token-url, --client-id, --scopes; secret prompted)
|
|
229
|
+
--client <name> connect: run the consent from that OAuth client saved on the platform (secrets clients lists them)
|
|
230
|
+
--subagent <name> agent link, agent unlink: a worker it delegates to (subagents:)
|
|
231
|
+
--consult <name> agent link, agent unlink: a colleague it asks (agents:)
|
|
232
|
+
--can-ask agent link, agent unlink: ask in its tools — it may ask you mid-step
|
|
233
|
+
--description "<text>" agent link: what a --subagent is for, when its file has no description:
|
|
234
|
+
--eval <name> promote: the eval file to write to (default <target>-regressions)
|
|
235
|
+
--expect "<line>" promote: an assertion in eval syntax (contains: …, judge: …) — repeatable
|
|
236
|
+
--case <name> promote: the case's name
|
|
237
|
+
--tag <t> invoke, flow run: a label on the run — repeatable, sent as tags
|
|
238
|
+
--inputs <set> invoke, flow run: the task is a saved input set (evals/<flow>-inputs.md, or an eval case for the flow)
|
|
239
|
+
--preview storage cat: the platform reads the file and prints what is in it
|
|
240
|
+
--csv billing statement: the CSV file instead of the table
|
|
241
|
+
--csv audit: the log as CSV, for an auditor (--file <path> to save it)
|
|
242
|
+
--threshold / --amount billing wallet set: auto top-up refills --amount (5-500 USD) when the balance falls below --threshold
|
|
243
|
+
--name / --abn / --address / --email billing details set: the invoice's legal name, ABN, "line1, city, state, postcode, AU", receipt email
|
|
244
|
+
--days <n> usage: the window the charges cover (default 56)
|
|
245
|
+
--id <revision> history: one revision in full, as diffs
|
|
246
|
+
--engine <e> login: (with a site) the browser to sign in with — chrome (default: real Google Chrome, Chromium when it is not installed), chromium, firefox or safari
|
|
247
|
+
--new-client connect: enter a new OAuth client even when one is saved for the secret
|
|
248
|
+
--step <n> approve, reject: decide only that step; default is every step that is waiting
|
|
249
|
+
--note "<text>" approve, reject: guidance the agent reads — or the reason for a refusal
|
|
250
|
+
--yes approve, stop, rerun (bulk), flow rotate-hook, flow draft, history restore, billing plan, repo, workspace set name, and every rm/revoke/unshare: skip the confirmation, deliberately (required with no terminal)
|
|
251
|
+
--json report: the raw run record instead of the report; observe, usage, api version, changelog, find, preferences, workspace vocabulary: the raw document; version: {cli, core, platform}
|
|
252
|
+
--events <a,b> account set notify: failed, awaiting-approval, completed
|
|
253
|
+
--failed webhooks deliveries: only the ones that gave up
|
|
254
|
+
--limit <n> runs, billing, history, changelog: how many rows
|
|
74
255
|
--value "<text>" secrets set: skip the prompt (careful with shell history)
|
|
75
|
-
--
|
|
256
|
+
--out <file> api spec: where to write the OpenAPI document (default: stdout)
|
|
257
|
+
--file <path> source put, library put, flow paste: the local file to send (default: stdin); secrets set --kind file|service-account: the file it holds; storage get, report get, report live, account export, billing statement: where to write it (- for stdout)
|
|
76
258
|
--message "<why>" source put: recorded on the file's revision
|
|
77
259
|
--account secrets: account scope instead of the workspace's
|
|
78
|
-
--wait invoke: hold on and print the result
|
|
260
|
+
--wait invoke, agent run: hold on and print the result
|
|
261
|
+
--path <p> tool test: the path an http tool should probe, appended to its base:
|
|
79
262
|
--watch invoke: follow the run's trace here as it happens
|
|
80
263
|
--print open: print the URL only
|
|
81
|
-
--from <n> invoke: start at step n; earlier steps are skipped
|
|
82
|
-
--no-browser login, connect: print the address instead of opening it
|
|
264
|
+
--from <n> invoke, flow run, rerun: start at step n; earlier steps are recorded as skipped (a bulk rerun defaults to 1)
|
|
265
|
+
--no-browser login, connect, billing top-up/card/plan: print the address instead of opening it
|
|
83
266
|
--provider <name> connect: google, github, microsoft or linkedin (fills the URLs)
|
|
84
267
|
--scopes "<a b c>" connect: space-separated scopes (default: the provider's example)
|
|
85
268
|
--client-id / --client-secret connect: the OAuth app (prompted if omitted; secret never echoed)
|
|
@@ -87,19 +270,26 @@ Options
|
|
|
87
270
|
--authorize-url / --token-url connect: a provider with no preset
|
|
88
271
|
--role <r> keys create: viewer, editor (default) or admin
|
|
89
272
|
--for <workspace> keys create: a deploy key for one workspace (--access read|write)
|
|
273
|
+
--expires <span> keys create: when it stops working — 30d, 90d, 365d or never (default never)
|
|
274
|
+
--grace <span> keys rotate: keep the old key working for 1h or 24h while you swap the new one in (default 0: revoked now)
|
|
90
275
|
|
|
91
276
|
Platform options (deploy, invoke, secrets, logs, keys)
|
|
92
|
-
--to <workspace> workspace on the platform (deploy default: folder name)
|
|
277
|
+
--to <workspace> workspace on the platform (deploy default: folder name); flow add/show, agent link: edit or show the deployed copy instead of this folder
|
|
93
278
|
--tenant <name> account to deploy into (default: default, local only)
|
|
94
279
|
--data <dir> the installation's data directory
|
|
95
280
|
--url <url> a running platform (or FOLDRUN_URL, or where you last signed in)
|
|
96
281
|
--token <key> API key for --url (or FOLDRUN_TOKEN, or the one from foldrun login)
|
|
282
|
+
--timeout <s> seconds one request may take (or FOLDRUN_TIMEOUT; default 30, tool test 300)
|
|
97
283
|
FOLDRUN_TIMEOUT=<s> seconds one request to the platform may take (default 30)
|
|
98
284
|
--profile <name> act as one stored account for this command (see foldrun accounts)
|
|
285
|
+
--quiet leave out the dim "acting as …" line a platform command prints on stderr
|
|
99
286
|
--local deploy: into the installation on this machine, even when signed in
|
|
100
287
|
--commit <sha> deploy: record which commit this is
|
|
101
|
-
--dry-run deploy: check and report, change nothing
|
|
102
|
-
--
|
|
288
|
+
--dry-run deploy: check and report, change nothing; flow add, flow set, flow trigger, flow move-step, flow paste, flow draft, history restore, agent link, agent unlink: show the diff, write nothing; stop, rerun (bulk): list what matches, touch nothing
|
|
289
|
+
--no-runtimes deploy: do not wait for the workspace's environments to be built
|
|
290
|
+
--force deploy: deploy even while runs are in flight; pull, storage get: overwrite local files
|
|
291
|
+
--yes deploy: allow deleting files this folder no longer has (asked otherwise; required with no terminal)
|
|
292
|
+
--platform --yes workspaces rm: delete it on the platform, deliberately
|
|
103
293
|
|
|
104
294
|
Nothing here needs an account. Set ANTHROPIC_API_KEY to run; init and check
|
|
105
295
|
work without one. \`foldrun login\` is for the hosted platform, or your own.`;
|
|
@@ -115,7 +305,13 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
115
305
|
// `--value` as the account's argument and stored an empty secret; `--force
|
|
116
306
|
// ./dir` swallowed the directory. A flag followed by another flag is also
|
|
117
307
|
// boolean, so an unlisted switch at least does not eat its neighbour.
|
|
118
|
-
const BOOLEAN_FLAGS = new Set(["account", "follow", "force", "oauth2", "wait", "watch", "print", "dry-run", "help", "no-browser", "local", "test"]);
|
|
308
|
+
const BOOLEAN_FLAGS = new Set(["account", "follow", "force", "oauth2", "wait", "watch", "print", "dry-run", "help", "no-browser", "local", "test", "flat", "yes", "platform", "json", "forever", "all", "check", "new-client", "quiet", "off", "can-ask", "preview", "csv", "failed"]);
|
|
309
|
+
// Flags said more than once collect into a list: `--tag a --tag b`.
|
|
310
|
+
const REPEATABLE = new Set(["tag", "expect", "header"]);
|
|
311
|
+
// `--wait` is a switch everywhere but `flow add <flow> wait`, where it takes
|
|
312
|
+
// a span: `--wait 30m`, `--wait event`. Only a value that reads as one is
|
|
313
|
+
// taken, so `invoke x --wait` and friends are as they were.
|
|
314
|
+
const WAIT_VALUE = /^(\d+(\.\d+)?\s*[smhd]|event)$/i;
|
|
119
315
|
const flags = {};
|
|
120
316
|
const positional = [];
|
|
121
317
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -125,51 +321,133 @@ for (let i = 0; i < rest.length; i++) {
|
|
|
125
321
|
}
|
|
126
322
|
const name = rest[i].slice(2);
|
|
127
323
|
const next = rest[i + 1];
|
|
128
|
-
|
|
129
|
-
|
|
324
|
+
let value;
|
|
325
|
+
if (name === "wait" && next !== undefined && WAIT_VALUE.test(next)) value = rest[++i];
|
|
326
|
+
else if (BOOLEAN_FLAGS.has(name) || next === undefined || next.startsWith("--")) value = true;
|
|
327
|
+
else value = rest[++i];
|
|
328
|
+
if (REPEATABLE.has(name) && typeof value === "string") flags[name] = [...(Array.isArray(flags[name]) ? flags[name] : []), value];
|
|
329
|
+
else flags[name] = value;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Help, before anything runs. `foldrun deploy --help` used to parse --help
|
|
333
|
+
// into a flag nothing read and then DEPLOY (2026-09-30, to the wrong account).
|
|
334
|
+
// --help or -h anywhere, or `foldrun help <command>`, prints that command's
|
|
335
|
+
// lines and exits 0 — no platform is contacted, no file is touched.
|
|
336
|
+
if (flags.help === true || rest.includes("-h") || command === "help") {
|
|
337
|
+
const which = command === "help" ? positional[0] : command;
|
|
338
|
+
console.log(helpFor(which));
|
|
339
|
+
process.exit(0);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** The lines of HELP about one command: its `foldrun <command>` usage lines
|
|
343
|
+
* and the options whose description names it. The whole text when nothing
|
|
344
|
+
* matches or none was named. */
|
|
345
|
+
function helpFor(which) {
|
|
346
|
+
if (!which) return HELP;
|
|
347
|
+
const esc = which.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
348
|
+
const usage = new RegExp(`^\\s*foldrun ${esc}(\\s|$)`);
|
|
349
|
+
// "approve, reject: …" names approve too — a comma after the name is
|
|
350
|
+
// part of a list of commands, not the end of it.
|
|
351
|
+
const option = new RegExp(`(^|[;,]\\s*)${esc}([\\s,][^;:]*)?:`);
|
|
352
|
+
const lines = HELP.split("\n");
|
|
353
|
+
const own = lines.filter((l) => usage.test(l));
|
|
354
|
+
const opts = lines.filter((l) => /^\s*--/.test(l) && option.test(l.replace(/^\s*--\S+(\s+(\/\s+)?--\S+)*(\s"?<[^>]+>"?)?\s+/, "")));
|
|
355
|
+
if (!own.length && !opts.length) return HELP;
|
|
356
|
+
return ["", ...own, ...(opts.length ? ["", "Options:", ...opts] : []), "", "`foldrun --help` for everything."].join("\n");
|
|
130
357
|
}
|
|
131
358
|
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
359
|
+
// Commands that are about an ACCOUNT, not one folder: they read or write the
|
|
360
|
+
// account AGENTS.md, the shared library and several workspaces at once, so
|
|
361
|
+
// pinning FOLDRUN_WORKSPACE would collapse the layout to one of them.
|
|
362
|
+
// `deploy` has always been in this set — it writes into an installation,
|
|
363
|
+
// which has accounts and many workspaces.
|
|
364
|
+
const ACCOUNT_WIDE = new Set(["deploy", "pull", "status", "workspaces", "new"]);
|
|
137
365
|
|
|
138
366
|
// `init` and `check` take a directory; `run` and `eval` take the name of a
|
|
139
367
|
// thing to run, so a directory there would be ambiguous — use --workspace.
|
|
140
368
|
const takesDir = command === "init" || command === "check" || command === "extract";
|
|
141
|
-
|
|
142
|
-
|
|
369
|
+
// For `init` the flag names the first WORKSPACE, not a directory — the
|
|
370
|
+
// directory it makes is the account. A value that looks like a path is still
|
|
371
|
+
// read as one, so `foldrun init --workspace ./desk` keeps working.
|
|
372
|
+
const flagIsName =
|
|
373
|
+
command === "init" &&
|
|
374
|
+
typeof flags.workspace === "string" &&
|
|
375
|
+
!flags.workspace.includes(path.sep) &&
|
|
376
|
+
!flags.workspace.includes("/") &&
|
|
377
|
+
!fs.existsSync(flags.workspace);
|
|
378
|
+
const here = path.resolve(
|
|
379
|
+
(flagIsName ? undefined : flags.workspace) ?? (takesDir ? positional.shift() ?? "." : "."),
|
|
143
380
|
);
|
|
144
|
-
|
|
381
|
+
|
|
382
|
+
// Which shape is this? One helper in the core answers, so `check`, `deploy`,
|
|
383
|
+
// `status` and the runtime's own account scope cannot drift apart.
|
|
384
|
+
const { detectLayout, installationDataRoot } = await import("@foldrun/core/layout");
|
|
385
|
+
const layout = detectLayout(here);
|
|
386
|
+
const byName =
|
|
387
|
+
typeof flags.workspace === "string" && layout.workspacesDir && layout.workspaces.includes(flags.workspace)
|
|
388
|
+
? path.join(layout.workspacesDir, flags.workspace)
|
|
389
|
+
: null;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* The workspace directory this command should act on.
|
|
393
|
+
*
|
|
394
|
+
* At an account root with exactly one workspace there is nothing to ask about
|
|
395
|
+
* — that is the one. With several, a command that needs one says so itself,
|
|
396
|
+
* by name, rather than picking.
|
|
397
|
+
*/
|
|
398
|
+
function pinned() {
|
|
399
|
+
// `--workspace blog-desk` inside an account names one of its workspaces, not
|
|
400
|
+
// a directory relative to here. Only when it IS one of them, so a path that
|
|
401
|
+
// happens to look like a name still resolves as a path.
|
|
402
|
+
if (byName) return byName;
|
|
403
|
+
if (layout.workspaceDir && layout.kind !== "empty") return layout.workspaceDir;
|
|
404
|
+
if (layout.kind === "account" && layout.workspacesDir && layout.workspaces.length === 1) {
|
|
405
|
+
return path.join(layout.workspacesDir, layout.workspaces[0]);
|
|
406
|
+
}
|
|
407
|
+
if (layout.kind === "empty") return here;
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const workspace = pinned() ?? here;
|
|
412
|
+
if (!ACCOUNT_WIDE.has(command)) process.env.FOLDRUN_WORKSPACE = workspace;
|
|
413
|
+
// The account scope, decided once here and honoured by the core's
|
|
414
|
+
// singleAccountRoot — so `library/` beside `workspaces/` is what an agent's
|
|
415
|
+
// skills:, tools: and scripts: resolve against, exactly as it is on the
|
|
416
|
+
// platform.
|
|
417
|
+
process.env.FOLDRUN_ACCOUNT ??= layout.accountRoot;
|
|
145
418
|
|
|
146
419
|
// Where the secrets, keys and run store live.
|
|
147
420
|
//
|
|
148
|
-
//
|
|
149
|
-
// workspace
|
|
421
|
+
// An account on a laptop keeps them at its own root, in `.foldrun/`, so every
|
|
422
|
+
// workspace under it shares one vault — the way workspaces in an account share
|
|
423
|
+
// one vault on the platform. A flat workspace keeps them inside itself, which
|
|
424
|
+
// is where they have always been.
|
|
425
|
+
//
|
|
426
|
+
// A workspace that belongs to an INSTALLATION is a third case: it sits at
|
|
150
427
|
// `<data>/<tenant>/workspaces/<name>`, and its secrets belong to the tenant,
|
|
151
428
|
// two levels up — so pointing --workspace at one and defaulting to `.foldrun/`
|
|
152
429
|
// opened an empty store beside it. Every declared secret came back missing,
|
|
153
|
-
// and the error told you to add secrets that were already there.
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
const parent = path.basename(path.dirname(workspace));
|
|
158
|
-
const installationRoot =
|
|
159
|
-
parent === "workspaces" ? path.resolve(workspace, "..", "..", "..") : null;
|
|
430
|
+
// and the error told you to add secrets that were already there. The shape
|
|
431
|
+
// alone cannot tell an installation from an account folder (they are the same
|
|
432
|
+
// shape); the installation's key file at the data root can.
|
|
433
|
+
const installationRoot = installationDataRoot(layout.accountRoot);
|
|
160
434
|
|
|
161
|
-
if (
|
|
435
|
+
if (command === "deploy") {
|
|
162
436
|
// The destination is an installation, so --data names it outright. Without
|
|
163
437
|
// one, dataRoot()'s own default applies: data/ at the project root.
|
|
164
438
|
if (flags.data) process.env.FOLDRUN_DATA = path.resolve(flags.data);
|
|
165
439
|
} else {
|
|
166
|
-
process.env.FOLDRUN_DATA ??=
|
|
440
|
+
process.env.FOLDRUN_DATA ??=
|
|
441
|
+
installationRoot ??
|
|
442
|
+
(layout.kind === "flat" || layout.kind === "empty"
|
|
443
|
+
? path.join(workspace, ".foldrun")
|
|
444
|
+
: path.join(layout.accountRoot, ".foldrun"));
|
|
167
445
|
}
|
|
168
446
|
|
|
169
447
|
const { run, explain } = await import(path.join(HERE, "../src/commands.mjs"));
|
|
170
448
|
|
|
171
449
|
try {
|
|
172
|
-
const code = await run(command, positional, flags, workspace);
|
|
450
|
+
const code = await run(command, positional, flags, workspace, layout);
|
|
173
451
|
process.exit(code ?? 0);
|
|
174
452
|
} catch (err) {
|
|
175
453
|
// The whole chain, not the top: "fetch failed" is the top, and the
|