@shardflux/cli 0.8.1 → 0.10.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 CHANGED
@@ -3,10 +3,55 @@
3
3
  Every command and option the README shows is available from the version named here. Breaking changes ship in minor
4
4
  releases and are marked **Breaking**.
5
5
 
6
+ ## 0.10.0 (not yet published)
7
+
8
+ Needs `@shardflux/sdk` 0.15.0 or later.
9
+
10
+ ### One command per workspace
11
+
12
+ Additive (contracts §46), with `@shardflux/sdk` 0.15.0.
13
+
14
+ - `shard exec <key> --template <slug> -- <cmd>`: `--template` on `ws exec` and `ws exec-start` opens the key first,
15
+ so the workspace is created on first use (stderr: `created workspace <key> (template <slug>)`) and resumed if it is
16
+ suspended. Without `--template` the key must have a workspace, as before.
17
+ - `shard exec` is a shortcut for `shard workspaces exec`.
18
+ - `--template default` opens the platform default template (`python-node-browser`).
19
+
20
+ ### Computer use
21
+
22
+ - `shard ws computer status|screenshot|act|start|stop|stream <key>`: the workspace desktop (contracts §45).
23
+ `screenshot --out F` writes the image; `act <json|->` runs a batch of computer toolset actions, prints one line per
24
+ action and exits 1 when one failed (`--screenshot F` saves the screen after it; `--json` prints the full result);
25
+ `stream [--interactive] [--ttl D]` prints a private link to watch it, `stream --stop` ends every view.
26
+ - One command per mouse and keyboard action: `shard ws computer click [x y] [--button] [--count 2|3] [--mods]`,
27
+ `type <text|->`, `key <keys> [--repeat]`, `scroll <dir> [x y] [--amount]`, `move`, `drag`, `mouse-down`, `mouse-up`,
28
+ `hold-key`, `zoom <x0 y0 x1 y1> [--out F]`, `cursor`, `wait`. Every input command and `act` take `--screenshot F`,
29
+ `--settle MS` and `--format jpeg --quality N`; `screenshot` takes `--quality`.
30
+ - `shard ws computer enable|disable|inherit <key>` and `shard ws open --computer-use on|off|inherit`: the workspace's
31
+ computer use switch; `shard templates computer-use <slug> on|off`: the switch for every workspace of a template.
32
+
33
+ ## 0.9.0 — 2026-10-04 (not yet published)
34
+
35
+ - `shard ws hint <workspace>` explicitly stages a parked workspace ahead of a VM tool call, without waiting for restore or resuming a suspended workspace.
36
+
6
37
  ## 0.9.0 (not yet published)
7
38
 
8
39
  Needs `@shardflux/sdk` 0.14.0 or later.
9
40
 
41
+ ### Sign up from an agent
42
+
43
+ - `shard signup [--email <email>] [--name <name>] [--no-codex] [--no-setup] [--tool <tool>...]`: creates the account,
44
+ signs in and runs `shard setup` in one command, with no browser and no password. With the Codex CLI signed in with
45
+ ChatGPT on the machine, Codex refreshes its login (`codex app-server`) and the ID token from `$CODEX_HOME/auth.json`
46
+ verifies the email (the account starts with its trial; an existing account of that identity or email is signed
47
+ in). Without one, `--email` creates an account that works at once on the Free plan. `--json`: `{created, access,
48
+ method, user, setup}`.
49
+ - `shard verify [<code>]`: emails a 6-digit verification code (and asks for it on a terminal), or confirms one.
50
+ Nothing is signed out; an account from `shard signup` gets its trial.
51
+ - `shard auth login --codex` signs in with the Codex login; `shard auth step-up --codex` confirms a sensitive action
52
+ with it instead of the password.
53
+ - After signing in with an unverified email, the next step printed is `shard verify`.
54
+
10
55
  ### Workspaces working at once
11
56
 
12
57
  - A command whose workspace call finds every workspace the plan allows to work at once busy is sent again after
@@ -21,6 +66,55 @@ Needs `@shardflux/sdk` 0.14.0 or later.
21
66
  - `shard usage` says `retained state used up: new workspaces and forks are refused (...)` when the Retained state
22
67
  allowance's `cap_state` is `storage_blocked` (`enforcement` `storage_block`).
23
68
 
