@brimble/sandbox 0.1.0 → 0.1.2
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 +9 -5
- package/dist/package.json +10 -3
- package/dist/src/client.d.ts +3 -3
- package/dist/src/client.js +3 -3
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +2 -2
- package/dist/src/resources/files.d.ts +6 -1
- package/dist/src/resources/files.js +36 -0
- package/dist/src/resources/sandbox-handle.d.ts +6 -1
- package/dist/src/resources/sandbox-handle.js +8 -0
- package/dist/src/resources/scoped-sandbox.d.ts +3 -1
- package/dist/src/resources/scoped-sandbox.js +4 -0
- package/dist/src/types/files.d.ts +16 -0
- package/dist/src/types/index.d.ts +1 -1
- package/package.json +7 -3
- package/CODEX.md +0 -188
- package/PLAN.md +0 -364
- package/src/client.ts +0 -61
- package/src/constants.ts +0 -17
- package/src/enums/code-language.ts +0 -4
- package/src/enums/destroy-reason.ts +0 -8
- package/src/enums/destroy-timeout.ts +0 -8
- package/src/enums/index.ts +0 -7
- package/src/enums/sandbox-status.ts +0 -9
- package/src/enums/snapshot-mode.ts +0 -4
- package/src/enums/snapshot-status.ts +0 -5
- package/src/enums/volume-type.ts +0 -3
- package/src/errors/index.ts +0 -2
- package/src/errors/sandbox-api-error.ts +0 -54
- package/src/index.ts +0 -71
- package/src/resources/exec.ts +0 -56
- package/src/resources/files.ts +0 -46
- package/src/resources/index.ts +0 -8
- package/src/resources/path.ts +0 -16
- package/src/resources/sandbox-handle.ts +0 -215
- package/src/resources/sandboxes.ts +0 -297
- package/src/resources/scoped-sandbox.ts +0 -65
- package/src/resources/snapshots.ts +0 -104
- package/src/resources/stats.ts +0 -30
- package/src/resources/volumes.ts +0 -95
- package/src/transport/auth.ts +0 -4
- package/src/transport/http.ts +0 -501
- package/src/transport/pagination.ts +0 -10
- package/src/types/exec.ts +0 -42
- package/src/types/files.ts +0 -1
- package/src/types/index.ts +0 -23
- package/src/types/pagination.ts +0 -16
- package/src/types/region.ts +0 -19
- package/src/types/sandbox.ts +0 -103
- package/src/types/snapshot.ts +0 -17
- package/src/types/stats.ts +0 -35
- package/src/types/template.ts +0 -5
- package/src/types/volume.ts +0 -26
- package/test/integration/sandbox.integration.test.ts +0 -269
- package/test/unit/client.test.ts +0 -87
- package/test/unit/sandboxes.test.ts +0 -69
- package/test/unit/transport.test.ts +0 -126
- package/test/unit/volumes.test.ts +0 -122
- package/tsconfig.json +0 -16
- package/vitest.config.ts +0 -12
- package/vitest.integration.config.ts +0 -15
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,
|
|
27
|
+
import { CodeLanguage, Sandbox } from '@brimble/sandbox';
|
|
28
28
|
|
|
29
|
-
const client = new
|
|
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
|
|
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,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brimble/sandbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "TypeScript SDK for the Brimble Sandbox API",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
7
14
|
"scripts": {
|
|
8
15
|
"build": "rm -rf dist && tsc -p .",
|
|
9
16
|
"test": "vitest run --config vitest.config.ts",
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { SandboxesResource, SnapshotsResource, VolumesResource } from './resources';
|
|
2
2
|
import type { RetryOptions } from './transport/http';
|
|
3
|
-
export type
|
|
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
|
|
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?:
|
|
22
|
+
constructor(options?: SandboxOptions);
|
|
23
23
|
}
|
package/dist/src/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
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.
|
|
46
|
+
exports.Sandbox = Sandbox;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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 {
|
|
3
|
-
export type {
|
|
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.
|
|
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, "
|
|
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,7 +3,7 @@ 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
9
|
/** Lower-level exec/code runner resource. */
|
|
@@ -30,6 +30,8 @@ export declare class ScopedSandboxResource {
|
|
|
30
30
|
putFile(path: string, body: FileUploadBody, options?: RequestOptions): Promise<void>;
|
|
31
31
|
/** Download file bytes from this sandbox as a stream. */
|
|
32
32
|
getFile(path: string, options?: RequestOptions): Promise<ReadableStream<Uint8Array>>;
|
|
33
|
+
/** Upload multiple files to this sandbox in one request. */
|
|
34
|
+
putFiles(files: BatchFileUploadInput[], options?: RequestOptions): Promise<BatchFileUploadResponse>;
|
|
33
35
|
/** Fetch CPU, memory, and network stats for this sandbox. */
|
|
34
36
|
stats(query?: StatsQuery, options?: RequestOptions): Promise<Stats>;
|
|
35
37
|
/** Create a snapshot for this sandbox. */
|
|
@@ -35,6 +35,10 @@ class ScopedSandboxResource {
|
|
|
35
35
|
getFile(path, options) {
|
|
36
36
|
return this.files.get(path, options);
|
|
37
37
|
}
|
|
38
|
+
/** Upload multiple files to this sandbox in one request. */
|
|
39
|
+
putFiles(files, options) {
|
|
40
|
+
return this.files.putFiles(files, options);
|
|
41
|
+
}
|
|
38
42
|
/** Fetch CPU, memory, and network stats for this sandbox. */
|
|
39
43
|
stats(query = {}, options) {
|
|
40
44
|
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,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brimble/sandbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "TypeScript SDK for the Brimble Sandbox API",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
10
14
|
"scripts": {
|
|
11
15
|
"build": "rm -rf dist && tsc -p .",
|
|
12
16
|
"test": "vitest run --config vitest.config.ts",
|
package/CODEX.md
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# Coding Guidelines
|
|
2
|
-
|
|
3
|
-
These are the rules you follow when writing or modifying code in this codebase. Read them once, apply them always.
|
|
4
|
-
|
|
5
|
-
## 1. Separation of concerns
|
|
6
|
-
|
|
7
|
-
Keep functions, types, enums, and classes in their own files (or at minimum, their own clearly-scoped sections). Do not mix concerns.
|
|
8
|
-
|
|
9
|
-
- One responsibility per unit. A function does one thing; a class models one concept; a type describes one shape.
|
|
10
|
-
- Types and enums live in dedicated files (`types.ts`, `enums.ts`, or a `types/` / `enums/` folder) unless they are trivially local to a single consumer.
|
|
11
|
-
- Do not co-locate unrelated helpers inside a class or module just because they happen to touch the same data.
|
|
12
|
-
- If a file starts doing two jobs, split it.
|
|
13
|
-
|
|
14
|
-
## 2. Ternaries: short and flat, or not at all
|
|
15
|
-
|
|
16
|
-
Use a ternary only when it is short, single-line, and obvious at a glance.
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
// Good
|
|
20
|
-
const label = isActive ? 'on' : 'off';
|
|
21
|
-
|
|
22
|
-
// Bad — nested
|
|
23
|
-
const label = isActive ? (isAdmin ? 'admin-on' : 'user-on') : isAdmin ? 'admin-off' : 'user-off';
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
No nested ternaries. Ever. If branching gets past one level, use `if`/`else` or extract a function. Readability wins over cleverness.
|
|
27
|
-
|
|
28
|
-
## 3. Stop using `typeof` everywhere
|
|
29
|
-
|
|
30
|
-
`typeof` is a narrowing tool, not a default check. Do not reach for it when a simple truthy/falsy check is enough.
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
// Good
|
|
34
|
-
if (!user) return;
|
|
35
|
-
if (value) process(value);
|
|
36
|
-
|
|
37
|
-
// Bad
|
|
38
|
-
if (typeof user !== "undefined" && user !== null) { ... }
|
|
39
|
-
if (typeof value === "string" && value.length > 0) { ... } // when you just need truthiness
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Use `typeof` only when you genuinely need to discriminate primitive types (e.g. inside a union where `string | number` matters). Otherwise, trust truthiness, optional chaining, and nullish coalescing.
|
|
43
|
-
|
|
44
|
-
## 4. No over-engineered guard rails
|
|
45
|
-
|
|
46
|
-
Write defenses for realistic failure modes, not hypothetical ones.
|
|
47
|
-
|
|
48
|
-
- Validate at system boundaries (HTTP input, DB reads, external APIs). Do not re-validate the same data at every internal function call.
|
|
49
|
-
- No `assert` chains at the top of ever4y function.
|
|
50
|
-
- No wrapping every call in try/catch "just in case" — let errors bubble to a single handler that knows what to do with them.
|
|
51
|
-
- If a guard clause is protecting against a case that cannot happen given the types, delete it.
|
|
52
|
-
- Prefer types and schemas over runtime paranoia.
|
|
53
|
-
|
|
54
|
-
The goal is correctness, not defensiveness theatre.
|
|
55
|
-
|
|
56
|
-
## 5. Follow popular conventions
|
|
57
|
-
|
|
58
|
-
Use the community standard for the language and ecosystem. Do not invent personal styles.
|
|
59
|
-
|
|
60
|
-
- **TypeScript/JavaScript**: ESLint + Prettier defaults, camelCase for variables/functions, PascalCase for types/classes, `kebab-case` or `camelCase` file names consistent with the project.
|
|
61
|
-
- **Go**: `gofmt`, idiomatic error handling (`if err != nil`), package names short and lowercase, no stutter.
|
|
62
|
-
- **Python**: PEP 8, `ruff`/`black` formatting, `snake_case`.
|
|
63
|
-
- Match the project's existing style before imposing anything new. When in doubt, grep the codebase and do what it already does.
|
|
64
|
-
|
|
65
|
-
## 6. Comments: only when they add something
|
|
66
|
-
|
|
67
|
-
Code should read itself. Comments are for things the code cannot say.
|
|
68
|
-
|
|
69
|
-
Write a comment when:
|
|
70
|
-
|
|
71
|
-
- The _why_ is non-obvious (business rule, workaround for an upstream bug, performance-sensitive choice).
|
|
72
|
-
- A public API needs a docstring for consumers.
|
|
73
|
-
- A `TODO` / `FIXME` with context and ownership.
|
|
74
|
-
|
|
75
|
-
Do not write a comment when:
|
|
76
|
-
|
|
77
|
-
- It restates the code (`// increment counter` above `counter++`).
|
|
78
|
-
- It narrates obvious control flow (`// loop through users`).
|
|
79
|
-
- It was auto-generated boilerplate that nobody reads.
|
|
80
|
-
|
|
81
|
-
Delete stale comments on sight. A wrong comment is worse than no comment.
|
|
82
|
-
|
|
83
|
-
## 7. Don't re-invent what already exists
|
|
84
|
-
|
|
85
|
-
Before writing a helper, check:
|
|
86
|
-
|
|
87
|
-
1. The language's stdlib.
|
|
88
|
-
2. The framework the project uses.
|
|
89
|
-
3. Utilities already in this codebase.
|
|
90
|
-
|
|
91
|
-
Do not write a custom `debounce`, `deepClone`, `groupBy`, UUID generator, date parser, retry loop, or config loader when a well-tested one is already available. Do not introduce a second HTTP client, a second logger, or a second error class when the repo already has one — use what's there.
|
|
92
|
-
|
|
93
|
-
The only reasons to roll your own: the existing option is genuinely insufficient, or pulling it in costs more than writing it.
|
|
94
|
-
|
|
95
|
-
## 8. No `any`, no `as unknown as T`
|
|
96
|
-
|
|
97
|
-
When types get annoying, fix the type — don't escape it.
|
|
98
|
-
|
|
99
|
-
- `any` is banned except at genuine interop edges (and even then, narrow immediately).
|
|
100
|
-
- `as` casts are a last resort. If you're writing `as unknown as T`, the real answer is usually a type guard, a discriminated union, or a schema parse (Zod, etc.).
|
|
101
|
-
- `// @ts-ignore` and `// @ts-expect-error` require a comment explaining why.
|
|
102
|
-
|
|
103
|
-
The type system earns its keep when you respect it.
|
|
104
|
-
|
|
105
|
-
## 9. Delete dead code
|
|
106
|
-
|
|
107
|
-
- No commented-out blocks "for reference." Git remembers.
|
|
108
|
-
- No unused imports, unused variables, unused parameters (prefix with `_` if the signature forces it).
|
|
109
|
-
- No stale feature flags still wired up months after launch.
|
|
110
|
-
- No "v2" helper sitting next to the original with no caller.
|
|
111
|
-
|
|
112
|
-
If it isn't used, it leaves.
|
|
113
|
-
|
|
114
|
-
## 10. Consistent error handling
|
|
115
|
-
|
|
116
|
-
Pick one error strategy per layer and stick to it.
|
|
117
|
-
|
|
118
|
-
- Don't mix `throw`, `Result<T, E>`, `null` returns, and `{ error, data }` tuples in the same module.
|
|
119
|
-
- Don't catch errors only to re-throw them unchanged — that's noise.
|
|
120
|
-
- Don't swallow errors with empty `catch {}`. If you genuinely want to ignore one, log it or leave a comment saying why.
|
|
121
|
-
- Handle errors at the layer that knows what to do with them (usually the HTTP handler or job runner), not at every function on the way down.
|
|
122
|
-
|
|
123
|
-
## 11. No magic numbers or magic strings
|
|
124
|
-
|
|
125
|
-
Named constants and enums beat literals scattered through the code.
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
// Bad
|
|
129
|
-
setTimeout(retry, 86400000);
|
|
130
|
-
if (user.status === "active") { ... }
|
|
131
|
-
|
|
132
|
-
// Good
|
|
133
|
-
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
134
|
-
setTimeout(retry, ONE_DAY_MS);
|
|
135
|
-
if (user.status === UserStatus.Active) { ... }
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
If a value appears more than once, or its meaning isn't obvious from the number/string alone, name it.
|
|
139
|
-
|
|
140
|
-
## 12. Async correctness
|
|
141
|
-
|
|
142
|
-
- Always `await` or explicitly handle the promise. No dangling promises.
|
|
143
|
-
- Parallelize independent work with `Promise.all`; don't serialize it in a `for` loop by accident.
|
|
144
|
-
- Don't mix `.then()` chains and `await` in the same function — pick one.
|
|
145
|
-
- Handle rejections. An unhandled rejection in production is a bug waiting to page you.
|
|
146
|
-
|
|
147
|
-
## 13. Don't mutate function arguments
|
|
148
|
-
|
|
149
|
-
Treat inputs as read-only. If you need a modified version, return a new one.
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// Bad
|
|
153
|
-
function addTag(user, tag) {
|
|
154
|
-
user.tags.push(tag);
|
|
155
|
-
return user;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Good
|
|
159
|
-
function addTag(user, tag) {
|
|
160
|
-
return { ...user, tags: [...user.tags, tag] };
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Hidden mutation through a function call is one of the worst bugs to trace in a large system.
|
|
165
|
-
|
|
166
|
-
## 14. Respect the codebase's existing patterns
|
|
167
|
-
|
|
168
|
-
Before introducing something new, look around:
|
|
169
|
-
|
|
170
|
-
- If the project uses a specific HTTP client, logger, error class, config loader, or validation library — use it.
|
|
171
|
-
- If there's an established folder structure, follow it.
|
|
172
|
-
- If there's a naming convention in neighboring files, match it.
|
|
173
|
-
|
|
174
|
-
Don't bring in a new dependency or pattern just because it's what you reached for first. Consistency across the codebase is worth more than any individual preference.
|
|
175
|
-
|
|
176
|
-
## 15. No hardcoded environment values
|
|
177
|
-
|
|
178
|
-
URLs, ports, credentials, feature flags, and tunable limits belong in config or environment variables — not in source.
|
|
179
|
-
|
|
180
|
-
- Secrets never land in the repo. Not even temporarily.
|
|
181
|
-
- Defaults for local dev are fine in a `.env.example` or config file, but the real values are injected.
|
|
182
|
-
- If a value differs between staging and prod, it's config.
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## Summary
|
|
187
|
-
|
|
188
|
-
Clean separation, flat logic, minimal runtime paranoia, community idioms, and comments only where they earn their place. Reuse what exists, respect the type system, delete what's dead, handle errors consistently, and keep configuration out of source. If a change adds complexity without earning it, reject the change.
|