@sandboxxjs/core 0.5.0 → 2.0.0

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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/CHANGELOG.md +17 -0
  3. package/dist/allocator.d.ts +44 -0
  4. package/dist/allocator.d.ts.map +1 -0
  5. package/dist/allocator.js +14 -0
  6. package/dist/allocator.js.map +1 -0
  7. package/dist/client.d.ts +50 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +21 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/create-client.d.ts +11 -0
  12. package/dist/create-client.d.ts.map +1 -0
  13. package/dist/create-client.js +159 -0
  14. package/dist/create-client.js.map +1 -0
  15. package/dist/index.d.ts +25 -326
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +20 -16858
  18. package/dist/index.js.map +1 -295
  19. package/dist/protocol.d.ts +96 -0
  20. package/dist/protocol.d.ts.map +1 -0
  21. package/dist/protocol.js +15 -0
  22. package/dist/protocol.js.map +1 -0
  23. package/dist/provider.d.ts +54 -0
  24. package/dist/provider.d.ts.map +1 -0
  25. package/dist/provider.js +14 -0
  26. package/dist/provider.js.map +1 -0
  27. package/dist/registry.d.ts +28 -0
  28. package/dist/registry.d.ts.map +1 -0
  29. package/dist/registry.js +19 -0
  30. package/dist/registry.js.map +1 -0
  31. package/dist/router.d.ts +24 -0
  32. package/dist/router.d.ts.map +1 -0
  33. package/dist/router.js +18 -0
  34. package/dist/router.js.map +1 -0
  35. package/dist/sandbox.d.ts +54 -0
  36. package/dist/sandbox.d.ts.map +1 -0
  37. package/dist/sandbox.js +15 -0
  38. package/dist/sandbox.js.map +1 -0
  39. package/package.json +10 -35
  40. package/src/allocator.ts +48 -0
  41. package/src/client.ts +51 -0
  42. package/src/create-client.ts +187 -0
  43. package/src/index.ts +45 -0
  44. package/src/protocol.ts +133 -0
  45. package/src/provider.ts +54 -0
  46. package/src/registry.ts +29 -0
  47. package/src/router.ts +25 -0
  48. package/src/sandbox.ts +52 -0
  49. package/tsconfig.json +12 -0
  50. package/README.md +0 -60
