@cloudflare/sandbox 0.2.0 → 0.2.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/CHANGELOG.md +12 -0
- package/Dockerfile +31 -7
- package/README.md +226 -2
- package/container_src/bun.lock +122 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/index.ts +305 -10
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +448 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +83 -0
- package/dist/{chunk-YVZ3K26G.js → chunk-CUHYLCMT.js} +9 -21
- package/dist/chunk-CUHYLCMT.js.map +1 -0
- package/dist/chunk-EGC5IYXA.js +108 -0
- package/dist/chunk-EGC5IYXA.js.map +1 -0
- package/dist/chunk-FKBV7CZS.js +113 -0
- package/dist/chunk-FKBV7CZS.js.map +1 -0
- package/dist/chunk-LALY4SFU.js +129 -0
- package/dist/chunk-LALY4SFU.js.map +1 -0
- package/dist/{chunk-6THNBO4S.js → chunk-S5FFBU4Y.js} +1 -1
- package/dist/{chunk-6THNBO4S.js.map → chunk-S5FFBU4Y.js.map} +1 -1
- package/dist/chunk-VTKZL632.js +237 -0
- package/dist/chunk-VTKZL632.js.map +1 -0
- package/dist/{chunk-ZJN2PQOS.js → chunk-ZMPO44U4.js} +171 -72
- package/dist/chunk-ZMPO44U4.js.map +1 -0
- package/dist/{client-BXYlxy-j.d.ts → client-bzEV222a.d.ts} +52 -4
- package/dist/client.d.ts +2 -1
- package/dist/client.js +1 -1
- package/dist/errors.d.ts +95 -0
- package/dist/errors.js +27 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +33 -3
- package/dist/interpreter-types.d.ts +259 -0
- package/dist/interpreter-types.js +9 -0
- package/dist/interpreter-types.js.map +1 -0
- package/dist/interpreter.d.ts +33 -0
- package/dist/interpreter.js +8 -0
- package/dist/interpreter.js.map +1 -0
- package/dist/jupyter-client.d.ts +4 -0
- package/dist/jupyter-client.js +9 -0
- package/dist/jupyter-client.js.map +1 -0
- package/dist/request-handler.d.ts +2 -1
- package/dist/request-handler.js +8 -3
- package/dist/sandbox.d.ts +2 -1
- package/dist/sandbox.js +8 -3
- package/dist/types.d.ts +8 -0
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/src/client.ts +37 -54
- package/src/errors.ts +218 -0
- package/src/index.ts +44 -10
- package/src/interpreter-types.ts +383 -0
- package/src/interpreter.ts +150 -0
- package/src/jupyter-client.ts +349 -0
- package/src/sandbox.ts +281 -153
- package/src/types.ts +15 -0
- package/dist/chunk-YVZ3K26G.js.map +0 -1
- package/dist/chunk-ZJN2PQOS.js.map +0 -1
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HttpClient
|
|
3
|
-
} from "./chunk-YVZ3K26G.js";
|
|
4
1
|
import {
|
|
5
2
|
SecurityError,
|
|
6
3
|
logSecurityEvent,
|
|
7
4
|
sanitizeSandboxId,
|
|
8
5
|
validatePort
|
|
9
6
|
} from "./chunk-6UAWTJ5S.js";
|
|
7
|
+
import {
|
|
8
|
+
parseSSEStream
|
|
9
|
+
} from "./chunk-NNGBXDMY.js";
|
|
10
10
|
import {
|
|
11
11
|
ProcessNotFoundError,
|
|
12
12
|
SandboxError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-S5FFBU4Y.js";
|
|
14
|
+
import {
|
|
15
|
+
CodeInterpreter
|
|
16
|
+
} from "./chunk-FKBV7CZS.js";
|
|
17
|
+
import {
|
|
18
|
+
JupyterClient
|
|
19
|
+
} from "./chunk-VTKZL632.js";
|
|
14
20
|
|
|
15
21
|
// src/sandbox.ts
|
|
16
22
|
import { Container, getContainer } from "@cloudflare/containers";
|
|
@@ -127,18 +133,17 @@ var Sandbox = class extends Container {
|
|
|
127
133
|
// Sleep the sandbox if no requests are made in this timeframe
|
|
128
134
|
client;
|
|
129
135
|
sandboxName = null;
|
|
136
|
+
codeInterpreter;
|
|
130
137
|
constructor(ctx, env) {
|
|
131
138
|
super(ctx, env);
|
|
132
|
-
this.client = new
|
|
139
|
+
this.client = new JupyterClient({
|
|
133
140
|
onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {
|
|
134
141
|
console.log(
|
|
135
142
|
`[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`
|
|
136
143
|
);
|
|
137
144
|
},
|
|
138
145
|
onCommandStart: (command) => {
|
|
139
|
-
console.log(
|
|
140
|
-
`[Container] Command started: ${command}`
|
|
141
|
-
);
|
|
146
|
+
console.log(`[Container] Command started: ${command}`);
|
|
142
147
|
},
|
|
143
148
|
onError: (error, _command) => {
|
|
144
149
|
console.error(`[Container] Command error: ${error}`);
|
|
@@ -150,6 +155,7 @@ var Sandbox = class extends Container {
|
|
|
150
155
|
// Control plane port
|
|
151
156
|
stub: this
|
|
152
157
|
});
|
|
158
|
+
this.codeInterpreter = new CodeInterpreter(this);
|
|
153
159
|
this.ctx.blockConcurrencyWhile(async () => {
|
|
154
160
|
this.sandboxName = await this.ctx.storage.get("sandboxName") || null;
|
|
155
161
|
});
|
|
@@ -210,18 +216,24 @@ var Sandbox = class extends Container {
|
|
|
210
216
|
}
|
|
211
217
|
let result;
|
|
212
218
|
if (options?.stream && options?.onOutput) {
|
|
213
|
-
result = await this.executeWithStreaming(
|
|
214
|
-
} else {
|
|
215
|
-
const response = await this.client.execute(
|
|
219
|
+
result = await this.executeWithStreaming(
|
|
216
220
|
command,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
env: options?.env
|
|
221
|
-
}
|
|
221
|
+
options,
|
|
222
|
+
startTime,
|
|
223
|
+
timestamp
|
|
222
224
|
);
|
|
225
|
+
} else {
|
|
226
|
+
const response = await this.client.execute(command, {
|
|
227
|
+
sessionId: options?.sessionId,
|
|
228
|
+
cwd: options?.cwd,
|
|
229
|
+
env: options?.env
|
|
230
|
+
});
|
|
223
231
|
const duration = Date.now() - startTime;
|
|
224
|
-
result = this.mapExecuteResponseToExecResult(
|
|
232
|
+
result = this.mapExecuteResponseToExecResult(
|
|
233
|
+
response,
|
|
234
|
+
duration,
|
|
235
|
+
options?.sessionId
|
|
236
|
+
);
|
|
225
237
|
}
|
|
226
238
|
if (options?.onComplete) {
|
|
227
239
|
options.onComplete(result);
|
|
@@ -242,9 +254,13 @@ var Sandbox = class extends Container {
|
|
|
242
254
|
let stdout = "";
|
|
243
255
|
let stderr = "";
|
|
244
256
|
try {
|
|
245
|
-
const stream = await this.client.executeCommandStream(
|
|
246
|
-
|
|
247
|
-
|
|
257
|
+
const stream = await this.client.executeCommandStream(
|
|
258
|
+
command,
|
|
259
|
+
options.sessionId
|
|
260
|
+
);
|
|
261
|
+
for await (const event of parseSSEStream(
|
|
262
|
+
stream
|
|
263
|
+
)) {
|
|
248
264
|
if (options.signal?.aborted) {
|
|
249
265
|
throw new Error("Operation was aborted");
|
|
250
266
|
}
|
|
@@ -441,7 +457,10 @@ var Sandbox = class extends Container {
|
|
|
441
457
|
if (options?.signal?.aborted) {
|
|
442
458
|
throw new Error("Operation was aborted");
|
|
443
459
|
}
|
|
444
|
-
const stream = await this.client.executeCommandStream(
|
|
460
|
+
const stream = await this.client.executeCommandStream(
|
|
461
|
+
command,
|
|
462
|
+
options?.sessionId
|
|
463
|
+
);
|
|
445
464
|
return stream;
|
|
446
465
|
}
|
|
447
466
|
async streamProcessLogs(processId, options) {
|
|
@@ -475,9 +494,15 @@ var Sandbox = class extends Container {
|
|
|
475
494
|
async exposePort(port, options) {
|
|
476
495
|
await this.client.exposePort(port, options?.name);
|
|
477
496
|
if (!this.sandboxName) {
|
|
478
|
-
throw new Error(
|
|
497
|
+
throw new Error(
|
|
498
|
+
"Sandbox name not available. Ensure sandbox is accessed through getSandbox()"
|
|
499
|
+
);
|
|
479
500
|
}
|
|
480
|
-
const url = this.constructPreviewUrl(
|
|
501
|
+
const url = this.constructPreviewUrl(
|
|
502
|
+
port,
|
|
503
|
+
this.sandboxName,
|
|
504
|
+
options.hostname
|
|
505
|
+
);
|
|
481
506
|
return {
|
|
482
507
|
url,
|
|
483
508
|
port,
|
|
@@ -486,20 +511,32 @@ var Sandbox = class extends Container {
|
|
|
486
511
|
}
|
|
487
512
|
async unexposePort(port) {
|
|
488
513
|
if (!validatePort(port)) {
|
|
489
|
-
logSecurityEvent(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
514
|
+
logSecurityEvent(
|
|
515
|
+
"INVALID_PORT_UNEXPOSE",
|
|
516
|
+
{
|
|
517
|
+
port
|
|
518
|
+
},
|
|
519
|
+
"high"
|
|
520
|
+
);
|
|
521
|
+
throw new SecurityError(
|
|
522
|
+
`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
|
|
523
|
+
);
|
|
493
524
|
}
|
|
494
525
|
await this.client.unexposePort(port);
|
|
495
|
-
logSecurityEvent(
|
|
496
|
-
|
|
497
|
-
|
|
526
|
+
logSecurityEvent(
|
|
527
|
+
"PORT_UNEXPOSED",
|
|
528
|
+
{
|
|
529
|
+
port
|
|
530
|
+
},
|
|
531
|
+
"low"
|
|
532
|
+
);
|
|
498
533
|
}
|
|
499
534
|
async getExposedPorts(hostname) {
|
|
500
535
|
const response = await this.client.getExposedPorts();
|
|
501
536
|
if (!this.sandboxName) {
|
|
502
|
-
throw new Error(
|
|
537
|
+
throw new Error(
|
|
538
|
+
"Sandbox name not available. Ensure sandbox is accessed through getSandbox()"
|
|
539
|
+
);
|
|
503
540
|
}
|
|
504
541
|
return response.ports.map((port) => ({
|
|
505
542
|
url: this.constructPreviewUrl(port.port, this.sandboxName, hostname),
|
|
@@ -510,23 +547,33 @@ var Sandbox = class extends Container {
|
|
|
510
547
|
}
|
|
511
548
|
constructPreviewUrl(port, sandboxId, hostname) {
|
|
512
549
|
if (!validatePort(port)) {
|
|
513
|
-
logSecurityEvent(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
550
|
+
logSecurityEvent(
|
|
551
|
+
"INVALID_PORT_REJECTED",
|
|
552
|
+
{
|
|
553
|
+
port,
|
|
554
|
+
sandboxId,
|
|
555
|
+
hostname
|
|
556
|
+
},
|
|
557
|
+
"high"
|
|
558
|
+
);
|
|
559
|
+
throw new SecurityError(
|
|
560
|
+
`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
|
|
561
|
+
);
|
|
519
562
|
}
|
|
520
563
|
let sanitizedSandboxId;
|
|
521
564
|
try {
|
|
522
565
|
sanitizedSandboxId = sanitizeSandboxId(sandboxId);
|
|
523
566
|
} catch (error) {
|
|
524
|
-
logSecurityEvent(
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
567
|
+
logSecurityEvent(
|
|
568
|
+
"INVALID_SANDBOX_ID_REJECTED",
|
|
569
|
+
{
|
|
570
|
+
sandboxId,
|
|
571
|
+
port,
|
|
572
|
+
hostname,
|
|
573
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
574
|
+
},
|
|
575
|
+
"high"
|
|
576
|
+
);
|
|
530
577
|
throw error;
|
|
531
578
|
}
|
|
532
579
|
const isLocalhost = isLocalhostPattern(hostname);
|
|
@@ -538,22 +585,32 @@ var Sandbox = class extends Container {
|
|
|
538
585
|
const subdomainHost = `${port}-${sanitizedSandboxId}.${host}`;
|
|
539
586
|
baseUrl.hostname = subdomainHost;
|
|
540
587
|
const finalUrl = baseUrl.toString();
|
|
541
|
-
logSecurityEvent(
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
588
|
+
logSecurityEvent(
|
|
589
|
+
"PREVIEW_URL_CONSTRUCTED",
|
|
590
|
+
{
|
|
591
|
+
port,
|
|
592
|
+
sandboxId: sanitizedSandboxId,
|
|
593
|
+
hostname,
|
|
594
|
+
resultUrl: finalUrl,
|
|
595
|
+
environment: "localhost"
|
|
596
|
+
},
|
|
597
|
+
"low"
|
|
598
|
+
);
|
|
548
599
|
return finalUrl;
|
|
549
600
|
} catch (error) {
|
|
550
|
-
logSecurityEvent(
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
601
|
+
logSecurityEvent(
|
|
602
|
+
"URL_CONSTRUCTION_FAILED",
|
|
603
|
+
{
|
|
604
|
+
port,
|
|
605
|
+
sandboxId: sanitizedSandboxId,
|
|
606
|
+
hostname,
|
|
607
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
608
|
+
},
|
|
609
|
+
"high"
|
|
610
|
+
);
|
|
611
|
+
throw new SecurityError(
|
|
612
|
+
`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
613
|
+
);
|
|
557
614
|
}
|
|
558
615
|
}
|
|
559
616
|
try {
|
|
@@ -562,24 +619,66 @@ var Sandbox = class extends Container {
|
|
|
562
619
|
const subdomainHost = `${port}-${sanitizedSandboxId}.${hostname}`;
|
|
563
620
|
baseUrl.hostname = subdomainHost;
|
|
564
621
|
const finalUrl = baseUrl.toString();
|
|
565
|
-
logSecurityEvent(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
622
|
+
logSecurityEvent(
|
|
623
|
+
"PREVIEW_URL_CONSTRUCTED",
|
|
624
|
+
{
|
|
625
|
+
port,
|
|
626
|
+
sandboxId: sanitizedSandboxId,
|
|
627
|
+
hostname,
|
|
628
|
+
resultUrl: finalUrl,
|
|
629
|
+
environment: "production"
|
|
630
|
+
},
|
|
631
|
+
"low"
|
|
632
|
+
);
|
|
572
633
|
return finalUrl;
|
|
573
634
|
} catch (error) {
|
|
574
|
-
logSecurityEvent(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
635
|
+
logSecurityEvent(
|
|
636
|
+
"URL_CONSTRUCTION_FAILED",
|
|
637
|
+
{
|
|
638
|
+
port,
|
|
639
|
+
sandboxId: sanitizedSandboxId,
|
|
640
|
+
hostname,
|
|
641
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
642
|
+
},
|
|
643
|
+
"high"
|
|
644
|
+
);
|
|
645
|
+
throw new SecurityError(
|
|
646
|
+
`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
647
|
+
);
|
|
581
648
|
}
|
|
582
649
|
}
|
|
650
|
+
// Code Interpreter Methods
|
|
651
|
+
/**
|
|
652
|
+
* Create a new code execution context
|
|
653
|
+
*/
|
|
654
|
+
async createCodeContext(options) {
|
|
655
|
+
return this.codeInterpreter.createCodeContext(options);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Run code with streaming callbacks
|
|
659
|
+
*/
|
|
660
|
+
async runCode(code, options) {
|
|
661
|
+
const execution = await this.codeInterpreter.runCode(code, options);
|
|
662
|
+
return execution.toJSON();
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Run code and return a streaming response
|
|
666
|
+
*/
|
|
667
|
+
async runCodeStream(code, options) {
|
|
668
|
+
return this.codeInterpreter.runCodeStream(code, options);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* List all code contexts
|
|
672
|
+
*/
|
|
673
|
+
async listCodeContexts() {
|
|
674
|
+
return this.codeInterpreter.listCodeContexts();
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Delete a code context
|
|
678
|
+
*/
|
|
679
|
+
async deleteCodeContext(contextId) {
|
|
680
|
+
return this.codeInterpreter.deleteCodeContext(contextId);
|
|
681
|
+
}
|
|
583
682
|
};
|
|
584
683
|
|
|
585
684
|
export {
|
|
@@ -588,4 +687,4 @@ export {
|
|
|
588
687
|
proxyToSandbox,
|
|
589
688
|
isLocalhostPattern
|
|
590
689
|
};
|
|
591
|
-
//# sourceMappingURL=chunk-
|
|
690
|
+
//# sourceMappingURL=chunk-ZMPO44U4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sandbox.ts","../src/request-handler.ts"],"sourcesContent":["import { Container, getContainer } from \"@cloudflare/containers\";\nimport { CodeInterpreter } from \"./interpreter\";\nimport type {\n CodeContext,\n CreateContextOptions,\n ExecutionResult,\n RunCodeOptions,\n} from \"./interpreter-types\";\nimport { JupyterClient } from \"./jupyter-client\";\nimport { isLocalhostPattern } from \"./request-handler\";\nimport {\n logSecurityEvent,\n SecurityError,\n sanitizeSandboxId,\n validatePort,\n} from \"./security\";\nimport { parseSSEStream } from \"./sse-parser\";\nimport type {\n ExecOptions,\n ExecResult,\n ISandbox,\n Process,\n ProcessOptions,\n ProcessStatus,\n StreamOptions,\n} from \"./types\";\nimport { ProcessNotFoundError, SandboxError } from \"./types\";\n\nexport function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string) {\n const stub = getContainer(ns, id);\n\n // Store the name on first access\n stub.setSandboxName?.(id);\n\n return stub;\n}\n\nexport class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {\n defaultPort = 3000; // Default port for the container's Bun server\n sleepAfter = \"3m\"; // Sleep the sandbox if no requests are made in this timeframe\n client: JupyterClient;\n private sandboxName: string | null = null;\n private codeInterpreter: CodeInterpreter;\n\n constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env);\n this.client = new JupyterClient({\n onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {\n console.log(\n `[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`\n );\n },\n onCommandStart: (command) => {\n console.log(`[Container] Command started: ${command}`);\n },\n onError: (error, _command) => {\n console.error(`[Container] Command error: ${error}`);\n },\n onOutput: (stream, data, _command) => {\n console.log(`[Container] [${stream}] ${data}`);\n },\n port: 3000, // Control plane port\n stub: this,\n });\n\n // Initialize code interpreter\n this.codeInterpreter = new CodeInterpreter(this);\n\n // Load the sandbox name from storage on initialization\n this.ctx.blockConcurrencyWhile(async () => {\n this.sandboxName =\n (await this.ctx.storage.get<string>(\"sandboxName\")) || null;\n });\n }\n\n // RPC method to set the sandbox name\n async setSandboxName(name: string): Promise<void> {\n if (!this.sandboxName) {\n this.sandboxName = name;\n await this.ctx.storage.put(\"sandboxName\", name);\n console.log(`[Sandbox] Stored sandbox name via RPC: ${name}`);\n }\n }\n\n // RPC method to set environment variables\n async setEnvVars(envVars: Record<string, string>): Promise<void> {\n this.envVars = { ...this.envVars, ...envVars };\n console.log(`[Sandbox] Updated environment variables`);\n }\n\n override onStart() {\n console.log(\"Sandbox successfully started\");\n }\n\n override onStop() {\n console.log(\"Sandbox successfully shut down\");\n if (this.client) {\n this.client.clearSession();\n }\n }\n\n override onError(error: unknown) {\n console.log(\"Sandbox error:\", error);\n }\n\n // Override fetch to route internal container requests to appropriate ports\n override async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Capture and store the sandbox name from the header if present\n if (!this.sandboxName && request.headers.has(\"X-Sandbox-Name\")) {\n const name = request.headers.get(\"X-Sandbox-Name\")!;\n this.sandboxName = name;\n await this.ctx.storage.put(\"sandboxName\", name);\n console.log(`[Sandbox] Stored sandbox name: ${this.sandboxName}`);\n }\n\n // Determine which port to route to\n const port = this.determinePort(url);\n\n // Route to the appropriate port\n return await this.containerFetch(request, port);\n }\n\n private determinePort(url: URL): number {\n // Extract port from proxy requests (e.g., /proxy/8080/*)\n const proxyMatch = url.pathname.match(/^\\/proxy\\/(\\d+)/);\n if (proxyMatch) {\n return parseInt(proxyMatch[1]);\n }\n\n // All other requests go to control plane on port 3000\n // This includes /api/* endpoints and any other control requests\n return 3000;\n }\n\n // Enhanced exec method - always returns ExecResult with optional streaming\n // This replaces the old exec method to match ISandbox interface\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n const startTime = Date.now();\n const timestamp = new Date().toISOString();\n\n // Handle timeout\n let timeoutId: NodeJS.Timeout | undefined;\n\n try {\n // Handle cancellation\n if (options?.signal?.aborted) {\n throw new Error(\"Operation was aborted\");\n }\n\n let result: ExecResult;\n\n if (options?.stream && options?.onOutput) {\n // Streaming with callbacks - we need to collect the final result\n result = await this.executeWithStreaming(\n command,\n options,\n startTime,\n timestamp\n );\n } else {\n // Regular execution\n const response = await this.client.execute(command, {\n sessionId: options?.sessionId,\n cwd: options?.cwd,\n env: options?.env,\n });\n\n const duration = Date.now() - startTime;\n result = this.mapExecuteResponseToExecResult(\n response,\n duration,\n options?.sessionId\n );\n }\n\n // Call completion callback if provided\n if (options?.onComplete) {\n options.onComplete(result);\n }\n\n return result;\n } catch (error) {\n if (options?.onError && error instanceof Error) {\n options.onError(error);\n }\n throw error;\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n private async executeWithStreaming(\n command: string,\n options: ExecOptions,\n startTime: number,\n timestamp: string\n ): Promise<ExecResult> {\n let stdout = \"\";\n let stderr = \"\";\n\n try {\n const stream = await this.client.executeCommandStream(\n command,\n options.sessionId\n );\n\n for await (const event of parseSSEStream<import(\"./types\").ExecEvent>(\n stream\n )) {\n // Check for cancellation\n if (options.signal?.aborted) {\n throw new Error(\"Operation was aborted\");\n }\n\n switch (event.type) {\n case \"stdout\":\n case \"stderr\":\n if (event.data) {\n // Update accumulated output\n if (event.type === \"stdout\") stdout += event.data;\n if (event.type === \"stderr\") stderr += event.data;\n\n // Call user's callback\n if (options.onOutput) {\n options.onOutput(event.type, event.data);\n }\n }\n break;\n\n case \"complete\": {\n // Use result from complete event if available\n const duration = Date.now() - startTime;\n return (\n event.result || {\n success: event.exitCode === 0,\n exitCode: event.exitCode || 0,\n stdout,\n stderr,\n command,\n duration,\n timestamp,\n sessionId: options.sessionId,\n }\n );\n }\n\n case \"error\":\n throw new Error(event.error || \"Command execution failed\");\n }\n }\n\n // If we get here without a complete event, something went wrong\n throw new Error(\"Stream ended without completion event\");\n } catch (error) {\n if (options.signal?.aborted) {\n throw new Error(\"Operation was aborted\");\n }\n throw error;\n }\n }\n\n private mapExecuteResponseToExecResult(\n response: import(\"./client\").ExecuteResponse,\n duration: number,\n sessionId?: string\n ): ExecResult {\n return {\n success: response.success,\n exitCode: response.exitCode,\n stdout: response.stdout,\n stderr: response.stderr,\n command: response.command,\n duration,\n timestamp: response.timestamp,\n sessionId,\n };\n }\n\n // Background process management\n async startProcess(\n command: string,\n options?: ProcessOptions\n ): Promise<Process> {\n // Use the new HttpClient method to start the process\n try {\n const response = await this.client.startProcess(command, {\n processId: options?.processId,\n sessionId: options?.sessionId,\n timeout: options?.timeout,\n env: options?.env,\n cwd: options?.cwd,\n encoding: options?.encoding,\n autoCleanup: options?.autoCleanup,\n });\n\n const process = response.process;\n const processObj: Process = {\n id: process.id,\n pid: process.pid,\n command: process.command,\n status: process.status as ProcessStatus,\n startTime: new Date(process.startTime),\n endTime: undefined,\n exitCode: undefined,\n sessionId: process.sessionId,\n\n async kill(): Promise<void> {\n throw new Error(\"Method will be replaced\");\n },\n async getStatus(): Promise<ProcessStatus> {\n throw new Error(\"Method will be replaced\");\n },\n async getLogs(): Promise<{ stdout: string; stderr: string }> {\n throw new Error(\"Method will be replaced\");\n },\n };\n\n // Bind context properly\n processObj.kill = async (signal?: string) => {\n await this.killProcess(process.id, signal);\n };\n\n processObj.getStatus = async () => {\n const current = await this.getProcess(process.id);\n return current?.status || \"error\";\n };\n\n processObj.getLogs = async () => {\n const logs = await this.getProcessLogs(process.id);\n return { stdout: logs.stdout, stderr: logs.stderr };\n };\n\n // Call onStart callback if provided\n if (options?.onStart) {\n options.onStart(processObj);\n }\n\n return processObj;\n } catch (error) {\n if (options?.onError && error instanceof Error) {\n options.onError(error);\n }\n\n throw error;\n }\n }\n\n async listProcesses(): Promise<Process[]> {\n const response = await this.client.listProcesses();\n\n return response.processes.map((processData) => ({\n id: processData.id,\n pid: processData.pid,\n command: processData.command,\n status: processData.status,\n startTime: new Date(processData.startTime),\n endTime: processData.endTime ? new Date(processData.endTime) : undefined,\n exitCode: processData.exitCode,\n sessionId: processData.sessionId,\n\n kill: async (signal?: string) => {\n await this.killProcess(processData.id, signal);\n },\n\n getStatus: async () => {\n const current = await this.getProcess(processData.id);\n return current?.status || \"error\";\n },\n\n getLogs: async () => {\n const logs = await this.getProcessLogs(processData.id);\n return { stdout: logs.stdout, stderr: logs.stderr };\n },\n }));\n }\n\n async getProcess(id: string): Promise<Process | null> {\n const response = await this.client.getProcess(id);\n if (!response.process) {\n return null;\n }\n\n const processData = response.process;\n return {\n id: processData.id,\n pid: processData.pid,\n command: processData.command,\n status: processData.status,\n startTime: new Date(processData.startTime),\n endTime: processData.endTime ? new Date(processData.endTime) : undefined,\n exitCode: processData.exitCode,\n sessionId: processData.sessionId,\n\n kill: async (signal?: string) => {\n await this.killProcess(processData.id, signal);\n },\n\n getStatus: async () => {\n const current = await this.getProcess(processData.id);\n return current?.status || \"error\";\n },\n\n getLogs: async () => {\n const logs = await this.getProcessLogs(processData.id);\n return { stdout: logs.stdout, stderr: logs.stderr };\n },\n };\n }\n\n async killProcess(id: string, _signal?: string): Promise<void> {\n try {\n // Note: signal parameter is not currently supported by the HttpClient implementation\n await this.client.killProcess(id);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"Process not found\")\n ) {\n throw new ProcessNotFoundError(id);\n }\n throw new SandboxError(\n `Failed to kill process ${id}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n \"KILL_PROCESS_FAILED\"\n );\n }\n }\n\n async killAllProcesses(): Promise<number> {\n const response = await this.client.killAllProcesses();\n return response.killedCount;\n }\n\n async cleanupCompletedProcesses(): Promise<number> {\n // For now, this would need to be implemented as a container endpoint\n // as we no longer maintain local process storage\n // We'll return 0 as a placeholder until the container endpoint is added\n return 0;\n }\n\n async getProcessLogs(\n id: string\n ): Promise<{ stdout: string; stderr: string }> {\n try {\n const response = await this.client.getProcessLogs(id);\n return {\n stdout: response.stdout,\n stderr: response.stderr,\n };\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"Process not found\")\n ) {\n throw new ProcessNotFoundError(id);\n }\n throw error;\n }\n }\n\n // Streaming methods - return ReadableStream for RPC compatibility\n async execStream(\n command: string,\n options?: StreamOptions\n ): Promise<ReadableStream<Uint8Array>> {\n // Check for cancellation\n if (options?.signal?.aborted) {\n throw new Error(\"Operation was aborted\");\n }\n\n // Get the stream from HttpClient (need to add this method)\n const stream = await this.client.executeCommandStream(\n command,\n options?.sessionId\n );\n\n // Return the ReadableStream directly - can be converted to AsyncIterable by consumers\n return stream;\n }\n\n async streamProcessLogs(\n processId: string,\n options?: { signal?: AbortSignal }\n ): Promise<ReadableStream<Uint8Array>> {\n // Check for cancellation\n if (options?.signal?.aborted) {\n throw new Error(\"Operation was aborted\");\n }\n\n // Get the stream from HttpClient\n const stream = await this.client.streamProcessLogs(processId);\n\n // Return the ReadableStream directly - can be converted to AsyncIterable by consumers\n return stream;\n }\n\n async gitCheckout(\n repoUrl: string,\n options: { branch?: string; targetDir?: string }\n ) {\n return this.client.gitCheckout(repoUrl, options.branch, options.targetDir);\n }\n\n async mkdir(path: string, options: { recursive?: boolean } = {}) {\n return this.client.mkdir(path, options.recursive);\n }\n\n async writeFile(\n path: string,\n content: string,\n options: { encoding?: string } = {}\n ) {\n return this.client.writeFile(path, content, options.encoding);\n }\n\n async deleteFile(path: string) {\n return this.client.deleteFile(path);\n }\n\n async renameFile(oldPath: string, newPath: string) {\n return this.client.renameFile(oldPath, newPath);\n }\n\n async moveFile(sourcePath: string, destinationPath: string) {\n return this.client.moveFile(sourcePath, destinationPath);\n }\n\n async readFile(path: string, options: { encoding?: string } = {}) {\n return this.client.readFile(path, options.encoding);\n }\n\n async exposePort(port: number, options: { name?: string; hostname: string }) {\n await this.client.exposePort(port, options?.name);\n\n // We need the sandbox name to construct preview URLs\n if (!this.sandboxName) {\n throw new Error(\n \"Sandbox name not available. Ensure sandbox is accessed through getSandbox()\"\n );\n }\n\n const url = this.constructPreviewUrl(\n port,\n this.sandboxName,\n options.hostname\n );\n\n return {\n url,\n port,\n name: options?.name,\n };\n }\n\n async unexposePort(port: number) {\n if (!validatePort(port)) {\n logSecurityEvent(\n \"INVALID_PORT_UNEXPOSE\",\n {\n port,\n },\n \"high\"\n );\n throw new SecurityError(\n `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`\n );\n }\n\n await this.client.unexposePort(port);\n\n logSecurityEvent(\n \"PORT_UNEXPOSED\",\n {\n port,\n },\n \"low\"\n );\n }\n\n async getExposedPorts(hostname: string) {\n const response = await this.client.getExposedPorts();\n\n // We need the sandbox name to construct preview URLs\n if (!this.sandboxName) {\n throw new Error(\n \"Sandbox name not available. Ensure sandbox is accessed through getSandbox()\"\n );\n }\n\n return response.ports.map((port) => ({\n url: this.constructPreviewUrl(port.port, this.sandboxName!, hostname),\n port: port.port,\n name: port.name,\n exposedAt: port.exposedAt,\n }));\n }\n\n private constructPreviewUrl(\n port: number,\n sandboxId: string,\n hostname: string\n ): string {\n if (!validatePort(port)) {\n logSecurityEvent(\n \"INVALID_PORT_REJECTED\",\n {\n port,\n sandboxId,\n hostname,\n },\n \"high\"\n );\n throw new SecurityError(\n `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`\n );\n }\n\n let sanitizedSandboxId: string;\n try {\n sanitizedSandboxId = sanitizeSandboxId(sandboxId);\n } catch (error) {\n logSecurityEvent(\n \"INVALID_SANDBOX_ID_REJECTED\",\n {\n sandboxId,\n port,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw error;\n }\n\n const isLocalhost = isLocalhostPattern(hostname);\n\n if (isLocalhost) {\n // Unified subdomain approach for localhost (RFC 6761)\n const [host, portStr] = hostname.split(\":\");\n const mainPort = portStr || \"80\";\n\n // Use URL constructor for safe URL building\n try {\n const baseUrl = new URL(`http://${host}:${mainPort}`);\n // Construct subdomain safely\n const subdomainHost = `${port}-${sanitizedSandboxId}.${host}`;\n baseUrl.hostname = subdomainHost;\n\n const finalUrl = baseUrl.toString();\n\n logSecurityEvent(\n \"PREVIEW_URL_CONSTRUCTED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n resultUrl: finalUrl,\n environment: \"localhost\",\n },\n \"low\"\n );\n\n return finalUrl;\n } catch (error) {\n logSecurityEvent(\n \"URL_CONSTRUCTION_FAILED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw new SecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`\n );\n }\n }\n\n // Production subdomain logic - enforce HTTPS\n try {\n // Always use HTTPS for production (non-localhost)\n const protocol = \"https\";\n const baseUrl = new URL(`${protocol}://${hostname}`);\n\n // Construct subdomain safely\n const subdomainHost = `${port}-${sanitizedSandboxId}.${hostname}`;\n baseUrl.hostname = subdomainHost;\n\n const finalUrl = baseUrl.toString();\n\n logSecurityEvent(\n \"PREVIEW_URL_CONSTRUCTED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n resultUrl: finalUrl,\n environment: \"production\",\n },\n \"low\"\n );\n\n return finalUrl;\n } catch (error) {\n logSecurityEvent(\n \"URL_CONSTRUCTION_FAILED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw new SecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`\n );\n }\n }\n\n // Code Interpreter Methods\n\n /**\n * Create a new code execution context\n */\n async createCodeContext(\n options?: CreateContextOptions\n ): Promise<CodeContext> {\n return this.codeInterpreter.createCodeContext(options);\n }\n\n /**\n * Run code with streaming callbacks\n */\n async runCode(\n code: string,\n options?: RunCodeOptions\n ): Promise<ExecutionResult> {\n const execution = await this.codeInterpreter.runCode(code, options);\n // Convert to plain object for RPC serialization\n return execution.toJSON();\n }\n\n /**\n * Run code and return a streaming response\n */\n async runCodeStream(\n code: string,\n options?: RunCodeOptions\n ): Promise<ReadableStream> {\n return this.codeInterpreter.runCodeStream(code, options);\n }\n\n /**\n * List all code contexts\n */\n async listCodeContexts(): Promise<CodeContext[]> {\n return this.codeInterpreter.listCodeContexts();\n }\n\n /**\n * Delete a code context\n */\n async deleteCodeContext(contextId: string): Promise<void> {\n return this.codeInterpreter.deleteCodeContext(contextId);\n }\n}\n","import { getSandbox, type Sandbox } from \"./sandbox\";\nimport {\n logSecurityEvent,\n sanitizeSandboxId,\n validatePort\n} from \"./security\";\n\nexport interface SandboxEnv {\n Sandbox: DurableObjectNamespace<Sandbox>;\n}\n\nexport interface RouteInfo {\n port: number;\n sandboxId: string;\n path: string;\n}\n\nexport async function proxyToSandbox<E extends SandboxEnv>(\n request: Request,\n env: E\n): Promise<Response | null> {\n try {\n const url = new URL(request.url);\n const routeInfo = extractSandboxRoute(url);\n\n if (!routeInfo) {\n return null; // Not a request to an exposed container port\n }\n\n const { sandboxId, port, path } = routeInfo;\n const sandbox = getSandbox(env.Sandbox, sandboxId);\n\n // Build proxy request with proper headers\n let proxyUrl: string;\n\n // Route based on the target port\n if (port !== 3000) {\n // Route directly to user's service on the specified port\n proxyUrl = `http://localhost:${port}${path}${url.search}`;\n } else {\n // Port 3000 is our control plane - route normally\n proxyUrl = `http://localhost:3000${path}${url.search}`;\n }\n\n const proxyRequest = new Request(proxyUrl, {\n method: request.method,\n headers: {\n ...Object.fromEntries(request.headers),\n 'X-Original-URL': request.url,\n 'X-Forwarded-Host': url.hostname,\n 'X-Forwarded-Proto': url.protocol.replace(':', ''),\n 'X-Sandbox-Name': sandboxId, // Pass the friendly name\n },\n body: request.body,\n });\n\n return sandbox.containerFetch(proxyRequest, port);\n } catch (error) {\n console.error('[Sandbox] Proxy routing error:', error);\n return new Response('Proxy routing error', { status: 500 });\n }\n}\n\nfunction extractSandboxRoute(url: URL): RouteInfo | null {\n // Parse subdomain pattern: port-sandboxId.domain\n const subdomainMatch = url.hostname.match(/^(\\d{4,5})-([^.-][^.]*[^.-]|[^.-])\\.(.+)$/);\n\n if (!subdomainMatch) {\n // Log malformed subdomain attempts\n if (url.hostname.includes('-') && url.hostname.includes('.')) {\n logSecurityEvent('MALFORMED_SUBDOMAIN_ATTEMPT', {\n hostname: url.hostname,\n url: url.toString()\n }, 'medium');\n }\n return null;\n }\n\n const portStr = subdomainMatch[1];\n const sandboxId = subdomainMatch[2];\n const domain = subdomainMatch[3];\n\n const port = parseInt(portStr, 10);\n if (!validatePort(port)) {\n logSecurityEvent('INVALID_PORT_IN_SUBDOMAIN', {\n port,\n portStr,\n sandboxId,\n hostname: url.hostname,\n url: url.toString()\n }, 'high');\n return null;\n }\n\n let sanitizedSandboxId: string;\n try {\n sanitizedSandboxId = sanitizeSandboxId(sandboxId);\n } catch (error) {\n logSecurityEvent('INVALID_SANDBOX_ID_IN_SUBDOMAIN', {\n sandboxId,\n port,\n hostname: url.hostname,\n url: url.toString(),\n error: error instanceof Error ? error.message : 'Unknown error'\n }, 'high');\n return null;\n }\n\n // DNS subdomain length limit is 63 characters\n if (sandboxId.length > 63) {\n logSecurityEvent('SANDBOX_ID_LENGTH_VIOLATION', {\n sandboxId,\n length: sandboxId.length,\n port,\n hostname: url.hostname\n }, 'medium');\n return null;\n }\n\n logSecurityEvent('SANDBOX_ROUTE_EXTRACTED', {\n port,\n sandboxId: sanitizedSandboxId,\n domain,\n path: url.pathname || \"/\",\n hostname: url.hostname\n }, 'low');\n\n return {\n port,\n sandboxId: sanitizedSandboxId,\n path: url.pathname || \"/\",\n };\n}\n\nexport function isLocalhostPattern(hostname: string): boolean {\n const hostPart = hostname.split(\":\")[0];\n return (\n hostPart === \"localhost\" ||\n hostPart === \"127.0.0.1\" ||\n hostPart === \"::1\" ||\n hostPart === \"[::1]\" ||\n hostPart === \"0.0.0.0\"\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
import { Container } from '@cloudflare/containers';
|
|
2
|
+
import { CreateContextOptions, CodeContext, OutputMessage, Result, ExecutionError, RunCodeOptions, ExecutionResult } from './interpreter-types.js';
|
|
2
3
|
import { ISandbox, ExecOptions, ExecResult, ProcessOptions, Process, StreamOptions, BaseExecOptions, StartProcessResponse, ListProcessesResponse, GetProcessResponse, GetProcessLogsResponse } from './types.js';
|
|
3
4
|
|
|
5
|
+
interface ExecutionCallbacks {
|
|
6
|
+
onStdout?: (output: OutputMessage) => void | Promise<void>;
|
|
7
|
+
onStderr?: (output: OutputMessage) => void | Promise<void>;
|
|
8
|
+
onResult?: (result: Result) => void | Promise<void>;
|
|
9
|
+
onError?: (error: ExecutionError) => void | Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
declare class JupyterClient extends HttpClient {
|
|
12
|
+
private readonly maxRetries;
|
|
13
|
+
private readonly retryDelayMs;
|
|
14
|
+
createCodeContext(options?: CreateContextOptions): Promise<CodeContext>;
|
|
15
|
+
runCodeStream(contextId: string | undefined, code: string, language: string | undefined, callbacks: ExecutionCallbacks): Promise<void>;
|
|
16
|
+
private readLines;
|
|
17
|
+
private parseExecutionResult;
|
|
18
|
+
listCodeContexts(): Promise<CodeContext[]>;
|
|
19
|
+
deleteCodeContext(contextId: string): Promise<void>;
|
|
20
|
+
doFetch(path: string, options?: RequestInit): Promise<Response>;
|
|
21
|
+
/**
|
|
22
|
+
* Execute an operation with automatic retry for transient errors
|
|
23
|
+
*/
|
|
24
|
+
private executeWithRetry;
|
|
25
|
+
/**
|
|
26
|
+
* Check if an error is retryable
|
|
27
|
+
*/
|
|
28
|
+
private isRetryableError;
|
|
29
|
+
}
|
|
30
|
+
|
|
4
31
|
declare function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string): DurableObjectStub<Sandbox<unknown>>;
|
|
5
32
|
declare class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
|
|
6
33
|
defaultPort: number;
|
|
7
34
|
sleepAfter: string;
|
|
8
|
-
client:
|
|
35
|
+
client: JupyterClient;
|
|
9
36
|
private sandboxName;
|
|
37
|
+
private codeInterpreter;
|
|
10
38
|
constructor(ctx: DurableObjectState, env: Env);
|
|
11
39
|
setSandboxName(name: string): Promise<void>;
|
|
12
40
|
setEnvVars(envVars: Record<string, string>): Promise<void>;
|
|
@@ -64,6 +92,26 @@ declare class Sandbox<Env = unknown> extends Container<Env> implements ISandbox
|
|
|
64
92
|
exposedAt: string;
|
|
65
93
|
}[]>;
|
|
66
94
|
private constructPreviewUrl;
|
|
95
|
+
/**
|
|
96
|
+
* Create a new code execution context
|
|
97
|
+
*/
|
|
98
|
+
createCodeContext(options?: CreateContextOptions): Promise<CodeContext>;
|
|
99
|
+
/**
|
|
100
|
+
* Run code with streaming callbacks
|
|
101
|
+
*/
|
|
102
|
+
runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult>;
|
|
103
|
+
/**
|
|
104
|
+
* Run code and return a streaming response
|
|
105
|
+
*/
|
|
106
|
+
runCodeStream(code: string, options?: RunCodeOptions): Promise<ReadableStream>;
|
|
107
|
+
/**
|
|
108
|
+
* List all code contexts
|
|
109
|
+
*/
|
|
110
|
+
listCodeContexts(): Promise<CodeContext[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Delete a code context
|
|
113
|
+
*/
|
|
114
|
+
deleteCodeContext(contextId: string): Promise<void>;
|
|
67
115
|
}
|
|
68
116
|
|
|
69
117
|
interface ExecuteResponse {
|
|
@@ -166,8 +214,8 @@ declare class HttpClient {
|
|
|
166
214
|
private options;
|
|
167
215
|
private sessionId;
|
|
168
216
|
constructor(options?: HttpClientOptions);
|
|
169
|
-
|
|
170
|
-
execute(command: string, options: Pick<BaseExecOptions,
|
|
217
|
+
protected doFetch(path: string, options?: RequestInit): Promise<Response>;
|
|
218
|
+
execute(command: string, options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">): Promise<ExecuteResponse>;
|
|
171
219
|
executeCommandStream(command: string, sessionId?: string): Promise<ReadableStream<Uint8Array>>;
|
|
172
220
|
gitCheckout(repoUrl: string, branch?: string, targetDir?: string, sessionId?: string): Promise<GitCheckoutResponse>;
|
|
173
221
|
mkdir(path: string, recursive?: boolean, sessionId?: string): Promise<MkdirResponse>;
|
|
@@ -208,4 +256,4 @@ declare class HttpClient {
|
|
|
208
256
|
streamProcessLogs(processId: string): Promise<ReadableStream<Uint8Array>>;
|
|
209
257
|
}
|
|
210
258
|
|
|
211
|
-
export { type DeleteFileResponse as D, type ExecuteResponse as E, type GitCheckoutResponse as G, HttpClient as H, type MkdirResponse as M, type ReadFileResponse as R, Sandbox as S, type WriteFileResponse as W, type MoveFileResponse as a, type RenameFileResponse as b, getSandbox as g };
|
|
259
|
+
export { type DeleteFileResponse as D, type ExecuteResponse as E, type GitCheckoutResponse as G, HttpClient as H, JupyterClient as J, type MkdirResponse as M, type ReadFileResponse as R, Sandbox as S, type WriteFileResponse as W, type MoveFileResponse as a, type RenameFileResponse as b, type ExecutionCallbacks as c, getSandbox as g };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { D as DeleteFileResponse, E as ExecuteResponse, G as GitCheckoutResponse, H as HttpClient, M as MkdirResponse, a as MoveFileResponse, R as ReadFileResponse, b as RenameFileResponse, W as WriteFileResponse } from './client-
|
|
1
|
+
export { D as DeleteFileResponse, E as ExecuteResponse, G as GitCheckoutResponse, H as HttpClient, M as MkdirResponse, a as MoveFileResponse, R as ReadFileResponse, b as RenameFileResponse, W as WriteFileResponse } from './client-bzEV222a.js';
|
|
2
2
|
import './types.js';
|
|
3
3
|
import '@cloudflare/containers';
|
|
4
|
+
import './interpreter-types.js';
|
package/dist/client.js
CHANGED
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard error response from the sandbox API
|
|
3
|
+
*/
|
|
4
|
+
interface SandboxErrorResponse {
|
|
5
|
+
error?: string;
|
|
6
|
+
status?: string;
|
|
7
|
+
progress?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Base error class for all Sandbox-related errors
|
|
11
|
+
*/
|
|
12
|
+
declare class SandboxError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when Jupyter functionality is requested but the service is still initializing.
|
|
17
|
+
*
|
|
18
|
+
* Note: With the current implementation, requests wait for Jupyter to be ready.
|
|
19
|
+
* This error is only thrown when:
|
|
20
|
+
* 1. The request times out waiting for Jupyter (default: 30 seconds)
|
|
21
|
+
* 2. Jupyter initialization actually fails
|
|
22
|
+
*
|
|
23
|
+
* Most requests will succeed after a delay, not throw this error.
|
|
24
|
+
*/
|
|
25
|
+
declare class JupyterNotReadyError extends SandboxError {
|
|
26
|
+
readonly code = "JUPYTER_NOT_READY";
|
|
27
|
+
readonly retryAfter: number;
|
|
28
|
+
readonly progress?: number;
|
|
29
|
+
constructor(message?: string, options?: {
|
|
30
|
+
retryAfter?: number;
|
|
31
|
+
progress?: number;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Error thrown when a context is not found
|
|
36
|
+
*/
|
|
37
|
+
declare class ContextNotFoundError extends SandboxError {
|
|
38
|
+
readonly code = "CONTEXT_NOT_FOUND";
|
|
39
|
+
readonly contextId: string;
|
|
40
|
+
constructor(contextId: string);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Error thrown when code execution fails
|
|
44
|
+
*/
|
|
45
|
+
declare class CodeExecutionError extends SandboxError {
|
|
46
|
+
readonly code = "CODE_EXECUTION_ERROR";
|
|
47
|
+
readonly executionError?: {
|
|
48
|
+
ename?: string;
|
|
49
|
+
evalue?: string;
|
|
50
|
+
traceback?: string[];
|
|
51
|
+
};
|
|
52
|
+
constructor(message: string, executionError?: any);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Error thrown when the sandbox container is not ready
|
|
56
|
+
*/
|
|
57
|
+
declare class ContainerNotReadyError extends SandboxError {
|
|
58
|
+
readonly code = "CONTAINER_NOT_READY";
|
|
59
|
+
constructor(message?: string);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Error thrown when a network request to the sandbox fails
|
|
63
|
+
*/
|
|
64
|
+
declare class SandboxNetworkError extends SandboxError {
|
|
65
|
+
readonly code = "NETWORK_ERROR";
|
|
66
|
+
readonly statusCode?: number;
|
|
67
|
+
readonly statusText?: string;
|
|
68
|
+
constructor(message: string, statusCode?: number, statusText?: string);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Error thrown when service is temporarily unavailable (e.g., circuit breaker open)
|
|
72
|
+
*/
|
|
73
|
+
declare class ServiceUnavailableError extends SandboxError {
|
|
74
|
+
readonly code = "SERVICE_UNAVAILABLE";
|
|
75
|
+
readonly retryAfter?: number;
|
|
76
|
+
constructor(message?: string, retryAfter?: number);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Type guard to check if an error is a JupyterNotReadyError
|
|
80
|
+
*/
|
|
81
|
+
declare function isJupyterNotReadyError(error: unknown): error is JupyterNotReadyError;
|
|
82
|
+
/**
|
|
83
|
+
* Type guard to check if an error is any SandboxError
|
|
84
|
+
*/
|
|
85
|
+
declare function isSandboxError(error: unknown): error is SandboxError;
|
|
86
|
+
/**
|
|
87
|
+
* Helper to determine if an error is retryable
|
|
88
|
+
*/
|
|
89
|
+
declare function isRetryableError(error: unknown): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Parse error response from the sandbox API and return appropriate error instance
|
|
92
|
+
*/
|
|
93
|
+
declare function parseErrorResponse(response: Response): Promise<SandboxError>;
|
|
94
|
+
|
|
95
|
+
export { CodeExecutionError, ContainerNotReadyError, ContextNotFoundError, JupyterNotReadyError, SandboxError, type SandboxErrorResponse, SandboxNetworkError, ServiceUnavailableError, isJupyterNotReadyError, isRetryableError, isSandboxError, parseErrorResponse };
|