69
+ ### Inbound ports
70
+
71
+ Additive. Serve a port of a processful workspace at its own private HTTPS URL; a request wakes a parked or suspended
72
+ workspace.
73
+
74
+ - `shard ws port expose <id|key> <port>`: the URL and how requests get in (`--json`: the API's port object
75
+ `{port, url, created_at, callback}`). Idempotent.
76
+ - `shard ws port ls <id|key>`: port, URL, exposure time and callback URL (`--json`: `{ports}`).
77
+ - `shard ws port close <id|key> <port>` (`--json`: `{workspace_id, port, closed}`).
78
+ - `shard ws port token <id|key> <port> [--ttl 1m..24h]`: the port token alone on stdout, how to send it on stderr
79
+ (`--json`: `{token, expires_at, url}`).
80
+ - `shard ws port link <id|key> <port> [--ttl 1m..7d] [--path /...]`: a URL that opens the port in a browser
81
+ (`--json`: `{url, expires_at}`).
82
+ - `shard ws port callback <id|key> <port> [--revoke]`: the port's callback URL for webhooks and OAuth redirects, shown
83
+ once (`--json`: `{url, created_at}`; with `--revoke`: `{workspace_id, port, revoked}`).
84
+ - `ports` is an alias of `port`. A port outside 1-65535, a `--ttl` out of range or not in whole seconds, and a
85
+ `--path` without a leading `/` exit 2 before any request; a file-first workspace is refused before any port
86
+ request. Hints for `port_not_exposed` (expose it first) and `port_limit` (close a port first).
87
+
88
+ ### Memory across platform upgrades
89
+
90
+ - The cold-boot note and `--json --timing` name the new reason `runtime_retired` (the runtime the workspace was
91
+ suspended on was retired after an announced window): `note: workspace <id> resumed from its saved disk; running
92
+ processes were restarted (runtime_retired)`.
93
+
94
+ ### Resize a workspace
95
+
96
+ - `shard ws resize <id|key> [--memory MIB] [--held MIB] [--fixed|--elastic] [--cpu MILLIS] [--disk GIB]
97
+ [--timeout D] [--timing]`: changes the caps of a running, suspended or stopped workspace without a restart
98
+ (`PATCH /v1/workspaces/{id}/caps` through `workspace.resize()`), and waits until the resize has finished. It prints
99
+ per resource the change and when it applies, e.g. `memory 2048 → 6144 MiB now` or `memory 4096 → 8192 MiB
100
+ elastic, held 1024 MiB on resume`, with what was clamped to a limit or applied in part. `--json` prints the result
101
+ (`workspace_id`, `operation_id`, `state`, the `memory` / `cpu` / `disk` named, `caps`, and `operation` when one was
102
+ waited for). No resource flag, `--fixed` with `--elastic`, or `--held` with `--fixed` are usage errors (exit 2) with
103
+ no request. Ctrl-C names the operation, which continues server side.
104
+
105
+ ### Memory hint for commands
106
+
107
+ - `shard ws exec --resource-hint auto|light|heavy` and `shard ws exec-start --resource-hint …` send `resource_hint`
108
+ with the exec start: `heavy` gives an elastic workspace its memory before the command starts, `light` starts it at
109
+ once, `auto` (the default) decides from the command. Without the flag nothing is sent. On a file-first workspace
110
+ `--resource-hint` is a usage error (exit 2).
111
+
112
+ ### Elastic memory
113
+
114
+ - `--memory-held` on `shard ws open` / `shard ws fork` no longer needs `--allocation-mode elastic`: without a mode the
115
+ API decides (with elastic-by-default enabled for the organization a new workspace is elastic; otherwise
116
+ `requires_elastic`). With `--allocation-mode fixed` it stays a usage error.
117
+
24
118
  ## 0.8.1 (not yet published)
25
119
 
26
120
  Needs `@shardflux/sdk` 0.13.1 or later.
package/README.md CHANGED
@@ -37,16 +37,12 @@ Or run it without installing: `npx @shardflux/cli --help`. The unscoped
37
37
 
38
38
  ## Quick start
39
39
 
40
- No account yet? Everything happens in the terminal; a person only opens the emailed link (or hands
41
- it to `shard`) and, for a paid plan, pays in Stripe Checkout.
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
- printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com --name "Ada Lovelace"
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
48
- shard workspaces open acme/demo --template python-node-browser
49
- shard ws exec acme/demo -- python3 -c 'print(40 + 2)'
44
+ shard signup # or: shard signup --email you@example.com
45
+ shard exec acme/demo --template default -- python3 -c 'print(40 + 2)' # created on first use
50
46
  shard files write acme/demo /home/user/notes.txt --from notes.txt
51
47
  shard files read acme/demo /home/user/notes.txt
52
48
  shard ws suspend acme/demo --wait
@@ -111,16 +107,17 @@ npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo
111
107
  `shard` is built to be driven by an agent: no browser, no prompts when standard input is not a
