@shardflux/sdk 0.8.0 → 0.9.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 +199 -0
- package/README.md +235 -6
- package/dist/account.d.ts +469 -0
- package/dist/account.js +620 -0
- package/dist/cell.d.ts +197 -8
- package/dist/cell.js +449 -31
- package/dist/client.d.ts +76 -5
- package/dist/client.js +114 -6
- package/dist/errors.d.ts +62 -3
- package/dist/errors.js +65 -1
- package/dist/executions.d.ts +120 -0
- package/dist/executions.js +99 -0
- package/dist/feedback.d.ts +67 -0
- package/dist/feedback.js +39 -0
- package/dist/generated/app-api.d.ts +12323 -8072
- package/dist/generated/cell-api.d.ts +463 -8
- package/dist/http.d.ts +7 -1
- package/dist/http.js +26 -7
- package/dist/index.d.ts +17 -6
- package/dist/index.js +5 -1
- package/dist/lifecycle.d.ts +27 -2
- package/dist/lifecycle.js +5 -0
- package/dist/progress.js +4 -2
- package/dist/templates.js +2 -2
- package/dist/tools.d.ts +28 -1
- package/dist/tools.js +153 -21
- package/dist/version-check.d.ts +101 -0
- package/dist/version-check.js +191 -0
- package/dist/workspace.d.ts +77 -5
- package/dist/workspace.js +164 -12
- package/package.json +1 -1
package/dist/cell.d.ts
CHANGED
|
@@ -14,8 +14,17 @@
|
|
|
14
14
|
* Exec output is retained in the guest and addressed by byte offsets, so
|
|
15
15
|
* `exec.run()` survives gateway restarts/disconnects by reconnecting with the
|
|
16
16
|
* offsets it already processed; it never starts the command a second time.
|
|
17
|
+
*
|
|
18
|
+
* File-first workspaces (contracts §29) serve the files routes from a
|
|
19
|
+
* versioned tree and run commands as executions (`executions.run()`); the
|
|
20
|
+
* client tracks the tree revision every response reports (`X-Tree-Revision`)
|
|
21
|
+
* and, when it knows the workspace's mode, refuses calls the mode does not
|
|
22
|
+
* have before sending them (NotSupportedForModeError).
|
|
17
23
|
*/
|
|
18
24
|
import type { components, paths } from './generated/cell-api.js';
|
|
25
|
+
import type { WorkspaceMode } from './errors.js';
|
|
26
|
+
import { ExecutionResult } from './executions.js';
|
|
27
|
+
import type { ExecutionGetOptions, ExecutionRunOptions } from './executions.js';
|
|
19
28
|
import type { RequestOptions } from './http.js';
|
|
20
29
|
import type { ProgressListener } from './progress.js';
|
|
21
30
|
import type { ToolTokenManager } from './tokens.js';
|
|
@@ -30,6 +39,81 @@ export type ProcessList = S['ProcessList'];
|
|
|
30
39
|
export type FileInfo = S['FileInfo'];
|
|
31
40
|
export type FileList = S['FileList'];
|
|
32
41
|
export type FileWriteResult = S['FileWriteResult'];
|
|
42
|
+
/** Search request/result of `POST /files/search` (contracts §26.1). */
|
|
43
|
+
export type FileSearchRequest = S['FileSearchRequest'];
|
|
44
|
+
export type FileSearchMatch = S['FileSearchMatch'];
|
|
45
|
+
export type FileSearchResult = S['FileSearchResult'];
|
|
46
|
+
/** One text edit of `POST /files/patch` (contracts §26.2), as sent on the wire. */
|
|
47
|
+
export type FileEdit = S['FileEdit'];
|
|
48
|
+
export type FilePatchRequest = S['FilePatchRequest'];
|
|
49
|
+
export type FilePatchResult = S['FilePatchResult'];
|
|
50
|
+
/** A content SHA-256 (lowercase hex), or `absent` for a path that must not exist. */
|
|
51
|
+
export type FileRevision = S['FileRevision'];
|
|
52
|
+
export type WakeHintResult = S['WakeHintResult'];
|
|
53
|
+
/** Where a running workspace's VM is (contracts §25.1): resident, frozen, hibernated or restoring. */
|
|
54
|
+
export type Residency = WakeHintResult['residency'];
|
|
55
|
+
/** `X-Served-From`: `disk` when a read was served from a suspended or hibernated workspace's disk (contracts §26.4). */
|
|
56
|
+
export type ServedFrom = 'guest' | 'disk';
|
|
57
|
+
/** `files.readWithInfo()`: the bytes plus what the gateway reported about the file. */
|
|
58
|
+
export interface FileReadResult {
|
|
59
|
+
data: Uint8Array;
|
|
60
|
+
/** `X-File-Size`: the file's size when the read started (null when the gateway did not send it). */
|
|
61
|
+
size: number | null;
|
|
62
|
+
/**
|
|
63
|
+
* `X-File-Revision`: SHA-256 of the whole file (regular files of 16 MiB or less), usable as `expectedRevision` of
|
|
64
|
+
* `files.patch()`. Null when absent, and when a read continued over several requests saw different revisions (the
|
|
65
|
+
* file changed while it was read).
|
|
66
|
+
*/
|
|
67
|
+
revision: string | null;
|
|
68
|
+
/** `X-Served-From`: `disk` for a read of a sleeping workspace's disk (the state at suspension); null when absent. */
|
|
69
|
+
servedFrom: ServedFrom | null;
|
|
70
|
+
}
|
|
71
|
+
export interface FileSearchOptions {
|
|
72
|
+
/** RE2 syntax when true; a literal substring otherwise (default false). */
|
|
73
|
+
regex?: boolean;
|
|
74
|
+
caseInsensitive?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Globs (`*`, `**`, `?`, `[...]`), gitignore-style: without `/` a pattern matches a name at any depth (`*.py`); with
|
|
77
|
+
* `/` it matches the path relative to the searched directory (`src/**\/*.ts`; a leading `/` anchors it there, a
|
|
78
|
+
* trailing `/` matches directories only). A file must match one.
|
|
79
|
+
*/
|
|
80
|
+
include?: string[];
|
|
81
|
+
/** Globs skipped, same syntax (e.g. `build/`; default `.git` and `node_modules` anywhere; `[]` searches everything). */
|
|
82
|
+
exclude?: string[];
|
|
83
|
+
/** 1-5000 (gateway default 200). */
|
|
84
|
+
maxMatches?: number;
|
|
85
|
+
/** Files above this size are skipped (gateway default 1 MiB, at most 64 MiB). */
|
|
86
|
+
maxFileBytes?: number;
|
|
87
|
+
/** Lines of context before and after each match, 0-5. */
|
|
88
|
+
contextLines?: number;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
}
|
|
91
|
+
/** `files.search()`: the gateway's result plus `served_from` (`X-Served-From`; null when the gateway sent none). */
|
|
92
|
+
export type FileSearchResponse = FileSearchResult & {
|
|
93
|
+
served_from: ServedFrom | null;
|
|
94
|
+
};
|
|
95
|
+
/** One edit of `files.patch()`: `oldText` must occur exactly once in the file, or any number of times with `replaceAll`. */
|
|
96
|
+
export interface FilePatchEdit {
|
|
97
|
+
oldText: string;
|
|
98
|
+
newText: string;
|
|
99
|
+
replaceAll?: boolean;
|
|
100
|
+
}
|
|
101
|
+
interface FilePatchCommon {
|
|
102
|
+
path: string;
|
|
103
|
+
/** The file's current revision (`files.stat(path, { revision: true })`, a read, write or patch result), or `absent`. */
|
|
104
|
+
expectedRevision?: FileRevision;
|
|
105
|
+
createParents?: boolean;
|
|
106
|
+
/** Permission bits for a new file (an existing file keeps its mode and owner). */
|
|
107
|
+
mode?: string;
|
|
108
|
+
}
|
|
109
|
+
/** `files.patch()`: exactly one of `edits` (applied in order to UTF-8 text) or `content` (the whole new file). */
|
|
110
|
+
export type FilePatchParams = (FilePatchCommon & {
|
|
111
|
+
edits: FilePatchEdit[];
|
|
112
|
+
content?: undefined;
|
|
113
|
+
}) | (FilePatchCommon & {
|
|
114
|
+
content: string;
|
|
115
|
+
edits?: undefined;
|
|
116
|
+
});
|
|
33
117
|
export type GitCloneRequest = S['GitCloneRequest'];
|
|
34
118
|
export type GitCommitRequest = S['GitCommitRequest'];
|
|
35
119
|
export type GitResult = S['GitResult'];
|
|
@@ -72,7 +156,8 @@ export interface CellClientOptions {
|
|
|
72
156
|
* surfaces. It throws when the wake fails (OperationFailedError) or outlasts `timeoutMs` (OperationTimeoutError).
|
|
73
157
|
* Workspace.cell() supplies `workspace.wake()`. A call refused with `workspace_not_running` wakes the workspace and is
|
|
74
158
|
* retried; a refused call was never executed (contracts §20.4), so the retry cannot duplicate it. `null` surfaces
|
|
75
|
-
* the refusal instead.
|
|
159
|
+
* the refusal instead. A wake that put a new token into this client's manager (the held resume, contracts §22.6;
|
|
160
|
+
* `workspace.wake({ agentLabel, tools })`) is retried with it; otherwise the token is replaced before the retry.
|
|
76
161
|
*/
|
|
77
162
|
wake?: ((timeoutMs: number, signal?: AbortSignal) => Promise<boolean | void>) | null;
|
|
78
163
|
/**
|
|
@@ -88,11 +173,32 @@ export interface CellClientOptions {
|
|
|
88
173
|
onProgress?: ProgressListener;
|
|
89
174
|
/** Internal: see CAPTURE_BARRIER. */
|
|
90
175
|
[CAPTURE_BARRIER]?: (() => Promise<unknown> | undefined) | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* The workspace's mode (contracts §29), or a function returning it (undefined: unknown). When known, calls the mode
|
|
178
|
+
* does not have fail at once with NotSupportedForModeError (`local` true) instead of a request: exec sessions, PTY,
|
|
179
|
+
* processes, version control, browser and changes on a file-first workspace; executions and `ifTreeRevision` on a
|
|
180
|
+
* processful one. Workspace.cell() supplies the workspace's.
|
|
181
|
+
*/
|
|
182
|
+
mode?: WorkspaceMode | (() => WorkspaceMode | undefined);
|
|
183
|
+
/** Called with every tree revision a response reports (`X-Tree-Revision`, execution results, mismatch refusals). */
|
|
184
|
+
onTreeRevision?: (revision: number) => void;
|
|
185
|
+
}
|
|
186
|
+
/** `ifTreeRevision` (file-first workspaces, contracts §29.8): the tree revision a mutating files call applies to. */
|
|
187
|
+
export interface TreeRevisionOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Sent as `If-Match`: the call applies only while the tree is at this revision (`workspace.treeRevision`, or a
|
|
190
|
+
* revision an earlier call or execution reported), else it fails with TreeRevisionMismatchError (409 `conflict`,
|
|
191
|
+
* `tree_revision_mismatch`, `currentTreeRevision`) and nothing changes. File-first only: a processful workspace would
|
|
192
|
+
* ignore it, so the SDK refuses it there (NotSupportedForModeError).
|
|
193
|
+
*/
|
|
194
|
+
ifTreeRevision?: number;
|
|
91
195
|
}
|
|
92
196
|
/** Per-request transition handling (internal to CellClient). */
|
|
93
197
|
interface TransitionOptions {
|
|
94
198
|
/** Wake a suspended workspace for this call (default true; exec output follow reconnects pass false). */
|
|
95
199
|
wake?: boolean;
|
|
200
|
+
/** Wait out `workspace_busy` (default true; the wake hint passes false: it is best effort). */
|
|
201
|
+
busy?: boolean;
|
|
96
202
|
}
|
|
97
203
|
export declare const DEFAULT_TRANSITION_TIMEOUT_MS = 120000;
|
|
98
204
|
/**
|
|
@@ -149,6 +255,13 @@ export declare class CellClient {
|
|
|
149
255
|
readonly workspaceId: string;
|
|
150
256
|
readonly tokens: ToolTokenManager;
|
|
151
257
|
constructor(workspaceId: string, tokens: ToolTokenManager, opts?: CellClientOptions);
|
|
258
|
+
/** The workspace's mode when known (CellClientOptions.mode), else undefined. */
|
|
259
|
+
get mode(): WorkspaceMode | undefined;
|
|
260
|
+
/**
|
|
261
|
+
* The newest tree revision this client has seen (file-first workspaces: `X-Tree-Revision` of any response, execution
|
|
262
|
+
* results, `tree_revision_mismatch` refusals); null before the first. `workspace.treeRevision` combines every client's.
|
|
263
|
+
*/
|
|
264
|
+
get treeRevision(): number | null;
|
|
152
265
|
/** True after close(): every request (and stream) of this client is aborted. */
|
|
153
266
|
get closed(): boolean;
|
|
154
267
|
/**
|
|
@@ -164,6 +277,10 @@ export declare class CellClient {
|
|
|
164
277
|
* Refused calls were never executed, so retrying is safe. When the budget is spent the refusal surfaces.
|
|
165
278
|
*/
|
|
166
279
|
request(method: string, path: string, init?: RequestOptions & TransitionOptions): Promise<Response>;
|
|
280
|
+
/**
|
|
281
|
+
* Exec sessions of a processful workspace. On a file-first workspace these are refused (409 not_supported_for_mode;
|
|
282
|
+
* locally when the mode is known): use `executions.run()`.
|
|
283
|
+
*/
|
|
167
284
|
readonly exec: {
|
|
168
285
|
/** Starts argv (no shell). Idempotent by session_id: an existing session is returned, never re-run. */
|
|
169
286
|
start: (req: ExecStartRequest, signal?: AbortSignal) => Promise<ExecSession>;
|
|
@@ -180,9 +297,35 @@ export declare class CellClient {
|
|
|
180
297
|
/**
|
|
181
298
|
* Starts (or re-attaches to) a session and collects its output until it exits, reconnecting
|
|
182
299
|
* with offsets after dropped streams. Never issues a second start for the same session_id.
|
|
300
|
+
* A file-first workspace has no sessions: use `executions.run()` (refused locally when the mode is known).
|
|
183
301
|
*/
|
|
184
302
|
run: (argv: string[], opts?: RunOptions) => Promise<RunResult>;
|
|
185
303
|
};
|
|
304
|
+
readonly executions: {
|
|
305
|
+
/**
|
|
306
|
+
* Runs argv (no shell; `['bash', '-lc', line]` for shell syntax) as an execution of this file-first workspace: a
|
|
307
|
+
* fresh VM on the latest tree revision, answered when the command ended. The files it changed under /home/user are
|
|
308
|
+
* the next revision (`treeRevision`, `changed`); processes, memory and files elsewhere do not survive it.
|
|
309
|
+
*
|
|
310
|
+
* The execution id (`executionId`, default a fresh `ex-<uuid>`) is the idempotency key: network failures and
|
|
311
|
+
* retryable 429/5xx answers (503 `no_execution_host`: no host has room, with Retry-After) are retried with the same
|
|
312
|
+
* id and request, at most `maxRetries` times, and an attempt that waits longer than `attemptTimeoutMs` re-attaches
|
|
313
|
+
* with the same id. The cell answers 201 for the call that ran the command and 200 (`replayed`) with the recorded
|
|
314
|
+
* result otherwise, so the command runs at most once. `workspace_busy` (`execution_in_progress`: another execution
|
|
315
|
+
* holds the workspace) is waited out within `transitionTimeoutMs`; `execution_id_reused` (the id with another
|
|
316
|
+
* request) and validation errors are thrown at once. The SDK never retries with a new id: a result whose `state` is
|
|
317
|
+
* `failed` or `lost` (nothing published; `error.details.reason` says why) is returned, and running it again is the
|
|
318
|
+
* caller's decision. Processful workspaces: NotSupportedForModeError (locally when the mode is known).
|
|
319
|
+
*/
|
|
320
|
+
run: (argv: string[], opts?: ExecutionRunOptions) => Promise<ExecutionResult>;
|
|
321
|
+
/**
|
|
322
|
+
* The execution `executionId` of this file-first workspace (`GET /executions/{id}`): its result once it ended, or a
|
|
323
|
+
* pending result (`pending`, state `queued`/`running`, no output yet) while it runs; with `waitMs`, polls until it
|
|
324
|
+
* ended or the wait elapsed. Results are kept 7 days; an unknown id is 404 `not_found`. `replayed` is true.
|
|
325
|
+
*/
|
|
326
|
+
get: (executionId: string, opts?: ExecutionGetOptions) => Promise<ExecutionResult>;
|
|
327
|
+
};
|
|
328
|
+
/** PTY sessions of a processful workspace (file-first: NotSupportedForModeError). */
|
|
186
329
|
readonly pty: {
|
|
187
330
|
open: (req?: PtyOpenRequest) => Promise<PtySession>;
|
|
188
331
|
get: (sessionId: string) => Promise<PtySession>;
|
|
@@ -224,33 +367,79 @@ export declare class CellClient {
|
|
|
224
367
|
offset?: number;
|
|
225
368
|
length?: number;
|
|
226
369
|
}) => Promise<Uint8Array>;
|
|
370
|
+
/**
|
|
371
|
+
* Like `read()`, plus the file's size, `revision` (SHA-256 of the whole file, `X-File-Revision`, for regular files of
|
|
372
|
+
* 16 MiB or less; pass it as `expectedRevision` to `patch()`) and `servedFrom` (`disk` when a sleeping workspace was
|
|
373
|
+
* read from its disk without waking it, contracts §26.4). 0.9.0+.
|
|
374
|
+
*/
|
|
375
|
+
readWithInfo: (path: string, opts?: {
|
|
376
|
+
offset?: number;
|
|
377
|
+
length?: number;
|
|
378
|
+
}) => Promise<FileReadResult>;
|
|
227
379
|
readText: (path: string, opts?: {
|
|
228
380
|
offset?: number;
|
|
229
381
|
length?: number;
|
|
230
382
|
}) => Promise<string>;
|
|
231
|
-
/**
|
|
383
|
+
/**
|
|
384
|
+
* Atomic replace (or append), acknowledged after fsync of file and parent directory (file-first: once the new tree
|
|
385
|
+
* revision is published). `ifTreeRevision` (file-first) applies it only at that tree revision.
|
|
386
|
+
*/
|
|
232
387
|
write: (path: string, data: string | Uint8Array, opts?: {
|
|
233
388
|
mode?: string;
|
|
234
389
|
createParents?: boolean;
|
|
235
390
|
append?: boolean;
|
|
236
391
|
idempotencyKey?: string;
|
|
237
|
-
}) => Promise<FileWriteResult>;
|
|
392
|
+
} & TreeRevisionOptions) => Promise<FileWriteResult>;
|
|
238
393
|
remove: (path: string, opts?: {
|
|
239
394
|
recursive?: boolean;
|
|
240
|
-
}) => Promise<void>;
|
|
241
|
-
|
|
395
|
+
} & TreeRevisionOptions) => Promise<void>;
|
|
396
|
+
/** `revision: true` (0.9.0+) adds `revision`, the SHA-256 of a regular file's content (files of up to 256 MiB). */
|
|
397
|
+
stat: (path: string, opts?: {
|
|
398
|
+
revision?: boolean;
|
|
399
|
+
}) => Promise<FileInfo>;
|
|
242
400
|
list: (path: string, opts?: {
|
|
243
401
|
limit?: number;
|
|
244
402
|
}) => Promise<FileList>;
|
|
245
403
|
mkdir: (path: string, opts?: {
|
|
246
404
|
parents?: boolean;
|
|
247
405
|
mode?: string;
|
|
248
|
-
}) => Promise<FileInfo>;
|
|
406
|
+
} & TreeRevisionOptions) => Promise<FileInfo>;
|
|
249
407
|
move: (from: string, to: string, opts?: {
|
|
250
408
|
overwrite?: boolean;
|
|
251
|
-
}) => Promise<FileInfo>;
|
|
409
|
+
} & TreeRevisionOptions) => Promise<FileInfo>;
|
|
410
|
+
/**
|
|
411
|
+
* Searches file contents under `path`, a directory or one file (contracts §26.1; 0.9.0+): matching lines in path
|
|
412
|
+
* order, bounded by `maxMatches`, a 10 s budget and 4 MiB of results (`truncated`, `stop_reason` `max_matches`,
|
|
413
|
+
* `budget` or `max_bytes`). Binary files, symbolic links, special files and files above `maxFileBytes` are
|
|
414
|
+
* skipped. Read-only, so transient failures (e.g. 503 `host_capacity`) are retried; a suspended workspace whose
|
|
415
|
+
* disk a host still holds is searched there without waking it (`served_from: 'disk'`).
|
|
416
|
+
*/
|
|
417
|
+
search: (path: string, pattern: string, opts?: FileSearchOptions) => Promise<FileSearchResponse>;
|
|
418
|
+
/**
|
|
419
|
+
* Applies text `edits` (each `oldText` must occur exactly once unless `replaceAll`; in order) or replaces the whole
|
|
420
|
+
* file with `content`, atomically and durably (contracts §26.2; 0.9.0+). With `expectedRevision` the file must
|
|
421
|
+
* still have that revision (`absent`: must not exist), else 409 `conflict` `revision_mismatch` with
|
|
422
|
+
* `details.current_revision`. Edits that do not apply: 422 `validation_failed` `edit_not_found` / `edit_ambiguous`
|
|
423
|
+
* (`details.index`), `edit_not_text`. Limits: a request of up to 7 MiB (else 413 `payload_too_large`; larger
|
|
424
|
+
* content goes through `write()`), files of up to 64 MiB, no symlink targets. Always sends an Idempotency-Key, so
|
|
425
|
+
* a retried call is applied once (a replay returns the recorded result, refusals included). `ifTreeRevision`
|
|
426
|
+
* (file-first) applies it only at that tree revision.
|
|
427
|
+
*/
|
|
428
|
+
patch: (params: FilePatchParams, opts?: {
|
|
429
|
+
idempotencyKey?: string;
|
|
430
|
+
signal?: AbortSignal;
|
|
431
|
+
} & TreeRevisionOptions) => Promise<FilePatchResult>;
|
|
252
432
|
};
|
|
253
|
-
/**
|
|
433
|
+
/**
|
|
434
|
+
* `POST /wake-hint` (contracts §26.6; 0.9.0+): announces an imminent tool call so a hibernated workspace is restored
|
|
435
|
+
* ahead of it. It never wakes a suspended workspace, never waits out `workspace_busy` and is not retried: a suspended
|
|
436
|
+
* workspace answers 409 `workspace_not_running` (`Workspace.hint()` then wakes it in the background).
|
|
437
|
+
*/
|
|
438
|
+
wakeHint(signal?: AbortSignal): Promise<WakeHintResult>;
|
|
439
|
+
/**
|
|
440
|
+
* One page of the workspace's changes against its template (needs the `files` tool). File-first workspaces: each
|
|
441
|
+
* execution's result lists what it changed (`changed`); this route is refused (NotSupportedForModeError).
|
|
442
|
+
*/
|
|
254
443
|
changes(params?: WorkspaceChangesParams): Promise<WorkspaceChangesPage>;
|
|
255
444
|
/** Every change under `pathPrefix`, following next_cursor. */
|
|
256
445
|
changesAll(params?: Omit<WorkspaceChangesParams, 'cursor' | 'summary'>): AsyncGenerator<WorkspaceChange>;
|