@shardflux/sdk 0.11.0 → 0.12.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 +68 -27
- package/README.md +123 -61
- package/dist/account.d.ts +3 -3
- package/dist/account.js +2 -2
- package/dist/cell.d.ts +36 -21
- package/dist/cell.js +49 -20
- package/dist/client.d.ts +56 -35
- package/dist/client.js +89 -21
- package/dist/egress.d.ts +3 -3
- package/dist/errors.d.ts +51 -22
- package/dist/errors.js +61 -16
- package/dist/executions.d.ts +2 -2
- package/dist/feedback.d.ts +3 -3
- package/dist/feedback.js +1 -1
- package/dist/generated/app-api.d.ts +1579 -30
- package/dist/generated/cell-api.d.ts +66 -0
- package/dist/http.d.ts +7 -11
- package/dist/http.js +32 -21
- package/dist/index.d.ts +10 -5
- package/dist/index.js +4 -2
- package/dist/lifecycle.d.ts +19 -2
- package/dist/lifecycle.js +9 -2
- package/dist/progress.d.ts +83 -22
- package/dist/progress.js +71 -10
- package/dist/tar.d.ts +1 -1
- package/dist/tar.js +1 -1
- package/dist/template-file.d.ts +1 -1
- package/dist/template-file.js +1 -1
- package/dist/templates.d.ts +21 -21
- package/dist/templates.js +8 -8
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +4 -4
- package/dist/version-check.d.ts +1 -1
- package/dist/volumes.d.ts +1 -1
- package/dist/workspace.d.ts +42 -27
- package/dist/workspace.js +45 -22
- package/package.json +4 -1
|
@@ -170,6 +170,29 @@ export interface paths {
|
|
|
170
170
|
patch?: never;
|
|
171
171
|
trace?: never;
|
|
172
172
|
};
|
|
173
|
+
"/v1/workspaces/{workspace_id}/exec/{session_id}/stdin": {
|
|
174
|
+
parameters: {
|
|
175
|
+
query?: never;
|
|
176
|
+
header?: never;
|
|
177
|
+
path: {
|
|
178
|
+
workspace_id: components["parameters"]["WorkspaceId"];
|
|
179
|
+
session_id: components["parameters"]["SessionId"];
|
|
180
|
+
};
|
|
181
|
+
cookie?: never;
|
|
182
|
+
};
|
|
183
|
+
get?: never;
|
|
184
|
+
put?: never;
|
|
185
|
+
/**
|
|
186
|
+
* Write pipe stdin or send EOF to an exec session
|
|
187
|
+
* @description Requires stdin_open at start (processful only), host exec_stdin and guest exec_stdin.v1. Offset is the total bytes previously accepted (initially 0). A repeated identical last frame is safe; a different stale offset is refused. Writes are bounded: offset in the response may acknowledge only a prefix; continue from that offset. close takes effect only after the whole frame is accepted. The transport writes no input log or payload file; program output and full-state snapshots retain their normal persistence. An ended session refuses input.
|
|
188
|
+
*/
|
|
189
|
+
post: operations["execInput"];
|
|
190
|
+
delete?: never;
|
|
191
|
+
options?: never;
|
|
192
|
+
head?: never;
|
|
193
|
+
patch?: never;
|
|
194
|
+
trace?: never;
|
|
195
|
+
};
|
|
173
196
|
"/v1/workspaces/{workspace_id}/exec/{session_id}/signal": {
|
|
174
197
|
parameters: {
|
|
175
198
|
query?: never;
|
|
@@ -843,6 +866,8 @@ export interface components {
|
|
|
843
866
|
/** @description Absolute working directory; omitted or empty starts in the default (/home/user). A relative path is refused, not resolved: 422 validation_failed, details.reason invalid_cwd, details.field cwd, the message naming the absolute path it likely means. A cwd that is not a directory ends the session failed_to_start (processful) or the execution failed with details.reason exec_failed_to_start (file-first). */
|
|
844
867
|
cwd?: string;
|
|
845
868
|
user?: string;
|
|
869
|
+
/** @description Keep a pipe open for exec stdin writes; processful only, mutually exclusive with stdin. Requires exec_stdin.v1. */
|
|
870
|
+
stdin_open?: boolean;
|
|
846
871
|
/** @description Written to stdin, which is then closed (max 1 MiB decoded). */
|
|
847
872
|
stdin?: string;
|
|
848
873
|
/** Format: int64 */
|
|
@@ -979,6 +1004,19 @@ export interface components {
|
|
|
979
1004
|
};
|
|
980
1005
|
/** @description Signal number (1-64) or name (e.g. "SIGTERM", "TERM"). */
|
|
981
1006
|
SignalValue: number | string;
|
|
1007
|
+
ExecInputRequest: {
|
|
1008
|
+
/** @description At most 64 KiB decoded. */
|
|
1009
|
+
data?: string;
|
|
1010
|
+
/** Format: int64 */
|
|
1011
|
+
offset: number;
|
|
1012
|
+
/** @default false */
|
|
1013
|
+
close?: boolean;
|
|
1014
|
+
};
|
|
1015
|
+
ExecInputResult: {
|
|
1016
|
+
/** Format: int64 */
|
|
1017
|
+
offset: number;
|
|
1018
|
+
closed: boolean;
|
|
1019
|
+
};
|
|
982
1020
|
SignalRequest: {
|
|
983
1021
|
signal: components["schemas"]["SignalValue"];
|
|
984
1022
|
/**
|
|
@@ -1607,6 +1645,34 @@ export interface operations {
|
|
|
1607
1645
|
default: components["responses"]["Error"];
|
|
1608
1646
|
};
|
|
1609
1647
|
};
|
|
1648
|
+
execInput: {
|
|
1649
|
+
parameters: {
|
|
1650
|
+
query?: never;
|
|
1651
|
+
header?: never;
|
|
1652
|
+
path: {
|
|
1653
|
+
workspace_id: components["parameters"]["WorkspaceId"];
|
|
1654
|
+
session_id: components["parameters"]["SessionId"];
|
|
1655
|
+
};
|
|
1656
|
+
cookie?: never;
|
|
1657
|
+
};
|
|
1658
|
+
requestBody: {
|
|
1659
|
+
content: {
|
|
1660
|
+
"application/json": components["schemas"]["ExecInputRequest"];
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1663
|
+
responses: {
|
|
1664
|
+
/** @description Acknowledged input offset and EOF state. */
|
|
1665
|
+
200: {
|
|
1666
|
+
headers: {
|
|
1667
|
+
[name: string]: unknown;
|
|
1668
|
+
};
|
|
1669
|
+
content: {
|
|
1670
|
+
"application/json": components["schemas"]["ExecInputResult"];
|
|
1671
|
+
};
|
|
1672
|
+
};
|
|
1673
|
+
default: components["responses"]["Error"];
|
|
1674
|
+
};
|
|
1675
|
+
};
|
|
1610
1676
|
execSignal: {
|
|
1611
1677
|
parameters: {
|
|
1612
1678
|
query?: never;
|
package/dist/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RetryRecord } from './progress.js';
|
|
2
|
-
export declare const SDK_VERSION = "0.
|
|
2
|
+
export declare const SDK_VERSION = "0.12.0";
|
|
3
3
|
export interface RequestOptions {
|
|
4
4
|
query?: Record<string, string | number | boolean | undefined | null>;
|
|
5
5
|
json?: unknown;
|
|
@@ -29,17 +29,13 @@ export interface HttpOptions {
|
|
|
29
29
|
}
|
|
30
30
|
export declare const defaultSleep: (ms: number) => Promise<void>;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* with 0.2-12 s gaps; Node 22 / undici 6.28 max 351 ms; Node 26 with `Connection: close` max 435 ms; node:http2 on the
|
|
36
|
-
* same load balancer and gaps max 193 ms, and the load balancer's idle timeout is 3600 s, so it is not an idle-timeout
|
|
37
|
-
* interaction). The CLI and MCP server do the same. Other runtimes keep the runtime's keep-alive pooling.
|
|
38
|
-
* `SHARDFLUX_HTTP_KEEPALIVE=1` disables the workaround.
|
|
32
|
+
* Reuses TLS connections. Node 26's bundled undici 8.9 can stall reused connections: use pinned undici with a
|
|
33
|
+
* private HTTP/1.1-only dispatcher. Other runtimes retain native fetch. SHARDFLUX_HTTP_KEEPALIVE=0 forces close
|
|
34
|
+
* for diagnosis; =1 retains the historical opt-in to native pooling. An explicitly supplied fetch is untouched.
|
|
39
35
|
*/
|
|
40
36
|
export declare function defaultFetch(env?: Record<string, string | undefined> | undefined, undiciVersion?: string | undefined): typeof fetch;
|
|
41
37
|
export declare function buildUrl(base: string, path: string, query?: RequestOptions['query']): string;
|
|
42
|
-
/** `X-Tree-Revision` (file-first workspaces
|
|
38
|
+
/** `X-Tree-Revision` (file-first workspaces): a non-negative integer, else null. */
|
|
43
39
|
export declare function treeRevisionOf(headers: Headers): number | null;
|
|
44
40
|
/** Turns a non-2xx response into a ShardfluxApiError (or a protocol error for undocumented bodies). */
|
|
45
41
|
export declare function errorFrom(res: Response, source: 'api' | 'cell'): Promise<Error>;
|
|
@@ -61,10 +57,10 @@ export declare class HttpClient {
|
|
|
61
57
|
body: T;
|
|
62
58
|
}>;
|
|
63
59
|
}
|
|
64
|
-
/** Longest server-side wait the SDK asks for (
|
|
60
|
+
/** Longest server-side wait the SDK asks for (servers cap `Prefer: wait` at 20 s). */
|
|
65
61
|
export declare const SERVER_WAIT_MAX_S = 20;
|
|
66
62
|
/**
|
|
67
|
-
* One bounded-wait poll
|
|
63
|
+
* One bounded-wait poll: GET `path` with `Prefer: wait=<s>` when `waitS` >= 1. `applied` is true only
|
|
68
64
|
* when the server says it waited (Preference-Applied); otherwise the caller keeps its own backoff.
|
|
69
65
|
*/
|
|
70
66
|
export declare function pollWithWait<T>(http: HttpClient, path: string, authorization: string, waitS: number, signal?: AbortSignal, onRetry?: RequestOptions['onRetry']): Promise<{
|
package/dist/http.js
CHANGED
|
@@ -8,26 +8,37 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { ShardfluxApiError, ShardfluxProtocolError, apiError, isErrorBody } from "./errors.js";
|
|
10
10
|
import { describeFailure } from "./progress.js";
|
|
11
|
-
export const SDK_VERSION = '0.
|
|
11
|
+
export const SDK_VERSION = '0.12.0';
|
|
12
12
|
export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
13
|
+
/** One private HTTP/1.1 pool on Node 26+, shared by SDK clients. No global dispatcher changes. */
|
|
14
|
+
let nodeFetch;
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* with 0.2-12 s gaps; Node 22 / undici 6.28 max 351 ms; Node 26 with `Connection: close` max 435 ms; node:http2 on the
|
|
18
|
-
* same load balancer and gaps max 193 ms, and the load balancer's idle timeout is 3600 s, so it is not an idle-timeout
|
|
19
|
-
* interaction). The CLI and MCP server do the same. Other runtimes keep the runtime's keep-alive pooling.
|
|
20
|
-
* `SHARDFLUX_HTTP_KEEPALIVE=1` disables the workaround.
|
|
16
|
+
* Reuses TLS connections. Node 26's bundled undici 8.9 can stall reused connections: use pinned undici with a
|
|
17
|
+
* private HTTP/1.1-only dispatcher. Other runtimes retain native fetch. SHARDFLUX_HTTP_KEEPALIVE=0 forces close
|
|
18
|
+
* for diagnosis; =1 retains the historical opt-in to native pooling. An explicitly supplied fetch is untouched.
|
|
21
19
|
*/
|
|
22
20
|
export function defaultFetch(env = globalThis.process?.env, undiciVersion = globalThis.process?.versions?.undici) {
|
|
21
|
+
// Why a fresh connection on Node 26 (its bundled HTTP client, measured): docs/progress/startup-latency.md.
|
|
23
22
|
const base = (input, init) => fetch(input, init);
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (env?.SHARDFLUX_HTTP_KEEPALIVE === '0')
|
|
24
|
+
return (input, init) => {
|
|
25
|
+
const headers = new Headers(init?.headers);
|
|
26
|
+
headers.set('connection', 'close');
|
|
27
|
+
return base(input, { ...init, headers });
|
|
28
|
+
};
|
|
29
|
+
if (Number((undiciVersion ?? '').split('.')[0]) < 8 || !undiciVersion || env?.SHARDFLUX_HTTP_KEEPALIVE === '1')
|
|
26
30
|
return base;
|
|
27
|
-
return (input, init) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
return async (input, init) => {
|
|
32
|
+
nodeFetch ??= import('undici').then(({ Agent, fetch: pooledFetch }) => {
|
|
33
|
+
const dispatcher = new Agent({ allowH2: false, pipelining: 1 });
|
|
34
|
+
return async (input, init) => {
|
|
35
|
+
const response = await pooledFetch(input, { ...init, dispatcher });
|
|
36
|
+
// Web-standard runtime shape; undici and DOM iterator declarations differ.
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Required by consumers compiling with lib.dom instead of only Node types.
|
|
38
|
+
return response;
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
return (await nodeFetch)(input, init);
|
|
31
42
|
};
|
|
32
43
|
}
|
|
33
44
|
export function buildUrl(base, path, query) {
|
|
@@ -45,7 +56,7 @@ function retryAfterSeconds(res) {
|
|
|
45
56
|
const n = Number(v);
|
|
46
57
|
return Number.isFinite(n) && n >= 0 ? n : undefined;
|
|
47
58
|
}
|
|
48
|
-
/** `X-Tree-Revision` (file-first workspaces
|
|
59
|
+
/** `X-Tree-Revision` (file-first workspaces): a non-negative integer, else null. */
|
|
49
60
|
export function treeRevisionOf(headers) {
|
|
50
61
|
const v = headers.get('x-tree-revision');
|
|
51
62
|
if (v === null || !/^\d{1,19}$/.test(v))
|
|
@@ -65,7 +76,7 @@ export async function errorFrom(res, source) {
|
|
|
65
76
|
}
|
|
66
77
|
if (isErrorBody(parsed))
|
|
67
78
|
return apiError(res.status, parsed, source, retryAfterSeconds(res), treeRevisionOf(res.headers) ?? undefined);
|
|
68
|
-
return new ShardfluxProtocolError(`HTTP ${res.status} from ${source === 'api' ? 'the Shardflux API' : 'the cell gateway'} without an error body`, res.status);
|
|
79
|
+
return new ShardfluxProtocolError(`HTTP ${res.status} from ${source === 'api' ? 'the Shardflux API' : 'the cell gateway'} without an error body`, res.status, source);
|
|
69
80
|
}
|
|
70
81
|
export class HttpClient {
|
|
71
82
|
opts;
|
|
@@ -145,7 +156,7 @@ export class HttpClient {
|
|
|
145
156
|
return JSON.parse(text);
|
|
146
157
|
}
|
|
147
158
|
catch {
|
|
148
|
-
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status);
|
|
159
|
+
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status, this.opts.source);
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
162
|
/** Like json() but also returns the status and the response headers (bounded waits read Preference-Applied). */
|
|
@@ -156,7 +167,7 @@ export class HttpClient {
|
|
|
156
167
|
return { status: res.status, headers: res.headers, body: (text.length === 0 ? undefined : JSON.parse(text)) };
|
|
157
168
|
}
|
|
158
169
|
catch {
|
|
159
|
-
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status);
|
|
170
|
+
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status, this.opts.source);
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
/** Like json() but also returns the HTTP status (open() distinguishes 200 from 202). */
|
|
@@ -167,14 +178,14 @@ export class HttpClient {
|
|
|
167
178
|
return { status: res.status, body: (text.length === 0 ? undefined : JSON.parse(text)) };
|
|
168
179
|
}
|
|
169
180
|
catch {
|
|
170
|
-
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status);
|
|
181
|
+
throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status, this.opts.source);
|
|
171
182
|
}
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
|
-
/** Longest server-side wait the SDK asks for (
|
|
185
|
+
/** Longest server-side wait the SDK asks for (servers cap `Prefer: wait` at 20 s). */
|
|
175
186
|
export const SERVER_WAIT_MAX_S = 20;
|
|
176
187
|
/**
|
|
177
|
-
* One bounded-wait poll
|
|
188
|
+
* One bounded-wait poll: GET `path` with `Prefer: wait=<s>` when `waitS` >= 1. `applied` is true only
|
|
178
189
|
* when the server says it waited (Preference-Applied); otherwise the caller keeps its own backoff.
|
|
179
190
|
*/
|
|
180
191
|
export async function pollWithWait(http, path, authorization, waitS, signal, onRetry) {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* const workspace = await cloud.workspaces.open({ key: `${customerId}/${projectId}`, template: 'python-node-browser' });
|
|
7
7
|
* await agent.run({ input: userMessage, tools: workspaceTools(workspace) });
|
|
8
8
|
*
|
|
9
|
-
* File-first workspaces (`mode: 'file_first'
|
|
9
|
+
* File-first workspaces (`mode: 'file_first'`) keep a versioned file tree and run each command as an
|
|
10
10
|
* execution: `await workspace.executions.run(['bash', '-lc', 'pytest -q'])`.
|
|
11
11
|
*
|
|
12
12
|
* Types come from the committed OpenAPI documents: application API
|
|
@@ -16,9 +16,9 @@ export type { components, operations, paths } from './generated/app-api.js';
|
|
|
16
16
|
export type { components as CellComponents, paths as CellPaths } from './generated/cell-api.js';
|
|
17
17
|
export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog, pickByKey } from './client.js';
|
|
18
18
|
export type { AgentSession, BillingCatalog, BillingSubscription, Caps, CheckoutSession, DiskLayout, Entitlements, FindByKeyOptions, ForkTarget, Invoice, InvoicePage, LifetimeFilter, ListParams, Me, OpenParams, OpenResponse, Operation, Page, PortalSession, PurposeFilter, ResetWorkspaceBody, ResumeAnswer, ResumeRequestOptions, ResumeResponse, ShardfluxOptions, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResponse, SuspendWhenIdleResult, UpdatePolicy, WaitOptions, WorkspaceInputs, WorkspaceLifetime, WorkspaceOrigin, WorkspacePurpose, WorkspaceView, } from './client.js';
|
|
19
|
-
export type { FinishedOperation, LifecycleOptions, ResumeOptions, WaitedLifecycleOptions, WaitedResumeOptions } from './lifecycle.js';
|
|
20
|
-
export { formatTiming } from './progress.js';
|
|
21
|
-
export type { LifecycleAction, LifecyclePhase, LifecycleTiming, ProgressEvent, ProgressListener, RetryRecord, ServerTiming, TimingOutcome, TimingPhase, } from './progress.js';
|
|
19
|
+
export type { FinishedOperation, LifecycleOptions, ResumeOptions, SuspendOptions, WaitedLifecycleOptions, WaitedResumeOptions, WaitedSuspendOptions } from './lifecycle.js';
|
|
20
|
+
export { durabilityOf, formatTiming, isDurable, lostSuspendOf } from './progress.js';
|
|
21
|
+
export type { Durability, LostSuspend, LifecycleAction, LifecyclePhase, LifecycleTiming, ProgressEvent, ProgressListener, RetryRecord, ServerTiming, TimingOutcome, TimingPhase, } from './progress.js';
|
|
22
22
|
export { FEEDBACK_CATEGORIES, FEEDBACK_MESSAGE_MAX_LENGTH } from './feedback.js';
|
|
23
23
|
export type { AccountFeedbackParams, FeedbackCategory, FeedbackContext, FeedbackReceipt, SendFeedbackParams } from './feedback.js';
|
|
24
24
|
export { UsageApi } from './usage.js';
|
|
@@ -50,10 +50,15 @@ export type { AnthropicToolDefinition, JsonSchema, OpenAIChatToolDefinition, Ope
|
|
|
50
50
|
export { CaptureError, ToolCallCapture, captureTool } from './capture.js';
|
|
51
51
|
export type { CallLike, CallRef, CaptureCall, CaptureErrorKind, CaptureEvent, CaptureFlushResult, CapturePart, CaptureSelector, CaptureSource, CaptureStats, CaptureStatus, DropReason, ToolCallCaptureOptions, WrapOptions, } from './capture.js';
|
|
52
52
|
export type { AiSdkAdapter, AiSdkToolEndEvent, AnthropicAdapter, ClaudeAdapter, ClaudeCaptureHooks, ClaudeHookCallback, ClaudeHookMatcher, LangChainAdapter, LangChainToolHandler, MastraAdapter, MastraAfterToolCallContext, McpAdapter, OpenAIAgentsAdapter, } from './capture-adapters.js';
|
|
53
|
-
export { ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from './errors.js';
|
|
53
|
+
export { DurabilityLostError, ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from './errors.js';
|
|
54
54
|
export type { AppErrorCode, CellErrorCode, ErrorCode, ErrorReason, KnownErrorReason, WorkspaceMode } from './errors.js';
|
|
55
55
|
export { SDK_VERSION } from './http.js';
|
|
56
56
|
export { API_KEY_TOOL_PERMISSIONS, AccountApiKeysApi, AccountAuditApi, AccountAuthApi, AccountBillingApi, AccountExportsApi, AccountInvitationsApi, AccountMembersApi, AccountOrganizationsApi, AccountProjectsApi, AccountTemplatesApi, AccountTotpApi, AccountUserApi, CheckoutTimeoutError, OrganizationExportsApi, SESSION_TOKEN_PATTERN, ShardfluxAccount, isSessionToken, parseEmailToken, } from './account.js';
|
|
57
57
|
export type { AccountClientOptions, AccountDeletion, AccountDeletionCanceled, AccountDeletionScheduled, ApiKey, ApiKeyPage, ApiKeyToolPermission, AuditExportParams, AuthSessionInfo, AuthSessionPage, AuthSessionState, AuthUser, BillingInvoicePage, BillingPortalSession, CheckoutStatus, CreateApiKeyParams, CreatedApiKey, DataExport, EmailChangeConfirmResult, EmailChangeResult, Invitation, InvitationPage, LoginResult, Member, MemberPage, MemberRole, MfaChallengeResult, Organization, OrganizationDeletion, OrganizationDeletionResult, OrganizationPage, OrganizationRole, OrganizationWorkspacePage, OrganizationWorkspacesParams, PageParams, PasswordChangeResult, PasswordResetConfirmResult, PasswordResetRequestResult, Project, ProjectPage, RecoveryCodes, RegisterResult, ResendVerificationResult, SessionTokenUpdate, ShardfluxAccountOptions, SpendPolicyUpdate, StepUpResult, TotpConfirmResult, TotpDisableResult, TotpEnrollment, VerifyEmailResult, WaitForCheckoutOptions, } from './account.js';
|
|
58
58
|
export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from './version-check.js';
|
|
59
59
|
export type { CheckClientVersionOptions, ClientEcosystem, ClientVersionEntry, ClientVersionStatus, ClientVersionStatusKind, ClientVersions, VersionCheckIdentity, VersionCheckOption, } from './version-check.js';
|
|
60
|
+
export { isWorkspaceGone } from './errors.js';
|
|
61
|
+
export { defaultFetch } from './http.js';
|
|
62
|
+
export type { IdlePolicy } from './client.js';
|
|
63
|
+
export type { IdleStatus, KeepaliveResult } from './cell.js';
|
|
64
|
+
export type { ExecInputResult } from './cell.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog, pickByKey } from "./client.js";
|
|
2
|
-
export { formatTiming } from "./progress.js";
|
|
2
|
+
export { durabilityOf, formatTiming, isDurable, lostSuspendOf } from "./progress.js";
|
|
3
3
|
export { FEEDBACK_CATEGORIES, FEEDBACK_MESSAGE_MAX_LENGTH } from "./feedback.js";
|
|
4
4
|
export { UsageApi } from "./usage.js";
|
|
5
5
|
export { TemplateBuildTimeoutError, TemplateBuildsApi, TemplateDraftApi, TemplatePackagesApi, TemplateUploadError, TemplateUploadsApi, TemplateVersionTestInstancesApi, TemplateVersionsApi, TemplatesApi, buildSettled, saveAsTemplateBody, } from "./templates.js";
|
|
@@ -15,7 +15,9 @@ export { EXECUTION_ID, ExecutionResult, newExecutionId } from "./executions.js";
|
|
|
15
15
|
export { ToolTokenManager } from "./tokens.js";
|
|
16
16
|
export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from "./tools.js";
|
|
17
17
|
export { CaptureError, ToolCallCapture, captureTool } from "./capture.js";
|
|
18
|
-
export { ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from "./errors.js";
|
|
18
|
+
export { DurabilityLostError, ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from "./errors.js";
|
|
19
19
|
export { SDK_VERSION } from "./http.js";
|
|
20
20
|
export { API_KEY_TOOL_PERMISSIONS, AccountApiKeysApi, AccountAuditApi, AccountAuthApi, AccountBillingApi, AccountExportsApi, AccountInvitationsApi, AccountMembersApi, AccountOrganizationsApi, AccountProjectsApi, AccountTemplatesApi, AccountTotpApi, AccountUserApi, CheckoutTimeoutError, OrganizationExportsApi, SESSION_TOKEN_PATTERN, ShardfluxAccount, isSessionToken, parseEmailToken, } from "./account.js";
|
|
21
21
|
export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from "./version-check.js";
|
|
22
|
+
export { isWorkspaceGone } from "./errors.js";
|
|
23
|
+
export { defaultFetch } from "./http.js";
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -32,9 +32,25 @@ export interface LifecycleOptions {
|
|
|
32
32
|
export type WaitedLifecycleOptions = LifecycleOptions & {
|
|
33
33
|
wait: true | WaitOptions;
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* `suspend()` options (0.12.0+). A waited suspend resolves as soon as the workspace is sealed on its host, typically in a
|
|
37
|
+
* few hundred ms; the finished operation's `result.durable` turns true when the copy lands in durable storage,
|
|
38
|
+
* typically within a second. `durable: true` resolves only then: it waits for the suspend (so it implies `wait`), then
|
|
39
|
+
* for `result.durable`, within the same `timeoutMs` and `signal`. It throws DurabilityLostError if the copy cannot be
|
|
40
|
+
* made, and OperationTimeoutError (`durable: true`) when the time runs out first; the copy continues server side.
|
|
41
|
+
*/
|
|
42
|
+
export interface SuspendOptions extends LifecycleOptions {
|
|
43
|
+
durable?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** Suspend options that wait: `wait` or `durable`. */
|
|
46
|
+
export type WaitedSuspendOptions = SuspendOptions & ({
|
|
47
|
+
wait: true | WaitOptions;
|
|
48
|
+
} | {
|
|
49
|
+
durable: true;
|
|
50
|
+
});
|
|
35
51
|
/**
|
|
36
52
|
* `resume()` options (0.9.0). With `wait`, the server holds the resume until the workspace runs and returns a tool token
|
|
37
|
-
* with it
|
|
53
|
+
* with it: `agentLabel` and `tools` choose that token (defaults: a workspace handle's own, those given
|
|
38
54
|
* to open(); `workspaces.resume(id)` otherwise the key's tools and label `default`). A handle keeps it for its `cell()`
|
|
39
55
|
* clients of the same label and tools; `workspaces.resume(id)` only attributes it.
|
|
40
56
|
*/
|
|
@@ -50,7 +66,7 @@ export declare const TRACE: unique symbol;
|
|
|
50
66
|
/** Internal: work a workspace handle does after the operation finished, inside the same trace (refreshing its view). */
|
|
51
67
|
export declare const AFTER_WAIT: unique symbol;
|
|
52
68
|
/**
|
|
53
|
-
* Internal: a workspace handle's part in a held resume
|
|
69
|
+
* Internal: a workspace handle's part in a held resume: the tool token to ask for, and how it takes the
|
|
54
70
|
* running workspace and that token from a 200 (instead of reading the view and fetching a token afterwards).
|
|
55
71
|
*/
|
|
56
72
|
export declare const HELD_RESUME: unique symbol;
|
|
@@ -63,6 +79,7 @@ export type InternalWaitOptions = WaitOptions & {
|
|
|
63
79
|
[TRACE]?: Trace;
|
|
64
80
|
};
|
|
65
81
|
export type InternalLifecycleOptions = ResumeOptions & {
|
|
82
|
+
durable?: boolean;
|
|
66
83
|
[AFTER_WAIT]?: (trace: Trace, operation: Operation) => Promise<void>;
|
|
67
84
|
[HELD_RESUME]?: HeldResumeTarget;
|
|
68
85
|
};
|
package/dist/lifecycle.js
CHANGED
|
@@ -5,7 +5,7 @@ export const TRACE = Symbol('shardflux.trace');
|
|
|
5
5
|
/** Internal: work a workspace handle does after the operation finished, inside the same trace (refreshing its view). */
|
|
6
6
|
export const AFTER_WAIT = Symbol('shardflux.afterWait');
|
|
7
7
|
/**
|
|
8
|
-
* Internal: a workspace handle's part in a held resume
|
|
8
|
+
* Internal: a workspace handle's part in a held resume: the tool token to ask for, and how it takes the
|
|
9
9
|
* running workspace and that token from a 200 (instead of reading the view and fetching a token afterwards).
|
|
10
10
|
*/
|
|
11
11
|
export const HELD_RESUME = Symbol('shardflux.heldResume');
|
|
@@ -19,7 +19,9 @@ export function waitOptionsOf(opts) {
|
|
|
19
19
|
* the request, every observed state, and `AFTER_WAIT`; it ends with `done` either way.
|
|
20
20
|
*/
|
|
21
21
|
export async function runLifecycle(ctx, kind, workspaceId, start, opts, capture = {}) {
|
|
22
|
-
|
|
22
|
+
// `durable` (suspend) waits for the durable copy, so it implies `wait`.
|
|
23
|
+
const waitOpts = waitOptionsOf(opts) ?? (opts.durable ? {} : null);
|
|
24
|
+
const started = Date.now();
|
|
23
25
|
const trace = new Trace(kind, combineListeners(ctx.onProgress, opts.onProgress, waitOpts?.onProgress), { workspaceId });
|
|
24
26
|
return traced(trace, async () => {
|
|
25
27
|
// Tool-call capture writes recorded before this call land first (snapshot, fork, suspend and close include them).
|
|
@@ -36,6 +38,11 @@ export async function runLifecycle(ctx, kind, workspaceId, start, opts, capture
|
|
|
36
38
|
throw new OperationFailedError(operation);
|
|
37
39
|
if (operation.state !== 'succeeded')
|
|
38
40
|
final = await ctx.workspaces.waitForOperation(operation.id, { ...waitOpts, [TRACE]: trace });
|
|
41
|
+
if (opts.durable) {
|
|
42
|
+
// One budget for the whole call: what the suspend itself left.
|
|
43
|
+
const timeoutMs = Math.max(1, (waitOpts.timeoutMs ?? 300_000) - (Date.now() - started));
|
|
44
|
+
final = await ctx.workspaces.waitForDurable(final, { ...waitOpts, timeoutMs, [TRACE]: trace });
|
|
45
|
+
}
|
|
39
46
|
await opts[AFTER_WAIT]?.(trace, final);
|
|
40
47
|
return final;
|
|
41
48
|
});
|
package/dist/progress.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lifecycle timing and progress.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Lifecycle timing and progress. Every open, resume and wake says where its time went, without a packet capture: the
|
|
3
|
+
* queue, the cell booting or restoring the VM, the network between the caller and the API, the first tool token, or
|
|
4
|
+
* retries.
|
|
5
5
|
*
|
|
6
6
|
* A Trace follows one SDK call (open, a lifecycle call with `wait`, a wake, a token fetch). It records two clocks and
|
|
7
7
|
* never mixes them:
|
|
@@ -28,8 +28,10 @@ export type LifecycleAction = 'open' | 'suspend' | 'resume' | 'snapshot' | 'fork
|
|
|
28
28
|
* - `busy`: a tool call waiting out `workspace_busy` (only in `tool` events).
|
|
29
29
|
* - `capture_flush` (0.7.0+): a lifecycle call waiting for tool-call capture writes recorded before it (only when some
|
|
30
30
|
* were pending).
|
|
31
|
+
* - `durable` (0.12.0+): `suspend({ durable: true })` or `waitForDurable()` waiting for the durable copy after the
|
|
32
|
+
* operation succeeded (reason `overdue` once the result has `durability.overdue_at`).
|
|
31
33
|
*/
|
|
32
|
-
export type LifecyclePhase = 'request' | 'queued' | 'capacity_pending' | 'running' | 'view' | 'token' | 'busy' | 'capture_flush';
|
|
34
|
+
export type LifecyclePhase = 'request' | 'queued' | 'capacity_pending' | 'running' | 'view' | 'token' | 'busy' | 'capture_flush' | 'durable';
|
|
33
35
|
export interface TimingPhase {
|
|
34
36
|
phase: LifecyclePhase;
|
|
35
37
|
/** The server's state_reason, or why the SDK entered the phase (`held`, `initial`, `expiring`, `invalidated`). */
|
|
@@ -56,8 +58,8 @@ export interface ServerTiming {
|
|
|
56
58
|
kind: Operation['kind'];
|
|
57
59
|
state: Operation['state'];
|
|
58
60
|
/**
|
|
59
|
-
* created_at → started_at: waiting until the cell began running the operation, including any
|
|
60
|
-
*
|
|
61
|
+
* created_at → started_at: waiting until the cell began running the operation, including any time in
|
|
62
|
+
* `capacity_pending` (started_at is set when the operation first enters `running`). Null when the API does not report
|
|
61
63
|
* started_at or the operation has not run yet. An operation that finished without running (e.g. a failed dependency)
|
|
62
64
|
* has started_at = completed_at, so all of its time shows here.
|
|
63
65
|
*/
|
|
@@ -71,10 +73,10 @@ export interface ServerTiming {
|
|
|
71
73
|
/** `result.warm_fallback`: why a start that could be warm booted instead. */
|
|
72
74
|
warmFallback: string | null;
|
|
73
75
|
/**
|
|
74
|
-
* `result.resume_path`: `local_cache`, `prestaged` (copied to this host ahead of the resume
|
|
75
|
-
* `download` (the checkpoint had to be fetched first), `cold_boot` (0.11.0+:
|
|
76
|
-
*
|
|
77
|
-
*
|
|
76
|
+
* `result.resume_path`: `local_cache`, `prestaged` (copied to this host ahead of the resume),
|
|
77
|
+
* `download` (the checkpoint had to be fetched first), `cold_boot` (0.11.0+: the saved disk was booted after a
|
|
78
|
+
* platform runtime change; processes restarted; see `memoryRestored`) or `reset_blank_layer` (the first start after a
|
|
79
|
+
* reset).
|
|
78
80
|
*/
|
|
79
81
|
resumePath: string | null;
|
|
80
82
|
/**
|
|
@@ -89,11 +91,70 @@ export interface ServerTiming {
|
|
|
89
91
|
* `runtime_changed` (the platform's VM runtime changed after the suspend). Null otherwise.
|
|
90
92
|
*/
|
|
91
93
|
coldBootReason?: string | null;
|
|
94
|
+
/**
|
|
95
|
+
* `result.durable` (0.12.0+), suspend and fork: `true` when the capture is in durable storage, `false` while its
|
|
96
|
+
* durable copy is being written (see `durability`). `null` when the result does not say (other kinds, an older API).
|
|
97
|
+
*/
|
|
98
|
+
durable?: boolean | null;
|
|
99
|
+
/** `result.suspend_path` (0.12.0+): `local_commit` when the suspend returned as soon as it was sealed on its host. */
|
|
100
|
+
suspendPath?: string | null;
|
|
101
|
+
/** `result.durability` (0.12.0+): the durable copy's state and timestamps. Null when the result has none. */
|
|
102
|
+
durability?: Durability | null;
|
|
103
|
+
/**
|
|
104
|
+
* `result.lost_suspend` (0.12.0+), resume: the workspace's latest suspend could not be kept and this resume restored the
|
|
105
|
+
* checkpoint before it (`restoredCheckpointId`, state as of `stateAsOf`). Null otherwise.
|
|
106
|
+
*/
|
|
107
|
+
lostSuspend?: LostSuspend | null;
|
|
92
108
|
/** `result.boot_to_ready_ms`: VM start until the guest agent answered. */
|
|
93
109
|
bootToReadyMs: number | null;
|
|
94
110
|
/** `result.host_timings_ms`: the host's own steps (restore: load, after_restore, ready, …). */
|
|
95
111
|
hostTimingsMs: Record<string, number> | null;
|
|
96
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* `result.durability` of a suspend or fork (0.12.0+), camelCased. A suspend returns as soon as the workspace is sealed on
|
|
115
|
+
* its host (`pending`); `durable` follows when the copy lands in durable storage (`durableAt`). Timestamps are RFC 3339.
|
|
116
|
+
*/
|
|
117
|
+
export interface Durability {
|
|
118
|
+
/** `pending` (the durable copy is being written), `durable` (it committed) or `lost` (see the lifecycle reference). */
|
|
119
|
+
state: 'pending' | 'durable' | 'lost';
|
|
120
|
+
checkpointId: string | null;
|
|
121
|
+
generationId: string | null;
|
|
122
|
+
/** When the suspend (or fork) completed on its host. */
|
|
123
|
+
localCommitAt: string | null;
|
|
124
|
+
/** When the durable copy is expected by. */
|
|
125
|
+
durableBy: string | null;
|
|
126
|
+
/** When the durable copy committed (state `durable`). */
|
|
127
|
+
durableAt: string | null;
|
|
128
|
+
/** Local commit to durable commit, in milliseconds (state `durable`). */
|
|
129
|
+
localCommitToDurableMs: number | null;
|
|
130
|
+
/** Set once `durableBy` passed while the copy was still being written; it keeps going. */
|
|
131
|
+
overdueAt: string | null;
|
|
132
|
+
/** State `lost`: why (e.g. `host_lost`). */
|
|
133
|
+
reason: string | null;
|
|
134
|
+
}
|
|
135
|
+
/** `result.lost_suspend` of a resume (0.12.0+), camelCased. See the lifecycle reference. */
|
|
136
|
+
export interface LostSuspend {
|
|
137
|
+
checkpointId: string;
|
|
138
|
+
generationId: string | null;
|
|
139
|
+
reason: string | null;
|
|
140
|
+
suspendedAt: string | null;
|
|
141
|
+
/** The earlier checkpoint this resume restored. */
|
|
142
|
+
restoredCheckpointId: string | null;
|
|
143
|
+
/** The restored state is as of this time (RFC 3339). */
|
|
144
|
+
stateAsOf: string | null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The durable copy of a suspend or fork operation (0.12.0+): `result.durability` camelCased, or null when the result has
|
|
148
|
+
* none (the suspend was stored durably before it completed, or another kind).
|
|
149
|
+
*/
|
|
150
|
+
export declare function durabilityOf(op: Pick<Operation, 'result'>): Durability | null;
|
|
151
|
+
/** A resume operation's `result.lost_suspend` camelCased (0.12.0+), or null. */
|
|
152
|
+
export declare function lostSuspendOf(op: Pick<Operation, 'result'>): LostSuspend | null;
|
|
153
|
+
/**
|
|
154
|
+
* Whether a suspend or fork operation's capture is in durable storage (0.12.0+): `result.durable`, else `true` for a
|
|
155
|
+
* succeeded suspend or fork whose result predates the field, else null.
|
|
156
|
+
*/
|
|
157
|
+
export declare function isDurable(op: Pick<Operation, 'result' | 'kind' | 'state'>): boolean | null;
|
|
97
158
|
export type TimingOutcome = 'succeeded' | 'failed' | 'timed_out' | 'error';
|
|
98
159
|
export interface LifecycleTiming {
|
|
99
160
|
action: LifecycleAction;
|
|
@@ -123,8 +184,8 @@ interface EventBase {
|
|
|
123
184
|
}
|
|
124
185
|
/**
|
|
125
186
|
* - `phase`: a phase began (live progress: "capacity_pending: no_ready_host"). `tool` phases come from tool calls.
|
|
126
|
-
* A `capacity_pending` phase carries `deadlineAt` (0.6.2+, when the API reports it):
|
|
127
|
-
*
|
|
187
|
+
* A `capacity_pending` phase carries `deadlineAt` (0.6.2+, when the API reports it): the start's deadline, past which
|
|
188
|
+
* it fails with `capacity_unavailable` (retryable; nothing was started).
|
|
128
189
|
* - `retry`: a request is retried after a transient failure (also emitted for tool calls, action `tool`).
|
|
129
190
|
* - `done`: the call ended, successfully or not, with its full timing.
|
|
130
191
|
*/
|
|
@@ -146,9 +207,9 @@ export declare function emitTo(listeners: ReadonlyArray<ProgressListener | undef
|
|
|
146
207
|
/** Combines listeners (client-level and per call) into one; undefined when there are none. */
|
|
147
208
|
export declare function combineListeners(...listeners: Array<ProgressListener | undefined>): ProgressListener | undefined;
|
|
148
209
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
210
|
+
* The deadline of a start in `capacity_pending`: the operation's `error.details.deadline_at` (RFC 3339). A start still
|
|
211
|
+
* queued then fails with `capacity_unavailable` (retryable; nothing was started). Null in any other state, or from an
|
|
212
|
+
* API that does not report it.
|
|
152
213
|
*/
|
|
153
214
|
export declare function capacityDeadlineOf(op: Operation): string | null;
|
|
154
215
|
/** Server timing from an operation as GET /v1/operations/{id} returns it. */
|
|
@@ -169,7 +230,7 @@ export declare class Trace {
|
|
|
169
230
|
get finished(): LifecycleTiming | null;
|
|
170
231
|
/**
|
|
171
232
|
* Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase.
|
|
172
|
-
* `deadlineAt`:
|
|
233
|
+
* `deadlineAt`: the deadline of a `capacity_pending` start (added to the event only).
|
|
173
234
|
*/
|
|
174
235
|
phase(phase: LifecyclePhase, reason?: string | null, deadlineAt?: string | null): void;
|
|
175
236
|
/** Runs `fn` as a phase that may overlap others (open() reads the view and issues the token together). */
|
|
@@ -185,14 +246,14 @@ export declare class Trace {
|
|
|
185
246
|
/** Runs `fn` under `trace` and ends the trace either way (the error keeps its timing). */
|
|
186
247
|
export declare function traced<T>(trace: Trace, fn: () => Promise<T>): Promise<T>;
|
|
187
248
|
/**
|
|
188
|
-
* A human-readable account of a timing, for logs and bug reports:
|
|
249
|
+
* A human-readable account of a timing, for logs and bug reports. A resume in production:
|
|
189
250
|
*
|
|
190
|
-
*
|
|
191
|
-
* client: request
|
|
192
|
-
* server: queued
|
|
193
|
-
* outside the server:
|
|
194
|
-
* retries: 1 (POST /v1/workspaces/open, HTTP 503 unavailable, after 200 ms)
|
|
251
|
+
* resume 413 ms, succeeded (workspace <id>, operation <id>)
|
|
252
|
+
* client: request 218 ms → queued 195 ms
|
|
253
|
+
* server: queued 51 ms, ran 290 ms, total 341 ms; resume from local_cache, boot to ready 211 ms, host disk 0 ms, load 6 ms, ready 62 ms, total 211 ms, after_restore 36 ms
|
|
254
|
+
* outside the server: 72 ms
|
|
195
255
|
*
|
|
256
|
+
* A call that retried a request adds `retries: <n> (<request>, <cause>, after <delay>)`.
|
|
196
257
|
* A resume that booted the workspace instead of restoring its memory (0.11.0+) says so in the server line:
|
|
197
258
|
* `resume from cold_boot: processes restarted (runtime_changed)`.
|
|
198
259
|
*/
|