@boatdev/sdk 1.1.0 → 1.3.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.
Files changed (54) hide show
  1. package/.openapi-generator/FILES +2 -0
  2. package/dist/apis/BoatApi.d.ts +2 -2
  3. package/dist/apis/BoatApi.js +2 -2
  4. package/dist/boat-helpers.d.ts +17 -1
  5. package/dist/boat-helpers.js +61 -0
  6. package/dist/esm/apis/BoatApi.d.ts +2 -2
  7. package/dist/esm/apis/BoatApi.js +2 -2
  8. package/dist/esm/boat-helpers.d.ts +17 -1
  9. package/dist/esm/boat-helpers.js +60 -0
  10. package/dist/esm/models/CommandRequest.d.ts +6 -0
  11. package/dist/esm/models/CommandRequest.js +2 -0
  12. package/dist/esm/models/CommandStreamFrame.d.ts +85 -0
  13. package/dist/esm/models/CommandStreamFrame.js +67 -0
  14. package/dist/esm/models/CreateSandboxRequest.d.ts +12 -0
  15. package/dist/esm/models/CreateSandboxRequest.js +4 -0
  16. package/dist/esm/models/DataRetentionPolicyResponse.d.ts +12 -0
  17. package/dist/esm/models/DataRetentionPolicyResponse.js +4 -0
  18. package/dist/esm/models/DataRetentionUpdateRequest.d.ts +8 -2
  19. package/dist/esm/models/DataRetentionUpdateRequest.js +3 -3
  20. package/dist/esm/models/Sandbox.d.ts +6 -0
  21. package/dist/esm/models/Sandbox.js +2 -0
  22. package/dist/esm/models/index.d.ts +1 -0
  23. package/dist/esm/models/index.js +1 -0
  24. package/dist/models/CommandRequest.d.ts +6 -0
  25. package/dist/models/CommandRequest.js +2 -0
  26. package/dist/models/CommandStreamFrame.d.ts +85 -0
  27. package/dist/models/CommandStreamFrame.js +75 -0
  28. package/dist/models/CreateSandboxRequest.d.ts +12 -0
  29. package/dist/models/CreateSandboxRequest.js +4 -0
  30. package/dist/models/DataRetentionPolicyResponse.d.ts +12 -0
  31. package/dist/models/DataRetentionPolicyResponse.js +4 -0
  32. package/dist/models/DataRetentionUpdateRequest.d.ts +8 -2
  33. package/dist/models/DataRetentionUpdateRequest.js +3 -3
  34. package/dist/models/Sandbox.d.ts +6 -0
  35. package/dist/models/Sandbox.js +2 -0
  36. package/dist/models/index.d.ts +1 -0
  37. package/dist/models/index.js +1 -0
  38. package/docs/BoatApi.md +4 -3
  39. package/docs/CommandRequest.md +2 -0
  40. package/docs/CommandStreamFrame.md +47 -0
  41. package/docs/CreateSandboxRequest.md +4 -0
  42. package/docs/DataRetentionPolicyResponse.md +4 -0
  43. package/docs/DataRetentionUpdateRequest.md +3 -0
  44. package/docs/Sandbox.md +2 -0
  45. package/package.json +1 -1
  46. package/src/apis/BoatApi.ts +7 -2
  47. package/src/boat-helpers.ts +75 -1
  48. package/src/models/CommandRequest.ts +8 -0
  49. package/src/models/CommandStreamFrame.ts +136 -0
  50. package/src/models/CreateSandboxRequest.ts +16 -0
  51. package/src/models/DataRetentionPolicyResponse.ts +16 -0
  52. package/src/models/DataRetentionUpdateRequest.ts +11 -4
  53. package/src/models/Sandbox.ts +8 -0
  54. package/src/models/index.ts +1 -0
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { BoatApi } from './apis/BoatApi';
3
- import type { Sandbox, SandboxEvent, DesktopResponse, PromptRequest, PromptRun } from './models';
3
+ import type { Sandbox, SandboxEvent, DesktopResponse, PromptRequest, PromptRun, CommandStreamFrame } from './models';
4
4
 
