@shardflux/sdk 0.12.0 → 0.13.1
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 +189 -1
- package/README.md +118 -11
- package/dist/cell.d.ts +51 -4
- package/dist/cell.js +138 -22
- package/dist/client.d.ts +30 -6
- package/dist/client.js +33 -5
- package/dist/errors.d.ts +40 -2
- package/dist/errors.js +10 -0
- package/dist/generated/app-api.d.ts +351 -55
- package/dist/generated/cell-api.d.ts +172 -10
- package/dist/http.d.ts +45 -3
- package/dist/http.js +85 -12
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/lifecycle.d.ts +7 -1
- package/dist/lifecycle.js +2 -2
- package/dist/progress.d.ts +47 -7
- package/dist/progress.js +35 -1
- package/dist/templates.d.ts +25 -3
- package/dist/tools.d.ts +7 -0
- package/dist/tools.js +275 -7
- package/dist/usage.d.ts +3 -1
- package/dist/usage.js +3 -1
- package/dist/workspace.d.ts +36 -10
- package/dist/workspace.js +27 -3
- package/package.json +1 -1
|
@@ -80,6 +80,45 @@ export interface paths {
|
|
|
80
80
|
* `503 service_unavailable` (`no_execution_host`: no host with
|
|
81
81
|
* protocol feature `file_first` has room; retry), secret resolution
|
|
82
82
|
* errors as for processful starts.
|
|
83
|
+
*
|
|
84
|
+
* Burst execution (contracts §33, not public yet): `burst: always` runs
|
|
85
|
+
* the command on a larger, short-lived burst VM on the workspace's
|
|
86
|
+
* host over a copy of the workspace disk and applies its file changes
|
|
87
|
+
* back when it exits. The session is read, streamed and attached like
|
|
88
|
+
* any other; it carries `burst` (BurstSummary) and cannot be signalled
|
|
89
|
+
* or canceled (`409 conflict`, `details.reason burst_not_supported`).
|
|
90
|
+
* Refusals: `422 validation_failed` with `details.reason`
|
|
91
|
+
* `burst_mode_not_supported` (`auto`), `burst_not_supported` (stdin),
|
|
92
|
+
* `burst_size_exceeds_plan` (`burst_vcpus`/`burst_memory_mib` above the
|
|
93
|
+
* plan's per-workspace ceilings; `details.field`, `details.limit`);
|
|
94
|
+
* `409 burst_unavailable` with `details.reason` `not_available` (no
|
|
95
|
+
* entitlement `policy.burst_exec`, or the host lacks protocol feature
|
|
96
|
+
* `burst_exec`), `layout_unsupported`, `shared_volumes`,
|
|
97
|
+
* `host_capacity`, `fence_not_drained`, `workspace_fenced`,
|
|
98
|
+
* `apply_pending`, `park_failed`, `workspace_resumed`, `interrupted`;
|
|
99
|
+
* `409 burst_apply_failed` (`details.reason` `disk_full` or
|
|
100
|
+
* `apply_failed`, `details.applied_entries`, `details.pending_entries`;
|
|
101
|
+
* the workspace refuses writes until a retry with the same
|
|
102
|
+
* `session_id` completes the apply). A refusal the host gives within
|
|
103
|
+
* the first seconds is the start's answer; a later one ends the
|
|
104
|
+
* session (`failed_to_start` for `burst_unavailable`, `lost` for
|
|
105
|
+
* `burst_apply_failed`), is recorded in `burst.error`, and is the
|
|
106
|
+
* output stream's final `error` event. A retry with the same
|
|
107
|
+
* `session_id` replays the recorded outcome (the host journals the
|
|
108
|
+
* burst by the exec's operation id) or resumes an unfinished apply.
|
|
109
|
+
*
|
|
110
|
+
* Start and follow in one request: a processful start sent with
|
|
111
|
+
* `Accept: application/x-ndjson` (or `text/event-stream`) answers 200
|
|
112
|
+
* with the session's output stream, as `GET .../output?follow=true`
|
|
113
|
+
* from offset 0 (output events, `exit`, a final `error`), and
|
|
114
|
+
* `X-Exec-Session-Id` names the session. The stream slot is taken
|
|
115
|
+
* before the command starts (`503 service_unavailable` when the
|
|
116
|
+
* gateway has no stream slot: nothing ran). Headers are sent as soon
|
|
117
|
+
* as the command started, before any output. A `failed_to_start`
|
|
118
|
+
* session, a refusal and a file-first execution keep their JSON
|
|
119
|
+
* answers. A replayed `session_id` streams the existing session and
|
|
120
|
+
* never runs it again; after a dropped stream, reconnect with
|
|
121
|
+
* `GET .../output` from the byte offsets already read.
|
|
83
122
|
*/
|
|
84
123
|
post: operations["execStart"];
|
|
85
124
|
delete?: never;
|
|
@@ -152,7 +191,7 @@ export interface paths {
|
|
|
152
191
|
* Client messages are JSON `ExecClientMessage`. Close codes: 1000 after
|
|
153
192
|
* the `exit` event, 1001 on gateway shutdown (reconnect with offsets),
|
|
154
193
|
* 4401 unauthenticated, 4403 forbidden, 4409 stale epoch / workspace
|
|
155
|
-
* busy / workspace not running, 4410 workspace gone, 4429 rate limited,
|
|
194
|
+
* busy / workspace not running, 4410 workspace gone, 4429 rate limited / quota exceeded,
|
|
156
195
|
* 4500/4503/4504 internal/unavailable/timeout (4000 + HTTP status; see
|
|
157
196
|
* "WebSocket close codes" above; the close reason is compact JSON with
|
|
158
197
|
* the error `code`). A refused upgrade from an allowed Origin is
|
|
@@ -225,7 +264,20 @@ export interface paths {
|
|
|
225
264
|
};
|
|
226
265
|
get?: never;
|
|
227
266
|
put?: never;
|
|
228
|
-
/**
|
|
267
|
+
/**
|
|
268
|
+
* Stop the command and every process it started (SIGTERM, SIGKILL after grace)
|
|
269
|
+
* @description Sends SIGTERM to every process the command started, background and
|
|
270
|
+
* setsid processes included, and SIGKILL after `grace_ms` (0 to 60000;
|
|
271
|
+
* 0 or omitted is 5000). Answers when they have all exited: at once when
|
|
272
|
+
* SIGTERM ends them, otherwise after the grace and the SIGKILL. A lost
|
|
273
|
+
* command is stopped the same way; a command that already exited keeps
|
|
274
|
+
* its result and what it left running is stopped. `503
|
|
275
|
+
* dependency_unavailable` (retryable) while a process cannot exit yet.
|
|
276
|
+
* Template versions before python-node-browser 13, ubuntu-24.04 4,
|
|
277
|
+
* ubuntu-22.04 2 and debian-12 2 signal the command's process group and
|
|
278
|
+
* return an ended session as it is. A `grace_ms` outside the range is `422 validation_failed`
|
|
279
|
+
* (`details.field` `grace_ms`, `details.minimum`, `details.maximum`).
|
|
280
|
+
*/
|
|
229
281
|
post: operations["execCancel"];
|
|
230
282
|
delete?: never;
|
|
231
283
|
options?: never;
|
|
@@ -293,7 +345,11 @@ export interface paths {
|
|
|
293
345
|
get: operations["ptyGet"];
|
|
294
346
|
put?: never;
|
|
295
347
|
post?: never;
|
|
296
|
-
/**
|
|
348
|
+
/**
|
|
349
|
+
* Close (SIGHUP, then SIGKILL after grace)
|
|
350
|
+
* @description Sends SIGHUP to the session's process group and SIGKILL 2 s later
|
|
351
|
+
* if it is still running; answers once it has ended.
|
|
352
|
+
*/
|
|
297
353
|
delete: operations["ptyClose"];
|
|
298
354
|
options?: never;
|
|
299
355
|
head?: never;
|
|
@@ -744,8 +800,9 @@ export interface paths {
|
|
|
744
800
|
* @description `idle_since` = max(`last_tool_at`, `last_attach_at`, `keepalive_until`,
|
|
745
801
|
* `running_since`, the work horizon of running exec commands) (null when
|
|
746
802
|
* none is known; in the future while a keepalive or a running command is
|
|
747
|
-
* active). A running exec command counts as work until
|
|
748
|
-
*
|
|
803
|
+
* active). A running exec command counts as work until it ends or its
|
|
804
|
+
* timeout passes: its start + `timeout_ms` when it has one, else its
|
|
805
|
+
* start + `IDLE_COMMAND_MAX_SECONDS` (1 hour by default). Its end is
|
|
749
806
|
* activity (contracts §20.6): an end seen without a client call is
|
|
750
807
|
* recorded in `last_attach_at`, which is work but not a tool call.
|
|
751
808
|
* `suspend_at` = `idle_since` + `idle_minutes` when
|
|
@@ -823,7 +880,7 @@ export type webhooks = Record<string, never>;
|
|
|
823
880
|
export interface components {
|
|
824
881
|
schemas: {
|
|
825
882
|
/** @enum {string} */
|
|
826
|
-
ErrorCode: "bad_request" | "unauthenticated" | "forbidden" | "not_found" | "conflict" | "stale_epoch" | "workspace_not_running" | "validation_failed" | "payload_too_large" | "range_not_satisfiable" | "rate_limited" | "capacity_pending" | "dependency_unavailable" | "timeout" | "internal_error" | "service_unavailable" | "workspace_busy";
|
|
883
|
+
ErrorCode: "bad_request" | "unauthenticated" | "forbidden" | "not_found" | "conflict" | "stale_epoch" | "workspace_not_running" | "validation_failed" | "payload_too_large" | "range_not_satisfiable" | "rate_limited" | "capacity_pending" | "dependency_unavailable" | "timeout" | "internal_error" | "service_unavailable" | "workspace_busy" | "burst_unavailable" | "burst_apply_failed" | "quota_exceeded";
|
|
827
884
|
ErrorBody: {
|
|
828
885
|
error: {
|
|
829
886
|
code: components["schemas"]["ErrorCode"];
|
|
@@ -872,7 +929,10 @@ export interface components {
|
|
|
872
929
|
stdin?: string;
|
|
873
930
|
/** Format: int64 */
|
|
874
931
|
timeout_ms?: number;
|
|
875
|
-
/**
|
|
932
|
+
/**
|
|
933
|
+
* Format: int64
|
|
934
|
+
* @description Time between the SIGTERM and the SIGKILL when timeout_ms ends the command; 0 or omitted is 5000.
|
|
935
|
+
*/
|
|
876
936
|
kill_grace_ms?: number;
|
|
877
937
|
secret_refs?: components["schemas"]["SecretRefs"];
|
|
878
938
|
/** @description File-first workspaces (required there, refused otherwise) — the execution's idempotency key. */
|
|
@@ -883,6 +943,16 @@ export interface components {
|
|
|
883
943
|
* @default 1048576
|
|
884
944
|
*/
|
|
885
945
|
output_limit_bytes?: number;
|
|
946
|
+
/**
|
|
947
|
+
* @description Burst execution (contracts §33): `always` runs this run-to-completion command on a larger, short-lived burst VM and applies its file changes back. Needs entitlement `policy.burst_exec` and a layered processful workspace; not with stdin. `auto` is reserved (422 validation_failed, details.reason burst_mode_not_supported).
|
|
948
|
+
* @default never
|
|
949
|
+
* @enum {string}
|
|
950
|
+
*/
|
|
951
|
+
burst?: "never" | "always";
|
|
952
|
+
/** @description The burst VM's vCPUs (burst always only; default the host's, 8), at most the plan's workspace CPU ceiling. */
|
|
953
|
+
burst_vcpus?: number;
|
|
954
|
+
/** @description The burst VM's memory in MiB (burst always only; default the host's, 8192), at most the plan's workspace memory ceiling. */
|
|
955
|
+
burst_memory_mib?: number;
|
|
886
956
|
};
|
|
887
957
|
/** @description Names of customer secrets (contracts §17) to inject as environment variables NAME=value of this process only. The cell resolves them at session start through the API with the caller's tool token (permission-checked and audited by the API); values are never logged, persisted or returned by the cell. Any name the caller may not use (unknown, not permitted for this workspace/project/tool) refuses the whole start with 403 forbidden (details.reason secret_not_available, details.names); nothing is started. A name that is also a key of `env` is refused (422 validation_failed). Requires a workspace tool token (browser stream tickets cannot resolve secrets: 403, details.reason secret_refs_require_tool_token). Resolution failures: 401 (token revoked/expired), 409 stale_epoch, 429 rate_limited, 503 dependency_unavailable. A retried start with the same session_id re-resolves the names; if a value changed since the first start the host refuses the different request (409 conflict). */
|
|
888
958
|
SecretRefs: string[];
|
|
@@ -980,6 +1050,88 @@ export interface components {
|
|
|
980
1050
|
ended_at?: string | null;
|
|
981
1051
|
/** @description Why the session ended without an exit code. For failed_to_start the command never ran and this names the cause, e.g. `working directory "/home/user/app" is not a directory` or `executable "foo" not found in PATH`. */
|
|
982
1052
|
error?: string;
|
|
1053
|
+
memory_grow?: components["schemas"]["MemoryGrow"];
|
|
1054
|
+
burst?: components["schemas"]["BurstSummary"];
|
|
1055
|
+
};
|
|
1056
|
+
/** @description Burst execution (contracts §33.5): present on every session started with `burst: always`. The size, method and counts are set once the burst ended (the host's result); while it runs only host, applied and the requested size are known. */
|
|
1057
|
+
BurstSummary: {
|
|
1058
|
+
/**
|
|
1059
|
+
* @description Where the burst VM ran; `local` = the workspace's host (`remote` is reserved).
|
|
1060
|
+
* @enum {string}
|
|
1061
|
+
*/
|
|
1062
|
+
host: "local" | "remote";
|
|
1063
|
+
/** @description The burst VM's vCPUs (the requested count while it runs, absent for the host default). */
|
|
1064
|
+
vcpus?: number;
|
|
1065
|
+
/** @description The burst VM's memory in MiB (the requested size while it runs, absent for the host default). */
|
|
1066
|
+
memory_mib?: number;
|
|
1067
|
+
/**
|
|
1068
|
+
* @description How the burst VM got the workspace copy.
|
|
1069
|
+
* @enum {string}
|
|
1070
|
+
*/
|
|
1071
|
+
method?: "layer" | "clone";
|
|
1072
|
+
/** @description The command's file changes were applied to the workspace. */
|
|
1073
|
+
applied: boolean;
|
|
1074
|
+
/** Format: int64 */
|
|
1075
|
+
written_files?: number;
|
|
1076
|
+
/** Format: int64 */
|
|
1077
|
+
written_dirs?: number;
|
|
1078
|
+
/** Format: int64 */
|
|
1079
|
+
removed?: number;
|
|
1080
|
+
/** Format: int64 */
|
|
1081
|
+
written_bytes?: number;
|
|
1082
|
+
/** @description Processes the command left behind in the burst VM (killed; they do not survive a burst). */
|
|
1083
|
+
leftover_killed?: number;
|
|
1084
|
+
/**
|
|
1085
|
+
* Format: int64
|
|
1086
|
+
* @description Wall time of the burst minus the command's own run time.
|
|
1087
|
+
*/
|
|
1088
|
+
overhead_ms?: number;
|
|
1089
|
+
/** @description The burst VM's own paths whose changes are never carried back (the host's denylist). */
|
|
1090
|
+
excluded_paths?: string[];
|
|
1091
|
+
/** @description The host's phase timings (ms) and counters. */
|
|
1092
|
+
timings?: {
|
|
1093
|
+
[key: string]: number;
|
|
1094
|
+
};
|
|
1095
|
+
/** @description The recorded outcome of an earlier attempt with the same session_id. */
|
|
1096
|
+
replayed?: boolean;
|
|
1097
|
+
/** @description Set when the burst failed after the start answered: burst_unavailable (the workspace is unchanged) or burst_apply_failed (details.applied_entries, details.pending_entries). */
|
|
1098
|
+
error?: components["schemas"]["BurstError"];
|
|
1099
|
+
};
|
|
1100
|
+
BurstError: {
|
|
1101
|
+
code: components["schemas"]["ErrorCode"];
|
|
1102
|
+
message: string;
|
|
1103
|
+
retryable: boolean;
|
|
1104
|
+
details?: {
|
|
1105
|
+
[key: string]: unknown;
|
|
1106
|
+
};
|
|
1107
|
+
};
|
|
1108
|
+
/** @description Elastic memory (contracts §32.3, §32.6): the host grew the workspace's memory before this exec started (the exec waited for it). Present only on the session returned by the start that grew the VM; absent when no grow ran (fixed workspaces, or already at the exec size). */
|
|
1109
|
+
MemoryGrow: {
|
|
1110
|
+
/**
|
|
1111
|
+
* @description delivered: the whole grow; partial: less than wanted; missed: nothing could be plugged; failed: the plug did not converge or errored.
|
|
1112
|
+
* @enum {string}
|
|
1113
|
+
*/
|
|
1114
|
+
outcome: "delivered" | "partial" | "missed" | "failed";
|
|
1115
|
+
/**
|
|
1116
|
+
* Format: int64
|
|
1117
|
+
* @description Guest memory before the grow (MiB).
|
|
1118
|
+
*/
|
|
1119
|
+
from_mib: number;
|
|
1120
|
+
/**
|
|
1121
|
+
* Format: int64
|
|
1122
|
+
* @description Guest memory the grow aimed for (MiB).
|
|
1123
|
+
*/
|
|
1124
|
+
want_mib: number;
|
|
1125
|
+
/**
|
|
1126
|
+
* Format: int64
|
|
1127
|
+
* @description Guest memory after the grow (MiB).
|
|
1128
|
+
*/
|
|
1129
|
+
got_mib: number;
|
|
1130
|
+
/**
|
|
1131
|
+
* Format: int64
|
|
1132
|
+
* @description Trigger to plugged and guest MemTotal settled (ms, rounded).
|
|
1133
|
+
*/
|
|
1134
|
+
deliver_ms: number;
|
|
983
1135
|
};
|
|
984
1136
|
OutputEvent: {
|
|
985
1137
|
/** @enum {string} */
|
|
@@ -999,7 +1151,10 @@ export interface components {
|
|
|
999
1151
|
/** @enum {string} */
|
|
1000
1152
|
type: "signal" | "cancel";
|
|
1001
1153
|
signal?: components["schemas"]["SignalValue"];
|
|
1002
|
-
/**
|
|
1154
|
+
/**
|
|
1155
|
+
* Format: int64
|
|
1156
|
+
* @description For cancel, as CancelRequest.grace_ms; out of range is an `error` event (validation_failed).
|
|
1157
|
+
*/
|
|
1003
1158
|
grace_ms?: number;
|
|
1004
1159
|
};
|
|
1005
1160
|
/** @description Signal number (1-64) or name (e.g. "SIGTERM", "TERM"). */
|
|
@@ -1026,7 +1181,10 @@ export interface components {
|
|
|
1026
1181
|
only_leader?: boolean;
|
|
1027
1182
|
};
|
|
1028
1183
|
CancelRequest: {
|
|
1029
|
-
/**
|
|
1184
|
+
/**
|
|
1185
|
+
* Format: int64
|
|
1186
|
+
* @description Time between the SIGTERM and the SIGKILL; 0 or omitted is 5000.
|
|
1187
|
+
*/
|
|
1030
1188
|
grace_ms?: number;
|
|
1031
1189
|
};
|
|
1032
1190
|
PtyOpenRequest: {
|
|
@@ -1545,12 +1703,16 @@ export interface operations {
|
|
|
1545
1703
|
};
|
|
1546
1704
|
};
|
|
1547
1705
|
responses: {
|
|
1548
|
-
/** @description
|
|
1706
|
+
/** @description An existing session/result, or followed output when a processful start accepts NDJSON/SSE. A failed-to-start session stays JSON. */
|
|
1549
1707
|
200: {
|
|
1550
1708
|
headers: {
|
|
1709
|
+
/** @description The streamed session id; replay/reconnect use the same session and byte offsets. */
|
|
1710
|
+
"X-Exec-Session-Id"?: string;
|
|
1551
1711
|
[name: string]: unknown;
|
|
1552
1712
|
};
|
|
1553
1713
|
content: {
|
|
1714
|
+
"application/x-ndjson": string;
|
|
1715
|
+
"text/event-stream": string;
|
|
1554
1716
|
"application/json": components["schemas"]["ExecSession"] | components["schemas"]["ExecutionResult"];
|
|
1555
1717
|
};
|
|
1556
1718
|
};
|
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.13.1";
|
|
3
3
|
export interface RequestOptions {
|
|
4
4
|
query?: Record<string, string | number | boolean | undefined | null>;
|
|
5
5
|
json?: unknown;
|
|
@@ -10,9 +10,19 @@ export interface RequestOptions {
|
|
|
10
10
|
idempotencyKey?: string;
|
|
11
11
|
/** The request has no effect a retry could duplicate (a read-only POST such as files/search): retried like a GET. */
|
|
12
12
|
idempotent?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* false: the cell gateway's 429 `quota_exceeded` surfaces at once instead of being retried (the wake hint, which
|
|
15
|
+
* never waits). Default true.
|
|
16
|
+
*/
|
|
17
|
+
quotaRetry?: boolean;
|
|
13
18
|
signal?: AbortSignal;
|
|
14
19
|
/** Override the client's default request timeout (ms); 0 disables it (streams). */
|
|
15
20
|
timeoutMs?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Bounds each attempt until the response headers arrive (ms), leaving the body unbounded: a stream that answers a
|
|
23
|
+
* start (exec.run's combined start and output). Usually with `timeoutMs: 0`.
|
|
24
|
+
*/
|
|
25
|
+
headersTimeoutMs?: number;
|
|
16
26
|
/** Called before each retry of a transient failure (lifecycle timing records these). */
|
|
17
27
|
onRetry?: (retry: Omit<RetryRecord, 'atMs'>) => void;
|
|
18
28
|
}
|
|
@@ -28,13 +38,45 @@ export interface HttpOptions {
|
|
|
28
38
|
onSuccess?: (() => void) | undefined;
|
|
29
39
|
}
|
|
30
40
|
export declare const defaultSleep: (ms: number) => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* How long an idle pooled connection stays reusable (ms): 5 minutes, instead of undici's 4 s default, so the request
|
|
43
|
+
* after an agent's usual pause between tool calls (15 s to a few minutes) skips a new TCP and TLS handshake. Far below
|
|
44
|
+
* the 3600 s idle timeout of the load balancer in front of the API and the cell endpoints, so the server side never
|
|
45
|
+
* closes a connection for idleness while the client still treats it as reusable; below the AWS NAT gateway's 350 s idle
|
|
46
|
+
* timeout; and the same limit Chrome keeps for used idle sockets. Also caps a server's `Keep-Alive: timeout` hint.
|
|
47
|
+
*/
|
|
48
|
+
export declare const KEEP_ALIVE_TIMEOUT_MS = 300000;
|
|
49
|
+
/**
|
|
50
|
+
* TCP keepalive probes start after a pooled socket has been idle this long (ms), so NATs and firewalls with shorter
|
|
51
|
+
* idle timeouts than the pool's (Azure SNAT: 4 minutes) keep the connection instead of silently dropping it. Equal to
|
|
52
|
+
* undici's own default, set explicitly so it is visible and tested.
|
|
53
|
+
*/
|
|
54
|
+
export declare const TCP_KEEPALIVE_INITIAL_DELAY_MS = 60000;
|
|
55
|
+
/** The private pool's undici Agent options (exported for tests). */
|
|
56
|
+
export declare const POOL_OPTIONS: {
|
|
57
|
+
readonly allowH2: false;
|
|
58
|
+
readonly pipelining: 1;
|
|
59
|
+
readonly keepAliveTimeout: 300000;
|
|
60
|
+
readonly keepAliveMaxTimeout: 300000;
|
|
61
|
+
readonly connect: {
|
|
62
|
+
readonly keepAlive: true;
|
|
63
|
+
readonly keepAliveInitialDelay: 60000;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
31
66
|
/**
|
|
32
67
|
* 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
|
|
34
|
-
* for diagnosis; =1 retains the historical opt-in to
|
|
68
|
+
* private HTTP/1.1-only dispatcher whose idle connections stay reusable for KEEP_ALIVE_TIMEOUT_MS. Other runtimes
|
|
69
|
+
* retain native fetch. SHARDFLUX_HTTP_KEEPALIVE=0 forces close for diagnosis; =1 retains the historical opt-in to
|
|
70
|
+
* native pooling. An explicitly supplied fetch is untouched. Idle pooled sockets do not keep the process alive.
|
|
35
71
|
*/
|
|
36
72
|
export declare function defaultFetch(env?: Record<string, string | undefined> | undefined, undiciVersion?: string | undefined): typeof fetch;
|
|
37
73
|
export declare function buildUrl(base: string, path: string, query?: RequestOptions['query']): string;
|
|
74
|
+
/**
|
|
75
|
+
* True when fetch failed because its connection closed or reset before a response arrived: what a request sees when
|
|
76
|
+
* it went out on a pooled connection the other side closed while it sat idle. Connect failures (ECONNREFUSED,
|
|
77
|
+
* ENOTFOUND, UND_ERR_CONNECT_TIMEOUT), timeouts and aborts are not this.
|
|
78
|
+
*/
|
|
79
|
+
export declare function isStaleConnectionError(err: unknown): boolean;
|
|
38
80
|
/** `X-Tree-Revision` (file-first workspaces): a non-negative integer, else null. */
|
|
39
81
|
export declare function treeRevisionOf(headers: Headers): number | null;
|
|
40
82
|
/** Turns a non-2xx response into a ShardfluxApiError (or a protocol error for undocumented bodies). */
|
package/dist/http.js
CHANGED
|
@@ -4,18 +4,44 @@
|
|
|
4
4
|
* retries only where a retry cannot duplicate an effect (safe methods,
|
|
5
5
|
* requests carrying an Idempotency-Key, and read-only POSTs marked
|
|
6
6
|
* `idempotent`). A retryable 429/502/503/504 (e.g. 503 `host_capacity`, 503
|
|
7
|
-
* `wake_failed`) waits `Retry-After` (at most 5 s) before the retry.
|
|
7
|
+
* `wake_failed`) waits `Retry-After` (at most 5 s) before the retry. Such a request whose connection closed before
|
|
8
|
+
* any response (a pooled connection gone stale while idle) is sent again at once, once, outside `maxRetries`. The cell
|
|
9
|
+
* gateway's 429 `quota_exceeded` (every working slot of the plan is taken) is retried for any request (0.14.0+): it is
|
|
10
|
+
* refused at admission, so nothing ran.
|
|
8
11
|
*/
|
|
9
|
-
import { ShardfluxApiError, ShardfluxProtocolError, apiError, isErrorBody } from "./errors.js";
|
|
12
|
+
import { ShardfluxApiError, ShardfluxProtocolError, apiError, isErrorBody, isWorkingQuotaRefusal } from "./errors.js";
|
|
10
13
|
import { describeFailure } from "./progress.js";
|
|
11
|
-
export const SDK_VERSION = '0.
|
|
14
|
+
export const SDK_VERSION = '0.13.1';
|
|
12
15
|
export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
16
|
+
/**
|
|
17
|
+
* How long an idle pooled connection stays reusable (ms): 5 minutes, instead of undici's 4 s default, so the request
|
|
18
|
+
* after an agent's usual pause between tool calls (15 s to a few minutes) skips a new TCP and TLS handshake. Far below
|
|
19
|
+
* the 3600 s idle timeout of the load balancer in front of the API and the cell endpoints, so the server side never
|
|
20
|
+
* closes a connection for idleness while the client still treats it as reusable; below the AWS NAT gateway's 350 s idle
|
|
21
|
+
* timeout; and the same limit Chrome keeps for used idle sockets. Also caps a server's `Keep-Alive: timeout` hint.
|
|
22
|
+
*/
|
|
23
|
+
export const KEEP_ALIVE_TIMEOUT_MS = 300_000;
|
|
24
|
+
/**
|
|
25
|
+
* TCP keepalive probes start after a pooled socket has been idle this long (ms), so NATs and firewalls with shorter
|
|
26
|
+
* idle timeouts than the pool's (Azure SNAT: 4 minutes) keep the connection instead of silently dropping it. Equal to
|
|
27
|
+
* undici's own default, set explicitly so it is visible and tested.
|
|
28
|
+
*/
|
|
29
|
+
export const TCP_KEEPALIVE_INITIAL_DELAY_MS = 60_000;
|
|
30
|
+
/** The private pool's undici Agent options (exported for tests). */
|
|
31
|
+
export const POOL_OPTIONS = {
|
|
32
|
+
allowH2: false,
|
|
33
|
+
pipelining: 1,
|
|
34
|
+
keepAliveTimeout: KEEP_ALIVE_TIMEOUT_MS,
|
|
35
|
+
keepAliveMaxTimeout: KEEP_ALIVE_TIMEOUT_MS,
|
|
36
|
+
connect: { keepAlive: true, keepAliveInitialDelay: TCP_KEEPALIVE_INITIAL_DELAY_MS },
|
|
37
|
+
};
|
|
13
38
|
/** One private HTTP/1.1 pool on Node 26+, shared by SDK clients. No global dispatcher changes. */
|
|
14
39
|
let nodeFetch;
|
|
15
40
|
/**
|
|
16
41
|
* 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
|
|
18
|
-
* for diagnosis; =1 retains the historical opt-in to
|
|
42
|
+
* private HTTP/1.1-only dispatcher whose idle connections stay reusable for KEEP_ALIVE_TIMEOUT_MS. Other runtimes
|
|
43
|
+
* retain native fetch. SHARDFLUX_HTTP_KEEPALIVE=0 forces close for diagnosis; =1 retains the historical opt-in to
|
|
44
|
+
* native pooling. An explicitly supplied fetch is untouched. Idle pooled sockets do not keep the process alive.
|
|
19
45
|
*/
|
|
20
46
|
export function defaultFetch(env = globalThis.process?.env, undiciVersion = globalThis.process?.versions?.undici) {
|
|
21
47
|
// Why a fresh connection on Node 26 (its bundled HTTP client, measured): docs/progress/startup-latency.md.
|
|
@@ -30,7 +56,7 @@ export function defaultFetch(env = globalThis.process?.env, undiciVersion = glob
|
|
|
30
56
|
return base;
|
|
31
57
|
return async (input, init) => {
|
|
32
58
|
nodeFetch ??= import('undici').then(({ Agent, fetch: pooledFetch }) => {
|
|
33
|
-
const dispatcher = new Agent({
|
|
59
|
+
const dispatcher = new Agent({ ...POOL_OPTIONS, connect: { ...POOL_OPTIONS.connect } });
|
|
34
60
|
return async (input, init) => {
|
|
35
61
|
const response = await pooledFetch(input, { ...init, dispatcher });
|
|
36
62
|
// Web-standard runtime shape; undici and DOM iterator declarations differ.
|
|
@@ -49,6 +75,25 @@ export function buildUrl(base, path, query) {
|
|
|
49
75
|
}
|
|
50
76
|
return u.toString();
|
|
51
77
|
}
|
|
78
|
+
/** Codes (on fetch's `cause` chain) of a connection that closed or reset before the response arrived. */
|
|
79
|
+
const STALE_CONNECTION_CODES = new Set(['UND_ERR_SOCKET', 'ECONNRESET', 'EPIPE', 'UND_ERR_CLOSED']);
|
|
80
|
+
/**
|
|
81
|
+
* True when fetch failed because its connection closed or reset before a response arrived: what a request sees when
|
|
82
|
+
* it went out on a pooled connection the other side closed while it sat idle. Connect failures (ECONNREFUSED,
|
|
83
|
+
* ENOTFOUND, UND_ERR_CONNECT_TIMEOUT), timeouts and aborts are not this.
|
|
84
|
+
*/
|
|
85
|
+
export function isStaleConnectionError(err) {
|
|
86
|
+
if (!(err instanceof TypeError))
|
|
87
|
+
return false;
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
for (let c = err.cause; typeof c === 'object' && c !== null && !seen.has(c); c = c.cause) {
|
|
90
|
+
seen.add(c);
|
|
91
|
+
const code = c.code;
|
|
92
|
+
if (typeof code === 'string' && STALE_CONNECTION_CODES.has(code))
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
52
97
|
function retryAfterSeconds(res) {
|
|
53
98
|
const v = res.headers.get('retry-after');
|
|
54
99
|
if (v === null)
|
|
@@ -89,6 +134,9 @@ export class HttpClient {
|
|
|
89
134
|
const safe = method === 'GET' || method === 'HEAD';
|
|
90
135
|
const retriable = safe || init.idempotencyKey !== undefined || init.idempotent === true;
|
|
91
136
|
const sleep = this.opts.sleep ?? defaultSleep;
|
|
137
|
+
// Retries counted against maxRetries (they also set the backoff); `attempt` counts every send.
|
|
138
|
+
let counted = 0;
|
|
139
|
+
let staleReplayed = false;
|
|
92
140
|
for (let attempt = 0;; attempt += 1) {
|
|
93
141
|
const headers = {
|
|
94
142
|
accept: init.accept ?? 'application/json',
|
|
@@ -108,7 +156,13 @@ export class HttpClient {
|
|
|
108
156
|
}
|
|
109
157
|
const timeoutMs = init.timeoutMs ?? this.opts.timeoutMs;
|
|
110
158
|
const timeout = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
111
|
-
const
|
|
159
|
+
const headersMs = init.headersTimeoutMs ?? 0;
|
|
160
|
+
const headersAbort = headersMs > 0 ? new AbortController() : undefined;
|
|
161
|
+
const headersTimer = headersAbort
|
|
162
|
+
? setTimeout(() => headersAbort.abort(new DOMException('The response headers timed out.', 'TimeoutError')), headersMs)
|
|
163
|
+
: undefined;
|
|
164
|
+
const signals = [timeout, init.signal, headersAbort?.signal].filter((s) => s !== undefined);
|
|
165
|
+
const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
112
166
|
let res;
|
|
113
167
|
try {
|
|
114
168
|
res = await this.opts.fetch(url, { method, headers, ...(body === undefined ? {} : { body: body }), ...(signal ? { signal } : {}) });
|
|
@@ -116,13 +170,27 @@ export class HttpClient {
|
|
|
116
170
|
catch (err) {
|
|
117
171
|
if (init.signal?.aborted)
|
|
118
172
|
throw err;
|
|
119
|
-
if (!retriable
|
|
173
|
+
if (!retriable)
|
|
120
174
|
throw err;
|
|
121
|
-
|
|
122
|
-
|
|
175
|
+
if (!staleReplayed && !signal?.aborted && isStaleConnectionError(err)) {
|
|
176
|
+
// The connection closed before any response, typically a pooled one the server or a NAT closed while idle:
|
|
177
|
+
// send again at once on a fresh connection, once per request and outside maxRetries (as Go's net/http
|
|
178
|
+
// replays a request that failed on a reused connection when it is idempotent or carries an Idempotency-Key).
|
|
179
|
+
staleReplayed = true;
|
|
180
|
+
init.onRetry?.({ request: `${method} ${path}`, attempt: attempt + 1, cause: describeFailure(err), delayMs: 0 });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (counted >= this.opts.maxRetries)
|
|
184
|
+
throw err;
|
|
185
|
+
const delayMs = Math.min(2_000, 200 * 2 ** counted);
|
|
186
|
+
counted += 1;
|
|
187
|
+
init.onRetry?.({ request: `${method} ${path}`, attempt: attempt + 1, cause: describeFailure(err, headersAbort?.signal.aborted ? headersMs : timeoutMs), delayMs });
|
|
123
188
|
await sleep(delayMs);
|
|
124
189
|
continue;
|
|
125
190
|
}
|
|
191
|
+
finally {
|
|
192
|
+
clearTimeout(headersTimer);
|
|
193
|
+
}
|
|
126
194
|
if (res.ok) {
|
|
127
195
|
if (this.opts.onSuccess) {
|
|
128
196
|
try {
|
|
@@ -137,10 +205,15 @@ export class HttpClient {
|
|
|
137
205
|
const error = await errorFrom(res, this.opts.source);
|
|
138
206
|
const retryableStatus = res.status === 429 || res.status === 502 || res.status === 503 || res.status === 504;
|
|
139
207
|
const retryableError = error instanceof ShardfluxApiError ? error.retryable && retryableStatus : retryableStatus;
|
|
140
|
-
|
|
208
|
+
// The working-at-once quota (cell 429 quota_exceeded): the gateway refuses the call at admission, before the
|
|
209
|
+
// workspace is touched, so nothing ran and even a non-idempotent request (exec start, stdin, PTY create,
|
|
210
|
+
// keepalive) is safe to send again. Same Retry-After wait and retry budget as every other retry.
|
|
211
|
+
const admissionRefusal = init.quotaRetry !== false && isWorkingQuotaRefusal(error);
|
|
212
|
+
if ((!retriable && !admissionRefusal) || !retryableError || counted >= this.opts.maxRetries)
|
|
141
213
|
throw error;
|
|
142
214
|
const hinted = error instanceof ShardfluxApiError ? error.retryAfterSeconds : undefined;
|
|
143
|
-
const delayMs = Math.min(5_000, hinted !== undefined ? hinted * 1000 : 200 * 2 **
|
|
215
|
+
const delayMs = Math.min(5_000, hinted !== undefined ? hinted * 1000 : 200 * 2 ** counted);
|
|
216
|
+
counted += 1;
|
|
144
217
|
init.onRetry?.({ request: `${method} ${path}`, attempt: attempt + 1, cause: describeFailure(error), delayMs });
|
|
145
218
|
await sleep(delayMs);
|
|
146
219
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
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
|
-
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,
|
|
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';
|
|
18
|
+
export type { AgentSession, AllocationMode, 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, WaitOptions, WorkspaceInputs, WorkspaceLifetime, WorkspaceMemory, WorkspaceOrigin, WorkspacePurpose, WorkspaceView, } from './client.js';
|
|
19
|
+
export type { FinishedOperation, ForkOptions, LifecycleOptions, ResumeOptions, SuspendOptions, WaitedForkOptions, WaitedLifecycleOptions, WaitedResumeOptions, WaitedSuspendOptions } from './lifecycle.js';
|
|
20
|
+
export { durabilityOf, formatTiming, hostLostOf, isDurable, lostSuspendOf } from './progress.js';
|
|
21
|
+
export type { ColdBootReason, Durability, HostLost, 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';
|
|
@@ -28,7 +28,7 @@ export { TemplateFileError, packDirectory, parseTemplateText, readTemplateFile }
|
|
|
28
28
|
export type { PackedFile, YamlParser } from './template-file.js';
|
|
29
29
|
export { tarEnd, tarHeader, tarPadding } from './tar.js';
|
|
30
30
|
export type { TarEntry, TarEntryType } from './tar.js';
|
|
31
|
-
export type { BuildFromFileEvent, BuildFromFileOptions, BuildFromFileResult, BuildFromRecipeOptions, BuilderAvailability, CreateDraftBody, CreateDraftParams, CreateTemplateBuildParams, CreateTestInstanceBody, CreateVersionTestInstanceBody, CreateVersionTestInstanceParams, DraftOpened, LocalUpload, DraftState, OpenTestInstanceParams, OrgTemplateStorage, PublishDraftBody, PublishDraftParams, PutUploadOptions, SaveAsTemplateBody, SaveAsTemplateParams, SaveAsTemplateResponse, TemplateBuild, TemplateBuildLogUrl, TemplateBuildRecipeV2, TemplateBuildRegistrationState, TemplateBuildState, TemplateCategory, TemplateDefaults, TemplateDefaultsInput, TemplateDetail, TemplateDiffChange, TemplateDiffEntry, TemplateDiffPage, TemplateDiffParams, TemplateDraft, TemplateDraftSummary, TemplateEgressDefault, TemplateFileEntry, TemplateFilePage, TemplateFilesParams, TemplateFilesSummary, TemplateInput, TemplateLanguage, TemplateLanguages, TemplateOwner, TemplatePackage, TemplatePackageEcosystem, TemplatePackagePage, TemplateRecipe, TemplateRecipeV2, TemplateRecipeV2File, TemplateService, TemplateSettings, TemplateSettingsInput, TemplateSource, TemplateStartCommand, TemplateStorage, TemplateStorageWarning, TemplateSummary, TemplateUpload, TemplateUploadRequest, TemplateUploadResponse, TemplateUploadResult, TemplateVersion, TemplateVersionRecipe, TemplateVersionState, UploadData, WaitForBuildOptions, WorkspaceStartup, } from './templates.js';
|
|
31
|
+
export type { BuildFromFileEvent, BuildFromFileOptions, BuildFromFileResult, BuildFromRecipeOptions, BuilderAvailability, CreateDraftBody, CreateDraftParams, CreateTemplateBuildParams, CreateTestInstanceBody, CreateVersionTestInstanceBody, CreateVersionTestInstanceParams, DraftOpened, LocalUpload, DraftState, OpenTestInstanceParams, OrgTemplateStorage, PublishDraftBody, PublishDraftParams, PutUploadOptions, SaveAsTemplateBody, SaveAsTemplateParams, SaveAsTemplateResponse, TemplateBuild, TemplateBuildLogUrl, TemplateBuildRecipeV2, TemplateBuildRegistrationState, TemplateBuildState, TemplateCategory, TemplateDefaults, TemplateDefaultsInput, TemplateDetail, TemplateDiffChange, TemplateDiffEntry, TemplateDiffPage, TemplateDiffParams, TemplateDraft, TemplateDraftSummary, TemplateEgressDefault, TemplateFileEntry, TemplateFilePage, TemplateFilesParams, TemplateFilesSummary, TemplateInput, TemplateLanguage, TemplateLanguages, TemplateOwner, TemplatePackage, TemplatePackageEcosystem, TemplatePackagePage, TemplateRecipe, TemplateRecipeV2, TemplateRecipeV2File, TemplateService, TemplateSettings, TemplateSettingsInput, TemplateSource, TemplateStartCommand, TemplateStorage, TemplateStorageWarning, TemplateSummary, TemplateUpload, TemplateUploadRequest, TemplateUploadResponse, TemplateUploadResult, TemplateVersion, TemplateVersionImmutable, TemplateVersionRecipe, TemplateVersionState, UploadData, WaitForBuildOptions, WorkspaceStartup, } from './templates.js';
|
|
32
32
|
export { SecretsApi, WorkspaceSecrets } from './secrets.js';
|
|
33
33
|
export type { BoundSecretStatus, CreateOrganizationSecretParams, CreateSecretParams, Secret, SecretAccessEvent, SecretPermissions, SecretScope, SecretVersion, UpdateSecretParams, WorkspaceSecretBindings, } from './secrets.js';
|
|
34
34
|
export { EgressPolicyApi } from './egress.js';
|
|
@@ -42,7 +42,7 @@ export type { HintOptions, HintResult, WakeOptions } from './workspace.js';
|
|
|
42
42
|
export { CellClient, DEFAULT_TRANSITION_TIMEOUT_MS, cellPath, ndjson } from './cell.js';
|
|
43
43
|
export { EXECUTION_ID, ExecutionResult, newExecutionId } from './executions.js';
|
|
44
44
|
export type { ExecutionChange, ExecutionError, ExecutionGetOptions, ExecutionResultBody, ExecutionRunOptions, ExecutionState } from './executions.js';
|
|
45
|
-
export type { TreeRevisionOptions, BrowserContent, BrowserContentRequest, BrowserScreenshotRequest, CellClientOptions, ExecSession, ExecStartRequest, FileEdit, FileInfo, FileList, FilePatchEdit, FilePatchParams, FilePatchRequest, FilePatchResult, FileReadResult, FileRevision, FileSearchMatch, FileSearchOptions, FileSearchRequest, FileSearchResponse, FileSearchResult, FileWriteResult, GitResult, GitStatus, OutputEvent, ProcessList, PtyOpenRequest, PtySession, Residency, RunOptions, RunResult, ServedFrom, Signal, WakeHintResult, WorkspaceChange, WorkspaceChangeKind, WorkspaceChangesPage, WorkspaceChangesParams, WorkspaceChangesSummary, } from './cell.js';
|
|
45
|
+
export type { TreeRevisionOptions, BrowserContent, BrowserContentRequest, BrowserScreenshotRequest, CellClientOptions, ExecSession, ExecStartRequest, MemoryGrow, BurstSummary, BurstError, FileEdit, FileInfo, FileList, FilePatchEdit, FilePatchParams, FilePatchRequest, FilePatchResult, FileReadResult, FileRevision, FileSearchMatch, FileSearchOptions, FileSearchRequest, FileSearchResponse, FileSearchResult, FileWriteResult, GitResult, GitStatus, OutputEvent, ProcessList, PtyOpenRequest, PtySession, Residency, RunOptions, RunResult, ServedFrom, Signal, WakeHintResult, WorkspaceChange, WorkspaceChangeKind, WorkspaceChangesPage, WorkspaceChangesParams, WorkspaceChangesSummary, } from './cell.js';
|
|
46
46
|
export { ToolTokenManager } from './tokens.js';
|
|
47
47
|
export type { ToolName, ToolToken, ToolTokenOptions } from './tokens.js';
|
|
48
48
|
export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from './tools.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog, pickByKey } from "./client.js";
|
|
2
|
-
export { durabilityOf, formatTiming, isDurable, lostSuspendOf } from "./progress.js";
|
|
2
|
+
export { durabilityOf, formatTiming, hostLostOf, 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";
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -61,6 +61,11 @@ export interface ResumeOptions extends LifecycleOptions {
|
|
|
61
61
|
export type WaitedResumeOptions = ResumeOptions & {
|
|
62
62
|
wait: true | WaitOptions;
|
|
63
63
|
};
|
|
64
|
+
/** A waited fork (0.13.0+) returns the target view and token together when the API supports it. */
|
|
65
|
+
export type ForkOptions = ResumeOptions;
|
|
66
|
+
export type WaitedForkOptions = ForkOptions & {
|
|
67
|
+
wait: true | WaitOptions;
|
|
68
|
+
};
|
|
64
69
|
/** Internal: the trace a wait continues instead of starting its own (open(), wake() and waited lifecycle calls). */
|
|
65
70
|
export declare const TRACE: unique symbol;
|
|
66
71
|
/** Internal: work a workspace handle does after the operation finished, inside the same trace (refreshing its view). */
|
|
@@ -88,6 +93,7 @@ export declare function waitOptionsOf(opts: LifecycleOptions): WaitOptions | nul
|
|
|
88
93
|
* Starts a lifecycle operation with `start` and, when `opts.wait` asks for it, waits for it to finish. The trace covers
|
|
89
94
|
* the request, every observed state, and `AFTER_WAIT`; it ends with `done` either way.
|
|
90
95
|
*/
|
|
91
|
-
export declare function runLifecycle(ctx: ClientContext, kind: LifecycleAction, workspaceId: string, start: (init: Pick<RequestOptions, 'onRetry'
|
|
96
|
+
export declare function runLifecycle(ctx: ClientContext, kind: LifecycleAction, workspaceId: string, start: (init: Pick<RequestOptions, 'onRetry'>, trace: Trace) => Promise<Operation>, opts: InternalLifecycleOptions, capture?: {
|
|
92
97
|
settle?: boolean;
|
|
98
|
+
requestReason?: string | null;
|
|
93
99
|
}): Promise<Operation>;
|
package/dist/lifecycle.js
CHANGED
|
@@ -28,8 +28,8 @@ export async function runLifecycle(ctx, kind, workspaceId, start, opts, capture
|
|
|
28
28
|
const pending = capture.settle ? ctx.captures.settle(workspaceId) : undefined;
|
|
29
29
|
if (pending)
|
|
30
30
|
await trace.span('capture_flush', () => pending);
|
|
31
|
-
trace.phase('request');
|
|
32
|
-
const operation = await start({ onRetry: trace.onRetry });
|
|
31
|
+
trace.phase('request', capture.requestReason ?? null);
|
|
32
|
+
const operation = await start({ onRetry: trace.onRetry }, trace);
|
|
33
33
|
trace.observe(operation);
|
|
34
34
|
if (!waitOpts)
|
|
35
35
|
return operation;
|