bashkit 0.3.0 → 0.3.2
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 +2 -0
- package/dist/cloudflare/index.d.ts +20 -0
- package/dist/cloudflare/index.js +1251 -0
- package/dist/durable/durable-session.d.ts +220 -0
- package/dist/durable/index.d.ts +41 -0
- package/dist/durable/index.js +159 -0
- package/dist/durable/schema.d.ts +51 -0
- package/dist/durable/types.d.ts +208 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +646 -113
- package/dist/react/index.d.ts +42 -0
- package/dist/react/index.js +10 -0
- package/dist/react/types.d.ts +333 -0
- package/dist/react/use-agent.d.ts +33 -0
- package/dist/react/use-durable-chat.d.ts +39 -0
- package/dist/sandbox/ripgrep.d.ts +11 -0
- package/dist/tools/task.d.ts +6 -4
- package/dist/utils/debug.d.ts +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/workflow.d.ts +52 -0
- package/dist/workflow.js +1051 -0
- package/package.json +1 -1
- package/dist/tools/web-constants.d.ts +0 -5
|
@@ -0,0 +1,1251 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
19
|
+
var __toCommonJS = (from) => {
|
|
20
|
+
var entry = __moduleCache.get(from), desc;
|
|
21
|
+
if (entry)
|
|
22
|
+
return entry;
|
|
23
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
24
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
25
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
26
|
+
get: () => from[key],
|
|
27
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
28
|
+
}));
|
|
29
|
+
__moduleCache.set(from, entry);
|
|
30
|
+
return entry;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __export = (target, all) => {
|
|
34
|
+
for (var name in all)
|
|
35
|
+
__defProp(target, name, {
|
|
36
|
+
get: all[name],
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
set: (newValue) => all[name] = () => newValue
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
43
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
44
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
45
|
+
}) : x)(function(x) {
|
|
46
|
+
if (typeof require !== "undefined")
|
|
47
|
+
return require.apply(this, arguments);
|
|
48
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// node:path
|
|
52
|
+
var exports_path = {};
|
|
53
|
+
__export(exports_path, {
|
|
54
|
+
sep: () => sep,
|
|
55
|
+
resolve: () => resolve,
|
|
56
|
+
relative: () => relative,
|
|
57
|
+
posix: () => posix,
|
|
58
|
+
parse: () => parse,
|
|
59
|
+
normalize: () => normalize,
|
|
60
|
+
join: () => join,
|
|
61
|
+
isAbsolute: () => isAbsolute,
|
|
62
|
+
format: () => format,
|
|
63
|
+
extname: () => extname,
|
|
64
|
+
dirname: () => dirname,
|
|
65
|
+
delimiter: () => delimiter,
|
|
66
|
+
default: () => path_default,
|
|
67
|
+
basename: () => basename,
|
|
68
|
+
_makeLong: () => _makeLong
|
|
69
|
+
});
|
|
70
|
+
function assertPath(path) {
|
|
71
|
+
if (typeof path !== "string")
|
|
72
|
+
throw TypeError("Path must be a string. Received " + JSON.stringify(path));
|
|
73
|
+
}
|
|
74
|
+
function normalizeStringPosix(path, allowAboveRoot) {
|
|
75
|
+
var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code;
|
|
76
|
+
for (var i = 0;i <= path.length; ++i) {
|
|
77
|
+
if (i < path.length)
|
|
78
|
+
code = path.charCodeAt(i);
|
|
79
|
+
else if (code === 47)
|
|
80
|
+
break;
|
|
81
|
+
else
|
|
82
|
+
code = 47;
|
|
83
|
+
if (code === 47) {
|
|
84
|
+
if (lastSlash === i - 1 || dots === 1)
|
|
85
|
+
;
|
|
86
|
+
else if (lastSlash !== i - 1 && dots === 2) {
|
|
87
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
|
|
88
|
+
if (res.length > 2) {
|
|
89
|
+
var lastSlashIndex = res.lastIndexOf("/");
|
|
90
|
+
if (lastSlashIndex !== res.length - 1) {
|
|
91
|
+
if (lastSlashIndex === -1)
|
|
92
|
+
res = "", lastSegmentLength = 0;
|
|
93
|
+
else
|
|
94
|
+
res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
95
|
+
lastSlash = i, dots = 0;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
} else if (res.length === 2 || res.length === 1) {
|
|
99
|
+
res = "", lastSegmentLength = 0, lastSlash = i, dots = 0;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (allowAboveRoot) {
|
|
104
|
+
if (res.length > 0)
|
|
105
|
+
res += "/..";
|
|
106
|
+
else
|
|
107
|
+
res = "..";
|
|
108
|
+
lastSegmentLength = 2;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
if (res.length > 0)
|
|
112
|
+
res += "/" + path.slice(lastSlash + 1, i);
|
|
113
|
+
else
|
|
114
|
+
res = path.slice(lastSlash + 1, i);
|
|
115
|
+
lastSegmentLength = i - lastSlash - 1;
|
|
116
|
+
}
|
|
117
|
+
lastSlash = i, dots = 0;
|
|
118
|
+
} else if (code === 46 && dots !== -1)
|
|
119
|
+
++dots;
|
|
120
|
+
else
|
|
121
|
+
dots = -1;
|
|
122
|
+
}
|
|
123
|
+
return res;
|
|
124
|
+
}
|
|
125
|
+
function _format(sep, pathObject) {
|
|
126
|
+
var dir = pathObject.dir || pathObject.root, base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
127
|
+
if (!dir)
|
|
128
|
+
return base;
|
|
129
|
+
if (dir === pathObject.root)
|
|
130
|
+
return dir + base;
|
|
131
|
+
return dir + sep + base;
|
|
132
|
+
}
|
|
133
|
+
function resolve() {
|
|
134
|
+
var resolvedPath = "", resolvedAbsolute = false, cwd;
|
|
135
|
+
for (var i = arguments.length - 1;i >= -1 && !resolvedAbsolute; i--) {
|
|
136
|
+
var path;
|
|
137
|
+
if (i >= 0)
|
|
138
|
+
path = arguments[i];
|
|
139
|
+
else {
|
|
140
|
+
if (cwd === undefined)
|
|
141
|
+
cwd = process.cwd();
|
|
142
|
+
path = cwd;
|
|
143
|
+
}
|
|
144
|
+
if (assertPath(path), path.length === 0)
|
|
145
|
+
continue;
|
|
146
|
+
resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = path.charCodeAt(0) === 47;
|
|
147
|
+
}
|
|
148
|
+
if (resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute), resolvedAbsolute)
|
|
149
|
+
if (resolvedPath.length > 0)
|
|
150
|
+
return "/" + resolvedPath;
|
|
151
|
+
else
|
|
152
|
+
return "/";
|
|
153
|
+
else if (resolvedPath.length > 0)
|
|
154
|
+
return resolvedPath;
|
|
155
|
+
else
|
|
156
|
+
return ".";
|
|
157
|
+
}
|
|
158
|
+
function normalize(path) {
|
|
159
|
+
if (assertPath(path), path.length === 0)
|
|
160
|
+
return ".";
|
|
161
|
+
var isAbsolute = path.charCodeAt(0) === 47, trailingSeparator = path.charCodeAt(path.length - 1) === 47;
|
|
162
|
+
if (path = normalizeStringPosix(path, !isAbsolute), path.length === 0 && !isAbsolute)
|
|
163
|
+
path = ".";
|
|
164
|
+
if (path.length > 0 && trailingSeparator)
|
|
165
|
+
path += "/";
|
|
166
|
+
if (isAbsolute)
|
|
167
|
+
return "/" + path;
|
|
168
|
+
return path;
|
|
169
|
+
}
|
|
170
|
+
function isAbsolute(path) {
|
|
171
|
+
return assertPath(path), path.length > 0 && path.charCodeAt(0) === 47;
|
|
172
|
+
}
|
|
173
|
+
function join() {
|
|
174
|
+
if (arguments.length === 0)
|
|
175
|
+
return ".";
|
|
176
|
+
var joined;
|
|
177
|
+
for (var i = 0;i < arguments.length; ++i) {
|
|
178
|
+
var arg = arguments[i];
|
|
179
|
+
if (assertPath(arg), arg.length > 0)
|
|
180
|
+
if (joined === undefined)
|
|
181
|
+
joined = arg;
|
|
182
|
+
else
|
|
183
|
+
joined += "/" + arg;
|
|
184
|
+
}
|
|
185
|
+
if (joined === undefined)
|
|
186
|
+
return ".";
|
|
187
|
+
return normalize(joined);
|
|
188
|
+
}
|
|
189
|
+
function relative(from, to) {
|
|
190
|
+
if (assertPath(from), assertPath(to), from === to)
|
|
191
|
+
return "";
|
|
192
|
+
if (from = resolve(from), to = resolve(to), from === to)
|
|
193
|
+
return "";
|
|
194
|
+
var fromStart = 1;
|
|
195
|
+
for (;fromStart < from.length; ++fromStart)
|
|
196
|
+
if (from.charCodeAt(fromStart) !== 47)
|
|
197
|
+
break;
|
|
198
|
+
var fromEnd = from.length, fromLen = fromEnd - fromStart, toStart = 1;
|
|
199
|
+
for (;toStart < to.length; ++toStart)
|
|
200
|
+
if (to.charCodeAt(toStart) !== 47)
|
|
201
|
+
break;
|
|
202
|
+
var toEnd = to.length, toLen = toEnd - toStart, length = fromLen < toLen ? fromLen : toLen, lastCommonSep = -1, i = 0;
|
|
203
|
+
for (;i <= length; ++i) {
|
|
204
|
+
if (i === length) {
|
|
205
|
+
if (toLen > length) {
|
|
206
|
+
if (to.charCodeAt(toStart + i) === 47)
|
|
207
|
+
return to.slice(toStart + i + 1);
|
|
208
|
+
else if (i === 0)
|
|
209
|
+
return to.slice(toStart + i);
|
|
210
|
+
} else if (fromLen > length) {
|
|
211
|
+
if (from.charCodeAt(fromStart + i) === 47)
|
|
212
|
+
lastCommonSep = i;
|
|
213
|
+
else if (i === 0)
|
|
214
|
+
lastCommonSep = 0;
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
var fromCode = from.charCodeAt(fromStart + i), toCode = to.charCodeAt(toStart + i);
|
|
219
|
+
if (fromCode !== toCode)
|
|
220
|
+
break;
|
|
221
|
+
else if (fromCode === 47)
|
|
222
|
+
lastCommonSep = i;
|
|
223
|
+
}
|
|
224
|
+
var out = "";
|
|
225
|
+
for (i = fromStart + lastCommonSep + 1;i <= fromEnd; ++i)
|
|
226
|
+
if (i === fromEnd || from.charCodeAt(i) === 47)
|
|
227
|
+
if (out.length === 0)
|
|
228
|
+
out += "..";
|
|
229
|
+
else
|
|
230
|
+
out += "/..";
|
|
231
|
+
if (out.length > 0)
|
|
232
|
+
return out + to.slice(toStart + lastCommonSep);
|
|
233
|
+
else {
|
|
234
|
+
if (toStart += lastCommonSep, to.charCodeAt(toStart) === 47)
|
|
235
|
+
++toStart;
|
|
236
|
+
return to.slice(toStart);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function _makeLong(path) {
|
|
240
|
+
return path;
|
|
241
|
+
}
|
|
242
|
+
function dirname(path) {
|
|
243
|
+
if (assertPath(path), path.length === 0)
|
|
244
|
+
return ".";
|
|
245
|
+
var code = path.charCodeAt(0), hasRoot = code === 47, end = -1, matchedSlash = true;
|
|
246
|
+
for (var i = path.length - 1;i >= 1; --i)
|
|
247
|
+
if (code = path.charCodeAt(i), code === 47) {
|
|
248
|
+
if (!matchedSlash) {
|
|
249
|
+
end = i;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
} else
|
|
253
|
+
matchedSlash = false;
|
|
254
|
+
if (end === -1)
|
|
255
|
+
return hasRoot ? "/" : ".";
|
|
256
|
+
if (hasRoot && end === 1)
|
|
257
|
+
return "//";
|
|
258
|
+
return path.slice(0, end);
|
|
259
|
+
}
|
|
260
|
+
function basename(path, ext) {
|
|
261
|
+
if (ext !== undefined && typeof ext !== "string")
|
|
262
|
+
throw TypeError('"ext" argument must be a string');
|
|
263
|
+
assertPath(path);
|
|
264
|
+
var start = 0, end = -1, matchedSlash = true, i;
|
|
265
|
+
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
|
266
|
+
if (ext.length === path.length && ext === path)
|
|
267
|
+
return "";
|
|
268
|
+
var extIdx = ext.length - 1, firstNonSlashEnd = -1;
|
|
269
|
+
for (i = path.length - 1;i >= 0; --i) {
|
|
270
|
+
var code = path.charCodeAt(i);
|
|
271
|
+
if (code === 47) {
|
|
272
|
+
if (!matchedSlash) {
|
|
273
|
+
start = i + 1;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
if (firstNonSlashEnd === -1)
|
|
278
|
+
matchedSlash = false, firstNonSlashEnd = i + 1;
|
|
279
|
+
if (extIdx >= 0)
|
|
280
|
+
if (code === ext.charCodeAt(extIdx)) {
|
|
281
|
+
if (--extIdx === -1)
|
|
282
|
+
end = i;
|
|
283
|
+
} else
|
|
284
|
+
extIdx = -1, end = firstNonSlashEnd;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (start === end)
|
|
288
|
+
end = firstNonSlashEnd;
|
|
289
|
+
else if (end === -1)
|
|
290
|
+
end = path.length;
|
|
291
|
+
return path.slice(start, end);
|
|
292
|
+
} else {
|
|
293
|
+
for (i = path.length - 1;i >= 0; --i)
|
|
294
|
+
if (path.charCodeAt(i) === 47) {
|
|
295
|
+
if (!matchedSlash) {
|
|
296
|
+
start = i + 1;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
} else if (end === -1)
|
|
300
|
+
matchedSlash = false, end = i + 1;
|
|
301
|
+
if (end === -1)
|
|
302
|
+
return "";
|
|
303
|
+
return path.slice(start, end);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function extname(path) {
|
|
307
|
+
assertPath(path);
|
|
308
|
+
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, preDotState = 0;
|
|
309
|
+
for (var i = path.length - 1;i >= 0; --i) {
|
|
310
|
+
var code = path.charCodeAt(i);
|
|
311
|
+
if (code === 47) {
|
|
312
|
+
if (!matchedSlash) {
|
|
313
|
+
startPart = i + 1;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (end === -1)
|
|
319
|
+
matchedSlash = false, end = i + 1;
|
|
320
|
+
if (code === 46) {
|
|
321
|
+
if (startDot === -1)
|
|
322
|
+
startDot = i;
|
|
323
|
+
else if (preDotState !== 1)
|
|
324
|
+
preDotState = 1;
|
|
325
|
+
} else if (startDot !== -1)
|
|
326
|
+
preDotState = -1;
|
|
327
|
+
}
|
|
328
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
329
|
+
return "";
|
|
330
|
+
return path.slice(startDot, end);
|
|
331
|
+
}
|
|
332
|
+
function format(pathObject) {
|
|
333
|
+
if (pathObject === null || typeof pathObject !== "object")
|
|
334
|
+
throw TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
|
|
335
|
+
return _format("/", pathObject);
|
|
336
|
+
}
|
|
337
|
+
function parse(path) {
|
|
338
|
+
assertPath(path);
|
|
339
|
+
var ret = { root: "", dir: "", base: "", ext: "", name: "" };
|
|
340
|
+
if (path.length === 0)
|
|
341
|
+
return ret;
|
|
342
|
+
var code = path.charCodeAt(0), isAbsolute2 = code === 47, start;
|
|
343
|
+
if (isAbsolute2)
|
|
344
|
+
ret.root = "/", start = 1;
|
|
345
|
+
else
|
|
346
|
+
start = 0;
|
|
347
|
+
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, i = path.length - 1, preDotState = 0;
|
|
348
|
+
for (;i >= start; --i) {
|
|
349
|
+
if (code = path.charCodeAt(i), code === 47) {
|
|
350
|
+
if (!matchedSlash) {
|
|
351
|
+
startPart = i + 1;
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (end === -1)
|
|
357
|
+
matchedSlash = false, end = i + 1;
|
|
358
|
+
if (code === 46) {
|
|
359
|
+
if (startDot === -1)
|
|
360
|
+
startDot = i;
|
|
361
|
+
else if (preDotState !== 1)
|
|
362
|
+
preDotState = 1;
|
|
363
|
+
} else if (startDot !== -1)
|
|
364
|
+
preDotState = -1;
|
|
365
|
+
}
|
|
366
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
367
|
+
if (end !== -1)
|
|
368
|
+
if (startPart === 0 && isAbsolute2)
|
|
369
|
+
ret.base = ret.name = path.slice(1, end);
|
|
370
|
+
else
|
|
371
|
+
ret.base = ret.name = path.slice(startPart, end);
|
|
372
|
+
} else {
|
|
373
|
+
if (startPart === 0 && isAbsolute2)
|
|
374
|
+
ret.name = path.slice(1, startDot), ret.base = path.slice(1, end);
|
|
375
|
+
else
|
|
376
|
+
ret.name = path.slice(startPart, startDot), ret.base = path.slice(startPart, end);
|
|
377
|
+
ret.ext = path.slice(startDot, end);
|
|
378
|
+
}
|
|
379
|
+
if (startPart > 0)
|
|
380
|
+
ret.dir = path.slice(0, startPart - 1);
|
|
381
|
+
else if (isAbsolute2)
|
|
382
|
+
ret.dir = "/";
|
|
383
|
+
return ret;
|
|
384
|
+
}
|
|
385
|
+
var sep = "/", delimiter = ":", posix, path_default;
|
|
386
|
+
var init_path = __esm(() => {
|
|
387
|
+
posix = ((p) => (p.posix = p, p))({ resolve, normalize, isAbsolute, join, relative, _makeLong, dirname, basename, extname, format, parse, sep, delimiter, win32: null, posix: null });
|
|
388
|
+
path_default = posix;
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// node_modules/.pnpm/@vscode+ripgrep@1.17.0/node_modules/@vscode/ripgrep/lib/index.js
|
|
392
|
+
var require_lib = __commonJS((exports, module) => {
|
|
393
|
+
var __dirname = "/Users/joshbreite/Desktop/dev/bashkit/node_modules/.pnpm/@vscode+ripgrep@1.17.0/node_modules/@vscode/ripgrep/lib";
|
|
394
|
+
var path = (init_path(), __toCommonJS(exports_path));
|
|
395
|
+
exports.rgPath = path.join(__dirname, `../bin/rg${process.platform === "win32" ? ".exe" : ""}`);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// src/sandbox/lazy-singleton.ts
|
|
399
|
+
function createLazySingleton(factory) {
|
|
400
|
+
let promise = null;
|
|
401
|
+
return {
|
|
402
|
+
get: () => {
|
|
403
|
+
if (!promise) {
|
|
404
|
+
promise = factory();
|
|
405
|
+
}
|
|
406
|
+
return promise;
|
|
407
|
+
},
|
|
408
|
+
reset: () => {
|
|
409
|
+
promise = null;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/sandbox/ripgrep.ts
|
|
415
|
+
async function getBundledRgPath() {
|
|
416
|
+
try {
|
|
417
|
+
const { rgPath } = await Promise.resolve().then(() => __toESM(require_lib(), 1));
|
|
418
|
+
return rgPath;
|
|
419
|
+
} catch {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/sandbox/ensure-tools.ts
|
|
425
|
+
var RIPGREP_VERSION = "14.1.0";
|
|
426
|
+
var ARCH_MAP = {
|
|
427
|
+
x86_64: "x86_64-unknown-linux-musl",
|
|
428
|
+
aarch64: "aarch64-unknown-linux-gnu",
|
|
429
|
+
arm64: "aarch64-unknown-linux-gnu"
|
|
430
|
+
};
|
|
431
|
+
async function ensureSandboxTools(sandbox) {
|
|
432
|
+
const bundledRgPath = await getBundledRgPath();
|
|
433
|
+
if (bundledRgPath) {
|
|
434
|
+
const bundledCheck = await sandbox.exec(`test -x "${bundledRgPath}" && echo found`);
|
|
435
|
+
if (bundledCheck.stdout.includes("found")) {
|
|
436
|
+
sandbox.rgPath = bundledRgPath;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const tmpCheck = await sandbox.exec("test -x /tmp/rg && echo found");
|
|
441
|
+
if (tmpCheck.stdout.includes("found")) {
|
|
442
|
+
sandbox.rgPath = "/tmp/rg";
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const systemCheck = await sandbox.exec("which rg 2>/dev/null");
|
|
446
|
+
if (systemCheck.exitCode === 0 && systemCheck.stdout.trim()) {
|
|
447
|
+
sandbox.rgPath = systemCheck.stdout.trim();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const archResult = await sandbox.exec("uname -m");
|
|
451
|
+
const arch = archResult.stdout.trim();
|
|
452
|
+
const ripgrepArch = ARCH_MAP[arch];
|
|
453
|
+
if (!ripgrepArch) {
|
|
454
|
+
throw new Error(`Unsupported architecture: ${arch}. Supported: ${Object.keys(ARCH_MAP).join(", ")}`);
|
|
455
|
+
}
|
|
456
|
+
const ripgrepUrl = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-${ripgrepArch}.tar.gz`;
|
|
457
|
+
const tarPath = `ripgrep-${RIPGREP_VERSION}-${ripgrepArch}/rg`;
|
|
458
|
+
const installResult = await sandbox.exec(`
|
|
459
|
+
curl -sL "${ripgrepUrl}" |
|
|
460
|
+
tar xzf - -C /tmp --strip-components=1 ${tarPath} &&
|
|
461
|
+
chmod +x /tmp/rg
|
|
462
|
+
`);
|
|
463
|
+
if (installResult.exitCode !== 0) {
|
|
464
|
+
throw new Error(`Failed to install ripgrep: ${installResult.stderr}`);
|
|
465
|
+
}
|
|
466
|
+
sandbox.rgPath = "/tmp/rg";
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/sandbox/e2b.ts
|
|
470
|
+
async function createE2BSandbox(config = {}) {
|
|
471
|
+
let sandboxId = config.sandboxId;
|
|
472
|
+
const workingDirectory = config.cwd || "/home/user";
|
|
473
|
+
const timeout = config.timeout ?? 300000;
|
|
474
|
+
const sandbox = createLazySingleton(async () => {
|
|
475
|
+
let E2BSandboxSDK;
|
|
476
|
+
try {
|
|
477
|
+
const module = await import("@e2b/code-interpreter");
|
|
478
|
+
E2BSandboxSDK = module.Sandbox;
|
|
479
|
+
} catch {
|
|
480
|
+
throw new Error("E2BSandbox requires @e2b/code-interpreter. Install with: npm install @e2b/code-interpreter");
|
|
481
|
+
}
|
|
482
|
+
let sbx;
|
|
483
|
+
if (config.sandboxId) {
|
|
484
|
+
sbx = await E2BSandboxSDK.connect(config.sandboxId);
|
|
485
|
+
} else {
|
|
486
|
+
sbx = await E2BSandboxSDK.create({
|
|
487
|
+
apiKey: config.apiKey,
|
|
488
|
+
timeoutMs: timeout,
|
|
489
|
+
metadata: config.metadata
|
|
490
|
+
});
|
|
491
|
+
sandboxId = sbx.sandboxId;
|
|
492
|
+
}
|
|
493
|
+
return sbx;
|
|
494
|
+
});
|
|
495
|
+
const exec = async (command, options) => {
|
|
496
|
+
const sbx = await sandbox.get();
|
|
497
|
+
const startTime = performance.now();
|
|
498
|
+
try {
|
|
499
|
+
const result = await sbx.commands.run(command, {
|
|
500
|
+
cwd: options?.cwd || workingDirectory,
|
|
501
|
+
timeoutMs: options?.timeout
|
|
502
|
+
});
|
|
503
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
504
|
+
return {
|
|
505
|
+
stdout: result.stdout,
|
|
506
|
+
stderr: result.stderr,
|
|
507
|
+
exitCode: result.exitCode,
|
|
508
|
+
durationMs,
|
|
509
|
+
interrupted: false
|
|
510
|
+
};
|
|
511
|
+
} catch (error) {
|
|
512
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
513
|
+
if (error instanceof Error && error.message.toLowerCase().includes("timeout")) {
|
|
514
|
+
return {
|
|
515
|
+
stdout: "",
|
|
516
|
+
stderr: "Command timed out",
|
|
517
|
+
exitCode: 124,
|
|
518
|
+
durationMs,
|
|
519
|
+
interrupted: true
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (error instanceof Error) {
|
|
523
|
+
const exitMatch = error.message.match(/exit status (\d+)/i);
|
|
524
|
+
const exitCode = exitMatch ? parseInt(exitMatch[1], 10) : 1;
|
|
525
|
+
return {
|
|
526
|
+
stdout: "",
|
|
527
|
+
stderr: error.message,
|
|
528
|
+
exitCode,
|
|
529
|
+
durationMs,
|
|
530
|
+
interrupted: false
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
let rgPath;
|
|
537
|
+
const sandboxObj = {
|
|
538
|
+
exec,
|
|
539
|
+
get id() {
|
|
540
|
+
return sandboxId;
|
|
541
|
+
},
|
|
542
|
+
get rgPath() {
|
|
543
|
+
return rgPath;
|
|
544
|
+
},
|
|
545
|
+
set rgPath(path) {
|
|
546
|
+
rgPath = path;
|
|
547
|
+
},
|
|
548
|
+
async readFile(path) {
|
|
549
|
+
const result = await exec(`cat "${path}"`);
|
|
550
|
+
if (result.exitCode !== 0) {
|
|
551
|
+
throw new Error(`Failed to read file: ${result.stderr}`);
|
|
552
|
+
}
|
|
553
|
+
return result.stdout;
|
|
554
|
+
},
|
|
555
|
+
async writeFile(path, content) {
|
|
556
|
+
const sbx = await sandbox.get();
|
|
557
|
+
await sbx.files.write(path, content);
|
|
558
|
+
},
|
|
559
|
+
async readDir(path) {
|
|
560
|
+
const result = await exec(`ls -1 "${path}"`);
|
|
561
|
+
if (result.exitCode !== 0) {
|
|
562
|
+
throw new Error(`Failed to read directory: ${result.stderr}`);
|
|
563
|
+
}
|
|
564
|
+
return result.stdout.split(`
|
|
565
|
+
`).filter(Boolean);
|
|
566
|
+
},
|
|
567
|
+
async fileExists(path) {
|
|
568
|
+
const result = await exec(`test -e "${path}"`);
|
|
569
|
+
return result.exitCode === 0;
|
|
570
|
+
},
|
|
571
|
+
async isDirectory(path) {
|
|
572
|
+
const result = await exec(`test -d "${path}"`);
|
|
573
|
+
return result.exitCode === 0;
|
|
574
|
+
},
|
|
575
|
+
async destroy() {
|
|
576
|
+
try {
|
|
577
|
+
const sbx = await sandbox.get();
|
|
578
|
+
await sbx.kill();
|
|
579
|
+
} catch {}
|
|
580
|
+
sandbox.reset();
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
if (config.ensureTools !== false) {
|
|
584
|
+
await ensureSandboxTools(sandboxObj);
|
|
585
|
+
}
|
|
586
|
+
return sandboxObj;
|
|
587
|
+
}
|
|
588
|
+
// src/tools/bash.ts
|
|
589
|
+
import { tool, zodSchema } from "ai";
|
|
590
|
+
import { z } from "zod";
|
|
591
|
+
var bashInputSchema = z.object({
|
|
592
|
+
command: z.string().describe("The command to execute"),
|
|
593
|
+
timeout: z.number().optional().describe("Optional timeout in milliseconds (max 600000)"),
|
|
594
|
+
description: z.string().optional().describe("Clear, concise description of what this command does in 5-10 words"),
|
|
595
|
+
run_in_background: z.boolean().optional().describe("Set to true to run this command in the background")
|
|
596
|
+
});
|
|
597
|
+
var BASH_DESCRIPTION = `Executes a bash command in a persistent shell session with optional timeout.
|
|
598
|
+
|
|
599
|
+
IMPORTANT: For file operations (reading, writing, editing, searching, finding files) - use the specialized tools instead of bash commands.
|
|
600
|
+
|
|
601
|
+
Before executing the command, please follow these steps:
|
|
602
|
+
|
|
603
|
+
1. Directory Verification:
|
|
604
|
+
- If the command will create new directories or files, first use \`ls\` to verify the parent directory exists and is the correct location
|
|
605
|
+
- For example, before running "mkdir foo/bar", first use \`ls foo\` to check that "foo" exists
|
|
606
|
+
|
|
607
|
+
2. Command Execution:
|
|
608
|
+
- Always quote file paths that contain spaces with double quotes (e.g., cd "/path/with spaces")
|
|
609
|
+
- Examples of proper quoting:
|
|
610
|
+
- cd "/Users/name/My Documents" (correct)
|
|
611
|
+
- cd /Users/name/My Documents (incorrect - will fail)
|
|
612
|
+
- After ensuring proper quoting, execute the command
|
|
613
|
+
|
|
614
|
+
Usage notes:
|
|
615
|
+
- The command argument is required
|
|
616
|
+
- You can specify an optional timeout in milliseconds (max 600000ms / 10 minutes). Default is 120000ms (2 minutes).
|
|
617
|
+
- It is very helpful if you write a clear, concise description of what this command does in 5-10 words
|
|
618
|
+
- If the output exceeds 30000 characters, output will be truncated
|
|
619
|
+
- Avoid using \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands. Instead, use dedicated tools:
|
|
620
|
+
- File search: Use Glob (NOT find or ls)
|
|
621
|
+
- Content search: Use Grep (NOT grep or rg)
|
|
622
|
+
- Read files: Use Read (NOT cat/head/tail)
|
|
623
|
+
- Edit files: Use Edit (NOT sed/awk)
|
|
624
|
+
- Write files: Use Write (NOT echo >/cat <<EOF)
|
|
625
|
+
- When issuing multiple commands:
|
|
626
|
+
- If commands are independent, make multiple Bash tool calls in parallel
|
|
627
|
+
- If commands depend on each other, use '&&' to chain them (e.g., \`git add . && git commit -m "message"\`)
|
|
628
|
+
- Use ';' only when you need sequential execution but don't care if earlier commands fail
|
|
629
|
+
- DO NOT use newlines to separate commands
|
|
630
|
+
- Try to maintain your current working directory by using absolute paths and avoiding \`cd\``;
|
|
631
|
+
function createBashTool(sandbox, config) {
|
|
632
|
+
const maxOutputLength = config?.maxOutputLength ?? 30000;
|
|
633
|
+
const defaultTimeout = config?.timeout ?? 120000;
|
|
634
|
+
return tool({
|
|
635
|
+
description: BASH_DESCRIPTION,
|
|
636
|
+
inputSchema: zodSchema(bashInputSchema),
|
|
637
|
+
strict: config?.strict,
|
|
638
|
+
needsApproval: config?.needsApproval,
|
|
639
|
+
providerOptions: config?.providerOptions,
|
|
640
|
+
execute: async ({
|
|
641
|
+
command,
|
|
642
|
+
timeout,
|
|
643
|
+
description: _description,
|
|
644
|
+
run_in_background: _run_in_background
|
|
645
|
+
}) => {
|
|
646
|
+
if (config?.blockedCommands) {
|
|
647
|
+
for (const blocked of config.blockedCommands) {
|
|
648
|
+
if (command.includes(blocked)) {
|
|
649
|
+
return {
|
|
650
|
+
error: `Command blocked: contains '${blocked}'`
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
const effectiveTimeout = Math.min(timeout ?? defaultTimeout, 600000);
|
|
657
|
+
const result = await sandbox.exec(command, {
|
|
658
|
+
timeout: effectiveTimeout
|
|
659
|
+
});
|
|
660
|
+
let stdout = result.stdout;
|
|
661
|
+
let stderr = result.stderr;
|
|
662
|
+
if (stdout.length > maxOutputLength) {
|
|
663
|
+
stdout = stdout.slice(0, maxOutputLength) + `
|
|
664
|
+
[output truncated, ${stdout.length - maxOutputLength} chars omitted]`;
|
|
665
|
+
}
|
|
666
|
+
if (stderr.length > maxOutputLength) {
|
|
667
|
+
stderr = stderr.slice(0, maxOutputLength) + `
|
|
668
|
+
[output truncated, ${stderr.length - maxOutputLength} chars omitted]`;
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
stdout,
|
|
672
|
+
stderr,
|
|
673
|
+
exit_code: result.exitCode,
|
|
674
|
+
interrupted: result.interrupted,
|
|
675
|
+
duration_ms: result.durationMs
|
|
676
|
+
};
|
|
677
|
+
} catch (error) {
|
|
678
|
+
return {
|
|
679
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
// src/tools/read.ts
|
|
686
|
+
import { tool as tool2, zodSchema as zodSchema2 } from "ai";
|
|
687
|
+
import { z as z2 } from "zod";
|
|
688
|
+
var readInputSchema = z2.object({
|
|
689
|
+
file_path: z2.string().describe("Absolute path to file or directory"),
|
|
690
|
+
offset: z2.number().optional().describe("Line number to start reading from (1-indexed)"),
|
|
691
|
+
limit: z2.number().optional().describe("Maximum number of lines to read")
|
|
692
|
+
});
|
|
693
|
+
var READ_DESCRIPTION = `Reads a file from the local filesystem. You can access any file directly by using this tool.
|
|
694
|
+
Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
|
|
695
|
+
|
|
696
|
+
Usage:
|
|
697
|
+
- The file_path parameter must be an absolute path, not a relative path
|
|
698
|
+
- By default, it reads up to 500 lines starting from the beginning of the file
|
|
699
|
+
- You can optionally specify a line offset and limit (especially handy for long files)
|
|
700
|
+
- Results are returned with line numbers starting at 1
|
|
701
|
+
- This tool can only read text files, not binary files (images, PDFs, etc.)
|
|
702
|
+
- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.
|
|
703
|
+
- It is always better to speculatively read multiple potentially useful files in parallel
|
|
704
|
+
- If you read a file that exists but has empty contents you will receive a warning in place of file contents`;
|
|
705
|
+
function createReadTool(sandbox, config) {
|
|
706
|
+
return tool2({
|
|
707
|
+
description: READ_DESCRIPTION,
|
|
708
|
+
inputSchema: zodSchema2(readInputSchema),
|
|
709
|
+
strict: config?.strict,
|
|
710
|
+
needsApproval: config?.needsApproval,
|
|
711
|
+
providerOptions: config?.providerOptions,
|
|
712
|
+
execute: async ({
|
|
713
|
+
file_path,
|
|
714
|
+
offset,
|
|
715
|
+
limit
|
|
716
|
+
}) => {
|
|
717
|
+
if (config?.allowedPaths) {
|
|
718
|
+
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
719
|
+
if (!isAllowed) {
|
|
720
|
+
return { error: `Path not allowed: ${file_path}` };
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
const exists = await sandbox.fileExists(file_path);
|
|
725
|
+
if (!exists) {
|
|
726
|
+
return { error: `Path not found: ${file_path}` };
|
|
727
|
+
}
|
|
728
|
+
const isDir = await sandbox.isDirectory(file_path);
|
|
729
|
+
if (isDir) {
|
|
730
|
+
const entries = await sandbox.readDir(file_path);
|
|
731
|
+
return {
|
|
732
|
+
type: "directory",
|
|
733
|
+
entries,
|
|
734
|
+
count: entries.length
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const content = await sandbox.readFile(file_path);
|
|
738
|
+
const nullByteIndex = content.indexOf("\x00");
|
|
739
|
+
if (nullByteIndex !== -1 && nullByteIndex < 1000) {
|
|
740
|
+
const ext = file_path.split(".").pop()?.toLowerCase();
|
|
741
|
+
const binaryExtensions = [
|
|
742
|
+
"pdf",
|
|
743
|
+
"png",
|
|
744
|
+
"jpg",
|
|
745
|
+
"jpeg",
|
|
746
|
+
"gif",
|
|
747
|
+
"zip",
|
|
748
|
+
"tar",
|
|
749
|
+
"gz",
|
|
750
|
+
"exe",
|
|
751
|
+
"bin",
|
|
752
|
+
"so",
|
|
753
|
+
"dylib"
|
|
754
|
+
];
|
|
755
|
+
if (binaryExtensions.includes(ext || "")) {
|
|
756
|
+
return {
|
|
757
|
+
error: `Cannot read binary file: ${file_path} (file exists, ${content.length} bytes). Use appropriate tools to process ${ext?.toUpperCase()} files (e.g., Python scripts for PDFs).`
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const allLines = content.split(`
|
|
762
|
+
`);
|
|
763
|
+
const totalLines = allLines.length;
|
|
764
|
+
const maxLinesWithoutLimit = config?.maxFileSize || 500;
|
|
765
|
+
if (!limit && totalLines > maxLinesWithoutLimit) {
|
|
766
|
+
return {
|
|
767
|
+
error: `File is large (${totalLines} lines). Use 'offset' and 'limit' to read in chunks. Example: offset=1, limit=100 for first 100 lines.`
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
const startLine = offset ? offset - 1 : 0;
|
|
771
|
+
const endLine = limit ? startLine + limit : allLines.length;
|
|
772
|
+
const selectedLines = allLines.slice(startLine, endLine);
|
|
773
|
+
const lines = selectedLines.map((line, i) => ({
|
|
774
|
+
line_number: startLine + i + 1,
|
|
775
|
+
content: line
|
|
776
|
+
}));
|
|
777
|
+
return {
|
|
778
|
+
type: "text",
|
|
779
|
+
content: selectedLines.join(`
|
|
780
|
+
`),
|
|
781
|
+
lines,
|
|
782
|
+
total_lines: totalLines
|
|
783
|
+
};
|
|
784
|
+
} catch (error) {
|
|
785
|
+
return {
|
|
786
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
// src/tools/write.ts
|
|
793
|
+
import { tool as tool3, zodSchema as zodSchema3 } from "ai";
|
|
794
|
+
import { z as z3 } from "zod";
|
|
795
|
+
var writeInputSchema = z3.object({
|
|
796
|
+
file_path: z3.string().describe("Path to the file to write"),
|
|
797
|
+
content: z3.string().describe("Content to write to the file")
|
|
798
|
+
});
|
|
799
|
+
var WRITE_DESCRIPTION = `Writes content to a file on the filesystem.
|
|
800
|
+
|
|
801
|
+
**Important guidelines:**
|
|
802
|
+
- This tool will overwrite existing files at the provided path
|
|
803
|
+
- If modifying an existing file, you MUST use the Read tool first to read the file's contents
|
|
804
|
+
- ALWAYS prefer editing existing files over creating new ones
|
|
805
|
+
- NEVER proactively create documentation files (*.md) or README files unless explicitly requested
|
|
806
|
+
- The file_path must be an absolute path, not relative
|
|
807
|
+
|
|
808
|
+
**When to use Write vs Edit:**
|
|
809
|
+
- Use Write for creating new files or completely replacing file contents
|
|
810
|
+
- Use Edit for making targeted changes to existing files (preferred for modifications)`;
|
|
811
|
+
function createWriteTool(sandbox, config) {
|
|
812
|
+
return tool3({
|
|
813
|
+
description: WRITE_DESCRIPTION,
|
|
814
|
+
inputSchema: zodSchema3(writeInputSchema),
|
|
815
|
+
strict: config?.strict,
|
|
816
|
+
needsApproval: config?.needsApproval,
|
|
817
|
+
providerOptions: config?.providerOptions,
|
|
818
|
+
execute: async ({
|
|
819
|
+
file_path,
|
|
820
|
+
content
|
|
821
|
+
}) => {
|
|
822
|
+
const byteLength = Buffer.byteLength(content, "utf-8");
|
|
823
|
+
if (config?.maxFileSize && byteLength > config.maxFileSize) {
|
|
824
|
+
return {
|
|
825
|
+
error: `File content exceeds maximum size of ${config.maxFileSize} bytes (got ${byteLength})`
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
if (config?.allowedPaths) {
|
|
829
|
+
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
830
|
+
if (!isAllowed) {
|
|
831
|
+
return { error: `Path not allowed: ${file_path}` };
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
try {
|
|
835
|
+
await sandbox.writeFile(file_path, content);
|
|
836
|
+
return {
|
|
837
|
+
message: `Successfully wrote to ${file_path}`,
|
|
838
|
+
bytes_written: byteLength,
|
|
839
|
+
file_path
|
|
840
|
+
};
|
|
841
|
+
} catch (error) {
|
|
842
|
+
return {
|
|
843
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
// src/tools/edit.ts
|
|
850
|
+
import { tool as tool4, zodSchema as zodSchema4 } from "ai";
|
|
851
|
+
import { z as z4 } from "zod";
|
|
852
|
+
var editInputSchema = z4.object({
|
|
853
|
+
file_path: z4.string().describe("The absolute path to the file to modify"),
|
|
854
|
+
old_string: z4.string().describe("The text to replace"),
|
|
855
|
+
new_string: z4.string().describe("The text to replace it with (must be different from old_string)"),
|
|
856
|
+
replace_all: z4.boolean().optional().describe("Replace all occurrences of old_string (default false)")
|
|
857
|
+
});
|
|
858
|
+
var EDIT_DESCRIPTION = `Performs exact string replacements in files.
|
|
859
|
+
|
|
860
|
+
**Important guidelines:**
|
|
861
|
+
- You MUST use the Read tool first before editing any file
|
|
862
|
+
- Preserve exact indentation (tabs/spaces) when replacing text
|
|
863
|
+
- The old_string must be unique in the file, or the edit will fail
|
|
864
|
+
- If old_string appears multiple times, either provide more context to make it unique, or use replace_all=true
|
|
865
|
+
|
|
866
|
+
**Parameters:**
|
|
867
|
+
- old_string: The exact text to find and replace (must match exactly, including whitespace)
|
|
868
|
+
- new_string: The replacement text (must be different from old_string)
|
|
869
|
+
- replace_all: Set to true to replace all occurrences (useful for renaming variables)
|
|
870
|
+
|
|
871
|
+
**When to use:**
|
|
872
|
+
- Making targeted changes to existing files
|
|
873
|
+
- Renaming variables or functions (with replace_all=true)
|
|
874
|
+
- Updating specific sections`;
|
|
875
|
+
function createEditTool(sandbox, config) {
|
|
876
|
+
return tool4({
|
|
877
|
+
description: EDIT_DESCRIPTION,
|
|
878
|
+
inputSchema: zodSchema4(editInputSchema),
|
|
879
|
+
strict: config?.strict,
|
|
880
|
+
needsApproval: config?.needsApproval,
|
|
881
|
+
providerOptions: config?.providerOptions,
|
|
882
|
+
execute: async ({
|
|
883
|
+
file_path,
|
|
884
|
+
old_string,
|
|
885
|
+
new_string,
|
|
886
|
+
replace_all = false
|
|
887
|
+
}) => {
|
|
888
|
+
if (old_string === new_string) {
|
|
889
|
+
return { error: "old_string and new_string must be different" };
|
|
890
|
+
}
|
|
891
|
+
if (config?.allowedPaths) {
|
|
892
|
+
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
893
|
+
if (!isAllowed) {
|
|
894
|
+
return { error: `Path not allowed: ${file_path}` };
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
try {
|
|
898
|
+
const exists = await sandbox.fileExists(file_path);
|
|
899
|
+
if (!exists) {
|
|
900
|
+
return { error: `File not found: ${file_path}` };
|
|
901
|
+
}
|
|
902
|
+
const content = await sandbox.readFile(file_path);
|
|
903
|
+
const occurrences = content.split(old_string).length - 1;
|
|
904
|
+
if (occurrences === 0) {
|
|
905
|
+
return { error: `String not found in file: "${old_string}"` };
|
|
906
|
+
}
|
|
907
|
+
if (!replace_all && occurrences > 1) {
|
|
908
|
+
return {
|
|
909
|
+
error: `String appears ${occurrences} times in file. Use replace_all=true to replace all, or provide a more unique string.`
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
let newContent;
|
|
913
|
+
let replacements;
|
|
914
|
+
if (replace_all) {
|
|
915
|
+
newContent = content.split(old_string).join(new_string);
|
|
916
|
+
replacements = occurrences;
|
|
917
|
+
} else {
|
|
918
|
+
newContent = content.replace(old_string, new_string);
|
|
919
|
+
replacements = 1;
|
|
920
|
+
}
|
|
921
|
+
await sandbox.writeFile(file_path, newContent);
|
|
922
|
+
return {
|
|
923
|
+
message: `Successfully edited ${file_path}`,
|
|
924
|
+
file_path,
|
|
925
|
+
replacements
|
|
926
|
+
};
|
|
927
|
+
} catch (error) {
|
|
928
|
+
return {
|
|
929
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
// src/tools/glob.ts
|
|
936
|
+
import { tool as tool5, zodSchema as zodSchema5 } from "ai";
|
|
937
|
+
import { z as z5 } from "zod";
|
|
938
|
+
var globInputSchema = z5.object({
|
|
939
|
+
pattern: z5.string().describe('Glob pattern to match files (e.g., "**/*.ts", "src/**/*.js", "*.md")'),
|
|
940
|
+
path: z5.string().optional().describe("Directory to search in (defaults to working directory)")
|
|
941
|
+
});
|
|
942
|
+
var GLOB_DESCRIPTION = `
|
|
943
|
+
- Fast file pattern matching tool that works with any codebase size
|
|
944
|
+
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
|
|
945
|
+
- Returns matching file paths sorted by modification time
|
|
946
|
+
- Use this tool when you need to find files by name patterns
|
|
947
|
+
- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Task tool instead
|
|
948
|
+
- It is always better to speculatively perform multiple searches in parallel if they are potentially useful
|
|
949
|
+
`;
|
|
950
|
+
function createGlobTool(sandbox, config) {
|
|
951
|
+
return tool5({
|
|
952
|
+
description: GLOB_DESCRIPTION,
|
|
953
|
+
inputSchema: zodSchema5(globInputSchema),
|
|
954
|
+
strict: config?.strict,
|
|
955
|
+
needsApproval: config?.needsApproval,
|
|
956
|
+
providerOptions: config?.providerOptions,
|
|
957
|
+
execute: async ({
|
|
958
|
+
pattern,
|
|
959
|
+
path
|
|
960
|
+
}) => {
|
|
961
|
+
const searchPath = path || ".";
|
|
962
|
+
if (config?.allowedPaths) {
|
|
963
|
+
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
964
|
+
if (!isAllowed) {
|
|
965
|
+
return { error: `Path not allowed: ${searchPath}` };
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
try {
|
|
969
|
+
const result = await sandbox.exec(`find ${searchPath} -type f -name "${pattern}" 2>/dev/null | head -1000`, { timeout: config?.timeout });
|
|
970
|
+
if (result.exitCode !== 0 && result.stderr) {
|
|
971
|
+
return { error: result.stderr };
|
|
972
|
+
}
|
|
973
|
+
const matches = result.stdout.split(`
|
|
974
|
+
`).filter(Boolean).map((p) => p.trim());
|
|
975
|
+
return {
|
|
976
|
+
matches,
|
|
977
|
+
count: matches.length,
|
|
978
|
+
search_path: searchPath
|
|
979
|
+
};
|
|
980
|
+
} catch (error) {
|
|
981
|
+
return {
|
|
982
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
// src/tools/grep.ts
|
|
989
|
+
import { tool as tool6, zodSchema as zodSchema6 } from "ai";
|
|
990
|
+
import { z as z6 } from "zod";
|
|
991
|
+
var grepInputSchema = z6.object({
|
|
992
|
+
pattern: z6.string().describe("The regular expression pattern to search for in file contents"),
|
|
993
|
+
path: z6.string().optional().describe("File or directory to search in (defaults to cwd)"),
|
|
994
|
+
glob: z6.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}")'),
|
|
995
|
+
type: z6.string().optional().describe('File type to search (e.g. "js", "py", "rust")'),
|
|
996
|
+
output_mode: z6.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines, "files_with_matches" shows file paths (default), "count" shows match counts'),
|
|
997
|
+
"-i": z6.boolean().optional().describe("Case insensitive search"),
|
|
998
|
+
"-n": z6.boolean().optional().describe("Show line numbers in output. Requires output_mode: 'content'. Defaults to true."),
|
|
999
|
+
"-B": z6.number().optional().describe("Number of lines to show before each match. Requires output_mode: 'content'."),
|
|
1000
|
+
"-A": z6.number().optional().describe("Number of lines to show after each match. Requires output_mode: 'content'."),
|
|
1001
|
+
"-C": z6.number().optional().describe("Number of lines to show before and after each match. Requires output_mode: 'content'."),
|
|
1002
|
+
head_limit: z6.number().optional().describe("Limit output to first N lines/entries. Works across all output modes. Defaults to 0 (unlimited)."),
|
|
1003
|
+
offset: z6.number().optional().describe("Skip first N lines/entries before applying head_limit. Works across all output modes. Defaults to 0."),
|
|
1004
|
+
multiline: z6.boolean().optional().describe("Enable multiline mode where patterns can span lines. Default: false.")
|
|
1005
|
+
});
|
|
1006
|
+
var GREP_DESCRIPTION = `A powerful content search tool built on ripgrep with regex support.
|
|
1007
|
+
|
|
1008
|
+
**Usage:**
|
|
1009
|
+
- ALWAYS use Grep for search tasks. NEVER invoke \`grep\` or \`rg\` as a Bash command.
|
|
1010
|
+
- Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
|
|
1011
|
+
- Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")
|
|
1012
|
+
|
|
1013
|
+
**Output modes:**
|
|
1014
|
+
- "content": Shows matching lines with optional context
|
|
1015
|
+
- "files_with_matches": Shows only file paths (default)
|
|
1016
|
+
- "count": Shows match counts per file
|
|
1017
|
+
|
|
1018
|
+
**Context options (content mode only):**
|
|
1019
|
+
- -B: Lines to show before each match
|
|
1020
|
+
- -A: Lines to show after each match
|
|
1021
|
+
- -C: Lines to show before and after each match
|
|
1022
|
+
|
|
1023
|
+
**Pagination:**
|
|
1024
|
+
- Use offset to skip results (useful for pagination)
|
|
1025
|
+
- Use head_limit to limit total results returned`;
|
|
1026
|
+
function createGrepTool(sandbox, config) {
|
|
1027
|
+
return tool6({
|
|
1028
|
+
description: GREP_DESCRIPTION,
|
|
1029
|
+
inputSchema: zodSchema6(grepInputSchema),
|
|
1030
|
+
strict: config?.strict,
|
|
1031
|
+
needsApproval: config?.needsApproval,
|
|
1032
|
+
providerOptions: config?.providerOptions,
|
|
1033
|
+
execute: async (input) => {
|
|
1034
|
+
const {
|
|
1035
|
+
pattern,
|
|
1036
|
+
path,
|
|
1037
|
+
glob,
|
|
1038
|
+
type,
|
|
1039
|
+
output_mode = "files_with_matches",
|
|
1040
|
+
"-i": caseInsensitive,
|
|
1041
|
+
"-B": beforeContext,
|
|
1042
|
+
"-A": afterContext,
|
|
1043
|
+
"-C": context,
|
|
1044
|
+
head_limit,
|
|
1045
|
+
offset = 0,
|
|
1046
|
+
multiline
|
|
1047
|
+
} = input;
|
|
1048
|
+
const searchPath = path || ".";
|
|
1049
|
+
if (config?.allowedPaths) {
|
|
1050
|
+
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
1051
|
+
if (!isAllowed) {
|
|
1052
|
+
return { error: `Path not allowed: ${searchPath}` };
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
if (!sandbox.rgPath) {
|
|
1057
|
+
return {
|
|
1058
|
+
error: "Ripgrep not available. Call ensureSandboxTools(sandbox) before using Grep with remote sandboxes."
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
const cmd = buildRipgrepCommand({
|
|
1062
|
+
rgPath: sandbox.rgPath,
|
|
1063
|
+
pattern,
|
|
1064
|
+
searchPath,
|
|
1065
|
+
output_mode,
|
|
1066
|
+
caseInsensitive,
|
|
1067
|
+
beforeContext,
|
|
1068
|
+
afterContext,
|
|
1069
|
+
context,
|
|
1070
|
+
glob,
|
|
1071
|
+
type,
|
|
1072
|
+
multiline
|
|
1073
|
+
});
|
|
1074
|
+
const result = await sandbox.exec(cmd, { timeout: config?.timeout });
|
|
1075
|
+
if (output_mode === "files_with_matches") {
|
|
1076
|
+
return parseFilesOutput(result.stdout);
|
|
1077
|
+
} else if (output_mode === "count") {
|
|
1078
|
+
return parseCountOutput(result.stdout);
|
|
1079
|
+
} else {
|
|
1080
|
+
return parseContentOutput(result.stdout, head_limit, offset);
|
|
1081
|
+
}
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
return {
|
|
1084
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function buildRipgrepCommand(opts) {
|
|
1091
|
+
const flags = ["--json"];
|
|
1092
|
+
if (opts.caseInsensitive)
|
|
1093
|
+
flags.push("-i");
|
|
1094
|
+
if (opts.multiline)
|
|
1095
|
+
flags.push("-U", "--multiline-dotall");
|
|
1096
|
+
if (opts.output_mode === "content") {
|
|
1097
|
+
if (opts.context) {
|
|
1098
|
+
flags.push(`-C ${opts.context}`);
|
|
1099
|
+
} else {
|
|
1100
|
+
if (opts.beforeContext)
|
|
1101
|
+
flags.push(`-B ${opts.beforeContext}`);
|
|
1102
|
+
if (opts.afterContext)
|
|
1103
|
+
flags.push(`-A ${opts.afterContext}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (opts.glob)
|
|
1107
|
+
flags.push(`-g "${opts.glob}"`);
|
|
1108
|
+
if (opts.type)
|
|
1109
|
+
flags.push(`-t ${opts.type}`);
|
|
1110
|
+
const flagStr = flags.join(" ");
|
|
1111
|
+
return `${opts.rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
|
|
1112
|
+
}
|
|
1113
|
+
function parseFilesOutput(stdout) {
|
|
1114
|
+
const files = new Set;
|
|
1115
|
+
for (const line of stdout.split(`
|
|
1116
|
+
`).filter(Boolean)) {
|
|
1117
|
+
try {
|
|
1118
|
+
const msg = JSON.parse(line);
|
|
1119
|
+
if (msg.type === "begin") {
|
|
1120
|
+
const data = msg.data;
|
|
1121
|
+
files.add(data.path.text);
|
|
1122
|
+
}
|
|
1123
|
+
} catch {}
|
|
1124
|
+
}
|
|
1125
|
+
return {
|
|
1126
|
+
files: Array.from(files),
|
|
1127
|
+
count: files.size
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
function parseCountOutput(stdout) {
|
|
1131
|
+
const counts = new Map;
|
|
1132
|
+
for (const line of stdout.split(`
|
|
1133
|
+
`).filter(Boolean)) {
|
|
1134
|
+
try {
|
|
1135
|
+
const msg = JSON.parse(line);
|
|
1136
|
+
if (msg.type === "end") {
|
|
1137
|
+
const data = msg.data;
|
|
1138
|
+
counts.set(data.path.text, data.stats.matches);
|
|
1139
|
+
}
|
|
1140
|
+
} catch {}
|
|
1141
|
+
}
|
|
1142
|
+
const countsArray = Array.from(counts.entries()).map(([file, count]) => ({
|
|
1143
|
+
file,
|
|
1144
|
+
count
|
|
1145
|
+
}));
|
|
1146
|
+
const total = countsArray.reduce((sum, c) => sum + c.count, 0);
|
|
1147
|
+
return {
|
|
1148
|
+
counts: countsArray,
|
|
1149
|
+
total
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function parseContentOutput(stdout, head_limit, offset = 0) {
|
|
1153
|
+
const fileData = new Map;
|
|
1154
|
+
for (const line of stdout.split(`
|
|
1155
|
+
`).filter(Boolean)) {
|
|
1156
|
+
try {
|
|
1157
|
+
const msg = JSON.parse(line);
|
|
1158
|
+
if (msg.type === "begin") {
|
|
1159
|
+
const data = msg.data;
|
|
1160
|
+
fileData.set(data.path.text, { matches: [], contexts: [] });
|
|
1161
|
+
} else if (msg.type === "context") {
|
|
1162
|
+
const data = msg.data;
|
|
1163
|
+
const fd = fileData.get(data.path.text);
|
|
1164
|
+
if (fd) {
|
|
1165
|
+
fd.contexts.push({
|
|
1166
|
+
line_number: data.line_number,
|
|
1167
|
+
text: data.lines.text.replace(/\n$/, "")
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
} else if (msg.type === "match") {
|
|
1171
|
+
const data = msg.data;
|
|
1172
|
+
const fd = fileData.get(data.path.text);
|
|
1173
|
+
if (fd) {
|
|
1174
|
+
fd.matches.push({
|
|
1175
|
+
line_number: data.line_number,
|
|
1176
|
+
text: data.lines.text.replace(/\n$/, "")
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
} catch {}
|
|
1181
|
+
}
|
|
1182
|
+
const allMatches = [];
|
|
1183
|
+
for (const [file, { matches, contexts }] of fileData) {
|
|
1184
|
+
matches.sort((a, b) => a.line_number - b.line_number);
|
|
1185
|
+
contexts.sort((a, b) => a.line_number - b.line_number);
|
|
1186
|
+
const matchContexts = new Map;
|
|
1187
|
+
for (const match of matches) {
|
|
1188
|
+
matchContexts.set(match.line_number, { before: [], after: [] });
|
|
1189
|
+
}
|
|
1190
|
+
for (const ctx of contexts) {
|
|
1191
|
+
let bestMatch = null;
|
|
1192
|
+
let bestDistance = Infinity;
|
|
1193
|
+
let isBefore = false;
|
|
1194
|
+
for (const match of matches) {
|
|
1195
|
+
const distance = Math.abs(ctx.line_number - match.line_number);
|
|
1196
|
+
if (distance < bestDistance) {
|
|
1197
|
+
bestDistance = distance;
|
|
1198
|
+
bestMatch = match;
|
|
1199
|
+
isBefore = ctx.line_number < match.line_number;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (bestMatch) {
|
|
1203
|
+
const mc = matchContexts.get(bestMatch.line_number);
|
|
1204
|
+
if (mc) {
|
|
1205
|
+
if (isBefore) {
|
|
1206
|
+
mc.before.push(ctx.text);
|
|
1207
|
+
} else {
|
|
1208
|
+
mc.after.push(ctx.text);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
for (const match of matches) {
|
|
1214
|
+
const mc = matchContexts.get(match.line_number);
|
|
1215
|
+
allMatches.push({
|
|
1216
|
+
file,
|
|
1217
|
+
line_number: match.line_number,
|
|
1218
|
+
line: match.text,
|
|
1219
|
+
before_context: mc?.before ?? [],
|
|
1220
|
+
after_context: mc?.after ?? []
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
const grepMatches = allMatches.map((m) => ({
|
|
1225
|
+
file: m.file,
|
|
1226
|
+
line_number: m.line_number,
|
|
1227
|
+
line: m.line,
|
|
1228
|
+
before_context: m.before_context.length > 0 ? m.before_context : undefined,
|
|
1229
|
+
after_context: m.after_context.length > 0 ? m.after_context : undefined
|
|
1230
|
+
}));
|
|
1231
|
+
let result = grepMatches;
|
|
1232
|
+
if (offset > 0) {
|
|
1233
|
+
result = result.slice(offset);
|
|
1234
|
+
}
|
|
1235
|
+
if (head_limit && head_limit > 0) {
|
|
1236
|
+
result = result.slice(0, head_limit);
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
matches: result,
|
|
1240
|
+
total_matches: result.length
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
export {
|
|
1244
|
+
createWriteTool,
|
|
1245
|
+
createReadTool,
|
|
1246
|
+
createGrepTool,
|
|
1247
|
+
createGlobTool,
|
|
1248
|
+
createEditTool,
|
|
1249
|
+
createE2BSandbox,
|
|
1250
|
+
createBashTool
|
|
1251
|
+
};
|