@@ -0,0 +1,187 @@
1
+ /**
2
+ * createSandboxClient — the runtime implementation of SandboxClient.
3
+ *
4
+ * Platform-agnostic: uses standard WebSocket API (available in Node 22+, browsers).
5
+ * Platform differences are injected via SandboxProvider, which supplies
6
+ * Executor, FileSystem, and ProcessManager components.
7
+ */
8
+
9
+ import type { SandboxClient, SandboxClientOptions } from "./client";
10
+ import type {
11
+ ClientMessage,
12
+ ErrorMessage,
13
+ FsResultMessage,
14
+ ResultMessage,
15
+ ServiceMessage,
16
+ } from "./protocol";
17
+ import type { SandboxProvider } from "./provider";
18
+
19
+ export function createSandboxClient(provider: SandboxProvider): SandboxClient {
20
+ const executor = provider.createExecutor();
21
+ const fs = provider.createFileSystem();
22
+ const pm = provider.createProcessManager();
23
+
24
+ let ws: WebSocket | null = null;
25
+ let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
26
+ let isConnected = false;
27
+
28
+ async function connect(options: SandboxClientOptions): Promise<void> {
29
+ const { wsUrl, sandboxId, token, heartbeatInterval = 30_000 } = options;
30
+
31
+ return new Promise((resolve, reject) => {
32
+ ws = new WebSocket(wsUrl);
33
+
34
+ ws.onopen = () => {
35
+ // Step 3: Register
36
+ const registerMsg: ClientMessage = { type: "register", sandboxId, token };
37
+ ws!.send(JSON.stringify(registerMsg));
38
+ };
39
+
40
+ ws.onmessage = (event) => {
41
+ const data = typeof event.data === "string" ? event.data : "";
42
+ let msg: ServiceMessage;
43
+ try {
44
+ msg = JSON.parse(data);
45
+ } catch {
46
+ return;
47
+ }
48
+
49
+ if (msg.type === "registered") {
50
+ // Step 4: Ready
51
+ isConnected = true;
52
+ heartbeatTimer = setInterval(() => {
53
+ if (ws?.readyState === WebSocket.OPEN) {
54
+ const hb: ClientMessage = { type: "heartbeat" };
55
+ ws.send(JSON.stringify(hb));
56
+ }
57
+ }, heartbeatInterval);
58
+ resolve();
59
+ return;
60
+ }
61
+
62
+ // Step 5: Command — handle and respond
63
+ handleCommand(msg);
64
+ };
65
+
66
+ ws.onerror = () => {
67
+ if (!isConnected) reject(new Error("WebSocket connection failed"));
68
+ };
69
+
70
+ ws.onclose = () => {
71
+ cleanup();
72
+ };
73
+ });
74
+ }
75
+
76
+ async function handleCommand(msg: ServiceMessage): Promise<void> {
77
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
78
+
79
+ try {
80
+ switch (msg.type) {
81
+ // Executor
82
+ case "exec": {
83
+ const result = await executor.exec(msg.command, {
84
+ cwd: msg.cwd,
85
+ timeout: msg.timeout,
86
+ });
87
+ const reply: ResultMessage = {
88
+ type: "result",
89
+ id: msg.id,
90
+ stdout: result.stdout,
91
+ stderr: result.stderr,
92
+ exitCode: result.exitCode,
93
+ };
94
+ ws.send(JSON.stringify(reply));
95
+ break;
96
+ }
97
+
98
+ // FileSystem
99
+ case "fs.read": {
100
+ const content = await fs.readFile(msg.path);
101
+ sendFsResult(msg.id, { content });
102
+ break;
103
+ }
104
+
105
+ case "fs.write": {
106
+ await fs.writeFile(msg.path, msg.content);
107
+ sendFsResult(msg.id, { written: true });
108
+ break;
109
+ }
110
+
111
+ case "fs.list": {
112
+ const files = await fs.listFiles(msg.path);
113
+ sendFsResult(msg.id, files);
114
+ break;
115
+ }
116
+
117
+ case "fs.mkdir": {
118
+ await fs.mkdir(msg.path, { recursive: msg.recursive });
119
+ sendFsResult(msg.id, { created: true });
120
+ break;
121
+ }
122
+
123
+ case "fs.delete": {
124
+ await fs.deleteFile(msg.path);
125
+ sendFsResult(msg.id, { deleted: true });
126
+ break;
127
+ }
128
+
129
+ // ProcessManager
130
+ case "process.start": {
131
+ const proc = await pm.start(msg.command, { cwd: msg.cwd });
132
+ sendFsResult(msg.id, proc);
133
+ break;
134
+ }
135
+
136
+ case "process.kill": {
137
+ await pm.kill(msg.processId);
138
+ sendFsResult(msg.id, { killed: true });
139
+ break;
140
+ }
141
+
142
+ case "process.list": {
143
+ const procs = await pm.list();
144
+ sendFsResult(msg.id, procs);
145
+ break;
146
+ }
147
+ }
148
+ } catch (err) {
149
+ const errorMsg: ErrorMessage = {
150
+ type: "error",
151
+ id: (msg as { id?: string }).id || "unknown",
152
+ message: err instanceof Error ? err.message : "Command failed",
153
+ };
154
+ ws.send(JSON.stringify(errorMsg));
155
+ }
156
+ }
157
+
158
+ function sendFsResult(id: string, data: unknown): void {
159
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
160
+ const reply: FsResultMessage = { type: "fs.result", id, data };
161
+ ws.send(JSON.stringify(reply));
162
+ }
163
+
164
+ function cleanup(): void {
165
+ isConnected = false;
166
+ if (heartbeatTimer) {
167
+ clearInterval(heartbeatTimer);
168
+ heartbeatTimer = null;
169
+ }
170
+ ws = null;
171
+ }
172
+
173
+ async function disconnect(): Promise<void> {
174
+ if (ws) {
175
+ ws.close();
176
+ cleanup();
177
+ }
178
+ }
179
+
180
+ return {
181
+ connect,
182
+ disconnect,
183
+ get connected() {
184
+ return isConnected;
185
+ },
186
+ };
187
+ }
package/src/index.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @sandboxxjs/core — Sandbox abstraction framework.
3
+ *
4
+ * Unified lifecycle for any sandbox environment:
5
+ *
6
+ * Allocate → Prepare → Register → Ready → Command
7
+ *
8
+ * 1. Allocator provisions resources, returns SandboxContainer (status: pending)
9
+ * 2. Sandbox environment starts, sandbox-client prepares
10
+ * 3. sandbox-client connects to Registry via WebSocket, registers
11
+ * 4. Registry marks sandbox as ready
12
+ * 5. Router dispatches commands through Registry to sandbox-client
13
+ *
14
+ * Platform differences are injected via SandboxProvider:
15
+ * - @sandboxxjs/node-provider: child_process + node:fs
16
+ * - @sandboxxjs/web-provider: @webcontainer/api
17
+ * - Future: Docker, SSH, WASM, etc.
18
+ */
19
+
20
+ // Allocator — Step 1: Allocate
21
+ export type {
22
+ AllocateRequest,
23
+ SandboxAllocator,
24
+ SandboxContainer,
25
+ SandboxContainerType,
26
+ SandboxStatus,
27
+ } from "./allocator";
28
+
29
+ // Client — Step 2-3: Prepare + Register
30
+ export type { SandboxClient, SandboxClientOptions } from "./client";
31
+ export { createSandboxClient } from "./create-client";
32
+ // Provider — Platform capability injection
33
+ export type {
34
+ SandboxExecutor,
35
+ SandboxFileSystem,
36
+ SandboxProcessManager,
37
+ SandboxProvider,
38
+ } from "./provider";
39
+ // Registry — Step 3-4: Register + Ready
40
+ export type { SandboxConnection, SandboxRegistry } from "./registry";
41
+ // Router — Step 5: Command (RPC dispatch)
42
+ export type { SandboxRouter } from "./router";
43
+
44
+ // Sandbox — Step 5: Command (consumer-facing)
45
+ export type { ExecOptions, ExecResult, FileInfo, ProcessInfo, Sandbox } from "./sandbox";
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Sandbox protocol — WebSocket message types between sandbox-client and service.
3
+ *
4
+ * This protocol covers lifecycle steps 3-5:
5
+ * Register: client sends RegisterMessage, service responds RegisteredMessage
6
+ * Ready: heartbeat keeps connection alive
7
+ * Command: service sends commands, client returns results
8
+ *
9
+ * All sandbox-clients (cloud, web, future types) speak this same protocol.
10
+ *
11
+ * Lifecycle: Allocate → Prepare → Register → Ready → Command
12
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
13
+ */
14
+
15
+ // ==================== Client → Service ====================
16
+
17
+ export interface RegisterMessage {
18
+ type: "register";
19
+ sandboxId: string;
20
+ token: string;
21
+ }
22
+
23
+ export interface ResultMessage {
24
+ type: "result";
25
+ id: string;
26
+ stdout: string;
27
+ stderr: string;
28
+ exitCode: number;
29
+ }
30
+
31
+ export interface FsResultMessage {
32
+ type: "fs.result";
33
+ id: string;
34
+ data: unknown;
35
+ }
36
+
37
+ export interface ErrorMessage {
38
+ type: "error";
39
+ id: string;
40
+ message: string;
41
+ }
42
+
43
+ export interface HeartbeatMessage {
44
+ type: "heartbeat";
45
+ }
46
+
47
+ // ==================== Service → Client ====================
48
+
49
+ export interface ExecMessage {
50
+ type: "exec";
51
+ id: string;
52
+ command: string;
53
+ cwd?: string;
54
+ timeout?: number;
55
+ }
56
+
57
+ export interface FsReadMessage {
58
+ type: "fs.read";
59
+ id: string;
60
+ path: string;
61
+ }
62
+
63
+ export interface FsWriteMessage {
64
+ type: "fs.write";
65
+ id: string;
66
+ path: string;
67
+ content: string;
68
+ }
69
+
70
+ export interface FsListMessage {
71
+ type: "fs.list";
72
+ id: string;
73
+ path: string;
74
+ }
75
+
76
+ export interface FsMkdirMessage {
77
+ type: "fs.mkdir";
78
+ id: string;
79
+ path: string;
80
+ recursive?: boolean;
81
+ }
82
+
83
+ export interface FsDeleteMessage {
84
+ type: "fs.delete";
85
+ id: string;
86
+ path: string;
87
+ }
88
+
89
+ export interface ProcessStartMessage {
90
+ type: "process.start";
91
+ id: string;
92
+ command: string;
93
+ cwd?: string;
94
+ }
95
+
96
+ export interface ProcessKillMessage {
97
+ type: "process.kill";
98
+ id: string;
99
+ processId: string;
100
+ }
101
+
102
+ export interface ProcessListMessage {
103
+ type: "process.list";
104
+ id: string;
105
+ }
106
+
107
+ export interface RegisteredMessage {
108
+ type: "registered";
109
+ sandboxId: string;
110
+ }
111
+
112
+ // ==================== Union types ====================
113
+
114
+ /** Messages sent from sandbox-client to service */
115
+ export type ClientMessage =
116
+ | RegisterMessage
117
+ | ResultMessage
118
+ | FsResultMessage
119
+ | ErrorMessage
120
+ | HeartbeatMessage;
121
+
122
+ /** Messages sent from service to sandbox-client */
123
+ export type ServiceMessage =
124
+ | ExecMessage
125
+ | FsReadMessage
126
+ | FsWriteMessage
127
+ | FsListMessage
128
+ | FsMkdirMessage
129
+ | FsDeleteMessage
130
+ | ProcessStartMessage
131
+ | ProcessKillMessage
132
+ | ProcessListMessage
133
+ | RegisteredMessage;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * SandboxProvider — platform capability injection.
3
+ *
4
+ * Provider is a component factory that supplies platform-specific
5
+ * implementations of Executor, FileSystem, and ProcessManager.
6
+ *
7
+ * The core layer (createSandboxClient) only depends on this interface.
8
+ * Platform differences are isolated in provider implementations:
9
+ * - node-provider: child_process + node:fs
10
+ * - web-provider: @webcontainer/api
11
+ * - Future: Docker, SSH, etc.
12
+ */
13
+
14
+ import type { ExecOptions, ExecResult, FileInfo, ProcessInfo } from "./sandbox";
15
+
16
+ /**
17
+ * Command execution component.
18
+ */
19
+ export interface SandboxExecutor {
20
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
21
+ }
22
+
23
+ /**
24
+ * File system component.
25
+ */
26
+ export interface SandboxFileSystem {
27
+ readFile(path: string): Promise<string>;
28
+ writeFile(path: string, content: string): Promise<void>;
29
+ listFiles(path: string): Promise<FileInfo[]>;
30
+ mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;
31
+ deleteFile(path: string): Promise<void>;
32
+ }
33
+
34
+ /**
35
+ * Process management component.
36
+ */
37
+ export interface SandboxProcessManager {
38
+ start(command: string, options?: { cwd?: string }): Promise<ProcessInfo>;
39
+ kill(processId: string): Promise<void>;
40
+ list(): Promise<ProcessInfo[]>;
41
+ }
42
+
43
+ /**
44
+ * SandboxProvider — the component factory.
45
+ *
46
+ * Each platform implements this interface to provide its components.
47
+ * createSandboxClient(provider) obtains components and dispatches
48
+ * incoming messages to the appropriate component.
49
+ */
50
+ export interface SandboxProvider {
51
+ createExecutor(): SandboxExecutor;
52
+ createFileSystem(): SandboxFileSystem;
53
+ createProcessManager(): SandboxProcessManager;
54
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * SandboxRegistry — accepts sandbox-client connections and manages lifecycle.
3
+ *
4
+ * Step 3-4 of the lifecycle: Register → Ready.
5
+ *
6
+ * When a sandbox-client connects via WebSocket and sends a register message,
7
+ * the registry:
8
+ * 1. Validates the token
9
+ * 2. Binds the WebSocket connection to the sandboxId
10
+ * 3. Updates the sandbox status from "pending" to "ready"
11
+ * 4. Provides the command routing channel
12
+ *
13
+ * The registry also handles disconnection, heartbeat, and reconnection.
14
+ *
15
+ * Lifecycle: Allocate → Prepare → Register → Ready → Command
16
+ * ^^^^^^^^^^^^^^^^^
17
+ */
18
+
19
+ export interface SandboxConnection {
20
+ sandboxId: string;
21
+ connectedAt: number;
22
+ }
23
+
24
+ export interface SandboxRegistry {
25
+ /** Check if a sandbox-client is connected and ready */
26
+ has(sandboxId: string): boolean;
27
+ /** List all active connections */
28
+ connections(): SandboxConnection[];
29
+ }
package/src/router.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * SandboxRouter — routes RPC methods to sandbox operations.
3
+ *
4
+ * Step 5 of the lifecycle: Command.
5
+ *
6
+ * The router is the external-facing RPC interface.
7
+ * It combines the allocator (lifecycle) and registry (connections)
8
+ * to provide a unified dispatch: method + params → result.
9
+ *
10
+ * Internally, it resolves the sandboxId from params, finds the
11
+ * connected sandbox-client through the registry, and forwards
12
+ * the command over WebSocket.
13
+ *
14
+ * Lifecycle: Allocate → Prepare → Register → Ready → Command
15
+ * ^^^^^^^
16
+ */
17
+
18
+ import type { Sandbox } from "./sandbox";
19
+
20
+ export interface SandboxRouter {
21
+ /** Get a Sandbox handle by id — routes through registry to connected client */
22
+ getSandbox(sandboxId: string): Sandbox;
23
+ /** Dispatch an RPC method with params */
24
+ dispatch(method: string, params: Record<string, unknown>): Promise<unknown>;
25
+ }
package/src/sandbox.ts ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Sandbox — the unified interface for sandbox operations.
3
+ *
4
+ * Step 5 of the lifecycle: Command.
5
+ *
6
+ * Consumers get a Sandbox instance and operate on it.
7
+ * They never need to know if it's a cloud container, a browser WebContainer,
8
+ * or any other sandbox type. All commands are routed through the registry
9
+ * to the connected sandbox-client.
10
+ *
11
+ * Lifecycle: Allocate → Prepare → Register → Ready → Command
12
+ * ^^^^^^^
13
+ */
14
+
15
+ export interface ExecOptions {
16
+ cwd?: string;
17
+ timeout?: number;
18
+ }
19
+
20
+ export interface ExecResult {
21
+ stdout: string;
22
+ stderr: string;
23
+ exitCode: number;
24
+ success: boolean;
25
+ }
26
+
27
+ export interface ProcessInfo {
28
+ id: string;
29
+ pid?: number;
30
+ command: string;
31
+ status: string;
32
+ }
33
+
34
+ export interface FileInfo {
35
+ name: string;
36
+ type: "file" | "directory" | "symlink";
37
+ size?: number;
38
+ }
39
+
40
+ export interface Sandbox {
41
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
42
+ startProcess(command: string, options?: { cwd?: string }): Promise<ProcessInfo>;
43
+ killProcess(processId: string): Promise<void>;
44
+ listProcesses(): Promise<ProcessInfo[]>;
45
+ readFile(path: string): Promise<string>;
46
+ writeFile(path: string, content: string): Promise<void>;
47
+ listFiles(path: string): Promise<FileInfo[]>;
48
+ mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;
49
+ deleteFile(path: string): Promise<void>;
50
+ exposePort(port: number, hostname: string): Promise<{ url: string }>;
51
+ destroy(): Promise<void>;
52
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules", "dist"]
12
+ }
package/README.md DELETED
@@ -1,60 +0,0 @@
1
- # @@sandboxxjs/sandbox/core
2
-
3
- Core implementation for SandboX - secure code execution engine.
4
-
5
- ## Features
6
-
7
- - Multiple isolators (Local, Cloudflare, E2B, Firecracker)
8
- - Multiple runtimes (Node.js, Python, Bash, Docker)
9
- - File system operations
10
- - Resource limits
11
- - Event-driven architecture
12
-
13
- ## Installation
14
-
15
- ```bash
16
- npm install @@sandboxxjs/sandbox/core
17
- ```
18
-
19
- ## Usage
20
-
21
- ```typescript
22
- import { Sandbox, LocalIsolator } from "@@sandboxxjs/sandbox/core";
23
-
24
- const isolator = new LocalIsolator("node");
25
- const sandbox = new Sandbox({
26
- runtime: "node",
27
- isolator: "local",
28
- });
29
-
30
- const result = await sandbox.execute({
31
- code: 'console.log("Hello")',
32
- });
33
-
34
- console.log(result.stdout); // "Hello"
35
- ```
36
-
37
- ## API
38
-
39
- ### Sandbox
40
-
41
- ```typescript
42
- class Sandbox {
43
- execute(options: ExecuteOptions): Promise<ExecuteResult>;
44
- writeFile(path: string, data: string): Promise<void>;
45
- readFile(path: string): Promise<string>;
46
- destroy(): Promise<void>;
47
- on(event: string, handler: Function): void;
48
- fs: FileSystem;
49
- }
50
- ```
51
-
52
- ### Isolators
53
-
54
- - `LocalIsolator` - Process isolation via execa
55
- - `CloudflareContainerIsolator` - Docker via Bun binary
56
- - `E2BIsolator` - E2B microVM (not yet implemented)
57
-
58
- ## License
59
-
60
- MIT