@brimble/sandbox 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -24,9 +24,9 @@ BRIMBLE_SANDBOX_KEY=your_key_here npm run test:all
24
24
  ## Quickstart
25
25
 
26
26
  ```ts
27
- import { CodeLanguage, SandboxClient } from '@brimble/sandbox';
27
+ import { CodeLanguage, Sandbox } from '@brimble/sandbox';
28
28
 
29
- const client = new SandboxClient();
29
+ const client = new Sandbox();
30
30
 
31
31
  const sandbox = await client.sandboxes.createReady({
32
32
  template: 'node-22',
@@ -37,6 +37,10 @@ const sandbox = await client.sandboxes.createReady({
37
37
  await sandbox.exec({ cmd: 'node -v' });
38
38
 
39
39
  await sandbox.putFile('tmp/notes.txt', Buffer.from('hello sandbox'));
40
+ await sandbox.putFiles([
41
+ { path: '/tmp/hello.txt', body: 'hello from batch' },
42
+ { path: '/tmp/config.json', body: JSON.stringify({ mode: 'dev' }) },
43
+ ]);
40
44
  const stream = await sandbox.getFile('tmp/notes.txt');
41
45
 
42
46
  await sandbox.runCode({
@@ -98,7 +102,7 @@ Use `client.sandboxes.create({ ..., volumeId })` or `client.sandboxes.withVolume
98
102
  ## Retry, timeouts, and idempotency
99
103
 
