@shardflux/sdk 0.8.0 → 0.10.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 +258 -0
- package/README.md +338 -7
- package/dist/account.d.ts +493 -0
- package/dist/account.js +641 -0
- package/dist/cell.d.ts +203 -8
- package/dist/cell.js +457 -32
- package/dist/client.d.ts +120 -5
- package/dist/client.js +142 -6
- package/dist/errors.d.ts +90 -3
- package/dist/errors.js +93 -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 +10546 -5855
- package/dist/generated/cell-api.d.ts +501 -9
- package/dist/http.d.ts +7 -1
- package/dist/http.js +26 -7
- package/dist/index.d.ts +18 -7
- 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 +172 -21
- package/dist/usage.d.ts +36 -6
- package/dist/usage.js +19 -4
- package/dist/version-check.d.ts +101 -0
- package/dist/version-check.js +191 -0
- package/dist/workspace.d.ts +100 -6
- package/dist/workspace.js +198 -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
|
/**
|
|
@@ -117,6 +223,10 @@ export interface RunResult {
|
|
|
117
223
|
}
|
|
118
224
|
export interface RunOptions {
|
|
119
225
|
sessionId?: string;
|
|
226
|
+
/**
|
|
227
|
+
* Absolute working directory (default the workspace's, /home/user). The API refuses a relative path with 422
|
|
228
|
+
* `validation_failed`, details.reason `invalid_cwd`; one that is not a directory rejects with ExecStartError.
|
|
229
|
+
*/
|
|
120
230
|
cwd?: string;
|
|
121
231
|
env?: Record<string, string>;
|
|
122
232
|
user?: string;
|
|
@@ -149,6 +259,13 @@ export declare class CellClient {
|
|
|
149
259
|
readonly workspaceId: string;
|
|
150
260
|
readonly tokens: ToolTokenManager;
|
|
151
261
|
constructor(workspaceId: string, tokens: ToolTokenManager, opts?: CellClientOptions);
|
|
262
|
+
/** The workspace's mode when known (CellClientOptions.mode), else undefined. */
|
|
263
|
+
get mode(): WorkspaceMode | undefined;
|
|
264
|
+
/**
|
|
265
|
+
* The newest tree revision this client has seen (file-first workspaces: `X-Tree-Revision` of any response, execution
|
|
266
|
+
* results, `tree_revision_mismatch` refusals); null before the first. `workspace.treeRevision` combines every client's.
|
|
267
|
+
*/
|
|
268
|
+
get treeRevision(): number | null;
|
|
152
269
|
/** True after close(): every request (and stream) of this client is aborted. */
|
|
153
270
|
get closed(): boolean;
|
|
154
271
|
/**
|
|
@@ -164,6 +281,10 @@ export declare class CellClient {
|
|
|
164
281
|
* Refused calls were never executed, so retrying is safe. When the budget is spent the refusal surfaces.
|
|
165
282
|
*/
|
|
166
283
|
request(method: string, path: string, init?: RequestOptions & TransitionOptions): Promise<Response>;
|
|
284
|
+
/**
|
|
285
|
+
* Exec sessions of a processful workspace. On a file-first workspace these are refused (409 not_supported_for_mode;
|
|
286
|
+
* locally when the mode is known): use `executions.run()`.
|
|
287
|
+
*/
|
|
167
288
|
readonly exec: {
|
|
168
289
|
/** Starts argv (no shell). Idempotent by session_id: an existing session is returned, never re-run. */
|
|
169
290
|
start: (req: ExecStartRequest, signal?: AbortSignal) => Promise<ExecSession>;
|
|
@@ -180,9 +301,37 @@ export declare class CellClient {
|
|
|
180
301
|
/**
|
|
181
302
|
* Starts (or re-attaches to) a session and collects its output until it exits, reconnecting
|
|
182
303
|
* with offsets after dropped streams. Never issues a second start for the same session_id.
|
|
304
|
+
* A command that could not start (a cwd that is not a directory, a program not on PATH, an unknown user) rejects
|
|
305
|
+
* with ExecStartError (0.10.0+): nothing ran, so there is no exit code to return.
|
|
306
|
+
* A file-first workspace has no sessions: use `executions.run()` (refused locally when the mode is known).
|
|
183
307
|
*/
|
|
184
308
|
run: (argv: string[], opts?: RunOptions) => Promise<RunResult>;
|
|
185
309
|
};
|
|
310
|
+
readonly executions: {
|
|
311
|
+
/**
|
|
312
|
+
* Runs argv (no shell; `['bash', '-lc', line]` for shell syntax) as an execution of this file-first workspace: a
|
|
313
|
+
* fresh VM on the latest tree revision, answered when the command ended. The files it changed under /home/user are
|
|
314
|
+
* the next revision (`treeRevision`, `changed`); processes, memory and files elsewhere do not survive it.
|
|
315
|
+
*
|
|
316
|
+
* The execution id (`executionId`, default a fresh `ex-<uuid>`) is the idempotency key: network failures and
|
|
317
|
+
* retryable 429/5xx answers (503 `no_execution_host`: no host has room, with Retry-After) are retried with the same
|
|
318
|
+
* id and request, at most `maxRetries` times, and an attempt that waits longer than `attemptTimeoutMs` re-attaches
|
|
319
|
+
* with the same id. The cell answers 201 for the call that ran the command and 200 (`replayed`) with the recorded
|
|
320
|
+
* result otherwise, so the command runs at most once. `workspace_busy` (`execution_in_progress`: another execution
|
|
321
|
+
* holds the workspace) is waited out within `transitionTimeoutMs`; `execution_id_reused` (the id with another
|
|
322
|
+
* request) and validation errors are thrown at once. The SDK never retries with a new id: a result whose `state` is
|
|
323
|
+
* `failed` or `lost` (nothing published; `error.details.reason` says why) is returned, and running it again is the
|
|
324
|
+
* caller's decision. Processful workspaces: NotSupportedForModeError (locally when the mode is known).
|
|
325
|
+
*/
|
|
326
|
+
run: (argv: string[], opts?: ExecutionRunOptions) => Promise<ExecutionResult>;
|
|
327
|
+
/**
|
|
328
|
+
* The execution `executionId` of this file-first workspace (`GET /executions/{id}`): its result once it ended, or a
|
|
329
|
+
* pending result (`pending`, state `queued`/`running`, no output yet) while it runs; with `waitMs`, polls until it
|
|
330
|
+
* ended or the wait elapsed. Results are kept 7 days; an unknown id is 404 `not_found`. `replayed` is true.
|
|
331
|
+
*/
|
|
332
|
+
get: (executionId: string, opts?: ExecutionGetOptions) => Promise<ExecutionResult>;
|
|
333
|
+
};
|
|
334
|
+
/** PTY sessions of a processful workspace (file-first: NotSupportedForModeError). */
|
|
186
335
|
readonly pty: {
|
|
187
336
|
open: (req?: PtyOpenRequest) => Promise<PtySession>;
|
|
188
337
|
get: (sessionId: string) => Promise<PtySession>;
|
|
@@ -224,33 +373,79 @@ export declare class CellClient {
|
|
|
224
373
|
offset?: number;
|
|
225
374
|
length?: number;
|
|
226
375
|
}) => Promise<Uint8Array>;
|
|
376
|
+
/**
|
|
377
|
+
* Like `read()`, plus the file's size, `revision` (SHA-256 of the whole file, `X-File-Revision`, for regular files of
|
|
378
|
+
* 16 MiB or less; pass it as `expectedRevision` to `patch()`) and `servedFrom` (`disk` when a sleeping workspace was
|
|
379
|
+
* read from its disk without waking it, contracts §26.4). 0.9.0+.
|
|
380
|
+
*/
|
|
381
|
+
readWithInfo: (path: string, opts?: {
|
|
382
|
+
offset?: number;
|
|
383
|
+
length?: number;
|
|
384
|
+
}) => Promise<FileReadResult>;
|
|
227
385
|
readText: (path: string, opts?: {
|
|
228
386
|
offset?: number;
|
|
229
387
|
length?: number;
|
|
230
388
|
}) => Promise<string>;
|
|
231
|
-
/**
|
|
389
|
+
/**
|
|
390
|
+
* Atomic replace (or append), acknowledged after fsync of file and parent directory (file-first: once the new tree
|
|
391
|
+
* revision is published). `ifTreeRevision` (file-first) applies it only at that tree revision.
|
|
392
|
+
*/
|
|
232
393
|
write: (path: string, data: string | Uint8Array, opts?: {
|
|
233
394
|
mode?: string;
|
|
234
395
|
createParents?: boolean;
|
|
235
396
|
append?: boolean;
|
|
236
397
|
idempotencyKey?: string;
|
|
237
|
-
}) => Promise<FileWriteResult>;
|
|
398
|
+
} & TreeRevisionOptions) => Promise<FileWriteResult>;
|
|
238
399
|
remove: (path: string, opts?: {
|
|
239
400
|
recursive?: boolean;
|
|
240
|
-
}) => Promise<void>;
|
|
241
|
-
|
|
401
|
+
} & TreeRevisionOptions) => Promise<void>;
|
|
402
|
+
/** `revision: true` (0.9.0+) adds `revision`, the SHA-256 of a regular file's content (files of up to 256 MiB). */
|
|
403
|
+
stat: (path: string, opts?: {
|
|
404
|
+
revision?: boolean;
|
|
405
|
+
}) => Promise<FileInfo>;
|
|
242
406
|
list: (path: string, opts?: {
|
|
243
407
|
limit?: number;
|
|
244
408
|
}) => Promise<FileList>;
|
|
245
409
|
mkdir: (path: string, opts?: {
|
|
246
410
|
parents?: boolean;
|
|
247
411
|
mode?: string;
|
|
248
|
-
}) => Promise<FileInfo>;
|
|
412
|
+
} & TreeRevisionOptions) => Promise<FileInfo>;
|
|
249
413
|
move: (from: string, to: string, opts?: {
|
|
250
414
|
overwrite?: boolean;
|
|
251
|
-
}) => Promise<FileInfo>;
|
|
415
|
+
} & TreeRevisionOptions) => Promise<FileInfo>;
|
|
416
|
+
/**
|
|
417
|
+
* Searches file contents under `path`, a directory or one file (contracts §26.1; 0.9.0+): matching lines in path
|
|
418
|
+
* order, bounded by `maxMatches`, a 10 s budget and 4 MiB of results (`truncated`, `stop_reason` `max_matches`,
|
|
419
|
+
* `budget` or `max_bytes`). Binary files, symbolic links, special files and files above `maxFileBytes` are
|
|
420
|
+
* skipped. Read-only, so transient failures (e.g. 503 `host_capacity`) are retried; a suspended workspace whose
|
|
421
|
+
* disk a host still holds is searched there without waking it (`served_from: 'disk'`).
|
|
422
|
+
*/
|
|
423
|
+
search: (path: string, pattern: string, opts?: FileSearchOptions) => Promise<FileSearchResponse>;
|
|
424
|
+
/**
|
|
425
|
+
* Applies text `edits` (each `oldText` must occur exactly once unless `replaceAll`; in order) or replaces the whole
|
|
426
|
+
* file with `content`, atomically and durably (contracts §26.2; 0.9.0+). With `expectedRevision` the file must
|
|
427
|
+
* still have that revision (`absent`: must not exist), else 409 `conflict` `revision_mismatch` with
|
|
428
|
+
* `details.current_revision`. Edits that do not apply: 422 `validation_failed` `edit_not_found` / `edit_ambiguous`
|
|
429
|
+
* (`details.index`), `edit_not_text`. Limits: a request of up to 7 MiB (else 413 `payload_too_large`; larger
|
|
430
|
+
* content goes through `write()`), files of up to 64 MiB, no symlink targets. Always sends an Idempotency-Key, so
|
|
431
|
+
* a retried call is applied once (a replay returns the recorded result, refusals included). `ifTreeRevision`
|
|
432
|
+
* (file-first) applies it only at that tree revision.
|
|
433
|
+
*/
|
|
434
|
+
patch: (params: FilePatchParams, opts?: {
|
|
435
|
+
idempotencyKey?: string;
|
|
436
|
+
signal?: AbortSignal;
|
|
437
|
+
} & TreeRevisionOptions) => Promise<FilePatchResult>;
|
|
252
438
|
};
|
|
253
|
-
/**
|
|
439
|
+
/**
|
|
440
|
+
* `POST /wake-hint` (contracts §26.6; 0.9.0+): announces an imminent tool call so a hibernated workspace is restored
|
|
441
|
+
* ahead of it. It never wakes a suspended workspace, never waits out `workspace_busy` and is not retried: a suspended
|
|
442
|
+
* workspace answers 409 `workspace_not_running` (`Workspace.hint()` then wakes it in the background).
|
|
443
|
+
*/
|
|
444
|
+
wakeHint(signal?: AbortSignal): Promise<WakeHintResult>;
|
|
445
|
+
/**
|
|
446
|
+
* One page of the workspace's changes against its template (needs the `files` tool). File-first workspaces: each
|
|
447
|
+
* execution's result lists what it changed (`changed`); this route is refused (NotSupportedForModeError).
|
|
448
|
+
*/
|
|
254
449
|
changes(params?: WorkspaceChangesParams): Promise<WorkspaceChangesPage>;
|
|
255
450
|
/** Every change under `pathPrefix`, following next_cursor. */
|
|
256
451
|
changesAll(params?: Omit<WorkspaceChangesParams, 'cursor' | 'summary'>): AsyncGenerator<WorkspaceChange>;
|