@boatdev/sdk 1.5.0 → 1.6.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.
@@ -62,6 +62,7 @@ docs/LimitsResponse.md
62
62
  docs/MeResponse.md
63
63
  docs/MeResponseAllOfUser.md
64
64
  docs/NamedSnapshot.md
65
+ docs/NamedSnapshotAllowance.md
65
66
  docs/NamedSnapshotDeletedResponse.md
66
67
  docs/NamedSnapshotInfoResponse.md
67
68
  docs/NamedSnapshotListResponse.md
@@ -202,6 +203,7 @@ src/models/LimitsResponse.ts
202
203
  src/models/MeResponse.ts
203
204
  src/models/MeResponseAllOfUser.ts
204
205
  src/models/NamedSnapshot.ts
206
+ src/models/NamedSnapshotAllowance.ts
205
207
  src/models/NamedSnapshotDeletedResponse.ts
206
208
  src/models/NamedSnapshotInfoResponse.ts
207
209
  src/models/NamedSnapshotListResponse.ts
@@ -53,6 +53,12 @@ export declare function stopAndRemove(api: BoatApi, sandboxId: string, options?:
53
53
  }): Promise<import("./models").DeletionOperationResponse | import("./models").SandboxActionResponse>;
54
54
  export declare function readText(api: BoatApi, sandboxId: string, path: string): Promise<string>;
55
55
  export declare function writeText(api: BoatApi, sandboxId: string, path: string, content: string): Promise<import("./models").FileWriteResponse>;
56
+ /**
57
+ * Run a command and return its result.
58
+ *
59
+ * Safe to call right after `resume()`: a refusal that never ran the command
60
+ * (`retryable: true`, the sandbox is still restoring) is resent for up to 60 s.
61
+ */
56
62
  export declare function execCommand(api: BoatApi, sandboxId: string, command: string, cwd?: string, timeoutSeconds?: number): Promise<import("./models").Command200Response>;
57
63
  /** One line of a streamed command, as sent (an `exit` frame also carries e.g. `signal`, `oomKilled`). */
58
64
  export type CommandFrame = CommandStreamFrame & Record<string, unknown>;
