@cryer/star-cli 0.3.13 → 0.3.14
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 +17 -5
- package/README.zh-CN.md +15 -5
- package/dist/{chunk-6PLZ3FLG.js → chunk-2ISBRSBA.js} +375 -235
- package/dist/chunk-2ISBRSBA.js.map +1 -0
- package/dist/{chunk-QELCMXQX.js → chunk-4HANB3DJ.js} +381 -212
- package/dist/chunk-4HANB3DJ.js.map +1 -0
- package/dist/{chunk-OF42OVM4.js → chunk-54KH4JSO.js} +204 -125
- package/dist/chunk-54KH4JSO.js.map +1 -0
- package/dist/{chunk-RD4S5LKM.js → chunk-CECMCDZG.js} +43 -30
- package/dist/chunk-CECMCDZG.js.map +1 -0
- package/dist/{chunk-MVAMPVTA.js → chunk-KILMI3S5.js} +13 -5
- package/dist/{chunk-MVAMPVTA.js.map → chunk-KILMI3S5.js.map} +1 -1
- package/dist/{chunk-NMB3TNQU.js → chunk-NRLGGA7L.js} +507 -175
- package/dist/chunk-NRLGGA7L.js.map +1 -0
- package/dist/loop-M64D5YII.js +17 -0
- package/dist/main.js +177 -131
- package/dist/main.js.map +1 -1
- package/dist/{provider-EWMUPBLH.js → provider-3Z4B7RMF.js} +2 -2
- package/dist/{repl-ZABBT33D.js → repl-N2H7JDSH.js} +617 -327
- package/dist/repl-N2H7JDSH.js.map +1 -0
- package/dist/{tools-4OLADVDR.js → tools-5RKM7ZI3.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-6PLZ3FLG.js.map +0 -1
- package/dist/chunk-NMB3TNQU.js.map +0 -1
- package/dist/chunk-OF42OVM4.js.map +0 -1
- package/dist/chunk-QELCMXQX.js.map +0 -1
- package/dist/chunk-RD4S5LKM.js.map +0 -1
- package/dist/loop-DWQ5WTPD.js +0 -17
- package/dist/repl-ZABBT33D.js.map +0 -1
- /package/dist/{loop-DWQ5WTPD.js.map → loop-M64D5YII.js.map} +0 -0
- /package/dist/{provider-EWMUPBLH.js.map → provider-3Z4B7RMF.js.map} +0 -0
- /package/dist/{tools-4OLADVDR.js.map → tools-5RKM7ZI3.js.map} +0 -0
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
starHome,
|
|
24
24
|
walkFiles,
|
|
25
25
|
webFetchTool
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-2ISBRSBA.js";
|
|
27
27
|
|
|
28
28
|
// src/tools/fs/edit.ts
|
|
29
29
|
import { readFile, stat, writeFile } from "fs/promises";
|
|
@@ -42,6 +42,31 @@ function countOccurrences(haystack, needle) {
|
|
|
42
42
|
function normalizeEol(text) {
|
|
43
43
|
return text.replace(/\r\n/g, "\n");
|
|
44
44
|
}
|
|
45
|
+
function normalizedOffsets(text) {
|
|
46
|
+
const offsets = [0];
|
|
47
|
+
for (let i = 0; i < text.length; i++) {
|
|
48
|
+
if (text[i] === "\r" && text[i + 1] === "\n") i++;
|
|
49
|
+
offsets.push(i + 1);
|
|
50
|
+
}
|
|
51
|
+
return offsets;
|
|
52
|
+
}
|
|
53
|
+
function replacePreservingEol(raw, normalized, offsets, oldString, newString, replaceAll, useCrlf) {
|
|
54
|
+
const replacement = useCrlf ? newString.replace(/\n/g, "\r\n") : newString;
|
|
55
|
+
let normalizedFrom = 0;
|
|
56
|
+
let rawFrom = 0;
|
|
57
|
+
let updated = "";
|
|
58
|
+
while (true) {
|
|
59
|
+
const match = normalized.indexOf(oldString, normalizedFrom);
|
|
60
|
+
if (match < 0) break;
|
|
61
|
+
const rawStart = offsets[match] ?? raw.length;
|
|
62
|
+
const rawEnd = offsets[match + oldString.length] ?? raw.length;
|
|
63
|
+
updated += raw.slice(rawFrom, rawStart) + replacement;
|
|
64
|
+
rawFrom = rawEnd;
|
|
65
|
+
normalizedFrom = match + oldString.length;
|
|
66
|
+
if (!replaceAll) break;
|
|
67
|
+
}
|
|
68
|
+
return updated + raw.slice(rawFrom);
|
|
69
|
+
}
|
|
45
70
|
var schema = z.object({
|
|
46
71
|
path: z.string().describe("File path, absolute or relative to the working directory"),
|
|
47
72
|
old_string: z.string().min(1).describe("Exact text to replace"),
|
|
@@ -81,7 +106,8 @@ var editFileTool = {
|
|
|
81
106
|
let working = content;
|
|
82
107
|
let oldString = args.old_string;
|
|
83
108
|
let newString = args.new_string;
|
|
84
|
-
let
|
|
109
|
+
let offsets = null;
|
|
110
|
+
let useCrlf = false;
|
|
85
111
|
let count = countOccurrences(working, oldString);
|
|
86
112
|
if (count === 0) {
|
|
87
113
|
const normalizedContent = normalizeEol(content);
|
|
@@ -90,7 +116,8 @@ var editFileTool = {
|
|
|
90
116
|
if (normalizedCount > 0) {
|
|
91
117
|
const crlfCount = countOccurrences(content, "\r\n");
|
|
92
118
|
const loneLfCount = countOccurrences(content.replace(/\r\n/g, ""), "\n");
|
|
93
|
-
|
|
119
|
+
useCrlf = crlfCount > 0 && crlfCount >= loneLfCount;
|
|
120
|
+
offsets = normalizedOffsets(content);
|
|
94
121
|
working = normalizedContent;
|
|
95
122
|
oldString = normalizedOld;
|
|
96
123
|
newString = normalizeEol(newString);
|
|
@@ -106,10 +133,15 @@ var editFileTool = {
|
|
|
106
133
|
isError: true
|
|
107
134
|
};
|
|
108
135
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
const updated = offsets ? replacePreservingEol(
|
|
137
|
+
content,
|
|
138
|
+
working,
|
|
139
|
+
offsets,
|
|
140
|
+
oldString,
|
|
141
|
+
newString,
|
|
142
|
+
args.replace_all === true,
|
|
143
|
+
useCrlf
|
|
144
|
+
) : args.replace_all ? working.split(oldString).join(newString) : working.replace(oldString, () => newString);
|
|
113
145
|
try {
|
|
114
146
|
const now = await stat(filePath).catch(() => null);
|
|
115
147
|
if (!now || now.mtimeMs !== readMtimeMs) {
|
|
@@ -180,13 +212,17 @@ var globTool = {
|
|
|
180
212
|
const prefixSegments = literalDirPrefix(args.pattern);
|
|
181
213
|
const walkRoot = prefixSegments.length > 0 ? path2.join(root, ...prefixSegments) : root;
|
|
182
214
|
const prefix = prefixSegments.length > 0 ? `${prefixSegments.join("/")}/` : "";
|
|
183
|
-
|
|
215
|
+
let matched = [];
|
|
216
|
+
const { aborted } = await walkFiles(walkRoot, void 0, {
|
|
184
217
|
ignore: createIgnorePredicate(ctx.cwd),
|
|
185
218
|
signal: ctx.abortSignal,
|
|
186
219
|
symlinkBoundary: ctx.cwd,
|
|
187
|
-
nestedIgnore: true
|
|
220
|
+
nestedIgnore: true,
|
|
221
|
+
collect: false,
|
|
222
|
+
onBatch: (files) => {
|
|
223
|
+
matched = matched.concat(files.filter((f) => matchesGlob(args.pattern, `${prefix}${f.rel}`))).sort((a, b) => b.mtimeMs - a.mtimeMs || (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0)).slice(0, MAX_RESULTS);
|
|
224
|
+
}
|
|
188
225
|
});
|
|
189
|
-
const matched = files.filter((f) => matchesGlob(args.pattern, `${prefix}${f.rel}`)).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, MAX_RESULTS);
|
|
190
226
|
if (matched.length === 0) {
|
|
191
227
|
return {
|
|
192
228
|
content: aborted ? `Search interrupted before any files matched pattern: ${args.pattern}` : `No files matched pattern: ${args.pattern}`
|
|
@@ -236,44 +272,13 @@ var grepTool = {
|
|
|
236
272
|
return { content: `Invalid regular expression: ${err.message}`, isError: true };
|
|
237
273
|
}
|
|
238
274
|
const root = path3.resolve(ctx.cwd, args.path ?? ".");
|
|
239
|
-
let files;
|
|
240
275
|
let interrupted = false;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const resolved = await realpath(root).catch(() => null);
|
|
245
|
-
if (isSensitivePath(root) || resolved !== null && isSensitivePath(resolved)) {
|
|
246
|
-
return {
|
|
247
|
-
content: `Refused to grep sensitive file: ${args.path ?? "."}`,
|
|
248
|
-
isError: true
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
files = [{ abs: root, rel: path3.basename(root), mtimeMs: st.mtimeMs }];
|
|
252
|
-
} else {
|
|
253
|
-
const walked = await walkFiles(root, void 0, {
|
|
254
|
-
ignore: createIgnorePredicate(ctx.cwd),
|
|
255
|
-
withMtime: false,
|
|
256
|
-
signal: ctx.abortSignal,
|
|
257
|
-
symlinkBoundary: ctx.cwd,
|
|
258
|
-
nestedIgnore: true
|
|
259
|
-
});
|
|
260
|
-
interrupted = walked.aborted;
|
|
261
|
-
files = walked.files.filter(
|
|
262
|
-
(f) => !isSensitivePath(f.abs) && (f.resolved === void 0 || !isSensitivePath(f.resolved))
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
} catch {
|
|
266
|
-
return { content: `Path not found: ${args.path ?? "."}`, isError: true };
|
|
267
|
-
}
|
|
268
|
-
if (args.glob) {
|
|
269
|
-
const glob = args.glob;
|
|
270
|
-
files = files.filter((f) => matchesGlob(glob, f.rel));
|
|
271
|
-
}
|
|
276
|
+
const out = [];
|
|
277
|
+
let truncated = false;
|
|
278
|
+
let skippedLarge = 0;
|
|
272
279
|
const scanFile = async (file) => {
|
|
273
280
|
const st = await stat2(file.abs).catch(() => null);
|
|
274
|
-
if (!st)
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
281
|
+
if (!st) return null;
|
|
277
282
|
if (st.size > MAX_FILE_BYTES) {
|
|
278
283
|
skippedLarge += 1;
|
|
279
284
|
return null;
|
|
@@ -284,12 +289,10 @@ var grepTool = {
|
|
|
284
289
|
} catch {
|
|
285
290
|
return null;
|
|
286
291
|
}
|
|
287
|
-
if (buf.includes(0))
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
292
|
+
if (buf.includes(0)) return null;
|
|
290
293
|
const hits = [];
|
|
291
294
|
const lines = buf.toString("utf8").split("\n");
|
|
292
|
-
for (let i = 0; i < lines.length; i
|
|
295
|
+
for (let i = 0; i < lines.length && hits.length < MAX_MATCHES; i += 1) {
|
|
293
296
|
const line = lines[i] ?? "";
|
|
294
297
|
if (re.test(line)) {
|
|
295
298
|
const shown = line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS)}\u2026` : line;
|
|
@@ -298,29 +301,55 @@ var grepTool = {
|
|
|
298
301
|
}
|
|
299
302
|
return hits;
|
|
300
303
|
};
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
outer: for (let i = 0; i < files.length; i += WALK_CONCURRENCY) {
|
|
305
|
-
if (ctx.abortSignal?.aborted) {
|
|
306
|
-
interrupted = true;
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
const chunk = await Promise.all(
|
|
310
|
-
files.slice(i, i + WALK_CONCURRENCY).map((file) => scanFile(file))
|
|
304
|
+
const scanBatch = async (batch) => {
|
|
305
|
+
const files = batch.filter(
|
|
306
|
+
(f) => !isSensitivePath(f.abs) && (f.resolved === void 0 || !isSensitivePath(f.resolved)) && (!args.glob || matchesGlob(args.glob, f.rel))
|
|
311
307
|
);
|
|
312
|
-
for (
|
|
313
|
-
if (
|
|
314
|
-
|
|
308
|
+
outer: for (let i = 0; i < files.length; i += WALK_CONCURRENCY) {
|
|
309
|
+
if (ctx.abortSignal?.aborted) {
|
|
310
|
+
interrupted = true;
|
|
311
|
+
return false;
|
|
315
312
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
313
|
+
const chunk = await Promise.all(
|
|
314
|
+
files.slice(i, i + WALK_CONCURRENCY).map((file) => scanFile(file))
|
|
315
|
+
);
|
|
316
|
+
for (const hits of chunk) {
|
|
317
|
+
for (const hit of hits ?? []) {
|
|
318
|
+
out.push(hit);
|
|
319
|
+
if (out.length >= MAX_MATCHES) {
|
|
320
|
+
truncated = true;
|
|
321
|
+
break outer;
|
|
322
|
+
}
|
|
321
323
|
}
|
|
322
324
|
}
|
|
323
325
|
}
|
|
326
|
+
return !truncated && !ctx.abortSignal?.aborted;
|
|
327
|
+
};
|
|
328
|
+
try {
|
|
329
|
+
const st = await stat2(root);
|
|
330
|
+
if (st.isFile()) {
|
|
331
|
+
const resolved = await realpath(root).catch(() => null);
|
|
332
|
+
if (isSensitivePath(root) || resolved !== null && isSensitivePath(resolved)) {
|
|
333
|
+
return {
|
|
334
|
+
content: `Refused to grep sensitive file: ${args.path ?? "."}`,
|
|
335
|
+
isError: true
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
await scanBatch([{ abs: root, rel: path3.basename(root), mtimeMs: st.mtimeMs }]);
|
|
339
|
+
} else {
|
|
340
|
+
const walked = await walkFiles(root, void 0, {
|
|
341
|
+
ignore: createIgnorePredicate(ctx.cwd),
|
|
342
|
+
withMtime: false,
|
|
343
|
+
signal: ctx.abortSignal,
|
|
344
|
+
symlinkBoundary: ctx.cwd,
|
|
345
|
+
nestedIgnore: true,
|
|
346
|
+
collect: false,
|
|
347
|
+
onBatch: scanBatch
|
|
348
|
+
});
|
|
349
|
+
interrupted = walked.aborted;
|
|
350
|
+
}
|
|
351
|
+
} catch {
|
|
352
|
+
return { content: `Path not found: ${args.path ?? "."}`, isError: true };
|
|
324
353
|
}
|
|
325
354
|
const skippedNote = skippedLarge > 0 ? `(skipped ${skippedLarge} file(s) larger than 5MB)` : null;
|
|
326
355
|
if (out.length === 0) {
|
|
@@ -583,38 +612,82 @@ var codeOutlineTool = {
|
|
|
583
612
|
// src/tools/fs/read.ts
|
|
584
613
|
import { open, realpath as realpath3, stat as stat4 } from "fs/promises";
|
|
585
614
|
import path5 from "path";
|
|
615
|
+
import { StringDecoder } from "string_decoder";
|
|
586
616
|
import { z as z5 } from "zod";
|
|
587
617
|
var DEFAULT_LIMIT = 2e3;
|
|
588
618
|
var MAX_CHARS = 100 * 1024;
|
|
589
|
-
var MAX_READ_BYTES = 1024 * 1024;
|
|
590
619
|
var BINARY_PROBE_BYTES2 = 8192;
|
|
591
|
-
|
|
592
|
-
const len = buf.length - buf.length % 2;
|
|
593
|
-
const swapped = Buffer.allocUnsafe(len);
|
|
594
|
-
for (let k = 0; k < len; k += 2) {
|
|
595
|
-
swapped[k] = buf[k + 1] ?? 0;
|
|
596
|
-
swapped[k + 1] = buf[k] ?? 0;
|
|
597
|
-
}
|
|
598
|
-
return swapped.toString("utf16le");
|
|
599
|
-
}
|
|
600
|
-
function decodeTextBuffer(buf) {
|
|
601
|
-
if (buf.length >= 2 && buf[0] === 255 && buf[1] === 254) {
|
|
602
|
-
return { text: buf.subarray(2).toString("utf16le") };
|
|
603
|
-
}
|
|
604
|
-
if (buf.length >= 2 && buf[0] === 254 && buf[1] === 255) {
|
|
605
|
-
return { text: decodeUtf16be(buf.subarray(2)) };
|
|
606
|
-
}
|
|
607
|
-
if (buf.subarray(0, BINARY_PROBE_BYTES2).includes(0)) {
|
|
608
|
-
return { binary: true };
|
|
609
|
-
}
|
|
610
|
-
return { text: buf.toString("utf8") };
|
|
611
|
-
}
|
|
620
|
+
var READ_CHUNK_BYTES = 64 * 1024;
|
|
612
621
|
var schema5 = z5.object({
|
|
613
622
|
path: z5.string().describe("File path, absolute or relative to the working directory"),
|
|
614
623
|
offset: z5.number().int().positive().optional().describe("1-based line number to start reading from"),
|
|
615
624
|
limit: z5.number().int().positive().optional().describe("Maximum number of lines to read (default 2000)")
|
|
616
625
|
});
|
|
617
626
|
var READ_CACHE_MAX_ENTRIES = 200;
|
|
627
|
+
async function readWindow(handle, size, start, limit) {
|
|
628
|
+
const probe = Buffer.alloc(Math.min(size, BINARY_PROBE_BYTES2));
|
|
629
|
+
const { bytesRead: probeBytes } = await handle.read(probe, 0, probe.length, 0);
|
|
630
|
+
const head = probe.subarray(0, probeBytes);
|
|
631
|
+
const utf16le = head[0] === 255 && head[1] === 254;
|
|
632
|
+
const utf16be = head[0] === 254 && head[1] === 255;
|
|
633
|
+
if (!utf16le && !utf16be && head.includes(0)) return { binary: true };
|
|
634
|
+
const decoder = new StringDecoder(utf16le || utf16be ? "utf16le" : "utf8");
|
|
635
|
+
const requestedEnd = start - 1 + limit;
|
|
636
|
+
const result = {
|
|
637
|
+
totalLines: 1,
|
|
638
|
+
out: [],
|
|
639
|
+
lastShown: start - 1,
|
|
640
|
+
truncatedBySize: false
|
|
641
|
+
};
|
|
642
|
+
let lineNumber = 1;
|
|
643
|
+
let outputSize = 0;
|
|
644
|
+
let pending = "";
|
|
645
|
+
let beRemainder;
|
|
646
|
+
const showLine = (raw) => {
|
|
647
|
+
if (lineNumber >= start && lineNumber <= requestedEnd && !result.truncatedBySize) {
|
|
648
|
+
const numbered = `${lineNumber} ${raw.replace(/\r$/, "")}`;
|
|
649
|
+
if (outputSize + numbered.length > MAX_CHARS) {
|
|
650
|
+
result.truncatedBySize = true;
|
|
651
|
+
} else {
|
|
652
|
+
result.out.push(numbered);
|
|
653
|
+
outputSize += numbered.length;
|
|
654
|
+
result.lastShown = lineNumber;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
const accept = (text) => {
|
|
659
|
+
const parts = `${pending}${text}`.split("\n");
|
|
660
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
661
|
+
showLine(parts[i] ?? "");
|
|
662
|
+
lineNumber += 1;
|
|
663
|
+
}
|
|
664
|
+
pending = parts[parts.length - 1] ?? "";
|
|
665
|
+
};
|
|
666
|
+
let position = utf16le || utf16be ? 2 : 0;
|
|
667
|
+
while (position < size) {
|
|
668
|
+
const length = Math.min(READ_CHUNK_BYTES, size - position);
|
|
669
|
+
const { bytesRead, buffer } = await handle.read(Buffer.alloc(length), 0, length, position);
|
|
670
|
+
if (bytesRead === 0) break;
|
|
671
|
+
position += bytesRead;
|
|
672
|
+
let chunk = buffer.subarray(0, bytesRead);
|
|
673
|
+
if (utf16be) {
|
|
674
|
+
const combined = beRemainder === void 0 ? chunk : Buffer.concat([Buffer.from([beRemainder]), chunk]);
|
|
675
|
+
const pairedLength = combined.length - combined.length % 2;
|
|
676
|
+
const swapped = Buffer.allocUnsafe(pairedLength);
|
|
677
|
+
for (let i = 0; i < pairedLength; i += 2) {
|
|
678
|
+
swapped[i] = combined[i + 1] ?? 0;
|
|
679
|
+
swapped[i + 1] = combined[i] ?? 0;
|
|
680
|
+
}
|
|
681
|
+
beRemainder = pairedLength < combined.length ? combined[combined.length - 1] : void 0;
|
|
682
|
+
chunk = swapped;
|
|
683
|
+
}
|
|
684
|
+
accept(decoder.write(chunk));
|
|
685
|
+
}
|
|
686
|
+
accept(decoder.end());
|
|
687
|
+
showLine(pending);
|
|
688
|
+
result.totalLines = lineNumber;
|
|
689
|
+
return result;
|
|
690
|
+
}
|
|
618
691
|
function createReadFileTool() {
|
|
619
692
|
const cache = /* @__PURE__ */ new Map();
|
|
620
693
|
return {
|
|
@@ -648,55 +721,33 @@ function createReadFileTool() {
|
|
|
648
721
|
};
|
|
649
722
|
}
|
|
650
723
|
}
|
|
651
|
-
const bytesToRead = Math.min(st.size, MAX_READ_BYTES);
|
|
652
|
-
const fileCapped = st.size > MAX_READ_BYTES;
|
|
653
724
|
const handle = await open(filePath, "r").catch(() => null);
|
|
654
725
|
if (!handle) {
|
|
655
726
|
return { content: `Failed to read ${args.path}`, isError: true };
|
|
656
727
|
}
|
|
657
|
-
let
|
|
728
|
+
let decoded;
|
|
658
729
|
try {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
bytesToRead,
|
|
663
|
-
0
|
|
664
|
-
);
|
|
665
|
-
buf = buffer.subarray(0, bytesRead);
|
|
730
|
+
decoded = await readWindow(handle, st.size, start, limit);
|
|
731
|
+
} catch {
|
|
732
|
+
return { content: `Failed to read ${args.path}`, isError: true };
|
|
666
733
|
} finally {
|
|
667
734
|
await handle.close().catch(() => {
|
|
668
735
|
});
|
|
669
736
|
}
|
|
670
|
-
const decoded = decodeTextBuffer(buf);
|
|
671
737
|
if ("binary" in decoded) {
|
|
672
738
|
return { content: `Cannot read binary file: ${args.path}`, isError: true };
|
|
673
739
|
}
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
return { content: `(offset ${start} beyond end of file; file has ${totalDesc} lines)` };
|
|
740
|
+
const { totalLines, out, lastShown, truncatedBySize } = decoded;
|
|
741
|
+
if (start > totalLines) {
|
|
742
|
+
return { content: `(offset ${start} beyond end of file; file has ${totalLines} lines)` };
|
|
678
743
|
}
|
|
679
|
-
const end = Math.min(
|
|
680
|
-
const out = [];
|
|
681
|
-
let size = 0;
|
|
682
|
-
let truncatedBySize = false;
|
|
683
|
-
for (let i = start - 1; i < end; i++) {
|
|
684
|
-
const line = (lines[i] ?? "").replace(/\r$/, "");
|
|
685
|
-
const numbered = `${i + 1} ${line}`;
|
|
686
|
-
if (size + numbered.length > MAX_CHARS) {
|
|
687
|
-
truncatedBySize = true;
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
out.push(numbered);
|
|
691
|
-
size += numbered.length;
|
|
692
|
-
}
|
|
693
|
-
const lastShown = start - 1 + out.length;
|
|
744
|
+
const end = Math.min(totalLines, start - 1 + limit);
|
|
694
745
|
if (truncatedBySize) {
|
|
695
746
|
out.push(
|
|
696
|
-
`... (truncated: output exceeds 100KB, showing lines ${start}-${lastShown} of ${
|
|
747
|
+
`... (truncated: output exceeds 100KB, showing lines ${start}-${lastShown} of ${totalLines})`
|
|
697
748
|
);
|
|
698
|
-
} else if (end <
|
|
699
|
-
out.push(`... (truncated: showing lines ${start}-${end} of ${
|
|
749
|
+
} else if (end < totalLines) {
|
|
750
|
+
out.push(`... (truncated: showing lines ${start}-${end} of ${totalLines})`);
|
|
700
751
|
}
|
|
701
752
|
if (out.length > 0) {
|
|
702
753
|
if (cache.size >= READ_CACHE_MAX_ENTRIES) cache.clear();
|
|
@@ -704,7 +755,7 @@ function createReadFileTool() {
|
|
|
704
755
|
cache.set(cacheKey, {
|
|
705
756
|
mtimeMs: st.mtimeMs,
|
|
706
757
|
size: st.size,
|
|
707
|
-
totalLines
|
|
758
|
+
totalLines,
|
|
708
759
|
// Extend the remembered range when the new read overlaps or abuts
|
|
709
760
|
// the previous one; disjoint ranges replace it.
|
|
710
761
|
start: prev && start <= prev.end + 1 ? Math.min(prev.start, start) : start,
|
|
@@ -834,6 +885,13 @@ var writeFileTool = {
|
|
|
834
885
|
};
|
|
835
886
|
}
|
|
836
887
|
const snapshot = await captureSnapshot(filePath, "write_file", ctx.snapshotContext);
|
|
888
|
+
if (snapshot.existed && snapshot.content === null) {
|
|
889
|
+
const reason = snapshot.contentTooLarge ? "existing content is over the 5MB snapshot limit" : "existing content could not be read";
|
|
890
|
+
return {
|
|
891
|
+
content: `Refused to overwrite ${args.path}: ${reason}, and no restorable snapshot is available`,
|
|
892
|
+
isError: true
|
|
893
|
+
};
|
|
894
|
+
}
|
|
837
895
|
try {
|
|
838
896
|
await mkdir(path7.dirname(filePath), { recursive: true });
|
|
839
897
|
await writeFile2(filePath, args.content, "utf8");
|
|
@@ -1155,6 +1213,7 @@ function debugStreamLog(event, data) {
|
|
|
1155
1213
|
|
|
1156
1214
|
// src/tools/web/search.ts
|
|
1157
1215
|
var TIMEOUT_MS = 15e3;
|
|
1216
|
+
var MAX_BODY_BYTES = 2 * 1024 * 1024;
|
|
1158
1217
|
var DEFAULT_MAX_RESULTS = 5;
|
|
1159
1218
|
var MAX_RESULTS_LIMIT = 10;
|
|
1160
1219
|
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
|
|
@@ -1277,6 +1336,26 @@ var SEARCH_SOURCES = [
|
|
|
1277
1336
|
parse: parseBingResults
|
|
1278
1337
|
}
|
|
1279
1338
|
];
|
|
1339
|
+
async function readLimitedBody(res) {
|
|
1340
|
+
if (!res.body) return "";
|
|
1341
|
+
const reader = res.body.getReader();
|
|
1342
|
+
const decoder = new TextDecoder();
|
|
1343
|
+
let bytes = 0;
|
|
1344
|
+
let text = "";
|
|
1345
|
+
try {
|
|
1346
|
+
while (bytes < MAX_BODY_BYTES) {
|
|
1347
|
+
const { done, value } = await reader.read();
|
|
1348
|
+
if (done) break;
|
|
1349
|
+
bytes += value.byteLength;
|
|
1350
|
+
text += decoder.decode(value, { stream: true });
|
|
1351
|
+
}
|
|
1352
|
+
text += decoder.decode();
|
|
1353
|
+
} finally {
|
|
1354
|
+
await reader.cancel().catch(() => {
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
return text;
|
|
1358
|
+
}
|
|
1280
1359
|
var webSearchTool = {
|
|
1281
1360
|
name: "web_search",
|
|
1282
1361
|
description: "Search the web and return a numbered list of results with title, URL and snippet. Uses DuckDuckGo with automatic fallback to DuckDuckGo Lite and Bing when a source fails or returns nothing. No API key required. Use web_fetch to read a result's full page.",
|
|
@@ -1316,7 +1395,7 @@ var webSearchTool = {
|
|
|
1316
1395
|
}
|
|
1317
1396
|
let html;
|
|
1318
1397
|
try {
|
|
1319
|
-
html = await res
|
|
1398
|
+
html = await readLimitedBody(res);
|
|
1320
1399
|
} catch (err) {
|
|
1321
1400
|
const message = err instanceof Error ? err.message : String(err);
|
|
1322
1401
|
failures.push(`${source.name}: failed to read search response: ${message}`);
|
|
@@ -1424,4 +1503,4 @@ export {
|
|
|
1424
1503
|
ToolRegistry,
|
|
1425
1504
|
createDefaultRegistry
|
|
1426
1505
|
};
|
|
1427
|
-
//# sourceMappingURL=chunk-
|
|
1506
|
+
//# sourceMappingURL=chunk-54KH4JSO.js.map
|