@cloudflare/sandbox 1.0.0-rc.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/index.d.mts +21 -18
- package/dist/index.mjs +22 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -62,17 +62,17 @@ The image needs the helper that `Files` runs, and the Worker needs `nodejs_compa
|
|
|
62
62
|
|
|
63
63
|
## What you can build
|
|
64
64
|
|
|
65
|
-
| Goal | Guide | Example
|
|
66
|
-
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
67
|
-
| Run a script and read its output | [Execute commands](https://developers.cloudflare.com/containers/guides/execute-commands/) | [`workspace`](examples/workspace)
|
|
68
|
-
| Keep a server or build running | [Run background processes](https://developers.cloudflare.com/sandbox/commands/run-background-processes/) | [`process-workspace`](examples/process-workspace)
|
|
69
|
-
| Open a shell in the browser | [Open a terminal in the browser](https://developers.cloudflare.com/sandbox/commands/open-a-terminal-in-the-browser/) | [`terminal-workspace`](examples/terminal-workspace)
|
|
70
|
-
| Process files from a bucket | [Mount an R2 bucket](https://developers.cloudflare.com/sandbox/files/mount-an-r2-bucket/) | [`artifact-workspace`](examples/artifact-workspace)
|
|
71
|
-
| Save a workspace and resume it later | [Save and restore a sandbox](https://developers.cloudflare.com/sandbox/files/save-and-restore-a-workspace/) | [`checkpoint-workspace`](examples/checkpoint-workspace), [`backup-workspace`](examples/backup-workspace)
|
|
72
|
-
| Preview a web app while you edit it | [Preview a web application](https://developers.cloudflare.com/sandbox/previews/) | [`preview-workspace`](examples/preview-workspace)
|
|
73
|
-
| Share a port on its own URL | [Serve previews on their own hostnames](https://developers.cloudflare.com/sandbox/previews/serve-previews-on-their-own-hostnames/) | [`share-workspace`](examples/share-workspace)
|
|
74
|
-
| Choose which hosts a sandbox can reach | [Control network access](https://developers.cloudflare.com/sandbox/network/) | [`outbound-workspace`](examples/outbound-workspace)
|
|
75
|
-
| Run a coding agent on a repository | [Coding agents](https://developers.cloudflare.com/sandbox/coding-agents/) | [`coding-agents`](examples/coding-agents), [`devin`](devin), [`openai
|
|
65
|
+
| Goal | Guide | Example |
|
|
66
|
+
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| Run a script and read its output | [Execute commands](https://developers.cloudflare.com/containers/guides/execute-commands/) | [`workspace`](examples/workspace) |
|
|
68
|
+
| Keep a server or build running | [Run background processes](https://developers.cloudflare.com/sandbox/commands/run-background-processes/) | [`process-workspace`](examples/process-workspace) |
|
|
69
|
+
| Open a shell in the browser | [Open a terminal in the browser](https://developers.cloudflare.com/sandbox/commands/open-a-terminal-in-the-browser/) | [`terminal-workspace`](examples/terminal-workspace) |
|
|
70
|
+
| Process files from a bucket | [Mount an R2 bucket](https://developers.cloudflare.com/sandbox/files/mount-an-r2-bucket/) | [`artifact-workspace`](examples/artifact-workspace) |
|
|
71
|
+
| Save a workspace and resume it later | [Save and restore a sandbox](https://developers.cloudflare.com/sandbox/files/save-and-restore-a-workspace/) | [`checkpoint-workspace`](examples/checkpoint-workspace), [`backup-workspace`](examples/backup-workspace) |
|
|
72
|
+
| Preview a web app while you edit it | [Preview a web application](https://developers.cloudflare.com/sandbox/previews/) | [`preview-workspace`](examples/preview-workspace) |
|
|
73
|
+
| Share a port on its own URL | [Serve previews on their own hostnames](https://developers.cloudflare.com/sandbox/previews/serve-previews-on-their-own-hostnames/) | [`share-workspace`](examples/share-workspace) |
|
|
74
|
+
| Choose which hosts a sandbox can reach | [Control network access](https://developers.cloudflare.com/sandbox/network/) | [`outbound-workspace`](examples/outbound-workspace) |
|
|
75
|
+
| Run a coding agent on a repository | [Coding agents](https://developers.cloudflare.com/sandbox/coding-agents/) | [`coding-agents`](examples/coding-agents), [`templates/devin`](templates/devin), [`templates/openai-agents-api`](templates/openai-agents-api) |
|
|
76
76
|
|
|
77
77
|
To run JavaScript or Python without a Linux environment, use [Dynamic Workers](https://developers.cloudflare.com/sandbox/concepts/) instead.
|
|
78
78
|
|
package/dist/index.d.mts
CHANGED
|
@@ -41,7 +41,7 @@ interface DirectoryBackupOptions {
|
|
|
41
41
|
interface DirectoryRestoreOptions {
|
|
42
42
|
/** Absolute path to restore into instead of the record's `dir`. */
|
|
43
43
|
dir?: string;
|
|
44
|
-
/** Cancels the
|
|
44
|
+
/** Cancels the restore until the download is verified, without imposing a timeout. */
|
|
45
45
|
signal?: AbortSignal;
|
|
46
46
|
}
|
|
47
47
|
interface DirectoryBackupDeleteOptions {
|
|
@@ -101,7 +101,7 @@ interface DirectoryBackupGatewayBinding {
|
|
|
101
101
|
* The container reaches `fetch()` through the outbound intercept and can only use the grant
|
|
102
102
|
* its current operation holds. The other methods are for the Durable Object alone.
|
|
103
103
|
*/
|
|
104
|
-
declare class DirectoryBackupGateway extends WorkerEntrypoint<object, DirectoryBackupGatewayProps> {
|
|
104
|
+
export declare class DirectoryBackupGateway extends WorkerEntrypoint<object, DirectoryBackupGatewayProps> {
|
|
105
105
|
#private;
|
|
106
106
|
fetch(request: Request): Promise<Response>;
|
|
107
107
|
createUpload(name?: string): Promise<string>;
|
|
@@ -121,7 +121,7 @@ type DirectoryBackupContainer = Pick<Container, "exec" | "interceptOutboundHttp"
|
|
|
121
121
|
* starts, retries, or times out anything. The application stores the returned records and
|
|
122
122
|
* decides when to delete them.
|
|
123
123
|
*/
|
|
124
|
-
declare class DirectoryBackup {
|
|
124
|
+
export declare class DirectoryBackup {
|
|
125
125
|
#private;
|
|
126
126
|
constructor(container: DirectoryBackupContainer, gateway: DirectoryBackupGatewayBinding, storage: DirectoryBackupStorage);
|
|
127
127
|
/**
|
|
@@ -145,9 +145,12 @@ declare class DirectoryBackup {
|
|
|
145
145
|
backup(options: DirectoryBackupOptions): Promise<DirectoryBackupRecord>;
|
|
146
146
|
/**
|
|
147
147
|
* Replaces `options.dir`, or the record's `dir`, with the backup's contents. The directory is
|
|
148
|
-
* extracted beside the target and swapped in only after the download is verified
|
|
149
|
-
* or
|
|
150
|
-
*
|
|
148
|
+
* extracted beside the target and swapped in only after the download is verified. A
|
|
149
|
+
* `SandboxFileError` or `SandboxBackupError` means the target was not replaced, and so does
|
|
150
|
+
* an abort before the download is verified; after that, aborting no longer ends the call. If the
|
|
151
|
+
* connection to the Container is lost, as when the Durable Object restarts, the target may or
|
|
152
|
+
* may not have been replaced, and restoring again settles it. The target's parent must exist;
|
|
153
|
+
* the target need not.
|
|
151
154
|
*
|
|
152
155
|
* @throws {SandboxFileError} The target's parent is missing (`ENOENT`), the target isn't a
|
|
153
156
|
* directory (`ENOTDIR`), is a mount point or has one inside it (`EBUSY`), the swap fails (for
|
|
@@ -211,7 +214,7 @@ type MkdirOptions = FileOperationOptions & {
|
|
|
211
214
|
* Operations run against the current native container execution. Its image must provide the matching shim at
|
|
212
215
|
* `/usr/local/bin/sandbox-shim`.
|
|
213
216
|
*/
|
|
214
|
-
declare class Files {
|
|
217
|
+
export declare class Files {
|
|
215
218
|
#private;
|
|
216
219
|
constructor(container: Pick<Container, "exec">);
|
|
217
220
|
/**
|
|
@@ -449,7 +452,7 @@ interface S3GatewayBinding {
|
|
|
449
452
|
}
|
|
450
453
|
//#endregion
|
|
451
454
|
//#region src/s3-mount/s3-gateway.d.ts
|
|
452
|
-
declare class S3Gateway extends WorkerEntrypoint<object, S3GatewayProps> {
|
|
455
|
+
export declare class S3Gateway extends WorkerEntrypoint<object, S3GatewayProps> {
|
|
453
456
|
fetch(request: Request): Promise<Response>;
|
|
454
457
|
}
|
|
455
458
|
//#endregion
|
|
@@ -467,7 +470,7 @@ type S3MountContainer = Pick<Container, "exec" | "interceptOutboundHttp">;
|
|
|
467
470
|
* monitors, or replaces it. The mounted path is not a POSIX filesystem. Do not
|
|
468
471
|
* use it for locking or atomic rename.
|
|
469
472
|
*/
|
|
470
|
-
declare class S3Mount {
|
|
473
|
+
export declare class S3Mount {
|
|
471
474
|
#private;
|
|
472
475
|
constructor(container: S3MountContainer, gateway: S3GatewayBinding);
|
|
473
476
|
/**
|
|
@@ -502,7 +505,7 @@ declare const FILE_OPERATIONS: readonly ["readFile", "writeFile", "stat", "lstat
|
|
|
502
505
|
/** Filesystem operation that failed. */
|
|
503
506
|
type SandboxFileOperation = (typeof FILE_OPERATIONS)[number];
|
|
504
507
|
/** A native Linux filesystem failure reported by the sandbox container. */
|
|
505
|
-
interface SandboxFileError extends Error {
|
|
508
|
+
export interface SandboxFileError extends Error {
|
|
506
509
|
readonly name: "SandboxFileError";
|
|
507
510
|
readonly code: SandboxFileErrorCode;
|
|
508
511
|
readonly operation: SandboxFileOperation;
|
|
@@ -510,38 +513,38 @@ interface SandboxFileError extends Error {
|
|
|
510
513
|
readonly destination?: string;
|
|
511
514
|
readonly detail: string;
|
|
512
515
|
}
|
|
513
|
-
declare const SandboxFileError: {
|
|
516
|
+
export declare const SandboxFileError: {
|
|
514
517
|
/** Recognizes local and JSRPC-crossed SandboxFileError values. */
|
|
515
518
|
is(cause: unknown): cause is SandboxFileError;
|
|
516
519
|
};
|
|
517
520
|
/** An incompatible or malformed exchange with `sandbox-shim`. */
|
|
518
|
-
interface SandboxProtocolError extends Error {
|
|
521
|
+
export interface SandboxProtocolError extends Error {
|
|
519
522
|
readonly name: "SandboxProtocolError";
|
|
520
523
|
readonly code: "SANDBOX_PROTOCOL_ERROR";
|
|
521
524
|
readonly detail: string;
|
|
522
525
|
}
|
|
523
|
-
declare const SandboxProtocolError: {
|
|
526
|
+
export declare const SandboxProtocolError: {
|
|
524
527
|
/** Recognizes local and JSRPC-crossed SandboxProtocolError values. */
|
|
525
528
|
is(cause: unknown): cause is SandboxProtocolError;
|
|
526
529
|
};
|
|
527
530
|
type SandboxS3MountErrorCode = "S3_MOUNT_CONFLICT" | "S3_MOUNT_BUSY" | "S3_MOUNT_FAILED" | "S3_MOUNT_INCOMPATIBLE";
|
|
528
531
|
type S3MountOperation = "mount" | "inspect" | "unmount";
|
|
529
532
|
/** A classifiable failure while reconciling an S3-compatible filesystem mount. */
|
|
530
|
-
interface SandboxS3MountError extends Error {
|
|
533
|
+
export interface SandboxS3MountError extends Error {
|
|
531
534
|
readonly name: "SandboxS3MountError";
|
|
532
535
|
readonly code: SandboxS3MountErrorCode;
|
|
533
536
|
readonly operation: S3MountOperation;
|
|
534
537
|
readonly path: string;
|
|
535
538
|
readonly detail: string;
|
|
536
539
|
}
|
|
537
|
-
declare const SandboxS3MountError: {
|
|
540
|
+
export declare const SandboxS3MountError: {
|
|
538
541
|
/** Recognizes local and JSRPC-crossed SandboxS3MountError values. */
|
|
539
542
|
is(cause: unknown): cause is SandboxS3MountError;
|
|
540
543
|
};
|
|
541
544
|
type SandboxBackupErrorCode = "BACKUP_NOT_FOUND" | "BACKUP_INTEGRITY" | "BACKUP_TRANSFER";
|
|
542
545
|
type DirectoryBackupOperation = "backup" | "restore" | "delete";
|
|
543
546
|
/** A failure specific to a directory backup: a missing, altered, or unreachable object. */
|
|
544
|
-
interface SandboxBackupError extends Error {
|
|
547
|
+
export interface SandboxBackupError extends Error {
|
|
545
548
|
readonly name: "SandboxBackupError";
|
|
546
549
|
readonly code: SandboxBackupErrorCode;
|
|
547
550
|
readonly operation: DirectoryBackupOperation;
|
|
@@ -549,9 +552,9 @@ interface SandboxBackupError extends Error {
|
|
|
549
552
|
readonly path: string;
|
|
550
553
|
readonly detail: string;
|
|
551
554
|
}
|
|
552
|
-
declare const SandboxBackupError: {
|
|
555
|
+
export declare const SandboxBackupError: {
|
|
553
556
|
/** Recognizes local and JSRPC-crossed SandboxBackupError values. */
|
|
554
557
|
is(cause: unknown): cause is SandboxBackupError;
|
|
555
558
|
};
|
|
556
559
|
//#endregion
|
|
557
|
-
export {
|
|
560
|
+
export type { DirectoryBackupDeleteOptions, DirectoryBackupGatewayBinding, DirectoryBackupOperation, DirectoryBackupOptions, DirectoryBackupRecord, DirectoryBackupStorage, DirectoryRestoreOptions, FileContent, FileOperationOptions, MkdirOptions, RemoveOptions, S3GatewayBinding, S3MountInspection, S3MountOperation, S3MountOperationOptions, S3MountRequest, SandboxBackupErrorCode, SandboxDirectoryEntry, SandboxFileStat, SandboxFileType, SandboxS3MountErrorCode };
|
package/dist/index.mjs
CHANGED
|
@@ -367,6 +367,10 @@ var ShimSession = class ShimSession {
|
|
|
367
367
|
waitFor(operation) {
|
|
368
368
|
return this.#abort.waitFor(operation);
|
|
369
369
|
}
|
|
370
|
+
/** Stops following the caller's signal: later waits no longer reject when it aborts. */
|
|
371
|
+
stopFollowingSignal() {
|
|
372
|
+
this.#abort.dispose();
|
|
373
|
+
}
|
|
370
374
|
finish() {
|
|
371
375
|
if (this.#settled) return;
|
|
372
376
|
this.#settled = true;
|
|
@@ -490,6 +494,7 @@ const errorSchema = z.object({
|
|
|
490
494
|
detail: z.string()
|
|
491
495
|
});
|
|
492
496
|
const lockedSchema = z.strictObject({ kind: z.literal("locked") });
|
|
497
|
+
const verifiedSchema = z.strictObject({ kind: z.literal("verified") });
|
|
493
498
|
/**
|
|
494
499
|
* Runs one `directory-backup` shim exchange: wait for the lock, register the grant, acknowledge,
|
|
495
500
|
* wait for the result, then deny and close stdin, in that order on every path. The shim holds
|
|
@@ -497,7 +502,10 @@ const lockedSchema = z.strictObject({ kind: z.literal("locked") });
|
|
|
497
502
|
* operation's deny.
|
|
498
503
|
*
|
|
499
504
|
* An abort closes stdin rather than killing the shim, which then removes whatever it had
|
|
500
|
-
* partly written, and rejects
|
|
505
|
+
* partly written, and rejects with the signal's reason without waiting for the shim. A restore
|
|
506
|
+
* reports `verified` before it swaps and swaps only once acknowledged, so the exchange
|
|
507
|
+
* acknowledges it only while the signal hasn't aborted, and from then on reports the swap's
|
|
508
|
+
* outcome instead of the abort.
|
|
501
509
|
*/
|
|
502
510
|
async function runShimExchange(container, exchange) {
|
|
503
511
|
const { signal } = exchange;
|
|
@@ -535,6 +543,13 @@ async function runShimExchange(container, exchange) {
|
|
|
535
543
|
granting = exchange.grant();
|
|
536
544
|
await session.waitFor(granting);
|
|
537
545
|
await session.waitFor(input.write(ACKNOWLEDGEMENT));
|
|
546
|
+
if (exchange.command === "restore") {
|
|
547
|
+
const verified = await readMessage(control, operation, exchange.path);
|
|
548
|
+
if (!verifiedSchema.safeParse(verified).success) throw protocolError("sandbox-shim did not report a verified restore");
|
|
549
|
+
signal?.throwIfAborted();
|
|
550
|
+
session.stopFollowingSignal();
|
|
551
|
+
await input.write(ACKNOWLEDGEMENT);
|
|
552
|
+
}
|
|
538
553
|
const result = await readMessage(control, operation, exchange.path);
|
|
539
554
|
const done = exchange.done.safeParse(result);
|
|
540
555
|
if (!done.success) throw protocolError("sandbox-shim returned an invalid backup result");
|
|
@@ -704,9 +719,12 @@ var DirectoryBackup = class {
|
|
|
704
719
|
}
|
|
705
720
|
/**
|
|
706
721
|
* Replaces `options.dir`, or the record's `dir`, with the backup's contents. The directory is
|
|
707
|
-
* extracted beside the target and swapped in only after the download is verified
|
|
708
|
-
* or
|
|
709
|
-
*
|
|
722
|
+
* extracted beside the target and swapped in only after the download is verified. A
|
|
723
|
+
* `SandboxFileError` or `SandboxBackupError` means the target was not replaced, and so does
|
|
724
|
+
* an abort before the download is verified; after that, aborting no longer ends the call. If the
|
|
725
|
+
* connection to the Container is lost, as when the Durable Object restarts, the target may or
|
|
726
|
+
* may not have been replaced, and restoring again settles it. The target's parent must exist;
|
|
727
|
+
* the target need not.
|
|
710
728
|
*
|
|
711
729
|
* @throws {SandboxFileError} The target's parent is missing (`ENOENT`), the target isn't a
|
|
712
730
|
* directory (`ENOTDIR`), is a mount point or has one inside it (`EBUSY`), the swap fails (for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudflare/sandbox",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Structured workspace file operations for Cloudflare sandboxes",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -23,6 +23,6 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"aws4fetch": "1.0.20",
|
|
26
|
-
"zod": "4.5
|
|
26
|
+
"zod": "4.6.5"
|
|
27
27
|
}
|
|
28
28
|
}
|