neckbeard-agent 0.0.4 → 0.0.7

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 CHANGED
@@ -41,6 +41,9 @@ const agent = new Agent({
41
41
  summary: z.string(),
42
42
  keyPoints: z.array(z.string()),
43
43
  }),
44
+ envs: {
45
+ ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
46
+ },
44
47
  run: async (input) => {
45
48
  for await (const message of query({
46
49
  prompt: `Research "${input.topic}" and return JSON`,
@@ -67,7 +70,7 @@ npm install neckbeard-agent
67
70
 
68
71
  ```bash
69
72
  export E2B_API_KEY=your-key
70
- export ANTHROPIC_API_KEY=your-key
73
+ export ANTHROPIC_API_KEY=your-key # Pass via envs config, not auto-forwarded
71
74
  ```
72
75
 
73
76
  ## The Details
@@ -87,6 +90,7 @@ new Agent({
87
90
  },
88
91
  files?: [{ url, path }], // pre-download into sandbox
89
92
  claudeDir?: string, // upload .claude/ skills directory
93
+ envs?: Record<string, string | undefined>, // environment variables for sandbox
90
94
  })
91
95
  ```
92
96
 
@@ -102,6 +106,19 @@ The `files` option downloads things into the sandbox before your agent runs—us
102
106
 
103
107
  The `claudeDir` option uploads a local `.claude/` directory to the sandbox, enabling Claude Agent SDK skills. Point it at a directory containing `.claude/skills/*/SKILL.md` files.
104
108
 
109
+ The `envs` option passes environment variables to the sandbox. These are available to your agent code via `process.env` and `ctx.env`. Undefined values are filtered out:
110
+
111
+ ```typescript
112
+ const agent = new Agent({
113
+ template: 'code-interpreter-v1',
114
+ envs: {
115
+ ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
116
+ MY_API_KEY: process.env.MY_API_KEY,
117
+ },
118
+ // ...
119
+ });
120
+ ```
121
+
105
122
  Some packages can't be bundled because they spawn child processes or have native modules. The Claude Agent SDK is like this. These get automatically marked as external and installed via npm in the sandbox.
106
123
 
107
124
  The `run` function gets a context object with an `executionId`, an `AbortSignal`, environment variables, and a logger.
package/dist/index.cjs CHANGED
@@ -144,6 +144,25 @@ async function runSandboxCommand(sandbox, cmd, timeoutMs) {
144
144
  };
145
145
  }
146
146
  }
147
+ function isWorkspacePackage(pkg, searchPaths) {
148
+ for (const basePath of searchPaths) {
149
+ try {
150
+ const pkgJsonPath = require.resolve(`${pkg}/package.json`, { paths: [basePath] });
151
+ if (!pkgJsonPath.includes("node_modules")) {
152
+ return true;
153
+ }
154
+ const pkgJson = JSON.parse((0, import_node_fs.readFileSync)(pkgJsonPath, "utf-8"));
155
+ const version = pkgJson.version || "";
156
+ if (version.startsWith("workspace:")) {
157
+ return true;
158
+ }
159
+ return false;
160
+ } catch {
161
+ continue;
162
+ }
163
+ }
164
+ return false;
165
+ }
147
166
  async function resolvePackageVersion(pkg, sourceFileDir) {
148
167
  try {
149
168
  const pkgJsonPath = require.resolve(`${pkg}/package.json`, { paths: [sourceFileDir] });
@@ -232,6 +251,7 @@ var Agent = class {
232
251
  dependencies;
233
252
  files;
234
253
  claudeDir;
254
+ envs;
235
255
  /** @internal Used by the sandbox runner - must be public for bundled code access */
236
256
  _run;
237
257
  _sourceFile;
@@ -246,6 +266,7 @@ var Agent = class {
246
266
  this.dependencies = config.dependencies ?? DEFAULT_DEPENDENCIES;
247
267
  this.files = config.files ?? [];
248
268
  this.claudeDir = config.claudeDir;
269
+ this.envs = config.envs ?? {};
249
270
  }
250
271
  get sandboxId() {
251
272
  return this._sandboxId;
@@ -274,6 +295,7 @@ var Agent = class {
274
295
  const esbuild = await getEsbuild();
275
296
  const { Sandbox } = await getE2b();
276
297
  const collectedExternals = /* @__PURE__ */ new Set();
298
+ const sourceFileDir = (0, import_node_path.dirname)(this._sourceFile);
277
299
  const result = await esbuild.build({
278
300
  entryPoints: [this._sourceFile],
279
301
  bundle: true,
@@ -320,9 +342,14 @@ var __dirname = __neckbeard_dirname(__filename);
320
342
  return null;
321
343
  }
322
344
  const match = args.path.match(/^(@[^/]+\/[^/]+|[^/]+)/);
323
- if (match) {
324
- collectedExternals.add(match[1]);
345
+ if (!match) {
346
+ return { external: true };
325
347
  }
348
+ const pkgName = match[1];
349
+ if (isWorkspacePackage(pkgName, [sourceFileDir, process.cwd()])) {
350
+ return null;
351
+ }
352
+ collectedExternals.add(pkgName);
326
353
  return { external: true };
327
354
  });
328
355
  }
@@ -447,7 +474,6 @@ try {
447
474
  await sandbox.files.write(SANDBOX_PATHS.runnerModule, runnerCode);
448
475
  if (collectedExternals.size > 0) {
449
476
  const dependencies = {};
450
- const sourceFileDir = (0, import_node_path.dirname)(this._sourceFile);
451
477
  for (const pkg of collectedExternals) {
452
478
  try {
453
479
  dependencies[pkg] = await resolvePackageVersion(pkg, sourceFileDir);
@@ -520,9 +546,9 @@ try {
520
546
  let capturedStderr = "";
521
547
  const result = await sandbox.commands.run(`cd ${SANDBOX_PATHS.agentDir} && node runner.mjs`, {
522
548
  timeoutMs: this.maxDuration * 1e3,
523
- envs: {
524
- ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? ""
525
- },
549
+ envs: Object.fromEntries(
550
+ Object.entries(this.envs).filter(([_, v]) => v !== void 0).map(([k, v]) => [k, v])
551
+ ),
526
552
  onStdout: (data) => {
527
553
  capturedStdout += data;
528
554
  },
package/dist/index.d.cts CHANGED
@@ -124,6 +124,20 @@ interface AgentConfig<TInput, TOutput> {
124
124
  * - allowedTools includes 'Skill'
125
125
  */
126
126
  claudeDir?: string;
127
+ /**
128
+ * Environment variables to pass to the sandbox.
129
+ * These will be available to the agent code via process.env and ctx.env.
130
+ * Undefined values are filtered out.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * envs: {
135
+ * ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
136
+ * MY_API_KEY: process.env.MY_API_KEY,
137
+ * }
138
+ * ```
139
+ */
140
+ envs?: Record<string, string | undefined>;
127
141
  }
128
142
  declare class Agent<TInput, TOutput> {
129
143
  readonly template: string;
@@ -133,6 +147,7 @@ declare class Agent<TInput, TOutput> {
133
147
  readonly dependencies: OsDependencies;
134
148
  readonly files: FileDownload[];
135
149
  readonly claudeDir?: string;
150
+ readonly envs: Record<string, string | undefined>;
136
151
  /** @internal Used by the sandbox runner - must be public for bundled code access */
137
152
  _run: (input: TInput, ctx: AgentRunContext) => Promise<TOutput>;
138
153
  private _sourceFile;
package/dist/index.d.ts CHANGED
@@ -124,6 +124,20 @@ interface AgentConfig<TInput, TOutput> {
124
124
  * - allowedTools includes 'Skill'
125
125
  */
126
126
  claudeDir?: string;
127
+ /**
128
+ * Environment variables to pass to the sandbox.
129
+ * These will be available to the agent code via process.env and ctx.env.
130
+ * Undefined values are filtered out.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * envs: {
135
+ * ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
136
+ * MY_API_KEY: process.env.MY_API_KEY,
137
+ * }
138
+ * ```
139
+ */
140
+ envs?: Record<string, string | undefined>;
127
141
  }
128
142
  declare class Agent<TInput, TOutput> {
129
143
  readonly template: string;
@@ -133,6 +147,7 @@ declare class Agent<TInput, TOutput> {
133
147
  readonly dependencies: OsDependencies;
134
148
  readonly files: FileDownload[];
135
149
  readonly claudeDir?: string;
150
+ readonly envs: Record<string, string | undefined>;
136
151
  /** @internal Used by the sandbox runner - must be public for bundled code access */
137
152
  _run: (input: TInput, ctx: AgentRunContext) => Promise<TOutput>;
138
153
  private _sourceFile;
package/dist/index.js CHANGED
@@ -111,6 +111,25 @@ async function runSandboxCommand(sandbox, cmd, timeoutMs) {
111
111
  };
112
112
  }
113
113
  }
114
+ function isWorkspacePackage(pkg, searchPaths) {
115
+ for (const basePath of searchPaths) {
116
+ try {
117
+ const pkgJsonPath = __require.resolve(`${pkg}/package.json`, { paths: [basePath] });
118
+ if (!pkgJsonPath.includes("node_modules")) {
119
+ return true;
120
+ }
121
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
122
+ const version = pkgJson.version || "";
123
+ if (version.startsWith("workspace:")) {
124
+ return true;
125
+ }
126
+ return false;
127
+ } catch {
128
+ continue;
129
+ }
130
+ }
131
+ return false;
132
+ }
114
133
  async function resolvePackageVersion(pkg, sourceFileDir) {
115
134
  try {
116
135
  const pkgJsonPath = __require.resolve(`${pkg}/package.json`, { paths: [sourceFileDir] });
@@ -199,6 +218,7 @@ var Agent = class {
199
218
  dependencies;
200
219
  files;
201
220
  claudeDir;
221
+ envs;
202
222
  /** @internal Used by the sandbox runner - must be public for bundled code access */
203
223
  _run;
204
224
  _sourceFile;
@@ -213,6 +233,7 @@ var Agent = class {
213
233
  this.dependencies = config.dependencies ?? DEFAULT_DEPENDENCIES;
214
234
  this.files = config.files ?? [];
215
235
  this.claudeDir = config.claudeDir;
236
+ this.envs = config.envs ?? {};
216
237
  }
217
238
  get sandboxId() {
218
239
  return this._sandboxId;
@@ -241,6 +262,7 @@ var Agent = class {
241
262
  const esbuild = await getEsbuild();
242
263
  const { Sandbox } = await getE2b();
243
264
  const collectedExternals = /* @__PURE__ */ new Set();
265
+ const sourceFileDir = dirname(this._sourceFile);
244
266
  const result = await esbuild.build({
245
267
  entryPoints: [this._sourceFile],
246
268
  bundle: true,
@@ -287,9 +309,14 @@ var __dirname = __neckbeard_dirname(__filename);
287
309
  return null;
288
310
  }
289
311
  const match = args.path.match(/^(@[^/]+\/[^/]+|[^/]+)/);
290
- if (match) {
291
- collectedExternals.add(match[1]);
312
+ if (!match) {
313
+ return { external: true };
292
314
  }
315
+ const pkgName = match[1];
316
+ if (isWorkspacePackage(pkgName, [sourceFileDir, process.cwd()])) {
317
+ return null;
318
+ }
319
+ collectedExternals.add(pkgName);
293
320
  return { external: true };
294
321
  });
295
322
  }
@@ -414,7 +441,6 @@ try {
414
441
  await sandbox.files.write(SANDBOX_PATHS.runnerModule, runnerCode);
415
442
  if (collectedExternals.size > 0) {
416
443
  const dependencies = {};
417
- const sourceFileDir = dirname(this._sourceFile);
418
444
  for (const pkg of collectedExternals) {
419
445
  try {
420
446
  dependencies[pkg] = await resolvePackageVersion(pkg, sourceFileDir);
@@ -487,9 +513,9 @@ try {
487
513
  let capturedStderr = "";
488
514
  const result = await sandbox.commands.run(`cd ${SANDBOX_PATHS.agentDir} && node runner.mjs`, {
489
515
  timeoutMs: this.maxDuration * 1e3,
490
- envs: {
491
- ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? ""
492
- },
516
+ envs: Object.fromEntries(
517
+ Object.entries(this.envs).filter(([_, v]) => v !== void 0).map(([k, v]) => [k, v])
518
+ ),
493
519
  onStdout: (data) => {
494
520
  capturedStdout += data;
495
521
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neckbeard-agent",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "description": "Deploy AI agents to E2B sandboxes",
5
5
  "type": "module",
6
6
  "exports": {