112
108
  terminal, `--json` on every command, secrets only on standard input, and documented exit codes.
113
109
 
114
- 1. **Account:** `printf '%s\n' "$PASSWORD" | shard auth register --email <email>` (answers the same
115
- whether or not the address exists). Ask the person for the link in the verification email and run
116
- `shard auth verify-email '<link>'` (the whole link, quoted).
117
- 2. **Sign in:** `printf '%s\n' "$PASSWORD" | shard auth login --email <email> --json`. With two-factor
118
- authentication it exits 4 with `mfa_required`: ask the person for the code, then
119
- `shard auth mfa --code <code>` (within 10 minutes), or pass `--code` to `login`.
120
- 3. **Set up:** `shard setup --json`: an organization, a project and an API key with every workspace
121
- tool, saved in the credentials file. Idempotent: run it again any time.
122
- 4. **Work:** `shard ws open <key> --template <slug> --json`, `shard ws exec <key> -- <cmd>`,
123
- `shard files ...` use the saved key.
110
+ 1. **Account (0.9.0+):** `shard signup --json`. With the Codex CLI signed in with ChatGPT on the
111
+ machine it signs up (or signs in) verified by that login; otherwise pass `--email <email>` and the
112
+ account works at once on the Free plan. It then runs `shard setup`: an organization, a project and
113
+ an API key with every workspace tool, saved in the credentials file. With two-factor authentication
114
+ it exits 4 with `mfa_required`: ask the person for the code, then `shard auth mfa --code <code>`.
115
+ 2. **Verify the email** (an `--email` signup, to start the trial): `shard verify --json` emails a
116
+ 6-digit code; ask the person for it, then `shard verify <code>`. Nothing is signed out.
117
+ 3. **Set up again any time:** `shard setup --json` is idempotent.
118
+ 4. **Work:** `shard exec <key> --template default -- <cmd>` creates the workspace on first use and runs the command
119
+ (0.10.0+); later commands reuse it. `shard ws open <key> --template <slug> --json`, `shard files ...` use the saved
120
+ key too.
124
121
  5. **A paid plan:** `shard billing plans`, then `shard billing upgrade <plan> --json` prints the Stripe
125
122
  Checkout URL; give it to the person, then `shard billing upgrade <plan> --wait` (or
126
123
  `shard billing status`) until it is active. The API activates a plan only when Stripe confirms the
@@ -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 stays human: reading the verification email, entering a card in Stripe Checkout, and the
136
- authenticator app's codes.
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 auth verify-email`).
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> [--template S] [--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. `shard exec` is the same command (0.10.0+). `--template <slug>` (0.10.0+) opens the key first: the workspace is created on first use (stderr says so) and resumed if suspended; without it the key must have a workspace. 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> [--template S] [--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,9 @@ 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+). |
411
+ | `workspaces computer status\|screenshot\|act\|start\|stop\|stream <id\|key>`, `workspaces computer click\|type\|key\|scroll\|move\|drag\|mouse-down\|mouse-up\|hold-key\|zoom\|cursor\|wait <id\|key> ...`, `workspaces computer enable\|disable\|inherit <id\|key>`, `templates computer-use <slug> on\|off` | The workspace desktop for computer use: its state, a screenshot (`--out F`), one command per mouse and keyboard action, a batch of actions (a JSON array), a private link to watch or use it (`stream [--interactive] [--ttl D]`, `--stop`), and the switch for a workspace or every workspace of a template (0.10.0+; see [Computer use](#computer-use-0100)). |
401
412
  | `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
413
  | `secrets list [--org ID] [--include-deleted] [--all]`, `secrets get <id\|NAME>` | Secret metadata. Values are never shown. |
403
414
  | `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. |
@@ -650,19 +661,40 @@ shard ws suspend acme/demo --cancel-when-idle
650
661
 
651
662
  ## Elastic memory (0.8.0+)
652
663
 
653
- Give a workspace a large memory promise while it holds only what it uses. It idles at `--memory-held` (default
654
- 1024 MiB) and grows before each command starts, so builds and test runners see the memory they need from their first
655
- instruction.
664
+ A workspace can hold only the memory its commands use and grow up to `--memory-mib` when they need more; billing counts
665
+ the memory it holds. It idles at `--memory-held` (default 1024 MiB). Package installs, test runners, compilers and
666
+ bundlers get their memory before they start, so they see what they need from their first instruction; everything else
667
+ starts at once and the workspace grows while it runs.
656
668
 
657
669
  ```sh
658
670
  shard ws open acme/demo --template python-node-browser --memory-mib 8192 --allocation-mode elastic
