agentenkit 0.4.0 → 0.5.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.
- package/README.md +48 -1
- package/dist/adapters/brave.d.ts +21 -0
- package/dist/adapters/brave.d.ts.map +1 -0
- package/dist/adapters/brave.js +52 -0
- package/dist/adapters/brave.js.map +1 -0
- package/dist/adapters/computesdk.d.ts +71 -0
- package/dist/adapters/computesdk.d.ts.map +1 -0
- package/dist/adapters/computesdk.js +163 -0
- package/dist/adapters/computesdk.js.map +1 -0
- package/dist/adapters/docker.d.ts +35 -0
- package/dist/adapters/docker.d.ts.map +1 -0
- package/dist/adapters/docker.js +246 -0
- package/dist/adapters/docker.js.map +1 -0
- package/dist/adapters/e2b.d.ts +44 -0
- package/dist/adapters/e2b.d.ts.map +1 -0
- package/dist/adapters/e2b.js +349 -0
- package/dist/adapters/e2b.js.map +1 -0
- package/dist/adapters/jina.d.ts +34 -0
- package/dist/adapters/jina.d.ts.map +1 -0
- package/dist/adapters/jina.js +99 -0
- package/dist/adapters/jina.js.map +1 -0
- package/dist/adapters/local-sandbox.d.ts +32 -0
- package/dist/adapters/local-sandbox.d.ts.map +1 -0
- package/dist/adapters/local-sandbox.js +257 -0
- package/dist/adapters/local-sandbox.js.map +1 -0
- package/dist/adapters/memory.d.ts +81 -0
- package/dist/adapters/memory.d.ts.map +1 -1
- package/dist/adapters/memory.js +207 -0
- package/dist/adapters/memory.js.map +1 -1
- package/dist/adapters/page-reader.d.ts +39 -0
- package/dist/adapters/page-reader.d.ts.map +1 -0
- package/dist/adapters/page-reader.js +165 -0
- package/dist/adapters/page-reader.js.map +1 -0
- package/dist/adapters/sandbox-shell.d.ts +60 -0
- package/dist/adapters/sandbox-shell.d.ts.map +1 -0
- package/dist/adapters/sandbox-shell.js +129 -0
- package/dist/adapters/sandbox-shell.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +37 -1
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js +106 -7
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/adapters/web-search-shared.d.ts +11 -0
- package/dist/adapters/web-search-shared.d.ts.map +1 -0
- package/dist/adapters/web-search-shared.js +40 -0
- package/dist/adapters/web-search-shared.js.map +1 -0
- package/dist/core/builtin/definitions.d.ts +12 -0
- package/dist/core/builtin/definitions.d.ts.map +1 -0
- package/dist/core/builtin/definitions.js +149 -0
- package/dist/core/builtin/definitions.js.map +1 -0
- package/dist/core/builtin/html.d.ts +13 -0
- package/dist/core/builtin/html.d.ts.map +1 -0
- package/dist/core/builtin/html.js +96 -0
- package/dist/core/builtin/html.js.map +1 -0
- package/dist/core/builtin/index.d.ts +21 -0
- package/dist/core/builtin/index.d.ts.map +1 -0
- package/dist/core/builtin/index.js +55 -0
- package/dist/core/builtin/index.js.map +1 -0
- package/dist/core/builtin/run.d.ts +37 -0
- package/dist/core/builtin/run.d.ts.map +1 -0
- package/dist/core/builtin/run.js +55 -0
- package/dist/core/builtin/run.js.map +1 -0
- package/dist/core/builtin/sandbox-tools.d.ts +69 -0
- package/dist/core/builtin/sandbox-tools.d.ts.map +1 -0
- package/dist/core/builtin/sandbox-tools.js +389 -0
- package/dist/core/builtin/sandbox-tools.js.map +1 -0
- package/dist/core/builtin/sandbox.d.ts +52 -0
- package/dist/core/builtin/sandbox.d.ts.map +1 -0
- package/dist/core/builtin/sandbox.js +174 -0
- package/dist/core/builtin/sandbox.js.map +1 -0
- package/dist/core/builtin/web.d.ts +54 -0
- package/dist/core/builtin/web.d.ts.map +1 -0
- package/dist/core/builtin/web.js +167 -0
- package/dist/core/builtin/web.js.map +1 -0
- package/dist/core/deleteThread.d.ts.map +1 -1
- package/dist/core/deleteThread.js +3 -0
- package/dist/core/deleteThread.js.map +1 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +29 -5
- package/dist/core/engine.js.map +1 -1
- package/dist/core/permanent.d.ts +11 -0
- package/dist/core/permanent.d.ts.map +1 -0
- package/dist/core/permanent.js +37 -0
- package/dist/core/permanent.js.map +1 -0
- package/dist/core/subagent.d.ts.map +1 -1
- package/dist/core/subagent.js +13 -3
- package/dist/core/subagent.js.map +1 -1
- package/dist/core/tools.d.ts +9 -3
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/core/types.d.ts +17 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/ports/runtime.d.ts +17 -0
- package/dist/ports/runtime.d.ts.map +1 -1
- package/dist/ports/sandbox.d.ts +159 -0
- package/dist/ports/sandbox.d.ts.map +1 -0
- package/dist/ports/sandbox.js +44 -0
- package/dist/ports/sandbox.js.map +1 -0
- package/dist/ports/tools.d.ts +60 -0
- package/dist/ports/tools.d.ts.map +1 -0
- package/dist/ports/tools.js +5 -0
- package/dist/ports/tools.js.map +1 -0
- package/dist/pricing.d.ts +24 -0
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +29 -0
- package/dist/pricing.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { DEFAULT_COMMAND_TIMEOUT_MS, SandboxFileNotFoundError, SandboxGoneError, SandboxUnsupportedError, TIMED_OUT_EXIT_CODE, } from '../ports/sandbox.js';
|
|
3
|
+
import { FILE_SCRIPTS, NOT_FOUND_EXIT, OutputCollector, backgroundCommand, looksTimedOut, parseListing, randomId, resolveIn, runArgv, timeoutSeconds, toBytes, } from './sandbox-shell.js';
|
|
4
|
+
const DEADLINE_FILE = '/tmp/.agentenkit-deadline';
|
|
5
|
+
/** The container's main process: it waits until the deadline in
|
|
6
|
+
* DEADLINE_FILE has passed and then exits, and `--rm` removes the
|
|
7
|
+
* container. So a sandbox nobody uses ends by itself, even when the app
|
|
8
|
+
* that made it is gone. `setTimeout` moves the deadline. */
|
|
9
|
+
const WATCHDOG = `echo "$1" > ${DEADLINE_FILE}; ` +
|
|
10
|
+
`while [ "$(date +%s)" -lt "$(cat ${DEADLINE_FILE} 2>/dev/null || echo 0)" ]; do sleep 2; done`;
|
|
11
|
+
/** What the docker CLI says when the container is not there any more. */
|
|
12
|
+
const GONE = /Error response from daemon: (No such container|container \S+ is not running)|No such object/;
|
|
13
|
+
/** Sandboxes as Docker containers on the machine the docker CLI talks to:
|
|
14
|
+
* for self-hosting. One container per thread, removed when it has been idle
|
|
15
|
+
* past its time. Uses the docker CLI, so it needs no SDK and follows your
|
|
16
|
+
* DOCKER_HOST and contexts. The Go runtime has the same adapter
|
|
17
|
+
* (adapters/docker). */
|
|
18
|
+
export class DockerSandbox {
|
|
19
|
+
options;
|
|
20
|
+
name = 'docker';
|
|
21
|
+
docker;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.options = options;
|
|
24
|
+
this.docker = options.docker ?? 'docker';
|
|
25
|
+
}
|
|
26
|
+
async create(options) {
|
|
27
|
+
const network = options.network ?? this.options.network ?? 'none';
|
|
28
|
+
if (typeof network === 'object')
|
|
29
|
+
throw new SandboxUnsupportedError('docker', 'a network allow list');
|
|
30
|
+
const name = `agentenkit-${randomId()}`;
|
|
31
|
+
const workdir = this.options.workdir ?? '/workspace';
|
|
32
|
+
const deadline = Math.floor((Date.now() + options.timeoutMs) / 1000);
|
|
33
|
+
const args = [
|
|
34
|
+
'run', '-d', '--rm', '--init', '--name', name,
|
|
35
|
+
'--label', 'agentenkit=sandbox',
|
|
36
|
+
'--label', `agentenkit.threadId=${options.metadata.threadId}`,
|
|
37
|
+
...(options.metadata.runId ? ['--label', `agentenkit.runId=${options.metadata.runId}`] : []),
|
|
38
|
+
'-w', workdir,
|
|
39
|
+
'--network', network === 'all' ? 'bridge' : 'none',
|
|
40
|
+
'--security-opt', 'no-new-privileges',
|
|
41
|
+
];
|
|
42
|
+
const cpu = options.resources?.cpu ?? this.options.cpu;
|
|
43
|
+
const memory = options.resources?.memoryMiB ?? this.options.memoryMiB;
|
|
44
|
+
if (cpu)
|
|
45
|
+
args.push('--cpus', String(cpu));
|
|
46
|
+
if (memory)
|
|
47
|
+
args.push('--memory', `${memory}m`);
|
|
48
|
+
for (const port of this.options.ports ?? [])
|
|
49
|
+
args.push('-p', `127.0.0.1::${port}`);
|
|
50
|
+
for (const [k, v] of Object.entries({ ...this.options.env, ...options.envs }))
|
|
51
|
+
args.push('-e', `${k}=${v}`);
|
|
52
|
+
args.push(...(this.options.runArgs ?? []));
|
|
53
|
+
args.push(options.image ?? this.options.image ?? 'python:3.12-slim', 'sh', '-c', WATCHDOG, 'sandbox', String(deadline));
|
|
54
|
+
const r = await cli(this.docker, args);
|
|
55
|
+
if (r.code !== 0)
|
|
56
|
+
throw new Error(`docker: could not start a sandbox: ${text(r.stderr).trim()}`);
|
|
57
|
+
return new DockerBox(this.docker, name, workdir);
|
|
58
|
+
}
|
|
59
|
+
async connect(sandboxId) {
|
|
60
|
+
const r = await cli(this.docker, ['inspect', '-f', '{{.State.Running}}\t{{.Config.WorkingDir}}', sandboxId]);
|
|
61
|
+
const [running, workdir] = text(r.stdout).trim().split('\t');
|
|
62
|
+
if (r.code !== 0 || running !== 'true')
|
|
63
|
+
throw new SandboxGoneError(sandboxId);
|
|
64
|
+
return new DockerBox(this.docker, sandboxId, workdir || '/');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class DockerBox {
|
|
68
|
+
docker;
|
|
69
|
+
sandboxId;
|
|
70
|
+
workdir;
|
|
71
|
+
provider = 'docker';
|
|
72
|
+
filesystem;
|
|
73
|
+
constructor(docker, sandboxId, workdir) {
|
|
74
|
+
this.docker = docker;
|
|
75
|
+
this.sandboxId = sandboxId;
|
|
76
|
+
this.workdir = workdir;
|
|
77
|
+
this.filesystem = new DockerFiles(this);
|
|
78
|
+
}
|
|
79
|
+
/** `docker exec` a script with `sh -c`; a container that is gone throws. */
|
|
80
|
+
async script(script, args, stdin) {
|
|
81
|
+
const r = await cli(this.docker, ['exec', ...(stdin ? ['-i'] : []), this.sandboxId, 'sh', '-c', script, 'sandbox', ...args], { stdin });
|
|
82
|
+
this.checkGone(r);
|
|
83
|
+
return r;
|
|
84
|
+
}
|
|
85
|
+
checkGone(r) {
|
|
86
|
+
if (r.code !== 0 && GONE.test(text(r.stderr)))
|
|
87
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
88
|
+
}
|
|
89
|
+
path(p) {
|
|
90
|
+
return resolveIn(this.workdir, p);
|
|
91
|
+
}
|
|
92
|
+
async runCommand(command, options = {}) {
|
|
93
|
+
const limit = options.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
94
|
+
const args = ['exec', '-w', this.path(options.cwd ?? '.')];
|
|
95
|
+
for (const [k, v] of Object.entries(options.env ?? {}))
|
|
96
|
+
args.push('-e', `${k}=${v}`);
|
|
97
|
+
const cmd = options.background ? backgroundCommand(command) : command;
|
|
98
|
+
args.push(this.sandboxId, ...runArgv(cmd, options.background ? undefined : timeoutSeconds(limit)));
|
|
99
|
+
const started = Date.now();
|
|
100
|
+
const out = new OutputCollector(options.background ? undefined : options.onStdout);
|
|
101
|
+
const err = new OutputCollector(options.background ? undefined : options.onStderr);
|
|
102
|
+
// `timeout` inside the container stops the command; this is the backstop
|
|
103
|
+
// for an image without it.
|
|
104
|
+
const r = await cli(this.docker, args, { out, err, limitMs: limit + 5_000, signal: options.signal });
|
|
105
|
+
const stdout = out.end();
|
|
106
|
+
const stderr = err.end();
|
|
107
|
+
if (r.code !== 0 && GONE.test(stderr))
|
|
108
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
109
|
+
const durationMs = Date.now() - started;
|
|
110
|
+
if (options.background)
|
|
111
|
+
return { stdout: '', stderr: '', exitCode: r.code, durationMs };
|
|
112
|
+
const timedOut = r.killed || looksTimedOut(r.code, durationMs, limit);
|
|
113
|
+
return {
|
|
114
|
+
stdout,
|
|
115
|
+
stderr,
|
|
116
|
+
exitCode: timedOut ? TIMED_OUT_EXIT_CODE : r.code,
|
|
117
|
+
durationMs,
|
|
118
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
119
|
+
...(out.truncated || err.truncated ? { truncated: true } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async getUrl(options) {
|
|
123
|
+
const r = await cli(this.docker, ['port', this.sandboxId, `${options.port}/tcp`]);
|
|
124
|
+
this.checkGone(r);
|
|
125
|
+
const hostPort = text(r.stdout).trim().split('\n')[0];
|
|
126
|
+
if (r.code !== 0 || !hostPort) {
|
|
127
|
+
throw new SandboxUnsupportedError('docker', `a URL for port ${options.port} (publish it with the ports option)`);
|
|
128
|
+
}
|
|
129
|
+
return `${options.protocol ?? 'http'}://${hostPort.replace('0.0.0.0', '127.0.0.1')}`;
|
|
130
|
+
}
|
|
131
|
+
async getInfo() {
|
|
132
|
+
const r = await cli(this.docker, [
|
|
133
|
+
'inspect', '-f', '{{.Created}}\t{{.State.Status}}\t{{json .Config.Labels}}', this.sandboxId,
|
|
134
|
+
]);
|
|
135
|
+
if (r.code !== 0)
|
|
136
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
137
|
+
const [created, status, labels] = text(r.stdout).trim().split('\t');
|
|
138
|
+
const metadata = {};
|
|
139
|
+
for (const [k, v] of Object.entries((JSON.parse(labels || '{}') ?? {}))) {
|
|
140
|
+
if (k.startsWith('agentenkit.'))
|
|
141
|
+
metadata[k.slice('agentenkit.'.length)] = v;
|
|
142
|
+
}
|
|
143
|
+
const deadline = Number(text((await this.script(`cat ${DEADLINE_FILE}`, [])).stdout).trim());
|
|
144
|
+
return {
|
|
145
|
+
id: this.sandboxId,
|
|
146
|
+
provider: 'docker',
|
|
147
|
+
status: status === 'running' ? 'running' : status === 'exited' ? 'stopped' : 'error',
|
|
148
|
+
createdAt: new Date(created),
|
|
149
|
+
...(deadline ? { expiresAt: new Date(deadline * 1000) } : {}),
|
|
150
|
+
workdir: this.workdir,
|
|
151
|
+
metadata,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async setTimeout(timeoutMs) {
|
|
155
|
+
const deadline = Math.floor((Date.now() + timeoutMs) / 1000);
|
|
156
|
+
const r = await this.script(`echo "$1" > ${DEADLINE_FILE}`, [String(deadline)]);
|
|
157
|
+
if (r.code !== 0)
|
|
158
|
+
throw new Error(`docker: could not set the sandbox's time: ${text(r.stderr).trim()}`);
|
|
159
|
+
}
|
|
160
|
+
async destroy() {
|
|
161
|
+
const r = await cli(this.docker, ['rm', '-f', this.sandboxId]);
|
|
162
|
+
if (r.code !== 0 && !GONE.test(text(r.stderr))) {
|
|
163
|
+
throw new Error(`docker: could not remove ${this.sandboxId}: ${text(r.stderr).trim()}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
class DockerFiles {
|
|
168
|
+
box;
|
|
169
|
+
constructor(box) {
|
|
170
|
+
this.box = box;
|
|
171
|
+
}
|
|
172
|
+
async run(script, path, stdin) {
|
|
173
|
+
const r = await this.box.script(script, [this.box.path(path)], stdin);
|
|
174
|
+
if (r.code === NOT_FOUND_EXIT)
|
|
175
|
+
throw new SandboxFileNotFoundError(path);
|
|
176
|
+
if (r.code !== 0)
|
|
177
|
+
throw new Error(`docker: ${text(r.stderr).trim() || `exit ${r.code}`}`);
|
|
178
|
+
return r;
|
|
179
|
+
}
|
|
180
|
+
async readFile(path) {
|
|
181
|
+
return text((await this.run(FILE_SCRIPTS.read, path)).stdout);
|
|
182
|
+
}
|
|
183
|
+
async readFileBytes(path) {
|
|
184
|
+
return (await this.run(FILE_SCRIPTS.read, path)).stdout;
|
|
185
|
+
}
|
|
186
|
+
async writeFile(path, content) {
|
|
187
|
+
await this.run(FILE_SCRIPTS.write, path, toBytes(content));
|
|
188
|
+
}
|
|
189
|
+
async readdir(path) {
|
|
190
|
+
return parseListing(text((await this.run(FILE_SCRIPTS.list, path)).stdout));
|
|
191
|
+
}
|
|
192
|
+
async mkdir(path) {
|
|
193
|
+
await this.run(FILE_SCRIPTS.mkdir, path);
|
|
194
|
+
}
|
|
195
|
+
async exists(path) {
|
|
196
|
+
return (await this.box.script(FILE_SCRIPTS.exists, [this.box.path(path)])).code === 0;
|
|
197
|
+
}
|
|
198
|
+
async remove(path) {
|
|
199
|
+
await this.run(FILE_SCRIPTS.remove, path);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const text = (b) => new TextDecoder().decode(b);
|
|
203
|
+
/** Runs the docker CLI. With `out` / `err` the output goes there as it
|
|
204
|
+
* comes; otherwise it is collected whole. */
|
|
205
|
+
function cli(docker, args, opts = {}) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
const child = spawn(docker, args, { stdio: [opts.stdin ? 'pipe' : 'ignore', 'pipe', 'pipe'] });
|
|
208
|
+
const stdout = [];
|
|
209
|
+
const stderr = [];
|
|
210
|
+
child.stdout.on('data', (b) => (opts.out ? opts.out.push(b) : stdout.push(b)));
|
|
211
|
+
child.stderr.on('data', (b) => (opts.err ? opts.err.push(b) : stderr.push(b)));
|
|
212
|
+
let killed = false;
|
|
213
|
+
let aborted = false;
|
|
214
|
+
const timer = opts.limitMs
|
|
215
|
+
? setTimeout(() => {
|
|
216
|
+
killed = true;
|
|
217
|
+
child.kill('SIGKILL');
|
|
218
|
+
}, opts.limitMs)
|
|
219
|
+
: undefined;
|
|
220
|
+
const onAbort = () => {
|
|
221
|
+
aborted = true;
|
|
222
|
+
child.kill('SIGKILL');
|
|
223
|
+
};
|
|
224
|
+
opts.signal?.addEventListener('abort', onAbort, { once: true });
|
|
225
|
+
if (opts.signal?.aborted)
|
|
226
|
+
onAbort();
|
|
227
|
+
const done = () => {
|
|
228
|
+
if (timer)
|
|
229
|
+
clearTimeout(timer);
|
|
230
|
+
opts.signal?.removeEventListener('abort', onAbort);
|
|
231
|
+
};
|
|
232
|
+
child.on('error', (e) => {
|
|
233
|
+
done();
|
|
234
|
+
reject(new Error(`docker: could not run ${docker}: ${e.message}`));
|
|
235
|
+
});
|
|
236
|
+
child.on('close', (code) => {
|
|
237
|
+
done();
|
|
238
|
+
if (aborted)
|
|
239
|
+
return reject(opts.signal.reason ?? new Error('aborted'));
|
|
240
|
+
resolve({ code: code ?? -1, stdout: new Uint8Array(Buffer.concat(stdout)), stderr: new Uint8Array(Buffer.concat(stderr)), killed });
|
|
241
|
+
});
|
|
242
|
+
if (opts.stdin)
|
|
243
|
+
child.stdin.end(Buffer.from(opts.stdin));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/adapters/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type CreateSandboxOptions, type Sandbox, type SandboxNetwork, type SandboxProvider } from '../ports/sandbox.js';
|
|
2
|
+
export interface E2BSandboxOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** The template each sandbox starts from. Default `base`. */
|
|
5
|
+
template?: string;
|
|
6
|
+
/** `none` (the default), `all`, or `{ allow: [...] }`: only these hosts. */
|
|
7
|
+
network?: SandboxNetwork;
|
|
8
|
+
/** Default `e2b.app`. */
|
|
9
|
+
domain?: string;
|
|
10
|
+
/** Default `https://api.<domain>`. */
|
|
11
|
+
apiUrl?: string;
|
|
12
|
+
/** Sends every call to one sandbox here instead of
|
|
13
|
+
* `https://49983-<id>.<domain>`, naming the sandbox in a header. For
|
|
14
|
+
* tests and proxies. */
|
|
15
|
+
sandboxUrl?: string;
|
|
16
|
+
/** For tests. */
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
/** Hosted sandboxes on E2B: each is a small VM that starts in under a
|
|
20
|
+
* second. It talks to E2B's HTTP API directly, so it needs no SDK; the Go
|
|
21
|
+
* runtime has the same adapter (adapters/e2b). A sandbox shuts itself down
|
|
22
|
+
* at its timeout, which the runtime pushes back while the thread uses it. */
|
|
23
|
+
export declare class E2BSandbox implements SandboxProvider {
|
|
24
|
+
private readonly options;
|
|
25
|
+
readonly name = "e2b";
|
|
26
|
+
private readonly domain;
|
|
27
|
+
private readonly apiUrl;
|
|
28
|
+
constructor(options: E2BSandboxOptions);
|
|
29
|
+
get doFetch(): typeof fetch;
|
|
30
|
+
/** A call to E2B's API. */
|
|
31
|
+
api(method: string, path: string, body?: unknown): Promise<Response>;
|
|
32
|
+
create(options: CreateSandboxOptions): Promise<Sandbox>;
|
|
33
|
+
connect(sandboxId: string): Promise<Sandbox>;
|
|
34
|
+
private box;
|
|
35
|
+
}
|
|
36
|
+
/** One Connect streaming message: a flags byte, a 4-byte length, the JSON. */
|
|
37
|
+
export declare function envelope(message: unknown, flags?: number): Uint8Array<ArrayBuffer>;
|
|
38
|
+
/** The messages of a Connect stream. The last has `end: true` and says
|
|
39
|
+
* whether the call failed. */
|
|
40
|
+
export declare function frames(body: ReadableStream<Uint8Array>): AsyncGenerator<{
|
|
41
|
+
end: boolean;
|
|
42
|
+
message: any;
|
|
43
|
+
}>;
|
|
44
|
+
//# sourceMappingURL=e2b.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2b.d.ts","sourceRoot":"","sources":["../../src/adapters/e2b.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,oBAAoB,EAGzB,KAAK,OAAO,EAGZ,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;6BAEyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAWD;;;8EAG8E;AAC9E,qBAAa,UAAW,YAAW,eAAe;IAKpC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,IAAI,SAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,iBAAiB;IAMvD,IAAI,OAAO,IAAI,OAAO,KAAK,CAE1B;IAED,2BAA2B;IACrB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQpE,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBvD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOlD,OAAO,CAAC,GAAG;CAIZ;AAkPD,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAO7E;AAED;+BAC+B;AAC/B,wBAAuB,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,CAwB9G"}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { DEFAULT_COMMAND_TIMEOUT_MS, SandboxFileNotFoundError, SandboxGoneError, TIMED_OUT_EXIT_CODE, } from '../ports/sandbox.js';
|
|
2
|
+
import { OutputCollector, backgroundCommand, looksTimedOut, resolveIn, runArgv, sortEntries, timeoutSeconds, toBytes, } from './sandbox-shell.js';
|
|
3
|
+
const ENVD_PORT = 49983;
|
|
4
|
+
const WORKDIR = '/home/user';
|
|
5
|
+
/** Hosted sandboxes on E2B: each is a small VM that starts in under a
|
|
6
|
+
* second. It talks to E2B's HTTP API directly, so it needs no SDK; the Go
|
|
7
|
+
* runtime has the same adapter (adapters/e2b). A sandbox shuts itself down
|
|
8
|
+
* at its timeout, which the runtime pushes back while the thread uses it. */
|
|
9
|
+
export class E2BSandbox {
|
|
10
|
+
options;
|
|
11
|
+
name = 'e2b';
|
|
12
|
+
domain;
|
|
13
|
+
apiUrl;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
if (!options.apiKey)
|
|
17
|
+
throw new Error('E2BSandbox: apiKey is required');
|
|
18
|
+
this.domain = options.domain ?? 'e2b.app';
|
|
19
|
+
this.apiUrl = options.apiUrl ?? `https://api.${this.domain}`;
|
|
20
|
+
}
|
|
21
|
+
get doFetch() {
|
|
22
|
+
return this.options.fetch ?? fetch;
|
|
23
|
+
}
|
|
24
|
+
/** A call to E2B's API. */
|
|
25
|
+
async api(method, path, body) {
|
|
26
|
+
return this.doFetch(`${this.apiUrl}${path}`, {
|
|
27
|
+
method,
|
|
28
|
+
headers: { 'x-api-key': this.options.apiKey, ...(body !== undefined ? { 'content-type': 'application/json' } : {}) },
|
|
29
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async create(options) {
|
|
33
|
+
const network = options.network ?? this.options.network ?? 'none';
|
|
34
|
+
const metadata = { 'agentenkit.threadId': options.metadata.threadId };
|
|
35
|
+
if (options.metadata.runId)
|
|
36
|
+
metadata['agentenkit.runId'] = options.metadata.runId;
|
|
37
|
+
const res = await this.api('POST', '/v2/sandboxes', {
|
|
38
|
+
templateID: options.template ?? this.options.template ?? 'base',
|
|
39
|
+
timeout: seconds(options.timeoutMs),
|
|
40
|
+
metadata,
|
|
41
|
+
...(options.envs ? { envVars: options.envs } : {}),
|
|
42
|
+
...(typeof network === 'object'
|
|
43
|
+
? { allow_internet_access: true, network: { allowOut: network.allow, denyOut: ['0.0.0.0/0'] } }
|
|
44
|
+
: { allow_internet_access: network === 'all' }),
|
|
45
|
+
...(options.extra ?? {}),
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
throw new Error(`e2b: could not start a sandbox: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
49
|
+
return this.box((await res.json()));
|
|
50
|
+
}
|
|
51
|
+
async connect(sandboxId) {
|
|
52
|
+
const res = await this.api('POST', `/v2/sandboxes/${encodeURIComponent(sandboxId)}/connect`, {});
|
|
53
|
+
if (res.status === 404 || res.status === 410)
|
|
54
|
+
throw new SandboxGoneError(sandboxId);
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error(`e2b: could not reach sandbox ${sandboxId}: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
57
|
+
return this.box((await res.json()));
|
|
58
|
+
}
|
|
59
|
+
box(c) {
|
|
60
|
+
const envdUrl = this.options.sandboxUrl ?? `https://${ENVD_PORT}-${c.sandboxID}.${c.domain || this.domain}`;
|
|
61
|
+
return new E2BBox(this, c.sandboxID, c.domain || this.domain, envdUrl, c.envdAccessToken ?? undefined);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class E2BBox {
|
|
65
|
+
e2b;
|
|
66
|
+
sandboxId;
|
|
67
|
+
domain;
|
|
68
|
+
envdUrl;
|
|
69
|
+
accessToken;
|
|
70
|
+
provider = 'e2b';
|
|
71
|
+
filesystem;
|
|
72
|
+
constructor(e2b, sandboxId, domain, envdUrl, accessToken) {
|
|
73
|
+
this.e2b = e2b;
|
|
74
|
+
this.sandboxId = sandboxId;
|
|
75
|
+
this.domain = domain;
|
|
76
|
+
this.envdUrl = envdUrl;
|
|
77
|
+
this.accessToken = accessToken;
|
|
78
|
+
this.filesystem = new E2BFiles(this);
|
|
79
|
+
}
|
|
80
|
+
headers(extra = {}) {
|
|
81
|
+
return {
|
|
82
|
+
'e2b-sandbox-id': this.sandboxId,
|
|
83
|
+
'e2b-sandbox-port': String(ENVD_PORT),
|
|
84
|
+
...(this.accessToken ? { 'x-access-token': this.accessToken } : {}),
|
|
85
|
+
...extra,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** A call to the sandbox's own daemon (envd). */
|
|
89
|
+
async envd(path, init = {}) {
|
|
90
|
+
const res = await this.e2b.doFetch(`${this.envdUrl}${path}`, { ...init, headers: this.headers(init.headers) });
|
|
91
|
+
// E2B's proxy answers 502 for a sandbox that is not running any more.
|
|
92
|
+
if (res.status === 502)
|
|
93
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
94
|
+
return res;
|
|
95
|
+
}
|
|
96
|
+
/** A unary Connect call to envd, in JSON. */
|
|
97
|
+
async rpc(method, body) {
|
|
98
|
+
const res = await this.envd(`/${method}`, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'content-type': 'application/json', 'connect-protocol-version': '1' },
|
|
101
|
+
body: JSON.stringify(body),
|
|
102
|
+
});
|
|
103
|
+
const data = (await res.json().catch(() => ({})));
|
|
104
|
+
if (res.ok)
|
|
105
|
+
return { ok: true, data };
|
|
106
|
+
return { ok: false, code: String(data?.code ?? res.status), message: String(data?.message ?? '') };
|
|
107
|
+
}
|
|
108
|
+
path(p) {
|
|
109
|
+
return resolveIn(WORKDIR, p);
|
|
110
|
+
}
|
|
111
|
+
async runCommand(command, options = {}) {
|
|
112
|
+
const limit = options.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
113
|
+
const cmd = options.background ? backgroundCommand(command) : command;
|
|
114
|
+
// `timeout` inside the sandbox stops the command and what it started;
|
|
115
|
+
// the timer here is the backstop, and kills it through envd.
|
|
116
|
+
const [file, ...args] = runArgv(cmd, options.background ? undefined : timeoutSeconds(limit), true);
|
|
117
|
+
const started = Date.now();
|
|
118
|
+
const out = new OutputCollector(options.background ? undefined : options.onStdout);
|
|
119
|
+
const err = new OutputCollector(options.background ? undefined : options.onStderr);
|
|
120
|
+
const request = new AbortController();
|
|
121
|
+
let pid;
|
|
122
|
+
let timedOut = false;
|
|
123
|
+
const stop = async () => {
|
|
124
|
+
if (pid !== undefined) {
|
|
125
|
+
await this.rpc('process.Process/SendSignal', { process: { pid }, signal: 'SIGNAL_SIGKILL' }).catch(() => undefined);
|
|
126
|
+
}
|
|
127
|
+
request.abort();
|
|
128
|
+
};
|
|
129
|
+
const timer = setTimeout(() => {
|
|
130
|
+
timedOut = true;
|
|
131
|
+
void stop();
|
|
132
|
+
}, limit + 1_000);
|
|
133
|
+
const onAbort = () => void stop();
|
|
134
|
+
if (options.signal?.aborted) {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
throw options.signal.reason;
|
|
137
|
+
}
|
|
138
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
139
|
+
let exitCode = -1;
|
|
140
|
+
try {
|
|
141
|
+
const res = await this.envd('/process.Process/Start', {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
'content-type': 'application/connect+json',
|
|
145
|
+
'connect-protocol-version': '1',
|
|
146
|
+
'keepalive-ping-interval': '50',
|
|
147
|
+
},
|
|
148
|
+
body: envelope({
|
|
149
|
+
process: { cmd: file, args, cwd: this.path(options.cwd ?? '.'), envs: options.env ?? {} },
|
|
150
|
+
stdin: false,
|
|
151
|
+
}),
|
|
152
|
+
signal: request.signal,
|
|
153
|
+
});
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
const text = await res.text();
|
|
156
|
+
if (res.status === 404)
|
|
157
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
158
|
+
throw new Error(`e2b: could not run the command: ${res.status} ${text.slice(0, 300)}`);
|
|
159
|
+
}
|
|
160
|
+
for await (const frame of frames(res.body)) {
|
|
161
|
+
if (frame.end) {
|
|
162
|
+
const e = frame.message?.error;
|
|
163
|
+
if (e) {
|
|
164
|
+
if (e.code === 'not_found' || e.code === 'unavailable')
|
|
165
|
+
throw new SandboxGoneError(this.sandboxId, e.message);
|
|
166
|
+
throw new Error(`e2b: ${e.code}: ${e.message}`);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
const event = frame.message?.event ?? {};
|
|
171
|
+
if (event.start)
|
|
172
|
+
pid = event.start.pid;
|
|
173
|
+
if (event.data?.stdout)
|
|
174
|
+
out.push(base64Bytes(event.data.stdout));
|
|
175
|
+
if (event.data?.stderr)
|
|
176
|
+
err.push(base64Bytes(event.data.stderr));
|
|
177
|
+
if (event.end)
|
|
178
|
+
exitCode = event.end.exitCode ?? 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
if (!request.signal.aborted)
|
|
183
|
+
throw e;
|
|
184
|
+
if (!timedOut)
|
|
185
|
+
throw options.signal?.reason ?? e;
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
190
|
+
}
|
|
191
|
+
const durationMs = Date.now() - started;
|
|
192
|
+
if (options.background)
|
|
193
|
+
return { stdout: '', stderr: '', exitCode, durationMs };
|
|
194
|
+
timedOut ||= looksTimedOut(exitCode, durationMs, limit);
|
|
195
|
+
return {
|
|
196
|
+
stdout: out.end(),
|
|
197
|
+
stderr: err.end(),
|
|
198
|
+
exitCode: timedOut ? TIMED_OUT_EXIT_CODE : exitCode,
|
|
199
|
+
durationMs,
|
|
200
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
201
|
+
...(out.truncated || err.truncated ? { truncated: true } : {}),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async getUrl(options) {
|
|
205
|
+
return `${options.protocol ?? 'https'}://${options.port}-${this.sandboxId}.${this.domain}`;
|
|
206
|
+
}
|
|
207
|
+
async getInfo() {
|
|
208
|
+
const res = await this.e2b.api('GET', `/sandboxes/${encodeURIComponent(this.sandboxId)}`);
|
|
209
|
+
if (res.status === 404)
|
|
210
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
211
|
+
if (!res.ok)
|
|
212
|
+
throw new Error(`e2b: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
213
|
+
const d = (await res.json());
|
|
214
|
+
const metadata = {};
|
|
215
|
+
for (const [k, v] of Object.entries(d.metadata ?? {})) {
|
|
216
|
+
if (k.startsWith('agentenkit.'))
|
|
217
|
+
metadata[k.slice('agentenkit.'.length)] = v;
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
id: this.sandboxId,
|
|
221
|
+
provider: 'e2b',
|
|
222
|
+
status: !d.state || d.state === 'running' ? 'running' : 'stopped',
|
|
223
|
+
createdAt: new Date(d.startedAt),
|
|
224
|
+
...(d.endAt ? { expiresAt: new Date(d.endAt) } : {}),
|
|
225
|
+
workdir: WORKDIR,
|
|
226
|
+
metadata,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
async setTimeout(timeoutMs) {
|
|
230
|
+
const res = await this.e2b.api('POST', `/sandboxes/${encodeURIComponent(this.sandboxId)}/timeout`, {
|
|
231
|
+
timeout: seconds(timeoutMs),
|
|
232
|
+
});
|
|
233
|
+
if (res.status === 404)
|
|
234
|
+
throw new SandboxGoneError(this.sandboxId);
|
|
235
|
+
if (!res.ok)
|
|
236
|
+
throw new Error(`e2b: could not set the sandbox's time: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
237
|
+
}
|
|
238
|
+
async destroy() {
|
|
239
|
+
const res = await this.e2b.api('DELETE', `/sandboxes/${encodeURIComponent(this.sandboxId)}`);
|
|
240
|
+
if (!res.ok && res.status !== 404)
|
|
241
|
+
throw new Error(`e2b: could not end ${this.sandboxId}: ${res.status}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
class E2BFiles {
|
|
245
|
+
box;
|
|
246
|
+
constructor(box) {
|
|
247
|
+
this.box = box;
|
|
248
|
+
}
|
|
249
|
+
async readFile(path) {
|
|
250
|
+
return new TextDecoder().decode(await this.readFileBytes(path));
|
|
251
|
+
}
|
|
252
|
+
async readFileBytes(path) {
|
|
253
|
+
const res = await this.box.envd(`/files?${new URLSearchParams({ path: this.box.path(path) })}`);
|
|
254
|
+
if (res.status === 404)
|
|
255
|
+
throw new SandboxFileNotFoundError(path);
|
|
256
|
+
if (!res.ok)
|
|
257
|
+
throw new Error(`e2b: could not read ${path}: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
258
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
259
|
+
}
|
|
260
|
+
async writeFile(path, content) {
|
|
261
|
+
const full = this.box.path(path);
|
|
262
|
+
const form = new FormData();
|
|
263
|
+
// A copy, so its buffer is a plain ArrayBuffer as Blob wants.
|
|
264
|
+
form.append('file', new Blob([toBytes(content).slice()]), full);
|
|
265
|
+
const res = await this.box.envd(`/files?${new URLSearchParams({ path: full })}`, { method: 'POST', body: form });
|
|
266
|
+
if (!res.ok)
|
|
267
|
+
throw new Error(`e2b: could not write ${path}: ${res.status} ${(await res.text()).slice(0, 300)}`);
|
|
268
|
+
}
|
|
269
|
+
async readdir(path) {
|
|
270
|
+
const r = await this.box.rpc('filesystem.Filesystem/ListDir', { path: this.box.path(path), depth: 1 });
|
|
271
|
+
if (!r.ok) {
|
|
272
|
+
if (r.code === 'not_found' || r.code === 'invalid_argument')
|
|
273
|
+
throw new SandboxFileNotFoundError(path);
|
|
274
|
+
throw new Error(`e2b: could not list ${path}: ${r.code} ${r.message}`);
|
|
275
|
+
}
|
|
276
|
+
const entries = [];
|
|
277
|
+
for (const e of (r.data.entries ?? [])) {
|
|
278
|
+
entries.push(e.type === 'FILE_TYPE_DIRECTORY'
|
|
279
|
+
? { name: e.name, type: 'directory' }
|
|
280
|
+
: { name: e.name, type: 'file', size: Number(e.size ?? 0) });
|
|
281
|
+
}
|
|
282
|
+
return sortEntries(entries);
|
|
283
|
+
}
|
|
284
|
+
async mkdir(path) {
|
|
285
|
+
const r = await this.box.rpc('filesystem.Filesystem/MakeDir', { path: this.box.path(path) });
|
|
286
|
+
if (!r.ok && r.code !== 'already_exists')
|
|
287
|
+
throw new Error(`e2b: could not make ${path}: ${r.code} ${r.message}`);
|
|
288
|
+
}
|
|
289
|
+
async exists(path) {
|
|
290
|
+
const r = await this.box.rpc('filesystem.Filesystem/Stat', { path: this.box.path(path) });
|
|
291
|
+
if (r.ok)
|
|
292
|
+
return true;
|
|
293
|
+
if (r.code === 'not_found')
|
|
294
|
+
return false;
|
|
295
|
+
throw new Error(`e2b: could not check ${path}: ${r.code} ${r.message}`);
|
|
296
|
+
}
|
|
297
|
+
async remove(path) {
|
|
298
|
+
const r = await this.box.rpc('filesystem.Filesystem/Remove', { path: this.box.path(path) });
|
|
299
|
+
if (!r.ok && r.code !== 'not_found')
|
|
300
|
+
throw new Error(`e2b: could not remove ${path}: ${r.code} ${r.message}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/** E2B takes whole seconds. */
|
|
304
|
+
function seconds(ms) {
|
|
305
|
+
return Math.max(1, Math.ceil(ms / 1000));
|
|
306
|
+
}
|
|
307
|
+
function base64Bytes(s) {
|
|
308
|
+
return Uint8Array.from(Buffer.from(s, 'base64'));
|
|
309
|
+
}
|
|
310
|
+
/** One Connect streaming message: a flags byte, a 4-byte length, the JSON. */
|
|
311
|
+
export function envelope(message, flags = 0) {
|
|
312
|
+
const json = new TextEncoder().encode(JSON.stringify(message));
|
|
313
|
+
const out = new Uint8Array(5 + json.length);
|
|
314
|
+
out[0] = flags;
|
|
315
|
+
new DataView(out.buffer).setUint32(1, json.length);
|
|
316
|
+
out.set(json, 5);
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
/** The messages of a Connect stream. The last has `end: true` and says
|
|
320
|
+
* whether the call failed. */
|
|
321
|
+
export async function* frames(body) {
|
|
322
|
+
let buf = new Uint8Array(0);
|
|
323
|
+
const reader = body.getReader();
|
|
324
|
+
try {
|
|
325
|
+
for (;;) {
|
|
326
|
+
while (buf.length >= 5) {
|
|
327
|
+
const size = new DataView(buf.buffer, buf.byteOffset).getUint32(1);
|
|
328
|
+
if (buf.length < 5 + size)
|
|
329
|
+
break;
|
|
330
|
+
const flags = buf[0];
|
|
331
|
+
const payload = buf.subarray(5, 5 + size);
|
|
332
|
+
buf = buf.slice(5 + size);
|
|
333
|
+
const message = payload.length ? JSON.parse(new TextDecoder().decode(payload)) : {};
|
|
334
|
+
yield { end: (flags & 0x02) !== 0, message };
|
|
335
|
+
}
|
|
336
|
+
const { value, done } = await reader.read();
|
|
337
|
+
if (done)
|
|
338
|
+
return;
|
|
339
|
+
const next = new Uint8Array(buf.length + value.length);
|
|
340
|
+
next.set(buf);
|
|
341
|
+
next.set(value, buf.length);
|
|
342
|
+
buf = next;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
reader.releaseLock();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=e2b.js.map
|