@shardflux/cli 0.3.0 → 0.4.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 +47 -1
- package/README.md +79 -7
- package/dist/args.d.ts +1 -1
- package/dist/args.js +124 -7
- package/dist/commands.js +330 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +18 -4
- package/dist/format.d.ts +19 -2
- package/dist/format.js +97 -7
- package/dist/main.js +3 -0
- package/dist/template-yaml.d.ts +7 -0
- package/dist/template-yaml.js +62 -0
- package/dist/timing.d.ts +29 -1
- package/dist/timing.js +96 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,53 @@
|
|
|
3
3
|
Every command and option the README shows is available from the version named here. Below 1.0, a minor release may
|
|
4
4
|
break compatibility; breaking changes are marked **Breaking**.
|
|
5
5
|
|
|
6
|
-
## 0.
|
|
6
|
+
## 0.4.0 (not yet published; npm `latest` is 0.3.1)
|
|
7
|
+
|
|
8
|
+
Needs `@shardflux/sdk` 0.7.0 or later. New dependency: `yaml` (template.yaml).
|
|
9
|
+
|
|
10
|
+
### Template editor (contracts §24)
|
|
11
|
+
|
|
12
|
+
- `shard templates build <file> [--slug] [--display-name] [--description] [--publish] [--acknowledge PATH]... [--wait]
|
|
13
|
+
[--timeout] [--timing]`: builds a template from template.yaml. Local `from` files and folders are uploaded (folders
|
|
14
|
+
as the reproducible tar the SDKs pack; bytes the organization has are not sent again), then the recipe v2 build is
|
|
15
|
+
created. Progress on stderr; `--wait` follows the build (exit 6 failed or canceled, 5 timeout); `--timing` prints
|
|
16
|
+
packing, uploads, the request, the wait and the build's queued and run time (a `timing` field with `--json`). The
|
|
17
|
+
slug defaults to the directory holding the file. The version stays unpublished unless `--publish`.
|
|
18
|
+
- `shard templates init --base <slug[@version]> [--out] [--force]`: a starter template.yaml.
|
|
19
|
+
- `shard templates export <slug>@<version> [--out]`: the template.yaml a version was built from (exit 3 for a version
|
|
20
|
+
without a recipe).
|
|
21
|
+
- `shard templates get <slug>`: versions, what open resolves to, the open version's settings.
|
|
22
|
+
- `shard templates test <slug>@<version> [--input NAME=value]...`: a test instance of a version, published or not.
|
|
23
|
+
- `shard templates builds list|get|log|cancel`.
|
|
24
|
+
- `shard templates languages --base <slug>@<version>` and `shard templates packages search <apt|pip|npm> <query>`.
|
|
25
|
+
- `shard workspaces open --input NAME=value` (repeatable) and `--input-file` (NAME=value lines or a JSON object);
|
|
26
|
+
`shard workspaces inputs <ws>`.
|
|
27
|
+
- `shard workspaces get` (and the ready output of `open`) shows the startup of the template's start commands and
|
|
28
|
+
services; a failed step shows its exit code, reason and output tail.
|
|
29
|
+
- A template file `shard` cannot read or pack is a usage error (exit 2); a storage refusal of an upload is
|
|
30
|
+
`upload_failed` (exit 1); a build wait that runs out is `timeout` (exit 5).
|
|
31
|
+
- `buildText` names a recipe v2 build's recipe sha256, its build network and denied hosts, and points at
|
|
32
|
+
`shard templates builds get`.
|
|
33
|
+
|
|
34
|
+
## 0.3.1 (2026-09-28)
|
|
35
|
+
|
|
36
|
+
Needs `@shardflux/sdk` 0.6.2 or later.
|
|
37
|
+
|
|
38
|
+
The API no longer lets a start (open, resume, fork) wait in `capacity_pending` forever: one that no host could admit
|
|
39
|
+
15 minutes after it began fails with `capacity_unavailable` (retryable; nothing was started, a suspended workspace
|
|
40
|
+
stays suspended).
|
|
41
|
+
|
|
42
|
+
- A failed operation's error has `retryable` (the operation error's own flag): `retryable=true|false` in the error line,
|
|
43
|
+
`"retryable"` in the `--json` error document, next to `details.error_code`. `details.reason` carries the operation
|
|
44
|
+
error's reason (for `capacity_unavailable`, why no host fit, e.g. `no_ready_hosts`), printed after the message.
|
|
45
|
+
- A retryable failure gets a hint to retry later. Exit codes are unchanged (6 for any failed operation); `shard` does
|
|
46
|
+
not retry.
|
|
47
|
+
- A `--timeout` that ends while the start waits for capacity names the time it gives up (the SDK's message).
|
|
48
|
+
- Help and README: `--timeout` and exit codes 5 and 6 describe the 15-minute limit.
|
|
49
|
+
- `shard workspaces get` names the origin of a template version test instance (`template <id> version <n>`, contracts
|
|
50
|
+
§24.6) instead of reading it as a draft state.
|
|
51
|
+
|
|
52
|
+
## 0.3.0 (2026-09-28)
|
|
7
53
|
|
|
8
54
|
Needs `@shardflux/sdk` 0.6.0 or later.
|
|
9
55
|
|
package/README.md
CHANGED
|
@@ -56,9 +56,10 @@ every run.
|
|
|
56
56
|
| `login`, `whoami` | Show the principal behind the key. |
|
|
57
57
|
| `version` | Print the CLI and SDK versions. |
|
|
58
58
|
| `usage [--org <id>]` | Usage summary for the current period: plan, meters and allowances. |
|
|
59
|
-
| `workspaces open <key> --template <slug> [--secret NAME]... [--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 unless you pass `--no-wait`. `--secret` binds secret names (replacing the binding of an existing workspace). `--lifetime session`: the workspace is discarded when the session ends (`workspaces close` or the idle timeout), and the key then opens a new workspace. |
|
|
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
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). |
|
|
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. |
|
|
62
|
+
| `workspaces inputs <id\|key>` | The workspace's text inputs (0.4.0+). |
|
|
62
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. |
|
|
63
64
|
| `files read <id\|key> <path> [--out F]` | Raw bytes to stdout, or to a local file. |
|
|
64
65
|
| `files write <id\|key> <path> [--from F\|-] [--append] [--no-parents]` | Atomic write. The default source is stdin. |
|
|
@@ -79,6 +80,14 @@ every run.
|
|
|
79
80
|
| `secrets versions <id\|NAME>`, `secrets delete <id\|NAME> --yes`, `secrets access-log <id\|NAME>` | Versions, deletion (also removes the name from every workspace binding), and the access log. |
|
|
80
81
|
| `operations list <id\|key> [--state S] [--kind K]`, `operations get <op>`, `operations wait <op> [--timeout T] [--timing]` | Operations. |
|
|
81
82
|
| `templates list [--owner platform\|organization] [--include-archived] [--all]` | Templates the project can open, with their open version, layouts, file list state and draft. |
|
|
83
|
+
| `templates get <slug> [--owner O] [--include-archived]` | One template (0.4.0+): its versions, what open resolves to, and the open version's settings (env, inputs, start commands, services, defaults). |
|
|
84
|
+
| `templates init --base <slug[@version]> [--out F] [--force]` | Write a starter `template.yaml` on a base (0.4.0+). |
|
|
85
|
+
| `templates build <file> [--slug S] [--display-name N] [--description T] [--publish] [--acknowledge PATH]... [--wait] [--timeout 30m] [--timing]` | Build a template from `template.yaml` (0.4.0+): local files and folders are uploaded, then the build is created. See [Build a template](#build-a-template-from-templateyaml). |
|
|
86
|
+
| `templates export <slug>@<version> [--out F] [--force]` | The `template.yaml` a version was built from, ready to build again (0.4.0+). |
|
|
87
|
+
| `templates test <slug>@<version> [--input NAME=value]... [--instance-key K] [caps] [--no-wait] [--timing]` | A test instance of a version, published or not: a session workspace that runs its start commands and services (0.4.0+). |
|
|
88
|
+
| `templates builds list [--template S] [--state S] [--all]`, `templates builds get\|log\|cancel <build-id>` | The organization's template builds, one build, its full log (`--out F`), or cancel it (0.4.0+). |
|
|
89
|
+
| `templates languages --base <slug>@<version>` | The languages and versions a base offers `build.languages` (0.4.0+). |
|
|
90
|
+
| `templates packages search <apt\|pip\|npm> <query> [--base slug@version] [--limit N]` | Package names for `build.packages` (0.4.0+). apt searches the base's index and needs `--base`. |
|
|
82
91
|
| `templates files <slug> <version> [path] [--stat] [--owner O] [--all] [--limit N]` | One directory of a version's file tree, or one entry with `--stat`. |
|
|
83
92
|
| `templates diff <slug> --from <version\|base> --to <version> [--prefix P] [--change K] [--all]` | Diff two versions: added, removed, changed, type_changed, metadata. The totals come first. |
|
|
84
93
|
| `templates draft open <slug> [--base slug@version] [caps] [--no-wait]` | Open the template's draft, a layered workspace you edit with `workspaces exec`/`files`. |
|
|
@@ -94,6 +103,51 @@ is a UUID. Otherwise, or if no workspace has that id, it is matched as an exact
|
|
|
94
103
|
and purpose, so sessions, drafts and test instances resolve too. The live workspace wins over tombstones that ended
|
|
95
104
|
sessions left with the same key.
|
|
96
105
|
|
|
106
|
+
## Build a template from template.yaml
|
|
107
|
+
|
|
108
|
+
(0.4.0+) A template is a recipe v2: a base, what the build adds (languages, packages, files, build steps) and the
|
|
109
|
+
settings a workspace gets when it opens (environment, inputs, start commands, services). `template.yaml` is that
|
|
110
|
+
document; a file entry may name a local `from` path (relative to the file), which `shard` uploads.
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
shard templates init --base ubuntu-24.04 # writes template.yaml on the base's current version
|
|
114
|
+
$EDITOR template.yaml
|
|
115
|
+
shard templates languages --base ubuntu-24.04@1 # what build.languages offers there
|
|
116
|
+
shard templates packages search apt ffmpeg --base ubuntu-24.04@1
|
|
117
|
+
shard templates build template.yaml --slug acme-dev --wait --timing
|
|
118
|
+
shard templates test acme-dev@1 --input PROJECT_NAME=demo
|
|
119
|
+
shard templates build template.yaml --slug acme-dev --publish --wait
|
|
120
|
+
shard ws open acme/main --template acme-dev --input PROJECT_NAME=acme
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
base: ubuntu-24.04@1
|
|
125
|
+
build:
|
|
126
|
+
languages: [{ id: python }]
|
|
127
|
+
packages: { apt: [jq], pip: { packages: [pandas==2.3.2] } }
|
|
128
|
+
files:
|
|
129
|
+
- { from: ./app, to: /home/user/app, owner: user } # a folder: uploaded as a tar
|
|
130
|
+
- { from: ./settings.toml, to: /home/user/.config/acme/settings.toml, owner: user, mode: "0600" }
|
|
131
|
+
steps:
|
|
132
|
+
- { name: install, run: pip install -e /home/user/app, user: user }
|
|
133
|
+
settings:
|
|
134
|
+
env: { APP_ENV: development }
|
|
135
|
+
inputs: { PROJECT_NAME: { kind: text, required: true } }
|
|
136
|
+
start: [{ name: seed, when: create, run: python -m app.seed, user: user, cwd: /home/user/app }]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- Folders are packed as a reproducible tar (the same bytes as the SDKs pack), so the same folder gives the same
|
|
140
|
+
upload and the same `recipe_sha256`. Bytes the organization already has are not sent again. Progress lines go to
|
|
141
|
+
stderr (`packed`, `uploaded` or `already uploaded`, then the build's states with `--wait`).
|
|
142
|
+
- The version stays unpublished unless you pass `--publish`: try it with `shard templates test <slug>@<version>`.
|
|
143
|
+
Without `--slug` the template slug is the name of the directory holding the file.
|
|
144
|
+
- `--wait` follows the build until it is registered or fails (exit 6 when it failed or was canceled, 5 when
|
|
145
|
+
`--timeout` ran out; the build continues). A build whose network refused a host lists it under `denied hosts`: add
|
|
146
|
+
it to `build.network.extra_hosts` and build again.
|
|
147
|
+
- `--timing` prints where the time went: packing, uploads, the request, the wait, and the build's queued and run time.
|
|
148
|
+
- A file `shard` cannot read or pack (a symlink that leaves the folder, a device) is refused before any request (exit
|
|
149
|
+
2). The API's refusals (`upload_missing`, `language_unavailable`, `invalid_settings`, ...) exit 2 with the reason.
|
|
150
|
+
|
|
97
151
|
## Suspended workspaces
|
|
98
152
|
|
|
99
153
|
`workspaces exec`, `files read|write|ls` and `workspaces changes` wake a suspended workspace: they
|
|
@@ -106,6 +160,21 @@ never runs twice, because the workspace refused it before doing anything.
|
|
|
106
160
|
is still queued after 120000 ms`). The operation continues server side:
|
|
107
161
|
`shard operations wait <id>` keeps waiting.
|
|
108
162
|
- A resume or open that fails exits 6.
|
|
163
|
+
|
|
164
|
+
## Starts that wait for capacity
|
|
165
|
+
|
|
166
|
+
An open, resume or fork that no host can admit yet waits in `capacity_pending` for at most 15
|
|
167
|
+
minutes. If `--timeout` ends first, the timeout error names the time it gives up. A start still
|
|
168
|
+
pending then fails with `capacity_unavailable`: nothing was started, and a suspended
|
|
169
|
+
workspace stays suspended. `shard` exits 6 as for any failed operation, and the error says
|
|
170
|
+
`retryable=true` (with `--json`, `"retryable": true`) with a hint to retry later:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
error: Operation 01a0df37-b3a9-7a14-a3e7-11395c349c39 (open) failed: capacity_unavailable (no_ready_hosts) [code=operation_failed operation_id=01a0df37-b3a9-7a14-a3e7-11395c349c39 retryable=true]
|
|
174
|
+
hint: no host could admit it before its deadline; nothing was started. Retry later.
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`shard` does not retry it for you. A definitive failure has `retryable=false`.
|
|
109
178
|
- `--no-wake` (or `SHARDFLUX_NO_WAKE=1`) turns this off. A workspace that is not running is then
|
|
110
179
|
refused at once (exit 1, `workspace_not_running`).
|
|
111
180
|
|
|
@@ -139,7 +208,8 @@ Here the time went to waiting for a host with capacity; the network and the VM s
|
|
|
139
208
|
|
|
140
209
|
Which call is timed:
|
|
141
210
|
|
|
142
|
-
- `workspaces open` and `templates draft open|test`: the open, including the wait unless `--no-wait`.
|
|
211
|
+
- `workspaces open`, `templates test` and `templates draft open|test`: the open, including the wait unless `--no-wait`.
|
|
212
|
+
- `templates build` (0.4.0+): packing, uploads, the request and the wait, then the build's own queued and run time.
|
|
143
213
|
- `workspaces suspend|resume|fork|delete|close|reset` and `templates draft state|discard`: with `--wait`, the request
|
|
144
214
|
and the whole wait; without it, the request.
|
|
145
215
|
- `operations wait`: the wait.
|
|
@@ -179,7 +249,7 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
179
249
|
|
|
180
250
|
- **Human (default):** aligned tables and key/value blocks on stdout. Errors go to stderr as
|
|
181
251
|
`error: <message> (<reason>) [code=... status=... request_id=... operation_id=...]`, with a
|
|
182
|
-
hint when there is one.
|
|
252
|
+
hint when there is one. A failed operation adds `retryable=true|false` (0.3.1+).
|
|
183
253
|
- **`--json`:** stdout carries the API's own objects:
|
|
184
254
|
- `workspaces open`/`get` print the workspace view; `list` prints `{data, next_cursor}`.
|
|
185
255
|
- Lifecycle commands print the operation; `fork` prints `{operation, workspace}`.
|
|
@@ -190,7 +260,9 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
190
260
|
print `{data, next_cursor}`; `rotate` prints `{secret, version}`; `delete` prints
|
|
191
261
|
`{deleted, id, name, scope}`; `workspaces secrets` prints `{workspace_id, names, secrets}`.
|
|
192
262
|
|
|
193
|
-
Errors go to stderr as `{"error": {code, message, status, request_id, ...}}`.
|
|
263
|
+
Errors go to stderr as `{"error": {code, message, status, request_id, ...}}`. A failed operation is
|
|
264
|
+
`{"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.
|
|
194
266
|
- **`--timing`:** the timing goes to stderr after the output. With `--json` it is a `timing` field instead
|
|
195
267
|
(see "Timing").
|
|
196
268
|
|
|
@@ -203,8 +275,8 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
203
275
|
| 2 | usage error: bad arguments, or input the API rejected as `validation_failed` |
|
|
204
276
|
| 3 | not found: workspace, operation or route |
|
|
205
277
|
| 4 | authentication or authorization: missing, malformed, revoked or insufficient key |
|
|
206
|
-
| 5 | timeout: waiting gave up (`--timeout`, or `--wake-timeout` while waking a workspace); the operation continues server side (`shard operations wait <id>`) |
|
|
207
|
-
| 6 | the awaited operation ended `failed` or `canceled` (also a resume or open that failed while waking a workspace) |
|
|
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`) |
|
|
208
280
|
| 130 | interrupted (Ctrl-C). Waits stop at once, and exec sends a cancel to the session. |
|
|
209
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. |
|
|
210
282
|
|
package/dist/args.d.ts
CHANGED
package/dist/args.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { parseArgs } from 'node:util';
|
|
12
12
|
import { UsageError } from "./errors.js";
|
|
13
|
-
export const CLI_VERSION = '0.
|
|
13
|
+
export const CLI_VERSION = '0.4.0';
|
|
14
14
|
export const GLOBAL_OPTIONS = {
|
|
15
15
|
'api-url': { type: 'string', value: '<url>', description: 'API base URL (default $SHARDFLUX_API_URL, else https://api.shardflux.dev).' },
|
|
16
16
|
json: { type: 'boolean', description: 'Print machine-readable JSON on stdout (errors as JSON on stderr).' },
|
|
@@ -38,7 +38,7 @@ const WAKE_TIMING = {
|
|
|
38
38
|
};
|
|
39
39
|
const WAIT = {
|
|
40
40
|
wait: { type: 'boolean', description: 'Wait until the operation finishes.' },
|
|
41
|
-
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (e.g. 90s, 5m; default 5m). The operation continues server side.' },
|
|
41
|
+
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (e.g. 90s, 5m; default 5m). The operation continues server side (a start waiting for capacity until its deadline, 15 minutes after it began; then it fails with capacity_unavailable).' },
|
|
42
42
|
...TIMING,
|
|
43
43
|
};
|
|
44
44
|
const REF = { name: 'id|key', required: true };
|
|
@@ -58,6 +58,17 @@ const SAVE = {
|
|
|
58
58
|
wait: { type: 'boolean', description: 'Wait until the build is published and registered (or fails).' },
|
|
59
59
|
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 30m). The build continues server side.' },
|
|
60
60
|
};
|
|
61
|
+
/** Open-time text inputs of a template version (0.4.0; contracts §24.3). */
|
|
62
|
+
const INPUTS = {
|
|
63
|
+
input: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
multiple: true,
|
|
66
|
+
value: '<NAME=value>',
|
|
67
|
+
description: 'Text input of the template version (repeatable): in the environment of every exec, terminal, start command and service. Replaces the inputs of an existing workspace; omit to leave them unchanged. Not for credentials: those are secret inputs (stored secrets).',
|
|
68
|
+
},
|
|
69
|
+
'input-file': { type: 'string', value: '<file>', description: 'Read text inputs from a file: NAME=value lines (# comments), or a JSON object. --input wins for a name in both.' },
|
|
70
|
+
};
|
|
71
|
+
const BUILD_ID = { name: 'build-id', required: true };
|
|
61
72
|
const LIFETIME_FILTER = {
|
|
62
73
|
lifetime: { type: 'string', value: '<persistent|session|any>', description: 'Lifetime filter (default persistent: sessions are hidden).' },
|
|
63
74
|
purpose: { type: 'string', value: '<standard|template_draft|template_test|any>', description: 'Purpose filter (default standard: template drafts and test instances are hidden).' },
|
|
@@ -109,14 +120,16 @@ export const COMMANDS = [
|
|
|
109
120
|
value: '<persistent|session>',
|
|
110
121
|
description: 'session: the workspace is discarded when the session ends ("shard ws close" or the idle timeout) and the key then opens a new one. Default: the template’s default, else persistent.',
|
|
111
122
|
},
|
|
123
|
+
...INPUTS,
|
|
112
124
|
...CAPS,
|
|
113
|
-
wait: { type: 'boolean', description: 'Wait until ready (default; --no-wait returns at once).' },
|
|
114
|
-
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 5m). The start continues server side.' },
|
|
125
|
+
wait: { type: 'boolean', description: 'Wait until ready (default; --no-wait returns at once). Ready includes the template’s start commands and services.' },
|
|
126
|
+
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 5m). The start continues server side; one waiting for capacity gives up 15 minutes after it began (capacity_unavailable, retryable).' },
|
|
115
127
|
...TIMING,
|
|
116
128
|
},
|
|
117
129
|
examples: [
|
|
118
130
|
'shard workspaces open acme/demo --template python-node-browser',
|
|
119
131
|
'shard ws open job-42 --template python-node-browser --lifetime session',
|
|
132
|
+
'shard ws open acme/demo --template acme-notebook --input PROJECT_NAME=demo --input LOG_LEVEL=debug',
|
|
120
133
|
'shard ws open acme/demo --template python-node-browser --secret OPENAI_API_KEY --secret DATABASE_URL',
|
|
121
134
|
'shard ws open acme/demo --template python-node-browser --no-wait --json',
|
|
122
135
|
'shard ws open acme/demo --template python-node-browser --timing',
|
|
@@ -136,7 +149,8 @@ export const COMMANDS = [
|
|
|
136
149
|
cursor: { type: 'string', value: '<cursor>', description: 'Continue from a previous page.' },
|
|
137
150
|
},
|
|
138
151
|
},
|
|
139
|
-
{ path: ['workspaces', 'get'], summary: 'Show one workspace', positionals: [REF], options: {} },
|
|
152
|
+
{ path: ['workspaces', 'get'], summary: 'Show one workspace (state, template, startup)', positionals: [REF], options: {} },
|
|
153
|
+
{ path: ['workspaces', 'inputs'], summary: 'Show the text inputs of a workspace (secret inputs are bound secrets: shard ws secrets)', positionals: [REF], options: {} },
|
|
140
154
|
{
|
|
141
155
|
path: ['workspaces', 'exec'],
|
|
142
156
|
summary: 'Run a command in a workspace; streams its output and exits with its exit code',
|
|
@@ -326,6 +340,109 @@ export const COMMANDS = [
|
|
|
326
340
|
limit: { type: 'string', value: '<n>', description: 'Page size, 1-200 (default 50).' },
|
|
327
341
|
},
|
|
328
342
|
},
|
|
343
|
+
{
|
|
344
|
+
path: ['templates', 'get'],
|
|
345
|
+
summary: 'Show a template: its versions, what open resolves to, and the settings of its open version',
|
|
346
|
+
positionals: [SLUG],
|
|
347
|
+
options: {
|
|
348
|
+
owner: { type: 'string', value: '<platform|organization>', description: 'Pick the platform template even when an organization template shadows its slug.' },
|
|
349
|
+
'include-archived': { type: 'boolean', description: 'Include archived versions.' },
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
path: ['templates', 'init'],
|
|
354
|
+
summary: 'Write a template.yaml that starts from a base template',
|
|
355
|
+
description: 'The file is the recipe v2 document (contracts §24.1): languages, packages, files (local "from" paths are uploaded by "shard templates build"), build steps, and the settings a workspace gets when it opens (env, inputs, start commands, services, defaults). Edit it, then build it.',
|
|
356
|
+
positionals: [],
|
|
357
|
+
options: {
|
|
358
|
+
base: { type: 'string', value: '<slug[@version]>', description: 'Base template (required), e.g. ubuntu-24.04; without @version, the version open resolves to now.' },
|
|
359
|
+
out: { type: 'string', value: '<file>', description: 'Where to write it (default template.yaml).' },
|
|
360
|
+
force: { type: 'boolean', description: 'Overwrite an existing file.' },
|
|
361
|
+
},
|
|
362
|
+
examples: ['shard templates init --base ubuntu-24.04', 'shard templates init --base python-node-browser@7 --out acme/template.yaml'],
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
path: ['templates', 'build'],
|
|
366
|
+
summary: 'Build a template from template.yaml: upload its local files and folders, then build it',
|
|
367
|
+
description: 'Folders named by "from" are packed as a reproducible tar and uploaded with files unless the organization already has the same bytes. The version stays unpublished (test it with "shard templates test") unless --publish. Progress goes to stderr.',
|
|
368
|
+
positionals: [{ name: 'file', required: true }],
|
|
369
|
+
options: {
|
|
370
|
+
slug: { type: 'string', value: '<slug>', description: 'Organization template to build into (default: the name of the directory holding the file). Created by the first build.' },
|
|
371
|
+
'display-name': { type: 'string', value: '<name>', description: 'Template name when this build creates the template.' },
|
|
372
|
+
description: { type: 'string', value: '<text>', description: 'Version description.' },
|
|
373
|
+
publish: { type: 'boolean', description: 'Publish the version once it is registered (new workspaces of the slug then use it). Default: leave it unpublished.' },
|
|
374
|
+
acknowledge: { type: 'string', multiple: true, value: '<path>', description: 'Absolute path the credential scan may report without failing the build (repeatable, up to 200).' },
|
|
375
|
+
wait: { type: 'boolean', description: 'Wait until the build is registered (or fails).' },
|
|
376
|
+
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 30m). The build continues server side.' },
|
|
377
|
+
timing: { type: 'boolean', description: 'Print where the time went on stderr: packing, uploads, the request, the wait, and the build’s queued and run time (with --json: a "timing" field).' },
|
|
378
|
+
},
|
|
379
|
+
examples: ['shard templates build template.yaml --wait', 'shard templates build acme/template.yaml --slug acme-dev --publish --wait --timing'],
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
path: ['templates', 'export'],
|
|
383
|
+
summary: 'Print (or save) the template.yaml a version was built from, ready to build again',
|
|
384
|
+
description: 'Versions saved from a workspace (or a draft) and platform versions have no recipe: change their settings through a draft publish instead.',
|
|
385
|
+
positionals: [{ name: 'slug@version', required: true }],
|
|
386
|
+
options: {
|
|
387
|
+
out: { type: 'string', value: '<file>', description: 'Write to this file instead of stdout.' },
|
|
388
|
+
force: { type: 'boolean', description: 'Overwrite an existing --out file.' },
|
|
389
|
+
owner: { type: 'string', value: '<platform|organization>', description: 'Pick the platform template even when an organization template shadows its slug.' },
|
|
390
|
+
},
|
|
391
|
+
examples: ['shard templates export acme-dev@3 --out template.yaml'],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
path: ['templates', 'test'],
|
|
395
|
+
summary: 'Open a test instance of a version, published or not (a disposable session workspace)',
|
|
396
|
+
description: 'Runs the version’s start commands and services like a new workspace. It ends with "shard ws close <key>" or when idle. Owners, admins and API keys with a tool permission.',
|
|
397
|
+
positionals: [{ name: 'slug@version', required: true }],
|
|
398
|
+
options: {
|
|
399
|
+
'instance-key': { type: 'string', value: '<key>', description: 'Workspace key of the test instance (default sf:test:<slug>:<random>).' },
|
|
400
|
+
...INPUTS,
|
|
401
|
+
...CAPS,
|
|
402
|
+
wait: { type: 'boolean', description: 'Wait until ready (default; --no-wait returns at once).' },
|
|
403
|
+
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 5m).' },
|
|
404
|
+
...TIMING,
|
|
405
|
+
},
|
|
406
|
+
examples: ['shard templates test acme-dev@4 --input PROJECT_NAME=demo'],
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
path: ['templates', 'builds', 'list'],
|
|
410
|
+
summary: 'List the organization’s template builds, newest first',
|
|
411
|
+
positionals: [],
|
|
412
|
+
options: {
|
|
413
|
+
template: { type: 'string', value: '<slug>', description: 'Only builds of this template.' },
|
|
414
|
+
state: { type: 'string', value: '<state>', description: 'queued, building, testing, publishing, published, failed or canceled.' },
|
|
415
|
+
...PAGE,
|
|
416
|
+
limit: { type: 'string', value: '<n>', description: 'Page size, 1-200 (default 50).' },
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
{ path: ['templates', 'builds', 'get'], summary: 'Show one template build (state, registration, denied hosts, failure)', positionals: [BUILD_ID], options: {} },
|
|
420
|
+
{
|
|
421
|
+
path: ['templates', 'builds', 'log'],
|
|
422
|
+
summary: 'Print (or save) a build’s full log',
|
|
423
|
+
positionals: [BUILD_ID],
|
|
424
|
+
options: { out: { type: 'string', value: '<file>', description: 'Write to this file instead of stdout.' } },
|
|
425
|
+
},
|
|
426
|
+
{ path: ['templates', 'builds', 'cancel'], summary: 'Cancel a template build (queued: at once; running: honoured until publishing starts)', positionals: [BUILD_ID], options: {} },
|
|
427
|
+
{
|
|
428
|
+
path: ['templates', 'languages'],
|
|
429
|
+
summary: 'List the languages (and versions) a base offers a template.yaml',
|
|
430
|
+
description: 'included: the base already has that version (nothing is installed). A version the base has another version of is left out. HOSTS is what an auto build network allows for it.',
|
|
431
|
+
positionals: [],
|
|
432
|
+
options: { base: { type: 'string', value: '<slug@version>', description: 'The base (required), e.g. ubuntu-24.04@1.' } },
|
|
433
|
+
examples: ['shard templates languages --base ubuntu-24.04@1'],
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
path: ['templates', 'packages', 'search'],
|
|
437
|
+
summary: 'Search apt, pip or npm package names for a template.yaml',
|
|
438
|
+
description: 'apt searches the package index of a base (--base, required); pip returns names only; npm searches the registry.',
|
|
439
|
+
positionals: [{ name: 'ecosystem', required: true }, { name: 'query', required: true }],
|
|
440
|
+
options: {
|
|
441
|
+
base: { type: 'string', value: '<slug@version>', description: 'The base whose apt index to search (apt only).' },
|
|
442
|
+
limit: { type: 'string', value: '<n>', description: 'Results, 1-50 (default 20).' },
|
|
443
|
+
},
|
|
444
|
+
examples: ['shard templates packages search apt jq --base ubuntu-24.04@1', 'shard templates packages search npm typescript'],
|
|
445
|
+
},
|
|
329
446
|
{
|
|
330
447
|
path: ['templates', 'files'],
|
|
331
448
|
summary: 'List one directory of a template version’s file tree (or show one entry with --stat)',
|
|
@@ -420,7 +537,7 @@ const GROUPS = {
|
|
|
420
537
|
files: 'Read, write and list files in a workspace',
|
|
421
538
|
operations: 'Inspect and wait for lifecycle operations',
|
|
422
539
|
secrets: 'Manage secrets (values are write-only) and bind them to workspaces',
|
|
423
|
-
templates: '
|
|
540
|
+
templates: 'Build templates from template.yaml, test and export versions, browse templates (file tree, diff) and develop them in a draft',
|
|
424
541
|
};
|
|
425
542
|
const ALIASES = { ws: 'workspaces', workspace: 'workspaces', ops: 'operations', operation: 'operations', file: 'files', secret: 'secrets', template: 'templates' };
|
|
426
543
|
/** Canonical command words: aliases resolved, `workspaces files|operations` folded to the top-level group. */
|
|
@@ -691,7 +808,7 @@ export const EXIT_CODE_HELP = [
|
|
|
691
808
|
' 3 not found (workspace, operation or route)',
|
|
692
809
|
' 4 authentication/authorization (missing, malformed, revoked or insufficient key)',
|
|
693
810
|
' 5 timeout (waiting gave up; the operation continues server side)',
|
|
694
|
-
' 6 operation failed or canceled',
|
|
811
|
+
' 6 operation failed or canceled (retryable=true in the error: it may succeed later, e.g. capacity_unavailable)',
|
|
695
812
|
' 130 interrupted (Ctrl-C)',
|
|
696
813
|
' exec: the command’s own exit code (124 if it timed out, 128+N if killed by signal N)',
|
|
697
814
|
];
|