5
5
  export type WaitOptions = {
6
6
  timeoutMs?: number;
@@ -185,3 +185,77 @@ export async function writeText(api: BoatApi, sandboxId: string, path: string, c
185
185
  export async function execCommand(api: BoatApi, sandboxId: string, command: string, cwd?: string, timeoutSeconds = 30) {
186
186
  return api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
187
187
  }
188
+
189
+ /** One line of a streamed command, as sent (an `exit` frame also carries e.g. `signal`, `oomKilled`). */
190
+ export type CommandFrame = CommandStreamFrame & Record<string, unknown>;
191
+
192
+ export type StreamCommandOptions = {
193
+ cwd?: string;
194
+ timeoutSeconds?: number;
195
+ signal?: AbortSignal;
196
+ };
197
+
198
+ // A refusal that provably never ran the command (the sandbox is still restoring
199
+ // its disk, `retryable: true` before `started`) is retried for this long.
200
+ const STREAM_RETRY_MS = 60_000;
201
+
202
+ async function* ndjsonFrames(response: Response): AsyncGenerator<CommandFrame> {
203
+ if (!response.body) throw new Error('The command stream has no body');
204
+ const reader = response.body.getReader();
205
+ const decoder = new TextDecoder();
206
+ let buffered = '';
207
+ let done = false;
208
+ try {
209
+ while (!done) {
210
+ const chunk = await reader.read();
211
+ done = chunk.done;
212
+ buffered += done ? decoder.decode() : decoder.decode(chunk.value, { stream: true });
213
+ let newline = buffered.indexOf('\n');
214
+ while (newline !== -1) {
215
+ const line = buffered.slice(0, newline).trim();
216
+ buffered = buffered.slice(newline + 1);
217
+ if (line) yield JSON.parse(line);
218
+ newline = buffered.indexOf('\n');
219
+ }
220
+ }
221
+ if (buffered.trim()) yield JSON.parse(buffered);
222
+ } finally {
223
+ // The caller stopped early: drop the connection instead of reading the rest.
224
+ if (!done) await reader.cancel().catch(() => undefined);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Run a command and yield its output as the command writes it: `started`, then
230
+ * `stdout`/`stderr` chunks (`frame.data`), then exactly one `exit` (`exitCode`,
231
+ * `timedOut`, ...) or `error` (`error`, `message`).
232
+ *
233
+ * Safe to call right after `resume()`, without waiting: the API holds a command
234
+ * sent to a starting sandbox for up to 60 s instead of refusing it.
235
+ */
236
+ export async function* streamCommand(
237
+ api: BoatApi,
238
+ sandboxId: string,
239
+ command: string,
240
+ options?: StreamCommandOptions,
241
+ ): AsyncGenerator<CommandFrame> {
242
+ const deadline = Date.now() + STREAM_RETRY_MS;
243
+ for (;;) {
244
+ const response = await api.commandRaw(
245
+ { sandboxId, commandRequest: { command, cwd: options?.cwd, timeoutSeconds: options?.timeoutSeconds, stream: true } },
246
+ { signal: options?.signal },
247
+ );
248
+ let started = false;
249
+ let retry = false;
250
+ for await (const frame of ndjsonFrames(response.raw)) {
251
+ started = started || frame.type === 'started';
252
+ if (!started && frame.type === 'error' && frame.retryable === true && Date.now() < deadline) {
253
+ retry = true;
254
+ break;
255
+ }
256
+ yield frame;
257
+ }
258
+ if (!retry) return;
259
+ await delay(1_000);
260
+ }
261
+ }
@@ -43,6 +43,12 @@ export interface CommandRequest {
43
43
  * @memberof CommandRequest
44
44
  */
45
45
  detached?: boolean;
46
+ /**
47
+ * Stream the output as the command writes it: the response is newline-delimited JSON (application/x-ndjson), one CommandStreamFrame per line. Ignored with detached.
48
+ * @type {boolean}
49
+ * @memberof CommandRequest
50
+ */
51
+ stream?: boolean;
46
52
  }
47
53
 
48
54
  /**
@@ -67,6 +73,7 @@ export function CommandRequestFromJSONTyped(json: any, ignoreDiscriminator: bool
67
73
  'cwd': json['cwd'] == null ? undefined : json['cwd'],
68
74
  'timeoutSeconds': json['timeoutSeconds'] == null ? undefined : json['timeoutSeconds'],
69
75
  'detached': json['detached'] == null ? undefined : json['detached'],
76
+ 'stream': json['stream'] == null ? undefined : json['stream'],
70
77
  };
71
78
  }
72
79
 
@@ -85,6 +92,7 @@ export function CommandRequestToJSONTyped(value?: CommandRequest | null, ignoreD
85
92
  'cwd': value['cwd'],
86
93
  'timeoutSeconds': value['timeoutSeconds'],
87
94
  'detached': value['detached'],
95
+ 'stream': value['stream'],
88
96
  };
89
97
  }
90
98
 
@@ -0,0 +1,136 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ * Boat Public API v1
5
+ * Public JSON API for creating, operating, prompting, observing, and exposing sandboxes from backend services, CI jobs, hosted workers, and Boat automation products. The v1 reference intentionally documents the developer integration surface only. Dashboard billing actions are not part of v1.
6
+ *
7
+ * The version of the OpenAPI document: 1.0.0
8
+ *
9
+ *
10
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
11
+ * https://openapi-generator.tech
12
+ * Do not edit the class manually.
13
+ */
14
+
15
+ import { mapValues } from '../runtime';
16
+ /**
17
+ * One line of a streamed command. `started` first, then `stdout`/`stderr` chunks, then exactly one `exit` (the synchronous result, without stdout/stderr) or `error`.
18
+ * @export
19
+ * @interface CommandStreamFrame
20
+ */
21
+ export interface CommandStreamFrame {
22
+ /**
23
+ *
24
+ * @type {CommandStreamFrameTypeEnum}
25
+ * @memberof CommandStreamFrame
26
+ */
27
+ type: CommandStreamFrameTypeEnum;
28
+ /**
29
+ * stdout/stderr: the chunk of output, UTF-8.
30
+ * @type {string}
31
+ * @memberof CommandStreamFrame
32
+ */
33
+ data?: string;
34
+ /**
35
+ *
36
+ * @type {number}
37
+ * @memberof CommandStreamFrame
38
+ */
39
+ exitCode?: number | null;
40
+ /**
41
+ *
42
+ * @type {boolean}
43
+ * @memberof CommandStreamFrame
44
+ */
45
+ success?: boolean;
46
+ /**
47
+ *
48
+ * @type {boolean}
49
+ * @memberof CommandStreamFrame
50
+ */
51
+ timedOut?: boolean;
52
+ /**
53
+ *
54
+ * @type {string}
55
+ * @memberof CommandStreamFrame
56
+ */
57
+ error?: string;
58
+ /**
59
+ *
60
+ * @type {string}
61
+ * @memberof CommandStreamFrame
62
+ */
63
+ message?: string;
64
+ /**
65
+ * error: true when the command provably never ran (the sandbox is still restoring its disk), so sending it again is safe.
66
+ * @type {boolean}
67
+ * @memberof CommandStreamFrame
68
+ */
69
+ retryable?: boolean;
70
+ }
71
+
72
+
73
+ /**
74
+ * @export
75
+ */
76
+ export const CommandStreamFrameTypeEnum = {
77
+ Started: 'started',
78
+ Stdout: 'stdout',
79
+ Stderr: 'stderr',
80
+ Exit: 'exit',
81
+ Error: 'error'
82
+ } as const;
83
+ export type CommandStreamFrameTypeEnum = typeof CommandStreamFrameTypeEnum[keyof typeof CommandStreamFrameTypeEnum];
84
+
85
+
86
+ /**
87
+ * Check if a given object implements the CommandStreamFrame interface.
88
+ */
89
+ export function instanceOfCommandStreamFrame(value: object): value is CommandStreamFrame {
90
+ if (!('type' in value) || value['type'] === undefined) return false;
91
+ return true;
92
+ }
93
+
94
+ export function CommandStreamFrameFromJSON(json: any): CommandStreamFrame {
95
+ return CommandStreamFrameFromJSONTyped(json, false);
96
+ }
97
+
98
+ export function CommandStreamFrameFromJSONTyped(json: any, ignoreDiscriminator: boolean): CommandStreamFrame {
99
+ if (json == null) {
100
+ return json;
101
+ }
102
+ return {
103
+
104
+ 'type': json['type'],
105
+ 'data': json['data'] == null ? undefined : json['data'],
106
+ 'exitCode': json['exitCode'] == null ? undefined : json['exitCode'],
107
+ 'success': json['success'] == null ? undefined : json['success'],
108
+ 'timedOut': json['timedOut'] == null ? undefined : json['timedOut'],
109
+ 'error': json['error'] == null ? undefined : json['error'],
110
+ 'message': json['message'] == null ? undefined : json['message'],
111
+ 'retryable': json['retryable'] == null ? undefined : json['retryable'],
112
+ };
113
+ }
114
+
115
+ export function CommandStreamFrameToJSON(json: any): CommandStreamFrame {
116
+ return CommandStreamFrameToJSONTyped(json, false);
117
+ }
118
+
119
+ export function CommandStreamFrameToJSONTyped(value?: CommandStreamFrame | null, ignoreDiscriminator: boolean = false): any {
120
+ if (value == null) {
121
+ return value;
122
+ }
123
+
124
+ return {
125
+
126
+ 'type': value['type'],
127
+ 'data': value['data'],
128
+ 'exitCode': value['exitCode'],
129
+ 'success': value['success'],
130
+ 'timedOut': value['timedOut'],
131
+ 'error': value['error'],
132
+ 'message': value['message'],
133
+ 'retryable': value['retryable'],
134
+ };
135
+ }
136
+
@@ -49,6 +49,18 @@ export interface CreateSandboxRequest {
49
49
  * @memberof CreateSandboxRequest
50
50
  */
51
51
  noEnv?: boolean;
52
+ /**
53
+ * Pass `false` to never snapshot this sandbox. Nothing is captured in the background, so it pays no CPU or memory for it, and stopping it erases its disk the way zero data retention does. It cannot be resumed, forked, or saved as a named snapshot, and if its machine fails there is no backup to rebuild it from.
54
+ * @type {boolean}
55
+ * @memberof CreateSandboxRequest
56
+ */
57
+ snapshots?: boolean;
58
+ /**
59
+ * Pass `true` to get a sandbox only if a machine of this type is ready right now. The call answers within about 1.5 s: either the usual `202` with the sandbox, or `503` `no_ready_machine` with nothing created, billed, or counted against your start limits, so you can retry or fall back at once. Without it, a create waits for a machine to be built when none is ready. Not combinable with `from`.
60
+ * @type {boolean}
61
+ * @memberof CreateSandboxRequest
62
+ */
63
+ failFast?: boolean;
52
64
  /**
53
65
  * Shell script that runs on the sandbox after it is ready. Ready means "ready to accept the user", not "setup done": the script starts in the background once provisioning completes and never blocks the sandbox becoming usable. It runs as the sandbox user via `bash`, with the sandbox's environment applied, and its output goes to a log file on the sandbox. Observe the outcome as `setupStatus` (pending/running/done/failed) and `setupError` on the sandbox. Rejected with a 400 `invalid_setup_script` error when it is not a string or exceeds 64KB.
54
66
  * @type {string}
@@ -109,6 +121,8 @@ export function CreateSandboxRequestFromJSONTyped(json: any, ignoreDiscriminator
109
121
  'env': json['env'] == null ? undefined : json['env'],
110
122
  'environment': json['environment'] == null ? undefined : json['environment'],
111
123
  'noEnv': json['noEnv'] == null ? undefined : json['noEnv'],
124
+ 'snapshots': json['snapshots'] == null ? undefined : json['snapshots'],
125
+ 'failFast': json['failFast'] == null ? undefined : json['failFast'],
112
126
  'setupScript': json['setupScript'] == null ? undefined : json['setupScript'],
113
127
  'org': json['org'] == null ? undefined : json['org'],
114
128
  'teamId': json['teamId'] == null ? undefined : json['teamId'],
@@ -132,6 +146,8 @@ export function CreateSandboxRequestToJSONTyped(value?: CreateSandboxRequest | n
132
146
  'env': value['env'],
133
147
  'environment': value['environment'],
134
148
  'noEnv': value['noEnv'],
149
+ 'snapshots': value['snapshots'],
150
+ 'failFast': value['failFast'],
135
151
  'setupScript': value['setupScript'],
136
152
  'org': value['org'],
137
153
  'teamId': value['teamId'],
@@ -55,6 +55,18 @@ export interface DataRetentionPolicyResponse {
55
55
  * @memberof DataRetentionPolicyResponse
56
56
  */
57
57
  acceptedDeletionOperationsIrreversible?: boolean;
58
+ /**
59
+ * When true, every new sandbox of this account is created with snapshots off.
60
+ * @type {boolean}
61
+ * @memberof DataRetentionPolicyResponse
62
+ */
63
+ snapshotsOff?: boolean;
64
+ /**
65
+ *
66
+ * @type {Date}
67
+ * @memberof DataRetentionPolicyResponse
68
+ */
69
+ snapshotsOffAt?: Date | null;
58
70
  }
59
71
 
60
72
 
@@ -95,6 +107,8 @@ export function DataRetentionPolicyResponseFromJSONTyped(json: any, ignoreDiscri
95
107
  'enabledAt': (json['enabledAt'] == null ? null : new Date(json['enabledAt'])),
96
108
  'queuedSandboxes': json['queuedSandboxes'] == null ? undefined : json['queuedSandboxes'],
97
109
  'acceptedDeletionOperationsIrreversible': json['acceptedDeletionOperationsIrreversible'] == null ? undefined : json['acceptedDeletionOperationsIrreversible'],
110
+ 'snapshotsOff': json['snapshotsOff'] == null ? undefined : json['snapshotsOff'],
111
+ 'snapshotsOffAt': json['snapshotsOffAt'] == null ? undefined : (new Date(json['snapshotsOffAt'])),
98
112
  };
99
113
  }
100
114
 
@@ -115,6 +129,8 @@ export function DataRetentionPolicyResponseToJSONTyped(value?: DataRetentionPoli
115
129
  'enabledAt': value['enabledAt'] == null ? value['enabledAt'] : value['enabledAt'].toISOString(),
116
130
  'queuedSandboxes': value['queuedSandboxes'],
117
131
  'acceptedDeletionOperationsIrreversible': value['acceptedDeletionOperationsIrreversible'],
132
+ 'snapshotsOff': value['snapshotsOff'],
133
+ 'snapshotsOffAt': value['snapshotsOffAt'] == null ? value['snapshotsOffAt'] : value['snapshotsOffAt'].toISOString(),
118
134
  };
119
135
  }
120
136
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { mapValues } from '../runtime';
16
16
  /**
17
- *
17
+ * Pass `enabled` to change zero data retention, or `snapshotsOff` alone to change the account's snapshots-off setting.
18
18
  * @export
19
19
  * @interface DataRetentionUpdateRequest
20
20
  */
@@ -24,7 +24,13 @@ export interface DataRetentionUpdateRequest {
24
24
  * @type {boolean}
25
25
  * @memberof DataRetentionUpdateRequest
26
26
  */
27
- enabled: boolean;
27
+ enabled?: boolean;
28
+ /**
29
+ * Turn snapshots off for every sandbox created from now on. Existing sandboxes keep their setting. While on, a create or fork that passes `snapshots: true` is refused with 409 `snapshots_off_for_account`.
30
+ * @type {boolean}
31
+ * @memberof DataRetentionUpdateRequest
32
+ */
33
+ snapshotsOff?: boolean;
28
34
  /**
29
35
  * Required when enabling and must exactly equal `delete archived sandbox data`.
30
36
  * @type {DataRetentionUpdateRequestConfirmationEnum}
@@ -47,7 +53,6 @@ export type DataRetentionUpdateRequestConfirmationEnum = typeof DataRetentionUpd
47
53
  * Check if a given object implements the DataRetentionUpdateRequest interface.
48
54
  */
49
55
  export function instanceOfDataRetentionUpdateRequest(value: object): value is DataRetentionUpdateRequest {
50
- if (!('enabled' in value) || value['enabled'] === undefined) return false;
51
56
  return true;
52
57
  }
53
58
 
@@ -61,7 +66,8 @@ export function DataRetentionUpdateRequestFromJSONTyped(json: any, ignoreDiscrim
61
66
  }
62
67
  return {
63
68
 
64
- 'enabled': json['enabled'],
69
+ 'enabled': json['enabled'] == null ? undefined : json['enabled'],
70
+ 'snapshotsOff': json['snapshotsOff'] == null ? undefined : json['snapshotsOff'],
65
71
  'confirmation': json['confirmation'] == null ? undefined : json['confirmation'],
66
72
  };
67
73
  }
@@ -78,6 +84,7 @@ export function DataRetentionUpdateRequestToJSONTyped(value?: DataRetentionUpdat
78
84
  return {
79
85
 
80
86
  'enabled': value['enabled'],
87
+ 'snapshotsOff': value['snapshotsOff'],
81
88
  'confirmation': value['confirmation'],
82
89
  };
83
90
  }
@@ -129,6 +129,12 @@ export interface Sandbox {
129
129
  * @memberof Sandbox
130
130
  */
131
131
  desktopUrl?: string | null;
132
+ /**
133
+ * False when the sandbox was created with snapshots off.
134
+ * @type {boolean}
135
+ * @memberof Sandbox
136
+ */
137
+ snapshots?: boolean;
132
138
  /**
133
139
  *
134
140
  * @type {boolean}
@@ -298,6 +304,7 @@ export function SandboxFromJSONTyped(json: any, ignoreDiscriminator: boolean): S
298
304
  'archiveAfter': json['archiveAfter'] == null ? undefined : (new Date(json['archiveAfter'])),
299
305
  'desktopAvailable': json['desktopAvailable'],
300
306
  'desktopUrl': json['desktopUrl'] == null ? undefined : json['desktopUrl'],
307
+ 'snapshots': json['snapshots'] == null ? undefined : json['snapshots'],
301
308
  'snapshotAvailable': json['snapshotAvailable'],
302
309
  'snapshotCompletedAt': json['snapshotCompletedAt'] == null ? undefined : (new Date(json['snapshotCompletedAt'])),
303
310
  'snapshotVerifiedAt': json['snapshotVerifiedAt'] == null ? undefined : (new Date(json['snapshotVerifiedAt'])),
@@ -340,6 +347,7 @@ export function SandboxToJSONTyped(value?: Sandbox | null, ignoreDiscriminator:
340
347
  'archiveAfter': value['archiveAfter'] == null ? value['archiveAfter'] : value['archiveAfter'].toISOString(),
341
348
  'desktopAvailable': value['desktopAvailable'],
342
349
  'desktopUrl': value['desktopUrl'],
350
+ 'snapshots': value['snapshots'],
343
351
  'snapshotAvailable': value['snapshotAvailable'],
344
352
  'snapshotCompletedAt': value['snapshotCompletedAt'] == null ? value['snapshotCompletedAt'] : value['snapshotCompletedAt'].toISOString(),
345
353
  'snapshotVerifiedAt': value['snapshotVerifiedAt'] == null ? value['snapshotVerifiedAt'] : value['snapshotVerifiedAt'].toISOString(),
@@ -22,6 +22,7 @@ export * from './CommandRequest';
22
22
  export * from './CommandResponse';
23
23
  export * from './CommandStartedResponse';
24
24
  export * from './CommandStatusResponse';
25
+ export * from './CommandStreamFrame';
25
26
  export * from './CompletionEvent';
26
27
  export * from './Conversation';
27
28
  export * from './ConversationsResponse';