@shardflux/cli 0.8.0 → 0.9.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/CHANGELOG.md +104 -1
- package/README.md +99 -29
- package/dist/account-args.js +32 -4
- package/dist/account-commands.js +234 -111
- package/dist/args.d.ts +1 -1
- package/dist/args.js +80 -4
- package/dist/commands.d.ts +14 -0
- package/dist/commands.js +144 -3
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +1 -0
- package/dist/format.d.ts +21 -1
- package/dist/format.js +65 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.js +31 -0
- package/dist/timing.d.ts +14 -2
- package/dist/timing.js +37 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,112 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 — 2026-10-04 (not yet published)
|
|
4
|
+
|
|
5
|
+
- `shard ws hint <workspace>` explicitly stages a parked workspace ahead of a VM tool call, without waiting for restore or resuming a suspended workspace.
|
|
6
|
+
|
|
3
7
|
Every command and option the README shows is available from the version named here. Breaking changes ship in minor
|
|
4
8
|
releases and are marked **Breaking**.
|
|
5
9
|
|
|
6
|
-
## 0.
|
|
10
|
+
## 0.9.0 (not yet published)
|
|
11
|
+
|
|
12
|
+
Needs `@shardflux/sdk` 0.14.0 or later.
|
|
13
|
+
|
|
14
|
+
### Sign up from an agent
|
|
15
|
+
|
|
16
|
+
- `shard signup [--email <email>] [--name <name>] [--no-codex] [--no-setup] [--tool <tool>...]`: creates the account,
|
|
17
|
+
signs in and runs `shard setup` in one command, with no browser and no password. With the Codex CLI signed in with
|
|
18
|
+
ChatGPT on the machine, Codex refreshes its login (`codex app-server`) and the ID token from `$CODEX_HOME/auth.json`
|
|
19
|
+
verifies the email (the account starts with its trial; an existing account of that identity or email is signed
|
|
20
|
+
in). Without one, `--email` creates an account that works at once on the Free plan. `--json`: `{created, access,
|
|
21
|
+
method, user, setup}`.
|
|
22
|
+
- `shard verify [<code>]`: emails a 6-digit verification code (and asks for it on a terminal), or confirms one.
|
|
23
|
+
Nothing is signed out; an account from `shard signup` gets its trial.
|
|
24
|
+
- `shard auth login --codex` signs in with the Codex login; `shard auth step-up --codex` confirms a sensitive action
|
|
25
|
+
with it instead of the password.
|
|
26
|
+
- After signing in with an unverified email, the next step printed is `shard verify`.
|
|
27
|
+
|
|
28
|
+
### Workspaces working at once
|
|
29
|
+
|
|
30
|
+
- A command whose workspace call finds every workspace the plan allows to work at once busy is sent again after
|
|
31
|
+
`Retry-After` (the SDK's retries; the call did not run). A refusal that lasts exits 1 with 429 `quota_exceeded`
|
|
32
|
+
(`details.limit` `concurrent_workspaces`) and the hint `every workspace your plan allows to work at once is busy
|
|
33
|
+
and the call did not run (it was already retried); retry in 2 s, or upgrade: shard billing plans`.
|
|
34
|
+
- The API's 403 `quota_exceeded` with `details.limit` `retained_state` (opening a new key or forking while the
|
|
35
|
+
plan's Retained state is used up) hints `delete workspaces you no longer need (shard ws delete <key> --yes), or
|
|
36
|
+
upgrade: shard billing plans`. Neither suggests `shard feedback`.
|
|
37
|
+
- `--json` errors carry `retry_after_seconds` when the refusal has `Retry-After` (e.g. 429 `quota_exceeded`, 429
|
|
38
|
+
`rate_limited`).
|
|
39
|
+
- `shard usage` says `retained state used up: new workspaces and forks are refused (...)` when the Retained state
|
|
40
|
+
allowance's `cap_state` is `storage_blocked` (`enforcement` `storage_block`).
|
|
41
|
+
|
|
42
|
+
### Inbound ports
|
|
43
|
+
|
|
44
|
+
Additive. Serve a port of a processful workspace at its own private HTTPS URL; a request wakes a parked or suspended
|
|
45
|
+
workspace.
|
|
46
|
+
|
|
47
|
+
- `shard ws port expose <id|key> <port>`: the URL and how requests get in (`--json`: the API's port object
|
|
48
|
+
`{port, url, created_at, callback}`). Idempotent.
|
|
49
|
+
- `shard ws port ls <id|key>`: port, URL, exposure time and callback URL (`--json`: `{ports}`).
|
|
50
|
+
- `shard ws port close <id|key> <port>` (`--json`: `{workspace_id, port, closed}`).
|
|
51
|
+
- `shard ws port token <id|key> <port> [--ttl 1m..24h]`: the port token alone on stdout, how to send it on stderr
|
|
52
|
+
(`--json`: `{token, expires_at, url}`).
|
|
53
|
+
- `shard ws port link <id|key> <port> [--ttl 1m..7d] [--path /...]`: a URL that opens the port in a browser
|
|
54
|
+
(`--json`: `{url, expires_at}`).
|
|
55
|
+
- `shard ws port callback <id|key> <port> [--revoke]`: the port's callback URL for webhooks and OAuth redirects, shown
|
|
56
|
+
once (`--json`: `{url, created_at}`; with `--revoke`: `{workspace_id, port, revoked}`).
|
|
57
|
+
- `ports` is an alias of `port`. A port outside 1-65535, a `--ttl` out of range or not in whole seconds, and a
|
|
58
|
+
`--path` without a leading `/` exit 2 before any request; a file-first workspace is refused before any port
|
|
59
|
+
request. Hints for `port_not_exposed` (expose it first) and `port_limit` (close a port first).
|
|
60
|
+
|
|
61
|
+
### Memory across platform upgrades
|
|
62
|
+
|
|
63
|
+
- The cold-boot note and `--json --timing` name the new reason `runtime_retired` (the runtime the workspace was
|
|
64
|
+
suspended on was retired after an announced window): `note: workspace <id> resumed from its saved disk; running
|
|
65
|
+
processes were restarted (runtime_retired)`.
|
|
66
|
+
|
|
67
|
+
### Resize a workspace
|
|
68
|
+
|
|
69
|
+
- `shard ws resize <id|key> [--memory MIB] [--held MIB] [--fixed|--elastic] [--cpu MILLIS] [--disk GIB]
|
|
70
|
+
[--timeout D] [--timing]`: changes the caps of a running, suspended or stopped workspace without a restart
|
|
71
|
+
(`PATCH /v1/workspaces/{id}/caps` through `workspace.resize()`), and waits until the resize has finished. It prints
|
|
72
|
+
per resource the change and when it applies, e.g. `memory 2048 → 6144 MiB now` or `memory 4096 → 8192 MiB
|
|
73
|
+
elastic, held 1024 MiB on resume`, with what was clamped to a limit or applied in part. `--json` prints the result
|
|
74
|
+
(`workspace_id`, `operation_id`, `state`, the `memory` / `cpu` / `disk` named, `caps`, and `operation` when one was
|
|
75
|
+
waited for). No resource flag, `--fixed` with `--elastic`, or `--held` with `--fixed` are usage errors (exit 2) with
|
|
76
|
+
no request. Ctrl-C names the operation, which continues server side.
|
|
77
|
+
|
|
78
|
+
### Memory hint for commands
|
|
79
|
+
|
|
80
|
+
- `shard ws exec --resource-hint auto|light|heavy` and `shard ws exec-start --resource-hint …` send `resource_hint`
|
|
81
|
+
with the exec start: `heavy` gives an elastic workspace its memory before the command starts, `light` starts it at
|
|
82
|
+
once, `auto` (the default) decides from the command. Without the flag nothing is sent. On a file-first workspace
|
|
83
|
+
`--resource-hint` is a usage error (exit 2).
|
|
84
|
+
|
|
85
|
+
### Elastic memory
|
|
86
|
+
|
|
87
|
+
- `--memory-held` on `shard ws open` / `shard ws fork` no longer needs `--allocation-mode elastic`: without a mode the
|
|
88
|
+
API decides (with elastic-by-default enabled for the organization a new workspace is elastic; otherwise
|
|
89
|
+
`requires_elastic`). With `--allocation-mode fixed` it stays a usage error.
|
|
90
|
+
|
|
91
|
+
## 0.8.1 (not yet published)
|
|
92
|
+
|
|
93
|
+
Needs `@shardflux/sdk` 0.13.1 or later.
|
|
94
|
+
|
|
95
|
+
### Workspaces recover by themselves
|
|
96
|
+
|
|
97
|
+
When the machine a workspace runs on fails, the workspace is suspended, and the next command on it resumes it.
|
|
98
|
+
|
|
99
|
+
- A resume that booted the workspace's own disk (`cold_boot_reason` `host_lost`) prints the cold-boot note with the
|
|
100
|
+
reason in words: `note: workspace <id> resumed from its saved disk; running processes were restarted (the machine it
|
|
101
|
+
ran on failed)`.
|
|
102
|
+
- A resume that restored its newest checkpoint prints `note: workspace <id> resumed from checkpoint <id> (state as of
|
|
103
|
+
<time>) after the machine it ran on failed; changes after that time are not in it` on stderr (also with `--json`,
|
|
104
|
+
`ws resume --wait`, `operations wait` and a command's wake).
|
|
105
|
+
- `--timing --json`: `server.host_lost` (`detected_at`, `restored_from`, `restored_checkpoint_id`, `state_as_of`).
|
|
106
|
+
`--timing` prints the SDK's `restored checkpoint <id> (host_lost; state as of <time>)`.
|
|
107
|
+
- `hostLostNotice()` is exported next to `coldBootNotice()`.
|
|
108
|
+
|
|
109
|
+
## 0.8.0
|
|
7
110
|
|
|
8
111
|
Needs `@shardflux/sdk` 0.13.0 or later.
|
|
9
112
|
|
package/README.md
CHANGED
|
@@ -37,14 +37,11 @@ Or run it without installing: `npx @shardflux/cli --help`. The unscoped
|
|
|
37
37
|
|
|
38
38
|
## Quick start
|
|
39
39
|
|
|
40
|
-
No account yet?
|
|
41
|
-
|
|
40
|
+
No account yet? One command (0.9.0+) creates it, signs you in and saves an API key: no browser, no
|
|
41
|
+
password. With the Codex CLI signed in with ChatGPT on this machine, your email is verified by it.
|
|
42
42
|
|
|
43
43
|
```sh
|
|
44
|
-
|
|
45
|
-
shard auth verify-email 'https://app.shardflux.dev/auth/verify-email#token=...' # the link from the email
|
|
46
|
-
printf '%s\n' "$PASSWORD" | shard auth login --email you@example.com
|
|
47
|
-
shard setup # organization, project and an API key, saved
|
|
44
|
+
shard signup # or: shard signup --email you@example.com
|
|
48
45
|
shard workspaces open acme/demo --template python-node-browser
|
|
49
46
|
shard ws exec acme/demo -- python3 -c 'print(40 + 2)'
|
|
50
47
|
shard files write acme/demo /home/user/notes.txt --from notes.txt
|
|
@@ -111,14 +108,14 @@ npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo
|
|
|
111
108
|
`shard` is built to be driven by an agent: no browser, no prompts when standard input is not a
|
|
112
109
|
terminal, `--json` on every command, secrets only on standard input, and documented exit codes.
|
|
113
110
|
|
|
114
|
-
1. **Account:** `
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
1. **Account (0.9.0+):** `shard signup --json`. With the Codex CLI signed in with ChatGPT on the
|
|
112
|
+
machine it signs up (or signs in) verified by that login; otherwise pass `--email <email>` and the
|
|
113
|
+
account works at once on the Free plan. It then runs `shard setup`: an organization, a project and
|
|
114
|
+
an API key with every workspace tool, saved in the credentials file. With two-factor authentication
|
|
115
|
+
it exits 4 with `mfa_required`: ask the person for the code, then `shard auth mfa --code <code>`.
|
|
116
|
+
2. **Verify the email** (an `--email` signup, to start the trial): `shard verify --json` emails a
|
|
117
|
+
6-digit code; ask the person for it, then `shard verify <code>`. Nothing is signed out.
|
|
118
|
+
3. **Set up again any time:** `shard setup --json` is idempotent.
|
|
122
119
|
4. **Work:** `shard ws open <key> --template <slug> --json`, `shard ws exec <key> -- <cmd>`,
|
|
123
120
|
`shard files ...` use the saved key.
|
|
124
121
|
5. **A paid plan:** `shard billing plans`, then `shard billing upgrade <plan> --json` prints the Stripe
|
|
@@ -132,12 +129,15 @@ terminal, `--json` on every command, secrets only on standard input, and documen
|
|
|
132
129
|
7. **Feedback:** send `shard feedback` as you work, the moment something fails, confuses you or is
|
|
133
130
|
missing, or your user asks for an improvement (see [Feedback](#feedback-agents-use-this-while-you-work)).
|
|
134
131
|
|
|
135
|
-
What
|
|
136
|
-
|
|
132
|
+
What a person does: entering a card in Stripe Checkout, the authenticator app's codes, and (for an
|
|
133
|
+
`--email` signup) reading the 6-digit code.
|
|
137
134
|
|
|
138
135
|
## Accounts and sign-in (0.5.0+)
|
|
139
136
|
|
|
140
137
|
```sh
|
|
138
|
+
shard signup [--email you@example.com] [--name "Ada Lovelace"] [--no-codex] [--no-setup] # 0.9.0+
|
|
139
|
+
shard verify [<code>] # 0.9.0+: emails a 6-digit code, then confirms it
|
|
140
|
+
shard auth login --codex # 0.9.0+: sign in with the Codex login on this machine
|
|
141
141
|
printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com [--name "Ada Lovelace"]
|
|
142
142
|
shard auth verify-email '<link from the email>' # or just the token in it
|
|
143
143
|
shard auth resend-verification # signed in, email not verified yet
|
|
@@ -147,6 +147,7 @@ shard auth status # session and saved key,
|
|
|
147
147
|
shard auth sessions [--revoke <session-id>] # browser and CLI sessions
|
|
148
148
|
shard auth logout [--all]
|
|
149
149
|
printf '%s\n' "$PASSWORD" | shard auth step-up [--code 123456]
|
|
150
|
+
shard auth step-up --codex [--code 123456] # 0.9.0+: confirm with the linked Codex login
|
|
150
151
|
printf '%s\n%s\n' "$CURRENT" "$NEW" | shard auth password change
|
|
151
152
|
shard auth password reset --email you@example.com
|
|
152
153
|
printf '%s\n' "$NEW" | shard auth password reset-confirm '<link from the email>'
|
|
@@ -157,6 +158,12 @@ shard auth totp disable --code 123456
|
|
|
157
158
|
shard auth totp recovery-codes
|
|
158
159
|
```
|
|
159
160
|
|
|
161
|
+
- **`shard signup`** (0.9.0+) asks the Codex CLI on this machine to refresh its ChatGPT login
|
|
162
|
+
(`codex app-server`) and sends the ID token from `$CODEX_HOME/auth.json` (default `~/.codex`), never
|
|
163
|
+
Codex's access or refresh token. The account is verified and starts with its trial; an account that
|
|
164
|
+
already uses that ChatGPT identity or email is signed in instead. Without a Codex login, `--email`
|
|
165
|
+
creates an account that works at once on the Free plan; `shard verify` starts the trial.
|
|
166
|
+
`--no-codex` uses `--email` even when Codex is signed in; `--no-setup` skips `shard setup`.
|
|
160
167
|
- **Passwords never come from arguments** (`--password` is refused). On a terminal `shard` asks without
|
|
161
168
|
echo (a new password twice); otherwise it reads standard input, one line per secret: `auth password
|
|
162
169
|
change` reads the current password, then the new one. Emailed links and tokens are single-use
|
|
@@ -177,7 +184,7 @@ shard auth totp recovery-codes
|
|
|
177
184
|
it from standard input (or asks), sets it, and otherwise prints the `auth password reset-confirm`
|
|
178
185
|
command to run.
|
|
179
186
|
- After `auth login`, `shard` prints who you are, whether the email is verified, your organizations
|
|
180
|
-
and the next step (`shard setup`, or `shard
|
|
187
|
+
and the next step (`shard setup`, or `shard verify`).
|
|
181
188
|
|
|
182
189
|
## Setup (0.5.0+)
|
|
183
190
|
|
|
@@ -378,8 +385,8 @@ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "u
|
|
|
378
385
|
| `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+). |
|
|
379
386
|
| `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+). Its memory (0.8.0+): `fixed 4096 MiB`, or `elastic: promised …, held …, plugged +… MiB`. The template version whose [immutable paths](#immutable-paths-080) it has mounted (0.8.0+): `immutable paths v3 (mounted read-only)`. |
|
|
380
387
|
| `workspaces inputs <id\|key>` | The workspace's text inputs (0.4.0+). |
|
|
381
|
-
| `workspaces exec <id\|key> [--cwd D] [--env K=V]... [--timeout T] [--stdin F\|-] [-v] [--execution-id ID] [--output-limit N] [--burst [--burst-vcpus N] [--burst-memory MIB]] -- <cmd> [args...]` | Runs argv with no shell. Output is streamed (in the start request itself, 0.8.0+) 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, and the memory grow the command waited for on an elastic workspace (0.8.0+; `--json`: `memory_grow`). `--cwd` is an absolute path (default `/home/user`); a relative one is refused (`invalid_cwd`, exit 2). A command that could not start (a `--cwd` that is not a directory, a program not on `PATH`) exits 6 with the workspace's reason and a hint (0.5.1+; before, it exited 1 with no output). On a file-first workspace (0.5.0+) the command runs as an execution instead: see [File-first workspaces](#file-first-workspaces). `--burst` (0.8.0+): [burst execution](#burst-execution-080). |
|
|
382
|
-
| `workspaces exec-start <id\|key> [--session-id ID] [--cwd D] [--timeout T] [--stdin-open] -- <cmd> [args...]`, `workspaces exec-read <id\|key> <session-id> [--follow]`, `workspaces exec-cancel <id\|key> <session-id> [--grace D]` | Background commands (0.8.0+): start a command and return at once, read its output and state (`--follow` streams it to its exit and exits with its code; while it runs `exec-read` exits 5), stop it. See [Background commands](#background-commands-080). |
|
|
388
|
+
| `workspaces exec <id\|key> [--cwd D] [--env K=V]... [--timeout T] [--stdin F\|-] [-v] [--execution-id ID] [--output-limit N] [--resource-hint auto\|light\|heavy] [--burst [--burst-vcpus N] [--burst-memory MIB]] -- <cmd> [args...]` | Runs argv with no shell. Output is streamed (in the start request itself, 0.8.0+) 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, and the memory grow the command waited for on an elastic workspace (0.8.0+; `--json`: `memory_grow`). `--cwd` is an absolute path (default `/home/user`); a relative one is refused (`invalid_cwd`, exit 2). A command that could not start (a `--cwd` that is not a directory, a program not on `PATH`) exits 6 with the workspace's reason and a hint (0.5.1+; before, it exited 1 with no output). On a file-first workspace (0.5.0+) the command runs as an execution instead: see [File-first workspaces](#file-first-workspaces). `--burst` (0.8.0+): [burst execution](#burst-execution-080). `--resource-hint` (0.9.0+): [elastic memory](#elastic-memory-080). |
|
|
389
|
+
| `workspaces exec-start <id\|key> [--session-id ID] [--cwd D] [--timeout T] [--stdin-open] [--resource-hint auto\|light\|heavy] -- <cmd> [args...]`, `workspaces exec-read <id\|key> <session-id> [--follow]`, `workspaces exec-cancel <id\|key> <session-id> [--grace D]` | Background commands (0.8.0+): start a command and return at once, read its output and state (`--follow` streams it to its exit and exits with its code; while it runs `exec-read` exits 5), stop it. See [Background commands](#background-commands-080). |
|
|
383
390
|
| `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. |
|
|
384
391
|
| `files read <id\|key> <path> [--out F]` | Raw bytes to stdout, or to a local file. |
|
|
385
392
|
| `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). |
|
|
@@ -391,6 +398,7 @@ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "u
|
|
|
391
398
|
| `workspaces suspend <id\|key> --durable [--timeout T]` | (0.7.0+) Suspend and wait until the copy is in durable storage. See "Durable storage". |
|
|
392
399
|
| `workspaces suspend <id\|key> --when-idle D` / `--cancel-when-idle` | (0.5.1+) Suspend once the workspace has been idle for `D` (0 to 1h; `0` = as soon as it is idle, 0.6.1+), or cancel that. See "Suspend when idle". |
|
|
393
400
|
| `workspaces fork <id\|key> <new-key> [caps] [--allocation-mode fixed\|elastic] [--memory-held N] [--wait]` | Fork into a new key. Without caps the fork keeps the source's caps and memory allocation (0.8.0+). With `--wait` the API holds the fork until the copy runs and returns it ready in the same request (0.8.0+; `--timing` shows one `request` phase marked `(held)`). |
|
|
401
|
+
| `workspaces resize <id\|key> [--memory MIB] [--held MIB] [--fixed\|--elastic] [--cpu MILLIS] [--disk GIB] [--timeout 5m] [--timing]` | Change the memory, CPU and disk of a running or suspended workspace without a restart (0.9.0+). Prints per resource the change and when it applies (`now`, `on resume`, `next start`). See [Resize a workspace](#resize-a-workspace-090). |
|
|
394
402
|
| `workspaces delete <id\|key> --yes [--wait]` | Delete the workspace. Tool access ends at once. After deletion finishes, the key can open a new workspace ID. |
|
|
395
403
|
| `workspaces close <id\|key> [--wait]` | End a session workspace now: it is deleted, and the key then opens a new workspace. A persistent workspace is refused (`not_session`). |
|
|
396
404
|
| `workspaces reset <id\|key> --yes [--wait]` | Layered workspaces: wipe every change and restart on the template. The previous state stays restorable for 7 days. |
|
|
@@ -398,6 +406,8 @@ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "u
|
|
|
398
406
|
| `workspaces changes <id\|key> [--path P] [--hash] [--summary] [--all] [--limit N] [--cursor C]` | What a layered, running workspace changed against its template. |
|
|
399
407
|
| `workspaces sessions <id\|key>` | Attributed agent sessions: label, principal and tokens issued. |
|
|
400
408
|
| `workspaces secrets <id\|key> [--set NAME]... [--clear]` | Show the secret names bound to the workspace and their status, or replace them. |
|
|
409
|
+
| `workspaces port expose <id\|key> <port>`, `workspaces port ls <id\|key>`, `workspaces port close <id\|key> <port>` | Serve a port of the workspace at its own private HTTPS URL, list the exposed ports, close one (0.9.0+; see [Inbound ports](#inbound-ports-090)). |
|
|
410
|
+
| `workspaces port token <id\|key> <port> [--ttl D]`, `workspaces port link <id\|key> <port> [--ttl D] [--path P]`, `workspaces port callback <id\|key> <port> [--revoke]` | A port token (printed alone), a link that opens the port in a browser, the port's callback URL for webhooks and OAuth redirects (0.9.0+). |
|
|
401
411
|
| `secrets create <NAME> [--from-file F] [--description T] [--tool T]... [--allow-workspace ID]... [--org ID [--allow-project ID]... [--all-projects]]` | Create a secret. The value comes from standard input or `--from-file`, never from an argument. |
|
|
402
412
|
| `secrets list [--org ID] [--include-deleted] [--all]`, `secrets get <id\|NAME>` | Secret metadata. Values are never shown. |
|
|
403
413
|
| `secrets update <id\|NAME> [--description T] [--tool T]... [--allow-workspace ID]... [--any-workspace] [--allow-project ID]... [--all-projects] [--clear-projects]` | Change the description or usage permissions. |
|
|
@@ -607,11 +617,11 @@ without waking it (the state at suspension); `files search` then notes it on std
|
|
|
607
617
|
refused at once (exit 1, `workspace_not_running`).
|
|
608
618
|
|
|
609
619
|
**Detecting a cold resume (0.6.0+).** A resume restores memory and running processes. After a platform runtime
|
|
610
|
-
update,
|
|
611
|
-
workspace; check `memory_restored`. The command still runs
|
|
612
|
-
fresh, as after a reboot: start dev servers and background jobs again. `shard`
|
|
613
|
-
the output (also with `--json`, except on a failure, whose stderr is only the
|
|
614
|
-
pass `--timing`:
|
|
620
|
+
update, or after the machine the workspace ran on failed (0.8.1+), a resume can boot from the saved disk instead of
|
|
621
|
+
restoring memory (a cold boot), also when a command wakes the workspace; check `memory_restored`. The command still runs
|
|
622
|
+
with the files kept, and processes start fresh, as after a reboot: start dev servers and background jobs again. `shard`
|
|
623
|
+
then prints one line on stderr after the output (also with `--json`, except on a failure, whose stderr is only the
|
|
624
|
+
error document), whether or not you pass `--timing`:
|
|
615
625
|
|
|
616
626
|
```text
|
|
617
627
|
note: workspace 01a0e5a8-3edd-74ba-b489-d62b8925e342 resumed from its saved disk; running processes were restarted (runtime_changed)
|
|
@@ -621,6 +631,12 @@ note: workspace 01a0e5a8-3edd-74ba-b489-d62b8925e342 resumed from its saved disk
|
|
|
621
631
|
`cold_boot`, `memory_restored` false, `cold_boot_reason`), and `--timing` shows `resume from cold_boot: processes
|
|
622
632
|
restarted (runtime_changed)`. The same applies to `operations wait` of such a resume.
|
|
623
633
|
|
|
634
|
+
**Workspaces recover by themselves (0.8.1+).** When the machine a workspace runs on fails, the workspace is suspended,
|
|
635
|
+
and the next command on it resumes it; there is nothing extra to run. From its own disk, the note above ends in
|
|
636
|
+
`(the machine it ran on failed)`; from its newest checkpoint, the note names the checkpoint and the time its state is
|
|
637
|
+
from (`state as of <time>`). `--json` carries `result.host_lost` (`detected_at`, `restored_from` `disk` or
|
|
638
|
+
`checkpoint`, `restored_checkpoint_id`, `state_as_of`), and `--timing` adds `host_lost` to `server`.
|
|
639
|
+
|
|
624
640
|
## Suspend when idle (0.5.1+)
|
|
625
641
|
|
|
626
642
|
A running workspace is billed while it is awake. When an agent's turn ends, ask for a suspend once the workspace has
|
|
@@ -644,19 +660,40 @@ shard ws suspend acme/demo --cancel-when-idle
|
|
|
644
660
|
|
|
645
661
|
## Elastic memory (0.8.0+)
|
|
646
662
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
instruction
|
|
663
|
+
A workspace can hold only the memory its commands use and grow up to `--memory-mib` when they need more; billing counts
|
|
664
|
+
the memory it holds. It idles at `--memory-held` (default 1024 MiB). Package installs, test runners, compilers and
|
|
665
|
+
bundlers get their memory before they start, so they see what they need from their first instruction; everything else
|
|
666
|
+
starts at once and the workspace grows while it runs.
|
|
650
667
|
|
|
651
668
|
```sh
|
|
652
669
|
shard ws open acme/demo --template python-node-browser --memory-mib 8192 --allocation-mode elastic
|
|
653
670
|
shard ws get acme/demo # memory elastic: promised 8192 MiB, held 1024 MiB, plugged +3072 MiB
|
|
654
671
|
shard ws exec acme/demo -v -- npm test # memory grow delivered 1024 -> 4096 MiB in 175 ms
|
|
672
|
+
shard ws exec acme/demo --resource-hint heavy -- python3 train.py # 0.9.0+: memory first, then the command
|
|
655
673
|
```
|
|
656
674
|
|
|
657
|
-
|
|
675
|
+
`--allocation-mode fixed` gives a static allocation of `--memory-mib`. `--resource-hint` (0.9.0+) is `auto` (the
|
|
676
|
+
default: decided from the command), `heavy` or `light`. `--json` output carries `memory` on the workspace and
|
|
658
677
|
`memory_grow` on a command that grew it.
|
|
659
678
|
|
|
679
|
+
## Resize a workspace (0.9.0+)
|
|
680
|
+
|
|
681
|
+
Change the memory, CPU and disk of any workspace, running or suspended, fixed or elastic, without a restart:
|
|
682
|
+
|
|
683
|
+
```sh
|
|
684
|
+
$ shard ws resize acme/demo --memory 6144 --disk 20
|
|
685
|
+
Resized acme/demo (0f6c…), running:
|
|
686
|
+
memory 2048 → 6144 MiB now
|
|
687
|
+
disk 10 → 20 GiB now
|
|
688
|
+
|
|
689
|
+
$ shard ws resize acme/demo --elastic --held 1024 # switch modes live
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Memory changes live, and disks grow online. A suspended workspace gets its new size when it resumes, before its first
|
|
693
|
+
call. CPU changes live within the vCPUs the workspace booted with; a larger CPU size applies at the next start. Each
|
|
694
|
+
resource says when it applies (`now`, `on resume` or `next start`), and the new caps are stored, so every later start
|
|
695
|
+
uses them. `--json` prints the result with `applies_at` per resource and the stored `caps`.
|
|
696
|
+
|
|
660
697
|
## Burst execution (0.8.0+)
|
|
661
698
|
|
|
662
699
|
Run one heavy command, such as a cold build or a full test suite, on a larger VM without resizing the workspace:
|
|
@@ -689,6 +726,31 @@ shard ws exec-cancel acme/demo build-1 --grace 10s
|
|
|
689
726
|
code and output.
|
|
690
727
|
- `exec-cancel` sends SIGTERM, then SIGKILL after `--grace` (default 5s, at most 60s).
|
|
691
728
|
|
|
729
|
+
## Inbound ports (0.9.0+)
|
|
730
|
+
|
|
731
|
+
Serve a port of a workspace at its own HTTPS URL: a preview of the app your agent is building, a webhook receiver, an
|
|
732
|
+
OAuth redirect, a live view. A request wakes a parked or suspended workspace and is answered once it runs. Every port
|
|
733
|
+
is private: a request carries a port token, a browser link's session, or arrives on the port's callback URL.
|
|
734
|
+
|
|
735
|
+
```sh
|
|
736
|
+
shard ws exec-start acme/demo -- python3 -m http.server 3000 --bind 0.0.0.0
|
|
737
|
+
shard ws port expose acme/demo 3000 # https://3000-<handle>.shardflux.app
|
|
738
|
+
curl -H "Authorization: Bearer $(shard ws port token acme/demo 3000)" https://3000-<handle>.shardflux.app/
|
|
739
|
+
shard ws port link acme/demo 3000 --path /dashboard # open it in a browser
|
|
740
|
+
shard ws port callback acme/demo 3000 # register <url>webhooks/github with GitHub
|
|
741
|
+
shard ws port close acme/demo 3000
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
- The server inside the workspace listens on `0.0.0.0`, not only `127.0.0.1`.
|
|
745
|
+
- `expose` is idempotent: an exposed port keeps its URL, tokens, links and callback URL. `port ls` lists them.
|
|
746
|
+
- `token` prints the token alone on stdout (`--ttl` 1m to 24h, default 1h). Send it as `Authorization: Bearer
|
|
747
|
+
<token>`, or as `X-Shardflux-Token` when the app reads `Authorization` itself.
|
|
748
|
+
- `link` prints a URL that starts a browser session for the port (`--ttl` 1m to 7d, default 24h) and lands on
|
|
749
|
+
`--path`. Anyone with the link can open the port until it expires.
|
|
750
|
+
- `callback` prints the callback URL once; a request to `<url><rest>` reaches `/<rest>` without a token. Running it
|
|
751
|
+
again replaces the URL; `--revoke` removes it.
|
|
752
|
+
- `close` stops the port's tokens, links and callback URL at once.
|
|
753
|
+
|
|
692
754
|
## Start deadlines
|
|
693
755
|
|
|
694
756
|
A queued open, resume or fork (`capacity_pending`) has a deadline 15 minutes after it was created.
|
|
@@ -778,7 +840,7 @@ With `--json`, stdout carries the timing as a `timing` field of the command's JS
|
|
|
778
840
|
`workspace_id`, `operation_id`, `total_ms`, `phases` (`phase`, `reason`, `operation_id`, `start_ms`, `duration_ms`),
|
|
779
841
|
`retries`, `server` (`queued_ms`, `run_ms`, `total_ms`, `start_path`, `resume_path`, `memory_restored` and
|
|
780
842
|
`cold_boot_reason` (0.6.0+; `memory_restored` is `false` when a resume booted the saved disk, `null` when the API did
|
|
781
|
-
not say), `boot_to_ready_ms`, ...) and `outside_server_ms`. It is `null` when the command made no lifecycle call. On a failure the error document on stderr
|
|
843
|
+
not say), `host_lost` (0.8.1+), `boot_to_ready_ms`, ...) and `outside_server_ms`. It is `null` when the command made no lifecycle call. On a failure the error document on stderr
|
|
782
844
|
carries it: `{"error": {...}, "timing": {...}}`.
|
|
783
845
|
|
|
784
846
|
For live progress events, use the SDK's `onProgress`.
|
|
@@ -827,6 +889,10 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
827
889
|
- `secrets create`/`get`/`update` print the secret's metadata; `list`, `versions` and `access-log`
|
|
828
890
|
print `{data, next_cursor}`; `rotate` prints `{secret, version}`; `delete` prints
|
|
829
891
|
`{deleted, id, name, scope}`; `workspaces secrets` prints `{workspace_id, names, secrets}`.
|
|
892
|
+
- `workspaces port expose` prints the port `{port, url, created_at, callback}`; `port ls` prints `{ports}`;
|
|
893
|
+
`port token` prints `{token, expires_at, url}` (`jq -r .token`); `port link` prints `{url, expires_at}`;
|
|
894
|
+
`port callback` prints `{url, created_at}`; `port close` prints `{workspace_id, port, closed}` and
|
|
895
|
+
`port callback --revoke` prints `{workspace_id, port, revoked}` (0.9.0+).
|
|
830
896
|
- Account commands (0.5.0+) print the API's objects too: lists are `{data, next_cursor}` (with
|
|
831
897
|
`default_organization_id`, `default_project_id` or `saved_api_key_id` where a default applies);
|
|
832
898
|
`auth login`/`auth mfa` print `{status, user, session: {expires_at, credentials_file}, organizations,
|
|
@@ -911,3 +977,7 @@ shard ws exec-input qm/project-42 qm-worker-1 --offset 5 --close
|
|
|
911
977
|
```
|
|
912
978
|
|
|
913
979
|
Use the byte offset in each acknowledgement, including partial writes. `ws resume` can recover a failed workspace with its existing ID and disk. A completed delete frees the key; old IDs remain deleted. Protocol-error JSON includes `source`; HTTP 404 alone does not mean a workspace was deleted.
|
|
980
|
+
|
|
981
|
+
### Prepare an imminent command (0.9.0+)
|
|
982
|
+
|
|
983
|
+
Call `shard ws hint <workspace>` ahead of a tool call to stage a parked workspace while the input is prepared. It returns without waiting for restore. Resume a suspended workspace first. The hint is explicit; ordinary commands add no early hint.
|
package/dist/account-args.js
CHANGED
|
@@ -40,6 +40,30 @@ const VOLUME_WAIT = {
|
|
|
40
40
|
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 5m); the operation continues server side.' },
|
|
41
41
|
};
|
|
42
42
|
export const ACCOUNT_COMMANDS = [
|
|
43
|
+
// ---- agent signup (0.9.0+) -----------------------------------------------------------------------------
|
|
44
|
+
{
|
|
45
|
+
path: ['signup'],
|
|
46
|
+
summary: 'Create your account and get ready to run workspaces in one command: no browser, no password',
|
|
47
|
+
description: 'Signs up with the Codex login on this machine (ChatGPT): your email is verified by it, so the account starts with its trial, and an account that already uses that ChatGPT identity or email is signed in instead. Without a Codex login, pass --email: the account works at once on the Free plan, and "shard verify" starts the trial. Then it runs "shard setup" (organization, project and an API key, saved) unless --no-setup. Two-factor accounts: pass --code or type it at the prompt.',
|
|
48
|
+
positionals: [],
|
|
49
|
+
options: {
|
|
50
|
+
email: { type: 'string', value: '<email>', description: 'Your email address, used when there is no Codex login (or with --no-codex).' },
|
|
51
|
+
name: { type: 'string', value: '<display name>', description: 'Your name as others see it (setup names the organization after it).' },
|
|
52
|
+
codex: { type: 'boolean', description: 'Use the Codex login on this machine (default; --no-codex signs up with --email).' },
|
|
53
|
+
setup: { type: 'boolean', description: 'Run shard setup afterwards (default; --no-setup skips it).' },
|
|
54
|
+
tool: { type: 'string', multiple: true, value: '<tool>', description: 'Workspace tool the saved API key may use (repeatable; exec, files, pty, process, git, browser). Default: all.' },
|
|
55
|
+
...SECOND_FACTOR,
|
|
56
|
+
},
|
|
57
|
+
examples: ['shard signup', 'shard signup --email you@example.com', 'shard signup --no-codex --email you@example.com --name "Ada Lovelace"'],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: ['verify'],
|
|
61
|
+
summary: 'Verify your email with a 6-digit code: emails the code, then confirm it',
|
|
62
|
+
description: 'Without a code it emails one (valid 15 minutes) and, on a terminal, asks for it; otherwise pass it next: "shard verify <code>". Nothing is signed out and saved API keys keep working; an account from "shard signup" gets its trial.',
|
|
63
|
+
positionals: [{ name: 'code', required: false }],
|
|
64
|
+
options: {},
|
|
65
|
+
examples: ['shard verify', 'shard verify 123456'],
|
|
66
|
+
},
|
|
43
67
|
// ---- sign-in (auth) ------------------------------------------------------------------------------------
|
|
44
68
|
{
|
|
45
69
|
path: ['auth', 'register'],
|
|
@@ -66,8 +90,12 @@ export const ACCOUNT_COMMANDS = [
|
|
|
66
90
|
summary: 'Sign in: saves a session for this API in the credentials file (the password is read from standard input or a prompt)',
|
|
67
91
|
description: 'Two-factor accounts: pass --code (or --recovery-code), or type the code at the prompt; without either the pending sign-in is saved and "shard auth mfa --code <code>" completes it within 10 minutes (exit 4 until then). Prints who you are, your organizations and the next step. A session lasts 30 days of use (90 at most). SHARDFLUX_SESSION_TOKEN, when set, is used instead of the saved session.',
|
|
68
92
|
positionals: [],
|
|
69
|
-
options: {
|
|
70
|
-
|
|
93
|
+
options: {
|
|
94
|
+
email: { type: 'string', value: '<email>', description: 'Your email address (required without --codex).' },
|
|
95
|
+
codex: { type: 'boolean', description: 'Sign in with the Codex login on this machine instead of a password (0.9.0+; creates the account when there is none).' },
|
|
96
|
+
...SECOND_FACTOR,
|
|
97
|
+
},
|
|
98
|
+
examples: [`printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com`, `printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com --code 123456`, 'shard auth login --codex'],
|
|
71
99
|
},
|
|
72
100
|
{
|
|
73
101
|
path: ['auth', 'mfa'],
|
|
@@ -98,9 +126,9 @@ export const ACCOUNT_COMMANDS = [
|
|
|
98
126
|
{
|
|
99
127
|
path: ['auth', 'step-up'],
|
|
100
128
|
summary: 'Confirm your password (and code) for sensitive actions: valid 10 minutes',
|
|
101
|
-
description: 'Needed for two-factor changes, an email change, data exports and deletions. On a terminal "shard" asks by itself when the API requires it; scripts run this first. The password comes from standard input or a prompt;
|
|
129
|
+
description: 'Needed for two-factor changes, an email change, data exports and deletions. On a terminal "shard" asks by itself when the API requires it; scripts run this first. The password comes from standard input or a prompt; with --codex (0.9.0+) the Codex login linked to the account confirms it instead. Two-factor accounts add --code (or type it at the prompt).',
|
|
102
130
|
positionals: [],
|
|
103
|
-
options: { ...SECOND_FACTOR },
|
|
131
|
+
options: { codex: { type: 'boolean', description: 'Confirm with the Codex login on this machine instead of the password (accounts it signs in to).' }, ...SECOND_FACTOR },
|
|
104
132
|
examples: [`printf '%s\\n' "$PASSWORD" | shard auth step-up`, `printf '%s\\n' "$PASSWORD" | shard auth step-up --code 123456`],
|
|
105
133
|
},
|
|
106
134
|
{
|