@@ -172,12 +172,36 @@ async function readText(api, sandboxId, path) {
172
172
  async function writeText(api, sandboxId, path, content) {
173
173
  return api.writeFile({ sandboxId, fileWriteRequest: { path, content, encoding: 'utf8' } });
174
174
  }
175
- async function execCommand(api, sandboxId, command, cwd, timeoutSeconds = 30) {
176
- return api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
177
- }
178
175
  // A refusal that provably never ran the command (the sandbox is still restoring
179
- // its disk, `retryable: true` before `started`) is retried for this long.
176
+ // its disk, `retryable: true`) is retried for this long.
180
177
  const STREAM_RETRY_MS = 60000;
178
+ async function isRetryable(e) {
179
+ const response = e === null || e === void 0 ? void 0 : e.response;
180
+ if (!response)
181
+ return false;
182
+ const body = await response.clone().json().catch(() => undefined);
183
+ return (body === null || body === void 0 ? void 0 : body.retryable) === true;
184
+ }
185
+ /**
186
+ * Run a command and return its result.
187
+ *
188
+ * Safe to call right after `resume()`: a refusal that never ran the command
189
+ * (`retryable: true`, the sandbox is still restoring) is resent for up to 60 s.
190
+ */
191
+ async function execCommand(api, sandboxId, command, cwd, timeoutSeconds = 30) {
192
+ const deadline = Date.now() + STREAM_RETRY_MS;
193
+ for (;;) {
194
+ try {
195
+ return await api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
196
+ }
197
+ catch (e) {
198
+ // The 1 s pause before the resend must also fit before the deadline.
199
+ if (Date.now() + 1000 >= deadline || !(await isRetryable(e)))
200
+ throw e;
201
+ }
202
+ await delay(1000);
203
+ }
204
+ }
181
205
  async function* ndjsonFrames(response) {
182
206
  if (!response.body)
183
207
  throw new Error('The command stream has no body');
@@ -53,6 +53,12 @@ export declare function stopAndRemove(api: BoatApi, sandboxId: string, options?:
53
53
  }): Promise<import("./models").DeletionOperationResponse | import("./models").SandboxActionResponse>;
54
54
  export declare function readText(api: BoatApi, sandboxId: string, path: string): Promise<string>;
55
55
  export declare function writeText(api: BoatApi, sandboxId: string, path: string, content: string): Promise<import("./models").FileWriteResponse>;
56
+ /**
57
+ * Run a command and return its result.
58
+ *
59
+ * Safe to call right after `resume()`: a refusal that never ran the command
60
+ * (`retryable: true`, the sandbox is still restoring) is resent for up to 60 s.
61
+ */
56
62
  export declare function execCommand(api: BoatApi, sandboxId: string, command: string, cwd?: string, timeoutSeconds?: number): Promise<import("./models").Command200Response>;
57
63
  /** One line of a streamed command, as sent (an `exit` frame also carries e.g. `signal`, `oomKilled`). */
58
64
  export type CommandFrame = CommandStreamFrame & Record<string, unknown>;
@@ -158,12 +158,36 @@ export async function readText(api, sandboxId, path) {
158
158
  export async function writeText(api, sandboxId, path, content) {
159
159
  return api.writeFile({ sandboxId, fileWriteRequest: { path, content, encoding: 'utf8' } });
160
160
  }
161
- export async function execCommand(api, sandboxId, command, cwd, timeoutSeconds = 30) {
162
- return api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
163
- }
164
161
  // A refusal that provably never ran the command (the sandbox is still restoring
165
- // its disk, `retryable: true` before `started`) is retried for this long.
162
+ // its disk, `retryable: true`) is retried for this long.
166
163
  const STREAM_RETRY_MS = 60000;
164
+ async function isRetryable(e) {
165
+ const response = e === null || e === void 0 ? void 0 : e.response;
166
+ if (!response)
167
+ return false;
168
+ const body = await response.clone().json().catch(() => undefined);
169
+ return (body === null || body === void 0 ? void 0 : body.retryable) === true;
170
+ }
171
+ /**
172
+ * Run a command and return its result.
173
+ *
174
+ * Safe to call right after `resume()`: a refusal that never ran the command
175
+ * (`retryable: true`, the sandbox is still restoring) is resent for up to 60 s.
176
+ */
177
+ export async function execCommand(api, sandboxId, command, cwd, timeoutSeconds = 30) {
178
+ const deadline = Date.now() + STREAM_RETRY_MS;
179
+ for (;;) {
180
+ try {
181
+ return await api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
182
+ }
183
+ catch (e) {
184
+ // The 1 s pause before the resend must also fit before the deadline.
185
+ if (Date.now() + 1000 >= deadline || !(await isRetryable(e)))
186
+ throw e;
187
+ }
188
+ await delay(1000);
189
+ }
190
+ }
167
191
  async function* ndjsonFrames(response) {
168
192
  if (!response.body)
169
193
  throw new Error('The command stream has no body');
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Boat Public API v1
3
+ * 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.
4
+ *
5
+ * The version of the OpenAPI document: 1.0.0
6
+ *
7
+ *
8
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9
+ * https://openapi-generator.tech
10
+ * Do not edit the class manually.
11
+ */
12
+ /**
13
+ * What the request's wallet (your active organization, `?org=`, or personal) pays for named snapshots. 10 are free per wallet; each one above costs $1.70 a month, charged daily from usage. An organization's count includes every member's names billed to it.
14
+ * @export
15
+ * @interface NamedSnapshotAllowance
16
+ */
17
+ export interface NamedSnapshotAllowance {
18
+ /**
19
+ * Organization the numbers are for. null = personal.
20
+ * @type {string}
21
+ * @memberof NamedSnapshotAllowance
22
+ */
23
+ org?: string | null;
24
+ /**
25
+ * That organization's name.
26
+ * @type {string}
27
+ * @memberof NamedSnapshotAllowance
28
+ */
29
+ orgName?: string | null;
30
+ /**
31
+ * Named snapshots kept for free.
32
+ * @type {number}
33
+ * @memberof NamedSnapshotAllowance
34
+ */
35
+ free?: number;
36
+ /**
37
+ * Named snapshots this wallet keeps.
38
+ * @type {number}
39
+ * @memberof NamedSnapshotAllowance
40
+ */
41
+ used?: number;
42
+ /**
43
+ * Named snapshots above the free ones.
44
+ * @type {number}
45
+ * @memberof NamedSnapshotAllowance
46
+ */
47
+ extra?: number;
48
+ /**
49
+ * What the extra ones cost per month.
50
+ * @type {number}
51
+ * @memberof NamedSnapshotAllowance
52
+ */
53
+ extraMonthlyDollars?: number;
54
+ /**
55
+ * Boat time the extra ones take per day, in dollars (the daily charge).
56
+ * @type {number}
57
+ * @memberof NamedSnapshotAllowance
58
+ */
59
+ extraDailyDollars?: number;
60
+ /**
61
+ * Monthly price of one extra named snapshot.
62
+ * @type {number}
63
+ * @memberof NamedSnapshotAllowance
64
+ */
65
+ pricePerExtraMonthlyDollars?: number;
66
+ }
67
+ /**
68
+ * Check if a given object implements the NamedSnapshotAllowance interface.
69
+ */
70
+ export declare function instanceOfNamedSnapshotAllowance(value: object): value is NamedSnapshotAllowance;
71
+ export declare function NamedSnapshotAllowanceFromJSON(json: any): NamedSnapshotAllowance;
72
+ export declare function NamedSnapshotAllowanceFromJSONTyped(json: any, ignoreDiscriminator: boolean): NamedSnapshotAllowance;
73
+ export declare function NamedSnapshotAllowanceToJSON(json: any): NamedSnapshotAllowance;
74
+ export declare function NamedSnapshotAllowanceToJSONTyped(value?: NamedSnapshotAllowance | null, ignoreDiscriminator?: boolean): any;
@@ -0,0 +1,55 @@
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
+ * Check if a given object implements the NamedSnapshotAllowance interface.
16
+ */
17
+ export function instanceOfNamedSnapshotAllowance(value) {
18
+ return true;
19
+ }
20
+ export function NamedSnapshotAllowanceFromJSON(json) {
21
+ return NamedSnapshotAllowanceFromJSONTyped(json, false);
22
+ }
23
+ export function NamedSnapshotAllowanceFromJSONTyped(json, ignoreDiscriminator) {
24
+ if (json == null) {
25
+ return json;
26
+ }
27
+ return {
28
+ 'org': json['org'] == null ? undefined : json['org'],
29
+ 'orgName': json['orgName'] == null ? undefined : json['orgName'],
30
+ 'free': json['free'] == null ? undefined : json['free'],
31
+ 'used': json['used'] == null ? undefined : json['used'],
32
+ 'extra': json['extra'] == null ? undefined : json['extra'],
33
+ 'extraMonthlyDollars': json['extraMonthlyDollars'] == null ? undefined : json['extraMonthlyDollars'],
34
+ 'extraDailyDollars': json['extraDailyDollars'] == null ? undefined : json['extraDailyDollars'],
35
+ 'pricePerExtraMonthlyDollars': json['pricePerExtraMonthlyDollars'] == null ? undefined : json['pricePerExtraMonthlyDollars'],
36
+ };
37
+ }
38
+ export function NamedSnapshotAllowanceToJSON(json) {
39
+ return NamedSnapshotAllowanceToJSONTyped(json, false);
40
+ }
41
+ export function NamedSnapshotAllowanceToJSONTyped(value, ignoreDiscriminator = false) {
42
+ if (value == null) {
43
+ return value;
44
+ }
45
+ return {
46
+ 'org': value['org'],
47
+ 'orgName': value['orgName'],
48
+ 'free': value['free'],
49
+ 'used': value['used'],
50
+ 'extra': value['extra'],
51
+ 'extraMonthlyDollars': value['extraMonthlyDollars'],
52
+ 'extraDailyDollars': value['extraDailyDollars'],
53
+ 'pricePerExtraMonthlyDollars': value['pricePerExtraMonthlyDollars'],
54
+ };
55
+ }
@@ -9,6 +9,7 @@
9
9
  * https://openapi-generator.tech
10
10
  * Do not edit the class manually.
11
11
  */
12
+ import type { NamedSnapshotAllowance } from './NamedSnapshotAllowance';
12
13
  import type { NamedSnapshot } from './NamedSnapshot';
13
14
  /**
14
15
  *
@@ -34,6 +35,12 @@ export interface NamedSnapshotListResponse {
34
35
  * @memberof NamedSnapshotListResponse
35
36
  */
36
37
  snapshots: Array<NamedSnapshot>;
38
+ /**
39
+ *
40
+ * @type {NamedSnapshotAllowance}
41
+ * @memberof NamedSnapshotListResponse
42
+ */
43
+ allowance?: NamedSnapshotAllowance;
37
44
  }
38
45
  /**
39
46
  * @export
@@ -11,6 +11,7 @@
11
11
  * https://openapi-generator.tech
12
12
  * Do not edit the class manually.
13
13
  */
14
+ import { NamedSnapshotAllowanceFromJSON, NamedSnapshotAllowanceToJSON, } from './NamedSnapshotAllowance';
14
15
  import { NamedSnapshotFromJSON, NamedSnapshotToJSON, } from './NamedSnapshot';
15
16
  /**
16
17
  * @export
@@ -41,6 +42,7 @@ export function NamedSnapshotListResponseFromJSONTyped(json, ignoreDiscriminator
41
42
  'ok': json['ok'],
42
43
  'type': json['type'],
43
44
  'snapshots': (json['snapshots'].map(NamedSnapshotFromJSON)),
45
+ 'allowance': json['allowance'] == null ? undefined : NamedSnapshotAllowanceFromJSON(json['allowance']),
44
46
  };
45
47
  }
46
48
  export function NamedSnapshotListResponseToJSON(json) {
@@ -54,5 +56,6 @@ export function NamedSnapshotListResponseToJSONTyped(value, ignoreDiscriminator
54
56
  'ok': value['ok'],
55
57
  'type': value['type'],
56
58
  'snapshots': (value['snapshots'].map(NamedSnapshotToJSON)),
59
+ 'allowance': NamedSnapshotAllowanceToJSON(value['allowance']),
57
60
  };
58
61
  }
@@ -57,6 +57,7 @@ export * from './LimitsResponse';
57
57
  export * from './MeResponse';
58
58
  export * from './MeResponseAllOfUser';
59
59
  export * from './NamedSnapshot';
60
+ export * from './NamedSnapshotAllowance';
60
61
  export * from './NamedSnapshotDeletedResponse';
61
62
  export * from './NamedSnapshotInfoResponse';
62
63
  export * from './NamedSnapshotListResponse';
@@ -59,6 +59,7 @@ export * from './LimitsResponse';
59
59
  export * from './MeResponse';
60
60
  export * from './MeResponseAllOfUser';
61
61
  export * from './NamedSnapshot';
62
+ export * from './NamedSnapshotAllowance';
62
63
  export * from './NamedSnapshotDeletedResponse';
63
64
  export * from './NamedSnapshotInfoResponse';
64
65
  export * from './NamedSnapshotListResponse';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Boat Public API v1
3
+ * 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.
4
+ *
5
+ * The version of the OpenAPI document: 1.0.0
6
+ *
7
+ *
8
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9
+ * https://openapi-generator.tech
10
+ * Do not edit the class manually.
11
+ */
12
+ /**
13
+ * What the request's wallet (your active organization, `?org=`, or personal) pays for named snapshots. 10 are free per wallet; each one above costs $1.70 a month, charged daily from usage. An organization's count includes every member's names billed to it.
14
+ * @export
15
+ * @interface NamedSnapshotAllowance
16
+ */
17
+ export interface NamedSnapshotAllowance {
18
+ /**
19
+ * Organization the numbers are for. null = personal.
20
+ * @type {string}
21
+ * @memberof NamedSnapshotAllowance
22
+ */
23
+ org?: string | null;
24
+ /**
25
+ * That organization's name.
26
+ * @type {string}
27
+ * @memberof NamedSnapshotAllowance
28
+ */
29
+ orgName?: string | null;
30
+ /**
31
+ * Named snapshots kept for free.
32
+ * @type {number}
33
+ * @memberof NamedSnapshotAllowance
34
+ */
35
+ free?: number;
36
+ /**
37
+ * Named snapshots this wallet keeps.
38
+ * @type {number}
39
+ * @memberof NamedSnapshotAllowance
40
+ */
41
+ used?: number;
42
+ /**
43
+ * Named snapshots above the free ones.
44
+ * @type {number}
45
+ * @memberof NamedSnapshotAllowance
46
+ */
47
+ extra?: number;
48
+ /**
49
+ * What the extra ones cost per month.
50
+ * @type {number}
51
+ * @memberof NamedSnapshotAllowance
52
+ */
53
+ extraMonthlyDollars?: number;
54
+ /**
55
+ * Boat time the extra ones take per day, in dollars (the daily charge).
56
+ * @type {number}
57
+ * @memberof NamedSnapshotAllowance
58
+ */
59
+ extraDailyDollars?: number;
60
+ /**
61
+ * Monthly price of one extra named snapshot.
62
+ * @type {number}
63
+ * @memberof NamedSnapshotAllowance
64
+ */
65
+ pricePerExtraMonthlyDollars?: number;
66
+ }
67
+ /**
68
+ * Check if a given object implements the NamedSnapshotAllowance interface.
69
+ */
70
+ export declare function instanceOfNamedSnapshotAllowance(value: object): value is NamedSnapshotAllowance;
71
+ export declare function NamedSnapshotAllowanceFromJSON(json: any): NamedSnapshotAllowance;
72
+ export declare function NamedSnapshotAllowanceFromJSONTyped(json: any, ignoreDiscriminator: boolean): NamedSnapshotAllowance;
73
+ export declare function NamedSnapshotAllowanceToJSON(json: any): NamedSnapshotAllowance;
74
+ export declare function NamedSnapshotAllowanceToJSONTyped(value?: NamedSnapshotAllowance | null, ignoreDiscriminator?: boolean): any;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ /**
5
+ * Boat Public API v1
6
+ * 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.
7
+ *
8
+ * The version of the OpenAPI document: 1.0.0
9
+ *
10
+ *
11
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
12
+ * https://openapi-generator.tech
13
+ * Do not edit the class manually.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.instanceOfNamedSnapshotAllowance = instanceOfNamedSnapshotAllowance;
17
+ exports.NamedSnapshotAllowanceFromJSON = NamedSnapshotAllowanceFromJSON;
18
+ exports.NamedSnapshotAllowanceFromJSONTyped = NamedSnapshotAllowanceFromJSONTyped;
19
+ exports.NamedSnapshotAllowanceToJSON = NamedSnapshotAllowanceToJSON;
20
+ exports.NamedSnapshotAllowanceToJSONTyped = NamedSnapshotAllowanceToJSONTyped;
21
+ /**
22
+ * Check if a given object implements the NamedSnapshotAllowance interface.
23
+ */
24
+ function instanceOfNamedSnapshotAllowance(value) {
25
+ return true;
26
+ }
27
+ function NamedSnapshotAllowanceFromJSON(json) {
28
+ return NamedSnapshotAllowanceFromJSONTyped(json, false);
29
+ }
30
+ function NamedSnapshotAllowanceFromJSONTyped(json, ignoreDiscriminator) {
31
+ if (json == null) {
32
+ return json;
33
+ }
34
+ return {
35
+ 'org': json['org'] == null ? undefined : json['org'],
36
+ 'orgName': json['orgName'] == null ? undefined : json['orgName'],
37
+ 'free': json['free'] == null ? undefined : json['free'],
38
+ 'used': json['used'] == null ? undefined : json['used'],
39
+ 'extra': json['extra'] == null ? undefined : json['extra'],
40
+ 'extraMonthlyDollars': json['extraMonthlyDollars'] == null ? undefined : json['extraMonthlyDollars'],
41
+ 'extraDailyDollars': json['extraDailyDollars'] == null ? undefined : json['extraDailyDollars'],
42
+ 'pricePerExtraMonthlyDollars': json['pricePerExtraMonthlyDollars'] == null ? undefined : json['pricePerExtraMonthlyDollars'],
43
+ };
44
+ }
45
+ function NamedSnapshotAllowanceToJSON(json) {
46
+ return NamedSnapshotAllowanceToJSONTyped(json, false);
47
+ }
48
+ function NamedSnapshotAllowanceToJSONTyped(value, ignoreDiscriminator = false) {
49
+ if (value == null) {
50
+ return value;
51
+ }
52
+ return {
53
+ 'org': value['org'],
54
+ 'orgName': value['orgName'],
55
+ 'free': value['free'],
56
+ 'used': value['used'],
57
+ 'extra': value['extra'],
58
+ 'extraMonthlyDollars': value['extraMonthlyDollars'],
59
+ 'extraDailyDollars': value['extraDailyDollars'],
60
+ 'pricePerExtraMonthlyDollars': value['pricePerExtraMonthlyDollars'],
61
+ };
62
+ }
@@ -9,6 +9,7 @@
9
9
  * https://openapi-generator.tech
10
10
  * Do not edit the class manually.
11
11
  */
12
+ import type { NamedSnapshotAllowance } from './NamedSnapshotAllowance';
12
13
  import type { NamedSnapshot } from './NamedSnapshot';
13
14
  /**
14
15
  *
@@ -34,6 +35,12 @@ export interface NamedSnapshotListResponse {
34
35
  * @memberof NamedSnapshotListResponse
35
36
  */
36
37
  snapshots: Array<NamedSnapshot>;
38
+ /**
39
+ *
40
+ * @type {NamedSnapshotAllowance}
41
+ * @memberof NamedSnapshotListResponse
42
+ */
43
+ allowance?: NamedSnapshotAllowance;
37
44
  }
38
45
  /**
39
46
  * @export
@@ -19,6 +19,7 @@ exports.NamedSnapshotListResponseFromJSON = NamedSnapshotListResponseFromJSON;
19
19
  exports.NamedSnapshotListResponseFromJSONTyped = NamedSnapshotListResponseFromJSONTyped;
20
20
  exports.NamedSnapshotListResponseToJSON = NamedSnapshotListResponseToJSON;
21
21
  exports.NamedSnapshotListResponseToJSONTyped = NamedSnapshotListResponseToJSONTyped;
22
+ const NamedSnapshotAllowance_1 = require("./NamedSnapshotAllowance");
22
23
  const NamedSnapshot_1 = require("./NamedSnapshot");
23
24
  /**
24
25
  * @export
@@ -49,6 +50,7 @@ function NamedSnapshotListResponseFromJSONTyped(json, ignoreDiscriminator) {
49
50
  'ok': json['ok'],
50
51
  'type': json['type'],
51
52
  'snapshots': (json['snapshots'].map(NamedSnapshot_1.NamedSnapshotFromJSON)),
53
+ 'allowance': json['allowance'] == null ? undefined : (0, NamedSnapshotAllowance_1.NamedSnapshotAllowanceFromJSON)(json['allowance']),
52
54
  };
53
55
  }
54
56
  function NamedSnapshotListResponseToJSON(json) {
@@ -62,5 +64,6 @@ function NamedSnapshotListResponseToJSONTyped(value, ignoreDiscriminator = false
62
64
  'ok': value['ok'],
63
65
  'type': value['type'],
64
66
  'snapshots': (value['snapshots'].map(NamedSnapshot_1.NamedSnapshotToJSON)),
67
+ 'allowance': (0, NamedSnapshotAllowance_1.NamedSnapshotAllowanceToJSON)(value['allowance']),
65
68
  };
66
69
  }
@@ -57,6 +57,7 @@ export * from './LimitsResponse';
57
57
  export * from './MeResponse';
58
58
  export * from './MeResponseAllOfUser';
59
59
  export * from './NamedSnapshot';
60
+ export * from './NamedSnapshotAllowance';
60
61
  export * from './NamedSnapshotDeletedResponse';
61
62
  export * from './NamedSnapshotInfoResponse';
62
63
  export * from './NamedSnapshotListResponse';
@@ -75,6 +75,7 @@ __exportStar(require("./LimitsResponse"), exports);
75
75
  __exportStar(require("./MeResponse"), exports);
76
76
  __exportStar(require("./MeResponseAllOfUser"), exports);
77
77
  __exportStar(require("./NamedSnapshot"), exports);
78
+ __exportStar(require("./NamedSnapshotAllowance"), exports);
78
79
  __exportStar(require("./NamedSnapshotDeletedResponse"), exports);
79
80
  __exportStar(require("./NamedSnapshotInfoResponse"), exports);
80
81
  __exportStar(require("./NamedSnapshotListResponse"), exports);
package/docs/BoatApi.md CHANGED
@@ -3983,7 +3983,8 @@ example().catch(console.error);
3983
3983
  | **400** | Invalid request body or parameters. | - |
3984
3984
  | **401** | Missing or invalid bearer token. | - |
3985
3985
  | **404** | Resource not found. | - |
3986
- | **409** | &#x60;named_snapshot_limit&#x60; when you already keep the maximum of 10 named snapshots; remove one first. &#x60;save_in_progress&#x60; when a save under this name is already running; wait for it to settle rather than retrying immediately. | - |
3986
+ | **402** | &#x60;credit_required&#x60;: the wallet already keeps 10 or more named snapshots and has no credit left. Each named snapshot above the free 10 costs $1.70 a month, charged daily. Add credit or remove one. | - |
3987
+ | **409** | &#x60;save_in_progress&#x60; when a save under this name is already running; wait for it to settle rather than retrying immediately. | - |
3987
3988
 
3988
3989
  [[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md)
3989
3990
 
@@ -0,0 +1,47 @@
1
+
2
+ # NamedSnapshotAllowance
3
+
4
+ What the request\'s wallet (your active organization, `?org=`, or personal) pays for named snapshots. 10 are free per wallet; each one above costs $1.70 a month, charged daily from usage. An organization\'s count includes every member\'s names billed to it.
5
+
6
+ ## Properties
7
+
8
+ Name | Type
9
+ ------------ | -------------
10
+ `org` | string
11
+ `orgName` | string
12
+ `free` | number
13
+ `used` | number
14
+ `extra` | number
15
+ `extraMonthlyDollars` | number
16
+ `extraDailyDollars` | number
17
+ `pricePerExtraMonthlyDollars` | number
18
+
19
+ ## Example
20
+
21
+ ```typescript
22
+ import type { NamedSnapshotAllowance } from '@boatdev/sdk'
23
+
24
+ // TODO: Update the object below with actual values
25
+ const example = {
26
+ "org": null,
27
+ "orgName": null,
28
+ "free": null,
29
+ "used": null,
30
+ "extra": null,
31
+ "extraMonthlyDollars": null,
32
+ "extraDailyDollars": null,
33
+ "pricePerExtraMonthlyDollars": null,
34
+ } satisfies NamedSnapshotAllowance
35
+
36
+ console.log(example)
37
+
38
+ // Convert the instance to a JSON string
39
+ const exampleJSON: string = JSON.stringify(example)
40
+ console.log(exampleJSON)
41
+
42
+ // Parse the JSON string back to an object
43
+ const exampleParsed = JSON.parse(exampleJSON) as NamedSnapshotAllowance
44
+ console.log(exampleParsed)
45
+ ```
46
+
47
+ [[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md)
@@ -9,6 +9,7 @@ Name | Type
9
9
  `ok` | boolean
10
10
  `type` | string
11
11
  `snapshots` | [Array&lt;NamedSnapshot&gt;](NamedSnapshot.md)
12
+ `allowance` | [NamedSnapshotAllowance](NamedSnapshotAllowance.md)
12
13
 
13
14
  ## Example
14
15
 
@@ -20,6 +21,7 @@ const example = {
20
21
  "ok": null,
21
22
  "type": null,
22
23
  "snapshots": null,
24
+ "allowance": null,
23
25
  } satisfies NamedSnapshotListResponse
24
26
 
25
27
  console.log(example)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boatdev/sdk",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "OpenAPI client for @boatdev/sdk",
5
5
  "author": "OpenAPI-Generator",
6
6
  "license": "MIT",
@@ -182,8 +182,34 @@ export async function writeText(api: BoatApi, sandboxId: string, path: string, c
182
182
  return api.writeFile({ sandboxId, fileWriteRequest: { path, content, encoding: 'utf8' } });
183
183
  }
184
184
 
185
+ // A refusal that provably never ran the command (the sandbox is still restoring
186
+ // its disk, `retryable: true`) is retried for this long.
187
+ const STREAM_RETRY_MS = 60_000;
188
+
189
+ async function isRetryable(e: unknown): Promise<boolean> {
190
+ const response = (e as { response?: Response })?.response;
191
+ if (!response) return false;
192
+ const body = await response.clone().json().catch(() => undefined);
193
+ return body?.retryable === true;
194
+ }
195
+
196
+ /**
197
+ * Run a command and return its result.
198
+ *
199
+ * Safe to call right after `resume()`: a refusal that never ran the command
200
+ * (`retryable: true`, the sandbox is still restoring) is resent for up to 60 s.
201
+ */
185
202
  export async function execCommand(api: BoatApi, sandboxId: string, command: string, cwd?: string, timeoutSeconds = 30) {
186
- return api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
203
+ const deadline = Date.now() + STREAM_RETRY_MS;
204
+ for (;;) {
205
+ try {
206
+ return await api.command({ sandboxId, commandRequest: { command, cwd, timeoutSeconds } });
207
+ } catch (e) {
208
+ // The 1 s pause before the resend must also fit before the deadline.
209
+ if (Date.now() + 1_000 >= deadline || !(await isRetryable(e))) throw e;
210
+ }
211
+ await delay(1_000);
212
+ }
187
213
  }
188
214
 
189
215
  /** One line of a streamed command, as sent (an `exit` frame also carries e.g. `signal`, `oomKilled`). */
@@ -195,10 +221,6 @@ export type StreamCommandOptions = {
195
221
  signal?: AbortSignal;
196
222
  };
197
223
 
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
224
  async function* ndjsonFrames(response: Response): AsyncGenerator<CommandFrame> {
203
225
  if (!response.body) throw new Error('The command stream has no body');
204
226
  const reader = response.body.getReader();
@@ -0,0 +1,121 @@
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
+ * What the request's wallet (your active organization, `?org=`, or personal) pays for named snapshots. 10 are free per wallet; each one above costs $1.70 a month, charged daily from usage. An organization's count includes every member's names billed to it.
18
+ * @export
19
+ * @interface NamedSnapshotAllowance
20
+ */
21
+ export interface NamedSnapshotAllowance {
22
+ /**
23
+ * Organization the numbers are for. null = personal.
24
+ * @type {string}
25
+ * @memberof NamedSnapshotAllowance
26
+ */
27
+ org?: string | null;
28
+ /**
29
+ * That organization's name.
30
+ * @type {string}
31
+ * @memberof NamedSnapshotAllowance
32
+ */
33
+ orgName?: string | null;
34
+ /**
35
+ * Named snapshots kept for free.
36
+ * @type {number}
37
+ * @memberof NamedSnapshotAllowance
38
+ */
39
+ free?: number;
40
+ /**
41
+ * Named snapshots this wallet keeps.
42
+ * @type {number}
43
+ * @memberof NamedSnapshotAllowance
44
+ */
45
+ used?: number;
46
+ /**
47
+ * Named snapshots above the free ones.
48
+ * @type {number}
49
+ * @memberof NamedSnapshotAllowance
50
+ */
51
+ extra?: number;
52
+ /**
53
+ * What the extra ones cost per month.
54
+ * @type {number}
55
+ * @memberof NamedSnapshotAllowance
56
+ */
57
+ extraMonthlyDollars?: number;
58
+ /**
59
+ * Boat time the extra ones take per day, in dollars (the daily charge).
60
+ * @type {number}
61
+ * @memberof NamedSnapshotAllowance
62
+ */
63
+ extraDailyDollars?: number;
64
+ /**
65
+ * Monthly price of one extra named snapshot.
66
+ * @type {number}
67
+ * @memberof NamedSnapshotAllowance
68
+ */
69
+ pricePerExtraMonthlyDollars?: number;
70
+ }
71
+
72
+ /**
73
+ * Check if a given object implements the NamedSnapshotAllowance interface.
74
+ */
75
+ export function instanceOfNamedSnapshotAllowance(value: object): value is NamedSnapshotAllowance {
76
+ return true;
77
+ }
78
+
79
+ export function NamedSnapshotAllowanceFromJSON(json: any): NamedSnapshotAllowance {
80
+ return NamedSnapshotAllowanceFromJSONTyped(json, false);
81
+ }
82
+
83
+ export function NamedSnapshotAllowanceFromJSONTyped(json: any, ignoreDiscriminator: boolean): NamedSnapshotAllowance {
84
+ if (json == null) {
85
+ return json;
86
+ }
87
+ return {
88
+
89
+ 'org': json['org'] == null ? undefined : json['org'],
90
+ 'orgName': json['orgName'] == null ? undefined : json['orgName'],
91
+ 'free': json['free'] == null ? undefined : json['free'],
92
+ 'used': json['used'] == null ? undefined : json['used'],
93
+ 'extra': json['extra'] == null ? undefined : json['extra'],
94
+ 'extraMonthlyDollars': json['extraMonthlyDollars'] == null ? undefined : json['extraMonthlyDollars'],
95
+ 'extraDailyDollars': json['extraDailyDollars'] == null ? undefined : json['extraDailyDollars'],
96
+ 'pricePerExtraMonthlyDollars': json['pricePerExtraMonthlyDollars'] == null ? undefined : json['pricePerExtraMonthlyDollars'],
97
+ };
98
+ }
99
+
100
+ export function NamedSnapshotAllowanceToJSON(json: any): NamedSnapshotAllowance {
101
+ return NamedSnapshotAllowanceToJSONTyped(json, false);
102
+ }
103
+
104
+ export function NamedSnapshotAllowanceToJSONTyped(value?: NamedSnapshotAllowance | null, ignoreDiscriminator: boolean = false): any {
105
+ if (value == null) {
106
+ return value;
107
+ }
108
+
109
+ return {
110
+
111
+ 'org': value['org'],
112
+ 'orgName': value['orgName'],
113
+ 'free': value['free'],
114
+ 'used': value['used'],
115
+ 'extra': value['extra'],
116
+ 'extraMonthlyDollars': value['extraMonthlyDollars'],
117
+ 'extraDailyDollars': value['extraDailyDollars'],
118
+ 'pricePerExtraMonthlyDollars': value['pricePerExtraMonthlyDollars'],
119
+ };
120
+ }
121
+
@@ -13,6 +13,13 @@
13
13
  */
14
14
 
15
15
  import { mapValues } from '../runtime';
16
+ import type { NamedSnapshotAllowance } from './NamedSnapshotAllowance';
17
+ import {
18
+ NamedSnapshotAllowanceFromJSON,
19
+ NamedSnapshotAllowanceFromJSONTyped,
20
+ NamedSnapshotAllowanceToJSON,
21
+ NamedSnapshotAllowanceToJSONTyped,
22
+ } from './NamedSnapshotAllowance';
16
23
  import type { NamedSnapshot } from './NamedSnapshot';
17
24
  import {
18
25
  NamedSnapshotFromJSON,
@@ -45,6 +52,12 @@ export interface NamedSnapshotListResponse {
45
52
  * @memberof NamedSnapshotListResponse
46
53
  */
47
54
  snapshots: Array<NamedSnapshot>;
55
+ /**
56
+ *
57
+ * @type {NamedSnapshotAllowance}
58
+ * @memberof NamedSnapshotListResponse
59
+ */
60
+ allowance?: NamedSnapshotAllowance;
48
61
  }
49
62
 
50
63
 
@@ -80,6 +93,7 @@ export function NamedSnapshotListResponseFromJSONTyped(json: any, ignoreDiscrimi
80
93
  'ok': json['ok'],
81
94
  'type': json['type'],
82
95
  'snapshots': ((json['snapshots'] as Array<any>).map(NamedSnapshotFromJSON)),
96
+ 'allowance': json['allowance'] == null ? undefined : NamedSnapshotAllowanceFromJSON(json['allowance']),
83
97
  };
84
98
  }
85
99
 
@@ -97,6 +111,7 @@ export function NamedSnapshotListResponseToJSONTyped(value?: NamedSnapshotListRe
97
111
  'ok': value['ok'],
98
112
  'type': value['type'],
99
113
  'snapshots': ((value['snapshots'] as Array<any>).map(NamedSnapshotToJSON)),
114
+ 'allowance': NamedSnapshotAllowanceToJSON(value['allowance']),
100
115
  };
101
116
  }
102
117
 
@@ -59,6 +59,7 @@ export * from './LimitsResponse';
59
59
  export * from './MeResponse';
60
60
  export * from './MeResponseAllOfUser';
61
61
  export * from './NamedSnapshot';
62
+ export * from './NamedSnapshotAllowance';
62
63
  export * from './NamedSnapshotDeletedResponse';
63
64
  export * from './NamedSnapshotInfoResponse';
64
65
  export * from './NamedSnapshotListResponse';