@agen-ai/sandbox 0.0.0-stage → 0.4.4
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/LICENSE +21 -0
- package/README.md +141 -2
- package/dist/acp/index.d.ts +9 -0
- package/dist/acp/index.js +25 -0
- package/dist/backend/contracts.d.ts +41 -0
- package/dist/backend/contracts.js +0 -0
- package/dist/backend/index.d.ts +6 -0
- package/dist/backend/index.js +64 -0
- package/dist/backend/spill.d.ts +15 -0
- package/dist/backend/spill.js +0 -0
- package/dist/contracts.d.ts +37 -0
- package/dist/contracts.js +0 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.js +73 -0
- package/dist/files/bytes.d.ts +31 -0
- package/dist/files/bytes.js +134 -0
- package/dist/files/iterator.d.ts +13 -0
- package/dist/files/iterator.js +59 -0
- package/dist/files/metadata/index.d.ts +12 -0
- package/dist/files/metadata/index.js +193 -0
- package/dist/files/metadata/records.d.ts +21 -0
- package/dist/files/metadata/records.js +50 -0
- package/dist/files/metadata/sort.d.ts +13 -0
- package/dist/files/metadata/sort.js +63 -0
- package/dist/files/metadata/storage.d.ts +12 -0
- package/dist/files/metadata/storage.js +69 -0
- package/dist/files/metadata/types.d.ts +19 -0
- package/dist/files/metadata/types.js +0 -0
- package/dist/files/parse.d.ts +20 -0
- package/dist/files/parse.js +62 -0
- package/dist/files/pathSchemas.d.ts +5 -0
- package/dist/files/pathSchemas.js +16 -0
- package/dist/files/paths.d.ts +4 -0
- package/dist/files/paths.js +12 -0
- package/dist/files/quotas.d.ts +18 -0
- package/dist/files/quotas.js +37 -0
- package/dist/files/types.d.ts +81 -0
- package/dist/files/types.js +0 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +35 -0
- package/dist/internal/data.d.ts +6 -0
- package/dist/internal/data.js +68 -0
- package/dist/internal/operations.d.ts +4 -0
- package/dist/internal/operations.js +30 -0
- package/dist/internal/parse.d.ts +3 -0
- package/dist/internal/parse.js +12 -0
- package/dist/lifecycle/deadline.d.ts +3 -0
- package/dist/lifecycle/deadline.js +18 -0
- package/dist/lifecycle/journal.d.ts +14 -0
- package/dist/lifecycle/journal.js +51 -0
- package/dist/lifecycle/observation.d.ts +9 -0
- package/dist/lifecycle/observation.js +41 -0
- package/dist/lifecycle/owner.d.ts +38 -0
- package/dist/lifecycle/owner.js +288 -0
- package/dist/lifecycle/recovered.d.ts +16 -0
- package/dist/lifecycle/recovered.js +62 -0
- package/dist/lifecycle/recoveryIdentity.d.ts +3 -0
- package/dist/lifecycle/recoveryIdentity.js +18 -0
- package/dist/lifecycle/transfer.d.ts +10 -0
- package/dist/lifecycle/transfer.js +339 -0
- package/dist/node/directories/closing.d.ts +2 -0
- package/dist/node/directories/closing.js +19 -0
- package/dist/node/directories/export.d.ts +6 -0
- package/dist/node/directories/export.js +43 -0
- package/dist/node/directories/file.d.ts +26 -0
- package/dist/node/directories/file.js +104 -0
- package/dist/node/directories/import.d.ts +6 -0
- package/dist/node/directories/import.js +38 -0
- package/dist/node/directories/options.d.ts +4 -0
- package/dist/node/directories/options.js +10 -0
- package/dist/node/directories/ownership.d.ts +33 -0
- package/dist/node/directories/ownership.js +148 -0
- package/dist/node/directories/source.d.ts +25 -0
- package/dist/node/directories/source.js +143 -0
- package/dist/node/directories/stage.d.ts +43 -0
- package/dist/node/directories/stage.js +302 -0
- package/dist/node/directories/types.d.ts +23 -0
- package/dist/node/directories/types.js +17 -0
- package/dist/node/hostPath.d.ts +7 -0
- package/dist/node/hostPath.js +37 -0
- package/dist/node/index.d.ts +8 -0
- package/dist/node/index.js +14 -0
- package/dist/node/spill.d.ts +14 -0
- package/dist/node/spill.js +97 -0
- package/dist/node/spillDirectory.d.ts +4 -0
- package/dist/node/spillDirectory.js +17 -0
- package/dist/node/spillFile.d.ts +24 -0
- package/dist/node/spillFile.js +96 -0
- package/dist/policy/limits.d.ts +31 -0
- package/dist/policy/limits.js +35 -0
- package/dist/policy/parse.d.ts +11 -0
- package/dist/policy/parse.js +47 -0
- package/dist/policy/schemas.d.ts +148 -0
- package/dist/policy/schemas.js +75 -0
- package/dist/policy/types.d.ts +105 -0
- package/dist/policy/types.js +0 -0
- package/dist/process/input.d.ts +23 -0
- package/dist/process/input.js +93 -0
- package/dist/process/output.d.ts +13 -0
- package/dist/process/output.js +43 -0
- package/dist/process/parse.d.ts +4 -0
- package/dist/process/parse.js +32 -0
- package/dist/process/streams.d.ts +9 -0
- package/dist/process/streams.js +61 -0
- package/dist/process/types.d.ts +38 -0
- package/dist/process/types.js +0 -0
- package/dist/recovery/parse.d.ts +4 -0
- package/dist/recovery/parse.js +67 -0
- package/dist/recovery/types.d.ts +59 -0
- package/dist/recovery/types.js +0 -0
- package/dist/withSandbox.d.ts +4 -0
- package/dist/withSandbox.js +39 -0
- package/package.json +76 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class SandboxDirectoryCleanupError extends Error {
|
|
2
|
+
constructor(primaryError, cleanupError, directory, owner, publishedDirectory) {
|
|
3
|
+
super("Directory transfer failed or its local filesystem cleanup remains owned.", { cause: primaryError });
|
|
4
|
+
this.primaryError = primaryError;
|
|
5
|
+
this.cleanupError = cleanupError;
|
|
6
|
+
this.directory = directory;
|
|
7
|
+
this.owner = owner;
|
|
8
|
+
this.publishedDirectory = publishedDirectory;
|
|
9
|
+
this.name = "SandboxDirectoryCleanupError";
|
|
10
|
+
}
|
|
11
|
+
retryCleanup() {
|
|
12
|
+
return this.owner.close();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
SandboxDirectoryCleanupError
|
|
17
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BigIntStats } from 'node:fs';
|
|
2
|
+
export declare function canonicalHostPath(input: unknown): string;
|
|
3
|
+
export declare function verifyHostDirectory(path: string, signal?: AbortSignal): Promise<BigIntStats>;
|
|
4
|
+
export declare function sameIdentity(left: BigIntStats, right: BigIntStats): boolean;
|
|
5
|
+
export declare function verifyDirectoryIdentity(path: string, expected: BigIntStats): Promise<void>;
|
|
6
|
+
export declare function verifyRegularIdentity(current: BigIntStats, expected: BigIntStats): void;
|
|
7
|
+
//# sourceMappingURL=hostPath.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, normalize } from "node:path";
|
|
3
|
+
import { SandboxOperationError } from "../errors.js";
|
|
4
|
+
import { invalidInput } from "../internal/data.js";
|
|
5
|
+
function canonicalHostPath(input) {
|
|
6
|
+
if (typeof input !== "string" || input.includes("\0") || !isAbsolute(input) || normalize(input) !== input || input.length > 1 && input.endsWith("/")) invalidInput("Select an explicit canonical absolute host path");
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
async function verifyHostDirectory(path, signal) {
|
|
10
|
+
signal?.throwIfAborted();
|
|
11
|
+
const metadata = await lstat(path, { bigint: true });
|
|
12
|
+
signal?.throwIfAborted();
|
|
13
|
+
if (!metadata.isDirectory() || await realpath(path) !== path) invalidInput("Host root must be an existing canonical directory without symlink components");
|
|
14
|
+
signal?.throwIfAborted();
|
|
15
|
+
return metadata;
|
|
16
|
+
}
|
|
17
|
+
function sameIdentity(left, right) {
|
|
18
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
19
|
+
}
|
|
20
|
+
async function verifyDirectoryIdentity(path, expected) {
|
|
21
|
+
const current = await lstat(path, { bigint: true });
|
|
22
|
+
if (!current.isDirectory() || !sameIdentity(current, expected)) {
|
|
23
|
+
throw new SandboxOperationError("input_limit", "Host directory identity changed during the transfer.");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function verifyRegularIdentity(current, expected) {
|
|
27
|
+
if (!current.isFile() || !sameIdentity(current, expected) || current.size !== expected.size || current.mtimeNs !== expected.mtimeNs || current.ctimeNs !== expected.ctimeNs) {
|
|
28
|
+
throw new SandboxOperationError("input_limit", "Host file identity, length or modification metadata changed during the transfer.");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
canonicalHostPath,
|
|
33
|
+
sameIdentity,
|
|
34
|
+
verifyDirectoryIdentity,
|
|
35
|
+
verifyHostDirectory,
|
|
36
|
+
verifyRegularIdentity
|
|
37
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createSandboxSpillStore, SandboxSpillCleanupError } from './spill.js';
|
|
2
|
+
export type { SandboxSpillStoreOptions } from './spill.js';
|
|
3
|
+
export { parseSandboxSpillDirectory, verifySandboxSpillDirectory } from './spillDirectory.js';
|
|
4
|
+
export { importSandboxDirectory } from './directories/import.js';
|
|
5
|
+
export { exportSandboxDirectory } from './directories/export.js';
|
|
6
|
+
export { SandboxDirectoryCleanupError } from './directories/types.js';
|
|
7
|
+
export type { SandboxDirectoryImportOptions, SandboxDirectoryExportResult } from './directories/types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createSandboxSpillStore, SandboxSpillCleanupError } from "./spill.js";
|
|
2
|
+
import { parseSandboxSpillDirectory, verifySandboxSpillDirectory } from "./spillDirectory.js";
|
|
3
|
+
import { importSandboxDirectory } from "./directories/import.js";
|
|
4
|
+
import { exportSandboxDirectory } from "./directories/export.js";
|
|
5
|
+
import { SandboxDirectoryCleanupError } from "./directories/types.js";
|
|
6
|
+
export {
|
|
7
|
+
SandboxDirectoryCleanupError,
|
|
8
|
+
SandboxSpillCleanupError,
|
|
9
|
+
createSandboxSpillStore,
|
|
10
|
+
exportSandboxDirectory,
|
|
11
|
+
importSandboxDirectory,
|
|
12
|
+
parseSandboxSpillDirectory,
|
|
13
|
+
verifySandboxSpillDirectory
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SandboxDriverSpillStore } from '../backend/spill.js';
|
|
2
|
+
export interface SandboxSpillStoreOptions {
|
|
3
|
+
readonly directory: string;
|
|
4
|
+
readonly signal?: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
export declare class SandboxSpillCleanupError extends Error {
|
|
7
|
+
readonly primaryError: unknown;
|
|
8
|
+
readonly cleanupError: unknown;
|
|
9
|
+
readonly owner: SandboxDriverSpillStore;
|
|
10
|
+
constructor(primaryError: unknown, cleanupError: unknown, owner: SandboxDriverSpillStore);
|
|
11
|
+
retryCleanup(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function createSandboxSpillStore(input: SandboxSpillStoreOptions): Promise<SandboxDriverSpillStore>;
|
|
14
|
+
//# sourceMappingURL=spill.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { SandboxStateError } from "../errors.js";
|
|
6
|
+
import { invalidInput } from "../internal/data.js";
|
|
7
|
+
import { parseOperationOptions } from "../internal/operations.js";
|
|
8
|
+
import { parseSandboxSpillDirectory, verifySandboxSpillDirectory } from "./spillDirectory.js";
|
|
9
|
+
import { SpillFile } from "./spillFile.js";
|
|
10
|
+
class SandboxSpillCleanupError extends Error {
|
|
11
|
+
constructor(primaryError, cleanupError, owner) {
|
|
12
|
+
super("Spill acquisition failed and its exact cleanup remains owned.", { cause: primaryError });
|
|
13
|
+
this.primaryError = primaryError;
|
|
14
|
+
this.cleanupError = cleanupError;
|
|
15
|
+
this.owner = owner;
|
|
16
|
+
this.name = "SandboxSpillCleanupError";
|
|
17
|
+
}
|
|
18
|
+
retryCleanup() {
|
|
19
|
+
return this.owner.close();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
class SpillStore {
|
|
23
|
+
constructor(directory, signal) {
|
|
24
|
+
this.directory = directory;
|
|
25
|
+
this.signal = signal;
|
|
26
|
+
}
|
|
27
|
+
phase = "open";
|
|
28
|
+
files = /* @__PURE__ */ new Set();
|
|
29
|
+
acquiring;
|
|
30
|
+
closing;
|
|
31
|
+
createFile() {
|
|
32
|
+
this.admit();
|
|
33
|
+
if (this.acquiring || this.files.size === 4) return Promise.reject(new SandboxStateError("Spill store admits four files and one acquisition at a time."));
|
|
34
|
+
const path = join(this.directory, `agenai-metadata-${randomUUID()}`);
|
|
35
|
+
const file = new SpillFile(() => this.admit(), () => this.files.delete(file));
|
|
36
|
+
this.files.add(file);
|
|
37
|
+
const actual = (async () => {
|
|
38
|
+
try {
|
|
39
|
+
file.attach(await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_RDWR | constants.O_NOFOLLOW, 384), path);
|
|
40
|
+
await file.unlink();
|
|
41
|
+
this.admit();
|
|
42
|
+
return file;
|
|
43
|
+
} catch (primaryError) {
|
|
44
|
+
try {
|
|
45
|
+
await file.close();
|
|
46
|
+
} catch (cleanupError) {
|
|
47
|
+
throw new SandboxSpillCleanupError(primaryError, cleanupError, this);
|
|
48
|
+
}
|
|
49
|
+
throw primaryError;
|
|
50
|
+
}
|
|
51
|
+
})().finally(() => {
|
|
52
|
+
if (this.acquiring === actual) this.acquiring = void 0;
|
|
53
|
+
});
|
|
54
|
+
this.acquiring = actual;
|
|
55
|
+
return actual;
|
|
56
|
+
}
|
|
57
|
+
close() {
|
|
58
|
+
if (this.phase === "closed") return Promise.resolve();
|
|
59
|
+
if (this.closing) return this.closing;
|
|
60
|
+
this.phase = "closing";
|
|
61
|
+
this.closing = (async () => {
|
|
62
|
+
await this.acquiring?.catch(() => {
|
|
63
|
+
});
|
|
64
|
+
const failures = [];
|
|
65
|
+
for (const file of this.files) {
|
|
66
|
+
try {
|
|
67
|
+
await file.close();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
failures.push(error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (failures.length) throw new AggregateError(failures, "Spill store retains unresolved file cleanup.");
|
|
73
|
+
this.phase = "closed";
|
|
74
|
+
})().finally(() => {
|
|
75
|
+
this.closing = void 0;
|
|
76
|
+
});
|
|
77
|
+
return this.closing;
|
|
78
|
+
}
|
|
79
|
+
admit() {
|
|
80
|
+
if (this.phase !== "open") throw new SandboxStateError("Spill store admission is closed.");
|
|
81
|
+
this.signal?.throwIfAborted();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function createSandboxSpillStore(input) {
|
|
85
|
+
if (!input || typeof input !== "object" || Object.getPrototypeOf(input) !== Object.prototype && Object.getPrototypeOf(input) !== null || Reflect.ownKeys(input).some((key) => key !== "directory" && key !== "signal")) invalidInput("Spill options require only an explicit directory and optional signal");
|
|
86
|
+
const directory = Object.getOwnPropertyDescriptor(input, "directory");
|
|
87
|
+
const signal = Object.getOwnPropertyDescriptor(input, "signal");
|
|
88
|
+
if (!directory || !("value" in directory) || !directory.enumerable || signal && (!("value" in signal) || !signal.enumerable)) invalidInput("Spill options require enumerable data properties");
|
|
89
|
+
const root = parseSandboxSpillDirectory(directory.value);
|
|
90
|
+
const options = parseOperationOptions(signal ? { signal: signal.value } : void 0);
|
|
91
|
+
await verifySandboxSpillDirectory(root, options);
|
|
92
|
+
return new SpillStore(root, options.signal);
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
SandboxSpillCleanupError,
|
|
96
|
+
createSandboxSpillStore
|
|
97
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OperationOptions } from '../contracts.js';
|
|
2
|
+
export declare function parseSandboxSpillDirectory(input: unknown): string;
|
|
3
|
+
export declare function verifySandboxSpillDirectory(directory: string, input?: OperationOptions): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=spillDirectory.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SandboxUnsupportedError } from "../errors.js";
|
|
2
|
+
import { parseOperationOptions } from "../internal/operations.js";
|
|
3
|
+
import { canonicalHostPath, verifyHostDirectory } from "./hostPath.js";
|
|
4
|
+
function parseSandboxSpillDirectory(input) {
|
|
5
|
+
return canonicalHostPath(input);
|
|
6
|
+
}
|
|
7
|
+
async function verifySandboxSpillDirectory(directory, input) {
|
|
8
|
+
const root = parseSandboxSpillDirectory(directory);
|
|
9
|
+
const { signal } = parseOperationOptions(input);
|
|
10
|
+
signal?.throwIfAborted();
|
|
11
|
+
if (process.platform === "win32") throw new SandboxUnsupportedError("Metadata spill requires POSIX create-and-unlink semantics.");
|
|
12
|
+
await verifyHostDirectory(root, signal);
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
parseSandboxSpillDirectory,
|
|
16
|
+
verifySandboxSpillDirectory
|
|
17
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
import type { SandboxDriverSpillFile } from '../backend/spill.js';
|
|
3
|
+
export declare class SpillFile implements SandboxDriverSpillFile {
|
|
4
|
+
private readonly admit;
|
|
5
|
+
private readonly released;
|
|
6
|
+
private file;
|
|
7
|
+
private path;
|
|
8
|
+
private phase;
|
|
9
|
+
private active;
|
|
10
|
+
private closing;
|
|
11
|
+
constructor(admit: () => void, released: () => void);
|
|
12
|
+
attach(file: FileHandle, path: string): void;
|
|
13
|
+
unlink(): Promise<void>;
|
|
14
|
+
read(buffer: Uint8Array, offset: number, length: number, position: number): Promise<Readonly<{
|
|
15
|
+
bytesRead: number;
|
|
16
|
+
}>>;
|
|
17
|
+
write(buffer: Uint8Array, offset: number, length: number, position: number): Promise<Readonly<{
|
|
18
|
+
bytesWritten: number;
|
|
19
|
+
}>>;
|
|
20
|
+
truncate(size: number): Promise<void>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
private enter;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=spillFile.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { unlink } from "node:fs/promises";
|
|
2
|
+
import { SandboxStateError } from "../errors.js";
|
|
3
|
+
function address(value) {
|
|
4
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError("Spill file addresses must be nonnegative safe integers.");
|
|
5
|
+
}
|
|
6
|
+
function byteRange(buffer, offset, length, position) {
|
|
7
|
+
if (!(buffer instanceof Uint8Array)) throw new TypeError("Spill I/O requires a byte buffer.");
|
|
8
|
+
address(offset);
|
|
9
|
+
address(length);
|
|
10
|
+
address(position);
|
|
11
|
+
address(position + length);
|
|
12
|
+
if (offset + length > buffer.byteLength) throw new RangeError("Spill byte range exceeds its buffer.");
|
|
13
|
+
}
|
|
14
|
+
class SpillFile {
|
|
15
|
+
constructor(admit, released) {
|
|
16
|
+
this.admit = admit;
|
|
17
|
+
this.released = released;
|
|
18
|
+
}
|
|
19
|
+
file;
|
|
20
|
+
path;
|
|
21
|
+
phase = "open";
|
|
22
|
+
active;
|
|
23
|
+
closing;
|
|
24
|
+
attach(file, path) {
|
|
25
|
+
this.file = file;
|
|
26
|
+
this.path = path;
|
|
27
|
+
}
|
|
28
|
+
async unlink() {
|
|
29
|
+
if (!this.path) return;
|
|
30
|
+
await unlink(this.path);
|
|
31
|
+
this.path = void 0;
|
|
32
|
+
}
|
|
33
|
+
read(buffer, offset, length, position) {
|
|
34
|
+
byteRange(buffer, offset, length, position);
|
|
35
|
+
return this.enter(async (file) => {
|
|
36
|
+
const { bytesRead } = await file.read(buffer, offset, length, position);
|
|
37
|
+
return Object.freeze({ bytesRead });
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
write(buffer, offset, length, position) {
|
|
41
|
+
byteRange(buffer, offset, length, position);
|
|
42
|
+
return this.enter(async (file) => {
|
|
43
|
+
const { bytesWritten } = await file.write(buffer, offset, length, position);
|
|
44
|
+
return Object.freeze({ bytesWritten });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
truncate(size) {
|
|
48
|
+
address(size);
|
|
49
|
+
return this.enter((file) => file.truncate(size));
|
|
50
|
+
}
|
|
51
|
+
close() {
|
|
52
|
+
if (this.phase === "closed") return Promise.resolve();
|
|
53
|
+
if (this.closing) return this.closing;
|
|
54
|
+
this.phase = "closing";
|
|
55
|
+
this.closing = (async () => {
|
|
56
|
+
await this.active?.catch(() => {
|
|
57
|
+
});
|
|
58
|
+
const failures = [];
|
|
59
|
+
try {
|
|
60
|
+
await this.unlink();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") this.path = void 0;
|
|
63
|
+
else failures.push(error);
|
|
64
|
+
}
|
|
65
|
+
if (this.file) {
|
|
66
|
+
try {
|
|
67
|
+
await this.file.close();
|
|
68
|
+
this.file = void 0;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
failures.push(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (failures.length) throw new AggregateError(failures, "Native spill file cleanup remains owned.");
|
|
74
|
+
this.phase = "closed";
|
|
75
|
+
this.released();
|
|
76
|
+
})().finally(() => {
|
|
77
|
+
this.closing = void 0;
|
|
78
|
+
});
|
|
79
|
+
return this.closing;
|
|
80
|
+
}
|
|
81
|
+
enter(perform) {
|
|
82
|
+
this.admit();
|
|
83
|
+
if (this.phase !== "open" || !this.file || this.path || this.active) {
|
|
84
|
+
return Promise.reject(new SandboxStateError("Spill I/O requires an idle unlinked open file."));
|
|
85
|
+
}
|
|
86
|
+
const file = this.file;
|
|
87
|
+
const actual = perform(file).finally(() => {
|
|
88
|
+
if (this.active === actual) this.active = void 0;
|
|
89
|
+
});
|
|
90
|
+
this.active = actual;
|
|
91
|
+
return actual;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
SpillFile
|
|
96
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const SANDBOX_LIMITS: Readonly<{
|
|
2
|
+
maxTransferChunkBytes: number;
|
|
3
|
+
maxMetadataBatchBytes: number;
|
|
4
|
+
maxMetadataMergeFanIn: 32;
|
|
5
|
+
maxPathBytes: 1024;
|
|
6
|
+
maxPathSegments: 64;
|
|
7
|
+
maxPathSegmentBytes: 255;
|
|
8
|
+
maxArgs: 128;
|
|
9
|
+
maxEnvEntries: 128;
|
|
10
|
+
maxEnvValueBytes: number;
|
|
11
|
+
maxRecipeBytes: number;
|
|
12
|
+
maxBufferedOutputBytes: number;
|
|
13
|
+
maxBufferedInputBytes: number;
|
|
14
|
+
highWaterMarkBytes: number;
|
|
15
|
+
startupMs: 60000;
|
|
16
|
+
cleanupMs: 30000;
|
|
17
|
+
maxRecoveryBytes: number;
|
|
18
|
+
maxIdentityBytes: 256;
|
|
19
|
+
maxNamespaceBytes: 64;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const SANDBOX_DEFAULT_TRANSFER_BUFFERS: Readonly<{
|
|
22
|
+
chunkBytes: number;
|
|
23
|
+
metadataBatchBytes: number;
|
|
24
|
+
metadataMergeFanIn: 8;
|
|
25
|
+
}>;
|
|
26
|
+
export declare const SANDBOX_DEFAULT_STREAM_LIMITS: Readonly<{
|
|
27
|
+
maxBufferedInputBytes: number;
|
|
28
|
+
maxBufferedOutputBytes: number;
|
|
29
|
+
highWaterMarkBytes: number;
|
|
30
|
+
}>;
|
|
31
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const SANDBOX_LIMITS = Object.freeze({
|
|
2
|
+
maxTransferChunkBytes: 64 * 1024,
|
|
3
|
+
maxMetadataBatchBytes: 4 * 1024 * 1024,
|
|
4
|
+
maxMetadataMergeFanIn: 32,
|
|
5
|
+
maxPathBytes: 1024,
|
|
6
|
+
maxPathSegments: 64,
|
|
7
|
+
maxPathSegmentBytes: 255,
|
|
8
|
+
maxArgs: 128,
|
|
9
|
+
maxEnvEntries: 128,
|
|
10
|
+
maxEnvValueBytes: 16 * 1024,
|
|
11
|
+
maxRecipeBytes: 256 * 1024,
|
|
12
|
+
maxBufferedOutputBytes: 8 * 1024 * 1024,
|
|
13
|
+
maxBufferedInputBytes: 8 * 1024 * 1024,
|
|
14
|
+
highWaterMarkBytes: 64 * 1024,
|
|
15
|
+
startupMs: 6e4,
|
|
16
|
+
cleanupMs: 3e4,
|
|
17
|
+
maxRecoveryBytes: 64 * 1024,
|
|
18
|
+
maxIdentityBytes: 256,
|
|
19
|
+
maxNamespaceBytes: 64
|
|
20
|
+
});
|
|
21
|
+
const SANDBOX_DEFAULT_TRANSFER_BUFFERS = Object.freeze({
|
|
22
|
+
chunkBytes: SANDBOX_LIMITS.maxTransferChunkBytes,
|
|
23
|
+
metadataBatchBytes: 64 * 1024,
|
|
24
|
+
metadataMergeFanIn: 8
|
|
25
|
+
});
|
|
26
|
+
const SANDBOX_DEFAULT_STREAM_LIMITS = Object.freeze({
|
|
27
|
+
maxBufferedInputBytes: SANDBOX_LIMITS.maxBufferedInputBytes,
|
|
28
|
+
maxBufferedOutputBytes: SANDBOX_LIMITS.maxBufferedOutputBytes,
|
|
29
|
+
highWaterMarkBytes: SANDBOX_LIMITS.highWaterMarkBytes
|
|
30
|
+
});
|
|
31
|
+
export {
|
|
32
|
+
SANDBOX_DEFAULT_STREAM_LIMITS,
|
|
33
|
+
SANDBOX_DEFAULT_TRANSFER_BUFFERS,
|
|
34
|
+
SANDBOX_LIMITS
|
|
35
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SandboxEffectivePolicy, SandboxId, SandboxNamespace, SandboxPolicy, SandboxProcessId, SandboxRequest, SandboxSupport } from './types.js';
|
|
2
|
+
export declare function parseSandboxNamespace(input: unknown): SandboxNamespace;
|
|
3
|
+
export declare function parseSandboxId(input: unknown): SandboxId;
|
|
4
|
+
export declare function parseSandboxProcessId(input: unknown): SandboxProcessId;
|
|
5
|
+
export declare function parseSandboxPolicy(input: unknown): SandboxPolicy;
|
|
6
|
+
export declare function parseSandboxRequest(input: unknown): SandboxRequest;
|
|
7
|
+
export declare function parseSandboxSupport(input: unknown): SandboxSupport;
|
|
8
|
+
export declare function parseSandboxEffectivePolicy(input: unknown): SandboxEffectivePolicy;
|
|
9
|
+
export declare function assertSandboxRequestSupported(request: SandboxRequest, support: SandboxSupport): void;
|
|
10
|
+
export declare function assertSandboxEffectivePolicy(request: SandboxRequest, effective: SandboxEffectivePolicy): void;
|
|
11
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SandboxUnsupportedError } from "../errors.js";
|
|
2
|
+
import { parseData } from "../internal/parse.js";
|
|
3
|
+
import { effectiveSchema, namespaceSchema, policySchema, processIdSchema, requestSchema, sandboxIdSchema, supportSchema } from "./schemas.js";
|
|
4
|
+
function parseSandboxNamespace(input) {
|
|
5
|
+
return parseData(namespaceSchema, input);
|
|
6
|
+
}
|
|
7
|
+
function parseSandboxId(input) {
|
|
8
|
+
return parseData(sandboxIdSchema, input);
|
|
9
|
+
}
|
|
10
|
+
function parseSandboxProcessId(input) {
|
|
11
|
+
return parseData(processIdSchema, input);
|
|
12
|
+
}
|
|
13
|
+
function parseSandboxPolicy(input) {
|
|
14
|
+
return parseData(policySchema, input);
|
|
15
|
+
}
|
|
16
|
+
function parseSandboxRequest(input) {
|
|
17
|
+
return parseData(requestSchema, input);
|
|
18
|
+
}
|
|
19
|
+
function parseSandboxSupport(input) {
|
|
20
|
+
return parseData(supportSchema, input);
|
|
21
|
+
}
|
|
22
|
+
function parseSandboxEffectivePolicy(input) {
|
|
23
|
+
return parseData(effectiveSchema, input);
|
|
24
|
+
}
|
|
25
|
+
function assertSandboxRequestSupported(request, support) {
|
|
26
|
+
if (!support.networkModes.includes(request.policy.network.kind)) throw new SandboxUnsupportedError(`Backend does not support ${request.policy.network.kind} networking`);
|
|
27
|
+
if (request.policy.workspaceDiskQuota.kind === "required" && !support.workspaceDiskQuota) {
|
|
28
|
+
throw new SandboxUnsupportedError("Backend does not support a required allocated-block workspace disk quota");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function assertSandboxEffectivePolicy(request, effective) {
|
|
32
|
+
const desired = request.policy;
|
|
33
|
+
if (effective.resources.cpus !== desired.resources.cpus || effective.resources.memoryBytes !== desired.resources.memoryBytes || effective.resources.pids !== desired.resources.pids || effective.temporaryStorage.homeBytes !== desired.temporaryStorage.homeBytes || effective.temporaryStorage.tempBytes !== desired.temporaryStorage.tempBytes || effective.network.kind !== desired.network.kind || effective.workspaceDiskQuota.kind !== desired.workspaceDiskQuota.kind) {
|
|
34
|
+
throw new SandboxUnsupportedError("Backend effective policy does not match the requested policy");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
assertSandboxEffectivePolicy,
|
|
39
|
+
assertSandboxRequestSupported,
|
|
40
|
+
parseSandboxEffectivePolicy,
|
|
41
|
+
parseSandboxId,
|
|
42
|
+
parseSandboxNamespace,
|
|
43
|
+
parseSandboxPolicy,
|
|
44
|
+
parseSandboxProcessId,
|
|
45
|
+
parseSandboxRequest,
|
|
46
|
+
parseSandboxSupport
|
|
47
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { SandboxId, SandboxNamespace, SandboxProcessId } from './types.js';
|
|
3
|
+
export declare const identitySchema: z.ZodString;
|
|
4
|
+
export declare const namespaceSchema: z.ZodCustom<SandboxNamespace, SandboxNamespace>;
|
|
5
|
+
export declare const sandboxIdSchema: z.ZodCustom<SandboxId, SandboxId>;
|
|
6
|
+
export declare const processIdSchema: z.ZodCustom<SandboxProcessId, SandboxProcessId>;
|
|
7
|
+
export declare const transferBuffersSchema: z.ZodObject<{
|
|
8
|
+
chunkBytes: z.ZodNumber;
|
|
9
|
+
metadataBatchBytes: z.ZodNumber;
|
|
10
|
+
metadataMergeFanIn: z.ZodNumber;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const streamLimitsSchema: z.ZodObject<{
|
|
13
|
+
maxStdinBytes: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
maxOutputBytes: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
maxBufferedInputBytes: z.ZodNumber;
|
|
16
|
+
maxBufferedOutputBytes: z.ZodNumber;
|
|
17
|
+
highWaterMarkBytes: z.ZodNumber;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export declare const policySchema: z.ZodObject<{
|
|
20
|
+
resources: z.ZodObject<{
|
|
21
|
+
cpus: z.ZodNumber;
|
|
22
|
+
memoryBytes: z.ZodNumber;
|
|
23
|
+
pids: z.ZodNumber;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
temporaryStorage: z.ZodObject<{
|
|
26
|
+
homeBytes: z.ZodNumber;
|
|
27
|
+
tempBytes: z.ZodNumber;
|
|
28
|
+
}, z.core.$strict>;
|
|
29
|
+
network: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
30
|
+
kind: z.ZodLiteral<"isolated">;
|
|
31
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
32
|
+
kind: z.ZodLiteral<"outbound">;
|
|
33
|
+
}, z.core.$strict>], "kind">;
|
|
34
|
+
workspaceDiskQuota: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
35
|
+
kind: z.ZodLiteral<"not_requested">;
|
|
36
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
37
|
+
kind: z.ZodLiteral<"required">;
|
|
38
|
+
bytes: z.ZodNumber;
|
|
39
|
+
accounting: z.ZodLiteral<"allocated_blocks">;
|
|
40
|
+
}, z.core.$strict>], "kind">>;
|
|
41
|
+
execution: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
42
|
+
kind: z.ZodLiteral<"until_stopped">;
|
|
43
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
44
|
+
kind: z.ZodLiteral<"deadline">;
|
|
45
|
+
timeoutMs: z.ZodNumber;
|
|
46
|
+
}, z.core.$strict>], "kind">>;
|
|
47
|
+
deadlines: z.ZodObject<{
|
|
48
|
+
startupMs: z.ZodNumber;
|
|
49
|
+
cleanupMs: z.ZodNumber;
|
|
50
|
+
}, z.core.$strict>;
|
|
51
|
+
transfers: z.ZodObject<{
|
|
52
|
+
chunkBytes: z.ZodNumber;
|
|
53
|
+
metadataBatchBytes: z.ZodNumber;
|
|
54
|
+
metadataMergeFanIn: z.ZodNumber;
|
|
55
|
+
}, z.core.$strict>;
|
|
56
|
+
streams: z.ZodObject<{
|
|
57
|
+
maxStdinBytes: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
maxOutputBytes: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
maxBufferedInputBytes: z.ZodNumber;
|
|
60
|
+
maxBufferedOutputBytes: z.ZodNumber;
|
|
61
|
+
highWaterMarkBytes: z.ZodNumber;
|
|
62
|
+
}, z.core.$strict>;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
export declare const requestSchema: z.ZodObject<{
|
|
65
|
+
policy: z.ZodObject<{
|
|
66
|
+
resources: z.ZodObject<{
|
|
67
|
+
cpus: z.ZodNumber;
|
|
68
|
+
memoryBytes: z.ZodNumber;
|
|
69
|
+
pids: z.ZodNumber;
|
|
70
|
+
}, z.core.$strict>;
|
|
71
|
+
temporaryStorage: z.ZodObject<{
|
|
72
|
+
homeBytes: z.ZodNumber;
|
|
73
|
+
tempBytes: z.ZodNumber;
|
|
74
|
+
}, z.core.$strict>;
|
|
75
|
+
network: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
76
|
+
kind: z.ZodLiteral<"isolated">;
|
|
77
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
78
|
+
kind: z.ZodLiteral<"outbound">;
|
|
79
|
+
}, z.core.$strict>], "kind">;
|
|
80
|
+
workspaceDiskQuota: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
81
|
+
kind: z.ZodLiteral<"not_requested">;
|
|
82
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
83
|
+
kind: z.ZodLiteral<"required">;
|
|
84
|
+
bytes: z.ZodNumber;
|
|
85
|
+
accounting: z.ZodLiteral<"allocated_blocks">;
|
|
86
|
+
}, z.core.$strict>], "kind">>;
|
|
87
|
+
execution: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
88
|
+
kind: z.ZodLiteral<"until_stopped">;
|
|
89
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
90
|
+
kind: z.ZodLiteral<"deadline">;
|
|
91
|
+
timeoutMs: z.ZodNumber;
|
|
92
|
+
}, z.core.$strict>], "kind">>;
|
|
93
|
+
deadlines: z.ZodObject<{
|
|
94
|
+
startupMs: z.ZodNumber;
|
|
95
|
+
cleanupMs: z.ZodNumber;
|
|
96
|
+
}, z.core.$strict>;
|
|
97
|
+
transfers: z.ZodObject<{
|
|
98
|
+
chunkBytes: z.ZodNumber;
|
|
99
|
+
metadataBatchBytes: z.ZodNumber;
|
|
100
|
+
metadataMergeFanIn: z.ZodNumber;
|
|
101
|
+
}, z.core.$strict>;
|
|
102
|
+
streams: z.ZodObject<{
|
|
103
|
+
maxStdinBytes: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
maxOutputBytes: z.ZodOptional<z.ZodNumber>;
|
|
105
|
+
maxBufferedInputBytes: z.ZodNumber;
|
|
106
|
+
maxBufferedOutputBytes: z.ZodNumber;
|
|
107
|
+
highWaterMarkBytes: z.ZodNumber;
|
|
108
|
+
}, z.core.$strict>;
|
|
109
|
+
}, z.core.$strict>;
|
|
110
|
+
}, z.core.$strict>;
|
|
111
|
+
export declare const supportSchema: z.ZodObject<{
|
|
112
|
+
backendId: z.ZodString;
|
|
113
|
+
backendRevision: z.ZodString;
|
|
114
|
+
networkModes: z.ZodArray<z.ZodEnum<{
|
|
115
|
+
isolated: "isolated";
|
|
116
|
+
outbound: "outbound";
|
|
117
|
+
}>>;
|
|
118
|
+
workspaceDiskQuota: z.ZodLiteral<false>;
|
|
119
|
+
networkAllowlist: z.ZodLiteral<false>;
|
|
120
|
+
independentProcessTermination: z.ZodLiteral<false>;
|
|
121
|
+
}, z.core.$strict>;
|
|
122
|
+
export declare const effectiveSchema: z.ZodObject<{
|
|
123
|
+
backendIdentity: z.ZodString;
|
|
124
|
+
isolation: z.ZodEnum<{
|
|
125
|
+
container: "container";
|
|
126
|
+
virtual_machine: "virtual_machine";
|
|
127
|
+
}>;
|
|
128
|
+
resources: z.ZodObject<{
|
|
129
|
+
cpus: z.ZodNumber;
|
|
130
|
+
memoryBytes: z.ZodNumber;
|
|
131
|
+
pids: z.ZodNumber;
|
|
132
|
+
}, z.core.$strict>;
|
|
133
|
+
temporaryStorage: z.ZodObject<{
|
|
134
|
+
homeBytes: z.ZodNumber;
|
|
135
|
+
tempBytes: z.ZodNumber;
|
|
136
|
+
}, z.core.$strict>;
|
|
137
|
+
network: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
138
|
+
kind: z.ZodLiteral<"isolated">;
|
|
139
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
140
|
+
kind: z.ZodLiteral<"outbound">;
|
|
141
|
+
}, z.core.$strict>], "kind">;
|
|
142
|
+
nonRoot: z.ZodLiteral<true>;
|
|
143
|
+
readOnlyRoot: z.ZodLiteral<true>;
|
|
144
|
+
workspaceDiskQuota: z.ZodObject<{
|
|
145
|
+
kind: z.ZodLiteral<"not_requested">;
|
|
146
|
+
}, z.core.$strict>;
|
|
147
|
+
}, z.core.$strict>;
|
|
148
|
+
//# sourceMappingURL=schemas.d.ts.map
|