-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsandbox_executor.js
More file actions
128 lines (114 loc) · 3.81 KB
/
sandbox_executor.js
File metadata and controls
128 lines (114 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* Guest-side executor that runs inside the Wasm component.
*
* Implements hyperlight:sandbox/executor — receives code strings,
* evaluates them, and returns captured output.
*
* File I/O uses WASI filesystem (wasi:filesystem/preopens + types).
* HTTP uses the standard fetch API (provided by ComponentizeJS).
* Tools use hyperlight:sandbox/tools WIT interface.
*
* Guest code has access to:
* call_tool(name, args) — call a host-registered tool
* read_file(path) — read text from /input/ or /output/
* write_file(path, data) — write text to /output/
* fetch(url, init) — standard Fetch API (WASI-HTTP)
* console.log(...) — captured to stdout
* console.error(...) — captured to stderr
*/
import { dispatch } from "hyperlight:sandbox/tools";
import { getDirectories } from "wasi:filesystem/preopens@0.2.0";
// ---------- output capture ----------
let stdoutBuf = "";
let stderrBuf = "";
const origLog = console.log;
const origError = console.error;
function captureLog(...args) {
stdoutBuf += args.map(String).join(" ") + "\n";
}
function captureError(...args) {
stderrBuf += args.map(String).join(" ") + "\n";
}
// ---------- host helpers ----------
/** Call a host-registered tool via WIT tools.dispatch. */
function callTool(toolName, args = {}) {
try {
return JSON.parse(dispatch(toolName, JSON.stringify(args)));
} catch (e) {
throw new Error(`Tool '${toolName}' failed: ${e.message || e}`);
}
}
// ---------- WASI filesystem ----------
let inputDir = null;
let outputDir = null;
function ensurePreopens() {
// Always re-resolve after snapshot/restore (handles may be stale)
inputDir = null;
outputDir = null;
for (const [desc, path] of getDirectories()) {
if (path === "/input") inputDir = desc;
if (path === "/output") outputDir = desc;
}
}
/** Read a text file via WASI filesystem. */
function readFile(path) {
ensurePreopens();
let name = path;
let dir = inputDir;
if (name.startsWith("/input/")) {
name = name.slice(7);
dir = inputDir;
} else if (name.startsWith("/output/")) {
name = name.slice(8);
dir = outputDir;
}
if (!dir) throw new Error("Preopened directory not found for: " + path);
const fd = dir.openAt({}, name, {}, { read: true });
const stat = fd.stat();
const stream = fd.readViaStream(0n);
const bytes = stream.blockingRead(stat.size);
return new TextDecoder().decode(bytes);
}
/** Write a text file to /output/ via WASI filesystem. */
function writeFile(path, data) {
ensurePreopens();
if (!outputDir) throw new Error("/output/ preopened directory not found");
let name = path;
if (name.startsWith("/output/")) name = name.slice(8);
const fd = outputDir.openAt({}, name, { create: true, truncate: true }, { write: true });
const stream = fd.writeViaStream(0n);
stream.blockingWriteAndFlush(new TextEncoder().encode(data));
}
// ---------- executor export ----------
export const executor = {
async run(code) {
stdoutBuf = "";
stderrBuf = "";
console.log = captureLog;
console.error = captureError;
let exitCode = 0;
try {
// Use AsyncFunction so user code can `await` async helpers like http_get
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const fn = new AsyncFunction(
"call_tool", "read_file", "write_file", "console",
code,
);
await fn(
callTool, readFile, writeFile,
{ log: captureLog, error: captureError, warn: captureError },
);
} catch (e) {
stderrBuf += `${e.name}: ${e.message}\n`;
exitCode = 1;
} finally {
console.log = origLog;
console.error = origError;
}
return {
stdout: stdoutBuf,
stderr: stderrBuf,
exitCode,
};
},
};