neckbeard-agent 0.0.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/index.js ADDED
@@ -0,0 +1,556 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/index.ts
9
+ import { fileURLToPath } from "url";
10
+ import { readdirSync, readFileSync, statSync } from "fs";
11
+ import { join, relative, dirname } from "path";
12
+ import { builtinModules } from "module";
13
+ var getEsbuild = () => import("esbuild");
14
+ var getE2b = () => import("e2b");
15
+ var getPkgTypes = () => import("pkg-types");
16
+ var DEFAULT_MAX_DURATION = 300;
17
+ var SANDBOX_COMMAND_TIMEOUT_MS = 3e5;
18
+ var QUICK_COMMAND_TIMEOUT_MS = 3e4;
19
+ var NODE_TARGET = "node20";
20
+ var SANDBOX_HOME = "/home/user";
21
+ var SANDBOX_AGENT_DIR = `${SANDBOX_HOME}/agent`;
22
+ var SANDBOX_PATHS = {
23
+ /** Agent code directory */
24
+ agentDir: SANDBOX_AGENT_DIR,
25
+ /** I/O directory for input/output files */
26
+ ioDir: `${SANDBOX_HOME}/io`,
27
+ /** Task input file */
28
+ inputJson: `${SANDBOX_HOME}/io/input.json`,
29
+ /** Result output file (contains success/error) */
30
+ outputJson: `${SANDBOX_HOME}/io/output.json`,
31
+ /** Bundled agent module */
32
+ agentModule: `${SANDBOX_AGENT_DIR}/agent.mjs`,
33
+ /** Runner module that executes the agent */
34
+ runnerModule: `${SANDBOX_AGENT_DIR}/runner.mjs`,
35
+ /** Claude skills and settings directory */
36
+ claudeDir: `${SANDBOX_AGENT_DIR}/.claude`,
37
+ /** Package.json for npm dependencies */
38
+ packageJson: `${SANDBOX_AGENT_DIR}/package.json`
39
+ };
40
+ var NeckbeardError = class extends Error {
41
+ constructor(message) {
42
+ super(message);
43
+ this.name = "NeckbeardError";
44
+ }
45
+ };
46
+ var DeploymentError = class extends NeckbeardError {
47
+ constructor(message, cause) {
48
+ super(message);
49
+ this.cause = cause;
50
+ this.name = "DeploymentError";
51
+ }
52
+ };
53
+ var ValidationError = class extends NeckbeardError {
54
+ constructor(message, cause) {
55
+ super(message);
56
+ this.cause = cause;
57
+ this.name = "ValidationError";
58
+ }
59
+ };
60
+ var ExecutionError = class extends NeckbeardError {
61
+ constructor(message, cause) {
62
+ super(message);
63
+ this.cause = cause;
64
+ this.name = "ExecutionError";
65
+ }
66
+ };
67
+ var ConfigurationError = class extends NeckbeardError {
68
+ constructor(message) {
69
+ super(message);
70
+ this.name = "ConfigurationError";
71
+ }
72
+ };
73
+ var DEFAULT_DEPENDENCIES = {};
74
+ function requireEnv(name) {
75
+ const value = process.env[name];
76
+ if (!value) {
77
+ throw new ConfigurationError(
78
+ `${name} environment variable is required. Please set it before calling deploy() or run().`
79
+ );
80
+ }
81
+ return value;
82
+ }
83
+ function shellEscape(str) {
84
+ return `'${str.replace(/'/g, "'\\''")}'`;
85
+ }
86
+ async function runSandboxCommand(sandbox, cmd, timeoutMs) {
87
+ let stdout = "";
88
+ let stderr = "";
89
+ try {
90
+ const result = await sandbox.commands.run(cmd, {
91
+ timeoutMs,
92
+ onStdout: (data) => {
93
+ stdout += data;
94
+ },
95
+ onStderr: (data) => {
96
+ stderr += data;
97
+ }
98
+ });
99
+ return {
100
+ exitCode: result.exitCode,
101
+ stdout: stdout || result.stdout,
102
+ stderr: stderr || result.stderr
103
+ };
104
+ } catch (error) {
105
+ const exitCode = error.exitCode ?? 1;
106
+ const errorMessage = error instanceof Error ? error.message : String(error);
107
+ return {
108
+ exitCode,
109
+ stdout,
110
+ stderr: stderr || errorMessage
111
+ };
112
+ }
113
+ }
114
+ async function resolvePackageVersion(pkg, sourceFileDir) {
115
+ try {
116
+ const pkgJsonPath = __require.resolve(`${pkg}/package.json`, { paths: [sourceFileDir] });
117
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
118
+ if (pkgJson.version) return pkgJson.version;
119
+ } catch {
120
+ }
121
+ try {
122
+ const pkgJsonPath = __require.resolve(`${pkg}/package.json`, { paths: [process.cwd()] });
123
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
124
+ if (pkgJson.version) return pkgJson.version;
125
+ } catch {
126
+ }
127
+ try {
128
+ const { resolvePackageJSON, readPackageJSON } = await getPkgTypes();
129
+ const pkgDir = join(process.cwd(), "node_modules", pkg);
130
+ const pkgJsonPath = await resolvePackageJSON(pkgDir);
131
+ if (pkgJsonPath) {
132
+ const pkgJson = await readPackageJSON(pkgJsonPath);
133
+ if (pkgJson.version) return pkgJson.version;
134
+ }
135
+ } catch {
136
+ }
137
+ try {
138
+ const directPath = join(process.cwd(), "node_modules", pkg, "package.json");
139
+ const pkgJson = JSON.parse(readFileSync(directPath, "utf-8"));
140
+ if (pkgJson.version) return pkgJson.version;
141
+ } catch {
142
+ }
143
+ let currentDir = sourceFileDir;
144
+ while (currentDir !== dirname(currentDir)) {
145
+ try {
146
+ const pkgJsonPath = join(currentDir, "node_modules", pkg, "package.json");
147
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
148
+ if (pkgJson.version) return pkgJson.version;
149
+ } catch {
150
+ }
151
+ currentDir = dirname(currentDir);
152
+ }
153
+ throw new Error(
154
+ `Could not resolve version for package "${pkg}". Ensure it is installed in node_modules. Searched from: ${sourceFileDir} and ${process.cwd()}`
155
+ );
156
+ }
157
+ function getCallerFile() {
158
+ const stack = new Error().stack?.split("\n") ?? [];
159
+ for (const line of stack.slice(2)) {
160
+ const match = line.match(/\((.+?):\d+:\d+\)/) || line.match(/at (.+?):\d+:\d+/);
161
+ if (match) {
162
+ let file = match[1];
163
+ if (file.startsWith("file://")) file = fileURLToPath(file);
164
+ if (!file.includes("node:") && !file.includes("node_modules/agent-neckbeard") && !file.includes("agent-neckbeard/dist")) return file;
165
+ }
166
+ }
167
+ throw new Error("Could not determine source file");
168
+ }
169
+ function readDirectoryRecursively(dirPath) {
170
+ const files = [];
171
+ function walkDir(currentPath) {
172
+ const entries = readdirSync(currentPath);
173
+ for (const entry of entries) {
174
+ const fullPath = join(currentPath, entry);
175
+ const stat = statSync(fullPath);
176
+ if (stat.isDirectory()) {
177
+ walkDir(fullPath);
178
+ } else if (stat.isFile()) {
179
+ const relPath = relative(dirPath, fullPath);
180
+ const isBinary = /\.(png|jpg|jpeg|gif|ico|pdf|zip|tar|gz|bin|exe|dll|so|dylib|wasm)$/i.test(entry);
181
+ if (isBinary) {
182
+ const buffer = readFileSync(fullPath);
183
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
184
+ files.push({ relativePath: relPath, content: arrayBuffer });
185
+ } else {
186
+ files.push({ relativePath: relPath, content: readFileSync(fullPath, "utf-8") });
187
+ }
188
+ }
189
+ }
190
+ }
191
+ walkDir(dirPath);
192
+ return files;
193
+ }
194
+ var Agent = class {
195
+ template;
196
+ inputSchema;
197
+ outputSchema;
198
+ maxDuration;
199
+ dependencies;
200
+ files;
201
+ claudeDir;
202
+ /** @internal Used by the sandbox runner - must be public for bundled code access */
203
+ _run;
204
+ _sourceFile;
205
+ _sandboxId;
206
+ constructor(config) {
207
+ this.template = config.template;
208
+ this.inputSchema = config.inputSchema;
209
+ this.outputSchema = config.outputSchema;
210
+ this.maxDuration = config.maxDuration ?? DEFAULT_MAX_DURATION;
211
+ this._run = config.run;
212
+ this._sourceFile = getCallerFile();
213
+ this._sandboxId = config.sandboxId;
214
+ this.dependencies = config.dependencies ?? DEFAULT_DEPENDENCIES;
215
+ this.files = config.files ?? [];
216
+ this.claudeDir = config.claudeDir;
217
+ }
218
+ get sandboxId() {
219
+ return this._sandboxId;
220
+ }
221
+ /**
222
+ * Deploys the agent to an E2B sandbox.
223
+ *
224
+ * This method bundles the agent code using esbuild, creates a new E2B sandbox,
225
+ * installs any specified OS dependencies, downloads configured files, and
226
+ * uploads the agent code to the sandbox.
227
+ *
228
+ * The sandbox ID is stored and can be accessed via the `sandboxId` property
229
+ * after deployment completes.
230
+ *
231
+ * @throws {ConfigurationError} If E2B_API_KEY environment variable is not set
232
+ * @throws {DeploymentError} If sandbox creation, dependency installation, or file upload fails
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * const agent = new Agent({ ... });
237
+ * await agent.deploy();
238
+ * console.log(`Deployed to sandbox: ${agent.sandboxId}`);
239
+ * ```
240
+ */
241
+ async deploy() {
242
+ if (this._sandboxId) return;
243
+ const e2bApiKey = requireEnv("E2B_API_KEY");
244
+ const esbuild = await getEsbuild();
245
+ const { Sandbox, Template } = await getE2b();
246
+ const templateExists = await Template.aliasExists(this.template, { apiKey: e2bApiKey });
247
+ if (!templateExists) {
248
+ throw new DeploymentError(
249
+ `Template "${this.template}" not found. Create it via E2B CLI: e2b template build --name ${this.template} --cpu-count 2 --memory-mb 2048
250
+ Or use an existing template like 'code-interpreter-v1' (2 cores, 2048 MB) or 'base' (2 cores, 512 MB).`
251
+ );
252
+ }
253
+ const collectedExternals = /* @__PURE__ */ new Set();
254
+ const result = await esbuild.build({
255
+ entryPoints: [this._sourceFile],
256
+ bundle: true,
257
+ platform: "node",
258
+ target: NODE_TARGET,
259
+ format: "esm",
260
+ write: false,
261
+ minify: true,
262
+ keepNames: true,
263
+ treeShaking: false,
264
+ // Preserve exports for the sandbox runner to import
265
+ banner: {
266
+ js: `import { fileURLToPath as __neckbeard_fileURLToPath } from 'node:url';
267
+ import { dirname as __neckbeard_dirname } from 'node:path';
268
+ var __filename = __neckbeard_fileURLToPath(import.meta.url);
269
+ var __dirname = __neckbeard_dirname(__filename);
270
+ `
271
+ },
272
+ plugins: [{
273
+ name: "agent-neckbeard-externals",
274
+ setup(build) {
275
+ build.onResolve({ filter: /^agent-neckbeard$/ }, () => ({
276
+ path: "agent-neckbeard",
277
+ namespace: "agent-shim"
278
+ }));
279
+ build.onLoad({ filter: /.*/, namespace: "agent-shim" }, () => ({
280
+ contents: `
281
+ export class Agent {
282
+ constructor(config) {
283
+ this.inputSchema = config.inputSchema;
284
+ this.outputSchema = config.outputSchema;
285
+ this.maxDuration = config.maxDuration ?? 300;
286
+ this._run = config.run;
287
+ }
288
+ }
289
+ `,
290
+ loader: "js"
291
+ }));
292
+ build.onResolve({ filter: /^[^.\/]|^\.[^.\/]|^\.\.[^\/]/ }, (args) => {
293
+ if (args.path.startsWith("node:")) {
294
+ return null;
295
+ }
296
+ if (builtinModules.includes(args.path.replace("node:", ""))) {
297
+ return null;
298
+ }
299
+ const match = args.path.match(/^(@[^/]+\/[^/]+|[^/]+)/);
300
+ if (match) {
301
+ collectedExternals.add(match[1]);
302
+ }
303
+ return { external: true };
304
+ });
305
+ }
306
+ }]
307
+ });
308
+ const runnerCode = `
309
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
310
+
311
+ const IO_DIR = '/home/user/io';
312
+ const INPUT_FILE = IO_DIR + '/input.json';
313
+ const OUTPUT_FILE = IO_DIR + '/output.json';
314
+
315
+ mkdirSync(IO_DIR, { recursive: true });
316
+
317
+ let input, executionId;
318
+ try {
319
+ const taskData = JSON.parse(readFileSync(INPUT_FILE, 'utf-8'));
320
+ input = taskData.input;
321
+ executionId = taskData.executionId;
322
+ } catch (parseError) {
323
+ writeFileSync(OUTPUT_FILE, JSON.stringify({ success: false, error: { message: 'Task parse failed: ' + parseError.message, stack: parseError.stack } }));
324
+ process.exit(0);
325
+ }
326
+
327
+ let mod;
328
+ try {
329
+ mod = await import('./agent.mjs');
330
+ } catch (importError) {
331
+ writeFileSync(OUTPUT_FILE, JSON.stringify({ success: false, error: { message: 'Import failed: ' + importError.message, stack: importError.stack } }));
332
+ process.exit(0);
333
+ }
334
+
335
+ const agent = mod.default || Object.values(mod).find(v => v instanceof Object && v._run);
336
+ if (!agent) {
337
+ writeFileSync(OUTPUT_FILE, JSON.stringify({ success: false, error: { message: 'No agent found in module. Exports: ' + Object.keys(mod).join(', ') } }));
338
+ process.exit(0);
339
+ }
340
+
341
+ const ctx = {
342
+ executionId,
343
+ signal: AbortSignal.timeout(${this.maxDuration * 1e3}),
344
+ env: process.env,
345
+ logger: {
346
+ debug: (msg, ...args) => console.log('[DEBUG]', msg, ...args),
347
+ info: (msg, ...args) => console.log('[INFO]', msg, ...args),
348
+ warn: (msg, ...args) => console.warn('[WARN]', msg, ...args),
349
+ error: (msg, ...args) => console.error('[ERROR]', msg, ...args),
350
+ },
351
+ };
352
+
353
+ try {
354
+ const validated = agent.inputSchema.parse(input);
355
+ const output = await agent._run(validated, ctx);
356
+ const validatedOutput = agent.outputSchema.parse(output);
357
+ writeFileSync(OUTPUT_FILE, JSON.stringify({ success: true, output: validatedOutput }));
358
+ } catch (error) {
359
+ writeFileSync(OUTPUT_FILE, JSON.stringify({ success: false, error: { message: error.message, stack: error.stack } }));
360
+ }
361
+ `;
362
+ const sandbox = await Sandbox.create(this.template, {
363
+ apiKey: e2bApiKey
364
+ });
365
+ await runSandboxCommand(sandbox, `mkdir -p ${SANDBOX_PATHS.agentDir}`, QUICK_COMMAND_TIMEOUT_MS);
366
+ const { apt, commands } = this.dependencies;
367
+ if (apt && apt.length > 0) {
368
+ const aptCmd = `sudo apt-get update && sudo apt-get install -y ${apt.join(" ")}`;
369
+ const aptResult = await runSandboxCommand(sandbox, aptCmd, SANDBOX_COMMAND_TIMEOUT_MS);
370
+ if (aptResult.exitCode !== 0) {
371
+ const details = [
372
+ `Failed to install apt packages: ${apt.join(", ")}`,
373
+ aptResult.stderr ? `stderr: ${aptResult.stderr}` : "",
374
+ aptResult.stdout ? `stdout: ${aptResult.stdout}` : ""
375
+ ].filter(Boolean).join("\n");
376
+ throw new DeploymentError(details);
377
+ }
378
+ }
379
+ if (commands && commands.length > 0) {
380
+ for (const cmd of commands) {
381
+ const cmdResult = await runSandboxCommand(sandbox, cmd, SANDBOX_COMMAND_TIMEOUT_MS);
382
+ if (cmdResult.exitCode !== 0) {
383
+ const details = [
384
+ `Failed to run command: ${cmd}`,
385
+ cmdResult.stderr ? `stderr: ${cmdResult.stderr}` : "",
386
+ cmdResult.stdout ? `stdout: ${cmdResult.stdout}` : ""
387
+ ].filter(Boolean).join("\n");
388
+ throw new DeploymentError(details);
389
+ }
390
+ }
391
+ }
392
+ if (this.files.length > 0) {
393
+ for (const file of this.files) {
394
+ const destPath = file.path.startsWith("/") ? file.path : `${SANDBOX_HOME}/${file.path}`;
395
+ const parentDir = destPath.substring(0, destPath.lastIndexOf("/"));
396
+ if (parentDir) {
397
+ await runSandboxCommand(sandbox, `mkdir -p ${shellEscape(parentDir)}`, QUICK_COMMAND_TIMEOUT_MS);
398
+ }
399
+ const curlCmd = `curl -fsSL -o ${shellEscape(destPath)} ${shellEscape(file.url)}`;
400
+ const downloadResult = await runSandboxCommand(sandbox, curlCmd, SANDBOX_COMMAND_TIMEOUT_MS);
401
+ if (downloadResult.exitCode !== 0) {
402
+ const details = [
403
+ `Failed to download file from ${file.url} to ${destPath}`,
404
+ downloadResult.stderr ? `stderr: ${downloadResult.stderr}` : "",
405
+ downloadResult.stdout ? `stdout: ${downloadResult.stdout}` : ""
406
+ ].filter(Boolean).join("\n");
407
+ throw new DeploymentError(details);
408
+ }
409
+ }
410
+ }
411
+ if (this.claudeDir) {
412
+ const claudeFiles = readDirectoryRecursively(this.claudeDir);
413
+ await runSandboxCommand(sandbox, `mkdir -p ${SANDBOX_PATHS.claudeDir}`, QUICK_COMMAND_TIMEOUT_MS);
414
+ for (const file of claudeFiles) {
415
+ const destPath = `${SANDBOX_PATHS.claudeDir}/${file.relativePath}`;
416
+ const parentDir = destPath.substring(0, destPath.lastIndexOf("/"));
417
+ if (parentDir && parentDir !== SANDBOX_PATHS.claudeDir) {
418
+ await runSandboxCommand(sandbox, `mkdir -p ${shellEscape(parentDir)}`, QUICK_COMMAND_TIMEOUT_MS);
419
+ }
420
+ await sandbox.files.write(destPath, file.content);
421
+ }
422
+ }
423
+ await sandbox.files.write(SANDBOX_PATHS.agentModule, result.outputFiles[0].text);
424
+ await sandbox.files.write(SANDBOX_PATHS.runnerModule, runnerCode);
425
+ if (collectedExternals.size > 0) {
426
+ const dependencies = {};
427
+ const sourceFileDir = dirname(this._sourceFile);
428
+ for (const pkg of collectedExternals) {
429
+ try {
430
+ dependencies[pkg] = await resolvePackageVersion(pkg, sourceFileDir);
431
+ } catch (err) {
432
+ throw new DeploymentError(
433
+ `Failed to resolve version for package "${pkg}": ${err instanceof Error ? err.message : String(err)}`
434
+ );
435
+ }
436
+ }
437
+ const pkgJson = JSON.stringify({
438
+ name: "agent-sandbox",
439
+ type: "module",
440
+ dependencies
441
+ });
442
+ await sandbox.files.write(SANDBOX_PATHS.packageJson, pkgJson);
443
+ const installResult = await runSandboxCommand(sandbox, `cd ${SANDBOX_PATHS.agentDir} && npm install --legacy-peer-deps`, SANDBOX_COMMAND_TIMEOUT_MS);
444
+ if (installResult.exitCode !== 0) {
445
+ const details = [
446
+ `Failed to install npm packages: ${Object.keys(dependencies).join(", ")}`,
447
+ installResult.stderr ? `stderr: ${installResult.stderr}` : "",
448
+ installResult.stdout ? `stdout: ${installResult.stdout}` : ""
449
+ ].filter(Boolean).join("\n");
450
+ throw new DeploymentError(details);
451
+ }
452
+ }
453
+ this._sandboxId = sandbox.sandboxId;
454
+ }
455
+ /**
456
+ * Executes the agent with the given input.
457
+ *
458
+ * The agent must be deployed before calling this method. Input is validated
459
+ * against the input schema, then the agent runs in the sandbox with the
460
+ * validated input. Output is validated against the output schema before
461
+ * being returned.
462
+ *
463
+ * @param input - The input data for the agent, must conform to inputSchema
464
+ * @returns A result object indicating success or failure with output or error
465
+ *
466
+ * @example
467
+ * ```typescript
468
+ * const result = await agent.run({ prompt: 'Hello, world!' });
469
+ * if (result.ok) {
470
+ * console.log('Output:', result.output);
471
+ * } else {
472
+ * console.error('Error:', result.error.message);
473
+ * }
474
+ * ```
475
+ */
476
+ async run(input) {
477
+ const executionId = `exec_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
478
+ if (!this._sandboxId) {
479
+ return {
480
+ ok: false,
481
+ executionId,
482
+ error: new ExecutionError("Agent not deployed. Call agent.deploy() first or pass sandboxId to constructor.")
483
+ };
484
+ }
485
+ try {
486
+ const e2bApiKey = requireEnv("E2B_API_KEY");
487
+ const { Sandbox } = await getE2b();
488
+ const validatedInput = this.inputSchema.parse(input);
489
+ const sandbox = await Sandbox.connect(this._sandboxId, {
490
+ apiKey: e2bApiKey
491
+ });
492
+ await sandbox.files.write(SANDBOX_PATHS.inputJson, JSON.stringify({ input: validatedInput, executionId }));
493
+ let capturedStdout = "";
494
+ let capturedStderr = "";
495
+ const result = await sandbox.commands.run(`cd ${SANDBOX_PATHS.agentDir} && node runner.mjs`, {
496
+ timeoutMs: this.maxDuration * 1e3,
497
+ envs: {
498
+ ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? ""
499
+ },
500
+ onStdout: (data) => {
501
+ capturedStdout += data;
502
+ },
503
+ onStderr: (data) => {
504
+ capturedStderr += data;
505
+ }
506
+ });
507
+ if (result.exitCode !== 0) {
508
+ let resultError = "";
509
+ try {
510
+ const resultJson = await sandbox.files.read(SANDBOX_PATHS.outputJson);
511
+ const parsed = JSON.parse(resultJson);
512
+ if (!parsed.success && parsed.error) {
513
+ resultError = `
514
+ Result: ${parsed.error.message}`;
515
+ if (parsed.error.stack) resultError += `
516
+ Stack: ${parsed.error.stack}`;
517
+ }
518
+ } catch {
519
+ }
520
+ const errorDetails = [
521
+ `Agent failed with exit code ${result.exitCode}`,
522
+ result.stderr ? `Stderr: ${result.stderr}` : "",
523
+ capturedStderr ? `Captured stderr: ${capturedStderr}` : "",
524
+ capturedStdout ? `Stdout: ${capturedStdout}` : "",
525
+ resultError
526
+ ].filter(Boolean).join("\n");
527
+ return { ok: false, executionId, error: new ExecutionError(errorDetails) };
528
+ }
529
+ const output = JSON.parse(await sandbox.files.read(SANDBOX_PATHS.outputJson));
530
+ if (!output.success) {
531
+ const errorDetails = [
532
+ output.error.message,
533
+ capturedStderr ? `
534
+ Captured stderr: ${capturedStderr}` : "",
535
+ capturedStdout ? `
536
+ Stdout: ${capturedStdout}` : ""
537
+ ].filter(Boolean).join("");
538
+ const err = new ExecutionError(errorDetails);
539
+ err.stack = output.error.stack;
540
+ return { ok: false, executionId, error: err };
541
+ }
542
+ return { ok: true, executionId, output: this.outputSchema.parse(output.output) };
543
+ } catch (err) {
544
+ return { ok: false, executionId, error: err instanceof Error ? err : new Error(String(err)) };
545
+ }
546
+ }
547
+ };
548
+ export {
549
+ Agent,
550
+ ConfigurationError,
551
+ DEFAULT_DEPENDENCIES,
552
+ DeploymentError,
553
+ ExecutionError,
554
+ NeckbeardError,
555
+ ValidationError
556
+ };
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "neckbeard-agent",
3
+ "version": "0.0.1",
4
+ "description": "Deploy AI agents to E2B sandboxes",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
16
+ }
17
+ },
18
+ "main": "./dist/index.cjs",
19
+ "module": "./dist/index.js",
20
+ "types": "./dist/index.d.cts",
21
+ "sideEffects": false,
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20.0.0"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "typecheck": "tsc --noEmit",
32
+ "prepare": "npm run build"
33
+ },
34
+ "dependencies": {
35
+ "e2b": "^2.10.2",
36
+ "esbuild": "^0.24.0",
37
+ "pkg-types": "^2.3.0"
38
+ },
39
+ "peerDependencies": {
40
+ "zod": "^3.0.0 || ^4.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "zod": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.0.0",
49
+ "tsup": "^8.3.0",
50
+ "typescript": "^5.6.0"
51
+ },
52
+ "keywords": [
53
+ "ai",
54
+ "agent",
55
+ "deploy",
56
+ "e2b",
57
+ "sandbox",
58
+ "claude"
59
+ ],
60
+ "license": "MIT",
61
+ "author": "zacwellmer",
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/zacwellmer/agent-neckbeard.git"
65
+ },
66
+ "homepage": "https://github.com/zacwellmer/agent-neckbeard#readme",
67
+ "bugs": {
68
+ "url": "https://github.com/zacwellmer/agent-neckbeard/issues"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ }
73
+ }