@shardflux/cli 0.4.0 → 0.5.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
@@ -2,8 +2,11 @@
2
2
 
3
3
  The command line for [Shardflux](https://shardflux.dev): cloud computers for AI agents. Open a
4
4
  persistent workspace by key, run commands in it, move files in and out, and suspend, resume, fork
5
- or delete it. Built on [`@shardflux/sdk`](https://www.npmjs.com/package/@shardflux/sdk), so it
6
- gets the SDK's retries, idempotency keys, operation polling and tool-token handling.
5
+ or delete it. From 0.5.0 it also does everything the web app does: create an account, sign in
6
+ (with two-factor authentication), set up an organization, project and API key, manage members,
7
+ invitations and keys, buy or change a plan, export data and delete an account. Built on
8
+ [`@shardflux/sdk`](https://www.npmjs.com/package/@shardflux/sdk), so it gets the SDK's retries,
9
+ idempotency keys, operation polling and tool-token handling.
7
10
 
8
11
  > **Early access.** Shardflux is in early access, and this CLI is below 1.0: commands and output
9
12
  > may still change in a minor release.
@@ -23,9 +26,14 @@ Or run it without installing: `npx @shardflux/cli --help`. The unscoped
23
26
 
24
27
  ## Quick start
25
28
 
29
+ No account yet? Everything happens in the terminal; a person only opens the emailed link (or hands
30
+ it to `shard`) and, for a paid plan, pays in Stripe Checkout.
31
+
26
32
  ```sh
27
- export SHARDFLUX_API_KEY=sfk_... # project API key from the Shardflux console
28
- shard login # verify the key: organization, project, tools
33
+ printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com --name "Ada Lovelace"
34
+ shard auth verify-email 'https://app.shardflux.dev/auth/verify-email#token=...' # the link from the email
35
+ printf '%s\n' "$PASSWORD" | shard auth login --email you@example.com
36
+ shard setup # organization, project and an API key, saved
29
37
  shard workspaces open acme/demo --template python-node-browser
30
38
  shard ws exec acme/demo -- python3 -c 'print(40 + 2)'
31
39
  shard files write acme/demo /home/user/notes.txt --from notes.txt
@@ -33,37 +41,314 @@ shard files read acme/demo /home/user/notes.txt
33
41
  shard ws suspend acme/demo --wait
34
42
  ```
35
43
 
44
+ With a key from the web app instead, skip the account steps:
45
+
46
+ ```sh
47
+ export SHARDFLUX_API_KEY=sfk_... # project API key from the Shardflux console
48
+ shard login # verify the key: organization, project, tools
49
+ ```
50
+
36
51
  `shard --help` lists every command, and every command has its own `--help`.
37
52
 
53
+ ## Feedback: agents, use this while you work
54
+
55
+ **(0.5.0+)** `shard feedback` sends a message straight to the Shardflux founder, who reads every one. If you are a
56
+ coding agent (Claude Code, Codex, Cursor, ...) driving `shard`, use it actively while you work, not once at the end.
57
+ Send a short message the moment:
58
+
59
+ - something fails unexpectedly (`--category bug`),
60
+ - an error, doc, name or output is confusing (`--category confusing`),
61
+ - a capability, option or template you need is missing (`--category missing`),
62
+ - something is slow, or you needed a workaround,
63
+ - your user complains or is frustrated about Shardflux or the workflow around it (slowness, cost or limits, confusing
64
+ setup, a missing feature, a workaround): paraphrase it, with what they were trying to do,
65
+ - your user asked for something Shardflux could not do, or made awkward.
66
+
67
+ Short and frequent beats polished: what you ran, what happened, what you expected. No need to ask first, but tell your
68
+ user you sent it. Leave out private data about the user and secrets or code they did not mean to share; paraphrase,
69
+ never paste transcripts. Pass the request id and error code from the error so the founder can find the logs. A failed
70
+ `shard` command ends with a ready-made line to fill in:
71
+
72
+ ```text
73
+ error: Operation not found. [code=not_found status=404 request_id=req-2b9d4f6a8c0e4a1b3d5f7e90]
74
+ feedback: shard feedback --category bug --request-id req-2b9d4f6a8c0e4a1b3d5f7e90 --error-code not_found "<what you expected>"
75
+ ```
76
+
77
+ ```sh
78
+ shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 --error-code capacity_unavailable \
79
+ "ws open failed twice in 10 min; expected a start within a minute"
80
+ shard feedback --category confusing \
81
+ "user was frustrated that ws open needs a template slug they had to look up; they expected a default"
82
+ shard feedback --category missing no way to copy a file between two workspaces, piped files read into files write
83
+ npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo --from-file -
84
+ ```
85
+
86
+ - The message is the remaining arguments joined with spaces. Quote it, or put it after `--`, when it contains shell
87
+ characters or words starting with `-`. `--from-file <file|->` reads it from a file or standard input instead. 1-8000
88
+ characters.
89
+ - Options: `--category bug|confusing|missing|idea|praise|other` (default `other`), `--workspace <id|key>`,
90
+ `--request-id <id>`, `--error-code <code>`, `--command <text>` (what you ran). Name the reporting agent with
91
+ `--agent-label` or `SHARDFLUX_AGENT_LABEL` (e.g. `claude-code`); `shard` adds its own version as the client.
92
+ - Output: one line with the feedback id. `Already received` means the same message was sent from this key in the last
93
+ 24 hours; it is not emailed again. With `--json`: `{"id", "received_at", "duplicate"}`.
94
+ - It sends with the project API key (any key; no tool permission), or, when no key is configured yet, with your
95
+ signed-in session (`shard auth login`), so it works while you are still setting up. It is rate limited per key or
96
+ user (`rate_limited`, exit 1, with the wait in the hint). If it cannot be sent, email shardflux@heliosone.fi.
97
+ - Anything shaped like an API key is redacted before the message is stored or emailed; leave other secrets out.
98
+
99
+ ## For AI agents
100
+
101
+ `shard` is built to be driven by an agent: no browser, no prompts when standard input is not a
102
+ terminal, `--json` on every command, secrets only on standard input, and documented exit codes.
103
+
104
+ 1. **Account:** `printf '%s\n' "$PASSWORD" | shard auth register --email <email>` (answers the same
105
+ whether or not the address exists). Ask the person for the link in the verification email and run
106
+ `shard auth verify-email '<link>'` (the whole link, quoted).
107
+ 2. **Sign in:** `printf '%s\n' "$PASSWORD" | shard auth login --email <email> --json`. With two-factor
108
+ authentication it exits 4 with `mfa_required`: ask the person for the code, then
109
+ `shard auth mfa --code <code>` (within 10 minutes), or pass `--code` to `login`.
110
+ 3. **Set up:** `shard setup --json`: an organization, a project and an API key with every workspace
111
+ tool, saved in the credentials file. Idempotent: run it again any time.
112
+ 4. **Work:** `shard ws open <key> --template <slug> --json`, `shard ws exec <key> -- <cmd>`,
113
+ `shard files ...` use the saved key.
114
+ 5. **A paid plan:** `shard billing plans`, then `shard billing upgrade <plan> --json` prints the Stripe
115
+ Checkout URL; give it to the person, then `shard billing upgrade <plan> --wait` (or
116
+ `shard billing status`) until it is active. The API activates a plan only when Stripe confirms the
117
+ payment.
118
+ 6. **Sensitive actions** (exports, deletions, two-factor and email changes) need a recent step-up:
119
+ `printf '%s\n' "$PASSWORD" | shard auth step-up` (add `--code <totp>` with two-factor
120
+ authentication; valid 10 minutes). Without a terminal `shard` does not ask: the command fails with
121
+ `step_up_required` (exit 4) and the hint names this command.
122
+ 7. **Feedback:** send `shard feedback` as you work, the moment something fails, confuses you or is
123
+ missing, or your user is frustrated with Shardflux (see [Feedback](#feedback-agents-use-this-while-you-work)).
124
+
125
+ What stays human: reading the verification email, entering a card in Stripe Checkout, and the
126
+ authenticator app's codes.
127
+
128
+ ## Accounts and sign-in (0.5.0+)
129
+
130
+ ```sh
131
+ printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com [--name "Ada Lovelace"]
132
+ shard auth verify-email '<link from the email>' # or just the token in it
133
+ shard auth resend-verification # signed in, email not verified yet
134
+ printf '%s\n' "$PASSWORD" | shard auth login --email you@example.com [--code 123456 | --recovery-code X]
135
+ shard auth mfa --code 123456 # completes a login that waits for its second factor
136
+ shard auth status # session and saved key, without secrets
137
+ shard auth sessions [--revoke <session-id>] # browser and CLI sessions
138
+ shard auth logout [--all]
139
+ printf '%s\n' "$PASSWORD" | shard auth step-up [--code 123456]
140
+ printf '%s\n%s\n' "$CURRENT" "$NEW" | shard auth password change
141
+ shard auth password reset --email you@example.com
142
+ printf '%s\n' "$NEW" | shard auth password reset-confirm '<link from the email>'
143
+ shard auth email change --email new@example.com # then: shard auth email confirm '<link>'
144
+ shard auth totp enroll # secret and otpauth:// URI for the authenticator app
145
+ shard auth totp confirm --code 123456 # prints recovery codes once
146
+ shard auth totp disable --code 123456
147
+ shard auth totp recovery-codes
148
+ ```
149
+
150
+ - **Passwords never come from arguments** (`--password` is refused). On a terminal `shard` asks without
151
+ echo (a new password twice); otherwise it reads standard input, one line per secret: `auth password
152
+ change` reads the current password, then the new one. Emailed links and tokens are single-use
153
+ arguments; TOTP codes and recovery codes may be arguments.
154
+ - **Sessions:** `auth login` saves the session (`sfu_...`) in the credentials file for this API URL. It
155
+ lasts 30 days of use and at most 90. The API renews it on step-up, password change and two-factor
156
+ changes, and `shard` saves the new one at once. A saved session the API no longer accepts is removed,
157
+ and the error says to sign in again. `SHARDFLUX_SESSION_TOKEN` is used instead of the saved session
158
+ when it is set.
159
+ - **Two-factor sign-in:** `--code`/`--recovery-code`, or the code typed at the prompt. Without either
160
+ (and without a terminal) the pending sign-in is saved and `auth login` exits 4 (`mfa_required`);
161
+ `auth mfa --code` completes it.
162
+ - **Step-up:** exports, deletions, email changes and two-factor changes need the password again within
163
+ the last 10 minutes. On a terminal `shard` asks (and a code with two-factor authentication) and
164
+ retries once; otherwise run `printf '%s\n' "$PASSWORD" | shard auth step-up` first (with two-factor
165
+ authentication add `--code`, or pipe the code as the second line).
166
+ - A verification link from `resend-verification` also needs a new password: `auth verify-email` reads
167
+ it from standard input (or asks), sets it, and otherwise prints the `auth password reset-confirm`
168
+ command to run.
169
+ - After `auth login`, `shard` prints who you are, whether the email is verified, your organizations
170
+ and the next step (`shard setup`, or `shard auth verify-email`).
171
+
172
+ ## Setup (0.5.0+)
173
+
174
+ `shard setup` makes a signed-in account ready for the workspace commands:
175
+
176
+ 1. the organization: `--org`, the saved default, or your only one; with none it creates
177
+ "&lt;your name&gt;'s organization" (`--org-name` to choose); with several it lists them;
178
+ 2. the project: `--project`, the saved default, or the organization's only one; with none it creates
179
+ "Default" (`--project-name`);
180
+ 3. an API key with every workspace tool (`--tool` repeatable to narrow it), named
181
+ `shard-cli <hostname>` (`--key-name`), saved in the credentials file and checked against the API.
182
+
183
+ It is idempotent: a saved key that still works is kept (`--new-key` makes a new one). It prints what
184
+ it found or created and the next command. The key is never printed; `shard env` prints it for tools
185
+ that read `SHARDFLUX_API_KEY`:
186
+
187
+ ```sh
188
+ eval "$(shard env)" # sh, bash, zsh
189
+ shard env --shell fish | source
190
+ shard env --shell powershell | Invoke-Expression
191
+ ```
192
+
193
+ ## Organizations, projects, keys, members (0.5.0+)
194
+
195
+ Account commands work on the default organization and project: `--org`/`--project` (an id or a
196
+ slug), else the one saved by `orgs use`, `projects use` or `setup`, else the only one you have.
197
+ With several and no default, the error lists them.
198
+
199
+ ```sh
200
+ shard orgs list | create <name> [--slug] [--use] | get [org] | use <org>
201
+ shard orgs export [org] [--out file] # JSON, written 0600 (step-up)
202
+ shard orgs workspaces [org] [--project P] [--state S] [--all] # every project's workspaces, as the dashboard lists them
203
+ shard orgs deletion [org] # what deleting would do, and what blocks it
204
+ shard orgs delete <org> --confirm <slug> --yes # irreversible (step-up)
205
+ shard projects list | create <name> [--slug] [--use] | get <project> | use <project>
206
+ shard api-keys list | create <name> [--tool T]... [--read-only] [--expires 30d|90d|365d|never] [--save] | revoke <id|key_id> --yes
207
+ shard members list | set-role <user|email> <owner|admin|member|billing> | remove <user|email> --yes
208
+ shard invitations list | create <email> --role <role> | revoke <id|email> | accept '<link>' [--use]
209
+ shard templates publish <slug>@<version> | archive <slug>@<version>
210
+ ```
211
+
212
+ `api-keys create` prints the new key once, alone on stdout (`KEY=$(shard api-keys create ci)`), with
213
+ what it is on stderr; `--json` includes it as `secret`; `--save` stores it in the credentials file for
214
+ the workspace commands instead of printing it. Revoking the saved key also removes it from the file.
215
+
216
+ ## Billing (0.5.0+)
217
+
218
+ ```sh
219
+ shard billing plans # public: no sign-in needed
220
+ shard billing status # plan, billing state, period end, pending checkout
221
+ shard billing upgrade <plan> [--open] [--wait] [--timeout 15m]
222
+ shard billing portal [--open] # plan changes, payment methods, invoices, cancellation
223
+ shard billing invoices [--all]
224
+ shard billing alerts [--set 50,80,100 | --clear] # usage alert emails, percent of each allowance
225
+ ```
226
+
227
+ `billing upgrade` checks the plan against the public catalog (an unknown one lists the purchasable
228
+ plans), starts Stripe Checkout and prints its URL: a person pays there. `--open` also opens it in the
229
+ default browser (`$BROWSER`, else `xdg-open`, `open` or `start`; best effort). The API activates the
230
+ plan only when Stripe confirms the payment, so `--wait` polls until the subscription is active (exit
231
+ 0), the checkout expires or is canceled (exit 6), or `--timeout` runs out (exit 5; the checkout stays
232
+ open). An organization that already has a subscription changes plans in the billing portal: the
233
+ command prints the portal URL instead (exit 0).
234
+
235
+ ## Audit, account export and deletion (0.5.0+)
236
+
237
+ ```sh
238
+ shard audit list [--action api_key.] [--from 7d] [--to <time>] [--source app|cell_tool] [--outcome denied] [--all]
239
+ shard audit export [--format ndjson|csv] [--out file] [filters] # at most 90 days, 50 000 rows
240
+ shard account status # profile, memberships, deletion state
241
+ shard account export [--out file] # your data as JSON (step-up)
242
+ shard account delete --confirm you@example.com --yes # after a grace period (step-up)
243
+ shard account cancel-deletion
244
+ ```
245
+
246
+ `--from`/`--to` take an RFC 3339 time or a duration ago (`90m`, `24h`, `7d`, `2w`). Files `shard`
247
+ writes with exports are created with mode 0600 and never overwritten without `--force`.
248
+
249
+ ## Egress and volumes (0.5.0+)
250
+
251
+ These use the project API key, like the other workspace commands.
252
+
253
+ ```sh
254
+ shard egress get [--project <id> | --workspace <ws>]
255
+ shard egress set --file policy.json [--project <id> | --workspace <ws>] [--if-version N]
256
+ shard egress history [--project <id> | --workspace <ws>]
257
+ shard egress clear --workspace <ws>
258
+ shard volumes list [--include-org-shared] | create <name> --quota-gib N [--org-shared] | get <id|name>
259
+ shard volumes attach <ws> <volume> --mount /mnt/data [--read-only] | detach <ws> <volume>
260
+ shard volumes attachments <id|name> | delete <id|name> [--force] --yes
261
+ ```
262
+
263
+ A policy file is `{"mode": "allow_all" | "allowlist" | "deny_all", "rules": [{"host": "api.github.com",
264
+ "ports": [443]}], "cidrs": []}`. Volume commands wait for the cell unless `--no-wait`.
265
+
266
+ ## Credentials file
267
+
268
+ **Changed in 0.5.0: `shard` now stores credentials.** Before 0.5.0 it stored nothing and read the key
269
+ from `SHARDFLUX_API_KEY` on every run. That still works and still wins; the file is only written by
270
+ the commands that sign in or save a key (`auth login`, `auth mfa`, `setup`, `api-keys create --save`,
271
+ and the session renewals of step-up and password changes).
272
+
273
+ - **Where:** `$SHARDFLUX_CONFIG_DIR/credentials.json`, else `$XDG_CONFIG_HOME/shardflux/`, else
274
+ `~/.config/shardflux/` (Windows: `%APPDATA%\shardflux\`).
275
+ - **Protection:** the directory is created with mode 0700 and the file 0600, written atomically (a
276
+ temporary file, then a rename). `shard` warns when the file is readable by others. A file that is not
277
+ valid JSON (or not this format) is never overwritten silently: commands that read it stop with exit 2
278
+ naming it, and a command that writes credentials first moves it to `credentials.json.corrupt-<time>`.
279
+ - **Profiles:** one per API URL, so production, staging and a local API never mix:
280
+
281
+ ```json
282
+ {"version": 1, "profiles": {"https://api.shardflux.dev": {
283
+ "session": {"token": "sfu_...", "expires_at": "...", "user": {"id": "...", "email": "..."}, "state": "active"},
284
+ "organization_id": "...", "project_id": "...",
285
+ "api_key": {"token": "sfk_...", "id": "...", "key_id": "...", "project_id": "...", "name": "shard-cli laptop"}}}}
286
+ ```
287
+
288
+ - **Precedence:** the project API key is `SHARDFLUX_API_KEY`, else the saved `api_key`. The session is
289
+ `SHARDFLUX_SESSION_TOKEN`, else the saved `session`. `shard auth logout` removes the session (the key
290
+ stays); delete the file to forget everything.
291
+ - **Never printed:** the session and the saved key appear only in the file. Output shows key ids; error
292
+ output redacts anything shaped like a key or session token. `shard env` prints the key on purpose.
293
+
294
+ ## Version check
295
+
296
+ `shard` tells you when a newer version exists, on stderr after the command's output:
297
+
298
+ ```text
299
+ @shardflux/cli 0.5.0 is outdated: 0.6.0 is available. Update: npm install -g @shardflux/cli@latest
300
+ ```
301
+
302
+ or that the API no longer supports it (`... is no longer supported by the Shardflux API (minimum
303
+ 0.6.0). Update: ...`). It asks `GET /v1/client-versions` at most once a day per API URL (cached in
304
+ `<config dir>/update-check.json`), in parallel with the command: a slow answer is skipped (the next run
305
+ uses it), the exit code never changes, and nothing is printed after a command that failed with
306
+ `--json`. Installed through the `shardflux` npm package it checks `shardflux` instead. `shard version`
307
+ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "update": {"package",
308
+ "current", "status", "latest", "minimum_supported", "upgrade_command", "checked_at"}}`). Turn it off with
309
+ `SHARDFLUX_NO_UPDATE_CHECK=1` (or `NO_UPDATE_NOTIFIER=1`). The SDK's own check is off inside `shard`.
310
+
38
311
  ## Environment
39
312
 
40
313
  | Variable | Meaning |
41
314
  | --- | --- |
42
- | `SHARDFLUX_API_KEY` | Project API key (`sfk_<key id>_<secret>`). Required. `shard` never accepts the key as an argument (`--api-key`, `--token` and similar flags are refused) and never prints it; anything shaped like a key is redacted from error output. |
43
- | `SHARDFLUX_API_URL` | API base URL. Default `https://api.shardflux.dev`; `--api-url` overrides it. Plain `http://` is allowed only for loopback hosts. |
44
- | `SHARDFLUX_AGENT_LABEL` | Attribution label for the tool tokens `shard` obtains for exec and files. Default `cli`; `--agent-label` overrides it. Each label is one agent session, listed by `shard ws sessions`. |
315
+ | `SHARDFLUX_API_KEY` | Project API key (`sfk_<key id>_<secret>`) for the workspace commands. Default: the key `shard setup` saved. `shard` never accepts a key as an argument (`--api-key`, `--token` and similar flags are refused) and never prints it (except `shard env`); anything shaped like a key is redacted from error output. |
316
+ | `SHARDFLUX_SESSION_TOKEN` | User session (`sfu_...`) for the account commands (0.5.0+). Default: the session `shard auth login` saved. |
317
+ | `SHARDFLUX_API_URL` | API base URL. Default `https://api.shardflux.dev`; `--api-url` overrides it. Plain `http://` is allowed only for loopback hosts. Credentials are saved per API URL. |
318
+ | `SHARDFLUX_CONFIG_DIR` | Directory of `credentials.json` and `update-check.json` (0.5.0+). Default `$XDG_CONFIG_HOME/shardflux`, else `~/.config/shardflux` (`%APPDATA%\shardflux` on Windows). |
319
+ | `SHARDFLUX_NO_UPDATE_CHECK=1` | Never ask whether a newer `shard` exists (0.5.0+; also `true`, `yes`, `on`). `NO_UPDATE_NOTIFIER=1` does the same. |
320
+ | `SHARDFLUX_AGENT_LABEL` | Attribution label for the tool tokens `shard` obtains for exec and files, and the reporting agent of `shard feedback` (0.5.0+). Default `cli`; `--agent-label` overrides it. Each label is one agent session, listed by `shard ws sessions`. |
45
321
  | `SHARDFLUX_NO_WAKE=1` | Do not resume a suspended workspace on use; same as `--no-wake` (see "Suspended workspaces" below). `--wake` overrides it. |
46
322
  | `SHARDFLUX_WAKE_TIMEOUT_MS` | Longest wait, in milliseconds, for a workspace to wake or finish a transition. Default `120000`; `--wake-timeout <ms>` overrides it. |
47
323
  | `SHARDFLUX_HTTP_KEEPALIVE=1` | Reuse HTTP connections (see "HTTP connections" below). |
48
-
49
- `shard login` stores nothing. There is no config file: the key is read from the environment on
50
- every run.
324
+ | `BROWSER` | Program `--open` runs with the URL (0.5.0+). Default `xdg-open`, `open` (macOS) or `rundll32` (Windows). |
51
325
 
52
326
  ## Commands
53
327
 
54
328
  | Command | What it does |
55
329
  | --- | --- |
56
- | `login`, `whoami` | Show the principal behind the key. |
57
- | `version` | Print the CLI and SDK versions. |
58
- | `usage [--org <id>]` | Usage summary for the current period: plan, meters and allowances. |
59
- | `workspaces open <key> --template <slug> [--secret NAME]... [--input NAME=value]... [--input-file F] [--lifetime persistent\|session] [--cpu-millis N --memory-mib N --disk-gib N] [--no-wait] [--timeout 5m] [--timing]` | Open by key. Creates the workspace on first use and reconnects or resumes it afterwards, never resetting it. Waits until it is ready (the template's start commands and services included) unless you pass `--no-wait`. `--secret` binds secret names (replacing the binding of an existing workspace). `--input` (0.4.0+) passes the template's text inputs (replacing those of an existing workspace); `--input-file` reads `NAME=value` lines or a JSON object. `--lifetime session`: the workspace is discarded when the session ends (`workspaces close` or the idle timeout), and the key then opens a new workspace. |
60
- | `workspaces list [--state S] [--prefix P] [--lifetime persistent\|session\|any] [--purpose standard\|template_draft\|template_test\|any] [--include-deleted] [--all] [--limit N] [--cursor C]` | One page, or every page with `--all`. The next cursor is printed on stderr. By default only persistent standard workspaces are listed. |
61
- | `workspaces get <id\|key>` | One workspace (deleted ones included), with the startup of its template's start commands and services (0.4.0+): a failed step shows its exit code, reason and output tail. |
330
+ | `auth register\|verify-email\|resend-verification\|login\|mfa\|status\|logout\|sessions\|step-up`, `auth password change\|reset\|reset-confirm`, `auth email change\|confirm`, `auth totp enroll\|confirm\|disable\|recovery-codes` | The account and its sign-in (0.5.0+): see [Accounts and sign-in](#accounts-and-sign-in-050). |
331
+ | `setup [--org O] [--project P] [--org-name N] [--project-name N] [--key-name N] [--tool T]... [--new-key]`, `env [--shell sh\|fish\|powershell]` | Organization, project and a saved API key in one step; the saved key as shell exports (0.5.0+). See [Setup](#setup-050). |
332
+ | `orgs`, `projects`, `api-keys`, `members`, `invitations`, `templates publish\|archive` | Organizations, projects, API keys, members and invitations with the session (0.5.0+). See [Organizations, projects, keys, members](#organizations-projects-keys-members-050). |
333
+ | `billing plans\|status\|upgrade\|portal\|invoices\|alerts` | Plans, Stripe Checkout, the billing portal, invoices and usage alerts (0.5.0+). See [Billing](#billing-050). |
334
+ | `audit list\|export`, `account status\|export\|delete\|cancel-deletion` | The audit log, data exports and account deletion (0.5.0+). |
335
+ | `egress get\|set\|history\|clear`, `volumes list\|create\|get\|delete\|attachments\|attach\|detach` | Outbound network policies and shared volumes with the project key (0.5.0+). |
336
+ | `login`, `whoami` | Show the principal behind the project key (`SHARDFLUX_API_KEY`, else the saved one). |
337
+ | `version [--check]` | Print the CLI and SDK versions and the update status (`--check` asks the API now). |
338
+ | `feedback [--category C] [--workspace W] [--request-id ID] [--error-code CODE] [--command TEXT] [--from-file F\|-] <message...>` | Send feedback straight to the Shardflux founder (0.5.0+). See [Feedback](#feedback-agents-use-this-while-you-work). |
339
+ | `usage [--org <id>] [--estimate \| --series \| --workspace <id\|key>] [--from T] [--to T] [--granularity hour\|day] [--meter M]` | Usage summary for the current period: plan, meters and allowances. `--estimate`: the bill so far and projected allowance use; `--series`: usage over time; `--workspace`: one workspace's usage over time (0.5.0+). |
340
+ | `workspaces open <key> --template <slug> [--secret NAME]... [--input NAME=value]... [--input-file F] [--lifetime persistent\|session] [--mode processful\|file-first] [--cpu-millis N --memory-mib N --disk-gib N] [--no-wait] [--timeout 5m] [--timing]` | Open by key. Creates the workspace on first use and reconnects or resumes it afterwards, never resetting it. Waits until it is ready (the template's start commands and services included) unless you pass `--no-wait`. `--secret` binds secret names (replacing the binding of an existing workspace). `--input` (0.4.0+) passes the template's text inputs (replacing those of an existing workspace); `--input-file` reads `NAME=value` lines or a JSON object. `--lifetime session`: the workspace is discarded when the session ends (`workspaces close` or the idle timeout), and the key then opens a new workspace. `--mode file-first` (0.5.0+) opens a [file-first workspace](#file-first-workspaces); the default is processful for a new key and the stored mode for an existing one. |
341
+ | `workspaces list [--state S] [--prefix P] [--lifetime persistent\|session\|any] [--purpose standard\|template_draft\|template_test\|any] [--include-deleted] [--all] [--limit N] [--cursor C]` | One page, or every page with `--all`. The next cursor is printed on stderr. By default only persistent standard workspaces are listed. A file-first workspace's state reads `running (file-first)` (0.5.0+). |
342
+ | `workspaces get <id\|key>` | One workspace (deleted ones included), with the startup of its template's start commands and services (0.4.0+): a failed step shows its exit code, reason and output tail. Its mode, and a file-first workspace's tree revision (0.5.0+). |
62
343
  | `workspaces inputs <id\|key>` | The workspace's text inputs (0.4.0+). |
63
- | `workspaces exec <id\|key> [--cwd D] [--env K=V]... [--timeout T] [--stdin F\|-] -- <cmd> [args...]` | Runs argv with no shell. Output is streamed and resumes from byte offsets after dropped connections. `shard` exits with the command's exit code. Ctrl-C cancels the command. |
344
+ | `workspaces exec <id\|key> [--cwd D] [--env K=V]... [--timeout T] [--stdin F\|-] [-v] [--execution-id ID] [--output-limit N] -- <cmd> [args...]` | Runs argv with no shell. Output is streamed and resumes from byte offsets after dropped connections. `shard` exits with the command's exit code. Ctrl-C cancels the command. `-v` (0.5.0+) then prints the session id and exit status on stderr. On a file-first workspace (0.5.0+) the command runs as an execution instead: see [File-first workspaces](#file-first-workspaces). |
345
+ | `executions get <id\|key> <execution-id> [--wait D]` | The result of a file-first workspace's execution (0.5.0+): its output, then what ran. Exits with the command's code, 6 if the execution failed or was lost, 5 while it still runs. |
64
346
  | `files read <id\|key> <path> [--out F]` | Raw bytes to stdout, or to a local file. |
65
- | `files write <id\|key> <path> [--from F\|-] [--append] [--no-parents]` | Atomic write. The default source is stdin. |
347
+ | `files write <id\|key> <path> [--from F\|-] [--append] [--no-parents] [--if-revision N]` | Atomic write. The default source is stdin. `--if-revision` (0.5.0+): file-first workspaces only, see [Tree revisions](#tree-revisions). |
66
348
  | `files ls <id\|key> <path> [--limit N]` | Directory listing. |
349
+ | `files stat <id\|key> <path> [--revision]` | One path's metadata; `--revision` adds the content's SHA-256 (0.4.0+). |
350
+ | `files search <id\|key> <path> <pattern> [--regex] [-i\|--ignore-case] [--include G]... [--exclude G]... [--max N]` | Search file contents under a directory (or in one file; gitignore-style globs): `path:line:column: text` per match (0.4.0+). |
351
+ | `files patch <id\|key> <path> (--old T --new T)... [--replace-all] \| --content-file F\|- [--expected-revision R] [--if-revision N]` | Replace exact text (each `--old` must occur once unless `--replace-all`), or the whole content, atomically; `R` is a revision from `files stat --revision` or `absent` (0.4.0+). `--if-revision` (0.5.0+): file-first workspaces only. |
67
352
  | `workspaces suspend\|resume <id\|key> [--wait] [--timeout T] [--timing]` | Lifecycle operation. Returns immediately unless you pass `--wait`. |
68
353
  | `workspaces fork <id\|key> <new-key> [caps] [--wait]` | Fork into a new key. |
69
354
  | `workspaces delete <id\|key> --yes [--wait]` | Delete the workspace. Tool access ends at once. The key of a persistent workspace is never reused. |
@@ -97,8 +382,11 @@ every run.
97
382
  | `templates draft publish <slug> [--state ID] [--description T] [--default-lifetime L] [--acknowledge PATH]... [--no-auto-publish] [--wait]` | Publish the draft as the template's next version. |
98
383
  | `templates draft discard <slug> --yes [--wait]` | Delete the draft and end its live test instances. |
99
384
 
100
- Aliases: `ws` for `workspaces`, `ops` for `operations`, `secret` for `secrets`, `template` for `templates`.
101
- `shard workspaces files ...` and `shard workspaces operations ...` also work. `<id|key>` is tried as an id when it
385
+ Aliases: `ws` for `workspaces`, `ops` for `operations`, `execution` for `executions`, `secret` for `secrets`,
386
+ `template` for `templates`, `org`/`organizations` for `orgs`, `project` for `projects`, `keys`/`api-key` for
387
+ `api-keys`, `member` for `members`, `invite`/`invitation` for `invitations`, `volume` for `volumes`.
388
+ `shard workspaces files ...`, `shard workspaces operations ...` and `shard workspaces executions ...` (0.5.0+)
389
+ also work. `<id|key>` is tried as an id when it
102
390
  is a UUID. Otherwise, or if no workspace has that id, it is matched as an exact workspace key across every lifetime
103
391
  and purpose, so sessions, drafts and test instances resolve too. The live workspace wins over tombstones that ended
104
392
  sessions left with the same key.
@@ -148,12 +436,103 @@ settings:
148
436
  - A file `shard` cannot read or pack (a symlink that leaves the folder, a device) is refused before any request (exit
149
437
  2). The API's refusals (`upload_missing`, `language_unavailable`, `invalid_settings`, ...) exit 2 with the reason.
150
438
 
439
+ ## File-first workspaces
440
+
441
+ (0.5.0+) A file-first workspace has no VM between commands. Its state is a versioned file tree under `/home/user`.
442
+ Each `shard ws exec` runs the command as an *execution*: a fresh VM on the latest tree revision, and the files the
443
+ command changed under `/home/user` become the next revision. Processes, memory and files elsewhere do not survive
444
+ an execution. A file-first workspace is ready at once, never suspended and always persistent, and it needs a layered
445
+ template version. A workspace's mode never changes.
446
+
447
+ ```sh
448
+ shard ws open acme/agent-7 --template python-node-browser --mode file-first
449
+ shard files write acme/agent-7 /home/user/app/main.py --from main.py # prints the new tree revision
450
+ shard ws exec acme/agent-7 -v -- python3 /home/user/app/main.py
451
+ shard ws exec acme/agent-7 --execution-id build-42 --timeout 10m -- bash -lc "make -C /home/user/app"
452
+ shard executions get acme/agent-7 build-42 # the recorded result, again
453
+ shard files patch acme/agent-7 /home/user/app/main.py --old "DEBUG = True" --new "DEBUG = False" \
454
+ --if-revision "$(shard ws get acme/agent-7 --json | jq .tree_revision)"
455
+ ```
456
+
457
+ `-v` prints what ran on stderr, after the command's own output:
458
+
459
+ ```text
460
+ $ shard ws exec acme/agent-7 -v -- bash -lc "python3 app/main.py > app/out.txt"
461
+ execution ex-6f1c2a9e-0d7b-4c1e-9a53-2b8e1f0c4d7a
462
+ state succeeded (exit code 0)
463
+ revision 1 -> 2
464
+ timings blobs_fetched=… exec_ms=… publish_ms=… queue_ms=… run_ms=… total_ms=… vm_seconds=…
465
+ changed 1 path
466
+ A /home/user/app/out.txt
467
+ ```
468
+
469
+ - **Exit code**: the command's own once it ran to its end: 124 if it timed out (`--timeout`), 128+N if a signal N
470
+ killed it. An execution that `failed` (it could not run the command, e.g. `exec_failed_to_start`) or was `lost`
471
+ (its host stopped answering, e.g. `lease_expired`) published nothing and exits 6. The error names the reason and
472
+ says `retryable=true|false`; running it again needs a new `--execution-id`.
473
+ - **Output** is printed when the command ended, not streamed: stdout and stderr byte for byte, also when they are not
474
+ UTF-8. `--output-limit <bytes>` keeps at most that much of each (default 1 MiB, at most 16 MiB). A cut stream is
475
+ noted on stderr.
476
+ - **`-v` / `--verbose`**: the execution id, its state and exit status, `base -> new` tree revision (`unchanged` when
477
+ the command changed nothing), `replayed` when this was not the call that ran it, the timings, and the changed paths
478
+ (`A` added, `M` modified, `D` deleted; a directory ends in `/`; the first 100, `--json` lists all). Without `-v`,
479
+ `--timing` prints the timings line alone.
480
+ - **`--json`**: one object, `{execution_id, state, exit_code, term_signal, timed_out, base_revision, tree_revision,
481
+ changed, changed_truncated, stdout, stdout_truncated, stderr, stderr_truncated, replayed, timings, error,
482
+ created_at, finished_at}`. `stdout` and `stderr` are UTF-8 text; a stream that is not valid UTF-8 also has
483
+ `stdout_base64` / `stderr_base64` with the exact bytes.
484
+ - **`--execution-id <id>`**: the execution's idempotency key (8-128 characters of `A-Z a-z 0-9 . _ : -`, starting
485
+ with a letter or digit; default a fresh `ex-<uuid>`). The same id with the same command returns the recorded result
486
+ (`replayed`) instead of running it twice, so a script can repeat the call safely; the same id with another command
487
+ is refused (`execution_id_reused`). `shard` re-sends the request with the same id after a dropped connection and
488
+ after `no_execution_host` (no host had room; `Retry-After`, at most 5 times), and waits within `--wake-timeout` while
489
+ another execution holds the workspace (`execution_in_progress`).
490
+ - **Ctrl-C**: an execution cannot be canceled. Ctrl-C stops waiting (exit 130) and prints the command that fetches the
491
+ result later:
492
+
493
+ ```text
494
+ error: Interrupted; execution build-42 keeps running server side if it started (an execution cannot be canceled). [code=interrupted execution_id=build-42]
495
+ hint: fetch its result later with: shard executions get acme/agent-7 build-42
496
+ ```
497
+ - **`shard executions get <id|key> <execution-id> [--wait D]`** (also `shard ws executions get`): prints the output and
498
+ the `-v` summary (`--json`: the object above) and exits like `exec`. An execution still queued or running exits 5;
499
+ `--wait 10m` waits up to that long for it to end. Results are kept 7 days.
500
+ - **Not in file-first mode**: `shard` refuses suspend, resume, fork, reset, save-as-template and `workspaces changes`
501
+ without sending them (`not_supported_for_mode`, exit 1, with a hint). Only paths under `/home/user` exist
502
+ (`outside_tree_root`). `--mode file-first` with `--lifetime session` is a usage error (exit 2), and so are
503
+ `--execution-id`, `--output-limit` and `--if-revision` on a processful workspace.
504
+ - **Opening**: reopening a key with the other mode is `mode_mismatch` (exit 1); a deployment that does not offer
505
+ file-first workspaces yet answers `mode_not_available` (exit 2); a template version without layered disks
506
+ `layout_unsupported` (exit 1). Each comes with a hint.
507
+
508
+ ### Tree revisions
509
+
510
+ A file-first workspace starts at tree revision 0. Every write, patch or execution that changes files publishes the
511
+ next revision. `shard ws get` shows the current one (`tree_revision` with `--json`), and `files write` and
512
+ `files patch` print the revision after their change (`tree_revision` with `--json`).
513
+
514
+ `--if-revision <n>` on `files write` and `files patch` applies the change only while the tree is at revision n.
515
+ Otherwise nothing changes, `shard` exits 1 and names the revision the tree is at:
516
+
517
+ ```text
518
+ error: The workspace's tree is not at the If-Match revision. (tree_revision_mismatch) [code=conflict status=409 request_id=... source=cell]
519
+ hint: the tree is at revision 6 now (another write or execution changed it); nothing changed. Read what you need again, then retry with --if-revision 6
520
+ ```
521
+
151
522
  ## Suspended workspaces
152
523
 
153
- `workspaces exec`, `files read|write|ls` and `workspaces changes` wake a suspended workspace: they
154
- resume it (or join the resume or open already running), wait until it runs, then run the request.
524
+ `workspaces exec`, `files read|write|ls|stat|search|patch` and `workspaces changes` wake a suspended workspace:
525
+ they resume it (or join the resume or open already running), wait until it runs, then run the request.
155
526
  A request made during a suspend or resume waits for the transition to finish. The request itself
156
- never runs twice, because the workspace refused it before doing anything.
527
+ never runs twice, because the workspace refused it before doing anything. Since 0.5.0 the wake is one
528
+ request: the API holds the resume until the workspace runs and answers with its tool token (an older
529
+ API is waited for as before).
530
+
531
+ A file-first workspace (0.5.0+) is never suspended, so none of this applies to it.
532
+
533
+ `files read|ls|stat|search` of a suspended workspace whose disk a host still holds are answered from that disk
534
+ without waking it (the state at suspension); `files search` then notes it on stderr (`served_from: "disk"` with
535
+ `--json`).
157
536
 
158
537
  - `--wake-timeout <ms>` (or `SHARDFLUX_WAKE_TIMEOUT_MS`, default `120000`) bounds the wait. Past
159
538
  it, `shard` exits 5 and names the operation and its state (for example `Operation <id> (open)
@@ -213,8 +592,11 @@ Which call is timed:
213
592
  - `workspaces suspend|resume|fork|delete|close|reset` and `templates draft state|discard`: with `--wait`, the request
214
593
  and the whole wait; without it, the request.
215
594
  - `operations wait`: the wait.
216
- - `workspaces exec`, `files read|write|ls` and `workspaces changes`: the wake, when the command had to wake a
217
- suspended workspace. Without a wake nothing is printed.
595
+ - `workspaces exec`, `files read|write|ls|stat|search|patch` and `workspaces changes`: the wake, when the command had
596
+ to wake a suspended workspace. Without a wake nothing is printed.
597
+ - `workspaces exec` on a file-first workspace (0.5.0+): the execution's own timings as the cell reports them
598
+ (`execution <id> timings: blobs_fetched=... exec_ms=... publish_ms=... queue_ms=... run_ms=... total_ms=...`);
599
+ nothing is woken.
218
600
 
219
601
  With `--json`, stdout carries the timing as a `timing` field of the command's JSON instead: `action`, `outcome`,
220
602
  `workspace_id`, `operation_id`, `total_ms`, `phases` (`phase`, `reason`, `operation_id`, `start_ms`, `duration_ms`),
@@ -248,21 +630,47 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
248
630
  ## Output
249
631
 
250
632
  - **Human (default):** aligned tables and key/value blocks on stdout. Errors go to stderr as
251
- `error: <message> (<reason>) [code=... status=... request_id=... operation_id=...]`, with a
252
- hint when there is one. A failed operation adds `retryable=true|false` (0.3.1+).
633
+ `error: <message> (<reason>) [code=... status=... request_id=... operation_id=... execution_id=...]`, with a
634
+ hint when there is one. A failed operation adds `retryable=true|false` (0.3.1+), and so does a failed or lost
635
+ execution (0.5.0+). A failure ends with a `feedback: shard feedback ...` line carrying the request id and error
636
+ code (0.5.0+), except the expected flow: usage and validation errors, Ctrl-C, a missing or refused credential, a
637
+ sign-in or step-up to finish, a wait that gave up, and refusals whose hint names the next step.
253
638
  - **`--json`:** stdout carries the API's own objects:
254
639
  - `workspaces open`/`get` print the workspace view; `list` prints `{data, next_cursor}`.
255
640
  - Lifecycle commands print the operation; `fork` prints `{operation, workspace}`.
256
641
  - `login`/`whoami` print `{api_url, organization_id, project_id, api_key:{id,key_id}, tool_permissions}`.
257
642
  - `exec` prints `{session_id, exit_code, term_signal, timed_out, canceled, stdout, stderr, truncated, reconnects}`.
258
- - `files read` prints `{path, bytes, content_base64}`; `files write` prints the write result.
643
+ On a file-first workspace (0.5.0+), `exec` and `executions get` print the execution (see
644
+ [File-first workspaces](#file-first-workspaces)).
645
+ - `files read` prints `{path, bytes, content_base64}`; `files write` prints the write result. On a file-first
646
+ workspace `files write` and `files patch` add `tree_revision` (0.5.0+).
647
+ - `files stat` prints the file info (with `revision` when asked); `files search` prints
648
+ `{matches, truncated, stop_reason?, files_scanned, served_from}`; `files patch` prints
649
+ `{path, revision, previous_revision, bytes_written, durable, replacements?, file?}`.
259
650
  - `secrets create`/`get`/`update` print the secret's metadata; `list`, `versions` and `access-log`
260
651
  print `{data, next_cursor}`; `rotate` prints `{secret, version}`; `delete` prints
261
652
  `{deleted, id, name, scope}`; `workspaces secrets` prints `{workspace_id, names, secrets}`.
653
+ - Account commands (0.5.0+) print the API's objects too: lists are `{data, next_cursor}` (with
654
+ `default_organization_id`, `default_project_id` or `saved_api_key_id` where a default applies);
655
+ `auth login`/`auth mfa` print `{status, user, session: {expires_at, credentials_file}, organizations,
656
+ next}` (never the token); `auth status` prints `{api_url, credentials_file, session, api_key, defaults}`;
657
+ `setup` prints `{organization, project, api_key: {id, key_id, name, project_id, tool_permissions},
658
+ created: {organization, project, api_key}, credentials_file, next}`; `api-keys create` prints
659
+ `{api_key, secret, saved: false}` (or `{api_key, saved: true, credentials_file}` with `--save`);
660
+ `billing upgrade` prints `{subscription_exists: false, organization_id, plan_key, checkout, url, opened,
661
+ subscription_active}` or `{subscription_exists: true, organization_id, plan_key, portal_url, opened}`;
662
+ exports print `{export, out, bytes}`; `env` prints `{shell, variables}`; `version` prints
663
+ `{cli, sdk, update}`.
262
664
 
263
665
  Errors go to stderr as `{"error": {code, message, status, request_id, ...}}`. A failed operation is
264
666
  `{"error": {"code": "operation_failed", "message", "operation_id", "retryable", "details": {"error_code",
265
- "reason"}, "operation"}}`; `retryable` (0.3.1+) is the operation error's own flag.
667
+ "reason"}, "operation"}}`; `retryable` (0.3.1+) is the operation error's own flag. A failed or lost execution
668
+ (0.5.0+) is `{"error": {"code": "execution_failed", "message", "execution_id", "retryable", "details": {"reason",
669
+ "error_code", "state"}}}` after the execution on stdout; one still running (`executions get`) is
670
+ `{"error": {"code": "timeout", "message", "execution_id", "last_state"}}`.
671
+ The same failures that print a `feedback:` line add a `"feedback"` field next to `"error"` with that command
672
+ (0.5.0+).
673
+ - `feedback` prints `{id, received_at, duplicate}`.
266
674
  - **`--timing`:** the timing goes to stderr after the output. With `--json` it is a `timing` field instead
267
675
  (see "Timing").
268
676
 
@@ -274,11 +682,11 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
274
682
  | 1 | error: an API refusal such as `conflict`, `quota_exceeded` or `workspace_not_running`, or a network or protocol failure |
275
683
  | 2 | usage error: bad arguments, or input the API rejected as `validation_failed` |
276
684
  | 3 | not found: workspace, operation or route |
277
- | 4 | authentication or authorization: missing, malformed, revoked or insufficient key |
278
- | 5 | timeout: waiting gave up (`--timeout`, or `--wake-timeout` while waking a workspace); the operation continues server side (`shard operations wait <id>`), a start waiting for capacity for at most 15 minutes |
279
- | 6 | the awaited operation ended `failed` or `canceled` (also a resume or open that failed while waking a workspace). `retryable=true` in the error means it may succeed later (`capacity_unavailable`) |
280
- | 130 | interrupted (Ctrl-C). Waits stop at once, and exec sends a cancel to the session. |
281
- | exec | the command's own code: 124 if it timed out, 128+N if killed by signal N. Failures before the command ran use the codes above; use `--json` to tell them apart. |
685
+ | 4 | authentication or authorization: missing, malformed, revoked or insufficient key or session; `step_up_required` (run `shard auth step-up`), `mfa_required` (a sign-in waiting for its code), `email_unverified`, `invalid_credentials` |
686
+ | 5 | timeout: waiting gave up (`--timeout`, or `--wake-timeout` while waking a workspace); the operation continues server side (`shard operations wait <id>`), a start waiting for capacity for at most 15 minutes. `executions get` (0.5.0+): the execution is still queued or running. `billing upgrade --wait` (0.5.0+): nobody paid in time (the checkout stays open) |
687
+ | 6 | the awaited operation ended `failed` or `canceled` (also a resume or open that failed while waking a workspace). `retryable=true` in the error means it may succeed later (`capacity_unavailable`). Also a file-first execution that ended `failed` or `lost` (0.5.0+): nothing was published. `billing upgrade --wait` (0.5.0+): the checkout expired or was canceled (`checkout_ended`) |
688
+ | 130 | interrupted (Ctrl-C). Waits stop at once, and exec sends a cancel to the session. A file-first execution cannot be canceled: it keeps running, and the error names the `shard executions get` command for its result. |
689
+ | exec, executions get | the command's own code: 124 if it timed out, 128+N if killed by signal N. Failures before the command ran use the codes above; use `--json` to tell them apart. |
282
690
 
283
691
  ## HTTP connections
284
692
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The account plane's command table (0.5.0+; docs/progress/account-cli.md §5): sign-in, setup, organizations,
3
+ * projects, API keys, members, invitations, billing, audit, account export/deletion, plus the project-key commands
4
+ * that were dashboard-only before (egress, volumes) and template publish/archive. Pure data like args.ts, which
5
+ * merges it into COMMANDS.
6
+ *
7
+ * Credentials: commands under auth, orgs, projects, api-keys, members, invitations, billing, audit, account and
8
+ * templates publish|archive use the user session (SHARDFLUX_SESSION_TOKEN, else `shard auth login`'s saved one);
9
+ * egress and volumes use the project API key like the other workspace commands. Secrets never come from arguments:
10
+ * passwords are read from standard input or a no-echo prompt; emailed links are single-use positionals.
11
+ */
12
+ import type { CommandSpec } from './args.js';
13
+ export declare const ACCOUNT_COMMANDS: CommandSpec[];