bashkit 0.2.3 → 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 +268 -163
- 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/dist/types.d.ts +1 -4
- package/package.json +4 -3
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 {
|
|
@@ -1004,13 +1085,13 @@ var grepInputSchema = z7.object({
|
|
|
1004
1085
|
"-C": z7.number().optional().describe("Number of lines to show before and after each match. Requires output_mode: 'content'."),
|
|
1005
1086
|
head_limit: z7.number().optional().describe("Limit output to first N lines/entries. Works across all output modes. Defaults to 0 (unlimited)."),
|
|
1006
1087
|
offset: z7.number().optional().describe("Skip first N lines/entries before applying head_limit. Works across all output modes. Defaults to 0."),
|
|
1007
|
-
multiline: z7.boolean().optional().describe("Enable multiline mode where patterns can span lines
|
|
1088
|
+
multiline: z7.boolean().optional().describe("Enable multiline mode where patterns can span lines. Default: false.")
|
|
1008
1089
|
});
|
|
1009
|
-
var GREP_DESCRIPTION = `A powerful content search tool with regex support.
|
|
1090
|
+
var GREP_DESCRIPTION = `A powerful content search tool built on ripgrep with regex support.
|
|
1010
1091
|
|
|
1011
1092
|
**Usage:**
|
|
1012
1093
|
- ALWAYS use Grep for search tasks. NEVER invoke \`grep\` or \`rg\` as a Bash command.
|
|
1013
|
-
- Supports regex syntax (e.g., "log.*Error", "function\\s+\\w+")
|
|
1094
|
+
- Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
|
|
1014
1095
|
- Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")
|
|
1015
1096
|
|
|
1016
1097
|
**Output modes:**
|
|
@@ -1025,11 +1106,8 @@ var GREP_DESCRIPTION = `A powerful content search tool with regex support. Use t
|
|
|
1025
1106
|
|
|
1026
1107
|
**Pagination:**
|
|
1027
1108
|
- Use offset to skip results (useful for pagination)
|
|
1028
|
-
- Use head_limit to limit total results returned
|
|
1029
|
-
|
|
1030
|
-
**Note:** Set useRipgrep: true in config for better performance and multiline support (requires ripgrep installed).`;
|
|
1109
|
+
- Use head_limit to limit total results returned`;
|
|
1031
1110
|
function createGrepTool(sandbox, config) {
|
|
1032
|
-
const useRipgrep = config?.useRipgrep ?? false;
|
|
1033
1111
|
return tool7({
|
|
1034
1112
|
description: GREP_DESCRIPTION,
|
|
1035
1113
|
inputSchema: zodSchema7(grepInputSchema),
|
|
@@ -1044,7 +1122,6 @@ function createGrepTool(sandbox, config) {
|
|
|
1044
1122
|
type,
|
|
1045
1123
|
output_mode = "files_with_matches",
|
|
1046
1124
|
"-i": caseInsensitive,
|
|
1047
|
-
"-n": showLineNumbers = true,
|
|
1048
1125
|
"-B": beforeContext,
|
|
1049
1126
|
"-A": afterContext,
|
|
1050
1127
|
"-C": context,
|
|
@@ -1059,98 +1136,32 @@ function createGrepTool(sandbox, config) {
|
|
|
1059
1136
|
return { error: `Path not allowed: ${searchPath}` };
|
|
1060
1137
|
}
|
|
1061
1138
|
}
|
|
1062
|
-
if (multiline && !useRipgrep) {
|
|
1063
|
-
return {
|
|
1064
|
-
error: "Multiline mode requires ripgrep. Set useRipgrep: true in config."
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
1139
|
try {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (head_limit && head_limit > 0) {
|
|
1073
|
-
paginationSuffix += ` | head -${head_limit}`;
|
|
1074
|
-
}
|
|
1075
|
-
let cmd;
|
|
1076
|
-
if (useRipgrep) {
|
|
1077
|
-
cmd = buildRipgrepCommand({
|
|
1078
|
-
pattern,
|
|
1079
|
-
searchPath,
|
|
1080
|
-
output_mode,
|
|
1081
|
-
caseInsensitive,
|
|
1082
|
-
showLineNumbers,
|
|
1083
|
-
beforeContext,
|
|
1084
|
-
afterContext,
|
|
1085
|
-
context,
|
|
1086
|
-
glob,
|
|
1087
|
-
type,
|
|
1088
|
-
multiline,
|
|
1089
|
-
paginationSuffix
|
|
1090
|
-
});
|
|
1091
|
-
} else {
|
|
1092
|
-
cmd = buildGrepCommand({
|
|
1093
|
-
pattern,
|
|
1094
|
-
searchPath,
|
|
1095
|
-
output_mode,
|
|
1096
|
-
caseInsensitive,
|
|
1097
|
-
showLineNumbers,
|
|
1098
|
-
beforeContext,
|
|
1099
|
-
afterContext,
|
|
1100
|
-
context,
|
|
1101
|
-
glob,
|
|
1102
|
-
type,
|
|
1103
|
-
paginationSuffix
|
|
1104
|
-
});
|
|
1140
|
+
if (!sandbox.rgPath) {
|
|
1141
|
+
return {
|
|
1142
|
+
error: "Ripgrep not available. Call ensureSandboxTools(sandbox) before using Grep with remote sandboxes."
|
|
1143
|
+
};
|
|
1105
1144
|
}
|
|
1145
|
+
const cmd = buildRipgrepCommand({
|
|
1146
|
+
rgPath: sandbox.rgPath,
|
|
1147
|
+
pattern,
|
|
1148
|
+
searchPath,
|
|
1149
|
+
output_mode,
|
|
1150
|
+
caseInsensitive,
|
|
1151
|
+
beforeContext,
|
|
1152
|
+
afterContext,
|
|
1153
|
+
context,
|
|
1154
|
+
glob,
|
|
1155
|
+
type,
|
|
1156
|
+
multiline
|
|
1157
|
+
});
|
|
1106
1158
|
const result = await sandbox.exec(cmd, { timeout: config?.timeout });
|
|
1107
1159
|
if (output_mode === "files_with_matches") {
|
|
1108
|
-
|
|
1109
|
-
`).filter(Boolean);
|
|
1110
|
-
return {
|
|
1111
|
-
files,
|
|
1112
|
-
count: files.length
|
|
1113
|
-
};
|
|
1160
|
+
return parseFilesOutput(result.stdout);
|
|
1114
1161
|
} else if (output_mode === "count") {
|
|
1115
|
-
|
|
1116
|
-
`).filter(Boolean);
|
|
1117
|
-
const counts = lines.map((line) => {
|
|
1118
|
-
const lastColon = line.lastIndexOf(":");
|
|
1119
|
-
return {
|
|
1120
|
-
file: line.slice(0, lastColon),
|
|
1121
|
-
count: parseInt(line.slice(lastColon + 1), 10)
|
|
1122
|
-
};
|
|
1123
|
-
});
|
|
1124
|
-
const total = counts.reduce((sum, c) => sum + c.count, 0);
|
|
1125
|
-
return {
|
|
1126
|
-
counts,
|
|
1127
|
-
total
|
|
1128
|
-
};
|
|
1162
|
+
return parseCountOutput(result.stdout);
|
|
1129
1163
|
} else {
|
|
1130
|
-
|
|
1131
|
-
return {
|
|
1132
|
-
matches: [],
|
|
1133
|
-
total_matches: 0
|
|
1134
|
-
};
|
|
1135
|
-
}
|
|
1136
|
-
const lines = result.stdout.split(`
|
|
1137
|
-
`).filter(Boolean);
|
|
1138
|
-
const matches = [];
|
|
1139
|
-
for (const line of lines) {
|
|
1140
|
-
const colonMatch = line.match(/^(.+?):(\d+)[:|-](.*)$/);
|
|
1141
|
-
if (colonMatch) {
|
|
1142
|
-
const [, file, lineNum, content] = colonMatch;
|
|
1143
|
-
matches.push({
|
|
1144
|
-
file,
|
|
1145
|
-
line_number: parseInt(lineNum, 10),
|
|
1146
|
-
line: content
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return {
|
|
1151
|
-
matches,
|
|
1152
|
-
total_matches: matches.length
|
|
1153
|
-
};
|
|
1164
|
+
return parseContentOutput(result.stdout, head_limit, offset);
|
|
1154
1165
|
}
|
|
1155
1166
|
} catch (error) {
|
|
1156
1167
|
return {
|
|
@@ -1161,14 +1172,12 @@ function createGrepTool(sandbox, config) {
|
|
|
1161
1172
|
});
|
|
1162
1173
|
}
|
|
1163
1174
|
function buildRipgrepCommand(opts) {
|
|
1164
|
-
const flags = [];
|
|
1175
|
+
const flags = ["--json"];
|
|
1165
1176
|
if (opts.caseInsensitive)
|
|
1166
1177
|
flags.push("-i");
|
|
1167
1178
|
if (opts.multiline)
|
|
1168
1179
|
flags.push("-U", "--multiline-dotall");
|
|
1169
1180
|
if (opts.output_mode === "content") {
|
|
1170
|
-
if (opts.showLineNumbers)
|
|
1171
|
-
flags.push("-n");
|
|
1172
1181
|
if (opts.context) {
|
|
1173
1182
|
flags.push(`-C ${opts.context}`);
|
|
1174
1183
|
} else {
|
|
@@ -1183,42 +1192,137 @@ function buildRipgrepCommand(opts) {
|
|
|
1183
1192
|
if (opts.type)
|
|
1184
1193
|
flags.push(`-t ${opts.type}`);
|
|
1185
1194
|
const flagStr = flags.join(" ");
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1195
|
+
return `${opts.rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
|
|
1196
|
+
}
|
|
1197
|
+
function parseFilesOutput(stdout) {
|
|
1198
|
+
const files = new Set;
|
|
1199
|
+
for (const line of stdout.split(`
|
|
1200
|
+
`).filter(Boolean)) {
|
|
1201
|
+
try {
|
|
1202
|
+
const msg = JSON.parse(line);
|
|
1203
|
+
if (msg.type === "begin") {
|
|
1204
|
+
const data = msg.data;
|
|
1205
|
+
files.add(data.path.text);
|
|
1206
|
+
}
|
|
1207
|
+
} catch {}
|
|
1192
1208
|
}
|
|
1209
|
+
return {
|
|
1210
|
+
files: Array.from(files),
|
|
1211
|
+
count: files.size
|
|
1212
|
+
};
|
|
1193
1213
|
}
|
|
1194
|
-
function
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1214
|
+
function parseCountOutput(stdout) {
|
|
1215
|
+
const counts = new Map;
|
|
1216
|
+
for (const line of stdout.split(`
|
|
1217
|
+
`).filter(Boolean)) {
|
|
1218
|
+
try {
|
|
1219
|
+
const msg = JSON.parse(line);
|
|
1220
|
+
if (msg.type === "end") {
|
|
1221
|
+
const data = msg.data;
|
|
1222
|
+
counts.set(data.path.text, data.stats.matches);
|
|
1223
|
+
}
|
|
1224
|
+
} catch {}
|
|
1225
|
+
}
|
|
1226
|
+
const countsArray = Array.from(counts.entries()).map(([file, count]) => ({
|
|
1227
|
+
file,
|
|
1228
|
+
count
|
|
1229
|
+
}));
|
|
1230
|
+
const total = countsArray.reduce((sum, c) => sum + c.count, 0);
|
|
1231
|
+
return {
|
|
1232
|
+
counts: countsArray,
|
|
1233
|
+
total
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
function parseContentOutput(stdout, head_limit, offset = 0) {
|
|
1237
|
+
const fileData = new Map;
|
|
1238
|
+
for (const line of stdout.split(`
|
|
1239
|
+
`).filter(Boolean)) {
|
|
1240
|
+
try {
|
|
1241
|
+
const msg = JSON.parse(line);
|
|
1242
|
+
if (msg.type === "begin") {
|
|
1243
|
+
const data = msg.data;
|
|
1244
|
+
fileData.set(data.path.text, { matches: [], contexts: [] });
|
|
1245
|
+
} else if (msg.type === "context") {
|
|
1246
|
+
const data = msg.data;
|
|
1247
|
+
const fd = fileData.get(data.path.text);
|
|
1248
|
+
if (fd) {
|
|
1249
|
+
fd.contexts.push({
|
|
1250
|
+
line_number: data.line_number,
|
|
1251
|
+
text: data.lines.text.replace(/\n$/, "")
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
} else if (msg.type === "match") {
|
|
1255
|
+
const data = msg.data;
|
|
1256
|
+
const fd = fileData.get(data.path.text);
|
|
1257
|
+
if (fd) {
|
|
1258
|
+
fd.matches.push({
|
|
1259
|
+
line_number: data.line_number,
|
|
1260
|
+
text: data.lines.text.replace(/\n$/, "")
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
} catch {}
|
|
1265
|
+
}
|
|
1266
|
+
const allMatches = [];
|
|
1267
|
+
for (const [file, { matches, contexts }] of fileData) {
|
|
1268
|
+
matches.sort((a, b) => a.line_number - b.line_number);
|
|
1269
|
+
contexts.sort((a, b) => a.line_number - b.line_number);
|
|
1270
|
+
const matchContexts = new Map;
|
|
1271
|
+
for (const match of matches) {
|
|
1272
|
+
matchContexts.set(match.line_number, { before: [], after: [] });
|
|
1273
|
+
}
|
|
1274
|
+
for (const ctx of contexts) {
|
|
1275
|
+
let bestMatch = null;
|
|
1276
|
+
let bestDistance = Infinity;
|
|
1277
|
+
let isBefore = false;
|
|
1278
|
+
for (const match of matches) {
|
|
1279
|
+
const distance = Math.abs(ctx.line_number - match.line_number);
|
|
1280
|
+
if (distance < bestDistance) {
|
|
1281
|
+
bestDistance = distance;
|
|
1282
|
+
bestMatch = match;
|
|
1283
|
+
isBefore = ctx.line_number < match.line_number;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (bestMatch) {
|
|
1287
|
+
const mc = matchContexts.get(bestMatch.line_number);
|
|
1288
|
+
if (mc) {
|
|
1289
|
+
if (isBefore) {
|
|
1290
|
+
mc.before.push(ctx.text);
|
|
1291
|
+
} else {
|
|
1292
|
+
mc.after.push(ctx.text);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
for (const match of matches) {
|
|
1298
|
+
const mc = matchContexts.get(match.line_number);
|
|
1299
|
+
allMatches.push({
|
|
1300
|
+
file,
|
|
1301
|
+
line_number: match.line_number,
|
|
1302
|
+
line: match.text,
|
|
1303
|
+
before_context: mc?.before ?? [],
|
|
1304
|
+
after_context: mc?.after ?? []
|
|
1305
|
+
});
|
|
1208
1306
|
}
|
|
1209
1307
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
return `grep ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null${opts.paginationSuffix}`;
|
|
1308
|
+
const grepMatches = allMatches.map((m) => ({
|
|
1309
|
+
file: m.file,
|
|
1310
|
+
line_number: m.line_number,
|
|
1311
|
+
line: m.line,
|
|
1312
|
+
before_context: m.before_context.length > 0 ? m.before_context : undefined,
|
|
1313
|
+
after_context: m.after_context.length > 0 ? m.after_context : undefined
|
|
1314
|
+
}));
|
|
1315
|
+
let result = grepMatches;
|
|
1316
|
+
if (offset > 0) {
|
|
1317
|
+
result = result.slice(offset);
|
|
1221
1318
|
}
|
|
1319
|
+
if (head_limit && head_limit > 0) {
|
|
1320
|
+
result = result.slice(0, head_limit);
|
|
1321
|
+
}
|
|
1322
|
+
return {
|
|
1323
|
+
matches: result,
|
|
1324
|
+
total_matches: result.length
|
|
1325
|
+
};
|
|
1222
1326
|
}
|
|
1223
1327
|
|
|
1224
1328
|
// src/tools/read.ts
|
|
@@ -2760,6 +2864,7 @@ export {
|
|
|
2760
2864
|
estimateTokens,
|
|
2761
2865
|
estimateMessagesTokens,
|
|
2762
2866
|
estimateMessageTokens,
|
|
2867
|
+
ensureSandboxTools,
|
|
2763
2868
|
discoverSkills,
|
|
2764
2869
|
createWriteTool,
|
|
2765
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>;
|
package/dist/types.d.ts
CHANGED
|
@@ -21,10 +21,7 @@ export type ToolConfig = {
|
|
|
21
21
|
allowedPaths?: string[];
|
|
22
22
|
blockedCommands?: string[];
|
|
23
23
|
} & SDKToolOptions;
|
|
24
|
-
export type GrepToolConfig = ToolConfig
|
|
25
|
-
/** Use ripgrep (rg) instead of grep. Requires ripgrep to be installed. Default: false */
|
|
26
|
-
useRipgrep?: boolean;
|
|
27
|
-
};
|
|
24
|
+
export type GrepToolConfig = ToolConfig;
|
|
28
25
|
/**
|
|
29
26
|
* Supported web search providers.
|
|
30
27
|
* Currently only 'parallel' is implemented.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bashkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agentic coding tools for the Vercel AI SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "bun run src/index.ts",
|
|
24
24
|
"build": "bun run build:js && bun run build:cli && bun run build:types",
|
|
25
|
-
"build:js": "bun build src/index.ts --outdir dist --target node --format esm --external ai --external zod --external @ai-sdk/* --external @vercel/sandbox --external @e2b/code-interpreter --external parallel-web",
|
|
25
|
+
"build:js": "bun build src/index.ts --outdir dist --target node --format esm --external ai --external zod --external @ai-sdk/* --external @vercel/sandbox --external @e2b/code-interpreter --external parallel-web --external @vscode/ripgrep",
|
|
26
26
|
"build:cli": "bun build src/cli/init.ts --outdir dist/cli --target node --format esm --external @clack/prompts && chmod +x dist/cli/init.js",
|
|
27
27
|
"build:types": "tsc -p tsconfig.build.json",
|
|
28
28
|
"typecheck": "tsc --noEmit",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"url": "https://github.com/jbreite/bashkit"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@clack/prompts": "^0.7.0"
|
|
54
|
+
"@clack/prompts": "^0.7.0",
|
|
55
|
+
"@vscode/ripgrep": "^1.17.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@ai-sdk/anthropic": "^3.0.1",
|