@shardflux/sdk 0.13.0 → 0.14.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 +172 -2
- package/README.md +131 -15
- package/dist/account.d.ts +41 -2
- package/dist/account.js +36 -2
- package/dist/cell.d.ts +21 -4
- package/dist/cell.js +67 -10
- package/dist/client.d.ts +86 -2
- package/dist/client.js +148 -0
- package/dist/codex-proof.d.ts +53 -0
- package/dist/codex-proof.js +200 -0
- package/dist/errors.d.ts +20 -1
- package/dist/errors.js +10 -0
- package/dist/generated/app-api.d.ts +1995 -255
- package/dist/generated/cell-api.d.ts +19 -9
- package/dist/http.d.ts +40 -3
- package/dist/http.js +74 -10
- package/dist/index.d.ts +9 -5
- package/dist/index.js +3 -1
- package/dist/ports.d.ts +117 -0
- package/dist/ports.js +62 -0
- package/dist/progress.d.ts +52 -8
- package/dist/progress.js +37 -1
- package/dist/tools.d.ts +8 -0
- package/dist/tools.js +20 -0
- package/dist/usage.d.ts +3 -1
- package/dist/usage.js +3 -1
- package/dist/workspace.d.ts +43 -7
- package/dist/workspace.js +41 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,180 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0 — 2026-10-04 (not yet published)
|
|
4
|
+
|
|
5
|
+
- Opt-in `workspaceTools(workspace, { prewake: true })` input-start hooks prepare VM tools while their input streams. Default tools, offline reads and file-first workspaces install no hook.
|
|
6
|
+
|
|
3
7
|
Every API the README shows is available from the version named here. Breaking changes ship in minor releases and are
|
|
4
8
|
marked **Breaking**.
|
|
5
9
|
|
|
6
|
-
## 0.
|
|
10
|
+
## 0.14.0 (not yet published)
|
|
11
|
+
|
|
12
|
+
### Sign up from an agent
|
|
13
|
+
|
|
14
|
+
Additive (API contracts §44).
|
|
15
|
+
|
|
16
|
+
- `ShardfluxAccount.signup({ email?, displayName?, codexIdToken? })` (POST /v1/auth/signup): a working account and
|
|
17
|
+
its session in one call, with no browser and no email round-trip. Returns `{ account, result }`; `result.access`
|
|
18
|
+
is `verified`, `provisional` (works at once on the Free plan; the trial starts when the email is verified) or
|
|
19
|
+
`verification_required`, and `result.created` is false when a Codex proof signed in to an existing account.
|
|
20
|
+
- `codexIdentityProof(opts?)` (Node): the OpenAI ID token of the Codex CLI signed in on this machine, refreshed by
|
|
21
|
+
Codex itself (`codex app-server`, `account/read {refreshToken: true}`) and read from `$CODEX_HOME/auth.json`. Only
|
|
22
|
+
the ID token is returned. Never throws: `{ ok: false, reason: 'codex_not_found' | 'not_signed_in' |
|
|
23
|
+
'api_key_login' | 'stale', message }`.
|
|
24
|
+
- `account.auth.requestEmailCode()` and `account.auth.verifyEmailCode(code)` (POST /v1/auth/verify-email/code):
|
|
25
|
+
verify with a 6-digit emailed code; nothing is signed out.
|
|
26
|
+
- `account.auth.stepUp({ codexIdToken })`: a step-up with the linked Codex login instead of the password.
|
|
27
|
+
- New types `SignupResult`, `SignupAccess`, `EmailCodeResult`, `CodexIdentityProof`, `CodexProofOptions`,
|
|
28
|
+
`CodexProofUnavailable`.
|
|
29
|
+
|
|
30
|
+
### Workspaces working at once
|
|
31
|
+
|
|
32
|
+
Additive. The plan's workspace number caps workspaces working at the same moment; stored workspaces are bounded by
|
|
33
|
+
Retained state.
|
|
34
|
+
|
|
35
|
+
- A tool call that finds every workspace the plan allows to work at once busy is refused with 429 `quota_exceeded`
|
|
36
|
+
(retryable, `Retry-After`; `details.limit` `concurrent_workspaces`, `limit_value`, `current`,
|
|
37
|
+
`retry_after_seconds`). The call did not run, so the SDK sends the same request again after `Retry-After` (at most
|
|
38
|
+
5 s) within `maxRetries`, for every call: exec starts, stdin, PTY create, keepalive and process signals included.
|
|
39
|
+
A refusal that outlasts the retries is a `ShardfluxApiError` with `retryAfterSeconds`. `executions.run()` keeps
|
|
40
|
+
these retries and does not add its own on top; the wake hint is not retried (it never waits).
|
|
41
|
+
- `pty.read()` rejects with the refusal (`ShardfluxApiError`, e.g. 429 `quota_exceeded`, retryable) when an accepted
|
|
42
|
+
attach is closed with an error body and a 4xxx close code, instead of returning empty output.
|
|
43
|
+
- The API's 403 `quota_exceeded` is not retried, as before. New `details.limit`: `retained_state` (`limit_value` and
|
|
44
|
+
`current` in GiB): opening a new key and forking are refused while the plan's Retained state is used up; existing
|
|
45
|
+
workspaces keep running, waking, suspending and resuming. Usage summaries report that allowance with `enforcement`
|
|
46
|
+
`storage_block` and `cap_state` `storage_blocked`.
|
|
47
|
+
- Regenerated contract types: `ErrorCode` includes `quota_exceeded` for the workspace gateway; allowance
|
|
48
|
+
`enforcement` adds `storage_block`, `cap_state` adds `storage_blocked`.
|
|
49
|
+
|
|
50
|
+
### Inbound ports
|
|
51
|
+
|
|
52
|
+
Additive. A TCP port of a processful workspace can be served at its own private HTTPS URL,
|
|
53
|
+
`https://<port>-<handle>.<domain>`; a request wakes a parked or suspended workspace and is answered once it runs.
|
|
54
|
+
|
|
55
|
+
- `workspace.ports` and `cloud.workspaces.ports(id)` (class `WorkspacePorts`): `expose(port)` →
|
|
56
|
+
`{port, url, createdAt, callback, created}` (`ExposePortResult`; idempotent, `created` false when it already was),
|
|
57
|
+
`list()` → `ExposedPort[]`, `close(port)`, `token(port, {ttlSeconds?})` → `{token, expiresAt, url}` (`PortToken`),
|
|
58
|
+
`link(port, {ttlSeconds?, path?})` → `{url, expiresAt}` (`PortLink`), `createCallbackUrl(port)` →
|
|
59
|
+
`{url, createdAt}` (`PortCallbackUrl`) and `revokeCallbackUrl(port)`. `expose`, `close` and `revokeCallbackUrl` are
|
|
60
|
+
retried on transient failures; minting calls are not.
|
|
61
|
+
- Types `ExposedPort`, `ExposePortResult`, `PortToken`, `PortLink`, `PortCallbackUrl`, `PortTokenOptions`,
|
|
62
|
+
`PortLinkOptions` and the raw API shapes `PortView`, `PortTokenResponse`, `PortLinkResponse`, `PortCallbackResponse`
|
|
63
|
+
(regenerated contract types).
|
|
64
|
+
- `KnownErrorReason` adds `inbound_ports_not_available`, `port_not_exposed` and `port_limit`.
|
|
65
|
+
|
|
66
|
+
### Memory across platform upgrades
|
|
67
|
+
|
|
68
|
+
Additive. A resume keeps the workspace's memory and running processes across host kernel, CPU generation and
|
|
69
|
+
Firecracker upgrades of the platform.
|
|
70
|
+
|
|
71
|
+
- `ColdBootReason` gains `runtime_retired`, and `COLD_BOOT_REASONS` lists every reason this version knows
|
|
72
|
+
(`runtime_changed`, `host_lost`, `runtime_retired`). `runtime_retired`: the runtime the workspace was suspended on
|
|
73
|
+
was retired after an announced window; the resume booted the saved disk (`resumePath` `cold_boot`,
|
|
74
|
+
`memoryRestored` false). A reason this version does not know is still passed through as a string.
|
|
75
|
+
|
|
76
|
+
### Resize a workspace
|
|
77
|
+
|
|
78
|
+
Additive. Change the memory, CPU and disk of any workspace, running or suspended, fixed or elastic, without a restart
|
|
79
|
+
or a fork.
|
|
80
|
+
|
|
81
|
+
- `workspace.resize({ memoryMib, memoryMibHeld, allocationMode, cpuMillis, diskGib })` and
|
|
82
|
+
`cloud.workspaces.resize(id, params)` send `PATCH /v1/workspaces/{id}/caps` with the given caps (snake_case, at
|
|
83
|
+
least one; none throws `TypeError` before any request), an `Idempotency-Key` (`idempotencyKey`, default a fresh key
|
|
84
|
+
per call, so a retried request replays) and `Prefer: wait` (up to 20 s, within `timeoutMs`). They resolve once the
|
|
85
|
+
resize has finished: a resize the server answers with `202` is waited for like any operation (`timeoutMs`, default
|
|
86
|
+
300 000 ms; `signal`; `onProgress`, action `resize`). A resize that meets another lifecycle operation (409
|
|
87
|
+
`operation_in_progress`) waits for it and is sent again with the same `Idempotency-Key`, within `timeoutMs`. A failed
|
|
88
|
+
resize is its error: `ShardfluxApiError` 409 with `operationId` (reason `resize_failed`, ...) when the server held
|
|
89
|
+
the request until then, `OperationFailedError` when the SDK waited for the operation.
|
|
90
|
+
- `ResizeResult`: `workspaceId`, `operationId`, `state`, `caps` (the stored caps every later start uses),
|
|
91
|
+
`operation` (the finished operation after a `202`, else null) and, for each resource the request named,
|
|
92
|
+
`memory` / `cpu` / `disk` (`ResizeMemory`, `ResizeCpu`, `ResizeDisk`): `applies_at` (`now`, `resume` or
|
|
93
|
+
`next_start`), `requested_*`, `target_*`, `previous_*`, `applied_*`, `limit_reason` (`plan`, `template`,
|
|
94
|
+
`host_capacity`, `shrink_stalled`) and `reason` (`suspended`, `stopped`, `boot_vcpus`, `region`, `below_base`,
|
|
95
|
+
`legacy_layout`); memory adds `allocation_mode`, `held_mib` and `converged`. Types `ResizeParams`,
|
|
96
|
+
`ResizeAppliesAt`, `ResizeLimitReason` and `ResizeDeferReason` are exported.
|
|
97
|
+
- The handle refreshes its view after a resize. A file-first workspace is refused locally with
|
|
98
|
+
`NotSupportedForModeError`.
|
|
99
|
+
- A finished operation's `result` (the cell's) is returned in the held answer's shape: each named resource with its
|
|
100
|
+
documented keys, a key left out taken from the operation's input or null, other keys dropped.
|
|
101
|
+
- `KnownErrorReason` adds `resize_not_available` (409 `conflict`), `resize_failed` (409 `conflict`, a failed resize)
|
|
102
|
+
and `shrink_not_supported` (422 `validation_failed`, `details.current_disk_gib`). `LifecycleAction` adds `resize`.
|
|
103
|
+
|
|
104
|
+
### Memory hint for commands
|
|
105
|
+
|
|
106
|
+
Additive. `RunOptions.resourceHint` (`'auto' | 'light' | 'heavy'`, type `ResourceHint`) on `exec.run()`, and
|
|
107
|
+
`resource_hint` on `exec.start()` requests: `heavy` gives an elastic workspace its memory before the command starts,
|
|
108
|
+
`light` starts it at once, `auto` (the default) decides from the command. Sent only when set, so other requests are
|
|
109
|
+
unchanged. The processful `exec` agent tool takes `resource_hint` with the same values.
|
|
110
|
+
|
|
111
|
+
### Elastic by default
|
|
112
|
+
|
|
113
|
+
With elastic-by-default enabled for the organization, an open of a new processful workspace without
|
|
114
|
+
`caps.allocation_mode` is elastic: it holds the memory its commands use and grows up to `memory_mib` (when not given,
|
|
115
|
+
the plan's largest workspace, bounded by the template); billing counts the memory it holds. `allocation_mode: 'fixed'`
|
|
116
|
+
gives a static allocation of `memory_mib`. Reopening an existing workspace with caps that omit `allocation_mode` then
|
|
117
|
+
keeps its stored mode; a fork without caps inherits its source's. The SDK's requests do not change;
|
|
118
|
+
`workspace.memory` and `workspace.allocationMode` report the mode the workspace got.
|
|
119
|
+
|
|
120
|
+
## 0.13.1 (not yet published)
|
|
121
|
+
|
|
122
|
+
Patch, additive: the transport, a documented value, a fix for tool calls across a move and self-healing workspaces.
|
|
123
|
+
`@shardflux/cli` 0.8.1, `@shardflux/mcp` 0.7.1 and the `shardflux` bundle 0.10.1 follow (`^0.13.1`).
|
|
124
|
+
|
|
125
|
+
### Workspaces recover by themselves
|
|
126
|
+
|
|
127
|
+
Additive. When the machine a workspace runs on fails, the workspace is suspended, and its next use (a tool call's wake,
|
|
128
|
+
`resume()`, `open()`) restores it: from its own disk, files kept, or from its newest checkpoint. Nothing changes for
|
|
129
|
+
code that already handles a cold resume.
|
|
130
|
+
|
|
131
|
+
- `cold_boot_reason` / `ServerTiming.coldBootReason` can be `host_lost`: the resume booted the workspace's disk
|
|
132
|
+
(`resumePath` `cold_boot`, `memoryRestored` false, processes restarted). New type `ColdBootReason`
|
|
133
|
+
(`'runtime_changed' | 'host_lost'`, open to other strings; the field still accepts any string).
|
|
134
|
+
- `hostLostOf(op)` and `ServerTiming.hostLost`: `result.host_lost` camelCased (`HostLost`: `detectedAt`,
|
|
135
|
+
`restoredFrom` `'disk' | 'checkpoint'`, `restoredCheckpointId`, `stateAsOf`). A resume from the checkpoint has its
|
|
136
|
+
state as of `stateAsOf`; a suspend that found the machine failed succeeds with `durable: true` and only
|
|
137
|
+
`detectedAt` (`restoredFrom` null).
|
|
138
|
+
- `formatTiming()` prints `processes restarted (host_lost)` for the disk, `restored checkpoint <id> (host_lost; state as
|
|
139
|
+
of <time>)` for the checkpoint, and `host_lost (detected <time>)` for the suspend.
|
|
140
|
+
- Documented operation error codes: `resume_required` (a fork or snapshot of such a workspace before its resume; resume
|
|
141
|
+
it first; not retryable) and `workspace_storage_unavailable` with `details.reason` `host_lost` (not retryable).
|
|
142
|
+
`KnownErrorReason` gains `host_lost`.
|
|
143
|
+
|
|
144
|
+
### Connection reuse
|
|
145
|
+
|
|
146
|
+
Additive. The call after an agent's pause between tool calls reuses its connection instead of opening a new one.
|
|
147
|
+
|
|
148
|
+
- On Node 26 the pooled transport keeps an idle connection reusable for 5 minutes (was 4 s), so a request seconds or
|
|
149
|
+
minutes after the last one skips the TCP and TLS handshake. Pooled sockets send TCP keepalive probes after 60 s
|
|
150
|
+
idle, so NATs and firewalls along the way keep the connection open. Idle connections never keep a process alive. A
|
|
151
|
+
supplied `fetch`, other runtimes and `SHARDFLUX_HTTP_KEEPALIVE` work as before.
|
|
152
|
+
- A request the SDK retries (GET, a request with an `Idempotency-Key`, a read-only POST such as files search) whose
|
|
153
|
+
connection closes before the response arrives is sent again at once on a new connection. That one resend has no
|
|
154
|
+
backoff and does not count against `maxRetries`; timing records and progress `retry` events list it with
|
|
155
|
+
`delayMs: 0`. A further failure follows `maxRetries` and backoff as before, and a POST without an
|
|
156
|
+
`Idempotency-Key` is never sent twice.
|
|
157
|
+
|
|
158
|
+
### Faster suspend and resume
|
|
159
|
+
|
|
160
|
+
Additive. `resumePath` / `resume_path` can be `thaw`: a resume that arrives while its suspend is still being
|
|
161
|
+
written continues the same VM in place. Nothing else changes for your code.
|
|
162
|
+
|
|
163
|
+
### Tool calls across a move
|
|
164
|
+
|
|
165
|
+
Fix. A tool call made while its workspace finishes a resume or a move now runs once the workspace runs, instead of
|
|
166
|
+
failing with `409 conflict` (`workspace_not_running`).
|
|
167
|
+
|
|
168
|
+
- When the call's tool token was refused as not running and the wake then found the workspace already running (the
|
|
169
|
+
resume or move committed in between; `wake` resolves `false`), the call is retried with a current token: the held
|
|
170
|
+
resume's, or a new one. A refused call never ran, so the retry cannot run it twice. Before, the refusal surfaced.
|
|
171
|
+
- A refusal that comes back while the API keeps reporting the workspace running is retried after 500 ms, within the
|
|
172
|
+
same 3 wakes and `transitionTimeoutMs`, then surfaces as before.
|
|
173
|
+
- `onProgress` sees the retry as a `tool` event of type `retry`, cause `conflict workspace_not_running (the workspace
|
|
174
|
+
runs; new token)`.
|
|
175
|
+
- The CLI, the MCP server and the `shardflux` bundle get it through their `^0.13.0` dependency.
|
|
176
|
+
|
|
177
|
+
## 0.13.0
|
|
7
178
|
|
|
8
179
|
### Elastic memory
|
|
9
180
|
|
|
@@ -446,7 +617,6 @@ behavior is the automatic version check (below), which makes one background requ
|
|
|
446
617
|
- New exports: `FeedbackCategory`, `FeedbackContext`, `FeedbackReceipt`, `SendFeedbackParams`, `AccountFeedbackParams`,
|
|
447
618
|
`FEEDBACK_CATEGORIES`, `FEEDBACK_MESSAGE_MAX_LENGTH`.
|
|
448
619
|
|
|
449
|
-
|
|
450
620
|
## 0.8.0 (2026-09-28)
|
|
451
621
|
|
|
452
622
|
Types only; nothing changes at run time and the API is unchanged.
|
package/README.md
CHANGED
|
@@ -296,10 +296,10 @@ await cell.exec.run(['make', 'test']); // resumes the wo
|
|
|
296
296
|
```
|
|
297
297
|
|
|
298
298
|
**Detecting a cold resume (0.11.0+).** A resume restores memory and running processes from the checkpoint. After a
|
|
299
|
-
platform runtime update,
|
|
300
|
-
tool call wakes the workspace; check `memoryRestored`. The
|
|
301
|
-
processes start fresh, as after a reboot: start dev servers, databases and
|
|
302
|
-
says which it was:
|
|
299
|
+
platform runtime update, or after the machine the workspace ran on failed (0.13.1+), a resume can boot from the saved
|
|
300
|
+
disk instead of restoring memory (a cold boot), also when a tool call wakes the workspace; check `memoryRestored`. The
|
|
301
|
+
workspace runs with its files kept, and its processes start fresh, as after a reboot: start dev servers, databases and
|
|
302
|
+
background jobs again. The resume's timing says which it was:
|
|
303
303
|
|
|
304
304
|
```ts
|
|
305
305
|
const t = workspace.lastTiming; // after resume({ wait: true }), wake(), or a tool call that woke the workspace
|
|
@@ -310,11 +310,20 @@ if (t?.server?.memoryRestored === false) {
|
|
|
310
310
|
|
|
311
311
|
- `ServerTiming.memoryRestored`: `true` when memory and processes came back, `false` when the workspace booted
|
|
312
312
|
(`resumePath` `cold_boot`, or `reset_blank_layer` after a reset), `null` when the API did not say (older APIs).
|
|
313
|
-
- `ServerTiming.coldBootReason
|
|
314
|
-
`resume from cold_boot: processes restarted (runtime_changed)`.
|
|
313
|
+
- `ServerTiming.coldBootReason` (type `ColdBootReason`): why it booted, `runtime_changed` or `host_lost`
|
|
314
|
+
**(0.13.1+)**, else null. `formatTiming()` prints `resume from cold_boot: processes restarted (runtime_changed)`.
|
|
315
315
|
- The finished resume operation carries the same in `result` (`memory_restored`, `cold_boot_reason`, and
|
|
316
316
|
`cold_boot` with details such as `files_as_of`; it is informational and not part of the stable contract).
|
|
317
317
|
|
|
318
|
+
**Workspaces recover by themselves (0.13.1+).** When the machine a workspace runs on fails, the workspace is suspended,
|
|
319
|
+
and its next use (a tool call, `resume()`, `open()`) restores it; there is nothing extra to call.
|
|
320
|
+
`hostLostOf(op)` and `lastTiming.server.hostLost` (`HostLost`) say how:
|
|
321
|
+
|
|
322
|
+
- `restoredFrom: 'disk'`: it booted from its own disk, files kept (`coldBootReason` `host_lost`, processes restarted).
|
|
323
|
+
- `restoredFrom: 'checkpoint'`: it resumed its newest checkpoint (`restoredCheckpointId`); its state is as of
|
|
324
|
+
`stateAsOf`. `formatTiming()` adds `restored checkpoint <id> (host_lost; state as of <time>)`.
|
|
325
|
+
- A `suspend()` that finds the machine failed succeeds (`result.durable` true, `hostLost.detectedAt`).
|
|
326
|
+
|
|
318
327
|
`suspend`, `resume`, `fork`, `snapshot` and `delete` return the lifecycle operation.
|
|
319
328
|
`cloud.workspaces.waitForOperation(id)` waits for it (default timeout 5 minutes): each poll asks the API to hold
|
|
320
329
|
the response until the operation changes (`Prefer: wait`, at most 20 s per request), so completion arrives within
|
|
@@ -324,7 +333,15 @@ operation keeps running server side (a queued start until its `deadlineAt`); wai
|
|
|
324
333
|
same call. `templates.builds.waitForBuild()` waits the same way. A waited `open()` issues the first tool token
|
|
325
334
|
together with the final workspace read, so the first tool call starts at once.
|
|
326
335
|
|
|
327
|
-
From 0.12.0, Node 26 reuses TLS connections through a private HTTP/1.1 pool.
|
|
336
|
+
From 0.12.0, Node 26 reuses TLS connections through a private HTTP/1.1 pool. From 0.13.0, an idle connection stays
|
|
337
|
+
reusable for 5 minutes, so the call after an agent's pause between tool calls skips the TCP and TLS handshake, and
|
|
338
|
+
pooled sockets send TCP keepalive probes after 60 s idle. Idle connections never keep a process alive. Other runtimes
|
|
339
|
+
keep native fetch. A supplied `fetch` stays unchanged. `SHARDFLUX_HTTP_KEEPALIVE=0` forces connection closure for
|
|
340
|
+
diagnosis; `=1` opts into native pooling. No global dispatcher is installed.
|
|
341
|
+
|
|
342
|
+
A request the SDK retries (safe methods, requests with an `Idempotency-Key`, read-only POSTs) whose connection closes
|
|
343
|
+
before the response arrives is sent again at once on a new connection **(0.13.0+)**, without backoff and outside
|
|
344
|
+
`maxRetries`; timing records list it with `delayMs: 0`.
|
|
328
345
|
|
|
329
346
|
List and look up workspaces:
|
|
330
347
|
|
|
@@ -343,10 +360,11 @@ lookups by key.
|
|
|
343
360
|
|
|
344
361
|
### Elastic memory (0.13.0+)
|
|
345
362
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
363
|
+
A workspace can hold only the memory its commands use and grow up to `memory_mib` when they need more; billing counts
|
|
364
|
+
the memory it holds. An elastic workspace idles at its held floor (`memory_mib_held`, default 1024) and shrinks back
|
|
365
|
+
after 30 s without work. Package installs, test runners, compilers, type checkers and bundlers get their memory before
|
|
366
|
+
they start, so they size their heaps and workers from what they will actually get; everything else starts at once and
|
|
367
|
+
the workspace grows while it runs.
|
|
350
368
|
|
|
351
369
|
```ts
|
|
352
370
|
const ws = await cloud.workspaces.open({
|
|
@@ -358,10 +376,32 @@ ws.memory; // { allocation_mode: 'elastic', promised_mib: 8192, held_m
|
|
|
358
376
|
|
|
359
377
|
const r = await ws.cell().exec.run(['npm', 'test']);
|
|
360
378
|
r.memoryGrow; // { outcome: 'delivered', from_mib: 1024, want_mib: 4096, got_mib: 4096, deliver_ms: 175 }
|
|
379
|
+
|
|
380
|
+
await ws.cell().exec.run(['python3', 'train.py'], { resourceHint: 'heavy' }); // 0.14.0+: memory first, then the command
|
|
361
381
|
```
|
|
362
382
|
|
|
363
|
-
|
|
364
|
-
|
|
383
|
+
`resourceHint` (0.14.0+) is `'auto'` (the default: decided from the command), `'heavy'` or `'light'`; the exec agent
|
|
384
|
+
tool takes `resource_hint`. `allocation_mode: 'fixed'` gives a static allocation of `memory_mib`. Send
|
|
385
|
+
`allocation_mode` with every `caps` you pass to keep the mode you chose; an open without `caps` keeps the stored
|
|
386
|
+
layout, and a fork without `caps` inherits its source's. The exec agent tool adds `memory_grow` to its result when the
|
|
387
|
+
command's start grew the workspace.
|
|
388
|
+
|
|
389
|
+
### Resize a workspace (0.14.0+)
|
|
390
|
+
|
|
391
|
+
Change the memory, CPU and disk of any workspace, running or suspended, fixed or elastic, without a restart:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
const r = await ws.resize({ memoryMib: 6144, diskGib: 20 });
|
|
395
|
+
r.memory; // { applies_at: 'now', previous_mib: 2048, applied_mib: 6144, converged: true, ... }
|
|
396
|
+
r.disk; // { applies_at: 'now', previous_gib: 10, applied_gib: 20, ... }
|
|
397
|
+
|
|
398
|
+
await ws.resize({ allocationMode: 'elastic', memoryMibHeld: 1024 }); // switch modes live
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Memory changes live, and disks grow online. A suspended workspace gets its new size when it resumes, before its first
|
|
402
|
+
call. CPU changes live within the vCPUs the workspace booted with; a larger CPU size applies at the next start. Each
|
|
403
|
+
resource says when it applies (`applies_at`: `now`, `resume` or `next_start`), and the new caps are stored, so every
|
|
404
|
+
later start uses them.
|
|
365
405
|
|
|
366
406
|
### Burst execution (0.13.0+)
|
|
367
407
|
|
|
@@ -381,6 +421,36 @@ workspace's own processes resume where they were. A burst is not signalled or ca
|
|
|
381
421
|
fails (`burst_apply_failed`), retry `exec.run` with the same `sessionId` to finish the apply. The agent tools offer
|
|
382
422
|
bursts when asked: `workspaceTools(ws, { burst: true })`.
|
|
383
423
|
|
|
424
|
+
### Inbound ports (0.14.0+)
|
|
425
|
+
|
|
426
|
+
Serve a port of the workspace at its own HTTPS URL: a preview of the app your agent is building, a webhook receiver,
|
|
427
|
+
an OAuth redirect, a live view. A request wakes a parked or suspended workspace and is answered once it runs, so the
|
|
428
|
+
workspace can sleep between visits. Every port is private: a request carries a port token, a signed link's browser
|
|
429
|
+
session, or arrives on the port's callback URL.
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
await ws.cell().exec.start({ argv: ['python3', '-m', 'http.server', '3000', '--bind', '0.0.0.0'] });
|
|
433
|
+
const { url } = await ws.ports.expose(3000); // https://3000-<handle>.shardflux.app
|
|
434
|
+
|
|
435
|
+
const { token } = await ws.ports.token(3000); // { token: 'sfp_…', expiresAt, url }, 1 h by default
|
|
436
|
+
await fetch(url, { headers: { authorization: `Bearer ${token}` } });
|
|
437
|
+
|
|
438
|
+
const link = await ws.ports.link(3000, { path: '/dashboard' }); // open link.url in a browser (24 h by default)
|
|
439
|
+
const hook = await ws.ports.createCallbackUrl(3000); // register `${hook.url}github` as a webhook URL
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
- The server inside the workspace listens on `0.0.0.0` (all interfaces), not only `127.0.0.1`.
|
|
443
|
+
- `expose(port)` is idempotent: `created` is true when this call exposed the port; an exposed port keeps its URL, tokens,
|
|
444
|
+
links and callback URL. `list()` returns `{ port, url, createdAt, callback }` for each exposed port.
|
|
445
|
+
- `token(port, { ttlSeconds })` (60-86400): send it as `Authorization: Bearer <token>`, or as `X-Shardflux-Token`
|
|
446
|
+
when the app reads `Authorization` itself. `link(port, { ttlSeconds, path })` (60-604800 s): anyone with the link can
|
|
447
|
+
open the port until it expires, so share it like a password.
|
|
448
|
+
- `createCallbackUrl(port)`: a URL with a secret in its path for GitHub, Slack, Stripe or an OAuth provider; a request
|
|
449
|
+
to `<url><rest>` reaches `/<rest>` without a token header. Its secret is shown only once; calling it again replaces
|
|
450
|
+
it, `revokeCallbackUrl(port)` revokes it.
|
|
451
|
+
- `close(port)` stops the port's tokens, links and callback URL at once (also when the port is exposed again).
|
|
452
|
+
- By id, without reading the workspace: `cloud.workspaces.ports(id)`.
|
|
453
|
+
|
|
384
454
|
### Timing and progress (0.6.0+)
|
|
385
455
|
|
|
386
456
|
Every open, wake and waited lifecycle call is traced. `workspace.lastTiming` (and `err.timing` when the call fails)
|
|
@@ -412,7 +482,7 @@ This resume brought the workspace back from its host's local cache, memory and r
|
|
|
412
482
|
running, including any time in `capacity_pending`; `ran` is the cell's work (placement, boot or restore, guest
|
|
413
483
|
readiness). `start` / `resume from` and `boot to ready` / `host …` are what the cell reported. A resume that booted
|
|
414
484
|
the saved disk instead of restoring memory reads `resume from cold_boot: processes restarted (runtime_changed)`
|
|
415
|
-
(0.11.0+).
|
|
485
|
+
(0.11.0+), or `(host_lost)` (0.13.1+) after the machine the workspace ran on failed.
|
|
416
486
|
- **outside the server** is your total minus the operation's: network, TLS, polling latency, view and token. A large
|
|
417
487
|
value with a small server total points at the connection between you and the API, not at the workspace.
|
|
418
488
|
- **retries** lists transient failures the SDK retried (cause and backoff).
|
|
@@ -832,7 +902,7 @@ await workspace.cell().exec.run(['python3', 'agent.py']); // sees $OPEN
|
|
|
832
902
|
|
|
833
903
|
`ShardfluxAccount` does what a person does in the web app, with a user session instead of an API key: sign up, sign
|
|
834
904
|
in (with MFA), organizations, projects, API keys, members, invitations, billing, audit, data export and account
|
|
835
|
-
deletion.
|
|
905
|
+
deletion. Paying in Stripe Checkout is the step a person does.
|
|
836
906
|
|
|
837
907
|
```ts
|
|
838
908
|
import { Shardflux, ShardfluxAccount } from '@shardflux/sdk';
|
|
@@ -870,6 +940,37 @@ const cloud = new Shardflux({ apiKey: secret });
|
|
|
870
940
|
- `parseEmailToken(input)` returns the `token` of a link's `#token=` fragment or `?token=` query (else the trimmed
|
|
871
941
|
input), and throws for a link without one.
|
|
872
942
|
|
|
943
|
+
### Sign up from an agent (0.14.0+)
|
|
944
|
+
|
|
945
|
+
One call gives a working account, with no browser and no email round-trip. With the Codex CLI signed in with ChatGPT
|
|
946
|
+
on the machine, the account is verified by that login and starts with its trial:
|
|
947
|
+
|
|
948
|
+
```ts
|
|
949
|
+
import { ShardfluxAccount, codexIdentityProof } from '@shardflux/sdk';
|
|
950
|
+
|
|
951
|
+
const proof = await codexIdentityProof(); // asks Codex to refresh its login, returns the ID token
|
|
952
|
+
const { account, result } = proof.ok
|
|
953
|
+
? await ShardfluxAccount.signup({ codexIdToken: proof.idToken, onSessionToken: (s) => save(s.token) })
|
|
954
|
+
: await ShardfluxAccount.signup({ email: 'ada@example.com', onSessionToken: (s) => save(s.token) });
|
|
955
|
+
// result.access: 'verified' | 'provisional' | 'verification_required'; result.created: false when it signed in
|
|
956
|
+
|
|
957
|
+
if (result.access !== 'verified') {
|
|
958
|
+
await account.auth.requestEmailCode(); // a 6-digit code to the inbox (15 minutes)
|
|
959
|
+
await account.auth.verifyEmailCode('123456'); // verified; the session and keys keep working
|
|
960
|
+
}
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
- `codexIdentityProof()` (Node) reads only the ID token from `$CODEX_HOME/auth.json` (default `~/.codex`), after
|
|
964
|
+
`codex app-server` refreshes it; it never throws: `{ ok: false, reason }` is `codex_not_found`, `not_signed_in`,
|
|
965
|
+
`api_key_login` or `stale`. The token proves the ChatGPT account's verified email and is not a credential to
|
|
966
|
+
OpenAI.
|
|
967
|
+
- With a Codex proof, `signup` signs in to the account of that ChatGPT identity or email when there is one, else
|
|
968
|
+
creates it. `result.status` `mfa_required`: `account.auth.completeMfa({ code })`.
|
|
969
|
+
- With an email only, a `provisional` account works at once on the Free plan; verifying the email starts the trial.
|
|
970
|
+
An address that has an account is 409 `conflict` (`details.reason` `email_registered`).
|
|
971
|
+
- `account.auth.stepUp({ codexIdToken })` confirms a sensitive action with the linked Codex login instead of a
|
|
972
|
+
password.
|
|
973
|
+
|
|
873
974
|
Upgrading a plan: a person pays at the Checkout `url`; the code waits for the subscription.
|
|
874
975
|
|
|
875
976
|
```ts
|
|
@@ -949,6 +1050,11 @@ of that base), or `invalid_path` (`details.field` `recipe.immutable[<i>]`). A bu
|
|
|
949
1050
|
`failure.code` `immutable_path_missing` (`failure.details.path` is not a directory in the built filesystem) or
|
|
950
1051
|
`immutable_image_too_large`.
|
|
951
1052
|
|
|
1053
|
+
Inbound ports **(0.14.0+)**: 403 `forbidden` with `reason` `inbound_ports_not_available` (inbound ports are not enabled
|
|
1054
|
+
for the organization), 404 `not_found` with `reason` `port_not_exposed` (a token, link or callback URL of a port that is
|
|
1055
|
+
not exposed: expose it first; `details.port`), 409 `conflict` with `reason` `port_limit` (close a port first;
|
|
1056
|
+
`details.limit`), and 422 `validation_failed` for a port outside 1-65535 or an option out of range.
|
|
1057
|
+
|
|
952
1058
|
## Usage and overage
|
|
953
1059
|
|
|
954
1060
|
`cloud.usage` reads the organization's usage (API keys see organization totals and their own project's workspaces):
|
|
@@ -1093,3 +1199,13 @@ import { isWorkspaceGone, ShardfluxProtocolError } from '@shardflux/sdk';
|
|
|
1093
1199
|
### Repositories with a minimum release age
|
|
1094
1200
|
|
|
1095
1201
|
Keep your repository's age policy. Pin an exact version that has aged enough; do not exempt the entire `@shardflux/*` scope. The first SDK, 0.5.0, was published September 26, 2026 at 21:13:52 UTC and reaches seven days on October 3 at that time. New versions, including 0.12.0, need their own seven days after publication. Before then no registry setting on our side can make them eligible. `npm view @shardflux/sdk time --json` shows publication times. A targeted exception for an inspected exact release is a repository-owner decision, not an installation requirement we bypass. Older versions do not contain this release's fixes.
|
|
1202
|
+
|
|
1203
|
+
### Prepare tool input (0.14.0+)
|
|
1204
|
+
|
|
1205
|
+
`workspaceTools(workspace, { prewake: true })` adds an optional `onInputStart()` callback to tools that need the VM. Call the matching tool's callback when the model starts streaming its input, then execute the tool normally once its arguments are complete. The callback returns immediately and prepares the workspace in the background. Without `prewake`, tools have no input-start callback. File reads served from disk and file-first tools have no callback.
|
|
1206
|
+
|
|
1207
|
+
```ts
|
|
1208
|
+
const tools = workspaceTools(workspace, { prewake: true });
|
|
1209
|
+
// In your model stream's tool-input-start handler:
|
|
1210
|
+
tools.find(tool => tool.name === toolName)?.onInputStart?.();
|
|
1211
|
+
```
|
package/dist/account.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* The account plane with a user session (0.9.0): what a person does in the web app, from code.
|
|
3
3
|
*
|
|
4
4
|
* // Before a session exists (no token needed)
|
|
5
|
+
* const proof = await codexIdentityProof(); // 0.14.0+: this machine's Codex login
|
|
6
|
+
* const { account } = await ShardfluxAccount.signup(proof.ok ? { codexIdToken: proof.idToken } : { email });
|
|
5
7
|
* await ShardfluxAccount.register({ email, password, displayName });
|
|
6
8
|
* await ShardfluxAccount.verifyEmail(linkFromTheEmail);
|
|
7
9
|
* const { account, result } = await ShardfluxAccount.login({ email, password, onSessionToken: (s) => save(s.token) });
|
|
@@ -44,6 +46,14 @@ type Ok<Op> = Op extends {
|
|
|
44
46
|
[K in keyof R]: K extends 200 | 201 | 202 ? JsonOf<R[K]> : never;
|
|
45
47
|
}[keyof R] : never;
|
|
46
48
|
export type RegisterResult = Ok<operations['postV1AuthRegister']>;
|
|
49
|
+
/**
|
|
50
|
+
* Agent signup (0.14.0+): `status`, `created`, `access` (`verified` | `provisional` | `verification_required`), the
|
|
51
|
+
* user, and the new `session_token`.
|
|
52
|
+
*/
|
|
53
|
+
export type SignupResult = Ok<operations['postV1AuthSignup']>;
|
|
54
|
+
export type SignupAccess = SignupResult['access'];
|
|
55
|
+
/** POST /v1/auth/verify-email/code: `{ status: 'accepted' }` (code sent) or `{ status: 'verified' }`. */
|
|
56
|
+
export type EmailCodeResult = Ok<operations['postV1AuthVerifyEmailCode']>;
|
|
47
57
|
export type VerifyEmailResult = Ok<operations['postV1AuthVerifyEmail']>;
|
|
48
58
|
export type PasswordResetRequestResult = Ok<operations['postV1AuthPasswordResetRequest']>;
|
|
49
59
|
export type PasswordResetConfirmResult = Ok<operations['postV1AuthPasswordResetConfirm']>;
|
|
@@ -178,11 +188,15 @@ export declare class AccountAuthApi {
|
|
|
178
188
|
sessions(): Promise<AuthSessionPage>;
|
|
179
189
|
revokeSession(sessionId: string): Promise<void>;
|
|
180
190
|
/**
|
|
181
|
-
* Confirms the password
|
|
191
|
+
* Confirms the password, or a Codex identity proof of the Codex login linked to the account (0.14.0+:
|
|
192
|
+
* `codexIdToken` from codexIdentityProof()), plus a code when MFA is on, for the calls that need a recent check (403
|
|
182
193
|
* `step_up_required`). Rotates the session token.
|
|
183
194
|
*/
|
|
184
|
-
stepUp(params: {
|
|
195
|
+
stepUp(params: ({
|
|
185
196
|
password: string;
|
|
197
|
+
} | {
|
|
198
|
+
codexIdToken: string;
|
|
199
|
+
}) & {
|
|
186
200
|
code?: string;
|
|
187
201
|
recoveryCode?: string;
|
|
188
202
|
}): Promise<StepUpResult>;
|
|
@@ -195,6 +209,13 @@ export declare class AccountAuthApi {
|
|
|
195
209
|
changeEmail(newEmail: string): Promise<EmailChangeResult>;
|
|
196
210
|
/** Sends the verification email again. */
|
|
197
211
|
resendVerification(): Promise<ResendVerificationResult>;
|
|
212
|
+
/**
|
|
213
|
+
* Emails a 6-digit verification code to the account address (0.14.0+; valid 15 minutes, once). Confirm it with
|
|
214
|
+
* verifyEmailCode: nothing is signed out, and an account from `signup` gets its trial.
|
|
215
|
+
*/
|
|
216
|
+
requestEmailCode(): Promise<EmailCodeResult>;
|
|
217
|
+
/** Verifies the email with the 6-digit code (0.14.0+). A wrong code is 400 `token_invalid`; 5 wrong attempts end it. */
|
|
218
|
+
verifyEmailCode(code: string): Promise<EmailCodeResult>;
|
|
198
219
|
}
|
|
199
220
|
export declare class OrganizationExportsApi {
|
|
200
221
|
#private;
|
|
@@ -458,6 +479,24 @@ export declare class ShardfluxAccount {
|
|
|
458
479
|
sendFeedback(params: AccountFeedbackParams): Promise<FeedbackReceipt>;
|
|
459
480
|
/** Raw access to any /v1 endpoint with the session's authentication and the SDK's error handling. */
|
|
460
481
|
request<T>(method: string, path: string, init?: RequestOptions): Promise<T>;
|
|
482
|
+
/**
|
|
483
|
+
* Signs up from an agent with no browser or email round-trip (0.14.0+); `account` holds the new session.
|
|
484
|
+
*
|
|
485
|
+
* - `codexIdToken` (from codexIdentityProof()): the email is verified by the Codex login's ChatGPT account. Signs in
|
|
486
|
+
* to the account of that identity or email when there is one (`result.created` false), else creates it.
|
|
487
|
+
* `result.status` `mfa_required`: complete with `account.auth.completeMfa({ code })`.
|
|
488
|
+
* - `email` only: creates an unverified account. `result.access` `provisional`: it works now on the default plan,
|
|
489
|
+
* and the trial comes with `auth.requestEmailCode()` + `auth.verifyEmailCode(code)`; `verification_required`:
|
|
490
|
+
* verify first. An address that has an account is 409 `conflict` (`details.reason` `email_registered`).
|
|
491
|
+
*/
|
|
492
|
+
static signup(params: PreSession<{
|
|
493
|
+
email?: string;
|
|
494
|
+
displayName?: string;
|
|
495
|
+
codexIdToken?: string;
|
|
496
|
+
}>): Promise<{
|
|
497
|
+
account: ShardfluxAccount;
|
|
498
|
+
result: SignupResult;
|
|
499
|
+
}>;
|
|
461
500
|
/** Creates an account; a verification link is emailed (`{ status: 'accepted' }` also when the email is taken). */
|
|
462
501
|
static register(params: PreSession<{
|
|
463
502
|
email: string;
|
package/dist/account.js
CHANGED
|
@@ -149,11 +149,12 @@ export class AccountAuthApi {
|
|
|
149
149
|
return json(this.#core, 'DELETE', `/v1/auth/sessions/${enc(sessionId)}`);
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
|
-
* Confirms the password
|
|
152
|
+
* Confirms the password, or a Codex identity proof of the Codex login linked to the account (0.14.0+:
|
|
153
|
+
* `codexIdToken` from codexIdentityProof()), plus a code when MFA is on, for the calls that need a recent check (403
|
|
153
154
|
* `step_up_required`). Rotates the session token.
|
|
154
155
|
*/
|
|
155
156
|
async stepUp(params) {
|
|
156
|
-
const body = { password: params.password };
|
|
157
|
+
const body = 'password' in params ? { password: params.password } : { codex_id_token: params.codexIdToken };
|
|
157
158
|
if (params.code !== undefined)
|
|
158
159
|
body.code = params.code;
|
|
159
160
|
if (params.recoveryCode !== undefined)
|
|
@@ -173,6 +174,17 @@ export class AccountAuthApi {
|
|
|
173
174
|
resendVerification() {
|
|
174
175
|
return json(this.#core, 'POST', '/v1/auth/verify-email/resend');
|
|
175
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Emails a 6-digit verification code to the account address (0.14.0+; valid 15 minutes, once). Confirm it with
|
|
179
|
+
* verifyEmailCode: nothing is signed out, and an account from `signup` gets its trial.
|
|
180
|
+
*/
|
|
181
|
+
requestEmailCode() {
|
|
182
|
+
return json(this.#core, 'POST', '/v1/auth/verify-email/code', { json: {} });
|
|
183
|
+
}
|
|
184
|
+
/** Verifies the email with the 6-digit code (0.14.0+). A wrong code is 400 `token_invalid`; 5 wrong attempts end it. */
|
|
185
|
+
verifyEmailCode(code) {
|
|
186
|
+
return json(this.#core, 'POST', '/v1/auth/verify-email/code', { json: { code: code.replace(/\s+/g, '') } });
|
|
187
|
+
}
|
|
176
188
|
}
|
|
177
189
|
export class OrganizationExportsApi {
|
|
178
190
|
#core;
|
|
@@ -603,6 +615,28 @@ export class ShardfluxAccount {
|
|
|
603
615
|
const account = ShardfluxAccount.#client(opts);
|
|
604
616
|
return account.#ctx.http.json('POST', path, body === undefined ? {} : { json: body });
|
|
605
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Signs up from an agent with no browser or email round-trip (0.14.0+); `account` holds the new session.
|
|
620
|
+
*
|
|
621
|
+
* - `codexIdToken` (from codexIdentityProof()): the email is verified by the Codex login's ChatGPT account. Signs in
|
|
622
|
+
* to the account of that identity or email when there is one (`result.created` false), else creates it.
|
|
623
|
+
* `result.status` `mfa_required`: complete with `account.auth.completeMfa({ code })`.
|
|
624
|
+
* - `email` only: creates an unverified account. `result.access` `provisional`: it works now on the default plan,
|
|
625
|
+
* and the trial comes with `auth.requestEmailCode()` + `auth.verifyEmailCode(code)`; `verification_required`:
|
|
626
|
+
* verify first. An address that has an account is 409 `conflict` (`details.reason` `email_registered`).
|
|
627
|
+
*/
|
|
628
|
+
static async signup(params) {
|
|
629
|
+
const { email, displayName, codexIdToken, ...opts } = params;
|
|
630
|
+
const account = ShardfluxAccount.#client(opts);
|
|
631
|
+
const body = {
|
|
632
|
+
...(email === undefined ? {} : { email }),
|
|
633
|
+
...(displayName === undefined ? {} : { display_name: displayName }),
|
|
634
|
+
...(codexIdToken === undefined ? {} : { codex_id_token: codexIdToken }),
|
|
635
|
+
};
|
|
636
|
+
const result = await account.#ctx.http.json('POST', '/v1/auth/signup', { json: body });
|
|
637
|
+
await account.#adopt(result);
|
|
638
|
+
return { account, result };
|
|
639
|
+
}
|
|
606
640
|
/** Creates an account; a verification link is emailed (`{ status: 'accepted' }` also when the email is taken). */
|
|
607
641
|
static register(params) {
|
|
608
642
|
const { email, password, displayName, ...opts } = params;
|
package/dist/cell.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ import type { RequestOptions } from './http.js';
|
|
|
30
30
|
import type { ProgressListener } from './progress.js';
|
|
31
31
|
import type { ToolTokenManager } from './tokens.js';
|
|
32
32
|
type S = components['schemas'];
|
|
33
|
+
/**
|
|
34
|
+
* How much memory a command needs at its start (0.14.0; `resource_hint` of an exec start): `heavy` grows an elastic
|
|
35
|
+
* workspace's memory before the command starts (a build, a test suite, a package install), `light` starts it at once,
|
|
36
|
+
* `auto` (the default) decides from the command. A fixed workspace has its memory already.
|
|
37
|
+
*/
|
|
38
|
+
export type ResourceHint = NonNullable<S['ExecStartRequest']['resource_hint']>;
|
|
33
39
|
export type ExecStartRequest = S['ExecStartRequest'];
|
|
34
40
|
export type ExecSession = S['ExecSession'];
|
|
35
41
|
/**
|
|
@@ -174,8 +180,9 @@ export interface CellClientOptions {
|
|
|
174
180
|
sleep?: (ms: number) => Promise<void>;
|
|
175
181
|
/**
|
|
176
182
|
* Wakes a suspended workspace (resume, or join the active resume/open) within `timeoutMs` and resolves once it runs;
|
|
177
|
-
* resolves `false` when there was nothing to wake (the API already reports it running)
|
|
178
|
-
*
|
|
183
|
+
* resolves `false` when there was nothing to wake (the API already reports it running): the refused call is then
|
|
184
|
+
* retried with a current token, since the transition that refused it ended meanwhile (0.13.1+). It throws when the
|
|
185
|
+
* wake fails (OperationFailedError) or outlasts `timeoutMs` (OperationTimeoutError).
|
|
179
186
|
* Workspace.cell() supplies `workspace.wake()`. A call refused with `workspace_not_running` wakes the workspace and is
|
|
180
187
|
* retried; a refused call was never executed, so the retry cannot duplicate it. `null` surfaces
|
|
181
188
|
* the refusal instead. A wake that put a new token into this client's manager (the held resume;
|
|
@@ -292,6 +299,14 @@ export interface RunOptions {
|
|
|
292
299
|
burstVcpus?: number;
|
|
293
300
|
/** Burst VM memory in MiB (512-65536; default the host's, 8192, or the plan's ceiling when lower). */
|
|
294
301
|
burstMemoryMib?: number;
|
|
302
|
+
/**
|
|
303
|
+
* How much memory the command needs at its start (0.14.0; sent as `resource_hint`, omitted when unset): `'heavy'`
|
|
304
|
+
* grows an elastic workspace's memory before the command starts, so a build, test suite or install sizes its heap and
|
|
305
|
+
* workers from the memory it gets; `'light'` starts it at once and the workspace grows while it runs; `'auto'` (the
|
|
306
|
+
* default) decides from the command (package installers, test runners, compilers, type checkers and bundlers are
|
|
307
|
+
* heavy). A value outside these is ShardfluxApiError 422 `validation_failed` (details.field `resource_hint`).
|
|
308
|
+
*/
|
|
309
|
+
resourceHint?: ResourceHint;
|
|
295
310
|
}
|
|
296
311
|
/** The API error of a burst's recorded failure (`burst.error` of its session). Internal (the agent tools use it too). */
|
|
297
312
|
export declare function burstFailure(e: BurstError): ShardfluxApiError;
|
|
@@ -320,8 +335,10 @@ export declare class CellClient {
|
|
|
320
335
|
* One authorized request. Refreshes the token once on stale_epoch / 401. Lifecycle transitions,
|
|
321
336
|
* bounded in total by `transitionTimeoutMs`: a call refused with `workspace_busy` is retried after `Retry-After`; one
|
|
322
337
|
* refused with `workspace_not_running` (or whose token cannot be minted because the workspace is not running) wakes
|
|
323
|
-
* the workspace through `wake`, given the time left, and is retried with a fresh token, at most 3 wakes per call.
|
|
324
|
-
*
|
|
338
|
+
* the workspace through `wake`, given the time left, and is retried with a fresh token, at most 3 wakes per call. A
|
|
339
|
+
* wake that finds the workspace already running (`false`) is retried the same way: the transition that refused the
|
|
340
|
+
* call ended meanwhile (0.13.1+). Refused calls were never executed, so retrying is safe. When the budget is spent
|
|
341
|
+
* the refusal surfaces.
|
|
325
342
|
*/
|
|
326
343
|
request(method: string, path: string, init?: RequestOptions & TransitionOptions): Promise<Response>;
|
|
327
344
|
/**
|