@mastra/cloudflare-sandbox 0.4.0-alpha.1 → 0.4.0-alpha.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/dist/bridge-client.d.ts +53 -0
- package/dist/bridge-client.d.ts.map +1 -1
- package/dist/index.cjs +80 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts +8 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/testing/fake-bridge.d.ts +10 -0
- package/dist/testing/fake-bridge.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/bridge-client.d.ts
CHANGED
|
@@ -24,6 +24,44 @@ export interface CloudflareExecRequest {
|
|
|
24
24
|
timeoutMs?: number;
|
|
25
25
|
cwd?: string;
|
|
26
26
|
}
|
|
27
|
+
export interface CloudflarePersistWorkspaceOptions {
|
|
28
|
+
/** Relative paths (under /workspace) to exclude from the archive. */
|
|
29
|
+
excludes?: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface CloudflareMountBucketCredentials {
|
|
32
|
+
accessKeyId: string;
|
|
33
|
+
secretAccessKey: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CloudflareMountBucketOptions {
|
|
36
|
+
/** S3-compatible endpoint, e.g. `https://<account>.r2.cloudflarestorage.com`. */
|
|
37
|
+
endpoint?: string;
|
|
38
|
+
/** Mount the bucket read-only. */
|
|
39
|
+
readOnly?: boolean;
|
|
40
|
+
/** Only expose objects under this bucket prefix at the mount point. */
|
|
41
|
+
prefix?: string;
|
|
42
|
+
/** Storage provider hint, e.g. `r2`. */
|
|
43
|
+
provider?: string;
|
|
44
|
+
/** Explicit credentials; omitted when the Worker resolves them from secrets. */
|
|
45
|
+
credentials?: CloudflareMountBucketCredentials;
|
|
46
|
+
}
|
|
47
|
+
export interface CloudflareMountBucketRequest {
|
|
48
|
+
/** Bucket name, e.g. `my-r2-bucket`. */
|
|
49
|
+
bucket: string;
|
|
50
|
+
/** Local filesystem path to mount at, e.g. `/mnt/data`. */
|
|
51
|
+
mountPath: string;
|
|
52
|
+
options?: CloudflareMountBucketOptions;
|
|
53
|
+
}
|
|
54
|
+
export interface CloudflareCreateSessionRequest {
|
|
55
|
+
/** Working directory the session starts in. */
|
|
56
|
+
cwd?: string;
|
|
57
|
+
/** Environment variables seeded into the session. */
|
|
58
|
+
env?: Record<string, string>;
|
|
59
|
+
/** Caller-chosen session id; must match `^[a-zA-Z0-9._-]{1,128}$`. Generated when omitted. */
|
|
60
|
+
sessionId?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface CloudflareSession {
|
|
63
|
+
id: string;
|
|
64
|
+
}
|
|
27
65
|
export declare class CloudflareSandboxBridgeError extends Error {
|
|
28
66
|
readonly status: number;
|
|
29
67
|
readonly body: string;
|
|
@@ -47,6 +85,20 @@ export declare class CloudflareSandboxBridgeClient {
|
|
|
47
85
|
deleteSandbox(id: string): Promise<void>;
|
|
48
86
|
/** `PUT /v1/sandbox/:id/file/*` — one file per request, raw bytes as the body. */
|
|
49
87
|
writeFile(id: string, absolutePath: string, content: Uint8Array | string): Promise<void>;
|
|
88
|
+
/** `GET /v1/sandbox/:id/file/*` — reads one file, returning its raw bytes. */
|
|
89
|
+
readFile(id: string, absolutePath: string): Promise<Uint8Array>;
|
|
90
|
+
/** `GET /v1/sandbox/:id/persist` — archives `/workspace`, returning raw tar bytes. */
|
|
91
|
+
persistWorkspace(id: string, options?: CloudflarePersistWorkspaceOptions): Promise<Uint8Array>;
|
|
92
|
+
/** `POST /v1/sandbox/:id/hydrate` — restores `/workspace` from a raw tar payload. */
|
|
93
|
+
hydrateWorkspace(id: string, tar: Uint8Array): Promise<void>;
|
|
94
|
+
/** `POST /v1/sandbox/:id/mount` — mounts an S3-compatible bucket as a local directory. */
|
|
95
|
+
mountBucket(id: string, request: CloudflareMountBucketRequest): Promise<void>;
|
|
96
|
+
/** `POST /v1/sandbox/:id/unmount` — unmounts a previously mounted bucket. */
|
|
97
|
+
unmountBucket(id: string, mountPath: string): Promise<void>;
|
|
98
|
+
/** `POST /v1/sandbox/:id/session` — creates an execution session, returning its id. */
|
|
99
|
+
createSession(id: string, request?: CloudflareCreateSessionRequest): Promise<CloudflareSession>;
|
|
100
|
+
/** `DELETE /v1/sandbox/:id/session/:sessionId` — tears down an execution session. */
|
|
101
|
+
deleteSession(id: string, sessionId: string): Promise<void>;
|
|
50
102
|
/** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */
|
|
51
103
|
exec(id: string, request: CloudflareExecRequest, options: {
|
|
52
104
|
signal?: AbortSignal;
|
|
@@ -55,5 +107,6 @@ export declare class CloudflareSandboxBridgeClient {
|
|
|
55
107
|
private emitBlock;
|
|
56
108
|
private headers;
|
|
57
109
|
private request;
|
|
110
|
+
private requestBytes;
|
|
58
111
|
}
|
|
59
112
|
//# sourceMappingURL=bridge-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,YAAY,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAKvC;CACF;AAmBD;;;;GAIG;AACH,qBAAa,6BAA6B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IAEpD,YAAY,OAAO,EAAE,oCAAoC,EAIxD;IAED,yBAAyB;IACnB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAGrC;IAED,oCAAoC;IAC9B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG5C;IAED,+BAA+B;IACzB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7C;IAED,kFAAkF;IAC5E,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7F;IAED,gFAAgF;IAC1E,IAAI,CACR,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;KAClD,GACA,OAAO,CAAC,IAAI,CAAC,CAmCf;IAED,OAAO,CAAC,SAAS;IAkCjB,OAAO,CAAC,OAAO;YAID,OAAO;
|
|
1
|
+
{"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -58,6 +58,55 @@ var CloudflareSandboxBridgeClient = class {
|
|
|
58
58
|
headers: { "content-type": "application/octet-stream" }
|
|
59
59
|
}, true);
|
|
60
60
|
}
|
|
61
|
+
/** `GET /v1/sandbox/:id/file/*` — reads one file, returning its raw bytes. */
|
|
62
|
+
async readFile(id, absolutePath) {
|
|
63
|
+
return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`, {});
|
|
64
|
+
}
|
|
65
|
+
/** `GET /v1/sandbox/:id/persist` — archives `/workspace`, returning raw tar bytes. */
|
|
66
|
+
async persistWorkspace(id, options = {}) {
|
|
67
|
+
const query = options.excludes?.length ? `?excludes=${encodeURIComponent(options.excludes.join(","))}` : "";
|
|
68
|
+
return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/persist${query}`, {});
|
|
69
|
+
}
|
|
70
|
+
/** `POST /v1/sandbox/:id/hydrate` — restores `/workspace` from a raw tar payload. */
|
|
71
|
+
async hydrateWorkspace(id, tar) {
|
|
72
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/hydrate`, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
body: tar,
|
|
75
|
+
headers: { "content-type": "application/octet-stream" }
|
|
76
|
+
}, true);
|
|
77
|
+
}
|
|
78
|
+
/** `POST /v1/sandbox/:id/mount` — mounts an S3-compatible bucket as a local directory. */
|
|
79
|
+
async mountBucket(id, request) {
|
|
80
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/mount`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: JSON.stringify(request),
|
|
83
|
+
headers: { "content-type": "application/json" }
|
|
84
|
+
}, true);
|
|
85
|
+
}
|
|
86
|
+
/** `POST /v1/sandbox/:id/unmount` — unmounts a previously mounted bucket. */
|
|
87
|
+
async unmountBucket(id, mountPath) {
|
|
88
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/unmount`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: JSON.stringify({ mountPath }),
|
|
91
|
+
headers: { "content-type": "application/json" }
|
|
92
|
+
}, true);
|
|
93
|
+
}
|
|
94
|
+
/** `POST /v1/sandbox/:id/session` — creates an execution session, returning its id. */
|
|
95
|
+
async createSession(id, request = {}) {
|
|
96
|
+
const body = {};
|
|
97
|
+
if (request.cwd !== void 0) body.cwd = request.cwd;
|
|
98
|
+
if (request.env !== void 0) body.env = request.env;
|
|
99
|
+
if (request.sessionId !== void 0) body.id = request.sessionId;
|
|
100
|
+
return this.request(`/v1/sandbox/${encodeURIComponent(id)}/session`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: JSON.stringify(body),
|
|
103
|
+
headers: { "content-type": "application/json" }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** `DELETE /v1/sandbox/:id/session/:sessionId` — tears down an execution session. */
|
|
107
|
+
async deleteSession(id, sessionId) {
|
|
108
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/session/${encodeURIComponent(sessionId)}`, { method: "DELETE" }, true);
|
|
109
|
+
}
|
|
61
110
|
/** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */
|
|
62
111
|
async exec(id, request, options) {
|
|
63
112
|
const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {
|
|
@@ -142,6 +191,17 @@ var CloudflareSandboxBridgeClient = class {
|
|
|
142
191
|
if (allowEmpty || response.status === 204) return void 0;
|
|
143
192
|
return response.json();
|
|
144
193
|
}
|
|
194
|
+
async requestBytes(path, init) {
|
|
195
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
196
|
+
...init,
|
|
197
|
+
headers: {
|
|
198
|
+
...this.headers(),
|
|
199
|
+
...init.headers
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
if (!response.ok) throw new CloudflareSandboxBridgeError(response.status, await response.text());
|
|
203
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
204
|
+
}
|
|
145
205
|
};
|
|
146
206
|
function base64ToBytes(value) {
|
|
147
207
|
return new Uint8Array(Buffer.from(value, "base64"));
|
|
@@ -325,6 +385,26 @@ var CloudflareSandbox = class extends _mastra_core_workspace.MastraSandbox {
|
|
|
325
385
|
for (const file of files) await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);
|
|
326
386
|
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
327
387
|
}
|
|
388
|
+
/** Reads a single file under /workspace, returning its raw bytes. */
|
|
389
|
+
async readFile(path$2) {
|
|
390
|
+
const sandboxId = this.requireSandboxId();
|
|
391
|
+
const bytes = await this.client.readFile(sandboxId, resolveWorkspacePath(path$2));
|
|
392
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
393
|
+
return bytes;
|
|
394
|
+
}
|
|
395
|
+
/** Archives /workspace, returning raw tar bytes that can later restore it via hydrateWorkspace. */
|
|
396
|
+
async persistWorkspace(options) {
|
|
397
|
+
const sandboxId = this.requireSandboxId();
|
|
398
|
+
const archive = await this.client.persistWorkspace(sandboxId, options);
|
|
399
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
400
|
+
return archive;
|
|
401
|
+
}
|
|
402
|
+
/** Restores /workspace from a raw tar payload produced by persistWorkspace. */
|
|
403
|
+
async hydrateWorkspace(tar) {
|
|
404
|
+
const sandboxId = this.requireSandboxId();
|
|
405
|
+
await this.client.hydrateWorkspace(sandboxId, tar);
|
|
406
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
407
|
+
}
|
|
328
408
|
getInfo() {
|
|
329
409
|
return {
|
|
330
410
|
id: this.id,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["posix","path","MastraSandbox"],"sources":["../src/bridge-client.ts","../src/sandbox.ts"],"sourcesContent":["export interface CloudflareSandboxBridgeClientOptions {\n baseUrl: string;\n apiToken?: string;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Terminal and streaming events emitted by `POST /v1/sandbox/:id/exec`. */\nexport type CloudflareCommandEvent =\n | { type: 'stdout'; data: Uint8Array }\n | { type: 'stderr'; data: Uint8Array }\n | { type: 'exit'; exitCode: number }\n | { type: 'error'; message: string; code?: string };\n\nexport interface CloudflareExecRequest {\n /** Command and arguments. The bridge applies ANSI-C quoting to each element. */\n argv: string[];\n timeoutMs?: number;\n cwd?: string;\n}\n\nexport class CloudflareSandboxBridgeError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Cloudflare Sandbox Bridge request failed (${status}): ${body || 'empty response'}`);\n this.name = 'CloudflareSandboxBridgeError';\n this.status = status;\n this.body = body;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') end--;\n return url.slice(0, end);\n}\n\n/** Encodes an absolute sandbox path for the `/file/*` route, which omits the leading slash. */\nfunction encodeFilePath(absolutePath: string): string {\n let start = 0;\n while (start < absolutePath.length && absolutePath[start] === '/') start++;\n return absolutePath\n .slice(start)\n .split('/')\n .map(segment => encodeURIComponent(segment))\n .join('/');\n}\n\n/**\n * Client for the Cloudflare Sandbox Bridge Worker.\n *\n * @see https://developers.cloudflare.com/sandbox/bridge/http-api/\n */\nexport class CloudflareSandboxBridgeClient {\n readonly baseUrl: string;\n private readonly apiToken?: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: CloudflareSandboxBridgeClientOptions) {\n this.baseUrl = stripTrailingSlashes(options.baseUrl);\n this.apiToken = options.apiToken;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** `POST /v1/sandbox` */\n async createSandbox(): Promise<string> {\n const created = await this.request<{ id: string }>('/v1/sandbox', { method: 'POST' });\n return created.id;\n }\n\n /** `GET /v1/sandbox/:id/running` */\n async isRunning(id: string): Promise<boolean> {\n const status = await this.request<{ running: boolean }>(`/v1/sandbox/${encodeURIComponent(id)}/running`, {});\n return status.running === true;\n }\n\n /** `DELETE /v1/sandbox/:id` */\n async deleteSandbox(id: string): Promise<void> {\n await this.request(`/v1/sandbox/${encodeURIComponent(id)}`, { method: 'DELETE' }, true);\n }\n\n /** `PUT /v1/sandbox/:id/file/*` — one file per request, raw bytes as the body. */\n async writeFile(id: string, absolutePath: string, content: Uint8Array | string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`,\n {\n method: 'PUT',\n body: content as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */\n async exec(\n id: string,\n request: CloudflareExecRequest,\n options: {\n signal?: AbortSignal;\n onEvent: (event: CloudflareCommandEvent) => void;\n },\n ): Promise<void> {\n const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {\n method: 'POST',\n headers: { ...this.headers(), 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({\n argv: request.argv,\n ...(request.timeoutMs === undefined ? {} : { timeout_ms: request.timeoutMs }),\n ...(request.cwd === undefined ? {} : { cwd: request.cwd }),\n }),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (!response.body) {\n throw new Error('Cloudflare Sandbox Bridge returned an empty command stream');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n buffer += decoder.decode(value, { stream: !done }).replace(/\\r\\n/g, '\\n');\n let boundary = buffer.indexOf('\\n\\n');\n while (boundary !== -1) {\n this.emitBlock(buffer.slice(0, boundary), options.onEvent);\n buffer = buffer.slice(boundary + 2);\n boundary = buffer.indexOf('\\n\\n');\n }\n if (done) break;\n }\n if (buffer.trim()) this.emitBlock(buffer, options.onEvent);\n }\n\n private emitBlock(block: string, onEvent: (event: CloudflareCommandEvent) => void): void {\n let eventName: string | undefined;\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n if (line.startsWith('event:')) eventName = line.slice(6).trim();\n else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''));\n }\n const data = dataLines.join('\\n');\n if (!eventName || !data) return;\n\n switch (eventName) {\n case 'stdout':\n case 'stderr':\n onEvent({ type: eventName, data: base64ToBytes(data) });\n return;\n case 'exit': {\n const parsed = safeJsonParse(data);\n onEvent({ type: 'exit', exitCode: typeof parsed?.exit_code === 'number' ? parsed.exit_code : 0 });\n return;\n }\n case 'error': {\n const parsed = safeJsonParse(data);\n onEvent({\n type: 'error',\n message: typeof parsed?.error === 'string' ? parsed.error : data,\n code: typeof parsed?.code === 'string' ? parsed.code : undefined,\n });\n return;\n }\n default:\n return;\n }\n }\n\n private headers(): Record<string, string> {\n return this.apiToken ? { authorization: `Bearer ${this.apiToken}` } : {};\n }\n\n private async request<T>(path: string, init: RequestInit, allowEmpty = false): Promise<T> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (allowEmpty || response.status === 204) return undefined as T;\n return response.json() as Promise<T>;\n }\n}\n\nfunction base64ToBytes(value: string): Uint8Array {\n return new Uint8Array(Buffer.from(value, 'base64'));\n}\n\nfunction safeJsonParse(value: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(value) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxFileInput,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, assertModesUnsupported } from '@mastra/core/workspace';\nimport { CloudflareSandboxBridgeClient, type CloudflareSandboxBridgeClientOptions } from './bridge-client';\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 300_000;\nconst WORKSPACE_ROOT = '/workspace';\n\ntype InstructionsOption = string | ((options: { defaultInstructions: string }) => string);\ntype BridgeClient = Pick<\n CloudflareSandboxBridgeClient,\n 'createSandbox' | 'isRunning' | 'deleteSandbox' | 'writeFile' | 'exec'\n>;\n\nexport interface CloudflareSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** URL of a deployed Cloudflare Sandbox Bridge Worker. */\n baseUrl: string;\n /** Bearer token matching the Worker's `SANDBOX_API_KEY` secret, when authentication is enabled. */\n apiToken?: string;\n /** Stable Mastra identifier for this sandbox instance. */\n id?: string;\n /** Existing Cloudflare sandbox ID to reconnect to instead of creating a sandbox. */\n sandboxId?: string;\n /** Human-readable name shown in Mastra sandbox metadata. */\n name?: string;\n /** Environment variables applied to every command. */\n env?: Record<string, string>;\n /** Working directory applied to every command. Must be under /workspace. */\n workingDirectory?: string;\n /** Default command timeout in milliseconds. */\n commandTimeout?: number;\n /** Custom instructions returned by getInstructions(). */\n instructions?: InstructionsOption;\n /** Custom fetch implementation, primarily for advanced networking setup and tests. */\n fetch?: CloudflareSandboxBridgeClientOptions['fetch'];\n /** Preconfigured Bridge client, primarily for tests. */\n client?: BridgeClient;\n}\n\n/**\n * Absolute path to the shell used to interpret bare command strings. Absolute so it\n * resolves even when a custom PATH excludes the standard system directories.\n */\nconst SHELL_PATH = '/bin/bash';\n\n/**\n * Builds the argv array sent to the bridge. The bridge applies ANSI-C quoting to\n * every element, so no local escaping is needed. Environment variables are applied\n * with `env`, which keeps each assignment a separate argv element.\n *\n * When no separate arguments are supplied (the shape the built-in Workspace\n * `execute_command` tool uses), `command` is a shell command string — pipes,\n * chaining, quoting, redirection — so it is run through a non-login shell rather\n * than treated as a single executable name. When explicit arguments are given,\n * each element stays a literal argv token.\n */\nfunction buildArgv(command: string, args: string[] | undefined, env: Record<string, string>): string[] {\n const assignments = Object.entries(env).map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid environment variable name: ${key}`);\n return `${key}=${value}`;\n });\n const invocation = args && args.length > 0 ? [command, ...args] : [SHELL_PATH, '-c', command];\n return assignments.length ? ['env', ...assignments, ...invocation] : invocation;\n}\n\n/** Resolves a path inside /workspace, rejecting anything that escapes the workspace root. */\nfunction resolveWorkspacePath(path: string): string {\n const resolved = posix.resolve(WORKSPACE_ROOT, path);\n if (resolved !== WORKSPACE_ROOT && !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {\n throw new Error(`Cloudflare Sandbox files must be written under ${WORKSPACE_ROOT}: ${path}`);\n }\n return resolved;\n}\n\nexport class CloudflareSandbox extends MastraSandbox {\n readonly id: string;\n readonly name: string;\n readonly provider = 'cloudflare-sandbox';\n status: ProviderStatus = 'pending';\n\n private readonly client: BridgeClient;\n private readonly commandTimeout: number;\n private readonly instructions?: InstructionsOption;\n private sandboxId?: string;\n private createdAt = new Date();\n private lastUsedAt?: Date;\n\n constructor(options: CloudflareSandboxOptions) {\n const name = options.name ?? 'Cloudflare Sandbox';\n super({ ...options, name });\n this.id = options.id ?? `cloudflare-sandbox-${randomUUID()}`;\n this.name = name;\n this.sandboxId = options.sandboxId;\n this.commandTimeout = options.commandTimeout ?? DEFAULT_COMMAND_TIMEOUT_MS;\n this.instructions = options.instructions;\n this.client =\n options.client ??\n new CloudflareSandboxBridgeClient({ baseUrl: options.baseUrl, apiToken: options.apiToken, fetch: options.fetch });\n }\n\n async start(): Promise<void> {\n if (this.sandboxId) {\n // The bridge boots the container on demand, so a stopped container is not fatal.\n const running = await this.client.isRunning(this.sandboxId);\n if (!running) {\n this.logger?.debug(`Cloudflare sandbox ${this.sandboxId} is not running yet; it starts on first use`);\n }\n return;\n }\n this.sandboxId = await this.client.createSandbox();\n this.createdAt = new Date();\n }\n\n async stop(): Promise<void> {\n // The bridge exposes create/delete but no suspend operation. Stop detaches this\n // Mastra lifecycle while preserving the remote sandbox for later reconnection.\n }\n\n async destroy(): Promise<void> {\n if (!this.sandboxId) return;\n await this.client.deleteSandbox(this.sandboxId);\n this.sandboxId = undefined;\n }\n\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n const sandboxId = this.requireSandboxId();\n\n const startedAt = Date.now();\n const timeout = options?.timeout ?? this.commandTimeout;\n if (!Number.isFinite(timeout) || timeout <= 0) throw new RangeError('Command timeout must be positive');\n\n const controller = new AbortController();\n let didTimeout = false;\n const timer = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, timeout);\n const signal = options?.abortSignal ? AbortSignal.any([controller.signal, options.abortSignal]) : controller.signal;\n\n // stdout and stderr are separate byte streams, so each needs its own streaming decoder.\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n let stdout = '';\n let stderr = '';\n let exitCode = 1;\n\n const env = Object.fromEntries(\n Object.entries({ ...this.getEnv(), ...options?.env }).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n\n try {\n await this.client.exec(\n sandboxId,\n {\n argv: buildArgv(command, args, env),\n timeoutMs: timeout,\n cwd: options?.cwd ?? this.workingDirectory,\n },\n {\n signal,\n onEvent: event => {\n switch (event.type) {\n case 'stdout': {\n const chunk = stdoutDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stdout += chunk;\n options?.onStdout?.(chunk);\n return;\n }\n case 'stderr': {\n const chunk = stderrDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stderr += chunk;\n options?.onStderr?.(chunk);\n return;\n }\n case 'exit':\n exitCode = event.exitCode;\n return;\n case 'error':\n stderr += event.message;\n options?.onStderr?.(event.message);\n return;\n }\n },\n },\n );\n } catch (error) {\n if (!signal.aborted) throw error;\n } finally {\n clearTimeout(timer);\n }\n\n // Flush each decoder so a trailing truncated multi-byte sequence isn't dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options?.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options?.onStderr?.(stderrTail);\n }\n\n this.lastUsedAt = new Date();\n return {\n command,\n args,\n success: exitCode === 0 && !signal.aborted,\n exitCode,\n stdout,\n stderr,\n executionTimeMs: Date.now() - startedAt,\n timedOut: didTimeout,\n killed: signal.aborted && !didTimeout,\n };\n }\n\n async writeFiles(files: SandboxFileInput[]): Promise<void> {\n assertModesUnsupported(files, 'Cloudflare');\n const sandboxId = this.requireSandboxId();\n // The bridge writes one file per request.\n for (const file of files) {\n await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);\n }\n this.lastUsedAt = new Date();\n }\n\n getInfo(): SandboxInfo {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this.createdAt,\n lastUsedAt: this.lastUsedAt,\n metadata: {\n sandboxId: this.sandboxId,\n bridgeBaseUrl: this.client instanceof CloudflareSandboxBridgeClient ? this.client.baseUrl : undefined,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions =\n 'Commands execute in a remote Cloudflare Sandbox. Read and write persistent project files under /workspace.';\n return typeof this.instructions === 'function'\n ? this.instructions({ defaultInstructions })\n : (this.instructions ?? defaultInstructions);\n }\n\n private requireSandboxId(): string {\n if (!this.sandboxId) throw new Error(`Cloudflare Sandbox ${this.id} has not been started`);\n return this.sandboxId;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["posix","path","MastraSandbox"],"sources":["../src/bridge-client.ts","../src/sandbox.ts"],"sourcesContent":["export interface CloudflareSandboxBridgeClientOptions {\n baseUrl: string;\n apiToken?: string;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Terminal and streaming events emitted by `POST /v1/sandbox/:id/exec`. */\nexport type CloudflareCommandEvent =\n | { type: 'stdout'; data: Uint8Array }\n | { type: 'stderr'; data: Uint8Array }\n | { type: 'exit'; exitCode: number }\n | { type: 'error'; message: string; code?: string };\n\nexport interface CloudflareExecRequest {\n /** Command and arguments. The bridge applies ANSI-C quoting to each element. */\n argv: string[];\n timeoutMs?: number;\n cwd?: string;\n}\n\nexport interface CloudflarePersistWorkspaceOptions {\n /** Relative paths (under /workspace) to exclude from the archive. */\n excludes?: string[];\n}\n\nexport interface CloudflareMountBucketCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n}\n\nexport interface CloudflareMountBucketOptions {\n /** S3-compatible endpoint, e.g. `https://<account>.r2.cloudflarestorage.com`. */\n endpoint?: string;\n /** Mount the bucket read-only. */\n readOnly?: boolean;\n /** Only expose objects under this bucket prefix at the mount point. */\n prefix?: string;\n /** Storage provider hint, e.g. `r2`. */\n provider?: string;\n /** Explicit credentials; omitted when the Worker resolves them from secrets. */\n credentials?: CloudflareMountBucketCredentials;\n}\n\nexport interface CloudflareMountBucketRequest {\n /** Bucket name, e.g. `my-r2-bucket`. */\n bucket: string;\n /** Local filesystem path to mount at, e.g. `/mnt/data`. */\n mountPath: string;\n options?: CloudflareMountBucketOptions;\n}\n\nexport interface CloudflareCreateSessionRequest {\n /** Working directory the session starts in. */\n cwd?: string;\n /** Environment variables seeded into the session. */\n env?: Record<string, string>;\n /** Caller-chosen session id; must match `^[a-zA-Z0-9._-]{1,128}$`. Generated when omitted. */\n sessionId?: string;\n}\n\nexport interface CloudflareSession {\n id: string;\n}\n\nexport class CloudflareSandboxBridgeError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Cloudflare Sandbox Bridge request failed (${status}): ${body || 'empty response'}`);\n this.name = 'CloudflareSandboxBridgeError';\n this.status = status;\n this.body = body;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') end--;\n return url.slice(0, end);\n}\n\n/** Encodes an absolute sandbox path for the `/file/*` route, which omits the leading slash. */\nfunction encodeFilePath(absolutePath: string): string {\n let start = 0;\n while (start < absolutePath.length && absolutePath[start] === '/') start++;\n return absolutePath\n .slice(start)\n .split('/')\n .map(segment => encodeURIComponent(segment))\n .join('/');\n}\n\n/**\n * Client for the Cloudflare Sandbox Bridge Worker.\n *\n * @see https://developers.cloudflare.com/sandbox/bridge/http-api/\n */\nexport class CloudflareSandboxBridgeClient {\n readonly baseUrl: string;\n private readonly apiToken?: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: CloudflareSandboxBridgeClientOptions) {\n this.baseUrl = stripTrailingSlashes(options.baseUrl);\n this.apiToken = options.apiToken;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** `POST /v1/sandbox` */\n async createSandbox(): Promise<string> {\n const created = await this.request<{ id: string }>('/v1/sandbox', { method: 'POST' });\n return created.id;\n }\n\n /** `GET /v1/sandbox/:id/running` */\n async isRunning(id: string): Promise<boolean> {\n const status = await this.request<{ running: boolean }>(`/v1/sandbox/${encodeURIComponent(id)}/running`, {});\n return status.running === true;\n }\n\n /** `DELETE /v1/sandbox/:id` */\n async deleteSandbox(id: string): Promise<void> {\n await this.request(`/v1/sandbox/${encodeURIComponent(id)}`, { method: 'DELETE' }, true);\n }\n\n /** `PUT /v1/sandbox/:id/file/*` — one file per request, raw bytes as the body. */\n async writeFile(id: string, absolutePath: string, content: Uint8Array | string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`,\n {\n method: 'PUT',\n body: content as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `GET /v1/sandbox/:id/file/*` — reads one file, returning its raw bytes. */\n async readFile(id: string, absolutePath: string): Promise<Uint8Array> {\n return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`, {});\n }\n\n /** `GET /v1/sandbox/:id/persist` — archives `/workspace`, returning raw tar bytes. */\n async persistWorkspace(id: string, options: CloudflarePersistWorkspaceOptions = {}): Promise<Uint8Array> {\n const query = options.excludes?.length ? `?excludes=${encodeURIComponent(options.excludes.join(','))}` : '';\n return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/persist${query}`, {});\n }\n\n /** `POST /v1/sandbox/:id/hydrate` — restores `/workspace` from a raw tar payload. */\n async hydrateWorkspace(id: string, tar: Uint8Array): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/hydrate`,\n {\n method: 'POST',\n body: tar as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/mount` — mounts an S3-compatible bucket as a local directory. */\n async mountBucket(id: string, request: CloudflareMountBucketRequest): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/mount`,\n {\n method: 'POST',\n body: JSON.stringify(request),\n headers: { 'content-type': 'application/json' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/unmount` — unmounts a previously mounted bucket. */\n async unmountBucket(id: string, mountPath: string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/unmount`,\n {\n method: 'POST',\n body: JSON.stringify({ mountPath }),\n headers: { 'content-type': 'application/json' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/session` — creates an execution session, returning its id. */\n async createSession(id: string, request: CloudflareCreateSessionRequest = {}): Promise<CloudflareSession> {\n const body: Record<string, unknown> = {};\n if (request.cwd !== undefined) body.cwd = request.cwd;\n if (request.env !== undefined) body.env = request.env;\n if (request.sessionId !== undefined) body.id = request.sessionId;\n return this.request<CloudflareSession>(`/v1/sandbox/${encodeURIComponent(id)}/session`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n });\n }\n\n /** `DELETE /v1/sandbox/:id/session/:sessionId` — tears down an execution session. */\n async deleteSession(id: string, sessionId: string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/session/${encodeURIComponent(sessionId)}`,\n { method: 'DELETE' },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */\n async exec(\n id: string,\n request: CloudflareExecRequest,\n options: {\n signal?: AbortSignal;\n onEvent: (event: CloudflareCommandEvent) => void;\n },\n ): Promise<void> {\n const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {\n method: 'POST',\n headers: { ...this.headers(), 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({\n argv: request.argv,\n ...(request.timeoutMs === undefined ? {} : { timeout_ms: request.timeoutMs }),\n ...(request.cwd === undefined ? {} : { cwd: request.cwd }),\n }),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (!response.body) {\n throw new Error('Cloudflare Sandbox Bridge returned an empty command stream');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n buffer += decoder.decode(value, { stream: !done }).replace(/\\r\\n/g, '\\n');\n let boundary = buffer.indexOf('\\n\\n');\n while (boundary !== -1) {\n this.emitBlock(buffer.slice(0, boundary), options.onEvent);\n buffer = buffer.slice(boundary + 2);\n boundary = buffer.indexOf('\\n\\n');\n }\n if (done) break;\n }\n if (buffer.trim()) this.emitBlock(buffer, options.onEvent);\n }\n\n private emitBlock(block: string, onEvent: (event: CloudflareCommandEvent) => void): void {\n let eventName: string | undefined;\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n if (line.startsWith('event:')) eventName = line.slice(6).trim();\n else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''));\n }\n const data = dataLines.join('\\n');\n if (!eventName || !data) return;\n\n switch (eventName) {\n case 'stdout':\n case 'stderr':\n onEvent({ type: eventName, data: base64ToBytes(data) });\n return;\n case 'exit': {\n const parsed = safeJsonParse(data);\n onEvent({ type: 'exit', exitCode: typeof parsed?.exit_code === 'number' ? parsed.exit_code : 0 });\n return;\n }\n case 'error': {\n const parsed = safeJsonParse(data);\n onEvent({\n type: 'error',\n message: typeof parsed?.error === 'string' ? parsed.error : data,\n code: typeof parsed?.code === 'string' ? parsed.code : undefined,\n });\n return;\n }\n default:\n return;\n }\n }\n\n private headers(): Record<string, string> {\n return this.apiToken ? { authorization: `Bearer ${this.apiToken}` } : {};\n }\n\n private async request<T>(path: string, init: RequestInit, allowEmpty = false): Promise<T> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (allowEmpty || response.status === 204) return undefined as T;\n return response.json() as Promise<T>;\n }\n\n private async requestBytes(path: string, init: RequestInit): Promise<Uint8Array> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n}\n\nfunction base64ToBytes(value: string): Uint8Array {\n return new Uint8Array(Buffer.from(value, 'base64'));\n}\n\nfunction safeJsonParse(value: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(value) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxFileInput,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, assertModesUnsupported } from '@mastra/core/workspace';\nimport {\n CloudflareSandboxBridgeClient,\n type CloudflarePersistWorkspaceOptions,\n type CloudflareSandboxBridgeClientOptions,\n} from './bridge-client';\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 300_000;\nconst WORKSPACE_ROOT = '/workspace';\n\ntype InstructionsOption = string | ((options: { defaultInstructions: string }) => string);\ntype BridgeClient = Pick<\n CloudflareSandboxBridgeClient,\n | 'createSandbox'\n | 'isRunning'\n | 'deleteSandbox'\n | 'writeFile'\n | 'readFile'\n | 'persistWorkspace'\n | 'hydrateWorkspace'\n | 'exec'\n>;\n\nexport interface CloudflareSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** URL of a deployed Cloudflare Sandbox Bridge Worker. */\n baseUrl: string;\n /** Bearer token matching the Worker's `SANDBOX_API_KEY` secret, when authentication is enabled. */\n apiToken?: string;\n /** Stable Mastra identifier for this sandbox instance. */\n id?: string;\n /** Existing Cloudflare sandbox ID to reconnect to instead of creating a sandbox. */\n sandboxId?: string;\n /** Human-readable name shown in Mastra sandbox metadata. */\n name?: string;\n /** Environment variables applied to every command. */\n env?: Record<string, string>;\n /** Working directory applied to every command. Must be under /workspace. */\n workingDirectory?: string;\n /** Default command timeout in milliseconds. */\n commandTimeout?: number;\n /** Custom instructions returned by getInstructions(). */\n instructions?: InstructionsOption;\n /** Custom fetch implementation, primarily for advanced networking setup and tests. */\n fetch?: CloudflareSandboxBridgeClientOptions['fetch'];\n /** Preconfigured Bridge client, primarily for tests. */\n client?: BridgeClient;\n}\n\n/**\n * Absolute path to the shell used to interpret bare command strings. Absolute so it\n * resolves even when a custom PATH excludes the standard system directories.\n */\nconst SHELL_PATH = '/bin/bash';\n\n/**\n * Builds the argv array sent to the bridge. The bridge applies ANSI-C quoting to\n * every element, so no local escaping is needed. Environment variables are applied\n * with `env`, which keeps each assignment a separate argv element.\n *\n * When no separate arguments are supplied (the shape the built-in Workspace\n * `execute_command` tool uses), `command` is a shell command string — pipes,\n * chaining, quoting, redirection — so it is run through a non-login shell rather\n * than treated as a single executable name. When explicit arguments are given,\n * each element stays a literal argv token.\n */\nfunction buildArgv(command: string, args: string[] | undefined, env: Record<string, string>): string[] {\n const assignments = Object.entries(env).map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid environment variable name: ${key}`);\n return `${key}=${value}`;\n });\n const invocation = args && args.length > 0 ? [command, ...args] : [SHELL_PATH, '-c', command];\n return assignments.length ? ['env', ...assignments, ...invocation] : invocation;\n}\n\n/** Resolves a path inside /workspace, rejecting anything that escapes the workspace root. */\nfunction resolveWorkspacePath(path: string): string {\n const resolved = posix.resolve(WORKSPACE_ROOT, path);\n if (resolved !== WORKSPACE_ROOT && !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {\n throw new Error(`Cloudflare Sandbox files must be written under ${WORKSPACE_ROOT}: ${path}`);\n }\n return resolved;\n}\n\nexport class CloudflareSandbox extends MastraSandbox {\n readonly id: string;\n readonly name: string;\n readonly provider = 'cloudflare-sandbox';\n status: ProviderStatus = 'pending';\n\n private readonly client: BridgeClient;\n private readonly commandTimeout: number;\n private readonly instructions?: InstructionsOption;\n private sandboxId?: string;\n private createdAt = new Date();\n private lastUsedAt?: Date;\n\n constructor(options: CloudflareSandboxOptions) {\n const name = options.name ?? 'Cloudflare Sandbox';\n super({ ...options, name });\n this.id = options.id ?? `cloudflare-sandbox-${randomUUID()}`;\n this.name = name;\n this.sandboxId = options.sandboxId;\n this.commandTimeout = options.commandTimeout ?? DEFAULT_COMMAND_TIMEOUT_MS;\n this.instructions = options.instructions;\n this.client =\n options.client ??\n new CloudflareSandboxBridgeClient({ baseUrl: options.baseUrl, apiToken: options.apiToken, fetch: options.fetch });\n }\n\n async start(): Promise<void> {\n if (this.sandboxId) {\n // The bridge boots the container on demand, so a stopped container is not fatal.\n const running = await this.client.isRunning(this.sandboxId);\n if (!running) {\n this.logger?.debug(`Cloudflare sandbox ${this.sandboxId} is not running yet; it starts on first use`);\n }\n return;\n }\n this.sandboxId = await this.client.createSandbox();\n this.createdAt = new Date();\n }\n\n async stop(): Promise<void> {\n // The bridge exposes create/delete but no suspend operation. Stop detaches this\n // Mastra lifecycle while preserving the remote sandbox for later reconnection.\n }\n\n async destroy(): Promise<void> {\n if (!this.sandboxId) return;\n await this.client.deleteSandbox(this.sandboxId);\n this.sandboxId = undefined;\n }\n\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n const sandboxId = this.requireSandboxId();\n\n const startedAt = Date.now();\n const timeout = options?.timeout ?? this.commandTimeout;\n if (!Number.isFinite(timeout) || timeout <= 0) throw new RangeError('Command timeout must be positive');\n\n const controller = new AbortController();\n let didTimeout = false;\n const timer = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, timeout);\n const signal = options?.abortSignal ? AbortSignal.any([controller.signal, options.abortSignal]) : controller.signal;\n\n // stdout and stderr are separate byte streams, so each needs its own streaming decoder.\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n let stdout = '';\n let stderr = '';\n let exitCode = 1;\n\n const env = Object.fromEntries(\n Object.entries({ ...this.getEnv(), ...options?.env }).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n\n try {\n await this.client.exec(\n sandboxId,\n {\n argv: buildArgv(command, args, env),\n timeoutMs: timeout,\n cwd: options?.cwd ?? this.workingDirectory,\n },\n {\n signal,\n onEvent: event => {\n switch (event.type) {\n case 'stdout': {\n const chunk = stdoutDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stdout += chunk;\n options?.onStdout?.(chunk);\n return;\n }\n case 'stderr': {\n const chunk = stderrDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stderr += chunk;\n options?.onStderr?.(chunk);\n return;\n }\n case 'exit':\n exitCode = event.exitCode;\n return;\n case 'error':\n stderr += event.message;\n options?.onStderr?.(event.message);\n return;\n }\n },\n },\n );\n } catch (error) {\n if (!signal.aborted) throw error;\n } finally {\n clearTimeout(timer);\n }\n\n // Flush each decoder so a trailing truncated multi-byte sequence isn't dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options?.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options?.onStderr?.(stderrTail);\n }\n\n this.lastUsedAt = new Date();\n return {\n command,\n args,\n success: exitCode === 0 && !signal.aborted,\n exitCode,\n stdout,\n stderr,\n executionTimeMs: Date.now() - startedAt,\n timedOut: didTimeout,\n killed: signal.aborted && !didTimeout,\n };\n }\n\n async writeFiles(files: SandboxFileInput[]): Promise<void> {\n assertModesUnsupported(files, 'Cloudflare');\n const sandboxId = this.requireSandboxId();\n // The bridge writes one file per request.\n for (const file of files) {\n await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);\n }\n this.lastUsedAt = new Date();\n }\n\n /** Reads a single file under /workspace, returning its raw bytes. */\n async readFile(path: string): Promise<Uint8Array> {\n const sandboxId = this.requireSandboxId();\n const bytes = await this.client.readFile(sandboxId, resolveWorkspacePath(path));\n this.lastUsedAt = new Date();\n return bytes;\n }\n\n /** Archives /workspace, returning raw tar bytes that can later restore it via hydrateWorkspace. */\n async persistWorkspace(options?: CloudflarePersistWorkspaceOptions): Promise<Uint8Array> {\n const sandboxId = this.requireSandboxId();\n const archive = await this.client.persistWorkspace(sandboxId, options);\n this.lastUsedAt = new Date();\n return archive;\n }\n\n /** Restores /workspace from a raw tar payload produced by persistWorkspace. */\n async hydrateWorkspace(tar: Uint8Array): Promise<void> {\n const sandboxId = this.requireSandboxId();\n await this.client.hydrateWorkspace(sandboxId, tar);\n this.lastUsedAt = new Date();\n }\n\n getInfo(): SandboxInfo {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this.createdAt,\n lastUsedAt: this.lastUsedAt,\n metadata: {\n sandboxId: this.sandboxId,\n bridgeBaseUrl: this.client instanceof CloudflareSandboxBridgeClient ? this.client.baseUrl : undefined,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions =\n 'Commands execute in a remote Cloudflare Sandbox. Read and write persistent project files under /workspace.';\n return typeof this.instructions === 'function'\n ? this.instructions({ defaultInstructions })\n : (this.instructions ?? defaultInstructions);\n }\n\n private requireSandboxId(): string {\n if (!this.sandboxId) throw new Error(`Cloudflare Sandbox ${this.id} has not been started`);\n return this.sandboxId;\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { CloudflareSandboxBridgeClient, CloudflareSandboxBridgeError, type CloudflareCommandEvent, type CloudflareExecRequest, type CloudflareSandboxBridgeClientOptions, } from './bridge-client.js';
|
|
1
|
+
export { CloudflareSandboxBridgeClient, CloudflareSandboxBridgeError, type CloudflareCommandEvent, type CloudflareCreateSessionRequest, type CloudflareExecRequest, type CloudflareMountBucketCredentials, type CloudflareMountBucketOptions, type CloudflareMountBucketRequest, type CloudflarePersistWorkspaceOptions, type CloudflareSandboxBridgeClientOptions, type CloudflareSession, } from './bridge-client.js';
|
|
2
2
|
export { CloudflareSandbox, type CloudflareSandboxOptions } from './sandbox.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,oCAAoC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,8BAA8B,EACnC,KAAK,qBAAqB,EAC1B,KAAK,gCAAgC,EACrC,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,iCAAiC,EACtC,KAAK,oCAAoC,EACzC,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,55 @@ var CloudflareSandboxBridgeClient = class {
|
|
|
57
57
|
headers: { "content-type": "application/octet-stream" }
|
|
58
58
|
}, true);
|
|
59
59
|
}
|
|
60
|
+
/** `GET /v1/sandbox/:id/file/*` — reads one file, returning its raw bytes. */
|
|
61
|
+
async readFile(id, absolutePath) {
|
|
62
|
+
return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`, {});
|
|
63
|
+
}
|
|
64
|
+
/** `GET /v1/sandbox/:id/persist` — archives `/workspace`, returning raw tar bytes. */
|
|
65
|
+
async persistWorkspace(id, options = {}) {
|
|
66
|
+
const query = options.excludes?.length ? `?excludes=${encodeURIComponent(options.excludes.join(","))}` : "";
|
|
67
|
+
return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/persist${query}`, {});
|
|
68
|
+
}
|
|
69
|
+
/** `POST /v1/sandbox/:id/hydrate` — restores `/workspace` from a raw tar payload. */
|
|
70
|
+
async hydrateWorkspace(id, tar) {
|
|
71
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/hydrate`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: tar,
|
|
74
|
+
headers: { "content-type": "application/octet-stream" }
|
|
75
|
+
}, true);
|
|
76
|
+
}
|
|
77
|
+
/** `POST /v1/sandbox/:id/mount` — mounts an S3-compatible bucket as a local directory. */
|
|
78
|
+
async mountBucket(id, request) {
|
|
79
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/mount`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
body: JSON.stringify(request),
|
|
82
|
+
headers: { "content-type": "application/json" }
|
|
83
|
+
}, true);
|
|
84
|
+
}
|
|
85
|
+
/** `POST /v1/sandbox/:id/unmount` — unmounts a previously mounted bucket. */
|
|
86
|
+
async unmountBucket(id, mountPath) {
|
|
87
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/unmount`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: JSON.stringify({ mountPath }),
|
|
90
|
+
headers: { "content-type": "application/json" }
|
|
91
|
+
}, true);
|
|
92
|
+
}
|
|
93
|
+
/** `POST /v1/sandbox/:id/session` — creates an execution session, returning its id. */
|
|
94
|
+
async createSession(id, request = {}) {
|
|
95
|
+
const body = {};
|
|
96
|
+
if (request.cwd !== void 0) body.cwd = request.cwd;
|
|
97
|
+
if (request.env !== void 0) body.env = request.env;
|
|
98
|
+
if (request.sessionId !== void 0) body.id = request.sessionId;
|
|
99
|
+
return this.request(`/v1/sandbox/${encodeURIComponent(id)}/session`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: JSON.stringify(body),
|
|
102
|
+
headers: { "content-type": "application/json" }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** `DELETE /v1/sandbox/:id/session/:sessionId` — tears down an execution session. */
|
|
106
|
+
async deleteSession(id, sessionId) {
|
|
107
|
+
await this.request(`/v1/sandbox/${encodeURIComponent(id)}/session/${encodeURIComponent(sessionId)}`, { method: "DELETE" }, true);
|
|
108
|
+
}
|
|
60
109
|
/** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */
|
|
61
110
|
async exec(id, request, options) {
|
|
62
111
|
const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {
|
|
@@ -141,6 +190,17 @@ var CloudflareSandboxBridgeClient = class {
|
|
|
141
190
|
if (allowEmpty || response.status === 204) return void 0;
|
|
142
191
|
return response.json();
|
|
143
192
|
}
|
|
193
|
+
async requestBytes(path, init) {
|
|
194
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
195
|
+
...init,
|
|
196
|
+
headers: {
|
|
197
|
+
...this.headers(),
|
|
198
|
+
...init.headers
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
if (!response.ok) throw new CloudflareSandboxBridgeError(response.status, await response.text());
|
|
202
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
203
|
+
}
|
|
144
204
|
};
|
|
145
205
|
function base64ToBytes(value) {
|
|
146
206
|
return new Uint8Array(Buffer.from(value, "base64"));
|
|
@@ -324,6 +384,26 @@ var CloudflareSandbox = class extends MastraSandbox {
|
|
|
324
384
|
for (const file of files) await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);
|
|
325
385
|
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
326
386
|
}
|
|
387
|
+
/** Reads a single file under /workspace, returning its raw bytes. */
|
|
388
|
+
async readFile(path) {
|
|
389
|
+
const sandboxId = this.requireSandboxId();
|
|
390
|
+
const bytes = await this.client.readFile(sandboxId, resolveWorkspacePath(path));
|
|
391
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
392
|
+
return bytes;
|
|
393
|
+
}
|
|
394
|
+
/** Archives /workspace, returning raw tar bytes that can later restore it via hydrateWorkspace. */
|
|
395
|
+
async persistWorkspace(options) {
|
|
396
|
+
const sandboxId = this.requireSandboxId();
|
|
397
|
+
const archive = await this.client.persistWorkspace(sandboxId, options);
|
|
398
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
399
|
+
return archive;
|
|
400
|
+
}
|
|
401
|
+
/** Restores /workspace from a raw tar payload produced by persistWorkspace. */
|
|
402
|
+
async hydrateWorkspace(tar) {
|
|
403
|
+
const sandboxId = this.requireSandboxId();
|
|
404
|
+
await this.client.hydrateWorkspace(sandboxId, tar);
|
|
405
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
406
|
+
}
|
|
327
407
|
getInfo() {
|
|
328
408
|
return {
|
|
329
409
|
id: this.id,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/bridge-client.ts","../src/sandbox.ts"],"sourcesContent":["export interface CloudflareSandboxBridgeClientOptions {\n baseUrl: string;\n apiToken?: string;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Terminal and streaming events emitted by `POST /v1/sandbox/:id/exec`. */\nexport type CloudflareCommandEvent =\n | { type: 'stdout'; data: Uint8Array }\n | { type: 'stderr'; data: Uint8Array }\n | { type: 'exit'; exitCode: number }\n | { type: 'error'; message: string; code?: string };\n\nexport interface CloudflareExecRequest {\n /** Command and arguments. The bridge applies ANSI-C quoting to each element. */\n argv: string[];\n timeoutMs?: number;\n cwd?: string;\n}\n\nexport class CloudflareSandboxBridgeError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Cloudflare Sandbox Bridge request failed (${status}): ${body || 'empty response'}`);\n this.name = 'CloudflareSandboxBridgeError';\n this.status = status;\n this.body = body;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') end--;\n return url.slice(0, end);\n}\n\n/** Encodes an absolute sandbox path for the `/file/*` route, which omits the leading slash. */\nfunction encodeFilePath(absolutePath: string): string {\n let start = 0;\n while (start < absolutePath.length && absolutePath[start] === '/') start++;\n return absolutePath\n .slice(start)\n .split('/')\n .map(segment => encodeURIComponent(segment))\n .join('/');\n}\n\n/**\n * Client for the Cloudflare Sandbox Bridge Worker.\n *\n * @see https://developers.cloudflare.com/sandbox/bridge/http-api/\n */\nexport class CloudflareSandboxBridgeClient {\n readonly baseUrl: string;\n private readonly apiToken?: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: CloudflareSandboxBridgeClientOptions) {\n this.baseUrl = stripTrailingSlashes(options.baseUrl);\n this.apiToken = options.apiToken;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** `POST /v1/sandbox` */\n async createSandbox(): Promise<string> {\n const created = await this.request<{ id: string }>('/v1/sandbox', { method: 'POST' });\n return created.id;\n }\n\n /** `GET /v1/sandbox/:id/running` */\n async isRunning(id: string): Promise<boolean> {\n const status = await this.request<{ running: boolean }>(`/v1/sandbox/${encodeURIComponent(id)}/running`, {});\n return status.running === true;\n }\n\n /** `DELETE /v1/sandbox/:id` */\n async deleteSandbox(id: string): Promise<void> {\n await this.request(`/v1/sandbox/${encodeURIComponent(id)}`, { method: 'DELETE' }, true);\n }\n\n /** `PUT /v1/sandbox/:id/file/*` — one file per request, raw bytes as the body. */\n async writeFile(id: string, absolutePath: string, content: Uint8Array | string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`,\n {\n method: 'PUT',\n body: content as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */\n async exec(\n id: string,\n request: CloudflareExecRequest,\n options: {\n signal?: AbortSignal;\n onEvent: (event: CloudflareCommandEvent) => void;\n },\n ): Promise<void> {\n const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {\n method: 'POST',\n headers: { ...this.headers(), 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({\n argv: request.argv,\n ...(request.timeoutMs === undefined ? {} : { timeout_ms: request.timeoutMs }),\n ...(request.cwd === undefined ? {} : { cwd: request.cwd }),\n }),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (!response.body) {\n throw new Error('Cloudflare Sandbox Bridge returned an empty command stream');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n buffer += decoder.decode(value, { stream: !done }).replace(/\\r\\n/g, '\\n');\n let boundary = buffer.indexOf('\\n\\n');\n while (boundary !== -1) {\n this.emitBlock(buffer.slice(0, boundary), options.onEvent);\n buffer = buffer.slice(boundary + 2);\n boundary = buffer.indexOf('\\n\\n');\n }\n if (done) break;\n }\n if (buffer.trim()) this.emitBlock(buffer, options.onEvent);\n }\n\n private emitBlock(block: string, onEvent: (event: CloudflareCommandEvent) => void): void {\n let eventName: string | undefined;\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n if (line.startsWith('event:')) eventName = line.slice(6).trim();\n else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''));\n }\n const data = dataLines.join('\\n');\n if (!eventName || !data) return;\n\n switch (eventName) {\n case 'stdout':\n case 'stderr':\n onEvent({ type: eventName, data: base64ToBytes(data) });\n return;\n case 'exit': {\n const parsed = safeJsonParse(data);\n onEvent({ type: 'exit', exitCode: typeof parsed?.exit_code === 'number' ? parsed.exit_code : 0 });\n return;\n }\n case 'error': {\n const parsed = safeJsonParse(data);\n onEvent({\n type: 'error',\n message: typeof parsed?.error === 'string' ? parsed.error : data,\n code: typeof parsed?.code === 'string' ? parsed.code : undefined,\n });\n return;\n }\n default:\n return;\n }\n }\n\n private headers(): Record<string, string> {\n return this.apiToken ? { authorization: `Bearer ${this.apiToken}` } : {};\n }\n\n private async request<T>(path: string, init: RequestInit, allowEmpty = false): Promise<T> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (allowEmpty || response.status === 204) return undefined as T;\n return response.json() as Promise<T>;\n }\n}\n\nfunction base64ToBytes(value: string): Uint8Array {\n return new Uint8Array(Buffer.from(value, 'base64'));\n}\n\nfunction safeJsonParse(value: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(value) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxFileInput,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, assertModesUnsupported } from '@mastra/core/workspace';\nimport { CloudflareSandboxBridgeClient, type CloudflareSandboxBridgeClientOptions } from './bridge-client';\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 300_000;\nconst WORKSPACE_ROOT = '/workspace';\n\ntype InstructionsOption = string | ((options: { defaultInstructions: string }) => string);\ntype BridgeClient = Pick<\n CloudflareSandboxBridgeClient,\n 'createSandbox' | 'isRunning' | 'deleteSandbox' | 'writeFile' | 'exec'\n>;\n\nexport interface CloudflareSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** URL of a deployed Cloudflare Sandbox Bridge Worker. */\n baseUrl: string;\n /** Bearer token matching the Worker's `SANDBOX_API_KEY` secret, when authentication is enabled. */\n apiToken?: string;\n /** Stable Mastra identifier for this sandbox instance. */\n id?: string;\n /** Existing Cloudflare sandbox ID to reconnect to instead of creating a sandbox. */\n sandboxId?: string;\n /** Human-readable name shown in Mastra sandbox metadata. */\n name?: string;\n /** Environment variables applied to every command. */\n env?: Record<string, string>;\n /** Working directory applied to every command. Must be under /workspace. */\n workingDirectory?: string;\n /** Default command timeout in milliseconds. */\n commandTimeout?: number;\n /** Custom instructions returned by getInstructions(). */\n instructions?: InstructionsOption;\n /** Custom fetch implementation, primarily for advanced networking setup and tests. */\n fetch?: CloudflareSandboxBridgeClientOptions['fetch'];\n /** Preconfigured Bridge client, primarily for tests. */\n client?: BridgeClient;\n}\n\n/**\n * Absolute path to the shell used to interpret bare command strings. Absolute so it\n * resolves even when a custom PATH excludes the standard system directories.\n */\nconst SHELL_PATH = '/bin/bash';\n\n/**\n * Builds the argv array sent to the bridge. The bridge applies ANSI-C quoting to\n * every element, so no local escaping is needed. Environment variables are applied\n * with `env`, which keeps each assignment a separate argv element.\n *\n * When no separate arguments are supplied (the shape the built-in Workspace\n * `execute_command` tool uses), `command` is a shell command string — pipes,\n * chaining, quoting, redirection — so it is run through a non-login shell rather\n * than treated as a single executable name. When explicit arguments are given,\n * each element stays a literal argv token.\n */\nfunction buildArgv(command: string, args: string[] | undefined, env: Record<string, string>): string[] {\n const assignments = Object.entries(env).map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid environment variable name: ${key}`);\n return `${key}=${value}`;\n });\n const invocation = args && args.length > 0 ? [command, ...args] : [SHELL_PATH, '-c', command];\n return assignments.length ? ['env', ...assignments, ...invocation] : invocation;\n}\n\n/** Resolves a path inside /workspace, rejecting anything that escapes the workspace root. */\nfunction resolveWorkspacePath(path: string): string {\n const resolved = posix.resolve(WORKSPACE_ROOT, path);\n if (resolved !== WORKSPACE_ROOT && !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {\n throw new Error(`Cloudflare Sandbox files must be written under ${WORKSPACE_ROOT}: ${path}`);\n }\n return resolved;\n}\n\nexport class CloudflareSandbox extends MastraSandbox {\n readonly id: string;\n readonly name: string;\n readonly provider = 'cloudflare-sandbox';\n status: ProviderStatus = 'pending';\n\n private readonly client: BridgeClient;\n private readonly commandTimeout: number;\n private readonly instructions?: InstructionsOption;\n private sandboxId?: string;\n private createdAt = new Date();\n private lastUsedAt?: Date;\n\n constructor(options: CloudflareSandboxOptions) {\n const name = options.name ?? 'Cloudflare Sandbox';\n super({ ...options, name });\n this.id = options.id ?? `cloudflare-sandbox-${randomUUID()}`;\n this.name = name;\n this.sandboxId = options.sandboxId;\n this.commandTimeout = options.commandTimeout ?? DEFAULT_COMMAND_TIMEOUT_MS;\n this.instructions = options.instructions;\n this.client =\n options.client ??\n new CloudflareSandboxBridgeClient({ baseUrl: options.baseUrl, apiToken: options.apiToken, fetch: options.fetch });\n }\n\n async start(): Promise<void> {\n if (this.sandboxId) {\n // The bridge boots the container on demand, so a stopped container is not fatal.\n const running = await this.client.isRunning(this.sandboxId);\n if (!running) {\n this.logger?.debug(`Cloudflare sandbox ${this.sandboxId} is not running yet; it starts on first use`);\n }\n return;\n }\n this.sandboxId = await this.client.createSandbox();\n this.createdAt = new Date();\n }\n\n async stop(): Promise<void> {\n // The bridge exposes create/delete but no suspend operation. Stop detaches this\n // Mastra lifecycle while preserving the remote sandbox for later reconnection.\n }\n\n async destroy(): Promise<void> {\n if (!this.sandboxId) return;\n await this.client.deleteSandbox(this.sandboxId);\n this.sandboxId = undefined;\n }\n\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n const sandboxId = this.requireSandboxId();\n\n const startedAt = Date.now();\n const timeout = options?.timeout ?? this.commandTimeout;\n if (!Number.isFinite(timeout) || timeout <= 0) throw new RangeError('Command timeout must be positive');\n\n const controller = new AbortController();\n let didTimeout = false;\n const timer = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, timeout);\n const signal = options?.abortSignal ? AbortSignal.any([controller.signal, options.abortSignal]) : controller.signal;\n\n // stdout and stderr are separate byte streams, so each needs its own streaming decoder.\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n let stdout = '';\n let stderr = '';\n let exitCode = 1;\n\n const env = Object.fromEntries(\n Object.entries({ ...this.getEnv(), ...options?.env }).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n\n try {\n await this.client.exec(\n sandboxId,\n {\n argv: buildArgv(command, args, env),\n timeoutMs: timeout,\n cwd: options?.cwd ?? this.workingDirectory,\n },\n {\n signal,\n onEvent: event => {\n switch (event.type) {\n case 'stdout': {\n const chunk = stdoutDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stdout += chunk;\n options?.onStdout?.(chunk);\n return;\n }\n case 'stderr': {\n const chunk = stderrDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stderr += chunk;\n options?.onStderr?.(chunk);\n return;\n }\n case 'exit':\n exitCode = event.exitCode;\n return;\n case 'error':\n stderr += event.message;\n options?.onStderr?.(event.message);\n return;\n }\n },\n },\n );\n } catch (error) {\n if (!signal.aborted) throw error;\n } finally {\n clearTimeout(timer);\n }\n\n // Flush each decoder so a trailing truncated multi-byte sequence isn't dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options?.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options?.onStderr?.(stderrTail);\n }\n\n this.lastUsedAt = new Date();\n return {\n command,\n args,\n success: exitCode === 0 && !signal.aborted,\n exitCode,\n stdout,\n stderr,\n executionTimeMs: Date.now() - startedAt,\n timedOut: didTimeout,\n killed: signal.aborted && !didTimeout,\n };\n }\n\n async writeFiles(files: SandboxFileInput[]): Promise<void> {\n assertModesUnsupported(files, 'Cloudflare');\n const sandboxId = this.requireSandboxId();\n // The bridge writes one file per request.\n for (const file of files) {\n await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);\n }\n this.lastUsedAt = new Date();\n }\n\n getInfo(): SandboxInfo {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this.createdAt,\n lastUsedAt: this.lastUsedAt,\n metadata: {\n sandboxId: this.sandboxId,\n bridgeBaseUrl: this.client instanceof CloudflareSandboxBridgeClient ? this.client.baseUrl : undefined,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions =\n 'Commands execute in a remote Cloudflare Sandbox. Read and write persistent project files under /workspace.';\n return typeof this.instructions === 'function'\n ? this.instructions({ defaultInstructions })\n : (this.instructions ?? defaultInstructions);\n }\n\n private requireSandboxId(): string {\n if (!this.sandboxId) throw new Error(`Cloudflare Sandbox ${this.id} has not been started`);\n return this.sandboxId;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/bridge-client.ts","../src/sandbox.ts"],"sourcesContent":["export interface CloudflareSandboxBridgeClientOptions {\n baseUrl: string;\n apiToken?: string;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Terminal and streaming events emitted by `POST /v1/sandbox/:id/exec`. */\nexport type CloudflareCommandEvent =\n | { type: 'stdout'; data: Uint8Array }\n | { type: 'stderr'; data: Uint8Array }\n | { type: 'exit'; exitCode: number }\n | { type: 'error'; message: string; code?: string };\n\nexport interface CloudflareExecRequest {\n /** Command and arguments. The bridge applies ANSI-C quoting to each element. */\n argv: string[];\n timeoutMs?: number;\n cwd?: string;\n}\n\nexport interface CloudflarePersistWorkspaceOptions {\n /** Relative paths (under /workspace) to exclude from the archive. */\n excludes?: string[];\n}\n\nexport interface CloudflareMountBucketCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n}\n\nexport interface CloudflareMountBucketOptions {\n /** S3-compatible endpoint, e.g. `https://<account>.r2.cloudflarestorage.com`. */\n endpoint?: string;\n /** Mount the bucket read-only. */\n readOnly?: boolean;\n /** Only expose objects under this bucket prefix at the mount point. */\n prefix?: string;\n /** Storage provider hint, e.g. `r2`. */\n provider?: string;\n /** Explicit credentials; omitted when the Worker resolves them from secrets. */\n credentials?: CloudflareMountBucketCredentials;\n}\n\nexport interface CloudflareMountBucketRequest {\n /** Bucket name, e.g. `my-r2-bucket`. */\n bucket: string;\n /** Local filesystem path to mount at, e.g. `/mnt/data`. */\n mountPath: string;\n options?: CloudflareMountBucketOptions;\n}\n\nexport interface CloudflareCreateSessionRequest {\n /** Working directory the session starts in. */\n cwd?: string;\n /** Environment variables seeded into the session. */\n env?: Record<string, string>;\n /** Caller-chosen session id; must match `^[a-zA-Z0-9._-]{1,128}$`. Generated when omitted. */\n sessionId?: string;\n}\n\nexport interface CloudflareSession {\n id: string;\n}\n\nexport class CloudflareSandboxBridgeError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Cloudflare Sandbox Bridge request failed (${status}): ${body || 'empty response'}`);\n this.name = 'CloudflareSandboxBridgeError';\n this.status = status;\n this.body = body;\n }\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') end--;\n return url.slice(0, end);\n}\n\n/** Encodes an absolute sandbox path for the `/file/*` route, which omits the leading slash. */\nfunction encodeFilePath(absolutePath: string): string {\n let start = 0;\n while (start < absolutePath.length && absolutePath[start] === '/') start++;\n return absolutePath\n .slice(start)\n .split('/')\n .map(segment => encodeURIComponent(segment))\n .join('/');\n}\n\n/**\n * Client for the Cloudflare Sandbox Bridge Worker.\n *\n * @see https://developers.cloudflare.com/sandbox/bridge/http-api/\n */\nexport class CloudflareSandboxBridgeClient {\n readonly baseUrl: string;\n private readonly apiToken?: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: CloudflareSandboxBridgeClientOptions) {\n this.baseUrl = stripTrailingSlashes(options.baseUrl);\n this.apiToken = options.apiToken;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** `POST /v1/sandbox` */\n async createSandbox(): Promise<string> {\n const created = await this.request<{ id: string }>('/v1/sandbox', { method: 'POST' });\n return created.id;\n }\n\n /** `GET /v1/sandbox/:id/running` */\n async isRunning(id: string): Promise<boolean> {\n const status = await this.request<{ running: boolean }>(`/v1/sandbox/${encodeURIComponent(id)}/running`, {});\n return status.running === true;\n }\n\n /** `DELETE /v1/sandbox/:id` */\n async deleteSandbox(id: string): Promise<void> {\n await this.request(`/v1/sandbox/${encodeURIComponent(id)}`, { method: 'DELETE' }, true);\n }\n\n /** `PUT /v1/sandbox/:id/file/*` — one file per request, raw bytes as the body. */\n async writeFile(id: string, absolutePath: string, content: Uint8Array | string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`,\n {\n method: 'PUT',\n body: content as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `GET /v1/sandbox/:id/file/*` — reads one file, returning its raw bytes. */\n async readFile(id: string, absolutePath: string): Promise<Uint8Array> {\n return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/file/${encodeFilePath(absolutePath)}`, {});\n }\n\n /** `GET /v1/sandbox/:id/persist` — archives `/workspace`, returning raw tar bytes. */\n async persistWorkspace(id: string, options: CloudflarePersistWorkspaceOptions = {}): Promise<Uint8Array> {\n const query = options.excludes?.length ? `?excludes=${encodeURIComponent(options.excludes.join(','))}` : '';\n return this.requestBytes(`/v1/sandbox/${encodeURIComponent(id)}/persist${query}`, {});\n }\n\n /** `POST /v1/sandbox/:id/hydrate` — restores `/workspace` from a raw tar payload. */\n async hydrateWorkspace(id: string, tar: Uint8Array): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/hydrate`,\n {\n method: 'POST',\n body: tar as RequestInit['body'],\n headers: { 'content-type': 'application/octet-stream' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/mount` — mounts an S3-compatible bucket as a local directory. */\n async mountBucket(id: string, request: CloudflareMountBucketRequest): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/mount`,\n {\n method: 'POST',\n body: JSON.stringify(request),\n headers: { 'content-type': 'application/json' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/unmount` — unmounts a previously mounted bucket. */\n async unmountBucket(id: string, mountPath: string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/unmount`,\n {\n method: 'POST',\n body: JSON.stringify({ mountPath }),\n headers: { 'content-type': 'application/json' },\n },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/session` — creates an execution session, returning its id. */\n async createSession(id: string, request: CloudflareCreateSessionRequest = {}): Promise<CloudflareSession> {\n const body: Record<string, unknown> = {};\n if (request.cwd !== undefined) body.cwd = request.cwd;\n if (request.env !== undefined) body.env = request.env;\n if (request.sessionId !== undefined) body.id = request.sessionId;\n return this.request<CloudflareSession>(`/v1/sandbox/${encodeURIComponent(id)}/session`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n });\n }\n\n /** `DELETE /v1/sandbox/:id/session/:sessionId` — tears down an execution session. */\n async deleteSession(id: string, sessionId: string): Promise<void> {\n await this.request(\n `/v1/sandbox/${encodeURIComponent(id)}/session/${encodeURIComponent(sessionId)}`,\n { method: 'DELETE' },\n true,\n );\n }\n\n /** `POST /v1/sandbox/:id/exec` — streams SSE events until `exit` or `error`. */\n async exec(\n id: string,\n request: CloudflareExecRequest,\n options: {\n signal?: AbortSignal;\n onEvent: (event: CloudflareCommandEvent) => void;\n },\n ): Promise<void> {\n const response = await this.fetchImpl(`${this.baseUrl}/v1/sandbox/${encodeURIComponent(id)}/exec`, {\n method: 'POST',\n headers: { ...this.headers(), 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({\n argv: request.argv,\n ...(request.timeoutMs === undefined ? {} : { timeout_ms: request.timeoutMs }),\n ...(request.cwd === undefined ? {} : { cwd: request.cwd }),\n }),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (!response.body) {\n throw new Error('Cloudflare Sandbox Bridge returned an empty command stream');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n buffer += decoder.decode(value, { stream: !done }).replace(/\\r\\n/g, '\\n');\n let boundary = buffer.indexOf('\\n\\n');\n while (boundary !== -1) {\n this.emitBlock(buffer.slice(0, boundary), options.onEvent);\n buffer = buffer.slice(boundary + 2);\n boundary = buffer.indexOf('\\n\\n');\n }\n if (done) break;\n }\n if (buffer.trim()) this.emitBlock(buffer, options.onEvent);\n }\n\n private emitBlock(block: string, onEvent: (event: CloudflareCommandEvent) => void): void {\n let eventName: string | undefined;\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n if (line.startsWith('event:')) eventName = line.slice(6).trim();\n else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''));\n }\n const data = dataLines.join('\\n');\n if (!eventName || !data) return;\n\n switch (eventName) {\n case 'stdout':\n case 'stderr':\n onEvent({ type: eventName, data: base64ToBytes(data) });\n return;\n case 'exit': {\n const parsed = safeJsonParse(data);\n onEvent({ type: 'exit', exitCode: typeof parsed?.exit_code === 'number' ? parsed.exit_code : 0 });\n return;\n }\n case 'error': {\n const parsed = safeJsonParse(data);\n onEvent({\n type: 'error',\n message: typeof parsed?.error === 'string' ? parsed.error : data,\n code: typeof parsed?.code === 'string' ? parsed.code : undefined,\n });\n return;\n }\n default:\n return;\n }\n }\n\n private headers(): Record<string, string> {\n return this.apiToken ? { authorization: `Bearer ${this.apiToken}` } : {};\n }\n\n private async request<T>(path: string, init: RequestInit, allowEmpty = false): Promise<T> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n if (allowEmpty || response.status === 204) return undefined as T;\n return response.json() as Promise<T>;\n }\n\n private async requestBytes(path: string, init: RequestInit): Promise<Uint8Array> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: { ...this.headers(), ...init.headers },\n });\n if (!response.ok) {\n throw new CloudflareSandboxBridgeError(response.status, await response.text());\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n}\n\nfunction base64ToBytes(value: string): Uint8Array {\n return new Uint8Array(Buffer.from(value, 'base64'));\n}\n\nfunction safeJsonParse(value: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(value) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { posix } from 'node:path';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxFileInput,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, assertModesUnsupported } from '@mastra/core/workspace';\nimport {\n CloudflareSandboxBridgeClient,\n type CloudflarePersistWorkspaceOptions,\n type CloudflareSandboxBridgeClientOptions,\n} from './bridge-client';\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 300_000;\nconst WORKSPACE_ROOT = '/workspace';\n\ntype InstructionsOption = string | ((options: { defaultInstructions: string }) => string);\ntype BridgeClient = Pick<\n CloudflareSandboxBridgeClient,\n | 'createSandbox'\n | 'isRunning'\n | 'deleteSandbox'\n | 'writeFile'\n | 'readFile'\n | 'persistWorkspace'\n | 'hydrateWorkspace'\n | 'exec'\n>;\n\nexport interface CloudflareSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** URL of a deployed Cloudflare Sandbox Bridge Worker. */\n baseUrl: string;\n /** Bearer token matching the Worker's `SANDBOX_API_KEY` secret, when authentication is enabled. */\n apiToken?: string;\n /** Stable Mastra identifier for this sandbox instance. */\n id?: string;\n /** Existing Cloudflare sandbox ID to reconnect to instead of creating a sandbox. */\n sandboxId?: string;\n /** Human-readable name shown in Mastra sandbox metadata. */\n name?: string;\n /** Environment variables applied to every command. */\n env?: Record<string, string>;\n /** Working directory applied to every command. Must be under /workspace. */\n workingDirectory?: string;\n /** Default command timeout in milliseconds. */\n commandTimeout?: number;\n /** Custom instructions returned by getInstructions(). */\n instructions?: InstructionsOption;\n /** Custom fetch implementation, primarily for advanced networking setup and tests. */\n fetch?: CloudflareSandboxBridgeClientOptions['fetch'];\n /** Preconfigured Bridge client, primarily for tests. */\n client?: BridgeClient;\n}\n\n/**\n * Absolute path to the shell used to interpret bare command strings. Absolute so it\n * resolves even when a custom PATH excludes the standard system directories.\n */\nconst SHELL_PATH = '/bin/bash';\n\n/**\n * Builds the argv array sent to the bridge. The bridge applies ANSI-C quoting to\n * every element, so no local escaping is needed. Environment variables are applied\n * with `env`, which keeps each assignment a separate argv element.\n *\n * When no separate arguments are supplied (the shape the built-in Workspace\n * `execute_command` tool uses), `command` is a shell command string — pipes,\n * chaining, quoting, redirection — so it is run through a non-login shell rather\n * than treated as a single executable name. When explicit arguments are given,\n * each element stays a literal argv token.\n */\nfunction buildArgv(command: string, args: string[] | undefined, env: Record<string, string>): string[] {\n const assignments = Object.entries(env).map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid environment variable name: ${key}`);\n return `${key}=${value}`;\n });\n const invocation = args && args.length > 0 ? [command, ...args] : [SHELL_PATH, '-c', command];\n return assignments.length ? ['env', ...assignments, ...invocation] : invocation;\n}\n\n/** Resolves a path inside /workspace, rejecting anything that escapes the workspace root. */\nfunction resolveWorkspacePath(path: string): string {\n const resolved = posix.resolve(WORKSPACE_ROOT, path);\n if (resolved !== WORKSPACE_ROOT && !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {\n throw new Error(`Cloudflare Sandbox files must be written under ${WORKSPACE_ROOT}: ${path}`);\n }\n return resolved;\n}\n\nexport class CloudflareSandbox extends MastraSandbox {\n readonly id: string;\n readonly name: string;\n readonly provider = 'cloudflare-sandbox';\n status: ProviderStatus = 'pending';\n\n private readonly client: BridgeClient;\n private readonly commandTimeout: number;\n private readonly instructions?: InstructionsOption;\n private sandboxId?: string;\n private createdAt = new Date();\n private lastUsedAt?: Date;\n\n constructor(options: CloudflareSandboxOptions) {\n const name = options.name ?? 'Cloudflare Sandbox';\n super({ ...options, name });\n this.id = options.id ?? `cloudflare-sandbox-${randomUUID()}`;\n this.name = name;\n this.sandboxId = options.sandboxId;\n this.commandTimeout = options.commandTimeout ?? DEFAULT_COMMAND_TIMEOUT_MS;\n this.instructions = options.instructions;\n this.client =\n options.client ??\n new CloudflareSandboxBridgeClient({ baseUrl: options.baseUrl, apiToken: options.apiToken, fetch: options.fetch });\n }\n\n async start(): Promise<void> {\n if (this.sandboxId) {\n // The bridge boots the container on demand, so a stopped container is not fatal.\n const running = await this.client.isRunning(this.sandboxId);\n if (!running) {\n this.logger?.debug(`Cloudflare sandbox ${this.sandboxId} is not running yet; it starts on first use`);\n }\n return;\n }\n this.sandboxId = await this.client.createSandbox();\n this.createdAt = new Date();\n }\n\n async stop(): Promise<void> {\n // The bridge exposes create/delete but no suspend operation. Stop detaches this\n // Mastra lifecycle while preserving the remote sandbox for later reconnection.\n }\n\n async destroy(): Promise<void> {\n if (!this.sandboxId) return;\n await this.client.deleteSandbox(this.sandboxId);\n this.sandboxId = undefined;\n }\n\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n const sandboxId = this.requireSandboxId();\n\n const startedAt = Date.now();\n const timeout = options?.timeout ?? this.commandTimeout;\n if (!Number.isFinite(timeout) || timeout <= 0) throw new RangeError('Command timeout must be positive');\n\n const controller = new AbortController();\n let didTimeout = false;\n const timer = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, timeout);\n const signal = options?.abortSignal ? AbortSignal.any([controller.signal, options.abortSignal]) : controller.signal;\n\n // stdout and stderr are separate byte streams, so each needs its own streaming decoder.\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n let stdout = '';\n let stderr = '';\n let exitCode = 1;\n\n const env = Object.fromEntries(\n Object.entries({ ...this.getEnv(), ...options?.env }).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n\n try {\n await this.client.exec(\n sandboxId,\n {\n argv: buildArgv(command, args, env),\n timeoutMs: timeout,\n cwd: options?.cwd ?? this.workingDirectory,\n },\n {\n signal,\n onEvent: event => {\n switch (event.type) {\n case 'stdout': {\n const chunk = stdoutDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stdout += chunk;\n options?.onStdout?.(chunk);\n return;\n }\n case 'stderr': {\n const chunk = stderrDecoder.decode(event.data, { stream: true });\n if (!chunk) return;\n stderr += chunk;\n options?.onStderr?.(chunk);\n return;\n }\n case 'exit':\n exitCode = event.exitCode;\n return;\n case 'error':\n stderr += event.message;\n options?.onStderr?.(event.message);\n return;\n }\n },\n },\n );\n } catch (error) {\n if (!signal.aborted) throw error;\n } finally {\n clearTimeout(timer);\n }\n\n // Flush each decoder so a trailing truncated multi-byte sequence isn't dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options?.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options?.onStderr?.(stderrTail);\n }\n\n this.lastUsedAt = new Date();\n return {\n command,\n args,\n success: exitCode === 0 && !signal.aborted,\n exitCode,\n stdout,\n stderr,\n executionTimeMs: Date.now() - startedAt,\n timedOut: didTimeout,\n killed: signal.aborted && !didTimeout,\n };\n }\n\n async writeFiles(files: SandboxFileInput[]): Promise<void> {\n assertModesUnsupported(files, 'Cloudflare');\n const sandboxId = this.requireSandboxId();\n // The bridge writes one file per request.\n for (const file of files) {\n await this.client.writeFile(sandboxId, resolveWorkspacePath(file.path), file.content);\n }\n this.lastUsedAt = new Date();\n }\n\n /** Reads a single file under /workspace, returning its raw bytes. */\n async readFile(path: string): Promise<Uint8Array> {\n const sandboxId = this.requireSandboxId();\n const bytes = await this.client.readFile(sandboxId, resolveWorkspacePath(path));\n this.lastUsedAt = new Date();\n return bytes;\n }\n\n /** Archives /workspace, returning raw tar bytes that can later restore it via hydrateWorkspace. */\n async persistWorkspace(options?: CloudflarePersistWorkspaceOptions): Promise<Uint8Array> {\n const sandboxId = this.requireSandboxId();\n const archive = await this.client.persistWorkspace(sandboxId, options);\n this.lastUsedAt = new Date();\n return archive;\n }\n\n /** Restores /workspace from a raw tar payload produced by persistWorkspace. */\n async hydrateWorkspace(tar: Uint8Array): Promise<void> {\n const sandboxId = this.requireSandboxId();\n await this.client.hydrateWorkspace(sandboxId, tar);\n this.lastUsedAt = new Date();\n }\n\n getInfo(): SandboxInfo {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this.createdAt,\n lastUsedAt: this.lastUsedAt,\n metadata: {\n sandboxId: this.sandboxId,\n bridgeBaseUrl: this.client instanceof CloudflareSandboxBridgeClient ? this.client.baseUrl : undefined,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions =\n 'Commands execute in a remote Cloudflare Sandbox. Read and write persistent project files under /workspace.';\n return typeof this.instructions === 'function'\n ? this.instructions({ defaultInstructions })\n : (this.instructions ?? defaultInstructions);\n }\n\n private requireSandboxId(): string {\n if (!this.sandboxId) throw new Error(`Cloudflare Sandbox ${this.id} has not been started`);\n return this.sandboxId;\n }\n}\n"],"mappings":"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"}
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CommandResult, ExecuteCommandOptions, MastraSandboxOptions, ProviderStatus, SandboxFileInput, SandboxInfo } from '@mastra/core/workspace';
|
|
2
2
|
import { MastraSandbox } from '@mastra/core/workspace';
|
|
3
|
-
import { CloudflareSandboxBridgeClient, type CloudflareSandboxBridgeClientOptions } from './bridge-client.js';
|
|
3
|
+
import { CloudflareSandboxBridgeClient, type CloudflarePersistWorkspaceOptions, type CloudflareSandboxBridgeClientOptions } from './bridge-client.js';
|
|
4
4
|
type InstructionsOption = string | ((options: {
|
|
5
5
|
defaultInstructions: string;
|
|
6
6
|
}) => string);
|
|
7
|
-
type BridgeClient = Pick<CloudflareSandboxBridgeClient, 'createSandbox' | 'isRunning' | 'deleteSandbox' | 'writeFile' | 'exec'>;
|
|
7
|
+
type BridgeClient = Pick<CloudflareSandboxBridgeClient, 'createSandbox' | 'isRunning' | 'deleteSandbox' | 'writeFile' | 'readFile' | 'persistWorkspace' | 'hydrateWorkspace' | 'exec'>;
|
|
8
8
|
export interface CloudflareSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {
|
|
9
9
|
/** URL of a deployed Cloudflare Sandbox Bridge Worker. */
|
|
10
10
|
baseUrl: string;
|
|
@@ -46,6 +46,12 @@ export declare class CloudflareSandbox extends MastraSandbox {
|
|
|
46
46
|
destroy(): Promise<void>;
|
|
47
47
|
executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult>;
|
|
48
48
|
writeFiles(files: SandboxFileInput[]): Promise<void>;
|
|
49
|
+
/** Reads a single file under /workspace, returning its raw bytes. */
|
|
50
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
51
|
+
/** Archives /workspace, returning raw tar bytes that can later restore it via hydrateWorkspace. */
|
|
52
|
+
persistWorkspace(options?: CloudflarePersistWorkspaceOptions): Promise<Uint8Array>;
|
|
53
|
+
/** Restores /workspace from a raw tar payload produced by persistWorkspace. */
|
|
54
|
+
hydrateWorkspace(tar: Uint8Array): Promise<void>;
|
|
49
55
|
getInfo(): SandboxInfo;
|
|
50
56
|
getInstructions(): string;
|
|
51
57
|
private requireSandboxId;
|
package/dist/sandbox.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAA0B,MAAM,wBAAwB,CAAC;AAC/E,OAAO,
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAA0B,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EACL,6BAA6B,EAC7B,KAAK,iCAAiC,EACtC,KAAK,oCAAoC,EAC1C,MAAM,iBAAiB,CAAC;AAKzB,KAAK,kBAAkB,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE;IAAE,mBAAmB,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAC,CAAC;AAC1F,KAAK,YAAY,GAAG,IAAI,CACtB,6BAA6B,EAC3B,eAAe,GACf,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,kBAAkB,GAClB,kBAAkB,GAClB,MAAM,CACT,CAAC;AAEF,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC;IACvF,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,sFAAsF;IACtF,KAAK,CAAC,EAAE,oCAAoC,CAAC,OAAO,CAAC,CAAC;IACtD,wDAAwD;IACxD,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAqCD,qBAAa,iBAAkB,SAAQ,aAAa;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,wBAAwB;IACzC,MAAM,EAAE,cAAc,CAAa;IAEnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAqB;IACnD,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAO;IAE1B,YAAY,OAAO,EAAE,wBAAwB,EAW5C;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAW3B;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1B;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAI7B;IAEK,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CA+F9G;IAEK,UAAU,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzD;IAED,qEAAqE;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAKhD;IAED,mGAAmG;IAC7F,gBAAgB,CAAC,OAAO,CAAC,EAAE,iCAAiC,GAAG,OAAO,CAAC,UAAU,CAAC,CAKvF;IAED,+EAA+E;IACzE,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrD;IAED,OAAO,IAAI,WAAW,CAarB;IAED,eAAe,IAAI,MAAM,CAMxB;IAED,OAAO,CAAC,gBAAgB;CAIzB"}
|
|
@@ -34,6 +34,16 @@ export interface FakeBridge {
|
|
|
34
34
|
execs: FakeExecRequest[];
|
|
35
35
|
files: Map<string, string>;
|
|
36
36
|
sandboxes: Set<string>;
|
|
37
|
+
/** Excludes query recorded per `GET /persist` call. */
|
|
38
|
+
persists: (string | null)[];
|
|
39
|
+
/** Raw tar payloads received by `POST /hydrate`. */
|
|
40
|
+
hydrations: Uint8Array[];
|
|
41
|
+
/** Bodies received by `POST /mount`. */
|
|
42
|
+
mounts: unknown[];
|
|
43
|
+
/** Bodies received by `POST /unmount`. */
|
|
44
|
+
unmounts: unknown[];
|
|
45
|
+
/** Live session ids created via `POST /session`. */
|
|
46
|
+
sessions: Set<string>;
|
|
37
47
|
/** Overrides the default `echo`-only behaviour. */
|
|
38
48
|
onExec?: (request: FakeExecRequest) => FakeExecResult;
|
|
39
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-bridge.d.ts","sourceRoot":"","sources":["../../src/testing/fake-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,cAAc,CAAC;CACvD;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"fake-bridge.d.ts","sourceRoot":"","sources":["../../src/testing/fake-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,uDAAuD;IACvD,QAAQ,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5B,oDAAoD;IACpD,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,wCAAwC;IACxC,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,cAAc,CAAC;CACvD;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,UAAU,CAuIlG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/cloudflare-sandbox",
|
|
3
|
-
"version": "0.4.0-alpha.
|
|
3
|
+
"version": "0.4.0-alpha.2",
|
|
4
4
|
"description": "Cloudflare Sandbox provider for Mastra workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"tsdown": "0.22.9",
|
|
29
29
|
"typescript": "^7.0.2",
|
|
30
30
|
"vitest": "4.1.10",
|
|
31
|
+
"@internal/lint": "0.0.132",
|
|
31
32
|
"@internal/types-builder": "0.0.107",
|
|
32
|
-
"@mastra/core": "1.67.0-alpha.3",
|
|
33
33
|
"@internal/workspace-test-utils": "0.0.76",
|
|
34
|
-
"@
|
|
34
|
+
"@mastra/core": "1.67.0-alpha.6"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@mastra/core": ">=1.67.0-0 <2.0.0-0"
|