100
104
  ```ts
101
- const client = new SandboxClient({
105
+ const client = new Sandbox({
102
106
  timeoutMs: 30_000,
103
107
  retry: {
104
108
  maxAttempts: 3,
@@ -120,9 +124,9 @@ If `region` is omitted, the SDK resolves the first available sandbox region auto
120
124
  - `client.sandboxes`
121
125
  - `create`, `createReady`, `withVolume`, `list`, `iterate`, `get`, `getReady`, `listRegions`, `listTemplates`, `getTemplate`, `destroy`, `pause`, `resume`, `quickstartNode`, `quickstartPython`, `use`
122
126
  - `sandbox` handle (returned from `create/get/list`)
123
- - `waitUntilReady`, `refresh`, `destroy`, `pause`, `resume`, `exec`, `runCode`, `putFile`, `getFile`, `stats`, `createSnapshot`, `listSnapshots`, `snapshots.create`, `snapshots.list`
127
+ - `waitUntilReady`, `refresh`, `destroy`, `pause`, `resume`, `exec`, `runCode`, `putFile`, `putFiles`, `getFile`, `stats`, `createSnapshot`, `listSnapshots`, `snapshots.create`, `snapshots.list`
124
128
  - `client.sandboxes.use(id)`
125
- - `exec`, `runCode`, `putFile`, `getFile`, `stats`, `createSnapshot`, `listSnapshots`
129
+ - `exec`, `runCode`, `putFile`, `putFiles`, `getFile`, `stats`, `createSnapshot`, `listSnapshots`
126
130
  - `client.snapshots`
127
131
  - `listAll`, `iterateAll`, `delete`
128
132
  - `client.volumes`
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimble/sandbox",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript SDK for the Brimble Sandbox API",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -1,13 +1,13 @@
1
1
  import { SandboxesResource, SnapshotsResource, VolumesResource } from './resources';
2
2
  import type { RetryOptions } from './transport/http';
3
- export type SandboxClientOptions = {
3
+ export type SandboxOptions = {
4
4
  apiKey?: string;
5
5
  baseUrl?: string;
6
6
  timeoutMs?: number;
7
7
  retry?: RetryOptions;
8
8
  fetchImpl?: typeof fetch;
9
9
  };
10
- export declare class SandboxClient {
10
+ export declare class Sandbox {
11
11
  /** Access sandbox lifecycle and per-sandbox scoped operations. */
12
12
  readonly sandboxes: SandboxesResource;
13
13
  /** Access account-level snapshot operations. */
@@ -19,5 +19,5 @@ export declare class SandboxClient {
19
19
  * Creates a client instance for the Brimble Sandbox API.
20
20
  * Pass `apiKey` directly or set `BRIMBLE_SANDBOX_KEY` in your environment.
21
21
  */
22
- constructor(options?: SandboxClientOptions);
22
+ constructor(options?: SandboxOptions);
23
23
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SandboxClient = void 0;
3
+ exports.Sandbox = void 0;
4
4
  const constants_1 = require("./constants");
5
5
  const resources_1 = require("./resources");
6
6
  const http_1 = require("./transport/http");
@@ -18,7 +18,7 @@ function resolveApiKey(options) {
18
18
  }
19
19
  throw new Error(`Sandbox API key is required. Pass "apiKey" explicitly or set ${constants_1.SANDBOX_API_KEY_ENV_NAME} in your environment.`);
20
20
  }
21
- class SandboxClient {
21
+ class Sandbox {
22
22
  /** Access sandbox lifecycle and per-sandbox scoped operations. */
23
23
  sandboxes;
24
24
  /** Access account-level snapshot operations. */
@@ -43,4 +43,4 @@ class SandboxClient {
43
43
  this.volumes = new resources_1.VolumesResource(this.transport);
44
44
  }
45
45
  }
46
- exports.SandboxClient = SandboxClient;
46
+ exports.Sandbox = Sandbox;
@@ -1,10 +1,10 @@
1
1
  export { DEFAULT_BASE_URL, DEFAULT_PAGE, DEFAULT_PAGE_LIMIT, DEFAULT_RETRY_BASE_DELAY_MS, DEFAULT_RETRY_MAX_ATTEMPTS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_RETRY_STATUSES, DEFAULT_TIMEOUT_MS, MAX_PAGE_LIMIT, SANDBOX_API_KEY_ENV_NAME, } from './constants';
2
- export { SandboxClient } from './client';
3
- export type { SandboxClientOptions } from './client';
2
+ export { Sandbox } from './client';
3
+ export type { SandboxOptions } from './client';
4
4
  export { AuthError, NotFoundError, RateLimitError, SandboxApiError, ValidationError } from './errors';
5
5
  export type { SandboxApiErrorArgs } from './errors';
6
6
  export { CodeLanguage, DestroyReason, DestroyTimeout, SandboxStatus, SnapshotMode, SnapshotStatus, VolumeType, } from './enums';
7
7
  export { ExecResource, FilesResource, SandboxHandle, SandboxesResource, ScopedSandboxResource, SnapshotScopeResource, SnapshotsResource, StatsResource, VolumesResource, } from './resources';
8
- export type { AckMessage, CodeInput, CreateSandboxInput, CreateSandboxResult, CreateSnapshotInput, CreateVolumeInput, ExecInput, ExecResult, ExecStreamFrame, FileUploadBody, Paginated, Pagination, RegionSummary, SandboxRegion, SandboxRegionsResult, Sandbox, SandboxSpecs, Snapshot, Stats, StatsAverageNetwork, StatsAverageNumeric, StatsQuery, StatsTimelinePoint, TeamScopedPagination, WaitUntilReadyOptions, Volume, } from './types';
8
+ export type { AckMessage, CodeInput, CreateSandboxInput, CreateSandboxResult, CreateSnapshotInput, CreateVolumeInput, ExecInput, ExecResult, ExecStreamFrame, FileUploadBody, Paginated, Pagination, RegionSummary, SandboxRegion, SandboxRegionsResult, Sandbox as SandboxData, SandboxSpecs, Snapshot, Stats, StatsAverageNetwork, StatsAverageNumeric, StatsQuery, StatsTimelinePoint, TeamScopedPagination, WaitUntilReadyOptions, Volume, } from './types';
9
9
  export type { RequestOptions } from './transport/http';
10
10
  export type { RetryOptions } from './transport/http';
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VolumesResource = exports.StatsResource = exports.SnapshotsResource = exports.SnapshotScopeResource = exports.ScopedSandboxResource = exports.SandboxesResource = exports.SandboxHandle = exports.FilesResource = exports.ExecResource = exports.VolumeType = exports.SnapshotStatus = exports.SnapshotMode = exports.SandboxStatus = exports.DestroyTimeout = exports.DestroyReason = exports.CodeLanguage = exports.ValidationError = exports.SandboxApiError = exports.RateLimitError = exports.NotFoundError = exports.AuthError = exports.SandboxClient = exports.SANDBOX_API_KEY_ENV_NAME = exports.MAX_PAGE_LIMIT = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_RETRY_STATUSES = exports.DEFAULT_RETRY_MAX_DELAY_MS = exports.DEFAULT_RETRY_MAX_ATTEMPTS = exports.DEFAULT_RETRY_BASE_DELAY_MS = exports.DEFAULT_PAGE_LIMIT = exports.DEFAULT_PAGE = exports.DEFAULT_BASE_URL = void 0;
3
+ exports.VolumesResource = exports.StatsResource = exports.SnapshotsResource = exports.SnapshotScopeResource = exports.ScopedSandboxResource = exports.SandboxesResource = exports.SandboxHandle = exports.FilesResource = exports.ExecResource = exports.VolumeType = exports.SnapshotStatus = exports.SnapshotMode = exports.SandboxStatus = exports.DestroyTimeout = exports.DestroyReason = exports.CodeLanguage = exports.ValidationError = exports.SandboxApiError = exports.RateLimitError = exports.NotFoundError = exports.AuthError = exports.Sandbox = exports.SANDBOX_API_KEY_ENV_NAME = exports.MAX_PAGE_LIMIT = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_RETRY_STATUSES = exports.DEFAULT_RETRY_MAX_DELAY_MS = exports.DEFAULT_RETRY_MAX_ATTEMPTS = exports.DEFAULT_RETRY_BASE_DELAY_MS = exports.DEFAULT_PAGE_LIMIT = exports.DEFAULT_PAGE = exports.DEFAULT_BASE_URL = void 0;
4
4
  var constants_1 = require("./constants");
5
5
  Object.defineProperty(exports, "DEFAULT_BASE_URL", { enumerable: true, get: function () { return constants_1.DEFAULT_BASE_URL; } });
6
6
  Object.defineProperty(exports, "DEFAULT_PAGE", { enumerable: true, get: function () { return constants_1.DEFAULT_PAGE; } });
@@ -13,7 +13,7 @@ Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", { enumerable: true, get: fu
13
13
  Object.defineProperty(exports, "MAX_PAGE_LIMIT", { enumerable: true, get: function () { return constants_1.MAX_PAGE_LIMIT; } });
14
14
  Object.defineProperty(exports, "SANDBOX_API_KEY_ENV_NAME", { enumerable: true, get: function () { return constants_1.SANDBOX_API_KEY_ENV_NAME; } });
15
15
  var client_1 = require("./client");
16
- Object.defineProperty(exports, "SandboxClient", { enumerable: true, get: function () { return client_1.SandboxClient; } });
16
+ Object.defineProperty(exports, "Sandbox", { enumerable: true, get: function () { return client_1.Sandbox; } });
17
17
  var errors_1 = require("./errors");
18
18
  Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return errors_1.AuthError; } });
19
19
  Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return errors_1.NotFoundError; } });
@@ -1,4 +1,4 @@
1
- import type { FileUploadBody } from '../types';
1
+ import type { BatchFileUploadInput, BatchFileUploadResponse, FileUploadBody } from '../types';
2
2
  import type { RequestOptions } from '../transport/http';
3
3
  import { HttpTransport } from '../transport/http';
4
4
  export declare class FilesResource {
@@ -13,4 +13,9 @@ export declare class FilesResource {
13
13
  put(path: string, body: FileUploadBody, options?: RequestOptions): Promise<void>;
14
14
  /** Download a file from the sandbox as a stream. */
15
15
  get(path: string, options?: RequestOptions): Promise<ReadableStream<Uint8Array>>;
16
+ /**
17
+ * Upload multiple files in one request using base64 payloads.
18
+ * Best for small/medium known files (max 100 per call).
19
+ */
20
+ putFiles(files: BatchFileUploadInput[], options?: RequestOptions): Promise<BatchFileUploadResponse>;
16
21
  }
@@ -37,5 +37,41 @@ class FilesResource {
37
37
  ...options,
38
38
  });
39
39
  }
40
+ /**
41
+ * Upload multiple files in one request using base64 payloads.
42
+ * Best for small/medium known files (max 100 per call).
43
+ */
44
+ async putFiles(files, options) {
45
+ if (files.length === 0) {
46
+ throw new Error('putFiles requires at least one file.');
47
+ }
48
+ if (files.length > 100) {
49
+ throw new Error('putFiles supports at most 100 files per request.');
50
+ }
51
+ const response = await this.transport.requestJson({
52
+ endpoint: `/sandboxes/${this.sandboxId}/files/batch`,
53
+ method: 'POST',
54
+ body: {
55
+ files: files.map((file) => ({
56
+ path: normalizeBatchPath(file.path),
57
+ content_base64: encodeBatchBody(file.body),
58
+ })),
59
+ },
60
+ ...options,
61
+ });
62
+ if (!response) {
63
+ throw new Error('Batch upload returned an empty response.');
64
+ }
65
+ return response;
66
+ }
40
67
  }
41
68
  exports.FilesResource = FilesResource;
69
+ function normalizeBatchPath(path) {
70
+ return path.startsWith('/') ? path : `/${path}`;
71
+ }
72
+ function encodeBatchBody(body) {
73
+ if (typeof body === 'string') {
74
+ return Buffer.from(body, 'utf-8').toString('base64');
75
+ }
76
+ return Buffer.from(body).toString('base64');
77
+ }
@@ -1,6 +1,6 @@
1
1
  import { SandboxStatus } from '../enums';
2
2
  import type { RequestOptions } from '../transport/http';
3
- import type { AckMessage, CodeInput, CreateSandboxResult, CreateSnapshotInput, ExecInput, ExecResult, FileUploadBody, Paginated, Pagination, Sandbox, SandboxRuntimeOptions, Snapshot, Stats, StatsQuery, WaitUntilReadyOptions } from '../types';
3
+ import type { AckMessage, BatchFileUploadInput, BatchFileUploadResponse, CodeInput, CreateSandboxResult, CreateSnapshotInput, ExecInput, ExecResult, FileUploadBody, Paginated, Pagination, Sandbox, SandboxRuntimeOptions, Snapshot, Stats, StatsQuery, WaitUntilReadyOptions } from '../types';
4
4
  import type { SandboxesResource } from './sandboxes';
5
5
  export declare class SandboxHandle {
6
6
  private readonly sandboxes;
@@ -58,6 +58,11 @@ export declare class SandboxHandle {
58
58
  * By default this throws when not ready; set `waitUntilReady` to auto-wait.
59
59
  */
60
60
  getFile(path: string, options?: SandboxRuntimeOptions): Promise<ReadableStream<Uint8Array>>;
61
+ /**
62
+ * Upload multiple files in one call.
63
+ * By default this throws when not ready; set `waitUntilReady` to auto-wait.
64
+ */
65
+ putFiles(files: BatchFileUploadInput[], options?: SandboxRuntimeOptions): Promise<BatchFileUploadResponse>;
61
66
  /**
62
67
  * Fetch usage stats for this sandbox.
63
68
  * By default this throws when not ready; set `waitUntilReady` to auto-wait.
@@ -104,6 +104,14 @@ class SandboxHandle {
104
104
  await this.ensureReady(options.waitUntilReady);
105
105
  return this.scope.getFile(path, options);
106
106
  }
107
+ /**
108
+ * Upload multiple files in one call.
109
+ * By default this throws when not ready; set `waitUntilReady` to auto-wait.
110
+ */
111
+ async putFiles(files, options = {}) {
112
+ await this.ensureReady(options.waitUntilReady);
113
+ return this.scope.putFiles(files, options);
114
+ }
107
115
  /**
108
116
  * Fetch usage stats for this sandbox.
109
117
  * By default this throws when not ready; set `waitUntilReady` to auto-wait.
@@ -3,9 +3,11 @@ import { FilesResource } from './files';
3
3
  import { SnapshotScopeResource } from './snapshots';
4
4
  import { StatsResource } from './stats';
5
5
  import { HttpTransport } from '../transport/http';
6
- import type { CodeInput, CreateSnapshotInput, ExecInput, ExecResult, FileUploadBody, Paginated, Pagination, Snapshot, Stats, StatsQuery } from '../types';
6
+ import type { BatchFileUploadInput, BatchFileUploadResponse, CodeInput, CreateSnapshotInput, ExecInput, ExecResult, FileUploadBody, Paginated, Pagination, Snapshot, Stats, StatsQuery } from '../types';
7
7
  import type { RequestOptions } from '../transport/http';
8
8
  export declare class ScopedSandboxResource {
9
+ private readonly transport;
10
+ private readonly sandboxId;
9
11
  /** Lower-level exec/code runner resource. */
10
12
  readonly execResource: ExecResource;
11
13
  /** File upload/download resource. */
@@ -16,6 +18,8 @@ export declare class ScopedSandboxResource {
16
18
  readonly statsResource: StatsResource;
17
19
  /** @internal Create a sandbox-scoped resource wrapper. */
18
20
  constructor(transport: HttpTransport, sandboxId: string);
21
+ /** Destroy this sandbox. */
22
+ destroy(options?: RequestOptions): Promise<void>;
19
23
  /** Run a shell command in this sandbox. */
20
24
  exec(input: ExecInput & {
21
25
  stream: true;
@@ -30,6 +34,8 @@ export declare class ScopedSandboxResource {
30
34
  putFile(path: string, body: FileUploadBody, options?: RequestOptions): Promise<void>;
31
35
  /** Download file bytes from this sandbox as a stream. */
32
36
  getFile(path: string, options?: RequestOptions): Promise<ReadableStream<Uint8Array>>;
37
+ /** Upload multiple files to this sandbox in one request. */
38
+ putFiles(files: BatchFileUploadInput[], options?: RequestOptions): Promise<BatchFileUploadResponse>;
33
39
  /** Fetch CPU, memory, and network stats for this sandbox. */
34
40
  stats(query?: StatsQuery, options?: RequestOptions): Promise<Stats>;
35
41
  /** Create a snapshot for this sandbox. */
@@ -6,6 +6,8 @@ const files_1 = require("./files");
6
6
  const snapshots_1 = require("./snapshots");
7
7
  const stats_1 = require("./stats");
8
8
  class ScopedSandboxResource {
9
+ transport;
10
+ sandboxId;
9
11
  /** Lower-level exec/code runner resource. */
10
12
  execResource;
11
13
  /** File upload/download resource. */
@@ -16,11 +18,21 @@ class ScopedSandboxResource {
16
18
  statsResource;
17
19
  /** @internal Create a sandbox-scoped resource wrapper. */
18
20
  constructor(transport, sandboxId) {
21
+ this.transport = transport;
22
+ this.sandboxId = sandboxId;
19
23
  this.execResource = new exec_1.ExecResource(transport, sandboxId);
20
24
  this.files = new files_1.FilesResource(transport, sandboxId);
21
25
  this.snapshots = new snapshots_1.SnapshotScopeResource(transport, sandboxId);
22
26
  this.statsResource = new stats_1.StatsResource(transport, sandboxId);
23
27
  }
28
+ /** Destroy this sandbox. */
29
+ async destroy(options) {
30
+ await this.transport.requestJson({
31
+ endpoint: `/sandboxes/${this.sandboxId}`,
32
+ method: 'DELETE',
33
+ ...options,
34
+ });
35
+ }
24
36
  exec(input, options) {
25
37
  return this.execResource.exec(input, options);
26
38
  }
@@ -35,6 +47,10 @@ class ScopedSandboxResource {
35
47
  getFile(path, options) {
36
48
  return this.files.get(path, options);
37
49
  }
50
+ /** Upload multiple files to this sandbox in one request. */
51
+ putFiles(files, options) {
52
+ return this.files.putFiles(files, options);
53
+ }
38
54
  /** Fetch CPU, memory, and network stats for this sandbox. */
39
55
  stats(query = {}, options) {
40
56
  return this.statsResource.stats(query, options);
@@ -1 +1,17 @@
1
1
  export type FileUploadBody = ReadableStream<Uint8Array> | Buffer | Uint8Array;
2
+ export type BatchFileUploadBody = Buffer | Uint8Array | string;
3
+ export type BatchFileUploadInput = {
4
+ path: string;
5
+ body: BatchFileUploadBody;
6
+ };
7
+ export type BatchFileUploadResult = {
8
+ path: string;
9
+ bytes: number;
10
+ success: boolean;
11
+ error?: string;
12
+ };
13
+ export type BatchFileUploadResponse = {
14
+ uploaded: number;
15
+ failed: number;
16
+ results: BatchFileUploadResult[];
17
+ };
@@ -1,5 +1,5 @@
1
1
  export type { CodeInput, ExecInput, ExecResult, ExecStreamFrame } from './exec';
2
- export type { FileUploadBody } from './files';
2
+ export type { BatchFileUploadBody, BatchFileUploadInput, BatchFileUploadResponse, BatchFileUploadResult, FileUploadBody } from './files';
3
3
  export type { Paginated, Pagination, TeamScopedPagination } from './pagination';
4
4
  export type { RegionSummary, SandboxRegion, SandboxRegionsResult } from './region';
5
5
  export type { AckMessage, CreateSandboxInput, CreateSandboxRequest, CreateSandboxResult, CreateSandboxWithVolumeInput, CreateSandboxWithVolumeResult, Sandbox, SandboxReadyRequestOptions, SandboxRegionInput, SandboxRuntimeOptions, SandboxSpecs, WaitPreference, WaitUntilReadyOptions, } from './sandbox';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimble/sandbox",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "TypeScript SDK for the Brimble Sandbox API",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",