@shardflux/sdk 0.6.0 → 0.6.2
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 +27 -1
- package/README.md +29 -9
- package/dist/client.d.ts +8 -2
- package/dist/client.js +10 -3
- package/dist/errors.d.ts +13 -1
- package/dist/errors.js +19 -2
- package/dist/generated/app-api.d.ts +2673 -181
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/progress.d.ts +18 -6
- package/dist/progress.js +25 -9
- package/dist/workspace.d.ts +3 -1
- package/dist/workspace.js +3 -1
- package/package.json +1 -1
package/dist/http.d.ts
CHANGED
package/dist/http.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { ShardfluxApiError, ShardfluxProtocolError, isErrorBody } from "./errors.js";
|
|
8
8
|
import { describeFailure } from "./progress.js";
|
|
9
|
-
export const SDK_VERSION = '0.6.
|
|
9
|
+
export const SDK_VERSION = '0.6.2';
|
|
10
10
|
export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
11
11
|
/**
|
|
12
12
|
* The fetch the SDK uses when none is given. On runtimes whose bundled undici is 8.x (Node 26) it sends
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ export interface LifecycleOptions {
|
|
|
20
20
|
/**
|
|
21
21
|
* Default (false): resolve once the change is requested; the returned operation is usually still `queued`.
|
|
22
22
|
* `true` or WaitOptions: resolve once it has finished (the operation `succeeded`); throws OperationFailedError when it
|
|
23
|
-
* fails and OperationTimeoutError after `timeoutMs` (default 5 minutes; the operation continues server side
|
|
23
|
+
* fails and OperationTimeoutError after `timeoutMs` (default 5 minutes; the operation continues server side, a start
|
|
24
|
+
* waiting for capacity until its deadline, then it fails with `capacity_unavailable`, retryable).
|
|
24
25
|
*/
|
|
25
26
|
wait?: boolean | WaitOptions;
|
|
26
27
|
/** Progress events of this call (phases, retries, and `done` with its timing). */
|
package/dist/progress.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export interface ServerTiming {
|
|
|
68
68
|
startPath: string | null;
|
|
69
69
|
/** `result.warm_fallback`: why a start that could be warm booted instead. */
|
|
70
70
|
warmFallback: string | null;
|
|
71
|
-
/** `result.resume_path`: `local_cache` or `download` (the checkpoint had to be fetched first). */
|
|
71
|
+
/** `result.resume_path`: `local_cache`, `prestaged` (copied to this host ahead of the resume, contracts §23) or `download` (the checkpoint had to be fetched first). */
|
|
72
72
|
resumePath: string | null;
|
|
73
73
|
/** `result.boot_to_ready_ms`: VM start until the guest agent answered. */
|
|
74
74
|
bootToReadyMs: number | null;
|
|
@@ -104,6 +104,8 @@ interface EventBase {
|
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* - `phase`: a phase began (live progress: "capacity_pending: no_ready_host"). `tool` phases come from tool calls.
|
|
107
|
+
* A `capacity_pending` phase carries `deadlineAt` (0.6.2+, when the API reports it): when the start gives up waiting
|
|
108
|
+
* for a host and fails with `capacity_unavailable` (retryable; nothing was started).
|
|
107
109
|
* - `retry`: a request is retried after a transient failure (also emitted for tool calls, action `tool`).
|
|
108
110
|
* - `done`: the call ended, successfully or not, with its full timing.
|
|
109
111
|
*/
|
|
@@ -111,6 +113,7 @@ export type ProgressEvent = (EventBase & {
|
|
|
111
113
|
type: 'phase';
|
|
112
114
|
phase: LifecyclePhase;
|
|
113
115
|
reason: string | null;
|
|
116
|
+
deadlineAt?: string;
|
|
114
117
|
}) | (EventBase & {
|
|
115
118
|
type: 'retry';
|
|
116
119
|
retry: RetryRecord;
|
|
@@ -123,6 +126,12 @@ export type ProgressListener = (event: ProgressEvent) => void;
|
|
|
123
126
|
export declare function emitTo(listeners: ReadonlyArray<ProgressListener | undefined>, event: ProgressEvent): void;
|
|
124
127
|
/** Combines listeners (client-level and per call) into one; undefined when there are none. */
|
|
125
128
|
export declare function combineListeners(...listeners: Array<ProgressListener | undefined>): ProgressListener | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* When a start waiting in `capacity_pending` gives up: the operation's `error.details.deadline_at` (RFC 3339). A start no
|
|
131
|
+
* host admits by then fails with `capacity_unavailable` (retryable; nothing was started). Null in any other state, or
|
|
132
|
+
* from an API that does not report it.
|
|
133
|
+
*/
|
|
134
|
+
export declare function capacityDeadlineOf(op: Operation): string | null;
|
|
126
135
|
/** Server timing from an operation as GET /v1/operations/{id} returns it. */
|
|
127
136
|
export declare function serverTiming(op: Operation): ServerTiming;
|
|
128
137
|
/** Why a request failed, in one short phrase (for retry records). */
|
|
@@ -139,8 +148,11 @@ export declare class Trace {
|
|
|
139
148
|
/** Milliseconds since the call began. */
|
|
140
149
|
now(): number;
|
|
141
150
|
get finished(): LifecycleTiming | null;
|
|
142
|
-
/**
|
|
143
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase.
|
|
153
|
+
* `deadlineAt`: when a `capacity_pending` start gives up (added to the event only).
|
|
154
|
+
*/
|
|
155
|
+
phase(phase: LifecyclePhase, reason?: string | null, deadlineAt?: string | null): void;
|
|
144
156
|
/** Runs `fn` as a phase that may overlap others (open() reads the view and issues the token together). */
|
|
145
157
|
span<T>(phase: LifecyclePhase, fn: () => Promise<T>, reason?: string | null): Promise<T>;
|
|
146
158
|
/** Records an operation snapshot: its ids, the observed state as a phase, and the server timing. */
|
|
@@ -157,9 +169,9 @@ export declare function traced<T>(trace: Trace, fn: () => Promise<T>): Promise<T
|
|
|
157
169
|
* A human-readable account of a timing, for logs and bug reports:
|
|
158
170
|
*
|
|
159
171
|
* open 34.18 s, succeeded (workspace <id>, operation <id>)
|
|
160
|
-
* client: request 20.01 s (held) → capacity_pending 13.52 s (no_ready_host) → running
|
|
161
|
-
* server: queued 33.40 s, ran
|
|
162
|
-
* outside the server:
|
|
172
|
+
* client: request 20.01 s (held) → capacity_pending 13.52 s (no_ready_host) → running 590 ms → view 42 ms ∥ token 61 ms
|
|
173
|
+
* server: queued 33.40 s, ran 620 ms, total 34.02 s; start warm, boot to ready 79 ms
|
|
174
|
+
* outside the server: 161 ms
|
|
163
175
|
* retries: 1 (POST /v1/workspaces/open, HTTP 503 unavailable, after 200 ms)
|
|
164
176
|
*/
|
|
165
177
|
export declare function formatTiming(t: LifecycleTiming): string;
|
package/dist/progress.js
CHANGED
|
@@ -30,6 +30,17 @@ function diffMs(from, to) {
|
|
|
30
30
|
}
|
|
31
31
|
const str = (v) => (typeof v === 'string' && v.length > 0 ? v : null);
|
|
32
32
|
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
|
|
33
|
+
/**
|
|
34
|
+
* When a start waiting in `capacity_pending` gives up: the operation's `error.details.deadline_at` (RFC 3339). A start no
|
|
35
|
+
* host admits by then fails with `capacity_unavailable` (retryable; nothing was started). Null in any other state, or
|
|
36
|
+
* from an API that does not report it.
|
|
37
|
+
*/
|
|
38
|
+
export function capacityDeadlineOf(op) {
|
|
39
|
+
if (op.state !== 'capacity_pending')
|
|
40
|
+
return null;
|
|
41
|
+
const details = op.error?.details;
|
|
42
|
+
return typeof details === 'object' && details !== null ? str(details.deadline_at) : null;
|
|
43
|
+
}
|
|
33
44
|
/** Server timing from an operation as GET /v1/operations/{id} returns it. */
|
|
34
45
|
export function serverTiming(op) {
|
|
35
46
|
const r = (op.result ?? {});
|
|
@@ -113,8 +124,11 @@ export class Trace {
|
|
|
113
124
|
this.#current.durationMs = round(at - this.#current.startMs);
|
|
114
125
|
this.#current = null;
|
|
115
126
|
}
|
|
116
|
-
/**
|
|
117
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase.
|
|
129
|
+
* `deadlineAt`: when a `capacity_pending` start gives up (added to the event only).
|
|
130
|
+
*/
|
|
131
|
+
phase(phase, reason = null, deadlineAt = null) {
|
|
118
132
|
if (this.#timing)
|
|
119
133
|
return;
|
|
120
134
|
if (this.#current && this.#current.phase === phase && this.#current.reason === reason)
|
|
@@ -123,7 +137,7 @@ export class Trace {
|
|
|
123
137
|
this.#close(at);
|
|
124
138
|
this.#current = { phase, reason, operationId: this.operationId, startMs: at, durationMs: 0 };
|
|
125
139
|
this.#phases.push(this.#current);
|
|
126
|
-
this.#emit({ ...this.#base(), type: 'phase', phase, reason });
|
|
140
|
+
this.#emit({ ...this.#base(), type: 'phase', phase, reason, ...(deadlineAt ? { deadlineAt } : {}) });
|
|
127
141
|
}
|
|
128
142
|
/** Runs `fn` as a phase that may overlap others (open() reads the view and issues the token together). */
|
|
129
143
|
async span(phase, fn, reason = null) {
|
|
@@ -145,7 +159,7 @@ export class Trace {
|
|
|
145
159
|
this.workspaceId ??= op.workspace_id;
|
|
146
160
|
this.#server = serverTiming(op);
|
|
147
161
|
if (!TERMINAL.has(op.state))
|
|
148
|
-
this.phase(op.state, op.state_reason ?? null);
|
|
162
|
+
this.phase(op.state, op.state_reason ?? null, capacityDeadlineOf(op));
|
|
149
163
|
}
|
|
150
164
|
retry(r) {
|
|
151
165
|
if (this.#timing)
|
|
@@ -202,9 +216,9 @@ const fmt = (ms) => (ms === null ? '?' : ms < 1000 ? `${Math.round(ms)} ms` : `$
|
|
|
202
216
|
* A human-readable account of a timing, for logs and bug reports:
|
|
203
217
|
*
|
|
204
218
|
* open 34.18 s, succeeded (workspace <id>, operation <id>)
|
|
205
|
-
* client: request 20.01 s (held) → capacity_pending 13.52 s (no_ready_host) → running
|
|
206
|
-
* server: queued 33.40 s, ran
|
|
207
|
-
* outside the server:
|
|
219
|
+
* client: request 20.01 s (held) → capacity_pending 13.52 s (no_ready_host) → running 590 ms → view 42 ms ∥ token 61 ms
|
|
220
|
+
* server: queued 33.40 s, ran 620 ms, total 34.02 s; start warm, boot to ready 79 ms
|
|
221
|
+
* outside the server: 161 ms
|
|
208
222
|
* retries: 1 (POST /v1/workspaces/open, HTTP 503 unavailable, after 200 ms)
|
|
209
223
|
*/
|
|
210
224
|
export function formatTiming(t) {
|
|
@@ -213,8 +227,10 @@ export function formatTiming(t) {
|
|
|
213
227
|
let client = '';
|
|
214
228
|
t.phases.forEach((p, i) => {
|
|
215
229
|
const prev = t.phases[i - 1];
|
|
216
|
-
// A phase that starts before the previous one ended ran alongside it (view and token at the end of open()).
|
|
217
|
-
|
|
230
|
+
// A phase that starts before the previous one ended ran alongside it (view and token at the end of open()). Times
|
|
231
|
+
// are 0.1 ms values, so a phase that follows another can appear to start a float error early (1000.2 + 300.1 >
|
|
232
|
+
// 1300.3): overlap must exceed that.
|
|
233
|
+
const sep = i === 0 ? '' : prev && p.startMs < prev.startMs + prev.durationMs - 0.05 ? ' ∥ ' : ' → ';
|
|
218
234
|
client += `${sep}${p.phase} ${fmt(p.durationMs)}${p.reason ? ` (${p.reason})` : ''}`;
|
|
219
235
|
});
|
|
220
236
|
if (client)
|
package/dist/workspace.d.ts
CHANGED
|
@@ -154,7 +154,9 @@ export declare class Workspace {
|
|
|
154
154
|
* (`timeoutMs`, default 120 000 ms) covers every step. Throws OperationFailedError when the resume/open fails,
|
|
155
155
|
* OperationTimeoutError (naming the operation, its state and reason) when it is still queued, running or
|
|
156
156
|
* capacity_pending at the deadline, and any other API error (a conflict other than already_running /
|
|
157
|
-
* operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`.
|
|
157
|
+
* operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`. A resume no
|
|
158
|
+
* host admits within 15 minutes fails with `capacity_unavailable` (OperationFailedError, `retryable`: the workspace
|
|
159
|
+
* stays suspended with its state; try again later).
|
|
158
160
|
*/
|
|
159
161
|
wake(opts?: WakeOptions): Promise<boolean>;
|
|
160
162
|
/** Tools granted by the most recent token (null before one was issued). */
|
package/dist/workspace.js
CHANGED
|
@@ -217,7 +217,9 @@ export class Workspace {
|
|
|
217
217
|
* (`timeoutMs`, default 120 000 ms) covers every step. Throws OperationFailedError when the resume/open fails,
|
|
218
218
|
* OperationTimeoutError (naming the operation, its state and reason) when it is still queued, running or
|
|
219
219
|
* capacity_pending at the deadline, and any other API error (a conflict other than already_running /
|
|
220
|
-
* operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`.
|
|
220
|
+
* operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`. A resume no
|
|
221
|
+
* host admits within 15 minutes fails with `capacity_unavailable` (OperationFailedError, `retryable`: the workspace
|
|
222
|
+
* stays suspended with its state; try again later).
|
|
221
223
|
*/
|
|
222
224
|
async wake(opts = {}) {
|
|
223
225
|
const trace = new Trace('wake', combineListeners(this.#ctx.onProgress, this.#tracked(opts).onProgress), { workspaceId: this.id });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shardflux/sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shardflux TypeScript SDK: open persistent agent workspaces by key and give your agent workspace tools (exec, files, processes, PTY, git, browser).",
|
|
6
6
|
"license": "Apache-2.0",
|