@shardflux/sdk 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -2
- package/README.md +131 -15
- package/dist/account.d.ts +41 -2
- package/dist/account.js +36 -2
- package/dist/cell.d.ts +21 -4
- package/dist/cell.js +67 -10
- package/dist/client.d.ts +86 -2
- package/dist/client.js +148 -0
- package/dist/codex-proof.d.ts +53 -0
- package/dist/codex-proof.js +200 -0
- package/dist/errors.d.ts +20 -1
- package/dist/errors.js +10 -0
- package/dist/generated/app-api.d.ts +1995 -255
- package/dist/generated/cell-api.d.ts +19 -9
- package/dist/http.d.ts +40 -3
- package/dist/http.js +74 -10
- package/dist/index.d.ts +9 -5
- package/dist/index.js +3 -1
- package/dist/ports.d.ts +117 -0
- package/dist/ports.js +62 -0
- package/dist/progress.d.ts +52 -8
- package/dist/progress.js +37 -1
- package/dist/tools.d.ts +8 -0
- package/dist/tools.js +20 -0
- package/dist/usage.d.ts +3 -1
- package/dist/usage.js +3 -1
- package/dist/workspace.d.ts +43 -7
- package/dist/workspace.js +41 -3
- package/package.json +1 -1
|
@@ -191,7 +191,7 @@ export interface paths {
|
|
|
191
191
|
* Client messages are JSON `ExecClientMessage`. Close codes: 1000 after
|
|
192
192
|
* the `exit` event, 1001 on gateway shutdown (reconnect with offsets),
|
|
193
193
|
* 4401 unauthenticated, 4403 forbidden, 4409 stale epoch / workspace
|
|
194
|
-
* busy / workspace not running, 4410 workspace gone, 4429 rate limited,
|
|
194
|
+
* busy / workspace not running, 4410 workspace gone, 4429 rate limited / quota exceeded,
|
|
195
195
|
* 4500/4503/4504 internal/unavailable/timeout (4000 + HTTP status; see
|
|
196
196
|
* "WebSocket close codes" above; the close reason is compact JSON with
|
|
197
197
|
* the error `code`). A refused upgrade from an allowed Origin is
|
|
@@ -265,14 +265,18 @@ export interface paths {
|
|
|
265
265
|
get?: never;
|
|
266
266
|
put?: never;
|
|
267
267
|
/**
|
|
268
|
-
*
|
|
269
|
-
* @description Sends SIGTERM to the
|
|
270
|
-
* `grace_ms` (0 to 60000;
|
|
271
|
-
*
|
|
272
|
-
* grace and the SIGKILL. A
|
|
273
|
-
*
|
|
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`
|
|
274
279
|
* (`details.field` `grace_ms`, `details.minimum`, `details.maximum`).
|
|
275
|
-
* An ended session is returned as it is.
|
|
276
280
|
*/
|
|
277
281
|
post: operations["execCancel"];
|
|
278
282
|
delete?: never;
|
|
@@ -876,7 +880,7 @@ export type webhooks = Record<string, never>;
|
|
|
876
880
|
export interface components {
|
|
877
881
|
schemas: {
|
|
878
882
|
/** @enum {string} */
|
|
879
|
-
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";
|
|
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";
|
|
880
884
|
ErrorBody: {
|
|
881
885
|
error: {
|
|
882
886
|
code: components["schemas"]["ErrorCode"];
|
|
@@ -949,6 +953,12 @@ export interface components {
|
|
|
949
953
|
burst_vcpus?: number;
|
|
950
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. */
|
|
951
955
|
burst_memory_mib?: number;
|
|
956
|
+
/**
|
|
957
|
+
* @description Contracts §41.1: whether an elastic workspace's memory grows before the command starts. `heavy` grows it to the exec-start size first; `light` starts at once (pressure grows cover a spike); `auto` (default) grows only for a heavy command family (package installers, test runners, compilers, type checkers, bundlers). Changes only elastic workspaces in memory layout v2; any other value is 422 validation_failed (details.field resource_hint).
|
|
958
|
+
* @default auto
|
|
959
|
+
* @enum {string}
|
|
960
|
+
*/
|
|
961
|
+
resource_hint?: "auto" | "light" | "heavy";
|
|
952
962
|
};
|
|
953
963
|
/** @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). */
|
|
954
964
|
SecretRefs: string[];
|
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.14.0";
|
|
3
3
|
export interface RequestOptions {
|
|
4
4
|
query?: Record<string, string | number | boolean | undefined | null>;
|
|
5
5
|
json?: unknown;
|
|
@@ -10,6 +10,11 @@ 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;
|
|
@@ -33,13 +38,45 @@ export interface HttpOptions {
|
|
|
33
38
|
onSuccess?: (() => void) | undefined;
|
|
34
39
|
}
|
|
35
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
|
+
};
|
|
36
66
|
/**
|
|
37
67
|
* Reuses TLS connections. Node 26's bundled undici 8.9 can stall reused connections: use pinned undici with a
|
|
38
|
-
* private HTTP/1.1-only dispatcher
|
|
39
|
-
* 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.
|
|
40
71
|
*/
|
|
41
72
|
export declare function defaultFetch(env?: Record<string, string | undefined> | undefined, undiciVersion?: string | undefined): typeof fetch;
|
|
42
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;
|
|
43
80
|
/** `X-Tree-Revision` (file-first workspaces): a non-negative integer, else null. */
|
|
44
81
|
export declare function treeRevisionOf(headers: Headers): number | null;
|
|
45
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.14.0';
|
|
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',
|
|
@@ -122,9 +170,20 @@ export class HttpClient {
|
|
|
122
170
|
catch (err) {
|
|
123
171
|
if (init.signal?.aborted)
|
|
124
172
|
throw err;
|
|
125
|
-
if (!retriable
|
|
173
|
+
if (!retriable)
|
|
174
|
+
throw err;
|
|
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)
|
|
126
184
|
throw err;
|
|
127
|
-
const delayMs = Math.min(2_000, 200 * 2 **
|
|
185
|
+
const delayMs = Math.min(2_000, 200 * 2 ** counted);
|
|
186
|
+
counted += 1;
|
|
128
187
|
init.onRetry?.({ request: `${method} ${path}`, attempt: attempt + 1, cause: describeFailure(err, headersAbort?.signal.aborted ? headersMs : timeoutMs), delayMs });
|
|
129
188
|
await sleep(delayMs);
|
|
130
189
|
continue;
|
|
@@ -146,10 +205,15 @@ export class HttpClient {
|
|
|
146
205
|
const error = await errorFrom(res, this.opts.source);
|
|
147
206
|
const retryableStatus = res.status === 429 || res.status === 502 || res.status === 503 || res.status === 504;
|
|
148
207
|
const retryableError = error instanceof ShardfluxApiError ? error.retryable && retryableStatus : retryableStatus;
|
|
149
|
-
|
|
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)
|
|
150
213
|
throw error;
|
|
151
214
|
const hinted = error instanceof ShardfluxApiError ? error.retryAfterSeconds : undefined;
|
|
152
|
-
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;
|
|
153
217
|
init.onRetry?.({ request: `${method} ${path}`, attempt: attempt + 1, cause: describeFailure(error), delayMs });
|
|
154
218
|
await sleep(delayMs);
|
|
155
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, 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';
|
|
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, ResizeAppliesAt, ResizeCpu, ResizeDeferReason, ResizeDisk, ResizeLimitReason, ResizeMemory, ResizeParams, ResizeResult, ResizeResponse, ResumeAnswer, ResumeRequestOptions, ResumeResponse, ShardfluxOptions, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResponse, SuspendWhenIdleResult, WaitOptions, WorkspaceInputs, WorkspaceLifetime, WorkspaceMemory, WorkspaceOrigin, WorkspacePurpose, WorkspaceView, } from './client.js';
|
|
19
19
|
export type { FinishedOperation, ForkOptions, LifecycleOptions, ResumeOptions, SuspendOptions, WaitedForkOptions, 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';
|
|
20
|
+
export { COLD_BOOT_REASONS, 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';
|
|
@@ -29,6 +29,8 @@ 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
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
|
+
export { WorkspacePorts } from './ports.js';
|
|
33
|
+
export type { ExposePortResult, ExposedPort, PortCallbackResponse, PortCallbackUrl, PortLink, PortLinkOptions, PortLinkResponse, PortToken, PortTokenOptions, PortTokenResponse, PortView } from './ports.js';
|
|
32
34
|
export { SecretsApi, WorkspaceSecrets } from './secrets.js';
|
|
33
35
|
export type { BoundSecretStatus, CreateOrganizationSecretParams, CreateSecretParams, Secret, SecretAccessEvent, SecretPermissions, SecretScope, SecretVersion, UpdateSecretParams, WorkspaceSecretBindings, } from './secrets.js';
|
|
34
36
|
export { EgressPolicyApi } from './egress.js';
|
|
@@ -42,7 +44,7 @@ export type { HintOptions, HintResult, WakeOptions } from './workspace.js';
|
|
|
42
44
|
export { CellClient, DEFAULT_TRANSITION_TIMEOUT_MS, cellPath, ndjson } from './cell.js';
|
|
43
45
|
export { EXECUTION_ID, ExecutionResult, newExecutionId } from './executions.js';
|
|
44
46
|
export type { ExecutionChange, ExecutionError, ExecutionGetOptions, ExecutionResultBody, ExecutionRunOptions, ExecutionState } from './executions.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';
|
|
47
|
+
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, ResourceHint, RunOptions, RunResult, ServedFrom, Signal, WakeHintResult, WorkspaceChange, WorkspaceChangeKind, WorkspaceChangesPage, WorkspaceChangesParams, WorkspaceChangesSummary, } from './cell.js';
|
|
46
48
|
export { ToolTokenManager } from './tokens.js';
|
|
47
49
|
export type { ToolName, ToolToken, ToolTokenOptions } from './tokens.js';
|
|
48
50
|
export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from './tools.js';
|
|
@@ -54,7 +56,9 @@ export { DurabilityLostError, ExecStartError, NotSupportedForModeError, Operatio
|
|
|
54
56
|
export type { AppErrorCode, CellErrorCode, ErrorCode, ErrorReason, KnownErrorReason, WorkspaceMode } from './errors.js';
|
|
55
57
|
export { SDK_VERSION } from './http.js';
|
|
56
58
|
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
|
-
export
|
|
59
|
+
export { codexIdentityProof } from './codex-proof.js';
|
|
60
|
+
export type { CodexIdentityProof, CodexProofOptions, CodexProofUnavailable } from './codex-proof.js';
|
|
61
|
+
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, SignupResult, SignupAccess, EmailCodeResult, ResendVerificationResult, SessionTokenUpdate, ShardfluxAccountOptions, SpendPolicyUpdate, StepUpResult, TotpConfirmResult, TotpDisableResult, TotpEnrollment, VerifyEmailResult, WaitForCheckoutOptions, } from './account.js';
|
|
58
62
|
export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from './version-check.js';
|
|
59
63
|
export type { CheckClientVersionOptions, ClientEcosystem, ClientVersionEntry, ClientVersionStatus, ClientVersionStatusKind, ClientVersions, VersionCheckIdentity, VersionCheckOption, } from './version-check.js';
|
|
60
64
|
export { isWorkspaceGone } from './errors.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog, pickByKey } from "./client.js";
|
|
2
|
-
export { durabilityOf, formatTiming, isDurable, lostSuspendOf } from "./progress.js";
|
|
2
|
+
export { COLD_BOOT_REASONS, 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";
|
|
6
6
|
export { TemplateFileError, packDirectory, parseTemplateText, readTemplateFile } from "./template-file.js";
|
|
7
7
|
export { tarEnd, tarHeader, tarPadding } from "./tar.js";
|
|
8
|
+
export { WorkspacePorts } from "./ports.js";
|
|
8
9
|
export { SecretsApi, WorkspaceSecrets } from "./secrets.js";
|
|
9
10
|
export { EgressPolicyApi } from "./egress.js";
|
|
10
11
|
export { VolumesApi } from "./volumes.js";
|
|
@@ -18,6 +19,7 @@ export { CaptureError, ToolCallCapture, captureTool } from "./capture.js";
|
|
|
18
19
|
export { DurabilityLostError, ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from "./errors.js";
|
|
19
20
|
export { SDK_VERSION } from "./http.js";
|
|
20
21
|
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";
|
|
22
|
+
export { codexIdentityProof } from "./codex-proof.js";
|
|
21
23
|
export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from "./version-check.js";
|
|
22
24
|
export { isWorkspaceGone } from "./errors.js";
|
|
23
25
|
export { defaultFetch } from "./http.js";
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound ports (0.14.0; contracts §39.3): serve a TCP port of a processful workspace at its own HTTPS URL,
|
|
3
|
+
* `https://<port>-<handle>.<ingress domain>`. Every port is private: a request carries a port token
|
|
4
|
+
* (`Authorization: Bearer sfp_…`), a signed link's browser session, or arrives on the port's callback URL. A request
|
|
5
|
+
* to a suspended or parked workspace wakes it and is served once it runs.
|
|
6
|
+
*
|
|
7
|
+
* const { url } = await workspace.ports.expose(3000);
|
|
8
|
+
* const { token } = await workspace.ports.token(3000);
|
|
9
|
+
* await fetch(url, { headers: { authorization: `Bearer ${token}` } });
|
|
10
|
+
*
|
|
11
|
+
* Types are written by hand (checked against the generated contract in type-checks.ts).
|
|
12
|
+
*/
|
|
13
|
+
import type { components, operations } from './generated/app-api.js';
|
|
14
|
+
import type { ClientContext } from './client.js';
|
|
15
|
+
type Created<Op> = Op extends {
|
|
16
|
+
responses: {
|
|
17
|
+
201: {
|
|
18
|
+
content: {
|
|
19
|
+
'application/json': infer T;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
} ? T : never;
|
|
24
|
+
/** An exposed port as the API sends it (`Port`). */
|
|
25
|
+
export type PortView = components['schemas']['Port'];
|
|
26
|
+
/** POST …/ports/{port}/tokens (201). */
|
|
27
|
+
export type PortTokenResponse = Created<operations['postV1WorkspacesWorkspaceIdPortsPortTokens']>;
|
|
28
|
+
/** POST …/ports/{port}/links (201). */
|
|
29
|
+
export type PortLinkResponse = Created<operations['postV1WorkspacesWorkspaceIdPortsPortLinks']>;
|
|
30
|
+
/** POST …/ports/{port}/callback (201). */
|
|
31
|
+
export type PortCallbackResponse = Created<operations['postV1WorkspacesWorkspaceIdPortsPortCallback']>;
|
|
32
|
+
/** An exposed port of a workspace. */
|
|
33
|
+
export interface ExposedPort {
|
|
34
|
+
/** TCP port inside the workspace (1-65535). */
|
|
35
|
+
port: number;
|
|
36
|
+
/** `https://<port>-<handle>.<ingress domain>`: requests need a port token, a link's browser session or the callback URL. */
|
|
37
|
+
url: string;
|
|
38
|
+
/** When the port was exposed (RFC 3339). Exposing it again after a close starts a new exposure. */
|
|
39
|
+
createdAt: string;
|
|
40
|
+
/** Set when the port has a callback URL (its secret is shown only by `createCallbackUrl()`). */
|
|
41
|
+
callback: {
|
|
42
|
+
createdAt: string;
|
|
43
|
+
} | null;
|
|
44
|
+
}
|
|
45
|
+
/** `expose()`: the port, and whether this call exposed it (201) or it already was (200, unchanged). */
|
|
46
|
+
export interface ExposePortResult extends ExposedPort {
|
|
47
|
+
created: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** A port token: send it as `Authorization: Bearer <token>` (or `X-Shardflux-Token`) to `url`. */
|
|
50
|
+
export interface PortToken {
|
|
51
|
+
/** `sfp_…`. Revoked when the port is closed, the workspace is deleted or the API key that minted it is revoked. */
|
|
52
|
+
token: string;
|
|
53
|
+
/** RFC 3339. */
|
|
54
|
+
expiresAt: string;
|
|
55
|
+
/** The port's URL. */
|
|
56
|
+
url: string;
|
|
57
|
+
}
|
|
58
|
+
/** A signed link: opening it in a browser starts a session for the port (a cookie until `expiresAt`) and lands on its path. */
|
|
59
|
+
export interface PortLink {
|
|
60
|
+
url: string;
|
|
61
|
+
/** RFC 3339: the link and the browser session it starts end then. */
|
|
62
|
+
expiresAt: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The port's callback URL: register `url` plus your own path with GitHub, Slack, Stripe or an OAuth provider. A request
|
|
66
|
+
* to `<url><rest>` reaches `/<rest>` (query kept) without a token header and wakes the workspace like any request.
|
|
67
|
+
*/
|
|
68
|
+
export interface PortCallbackUrl {
|
|
69
|
+
/** `https://<port>-<handle>.<domain>/__shardflux/callback/sfcb_…/`. Shown once: the API keeps only its hash. */
|
|
70
|
+
url: string;
|
|
71
|
+
/** RFC 3339. */
|
|
72
|
+
createdAt: string;
|
|
73
|
+
}
|
|
74
|
+
export interface PortTokenOptions {
|
|
75
|
+
/** Lifetime in seconds: 60-86400 (default 3600). */
|
|
76
|
+
ttlSeconds?: number;
|
|
77
|
+
}
|
|
78
|
+
export interface PortLinkOptions {
|
|
79
|
+
/** Lifetime of the link and of the browser session it starts, in seconds: 60-604800 (default 86400). */
|
|
80
|
+
ttlSeconds?: number;
|
|
81
|
+
/** Where the link lands: a path starting with one `/`, printable ASCII, query allowed (default `/`). */
|
|
82
|
+
path?: string;
|
|
83
|
+
}
|
|
84
|
+
export declare function exposedPortOf(view: PortView): ExposedPort;
|
|
85
|
+
/**
|
|
86
|
+
* The exposed ports of one workspace (`workspace.ports`, `cloud.workspaces.ports(id)`). Errors are ShardfluxApiError:
|
|
87
|
+
* 403 `forbidden` reason `inbound_ports_not_available` (inbound ports are not enabled for the organization), 404
|
|
88
|
+
* `not_found` reason `port_not_exposed` (tokens, links and callback URLs of a port that is not exposed), 409 `conflict`
|
|
89
|
+
* reason `port_limit` (at most 10 exposed ports, `details.limit`) or `workspace_deleted`, NotSupportedForModeError for a
|
|
90
|
+
* file-first workspace, 422 `validation_failed` for a port outside 1-65535 or an option out of range.
|
|
91
|
+
*/
|
|
92
|
+
export declare class WorkspacePorts {
|
|
93
|
+
#private;
|
|
94
|
+
constructor(ctx: ClientContext, workspaceId: string);
|
|
95
|
+
/**
|
|
96
|
+
* Exposes TCP `port` at its own HTTPS URL (idempotent: a port already exposed is returned unchanged, `created`
|
|
97
|
+
* false, and its tokens, links and callback URL stay valid). The server inside the workspace must listen on
|
|
98
|
+
* 0.0.0.0 (all interfaces), not only on 127.0.0.1.
|
|
99
|
+
*/
|
|
100
|
+
expose(port: number): Promise<ExposePortResult>;
|
|
101
|
+
/** The exposed ports, ordered by port. */
|
|
102
|
+
list(): Promise<ExposedPort[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Closes the port (idempotent: also when it is not exposed). Its tokens, links and callback URL stop working at once,
|
|
105
|
+
* and stay revoked if the port is exposed again.
|
|
106
|
+
*/
|
|
107
|
+
close(port: number): Promise<void>;
|
|
108
|
+
/** Mints a port token for an exposed port (`use` bearer): `Authorization: Bearer <token>` on requests to `url`. */
|
|
109
|
+
token(port: number, opts?: PortTokenOptions): Promise<PortToken>;
|
|
110
|
+
/** A signed link that opens the port in a browser. Share it like a password: anyone with it can open the port until it expires. */
|
|
111
|
+
link(port: number, opts?: PortLinkOptions): Promise<PortLink>;
|
|
112
|
+
/** Creates the port's callback URL, or replaces it (the previous one stops working). Its secret is shown only here. */
|
|
113
|
+
createCallbackUrl(port: number): Promise<PortCallbackUrl>;
|
|
114
|
+
/** Revokes the port's callback URL (idempotent while the port is exposed). */
|
|
115
|
+
revokeCallbackUrl(port: number): Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
export {};
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function exposedPortOf(view) {
|
|
2
|
+
return { port: view.port, url: view.url, createdAt: view.created_at, callback: view.callback ? { createdAt: view.callback.created_at } : null };
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* The exposed ports of one workspace (`workspace.ports`, `cloud.workspaces.ports(id)`). Errors are ShardfluxApiError:
|
|
6
|
+
* 403 `forbidden` reason `inbound_ports_not_available` (inbound ports are not enabled for the organization), 404
|
|
7
|
+
* `not_found` reason `port_not_exposed` (tokens, links and callback URLs of a port that is not exposed), 409 `conflict`
|
|
8
|
+
* reason `port_limit` (at most 10 exposed ports, `details.limit`) or `workspace_deleted`, NotSupportedForModeError for a
|
|
9
|
+
* file-first workspace, 422 `validation_failed` for a port outside 1-65535 or an option out of range.
|
|
10
|
+
*/
|
|
11
|
+
export class WorkspacePorts {
|
|
12
|
+
#ctx;
|
|
13
|
+
#workspaceId;
|
|
14
|
+
constructor(ctx, workspaceId) {
|
|
15
|
+
this.#ctx = ctx;
|
|
16
|
+
this.#workspaceId = workspaceId;
|
|
17
|
+
}
|
|
18
|
+
#path(port, rest = '') {
|
|
19
|
+
const base = `/v1/workspaces/${encodeURIComponent(this.#workspaceId)}/ports`;
|
|
20
|
+
return port === undefined ? base : `${base}/${encodeURIComponent(String(port))}${rest}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Exposes TCP `port` at its own HTTPS URL (idempotent: a port already exposed is returned unchanged, `created`
|
|
24
|
+
* false, and its tokens, links and callback URL stay valid). The server inside the workspace must listen on
|
|
25
|
+
* 0.0.0.0 (all interfaces), not only on 127.0.0.1.
|
|
26
|
+
*/
|
|
27
|
+
async expose(port) {
|
|
28
|
+
const r = await this.#ctx.http.jsonWithStatus('PUT', this.#path(port), { idempotent: true }, this.#ctx.authorization);
|
|
29
|
+
return { ...exposedPortOf(r.body), created: r.status === 201 };
|
|
30
|
+
}
|
|
31
|
+
/** The exposed ports, ordered by port. */
|
|
32
|
+
async list() {
|
|
33
|
+
const body = await this.#ctx.http.json('GET', this.#path(), {}, this.#ctx.authorization);
|
|
34
|
+
return body.ports.map(exposedPortOf);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Closes the port (idempotent: also when it is not exposed). Its tokens, links and callback URL stop working at once,
|
|
38
|
+
* and stay revoked if the port is exposed again.
|
|
39
|
+
*/
|
|
40
|
+
async close(port) {
|
|
41
|
+
await this.#ctx.http.json('DELETE', this.#path(port), { idempotent: true }, this.#ctx.authorization);
|
|
42
|
+
}
|
|
43
|
+
/** Mints a port token for an exposed port (`use` bearer): `Authorization: Bearer <token>` on requests to `url`. */
|
|
44
|
+
async token(port, opts = {}) {
|
|
45
|
+
const body = await this.#ctx.http.json('POST', this.#path(port, '/tokens'), { json: opts.ttlSeconds !== undefined ? { ttl_seconds: opts.ttlSeconds } : {} }, this.#ctx.authorization);
|
|
46
|
+
return { token: body.token, expiresAt: body.expires_at, url: body.url };
|
|
47
|
+
}
|
|
48
|
+
/** A signed link that opens the port in a browser. Share it like a password: anyone with it can open the port until it expires. */
|
|
49
|
+
async link(port, opts = {}) {
|
|
50
|
+
const body = await this.#ctx.http.json('POST', this.#path(port, '/links'), { json: { ...(opts.ttlSeconds !== undefined ? { ttl_seconds: opts.ttlSeconds } : {}), ...(opts.path !== undefined ? { path: opts.path } : {}) } }, this.#ctx.authorization);
|
|
51
|
+
return { url: body.url, expiresAt: body.expires_at };
|
|
52
|
+
}
|
|
53
|
+
/** Creates the port's callback URL, or replaces it (the previous one stops working). Its secret is shown only here. */
|
|
54
|
+
async createCallbackUrl(port) {
|
|
55
|
+
const body = await this.#ctx.http.json('POST', this.#path(port, '/callback'), {}, this.#ctx.authorization);
|
|
56
|
+
return { url: body.url, createdAt: body.created_at };
|
|
57
|
+
}
|
|
58
|
+
/** Revokes the port's callback URL (idempotent while the port is exposed). */
|
|
59
|
+
async revokeCallbackUrl(port) {
|
|
60
|
+
await this.#ctx.http.json('DELETE', this.#path(port, '/callback'), { idempotent: true }, this.#ctx.authorization);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/progress.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type { components } from './generated/app-api.js';
|
|
19
19
|
type Operation = components['schemas']['Operation'];
|
|
20
20
|
/** The SDK call a trace follows. `wait` is a direct waitForOperation(); `token` a tool token fetched for tool calls. */
|
|
21
|
-
export type LifecycleAction = 'open' | 'suspend' | 'resume' | 'snapshot' | 'fork' | 'delete' | 'close' | 'reset' | 'wake' | 'wait' | 'token';
|
|
21
|
+
export type LifecycleAction = 'open' | 'suspend' | 'resume' | 'snapshot' | 'fork' | 'delete' | 'close' | 'reset' | 'resize' | 'wake' | 'wait' | 'token';
|
|
22
22
|
/**
|
|
23
23
|
* - `request`: an API request that starts or joins the operation. A held open (`Prefer: wait`) spends the server's
|
|
24
24
|
* hold here (reason `held`), so the states inside it show only in the server timing.
|
|
@@ -74,9 +74,10 @@ export interface ServerTiming {
|
|
|
74
74
|
warmFallback: string | null;
|
|
75
75
|
/**
|
|
76
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
|
|
78
|
-
*
|
|
79
|
-
* reset).
|
|
77
|
+
* `download` (the checkpoint had to be fetched first), `cold_boot` (0.11.0+: the saved disk was booted instead of
|
|
78
|
+
* restoring memory, see `coldBootReason`; processes restarted; see `memoryRestored`), `reset_blank_layer` (the first
|
|
79
|
+
* start after a reset) or `thaw` (0.13.1+: resumed while its suspend was still being written; the same VM continued
|
|
80
|
+
* in place).
|
|
80
81
|
*/
|
|
81
82
|
resumePath: string | null;
|
|
82
83
|
/**
|
|
@@ -87,10 +88,12 @@ export interface ServerTiming {
|
|
|
87
88
|
*/
|
|
88
89
|
memoryRestored?: boolean | null;
|
|
89
90
|
/**
|
|
90
|
-
* `result.cold_boot_reason` (0.11.0+), with `resumePath` `cold_boot`: why the memory could not be restored
|
|
91
|
-
* `runtime_changed` (the platform's VM runtime changed after the suspend).
|
|
91
|
+
* `result.cold_boot_reason` (0.11.0+), with `resumePath` `cold_boot`: why the memory could not be restored:
|
|
92
|
+
* `runtime_changed` (the platform's VM runtime changed after the suspend), `host_lost` (0.13.1+: the machine the
|
|
93
|
+
* workspace ran on failed; it booted from its disk, files kept, see `hostLost`) or `runtime_retired` (0.14.0+: the
|
|
94
|
+
* runtime the workspace was suspended on was retired after an announced window). Null otherwise.
|
|
92
95
|
*/
|
|
93
|
-
coldBootReason?:
|
|
96
|
+
coldBootReason?: ColdBootReason | null;
|
|
94
97
|
/**
|
|
95
98
|
* `result.durable` (0.12.0+), suspend and fork: `true` when the capture is in durable storage, `false` while its
|
|
96
99
|
* durable copy is being written (see `durability`). `null` when the result does not say (other kinds, an older API).
|
|
@@ -105,6 +108,12 @@ export interface ServerTiming {
|
|
|
105
108
|
* checkpoint before it (`restoredCheckpointId`, state as of `stateAsOf`). Null otherwise.
|
|
106
109
|
*/
|
|
107
110
|
lostSuspend?: LostSuspend | null;
|
|
111
|
+
/**
|
|
112
|
+
* `result.host_lost` (0.13.1+): the machine the workspace ran on failed. A resume (or open) that restored the
|
|
113
|
+
* workspace says from what (`restoredFrom` `disk`, or `checkpoint` with `stateAsOf`); a suspend that found it so
|
|
114
|
+
* succeeds with only `detectedAt`. Null otherwise.
|
|
115
|
+
*/
|
|
116
|
+
hostLost?: HostLost | null;
|
|
108
117
|
/** `result.boot_to_ready_ms`: VM start until the guest agent answered. */
|
|
109
118
|
bootToReadyMs: number | null;
|
|
110
119
|
/** `result.host_timings_ms`: the host's own steps (restore: load, after_restore, ready, …). */
|
|
@@ -143,6 +152,33 @@ export interface LostSuspend {
|
|
|
143
152
|
/** The restored state is as of this time (RFC 3339). */
|
|
144
153
|
stateAsOf: string | null;
|
|
145
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* `result.cold_boot_reason` (0.11.0+): `runtime_changed` (the platform's VM runtime changed after the suspend),
|
|
157
|
+
* `host_lost` (0.13.1+: the machine the workspace ran on failed) or `runtime_retired` (0.14.0+: the runtime the
|
|
158
|
+
* workspace was suspended on was retired after an announced window). Any other string is a reason this version does
|
|
159
|
+
* not know.
|
|
160
|
+
*/
|
|
161
|
+
export type ColdBootReason = (typeof COLD_BOOT_REASONS)[number] | (string & {});
|
|
162
|
+
/** Every {@link ColdBootReason} this version knows (0.14.0+). */
|
|
163
|
+
export declare const COLD_BOOT_REASONS: readonly ["runtime_changed", "host_lost", "runtime_retired"];
|
|
164
|
+
/**
|
|
165
|
+
* `result.host_lost` (0.13.1+), camelCased: the machine the workspace ran on failed. The workspace was moved to
|
|
166
|
+
* `suspended` at that moment, and its next use (a resume, a tool call's wake, an open) restored it. See the lifecycle
|
|
167
|
+
* reference.
|
|
168
|
+
*/
|
|
169
|
+
export interface HostLost {
|
|
170
|
+
/** When the failure was detected (RFC 3339). */
|
|
171
|
+
detectedAt: string | null;
|
|
172
|
+
/**
|
|
173
|
+
* What the resume restored: `disk` (the workspace's own disk, files kept; `coldBootReason` `host_lost`, processes
|
|
174
|
+
* restarted) or `checkpoint` (its newest checkpoint, see `stateAsOf`). Null on a suspend's result.
|
|
175
|
+
*/
|
|
176
|
+
restoredFrom: 'disk' | 'checkpoint' | null;
|
|
177
|
+
/** `checkpoint`: the checkpoint the resume restored. */
|
|
178
|
+
restoredCheckpointId: string | null;
|
|
179
|
+
/** `checkpoint`: the restored state is as of this time (RFC 3339); changes after it are not in the workspace. */
|
|
180
|
+
stateAsOf: string | null;
|
|
181
|
+
}
|
|
146
182
|
/**
|
|
147
183
|
* The durable copy of a suspend or fork operation (0.12.0+): `result.durability` camelCased, or null when the result has
|
|
148
184
|
* none (the suspend was stored durably before it completed, or another kind).
|
|
@@ -150,6 +186,11 @@ export interface LostSuspend {
|
|
|
150
186
|
export declare function durabilityOf(op: Pick<Operation, 'result'>): Durability | null;
|
|
151
187
|
/** A resume operation's `result.lost_suspend` camelCased (0.12.0+), or null. */
|
|
152
188
|
export declare function lostSuspendOf(op: Pick<Operation, 'result'>): LostSuspend | null;
|
|
189
|
+
/**
|
|
190
|
+
* An operation's `result.host_lost` camelCased (0.13.1+), or null: on a resume or open that restored a workspace whose
|
|
191
|
+
* machine failed, and on a suspend that found it so (`detectedAt` only).
|
|
192
|
+
*/
|
|
193
|
+
export declare function hostLostOf(op: Pick<Operation, 'result'>): HostLost | null;
|
|
153
194
|
/**
|
|
154
195
|
* Whether a suspend or fork operation's capture is in durable storage (0.12.0+): `result.durable`, else `true` for a
|
|
155
196
|
* succeeded suspend or fork whose result predates the field, else null.
|
|
@@ -255,7 +296,10 @@ export declare function traced<T>(trace: Trace, fn: () => Promise<T>): Promise<T
|
|
|
255
296
|
*
|
|
256
297
|
* A call that retried a request adds `retries: <n> (<request>, <cause>, after <delay>)`.
|
|
257
298
|
* A resume that booted the workspace instead of restoring its memory (0.11.0+) says so in the server line:
|
|
258
|
-
* `resume from cold_boot: processes restarted (runtime_changed)
|
|
299
|
+
* `resume from cold_boot: processes restarted (runtime_changed)`, or `(host_lost)` (0.13.1+) when the machine the
|
|
300
|
+
* workspace ran on failed and it booted from its disk. A resume that restored the newest checkpoint after such a
|
|
301
|
+
* failure adds `restored checkpoint <id> (host_lost; state as of <time>)`; a suspend that found the machine failed
|
|
302
|
+
* adds `host_lost (detected <time>)`.
|
|
259
303
|
*/
|
|
260
304
|
export declare function formatTiming(t: LifecycleTiming): string;
|
|
261
305
|
export {};
|