bashkit 0.2.4 → 0.3.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/AGENTS.md +13 -8
- package/README.md +27 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +124 -37
- package/dist/sandbox/e2b.d.ts +6 -1
- package/dist/sandbox/ensure-tools.d.ts +22 -0
- package/dist/sandbox/index.d.ts +1 -0
- package/dist/sandbox/interface.d.ts +5 -0
- package/dist/sandbox/lazy-singleton.d.ts +25 -0
- package/dist/sandbox/vercel.d.ts +6 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -34,9 +34,12 @@ Runs in isolated Firecracker microVMs on Vercel's infrastructure.
|
|
|
34
34
|
```typescript
|
|
35
35
|
import { createAgentTools, createVercelSandbox } from "bashkit";
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// Async - automatically installs ripgrep for Grep tool
|
|
38
|
+
const sandbox = await createVercelSandbox({
|
|
38
39
|
runtime: "node22",
|
|
39
40
|
resources: { vcpus: 2 },
|
|
41
|
+
// ensureTools: true (default) - auto-setup ripgrep
|
|
42
|
+
// ensureTools: false - skip for faster startup if you don't need Grep
|
|
40
43
|
});
|
|
41
44
|
const { tools } = createAgentTools(sandbox);
|
|
42
45
|
|
|
@@ -51,8 +54,11 @@ Runs in E2B's cloud sandboxes. Requires `@e2b/code-interpreter` peer dependency.
|
|
|
51
54
|
```typescript
|
|
52
55
|
import { createAgentTools, createE2BSandbox } from "bashkit";
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
// Async - automatically installs ripgrep for Grep tool
|
|
58
|
+
const sandbox = await createE2BSandbox({
|
|
55
59
|
apiKey: process.env.E2B_API_KEY,
|
|
60
|
+
// ensureTools: true (default) - auto-setup ripgrep
|
|
61
|
+
// ensureTools: false - skip for faster startup if you don't need Grep
|
|
56
62
|
});
|
|
57
63
|
const { tools } = createAgentTools(sandbox);
|
|
58
64
|
|
|
@@ -65,18 +71,17 @@ Cloud sandboxes (E2B, Vercel) support reconnection via the `id` property and `sa
|
|
|
65
71
|
|
|
66
72
|
```typescript
|
|
67
73
|
// Create a new sandbox
|
|
68
|
-
const sandbox = createE2BSandbox({ apiKey: process.env.E2B_API_KEY });
|
|
74
|
+
const sandbox = await createE2BSandbox({ apiKey: process.env.E2B_API_KEY });
|
|
69
75
|
|
|
70
|
-
//
|
|
71
|
-
await sandbox.exec("echo hello");
|
|
76
|
+
// Sandbox ID is available immediately after creation
|
|
72
77
|
const sandboxId = sandbox.id; // "sbx_abc123..."
|
|
73
78
|
|
|
74
79
|
// Store sandboxId in your database (e.g., chat metadata)
|
|
75
80
|
await db.chat.update({ where: { id: chatId }, data: { sandboxId } });
|
|
76
81
|
|
|
77
|
-
// Later: reconnect to the same sandbox
|
|
82
|
+
// Later: reconnect to the same sandbox (fast - ripgrep already installed)
|
|
78
83
|
const savedId = chat.sandboxId;
|
|
79
|
-
const reconnected = createE2BSandbox({
|
|
84
|
+
const reconnected = await createE2BSandbox({
|
|
80
85
|
apiKey: process.env.E2B_API_KEY,
|
|
81
86
|
sandboxId: savedId, // Reconnects instead of creating new
|
|
82
87
|
});
|
|
@@ -442,7 +447,7 @@ const config = {
|
|
|
442
447
|
},
|
|
443
448
|
};
|
|
444
449
|
|
|
445
|
-
const sandbox = createVercelSandbox({});
|
|
450
|
+
const sandbox = await createVercelSandbox({});
|
|
446
451
|
const { skills } = await setupAgentEnvironment(sandbox, config);
|
|
447
452
|
|
|
448
453
|
// Use same config in prompt - stays in sync!
|
package/README.md
CHANGED
|
@@ -69,7 +69,8 @@ import { anthropic } from '@ai-sdk/anthropic';
|
|
|
69
69
|
import { streamText, stepCountIs } from 'ai';
|
|
70
70
|
|
|
71
71
|
// Create a Vercel sandbox (isolated Firecracker microVM)
|
|
72
|
-
|
|
72
|
+
// Note: async - automatically sets up ripgrep for Grep tool
|
|
73
|
+
const sandbox = await createVercelSandbox({
|
|
73
74
|
runtime: 'node22',
|
|
74
75
|
resources: { vcpus: 2 },
|
|
75
76
|
});
|
|
@@ -144,17 +145,19 @@ Runs in isolated Firecracker microVMs on Vercel's infrastructure. **Use when you
|
|
|
144
145
|
```typescript
|
|
145
146
|
import { createVercelSandbox } from 'bashkit';
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
// Async - automatically installs ripgrep for Grep tool
|
|
149
|
+
const sandbox = await createVercelSandbox({
|
|
148
150
|
runtime: 'node22',
|
|
149
151
|
resources: { vcpus: 2 },
|
|
152
|
+
// ensureTools: true (default) - auto-setup ripgrep
|
|
153
|
+
// ensureTools: false - skip for faster startup if you don't need Grep
|
|
150
154
|
});
|
|
151
155
|
|
|
152
|
-
//
|
|
153
|
-
await sandbox.exec('echo hello');
|
|
156
|
+
// Sandbox ID available immediately after creation
|
|
154
157
|
console.log(sandbox.id); // Sandbox ID for reconnection
|
|
155
158
|
|
|
156
|
-
// Later: reconnect to the same sandbox
|
|
157
|
-
const reconnected = createVercelSandbox({
|
|
159
|
+
// Later: reconnect to the same sandbox (fast - ripgrep already installed)
|
|
160
|
+
const reconnected = await createVercelSandbox({
|
|
158
161
|
sandboxId: 'existing-sandbox-id',
|
|
159
162
|
});
|
|
160
163
|
```
|
|
@@ -166,16 +169,18 @@ Runs in E2B's cloud sandboxes. Requires `@e2b/code-interpreter` peer dependency.
|
|
|
166
169
|
```typescript
|
|
167
170
|
import { createE2BSandbox } from 'bashkit';
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
// Async - automatically installs ripgrep for Grep tool
|
|
173
|
+
const sandbox = await createE2BSandbox({
|
|
170
174
|
apiKey: process.env.E2B_API_KEY,
|
|
175
|
+
// ensureTools: true (default) - auto-setup ripgrep
|
|
176
|
+
// ensureTools: false - skip for faster startup if you don't need Grep
|
|
171
177
|
});
|
|
172
178
|
|
|
173
|
-
//
|
|
174
|
-
await sandbox.exec('echo hello');
|
|
179
|
+
// Sandbox ID available immediately after creation
|
|
175
180
|
console.log(sandbox.id); // "sbx_abc123..."
|
|
176
181
|
|
|
177
|
-
// Later: reconnect to the same sandbox
|
|
178
|
-
const reconnected = createE2BSandbox({
|
|
182
|
+
// Later: reconnect to the same sandbox (fast - ripgrep already installed)
|
|
183
|
+
const reconnected = await createE2BSandbox({
|
|
179
184
|
apiKey: process.env.E2B_API_KEY,
|
|
180
185
|
sandboxId: 'sbx_abc123...', // Reconnect to existing sandbox
|
|
181
186
|
});
|
|
@@ -782,14 +787,20 @@ interface Sandbox {
|
|
|
782
787
|
writeFile(path: string, content: string): Promise<void>;
|
|
783
788
|
readDir(path: string): Promise<string[]>;
|
|
784
789
|
fileExists(path: string): Promise<boolean>;
|
|
790
|
+
isDirectory(path: string): Promise<boolean>;
|
|
785
791
|
destroy(): Promise<void>;
|
|
786
792
|
|
|
787
793
|
// Optional: Sandbox ID for reconnection (cloud providers only)
|
|
788
794
|
readonly id?: string;
|
|
795
|
+
|
|
796
|
+
// Path to ripgrep binary (set by ensureSandboxTools)
|
|
797
|
+
rgPath?: string;
|
|
789
798
|
}
|
|
790
799
|
```
|
|
791
800
|
|
|
792
|
-
The `id` property is available on cloud sandboxes (E2B, Vercel) after
|
|
801
|
+
The `id` property is available on cloud sandboxes (E2B, Vercel) after creation. Use it to persist the sandbox ID and reconnect later.
|
|
802
|
+
|
|
803
|
+
The `rgPath` property is set by `ensureSandboxTools()` (called automatically during sandbox creation). It points to the ripgrep binary for the Grep tool. Supports x86_64 and ARM64 architectures.
|
|
793
804
|
|
|
794
805
|
### Custom Sandbox Example
|
|
795
806
|
|
|
@@ -866,9 +877,10 @@ Creates a set of agent tools bound to a sandbox instance.
|
|
|
866
877
|
|
|
867
878
|
### Sandbox Factories
|
|
868
879
|
|
|
869
|
-
- `createLocalSandbox(config?)` - Local execution sandbox
|
|
870
|
-
- `createVercelSandbox(config?)` - Vercel Firecracker sandbox
|
|
871
|
-
- `createE2BSandbox(config?)` - E2B cloud sandbox
|
|
880
|
+
- `createLocalSandbox(config?)` - Local execution sandbox (sync)
|
|
881
|
+
- `createVercelSandbox(config?)` - Vercel Firecracker sandbox (async, auto-installs ripgrep)
|
|
882
|
+
- `createE2BSandbox(config?)` - E2B cloud sandbox (async, auto-installs ripgrep)
|
|
883
|
+
- `ensureSandboxTools(sandbox)` - Manually setup tools (called automatically by default)
|
|
872
884
|
|
|
873
885
|
### Workflow Tools
|
|
874
886
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { UIMessageStreamWriter, StreamTextResult, Tool, ToolSet, LanguageModel, LanguageModelMiddleware, Output, } from "ai";
|
|
2
2
|
export { anthropicPromptCacheMiddleware, anthropicPromptCacheMiddlewareV2, } from "./middleware";
|
|
3
3
|
export type { E2BSandboxConfig, LocalSandboxConfig, VercelSandboxConfig, } from "./sandbox";
|
|
4
|
-
export { createE2BSandbox, createLocalSandbox, createVercelSandbox, } from "./sandbox";
|
|
4
|
+
export { createE2BSandbox, createLocalSandbox, createVercelSandbox, ensureSandboxTools, } from "./sandbox";
|
|
5
5
|
export type { ExecOptions, ExecResult, Sandbox } from "./sandbox/interface";
|
|
6
6
|
export type { AgentToolsResult, AskUserError, AskUserOutput, AskUserResponseHandler, BashError, BashOutput, EditError, EditOutput, EnterPlanModeError, EnterPlanModeOutput, ExitPlanModeError, ExitPlanModeOutput, PlanModeState, GlobError, GlobOutput, GrepContentOutput, GrepCountOutput, GrepError, GrepFilesOutput, GrepMatch, GrepOutput, ReadDirectoryOutput, ReadError, ReadOutput, ReadTextOutput, SkillError, SkillOutput, SkillToolConfig, SubagentEventData, SubagentStepEvent, SubagentTypeConfig, TaskError, TaskOutput, TaskToolConfig, TodoItem, TodoState, TodoWriteError, TodoWriteOutput, WebFetchError, WebFetchOutput, WebSearchError, WebSearchOutput, WebSearchResult, WriteError, WriteOutput, } from "./tools";
|
|
7
7
|
export { createAgentTools, createAskUserTool, createBashTool, createEditTool, createEnterPlanModeTool, createExitPlanModeTool, createGlobTool, createGrepTool, createReadTool, createSkillTool, createTaskTool, createTodoWriteTool, createWebFetchTool, createWebSearchTool, createWriteTool, } from "./tools";
|
package/dist/index.js
CHANGED
|
@@ -49,15 +49,71 @@ var anthropicPromptCacheMiddleware = {
|
|
|
49
49
|
specificationVersion: "v3",
|
|
50
50
|
transformParams: async ({ params }) => applyCacheMarkers(params)
|
|
51
51
|
};
|
|
52
|
+
// src/sandbox/lazy-singleton.ts
|
|
53
|
+
function createLazySingleton(factory) {
|
|
54
|
+
let promise = null;
|
|
55
|
+
return {
|
|
56
|
+
get: () => {
|
|
57
|
+
if (!promise) {
|
|
58
|
+
promise = factory();
|
|
59
|
+
}
|
|
60
|
+
return promise;
|
|
61
|
+
},
|
|
62
|
+
reset: () => {
|
|
63
|
+
promise = null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/sandbox/ensure-tools.ts
|
|
69
|
+
import { rgPath as bundledRgPath } from "@vscode/ripgrep";
|
|
70
|
+
var RIPGREP_VERSION = "14.1.0";
|
|
71
|
+
var ARCH_MAP = {
|
|
72
|
+
x86_64: "x86_64-unknown-linux-musl",
|
|
73
|
+
aarch64: "aarch64-unknown-linux-gnu",
|
|
74
|
+
arm64: "aarch64-unknown-linux-gnu"
|
|
75
|
+
};
|
|
76
|
+
async function ensureSandboxTools(sandbox) {
|
|
77
|
+
const bundledCheck = await sandbox.exec(`test -x "${bundledRgPath}" && echo found`);
|
|
78
|
+
if (bundledCheck.stdout.includes("found")) {
|
|
79
|
+
sandbox.rgPath = bundledRgPath;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const tmpCheck = await sandbox.exec("test -x /tmp/rg && echo found");
|
|
83
|
+
if (tmpCheck.stdout.includes("found")) {
|
|
84
|
+
sandbox.rgPath = "/tmp/rg";
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const systemCheck = await sandbox.exec("which rg 2>/dev/null");
|
|
88
|
+
if (systemCheck.exitCode === 0 && systemCheck.stdout.trim()) {
|
|
89
|
+
sandbox.rgPath = systemCheck.stdout.trim();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const archResult = await sandbox.exec("uname -m");
|
|
93
|
+
const arch = archResult.stdout.trim();
|
|
94
|
+
const ripgrepArch = ARCH_MAP[arch];
|
|
95
|
+
if (!ripgrepArch) {
|
|
96
|
+
throw new Error(`Unsupported architecture: ${arch}. Supported: ${Object.keys(ARCH_MAP).join(", ")}`);
|
|
97
|
+
}
|
|
98
|
+
const ripgrepUrl = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-${ripgrepArch}.tar.gz`;
|
|
99
|
+
const tarPath = `ripgrep-${RIPGREP_VERSION}-${ripgrepArch}/rg`;
|
|
100
|
+
const installResult = await sandbox.exec(`
|
|
101
|
+
curl -sL "${ripgrepUrl}" |
|
|
102
|
+
tar xzf - -C /tmp --strip-components=1 ${tarPath} &&
|
|
103
|
+
chmod +x /tmp/rg
|
|
104
|
+
`);
|
|
105
|
+
if (installResult.exitCode !== 0) {
|
|
106
|
+
throw new Error(`Failed to install ripgrep: ${installResult.stderr}`);
|
|
107
|
+
}
|
|
108
|
+
sandbox.rgPath = "/tmp/rg";
|
|
109
|
+
}
|
|
110
|
+
|
|
52
111
|
// src/sandbox/e2b.ts
|
|
53
|
-
function createE2BSandbox(config = {}) {
|
|
54
|
-
let sandbox = null;
|
|
112
|
+
async function createE2BSandbox(config = {}) {
|
|
55
113
|
let sandboxId = config.sandboxId;
|
|
56
114
|
const workingDirectory = config.cwd || "/home/user";
|
|
57
115
|
const timeout = config.timeout ?? 300000;
|
|
58
|
-
const
|
|
59
|
-
if (sandbox)
|
|
60
|
-
return sandbox;
|
|
116
|
+
const sandbox = createLazySingleton(async () => {
|
|
61
117
|
let E2BSandboxSDK;
|
|
62
118
|
try {
|
|
63
119
|
const module = await import("@e2b/code-interpreter");
|
|
@@ -65,20 +121,21 @@ function createE2BSandbox(config = {}) {
|
|
|
65
121
|
} catch {
|
|
66
122
|
throw new Error("E2BSandbox requires @e2b/code-interpreter. Install with: npm install @e2b/code-interpreter");
|
|
67
123
|
}
|
|
124
|
+
let sbx;
|
|
68
125
|
if (config.sandboxId) {
|
|
69
|
-
|
|
126
|
+
sbx = await E2BSandboxSDK.connect(config.sandboxId);
|
|
70
127
|
} else {
|
|
71
|
-
|
|
128
|
+
sbx = await E2BSandboxSDK.create({
|
|
72
129
|
apiKey: config.apiKey,
|
|
73
130
|
timeoutMs: timeout,
|
|
74
131
|
metadata: config.metadata
|
|
75
132
|
});
|
|
76
|
-
sandboxId =
|
|
133
|
+
sandboxId = sbx.sandboxId;
|
|
77
134
|
}
|
|
78
|
-
return
|
|
79
|
-
};
|
|
135
|
+
return sbx;
|
|
136
|
+
});
|
|
80
137
|
const exec = async (command, options) => {
|
|
81
|
-
const sbx = await
|
|
138
|
+
const sbx = await sandbox.get();
|
|
82
139
|
const startTime = performance.now();
|
|
83
140
|
try {
|
|
84
141
|
const result = await sbx.commands.run(command, {
|
|
@@ -118,11 +175,18 @@ function createE2BSandbox(config = {}) {
|
|
|
118
175
|
throw error;
|
|
119
176
|
}
|
|
120
177
|
};
|
|
121
|
-
|
|
178
|
+
let rgPath;
|
|
179
|
+
const sandboxObj = {
|
|
122
180
|
exec,
|
|
123
181
|
get id() {
|
|
124
182
|
return sandboxId;
|
|
125
183
|
},
|
|
184
|
+
get rgPath() {
|
|
185
|
+
return rgPath;
|
|
186
|
+
},
|
|
187
|
+
set rgPath(path) {
|
|
188
|
+
rgPath = path;
|
|
189
|
+
},
|
|
126
190
|
async readFile(path) {
|
|
127
191
|
const result = await exec(`cat "${path}"`);
|
|
128
192
|
if (result.exitCode !== 0) {
|
|
@@ -131,7 +195,7 @@ function createE2BSandbox(config = {}) {
|
|
|
131
195
|
return result.stdout;
|
|
132
196
|
},
|
|
133
197
|
async writeFile(path, content) {
|
|
134
|
-
const sbx = await
|
|
198
|
+
const sbx = await sandbox.get();
|
|
135
199
|
await sbx.files.write(path, content);
|
|
136
200
|
},
|
|
137
201
|
async readDir(path) {
|
|
@@ -151,15 +215,21 @@ function createE2BSandbox(config = {}) {
|
|
|
151
215
|
return result.exitCode === 0;
|
|
152
216
|
},
|
|
153
217
|
async destroy() {
|
|
154
|
-
|
|
155
|
-
await sandbox.
|
|
156
|
-
|
|
157
|
-
}
|
|
218
|
+
try {
|
|
219
|
+
const sbx = await sandbox.get();
|
|
220
|
+
await sbx.kill();
|
|
221
|
+
} catch {}
|
|
222
|
+
sandbox.reset();
|
|
158
223
|
}
|
|
159
224
|
};
|
|
225
|
+
if (config.ensureTools !== false) {
|
|
226
|
+
await ensureSandboxTools(sandboxObj);
|
|
227
|
+
}
|
|
228
|
+
return sandboxObj;
|
|
160
229
|
}
|
|
161
230
|
// src/sandbox/local.ts
|
|
162
231
|
import { existsSync, mkdirSync } from "node:fs";
|
|
232
|
+
import { rgPath as bundledRgPath2 } from "@vscode/ripgrep";
|
|
163
233
|
function createLocalSandbox(config = {}) {
|
|
164
234
|
const workingDirectory = config.cwd || "/tmp";
|
|
165
235
|
if (!existsSync(workingDirectory)) {
|
|
@@ -201,6 +271,7 @@ function createLocalSandbox(config = {}) {
|
|
|
201
271
|
};
|
|
202
272
|
return {
|
|
203
273
|
exec,
|
|
274
|
+
rgPath: bundledRgPath2,
|
|
204
275
|
async readFile(path) {
|
|
205
276
|
const fullPath = path.startsWith("/") ? path : `${workingDirectory}/${path}`;
|
|
206
277
|
const file = Bun.file(fullPath);
|
|
@@ -239,8 +310,7 @@ function createLocalSandbox(config = {}) {
|
|
|
239
310
|
};
|
|
240
311
|
}
|
|
241
312
|
// src/sandbox/vercel.ts
|
|
242
|
-
function createVercelSandbox(config = {}) {
|
|
243
|
-
let sandbox = null;
|
|
313
|
+
async function createVercelSandbox(config = {}) {
|
|
244
314
|
let sandboxId = config.sandboxId;
|
|
245
315
|
const workingDirectory = config.cwd || "/vercel/sandbox";
|
|
246
316
|
const resolvedConfig = {
|
|
@@ -248,9 +318,7 @@ function createVercelSandbox(config = {}) {
|
|
|
248
318
|
resources: config.resources ?? { vcpus: 2 },
|
|
249
319
|
timeout: config.timeout ?? 300000
|
|
250
320
|
};
|
|
251
|
-
const
|
|
252
|
-
if (sandbox)
|
|
253
|
-
return sandbox;
|
|
321
|
+
const sandbox = createLazySingleton(async () => {
|
|
254
322
|
let VercelSandboxSDK;
|
|
255
323
|
try {
|
|
256
324
|
const module = await import("@vercel/sandbox");
|
|
@@ -269,16 +337,17 @@ function createVercelSandbox(config = {}) {
|
|
|
269
337
|
token: config.token
|
|
270
338
|
});
|
|
271
339
|
}
|
|
340
|
+
let sbx;
|
|
272
341
|
if (config.sandboxId) {
|
|
273
|
-
|
|
342
|
+
sbx = await VercelSandboxSDK.get({ sandboxId: config.sandboxId });
|
|
274
343
|
} else {
|
|
275
|
-
|
|
344
|
+
sbx = await VercelSandboxSDK.create(createOptions);
|
|
276
345
|
}
|
|
277
|
-
sandboxId =
|
|
278
|
-
return
|
|
279
|
-
};
|
|
346
|
+
sandboxId = sbx.sandboxId;
|
|
347
|
+
return sbx;
|
|
348
|
+
});
|
|
280
349
|
const exec = async (command, options) => {
|
|
281
|
-
const sbx = await
|
|
350
|
+
const sbx = await sandbox.get();
|
|
282
351
|
const startTime = performance.now();
|
|
283
352
|
let interrupted = false;
|
|
284
353
|
const abortController = new AbortController;
|
|
@@ -326,13 +395,20 @@ function createVercelSandbox(config = {}) {
|
|
|
326
395
|
throw error;
|
|
327
396
|
}
|
|
328
397
|
};
|
|
329
|
-
|
|
398
|
+
let rgPath;
|
|
399
|
+
const sandboxObj = {
|
|
330
400
|
exec,
|
|
331
401
|
get id() {
|
|
332
402
|
return sandboxId;
|
|
333
403
|
},
|
|
404
|
+
get rgPath() {
|
|
405
|
+
return rgPath;
|
|
406
|
+
},
|
|
407
|
+
set rgPath(path) {
|
|
408
|
+
rgPath = path;
|
|
409
|
+
},
|
|
334
410
|
async readFile(path) {
|
|
335
|
-
const sbx = await
|
|
411
|
+
const sbx = await sandbox.get();
|
|
336
412
|
const stream = await sbx.readFile({ path });
|
|
337
413
|
if (!stream) {
|
|
338
414
|
throw new Error(`File not found: ${path}`);
|
|
@@ -344,7 +420,7 @@ function createVercelSandbox(config = {}) {
|
|
|
344
420
|
return Buffer.concat(chunks).toString("utf-8");
|
|
345
421
|
},
|
|
346
422
|
async writeFile(path, content) {
|
|
347
|
-
const sbx = await
|
|
423
|
+
const sbx = await sandbox.get();
|
|
348
424
|
await sbx.writeFiles([
|
|
349
425
|
{
|
|
350
426
|
path,
|
|
@@ -369,12 +445,17 @@ function createVercelSandbox(config = {}) {
|
|
|
369
445
|
return result.exitCode === 0;
|
|
370
446
|
},
|
|
371
447
|
async destroy() {
|
|
372
|
-
|
|
373
|
-
await sandbox.
|
|
374
|
-
|
|
375
|
-
}
|
|
448
|
+
try {
|
|
449
|
+
const sbx = await sandbox.get();
|
|
450
|
+
await sbx.stop();
|
|
451
|
+
} catch {}
|
|
452
|
+
sandbox.reset();
|
|
376
453
|
}
|
|
377
454
|
};
|
|
455
|
+
if (config.ensureTools !== false) {
|
|
456
|
+
await ensureSandboxTools(sandboxObj);
|
|
457
|
+
}
|
|
458
|
+
return sandboxObj;
|
|
378
459
|
}
|
|
379
460
|
// src/cache/lru.ts
|
|
380
461
|
class LRUCacheStore {
|
|
@@ -991,7 +1072,6 @@ function createGlobTool(sandbox, config) {
|
|
|
991
1072
|
// src/tools/grep.ts
|
|
992
1073
|
import { tool as tool7, zodSchema as zodSchema7 } from "ai";
|
|
993
1074
|
import { z as z7 } from "zod";
|
|
994
|
-
import { rgPath } from "@vscode/ripgrep";
|
|
995
1075
|
var grepInputSchema = z7.object({
|
|
996
1076
|
pattern: z7.string().describe("The regular expression pattern to search for in file contents"),
|
|
997
1077
|
path: z7.string().optional().describe("File or directory to search in (defaults to cwd)"),
|
|
@@ -1057,7 +1137,13 @@ function createGrepTool(sandbox, config) {
|
|
|
1057
1137
|
}
|
|
1058
1138
|
}
|
|
1059
1139
|
try {
|
|
1140
|
+
if (!sandbox.rgPath) {
|
|
1141
|
+
return {
|
|
1142
|
+
error: "Ripgrep not available. Call ensureSandboxTools(sandbox) before using Grep with remote sandboxes."
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1060
1145
|
const cmd = buildRipgrepCommand({
|
|
1146
|
+
rgPath: sandbox.rgPath,
|
|
1061
1147
|
pattern,
|
|
1062
1148
|
searchPath,
|
|
1063
1149
|
output_mode,
|
|
@@ -1106,7 +1192,7 @@ function buildRipgrepCommand(opts) {
|
|
|
1106
1192
|
if (opts.type)
|
|
1107
1193
|
flags.push(`-t ${opts.type}`);
|
|
1108
1194
|
const flagStr = flags.join(" ");
|
|
1109
|
-
return `${rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
|
|
1195
|
+
return `${opts.rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
|
|
1110
1196
|
}
|
|
1111
1197
|
function parseFilesOutput(stdout) {
|
|
1112
1198
|
const files = new Set;
|
|
@@ -2778,6 +2864,7 @@ export {
|
|
|
2778
2864
|
estimateTokens,
|
|
2779
2865
|
estimateMessagesTokens,
|
|
2780
2866
|
estimateMessageTokens,
|
|
2867
|
+
ensureSandboxTools,
|
|
2781
2868
|
discoverSkills,
|
|
2782
2869
|
createWriteTool,
|
|
2783
2870
|
createWebSearchTool,
|
package/dist/sandbox/e2b.d.ts
CHANGED
|
@@ -7,5 +7,10 @@ export interface E2BSandboxConfig {
|
|
|
7
7
|
timeout?: number;
|
|
8
8
|
cwd?: string;
|
|
9
9
|
metadata?: Record<string, string>;
|
|
10
|
+
/**
|
|
11
|
+
* Ensure tools like ripgrep are available in the sandbox.
|
|
12
|
+
* Defaults to true. Set to false for faster startup if you don't need Grep.
|
|
13
|
+
*/
|
|
14
|
+
ensureTools?: boolean;
|
|
10
15
|
}
|
|
11
|
-
export declare function createE2BSandbox(config?: E2BSandboxConfig): Sandbox
|
|
16
|
+
export declare function createE2BSandbox(config?: E2BSandboxConfig): Promise<Sandbox>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Sandbox } from "./interface";
|
|
2
|
+
/**
|
|
3
|
+
* Ensures required tools (ripgrep) are available in the sandbox.
|
|
4
|
+
* Call this once during sandbox setup, before using tools like Grep.
|
|
5
|
+
*
|
|
6
|
+
* - For local sandboxes: verifies bundled binary exists
|
|
7
|
+
* - For remote sandboxes: installs ripgrep to /tmp/rg if not present
|
|
8
|
+
*
|
|
9
|
+
* Supports x86_64 and ARM64 architectures.
|
|
10
|
+
*
|
|
11
|
+
* After calling, `sandbox.rgPath` will be set to the correct path.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const sandbox = await createVercelSandbox();
|
|
16
|
+
* await ensureSandboxTools(sandbox);
|
|
17
|
+
*
|
|
18
|
+
* const { tools } = createAgentTools(sandbox);
|
|
19
|
+
* // Grep now works
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function ensureSandboxTools(sandbox: Sandbox): Promise<void>;
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createE2BSandbox, type E2BSandboxConfig } from "./e2b";
|
|
2
|
+
export { ensureSandboxTools } from "./ensure-tools";
|
|
2
3
|
export type { ExecOptions, ExecResult, Sandbox } from "./interface";
|
|
3
4
|
export { createLocalSandbox, type LocalSandboxConfig } from "./local";
|
|
4
5
|
export { createVercelSandbox, type VercelSandboxConfig } from "./vercel";
|
|
@@ -25,4 +25,9 @@ export interface Sandbox {
|
|
|
25
25
|
* - For local sandboxes: always undefined
|
|
26
26
|
*/
|
|
27
27
|
readonly id?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Path to ripgrep binary for this sandbox.
|
|
30
|
+
* Set by ensureSandboxTools() or defaults to bundled binary for local sandboxes.
|
|
31
|
+
*/
|
|
32
|
+
rgPath?: string;
|
|
28
33
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a lazy singleton that initializes on first access.
|
|
3
|
+
* Safe for concurrent calls - all callers await the same promise.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const sandbox = createLazySingleton(async () => {
|
|
8
|
+
* const sdk = await import("@vercel/sandbox");
|
|
9
|
+
* return sdk.Sandbox.create({ ... });
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* // Safe for parallel calls:
|
|
13
|
+
* const [a, b] = await Promise.all([sandbox.get(), sandbox.get()]);
|
|
14
|
+
* // a === b (same instance)
|
|
15
|
+
*
|
|
16
|
+
* // Reset for cleanup:
|
|
17
|
+
* sandbox.reset();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function createLazySingleton<T>(factory: () => Promise<T>): {
|
|
21
|
+
/** Get the singleton instance, creating it if needed */
|
|
22
|
+
get: () => Promise<T>;
|
|
23
|
+
/** Reset the singleton, allowing a new instance to be created */
|
|
24
|
+
reset: () => void;
|
|
25
|
+
};
|
package/dist/sandbox/vercel.d.ts
CHANGED
|
@@ -11,5 +11,10 @@ export interface VercelSandboxConfig {
|
|
|
11
11
|
teamId?: string;
|
|
12
12
|
projectId?: string;
|
|
13
13
|
token?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Ensure tools like ripgrep are available in the sandbox.
|
|
16
|
+
* Defaults to true. Set to false for faster startup if you don't need Grep.
|
|
17
|
+
*/
|
|
18
|
+
ensureTools?: boolean;
|
|
14
19
|
}
|
|
15
|
-
export declare function createVercelSandbox(config?: VercelSandboxConfig): Sandbox
|
|
20
|
+
export declare function createVercelSandbox(config?: VercelSandboxConfig): Promise<Sandbox>;
|