@ebowwa/sandbox 0.1.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/dist/compilers/index.d.ts +24 -0
- package/dist/compilers/index.d.ts.map +1 -0
- package/dist/compilers/index.js +42 -0
- package/dist/compilers/index.js.map +1 -0
- package/dist/compilers/javascript.d.ts +117 -0
- package/dist/compilers/javascript.d.ts.map +1 -0
- package/dist/compilers/javascript.js +462 -0
- package/dist/compilers/javascript.js.map +1 -0
- package/dist/compilers/python.d.ts +140 -0
- package/dist/compilers/python.d.ts.map +1 -0
- package/dist/compilers/python.js +650 -0
- package/dist/compilers/python.js.map +1 -0
- package/dist/compilers/typescript.d.ts +99 -0
- package/dist/compilers/typescript.d.ts.map +1 -0
- package/dist/compilers/typescript.js +323 -0
- package/dist/compilers/typescript.js.map +1 -0
- package/dist/core/cell.d.ts +160 -0
- package/dist/core/cell.d.ts.map +1 -0
- package/dist/core/cell.js +319 -0
- package/dist/core/cell.js.map +1 -0
- package/dist/core/compiler.d.ts +126 -0
- package/dist/core/compiler.d.ts.map +1 -0
- package/dist/core/compiler.js +123 -0
- package/dist/core/compiler.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/limits.d.ts +173 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +440 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/permissions.d.ts +103 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +341 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/runtime.d.ts +127 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +325 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/types.d.ts +380 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +67 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +279 -0
- package/dist/index.js.map +1 -0
- package/dist/multi/index.d.ts +9 -0
- package/dist/multi/index.d.ts.map +1 -0
- package/dist/multi/index.js +7 -0
- package/dist/multi/index.js.map +1 -0
- package/dist/multi/polyglot.d.ts +179 -0
- package/dist/multi/polyglot.d.ts.map +1 -0
- package/dist/multi/polyglot.js +319 -0
- package/dist/multi/polyglot.js.map +1 -0
- package/dist/runtimes/docker.d.ts +97 -0
- package/dist/runtimes/docker.d.ts.map +1 -0
- package/dist/runtimes/docker.js +368 -0
- package/dist/runtimes/docker.js.map +1 -0
- package/dist/runtimes/index.d.ts +11 -0
- package/dist/runtimes/index.d.ts.map +1 -0
- package/dist/runtimes/index.js +9 -0
- package/dist/runtimes/index.js.map +1 -0
- package/dist/runtimes/process.d.ts +47 -0
- package/dist/runtimes/process.d.ts.map +1 -0
- package/dist/runtimes/process.js +230 -0
- package/dist/runtimes/process.js.map +1 -0
- package/dist/session/index.d.ts +12 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +9 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/kernel.d.ts +199 -0
- package/dist/session/kernel.d.ts.map +1 -0
- package/dist/session/kernel.js +400 -0
- package/dist/session/kernel.js.map +1 -0
- package/dist/session/notebook.d.ts +168 -0
- package/dist/session/notebook.d.ts.map +1 -0
- package/dist/session/notebook.js +499 -0
- package/dist/session/notebook.js.map +1 -0
- package/dist/session/repl.d.ts +159 -0
- package/dist/session/repl.d.ts.map +1 -0
- package/dist/session/repl.js +409 -0
- package/dist/session/repl.js.map +1 -0
- package/package.json +142 -0
- package/src/compilers/index.ts +80 -0
- package/src/compilers/javascript.ts +571 -0
- package/src/compilers/python.ts +785 -0
- package/src/compilers/typescript.ts +442 -0
- package/src/core/cell.ts +439 -0
- package/src/core/compiler.ts +250 -0
- package/src/core/index.ts +123 -0
- package/src/core/limits.ts +508 -0
- package/src/core/permissions.ts +409 -0
- package/src/core/runtime.ts +499 -0
- package/src/core/types.ts +528 -0
- package/src/global.d.ts +59 -0
- package/src/index.ts +515 -0
- package/src/multi/index.ts +22 -0
- package/src/multi/polyglot.ts +461 -0
- package/src/runtimes/docker.ts +501 -0
- package/src/runtimes/index.ts +21 -0
- package/src/runtimes/process.ts +316 -0
- package/src/session/index.ts +41 -0
- package/src/session/kernel.ts +553 -0
- package/src/session/notebook.ts +635 -0
- package/src/session/repl.ts +521 -0
- package/src/wasm2wasm.d.ts +35 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Runtime
|
|
3
|
+
*
|
|
4
|
+
* Spawns child processes for code execution with elevated permissions.
|
|
5
|
+
* Supports filesystem and network access with configurable limits.
|
|
6
|
+
*
|
|
7
|
+
* Isolation level: 'process'
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
11
|
+
import { setTimeout as setTimeoutPromise } from "node:timers/promises";
|
|
12
|
+
import type {
|
|
13
|
+
ExecutionResult,
|
|
14
|
+
Permissions,
|
|
15
|
+
Limits,
|
|
16
|
+
DisplayOutput,
|
|
17
|
+
} from "../core/types.js";
|
|
18
|
+
import type {
|
|
19
|
+
IRuntime,
|
|
20
|
+
RuntimeOptions,
|
|
21
|
+
ExecutionRequest,
|
|
22
|
+
RuntimeCapabilities,
|
|
23
|
+
} from "../core/runtime.js";
|
|
24
|
+
|
|
25
|
+
/** Process runtime configuration */
|
|
26
|
+
export interface ProcessRuntimeConfig {
|
|
27
|
+
/** Default shell to use */
|
|
28
|
+
shell?: string;
|
|
29
|
+
/** Timeout for availability check */
|
|
30
|
+
availabilityTimeout?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Process Runtime
|
|
35
|
+
*
|
|
36
|
+
* Executes code in child processes with configurable permissions.
|
|
37
|
+
* Provides filesystem and network access based on permission level.
|
|
38
|
+
*/
|
|
39
|
+
export class ProcessRuntime implements IRuntime {
|
|
40
|
+
readonly name = "process";
|
|
41
|
+
readonly capabilities: RuntimeCapabilities = {
|
|
42
|
+
isolation: "process",
|
|
43
|
+
stateful: true,
|
|
44
|
+
async: true,
|
|
45
|
+
filesystem: true,
|
|
46
|
+
network: true,
|
|
47
|
+
maxMemory: 4 * 1024 * 1024 * 1024, // 4GB
|
|
48
|
+
wasi: false,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
private executing = false;
|
|
52
|
+
private config: ProcessRuntimeConfig;
|
|
53
|
+
|
|
54
|
+
constructor(config: ProcessRuntimeConfig = {}) {
|
|
55
|
+
this.config = {
|
|
56
|
+
shell: config.shell ?? "/bin/sh",
|
|
57
|
+
availabilityTimeout: config.availabilityTimeout ?? 5000,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async init(): Promise<void> {
|
|
62
|
+
// No special initialization needed for process runtime
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async isAvailable(): Promise<boolean> {
|
|
66
|
+
// Process spawning is always available in Node.js
|
|
67
|
+
return typeof process !== "undefined" && typeof spawn === "function";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async execute(
|
|
71
|
+
request: ExecutionRequest,
|
|
72
|
+
options: RuntimeOptions
|
|
73
|
+
): Promise<ExecutionResult> {
|
|
74
|
+
const startTime = Date.now();
|
|
75
|
+
this.executing = true;
|
|
76
|
+
|
|
77
|
+
const stdout: string[] = [];
|
|
78
|
+
const stderr: string[] = [];
|
|
79
|
+
const displays: DisplayOutput[] = [];
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Parse limits
|
|
83
|
+
const timeout = this.parseTimeout(options.limits?.timeout);
|
|
84
|
+
const maxMemory = this.parseMemory(options.limits?.memory);
|
|
85
|
+
|
|
86
|
+
// Build execution command based on code type
|
|
87
|
+
const { command, args } = this.buildCommand(request, options);
|
|
88
|
+
|
|
89
|
+
// Create abort controller for timeout
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
92
|
+
|
|
93
|
+
// Combine with external signal if provided
|
|
94
|
+
if (options.signal) {
|
|
95
|
+
options.signal.addEventListener("abort", () => {
|
|
96
|
+
controller.abort();
|
|
97
|
+
clearTimeout(timeoutId);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Spawn child process
|
|
102
|
+
const childProcess = spawn(command, args, {
|
|
103
|
+
cwd: options.cwd ?? process.cwd(),
|
|
104
|
+
env: {
|
|
105
|
+
...process.env,
|
|
106
|
+
...options.env,
|
|
107
|
+
NODE_OPTIONS: `--max-old-space-size=${Math.floor(maxMemory / (1024 * 1024))}`,
|
|
108
|
+
},
|
|
109
|
+
signal: controller.signal,
|
|
110
|
+
shell: false,
|
|
111
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Collect stdout
|
|
115
|
+
childProcess.stdout?.on("data", (data: Buffer) => {
|
|
116
|
+
stdout.push(data.toString("utf8"));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Collect stderr
|
|
120
|
+
childProcess.stderr?.on("data", (data: Buffer) => {
|
|
121
|
+
stderr.push(data.toString("utf8"));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Handle process events
|
|
125
|
+
const result = await new Promise<ExecutionResult>((resolve) => {
|
|
126
|
+
let returnValue: unknown = undefined;
|
|
127
|
+
|
|
128
|
+
childProcess.on("error", (error: Error) => {
|
|
129
|
+
clearTimeout(timeoutId);
|
|
130
|
+
resolve(
|
|
131
|
+
this.createErrorResult(
|
|
132
|
+
`Process error: ${error.message}`,
|
|
133
|
+
"runtime",
|
|
134
|
+
startTime,
|
|
135
|
+
stdout,
|
|
136
|
+
stderr
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
childProcess.on("close", (code: number, signal: NodeJS.Signals | null) => {
|
|
142
|
+
clearTimeout(timeoutId);
|
|
143
|
+
|
|
144
|
+
if (signal) {
|
|
145
|
+
resolve(
|
|
146
|
+
this.createErrorResult(
|
|
147
|
+
`Process killed by signal: ${signal}`,
|
|
148
|
+
signal === "SIGTERM" ? "timeout" : "cancel",
|
|
149
|
+
startTime,
|
|
150
|
+
stdout,
|
|
151
|
+
stderr
|
|
152
|
+
)
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Try to parse stdout as JSON for return value
|
|
158
|
+
const output = stdout.join("");
|
|
159
|
+
try {
|
|
160
|
+
const parsed = JSON.parse(output);
|
|
161
|
+
if (parsed.__return_value !== undefined) {
|
|
162
|
+
returnValue = parsed.__return_value;
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
// Not JSON, use raw output as value
|
|
166
|
+
returnValue = output.trim() || undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
resolve({
|
|
170
|
+
success: code === 0,
|
|
171
|
+
value: returnValue,
|
|
172
|
+
metrics: {
|
|
173
|
+
duration: Date.now() - startTime,
|
|
174
|
+
memoryUsed: 0, // Not tracked at process level
|
|
175
|
+
},
|
|
176
|
+
output: { stdout, stderr, displays },
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Write input if provided
|
|
181
|
+
if (request.input !== undefined) {
|
|
182
|
+
const inputStr =
|
|
183
|
+
typeof request.input === "string"
|
|
184
|
+
? request.input
|
|
185
|
+
: JSON.stringify(request.input);
|
|
186
|
+
childProcess.stdin?.write(inputStr);
|
|
187
|
+
childProcess.stdin?.end();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return result;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
194
|
+
return this.createErrorResult(
|
|
195
|
+
"Execution timed out",
|
|
196
|
+
"timeout",
|
|
197
|
+
startTime,
|
|
198
|
+
stdout,
|
|
199
|
+
stderr
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return this.createErrorResult(
|
|
204
|
+
error instanceof Error ? error.message : String(error),
|
|
205
|
+
"runtime",
|
|
206
|
+
startTime,
|
|
207
|
+
stdout,
|
|
208
|
+
stderr
|
|
209
|
+
);
|
|
210
|
+
} finally {
|
|
211
|
+
this.executing = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async terminate(): Promise<void> {
|
|
216
|
+
this.executing = false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
isExecuting(): boolean {
|
|
220
|
+
return this.executing;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Build command and arguments for execution
|
|
225
|
+
*/
|
|
226
|
+
private buildCommand(
|
|
227
|
+
request: ExecutionRequest,
|
|
228
|
+
options: RuntimeOptions
|
|
229
|
+
): { command: string; args: string[] } {
|
|
230
|
+
// Execute WASM using node with WASI support
|
|
231
|
+
return {
|
|
232
|
+
command: "node",
|
|
233
|
+
args: [
|
|
234
|
+
"--experimental-wasi-unstable-preview1",
|
|
235
|
+
"-e",
|
|
236
|
+
this.generateWasmRunner(request.wasm.wasmBytes, options.permissions),
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Generate WASM runner script
|
|
243
|
+
*/
|
|
244
|
+
private generateWasmRunner(
|
|
245
|
+
wasmBytes: Uint8Array,
|
|
246
|
+
permissions: Permissions
|
|
247
|
+
): string {
|
|
248
|
+
const hasFs = permissions.fs?.read || permissions.fs?.write;
|
|
249
|
+
const hasNetwork = permissions.network?.outbound || permissions.network?.inbound;
|
|
250
|
+
|
|
251
|
+
return `
|
|
252
|
+
const { WASI } = require('wasi');
|
|
253
|
+
const { readFile } = require('fs/promises');
|
|
254
|
+
|
|
255
|
+
async function run() {
|
|
256
|
+
const wasmBuffer = Buffer.from([${Array.from(wasmBytes).join(",")}]);
|
|
257
|
+
|
|
258
|
+
const wasi = new WASI({
|
|
259
|
+
version: 'preview1',
|
|
260
|
+
preopens: ${hasFs ? "{ '/': process.cwd() }" : "{}"},
|
|
261
|
+
env: process.env,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const { instance } = await WebAssembly.instantiate(wasmBuffer, {
|
|
265
|
+
wasi_snapshot_preview1: wasi.wasiImport,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
wasi.start(instance);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
run().catch(console.error);
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private createErrorResult(
|
|
276
|
+
message: string,
|
|
277
|
+
type: "compile" | "runtime" | "permission" | "limit" | "timeout" | "cancel",
|
|
278
|
+
startTime: number,
|
|
279
|
+
stdout: string[] = [],
|
|
280
|
+
stderr: string[] = []
|
|
281
|
+
): ExecutionResult {
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: { message, type },
|
|
285
|
+
metrics: {
|
|
286
|
+
duration: Date.now() - startTime,
|
|
287
|
+
memoryUsed: 0,
|
|
288
|
+
},
|
|
289
|
+
output: { stdout, stderr, displays: [] },
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private parseMemory(memory: string | number | undefined): number {
|
|
294
|
+
if (!memory) return 256 * 1024 * 1024; // 256MB default
|
|
295
|
+
if (typeof memory === "number") return memory;
|
|
296
|
+
const match = memory.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/i);
|
|
297
|
+
if (!match) return 256 * 1024 * 1024;
|
|
298
|
+
const [, num, unit] = match;
|
|
299
|
+
const multipliers: Record<string, number> = {
|
|
300
|
+
b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3,
|
|
301
|
+
};
|
|
302
|
+
return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "b"] ?? 1));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private parseTimeout(timeout: string | number | undefined): number {
|
|
306
|
+
if (!timeout) return 60000; // 60s default for process
|
|
307
|
+
if (typeof timeout === "number") return timeout;
|
|
308
|
+
const match = timeout.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m)?$/i);
|
|
309
|
+
if (!match) return 60000;
|
|
310
|
+
const [, num, unit] = match;
|
|
311
|
+
const multipliers: Record<string, number> = {
|
|
312
|
+
ms: 1, s: 1000, m: 60000,
|
|
313
|
+
};
|
|
314
|
+
return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "ms"] ?? 1));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Primitives
|
|
3
|
+
*
|
|
4
|
+
* Stateful execution contexts for interactive use.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Kernel - Language-specific execution session
|
|
8
|
+
export type {
|
|
9
|
+
KernelState,
|
|
10
|
+
KernelOptions,
|
|
11
|
+
KernelStats,
|
|
12
|
+
} from "./kernel.js";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
BaseKernel,
|
|
16
|
+
KernelManager,
|
|
17
|
+
} from "./kernel.js";
|
|
18
|
+
|
|
19
|
+
// Notebook - Multi-cell document execution
|
|
20
|
+
export type {
|
|
21
|
+
NotebookOptions,
|
|
22
|
+
NotebookState,
|
|
23
|
+
NotebookEvents,
|
|
24
|
+
} from "./notebook.js";
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
BaseNotebook,
|
|
28
|
+
createNotebook,
|
|
29
|
+
} from "./notebook.js";
|
|
30
|
+
|
|
31
|
+
// REPL - Interactive Read-Eval-Print-Loop
|
|
32
|
+
export type {
|
|
33
|
+
REPLOptions,
|
|
34
|
+
REPLHistoryEntry,
|
|
35
|
+
REPLState,
|
|
36
|
+
} from "./repl.js";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
BaseREPL,
|
|
40
|
+
createREPL,
|
|
41
|
+
} from "./repl.js";
|