659
671
  shard ws get acme/demo # memory elastic: promised 8192 MiB, held 1024 MiB, plugged +3072 MiB
660
672
  shard ws exec acme/demo -v -- npm test # memory grow delivered 1024 -> 4096 MiB in 175 ms
673
+ shard ws exec acme/demo --resource-hint heavy -- python3 train.py # 0.9.0+: memory first, then the command
661
674
  ```
662
675
 
663
- The layout takes effect at the workspace's next start. `--json` output carries `memory` on the workspace and
676
+ `--allocation-mode fixed` gives a static allocation of `--memory-mib`. `--resource-hint` (0.9.0+) is `auto` (the
677
+ default: decided from the command), `heavy` or `light`. `--json` output carries `memory` on the workspace and
664
678
  `memory_grow` on a command that grew it.
665
679
 
680
+ ## Resize a workspace (0.9.0+)
681
+
682
+ Change the memory, CPU and disk of any workspace, running or suspended, fixed or elastic, without a restart:
683
+
684
+ ```sh
685
+ $ shard ws resize acme/demo --memory 6144 --disk 20
686
+ Resized acme/demo (0f6c…), running:
687
+ memory 2048 → 6144 MiB now
688
+ disk 10 → 20 GiB now
689
+
690
+ $ shard ws resize acme/demo --elastic --held 1024 # switch modes live
691
+ ```
692
+
693
+ Memory changes live, and disks grow online. A suspended workspace gets its new size when it resumes, before its first
694
+ call. CPU changes live within the vCPUs the workspace booted with; a larger CPU size applies at the next start. Each
695
+ resource says when it applies (`now`, `on resume` or `next start`), and the new caps are stored, so every later start
696
+ uses them. `--json` prints the result with `applies_at` per resource and the stored `caps`.
697
+
666
698
  ## Burst execution (0.8.0+)
667
699
 
668
700
  Run one heavy command, such as a cold build or a full test suite, on a larger VM without resizing the workspace:
@@ -695,6 +727,71 @@ shard ws exec-cancel acme/demo build-1 --grace 10s
695
727
  code and output.
696
728
  - `exec-cancel` sends SIGTERM, then SIGKILL after `--grace` (default 5s, at most 60s).
697
729
 
730
+ ## Inbound ports (0.9.0+)
731
+
732
+ Serve a port of a workspace at its own HTTPS URL: a preview of the app your agent is building, a webhook receiver, an
733
+ OAuth redirect, a live view. A request wakes a parked or suspended workspace and is answered once it runs. Every port
734
+ is private: a request carries a port token, a browser link's session, or arrives on the port's callback URL.
735
+
736
+ ```sh
737
+ shard ws exec-start acme/demo -- python3 -m http.server 3000 --bind 0.0.0.0
738
+ shard ws port expose acme/demo 3000 # https://3000-<handle>.shardflux.app
739
+ curl -H "Authorization: Bearer $(shard ws port token acme/demo 3000)" https://3000-<handle>.shardflux.app/
740
+ shard ws port link acme/demo 3000 --path /dashboard # open it in a browser
741
+ shard ws port callback acme/demo 3000 # register <url>webhooks/github with GitHub
742
+ shard ws port close acme/demo 3000
743
+ ```
744
+
745
+ - The server inside the workspace listens on `0.0.0.0`, not only `127.0.0.1`.
746
+ - `expose` is idempotent: an exposed port keeps its URL, tokens, links and callback URL. `port ls` lists them.
747
+ - `token` prints the token alone on stdout (`--ttl` 1m to 24h, default 1h). Send it as `Authorization: Bearer
748
+ <token>`, or as `X-Shardflux-Token` when the app reads `Authorization` itself.
749
+ - `link` prints a URL that starts a browser session for the port (`--ttl` 1m to 7d, default 24h) and lands on
750
+ `--path`. Anyone with the link can open the port until it expires.
751
+ - `callback` prints the callback URL once; a request to `<url><rest>` reaches `/<rest>` without a token. Running it
752
+ again replaces the URL; `--revoke` removes it.
753
+ - `close` stops the port's tokens, links and callback URL at once.
754
+
755
+ ## Computer use (0.10.0+)
756
+
757
+ A desktop in the workspace for agents that operate GUI software: a screen, a mouse and a keyboard. Switch it on for a
758
+ workspace (or every workspace of your template); the platform starts the desktop on the first computer command.
759
+
760
+ ```sh
761
+ shard ws open acme/demo --template python-node-browser --computer-use on
762
+ shard ws exec-start acme/demo -- chromium https://example.com # opens on the desktop
763
+ shard ws computer screenshot acme/demo --out desktop.png
764
+ shard ws computer click acme/demo 640 400
765
+ shard ws computer type acme/demo "hello" --screenshot after.png
766
+ shard ws computer key acme/demo ctrl+s
767
+ open "$(shard ws computer stream acme/demo)" # watch it live
768
+ ```
769
+
770
+ | Command | Action |
771
+ | --- | --- |
772
+ | `click <ws> [x y] [--button left\|right\|middle] [--count 2\|3] [--mods keys]` | A click, double or triple click at a point or at the pointer |
773
+ | `type <ws> <text\|->` | Types text (Unicode; `-` reads standard input) |
774
+ | `key <ws> <keys> [--repeat n]` | Keys and combinations: `Return`, `ctrl+s`, `alt+Tab`, several separated by spaces |
775
+ | `scroll <ws> up\|down\|left\|right [x y] [--amount n] [--mods keys]` | Wheel clicks (default 3) |
776
+ | `move <ws> <x> <y>`, `drag <ws> <x1> <y1> <x2> <y2>` | Moves the pointer; drags with the left button |
777
+ | `mouse-down <ws>`, `mouse-up <ws>`, `hold-key <ws> <keys> <seconds>` | Holds the left button or keys |
778
+ | `zoom <ws> <x0> <y0> <x1> <y1> [--out F]` | Saves an enlarged view of a region |
779
+ | `cursor <ws>`, `wait <ws> <seconds>` | Prints the pointer position; waits |
780
+ | `act <ws> <json\|->` | Any of these in one batch: a JSON array of actions |
781
+
782
+ Every input command takes `--screenshot F` (a screenshot after it, written to `F`), `--settle MS` (the wait before
783
+ that screenshot, default 250) and `--format jpeg --quality N` for smaller images; `screenshot` and `zoom` take
784
+ `--format` and `--quality` too.
785
+
786
+ - The actions are Claude's computer toolset members with their parameters (`screenshot`, `zoom`, `left_click`,
787
+ `right_click`, `middle_click`, `double_click`, `triple_click`, `left_click_drag`, `mouse_move`, `left_mouse_down`,
788
+ `left_mouse_up`, `cursor_position`, `scroll`, `type`, `key`, `hold_key`, `wait`). Each command above and `act` print
789
+ one line per action and exit 1 when one failed (in a batch the rest are skipped).
790
+ - `stream` prints a link to the viewer (view only unless `--interactive`; `--ttl` 1m to 7d, default 24h); anyone with
791
+ it can watch until it expires. `stream --stop` ends every view.
792
+ - `ws computer enable|disable|inherit` sets the workspace's own switch (`inherit` follows the template);
793
+ `templates computer-use <slug> on|off` sets it for every workspace of one of your templates.
794
+
698
795
  ## Start deadlines
699
796
 
700
797
  A queued open, resume or fork (`capacity_pending`) has a deadline 15 minutes after it was created.
@@ -833,6 +930,10 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
833
930
  - `secrets create`/`get`/`update` print the secret's metadata; `list`, `versions` and `access-log`
834
931
  print `{data, next_cursor}`; `rotate` prints `{secret, version}`; `delete` prints
835
932
  `{deleted, id, name, scope}`; `workspaces secrets` prints `{workspace_id, names, secrets}`.
933
+ - `workspaces port expose` prints the port `{port, url, created_at, callback}`; `port ls` prints `{ports}`;
934
+ `port token` prints `{token, expires_at, url}` (`jq -r .token`); `port link` prints `{url, expires_at}`;
935
+ `port callback` prints `{url, created_at}`; `port close` prints `{workspace_id, port, closed}` and
936
+ `port callback --revoke` prints `{workspace_id, port, revoked}` (0.9.0+).
836
937
  - Account commands (0.5.0+) print the API's objects too: lists are `{data, next_cursor}` (with
837
938
  `default_organization_id`, `default_project_id` or `saved_api_key_id` where a default applies);
838
939
  `auth login`/`auth mfa` print `{status, user, session: {expires_at, credentials_file}, organizations,
@@ -917,3 +1018,7 @@ shard ws exec-input qm/project-42 qm-worker-1 --offset 5 --close
917
1018
  ```
918
1019
 
919
1020
  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.
1021
+
1022
+ ### Prepare an imminent command (0.9.0+)
1023
+
1024
+ 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.
@@ -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: { email: { type: 'string', value: '<email>', description: 'Your email address (required).' }, ...SECOND_FACTOR },
70
- 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`],
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; two-factor accounts add --code (or type it at the 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
  {