opencode-gbk-tools 0.1.11 → 0.1.13
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 +3 -0
- package/dist/opencode-tools/text_edit.js +93 -73
- package/dist/opencode-tools/text_read.js +240 -27
- package/dist/opencode-tools/text_write.js +36 -11
- package/dist/plugin/index.js +288 -93
- package/dist/plugins/opencode-gbk-tools.js +1 -0
- package/dist/release-manifest.json +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ npx opencode-gbk-tools uninstall
|
|
|
52
52
|
- 原编码
|
|
53
53
|
- BOM
|
|
54
54
|
- 换行风格
|
|
55
|
+
- 新文件在 `encoding=auto` 下不会自动默认成 `utf8`,需要显式指定 `encoding`
|
|
55
56
|
- 当前支持:
|
|
56
57
|
- `utf8`
|
|
57
58
|
- `utf8-bom`
|
|
@@ -194,6 +195,8 @@ A:不要。现在优先用 `mode="insertAfter"` 或 `mode="insertBefore"`,
|
|
|
194
195
|
|
|
195
196
|
| 版本 | 说明 |
|
|
196
197
|
|------|------|
|
|
198
|
+
| 0.1.13 | `text_write` 在新文件 + `encoding=auto` 下改为显式报错,不再静默默认 `utf8`;同时优化 `text_read` 流式读取与 `text_edit` 预览生成,减少重复读取 |
|
|
199
|
+
| 0.1.12 | 仅提升发布版本号,用于重新发布当前 `0.1.11` 的稳定内容 |
|
|
197
200
|
| 0.1.11 | `text_edit` / `gbk_edit` 新增 `insertAfter` / `insertBefore`,插入内容不再依赖 `oldString`;README 与全局提示同步改为“插入优先用 anchor/content” |
|
|
198
201
|
| 0.1.10 | 新增 `text_read` / `text_write` / `text_edit`,自动识别并保持原编码、BOM 与换行风格;同时通过 plugin 规则让所有 agents 默认优先使用 `text_*` |
|
|
199
202
|
| 0.1.9 | `gbk_edit` 修复精确匹配路径写入 CRLF 文件时换行风格变 mixed 的 bug |
|
|
@@ -16240,6 +16240,8 @@ tool.schema = external_exports;
|
|
|
16240
16240
|
|
|
16241
16241
|
// src/lib/text-file.ts
|
|
16242
16242
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
16243
|
+
import crypto2 from "crypto";
|
|
16244
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
16243
16245
|
import fs3 from "fs/promises";
|
|
16244
16246
|
import path3 from "path";
|
|
16245
16247
|
|
|
@@ -16360,6 +16362,7 @@ function countOccurrences(text, target) {
|
|
|
16360
16362
|
|
|
16361
16363
|
// src/lib/text-file.ts
|
|
16362
16364
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16365
|
+
var TEXT_DETECTION_SAMPLE_BYTES = 64 * 1024;
|
|
16363
16366
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16364
16367
|
function assertStringArgument2(value, name) {
|
|
16365
16368
|
if (typeof value !== "string") {
|
|
@@ -16380,6 +16383,21 @@ function resolveExplicitTextEncoding(value, fallback) {
|
|
|
16380
16383
|
const requested = value ?? "auto";
|
|
16381
16384
|
return requested === "auto" ? fallback : requested;
|
|
16382
16385
|
}
|
|
16386
|
+
function getBomPrefix(encoding, hasBom) {
|
|
16387
|
+
if (!hasBom) {
|
|
16388
|
+
return Buffer.alloc(0);
|
|
16389
|
+
}
|
|
16390
|
+
if (encoding === "utf8-bom") {
|
|
16391
|
+
return Buffer.from([239, 187, 191]);
|
|
16392
|
+
}
|
|
16393
|
+
if (encoding === "utf16le") {
|
|
16394
|
+
return Buffer.from([255, 254]);
|
|
16395
|
+
}
|
|
16396
|
+
if (encoding === "utf16be") {
|
|
16397
|
+
return Buffer.from([254, 255]);
|
|
16398
|
+
}
|
|
16399
|
+
return Buffer.alloc(0);
|
|
16400
|
+
}
|
|
16383
16401
|
function isSupportedEncoding(value) {
|
|
16384
16402
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
16385
16403
|
}
|
|
@@ -16391,23 +16409,23 @@ function assertTextEncodingSupported(encoding) {
|
|
|
16391
16409
|
function decodeUtf16be(buffer) {
|
|
16392
16410
|
return import_iconv_lite2.default.decode(buffer, "utf16be");
|
|
16393
16411
|
}
|
|
16394
|
-
function
|
|
16395
|
-
if (encoding === "utf8") {
|
|
16412
|
+
function encodeTextBody(content, encoding) {
|
|
16413
|
+
if (encoding === "utf8" || encoding === "utf8-bom") {
|
|
16396
16414
|
return Buffer.from(content, "utf8");
|
|
16397
16415
|
}
|
|
16398
|
-
if (encoding === "utf8-bom") {
|
|
16399
|
-
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(content, "utf8")]);
|
|
16400
|
-
}
|
|
16401
16416
|
if (encoding === "utf16le") {
|
|
16402
|
-
|
|
16403
|
-
return hasBom ? Buffer.concat([Buffer.from([255, 254]), body]) : body;
|
|
16417
|
+
return Buffer.from(content, "utf16le");
|
|
16404
16418
|
}
|
|
16405
16419
|
if (encoding === "utf16be") {
|
|
16406
|
-
|
|
16407
|
-
return hasBom ? Buffer.concat([Buffer.from([254, 255]), body]) : body;
|
|
16420
|
+
return import_iconv_lite2.default.encode(content, "utf16be");
|
|
16408
16421
|
}
|
|
16409
16422
|
return import_iconv_lite2.default.encode(content, encoding);
|
|
16410
16423
|
}
|
|
16424
|
+
function encodeText(content, encoding, hasBom = encoding === "utf8-bom") {
|
|
16425
|
+
const bom = getBomPrefix(encoding, hasBom);
|
|
16426
|
+
const body = encodeTextBody(content, encoding);
|
|
16427
|
+
return bom.length === 0 ? body : Buffer.concat([bom, body]);
|
|
16428
|
+
}
|
|
16411
16429
|
function decodeText(buffer, encoding) {
|
|
16412
16430
|
if (encoding === "utf8") {
|
|
16413
16431
|
return buffer.toString("utf8");
|
|
@@ -16683,6 +16701,30 @@ async function resolveReadableTextFile(input) {
|
|
|
16683
16701
|
}
|
|
16684
16702
|
return { filePath: candidatePath, stat };
|
|
16685
16703
|
}
|
|
16704
|
+
async function readDetectionBuffer(filePath, sampleSize = TEXT_DETECTION_SAMPLE_BYTES) {
|
|
16705
|
+
const handle = await fs3.open(filePath, "r");
|
|
16706
|
+
try {
|
|
16707
|
+
const buffer = Buffer.alloc(sampleSize);
|
|
16708
|
+
const { bytesRead } = await handle.read(buffer, 0, sampleSize, 0);
|
|
16709
|
+
return buffer.subarray(0, bytesRead);
|
|
16710
|
+
} finally {
|
|
16711
|
+
await handle.close();
|
|
16712
|
+
}
|
|
16713
|
+
}
|
|
16714
|
+
async function detectReadableTextFile(input) {
|
|
16715
|
+
const resolved = await resolveReadableTextFile(input);
|
|
16716
|
+
const sample = await readDetectionBuffer(resolved.filePath);
|
|
16717
|
+
assertLikelyTextBuffer(sample);
|
|
16718
|
+
const detected = detectTextEncodingFromBuffer(sample, input.encoding ?? "auto");
|
|
16719
|
+
return {
|
|
16720
|
+
...resolved,
|
|
16721
|
+
encoding: detected.detectedEncoding,
|
|
16722
|
+
requestedEncoding: detected.requestedEncoding,
|
|
16723
|
+
detectedEncoding: detected.detectedEncoding,
|
|
16724
|
+
confidence: detected.confidence,
|
|
16725
|
+
hasBom: detected.hasBom
|
|
16726
|
+
};
|
|
16727
|
+
}
|
|
16686
16728
|
async function readWholeTextFile(input) {
|
|
16687
16729
|
const resolved = await resolveReadableTextFile(input);
|
|
16688
16730
|
try {
|
|
@@ -16819,70 +16861,84 @@ async function replaceTextFileText(input) {
|
|
|
16819
16861
|
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
16820
16862
|
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
16821
16863
|
};
|
|
16822
|
-
const loaded = await readWholeTextFile({
|
|
16823
|
-
filePath: input.filePath,
|
|
16824
|
-
encoding: input.encoding ?? "auto",
|
|
16825
|
-
allowExternal: input.allowExternal,
|
|
16826
|
-
context: input.context
|
|
16827
|
-
});
|
|
16828
16864
|
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
16865
|
+
const loaded2 = await readWholeTextFile({
|
|
16866
|
+
filePath: input.filePath,
|
|
16867
|
+
encoding: input.encoding ?? "auto",
|
|
16868
|
+
allowExternal: input.allowExternal,
|
|
16869
|
+
context: input.context
|
|
16870
|
+
});
|
|
16829
16871
|
assertInsertArguments(normalizedInput);
|
|
16830
16872
|
const occurrence = normalizeOptionalPositiveInteger(normalizedInput.occurrence, "occurrence") ?? 1;
|
|
16831
16873
|
const insertResult = buildInsertOutput(
|
|
16832
|
-
|
|
16874
|
+
loaded2.content,
|
|
16833
16875
|
mode,
|
|
16834
16876
|
normalizedInput.anchor,
|
|
16835
16877
|
normalizedInput.content,
|
|
16836
16878
|
occurrence,
|
|
16837
16879
|
resolveInsertIfExists(normalizedInput.ifExists),
|
|
16838
|
-
|
|
16880
|
+
loaded2.newlineStyle
|
|
16839
16881
|
);
|
|
16840
16882
|
if (insertResult.skipped) {
|
|
16841
16883
|
return {
|
|
16842
16884
|
mode,
|
|
16843
|
-
filePath:
|
|
16844
|
-
encoding:
|
|
16845
|
-
requestedEncoding:
|
|
16846
|
-
detectedEncoding:
|
|
16847
|
-
confidence:
|
|
16848
|
-
hasBom:
|
|
16885
|
+
filePath: loaded2.filePath,
|
|
16886
|
+
encoding: loaded2.encoding,
|
|
16887
|
+
requestedEncoding: loaded2.requestedEncoding,
|
|
16888
|
+
detectedEncoding: loaded2.detectedEncoding,
|
|
16889
|
+
confidence: loaded2.confidence,
|
|
16890
|
+
hasBom: loaded2.hasBom,
|
|
16849
16891
|
anchor: insertResult.anchor,
|
|
16850
16892
|
occurrence: insertResult.occurrence,
|
|
16851
16893
|
anchorMatches: insertResult.anchorMatches,
|
|
16852
16894
|
inserted: false,
|
|
16853
16895
|
skipped: true,
|
|
16854
|
-
bytesRead:
|
|
16896
|
+
bytesRead: loaded2.bytesRead,
|
|
16855
16897
|
bytesWritten: 0,
|
|
16856
|
-
newlineStyle:
|
|
16898
|
+
newlineStyle: loaded2.newlineStyle,
|
|
16899
|
+
diffPreview: buildLineDiffPreview(loaded2.filePath, loaded2.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
16857
16900
|
};
|
|
16858
16901
|
}
|
|
16859
|
-
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ?
|
|
16860
|
-
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" :
|
|
16902
|
+
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ? loaded2.encoding : resolveExplicitTextEncoding(normalizedInput.encoding, loaded2.encoding);
|
|
16903
|
+
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" : loaded2.hasBom;
|
|
16861
16904
|
ensureLossless(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
16862
16905
|
const buffer2 = encodeText(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
16863
|
-
await fs3.writeFile(
|
|
16906
|
+
await fs3.writeFile(loaded2.filePath, buffer2);
|
|
16864
16907
|
return {
|
|
16865
16908
|
mode,
|
|
16866
|
-
filePath:
|
|
16909
|
+
filePath: loaded2.filePath,
|
|
16867
16910
|
encoding: targetEncoding2,
|
|
16868
|
-
requestedEncoding:
|
|
16869
|
-
detectedEncoding:
|
|
16870
|
-
confidence:
|
|
16911
|
+
requestedEncoding: loaded2.requestedEncoding,
|
|
16912
|
+
detectedEncoding: loaded2.detectedEncoding,
|
|
16913
|
+
confidence: loaded2.confidence,
|
|
16871
16914
|
hasBom: targetHasBom2,
|
|
16872
16915
|
anchor: insertResult.anchor,
|
|
16873
16916
|
occurrence: insertResult.occurrence,
|
|
16874
16917
|
anchorMatches: insertResult.anchorMatches,
|
|
16875
16918
|
inserted: true,
|
|
16876
16919
|
skipped: false,
|
|
16877
|
-
bytesRead:
|
|
16920
|
+
bytesRead: loaded2.bytesRead,
|
|
16878
16921
|
bytesWritten: buffer2.byteLength,
|
|
16879
|
-
newlineStyle: detectNewlineStyle(insertResult.outputText)
|
|
16922
|
+
newlineStyle: detectNewlineStyle(insertResult.outputText),
|
|
16923
|
+
diffPreview: buildLineDiffPreview(loaded2.filePath, targetEncoding2, insertResult.previewBefore, insertResult.previewAfter)
|
|
16880
16924
|
};
|
|
16881
16925
|
}
|
|
16882
16926
|
assertReplaceArguments(input);
|
|
16883
16927
|
if (input.oldString.length === 0) {
|
|
16884
16928
|
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16885
16929
|
}
|
|
16930
|
+
const resolved = await detectReadableTextFile({
|
|
16931
|
+
filePath: input.filePath,
|
|
16932
|
+
encoding: input.encoding ?? "auto",
|
|
16933
|
+
allowExternal: input.allowExternal,
|
|
16934
|
+
context: input.context
|
|
16935
|
+
});
|
|
16936
|
+
const loaded = await readWholeTextFile({
|
|
16937
|
+
filePath: input.filePath,
|
|
16938
|
+
encoding: input.encoding ?? "auto",
|
|
16939
|
+
allowExternal: input.allowExternal,
|
|
16940
|
+
context: input.context
|
|
16941
|
+
});
|
|
16886
16942
|
const scope = resolveEditScope(loaded.content, normalizedInput);
|
|
16887
16943
|
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
16888
16944
|
const preserveEncoding = normalizedInput.preserveEncoding ?? true;
|
|
@@ -16917,43 +16973,8 @@ async function replaceTextFileText(input) {
|
|
|
16917
16973
|
occurrencesBefore,
|
|
16918
16974
|
bytesRead: loaded.bytesRead,
|
|
16919
16975
|
bytesWritten: buffer.byteLength,
|
|
16920
|
-
newlineStyle: detectNewlineStyle(outputText)
|
|
16921
|
-
|
|
16922
|
-
}
|
|
16923
|
-
async function createTextDiffPreview(input) {
|
|
16924
|
-
const mode = resolveEditMode(input.mode);
|
|
16925
|
-
const loaded = await readWholeTextFile({
|
|
16926
|
-
filePath: input.filePath,
|
|
16927
|
-
encoding: input.encoding ?? "auto",
|
|
16928
|
-
allowExternal: input.allowExternal,
|
|
16929
|
-
context: input.context
|
|
16930
|
-
});
|
|
16931
|
-
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
16932
|
-
assertInsertArguments(input);
|
|
16933
|
-
const occurrence = normalizeOptionalPositiveInteger(input.occurrence, "occurrence") ?? 1;
|
|
16934
|
-
const insertResult = buildInsertOutput(
|
|
16935
|
-
loaded.content,
|
|
16936
|
-
mode,
|
|
16937
|
-
input.anchor,
|
|
16938
|
-
input.content,
|
|
16939
|
-
occurrence,
|
|
16940
|
-
resolveInsertIfExists(input.ifExists),
|
|
16941
|
-
loaded.newlineStyle
|
|
16942
|
-
);
|
|
16943
|
-
return {
|
|
16944
|
-
filePath: loaded.filePath,
|
|
16945
|
-
encoding: loaded.encoding,
|
|
16946
|
-
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
16947
|
-
};
|
|
16948
|
-
}
|
|
16949
|
-
assertReplaceArguments(input);
|
|
16950
|
-
const scope = resolveEditScope(loaded.content, input);
|
|
16951
|
-
const alignedNewString = alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
16952
|
-
const replaced = input.replaceAll ?? false ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
16953
|
-
return {
|
|
16954
|
-
filePath: loaded.filePath,
|
|
16955
|
-
encoding: loaded.encoding,
|
|
16956
|
-
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, scope.selectedText, replaced)
|
|
16976
|
+
newlineStyle: detectNewlineStyle(outputText),
|
|
16977
|
+
diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
|
|
16957
16978
|
};
|
|
16958
16979
|
}
|
|
16959
16980
|
|
|
@@ -16986,7 +17007,6 @@ var text_edit_default = tool({
|
|
|
16986
17007
|
},
|
|
16987
17008
|
async execute(args, context) {
|
|
16988
17009
|
const result = await replaceTextFileText({ ...args, context });
|
|
16989
|
-
const preview = await createTextDiffPreview({ ...args, context });
|
|
16990
17010
|
const title = result.mode === "replace" ? `\u6587\u672C\u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}` : result.inserted ? `\u6587\u672C\u63D2\u5165 ${result.encoding.toUpperCase()} #${result.occurrence}` : `\u6587\u672C\u8DF3\u8FC7 ${result.encoding.toUpperCase()} #${result.occurrence}`;
|
|
16991
17011
|
context.metadata({
|
|
16992
17012
|
title,
|
|
@@ -17005,7 +17025,7 @@ var text_edit_default = tool({
|
|
|
17005
17025
|
inserted: result.mode === "replace" ? void 0 : result.inserted,
|
|
17006
17026
|
skipped: result.mode === "replace" ? void 0 : result.skipped,
|
|
17007
17027
|
newlineStyle: result.newlineStyle,
|
|
17008
|
-
diffPreview:
|
|
17028
|
+
diffPreview: result.diffPreview ?? ""
|
|
17009
17029
|
}
|
|
17010
17030
|
});
|
|
17011
17031
|
return JSON.stringify(result, null, 2);
|
|
@@ -16240,6 +16240,8 @@ tool.schema = external_exports;
|
|
|
16240
16240
|
|
|
16241
16241
|
// src/lib/text-file.ts
|
|
16242
16242
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
16243
|
+
import crypto2 from "crypto";
|
|
16244
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
16243
16245
|
import fs3 from "fs/promises";
|
|
16244
16246
|
import path3 from "path";
|
|
16245
16247
|
|
|
@@ -16361,7 +16363,35 @@ function detectNewlineStyle(text) {
|
|
|
16361
16363
|
|
|
16362
16364
|
// src/lib/text-file.ts
|
|
16363
16365
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16366
|
+
var TEXT_DETECTION_SAMPLE_BYTES = 64 * 1024;
|
|
16364
16367
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16368
|
+
function finalizeNewlineStyle(crlfCount, lfCount) {
|
|
16369
|
+
if (crlfCount > 0 && lfCount === 0) {
|
|
16370
|
+
return "crlf";
|
|
16371
|
+
}
|
|
16372
|
+
if (lfCount > 0 && crlfCount === 0) {
|
|
16373
|
+
return "lf";
|
|
16374
|
+
}
|
|
16375
|
+
if (crlfCount > 0 && lfCount > 0) {
|
|
16376
|
+
return "mixed";
|
|
16377
|
+
}
|
|
16378
|
+
return "none";
|
|
16379
|
+
}
|
|
16380
|
+
function getBomPrefix(encoding, hasBom) {
|
|
16381
|
+
if (!hasBom) {
|
|
16382
|
+
return Buffer.alloc(0);
|
|
16383
|
+
}
|
|
16384
|
+
if (encoding === "utf8-bom") {
|
|
16385
|
+
return Buffer.from([239, 187, 191]);
|
|
16386
|
+
}
|
|
16387
|
+
if (encoding === "utf16le") {
|
|
16388
|
+
return Buffer.from([255, 254]);
|
|
16389
|
+
}
|
|
16390
|
+
if (encoding === "utf16be") {
|
|
16391
|
+
return Buffer.from([254, 255]);
|
|
16392
|
+
}
|
|
16393
|
+
return Buffer.alloc(0);
|
|
16394
|
+
}
|
|
16365
16395
|
function isSupportedEncoding(value) {
|
|
16366
16396
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
16367
16397
|
}
|
|
@@ -16373,23 +16403,23 @@ function assertTextEncodingSupported(encoding) {
|
|
|
16373
16403
|
function decodeUtf16be(buffer) {
|
|
16374
16404
|
return import_iconv_lite2.default.decode(buffer, "utf16be");
|
|
16375
16405
|
}
|
|
16376
|
-
function
|
|
16377
|
-
if (encoding === "utf8") {
|
|
16406
|
+
function encodeTextBody(content, encoding) {
|
|
16407
|
+
if (encoding === "utf8" || encoding === "utf8-bom") {
|
|
16378
16408
|
return Buffer.from(content, "utf8");
|
|
16379
16409
|
}
|
|
16380
|
-
if (encoding === "utf8-bom") {
|
|
16381
|
-
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(content, "utf8")]);
|
|
16382
|
-
}
|
|
16383
16410
|
if (encoding === "utf16le") {
|
|
16384
|
-
|
|
16385
|
-
return hasBom ? Buffer.concat([Buffer.from([255, 254]), body]) : body;
|
|
16411
|
+
return Buffer.from(content, "utf16le");
|
|
16386
16412
|
}
|
|
16387
16413
|
if (encoding === "utf16be") {
|
|
16388
|
-
|
|
16389
|
-
return hasBom ? Buffer.concat([Buffer.from([254, 255]), body]) : body;
|
|
16414
|
+
return import_iconv_lite2.default.encode(content, "utf16be");
|
|
16390
16415
|
}
|
|
16391
16416
|
return import_iconv_lite2.default.encode(content, encoding);
|
|
16392
16417
|
}
|
|
16418
|
+
function encodeText(content, encoding, hasBom = encoding === "utf8-bom") {
|
|
16419
|
+
const bom = getBomPrefix(encoding, hasBom);
|
|
16420
|
+
const body = encodeTextBody(content, encoding);
|
|
16421
|
+
return bom.length === 0 ? body : Buffer.concat([bom, body]);
|
|
16422
|
+
}
|
|
16393
16423
|
function decodeText(buffer, encoding) {
|
|
16394
16424
|
if (encoding === "utf8") {
|
|
16395
16425
|
return buffer.toString("utf8");
|
|
@@ -16543,6 +16573,169 @@ async function resolveReadableTextFile(input) {
|
|
|
16543
16573
|
}
|
|
16544
16574
|
return { filePath: candidatePath, stat };
|
|
16545
16575
|
}
|
|
16576
|
+
async function readDetectionBuffer(filePath, sampleSize = TEXT_DETECTION_SAMPLE_BYTES) {
|
|
16577
|
+
const handle = await fs3.open(filePath, "r");
|
|
16578
|
+
try {
|
|
16579
|
+
const buffer = Buffer.alloc(sampleSize);
|
|
16580
|
+
const { bytesRead } = await handle.read(buffer, 0, sampleSize, 0);
|
|
16581
|
+
return buffer.subarray(0, bytesRead);
|
|
16582
|
+
} finally {
|
|
16583
|
+
await handle.close();
|
|
16584
|
+
}
|
|
16585
|
+
}
|
|
16586
|
+
async function detectReadableTextFile(input) {
|
|
16587
|
+
const resolved = await resolveReadableTextFile(input);
|
|
16588
|
+
const sample = await readDetectionBuffer(resolved.filePath);
|
|
16589
|
+
assertLikelyTextBuffer(sample);
|
|
16590
|
+
const detected = detectTextEncodingFromBuffer(sample, input.encoding ?? "auto");
|
|
16591
|
+
return {
|
|
16592
|
+
...resolved,
|
|
16593
|
+
encoding: detected.detectedEncoding,
|
|
16594
|
+
requestedEncoding: detected.requestedEncoding,
|
|
16595
|
+
detectedEncoding: detected.detectedEncoding,
|
|
16596
|
+
confidence: detected.confidence,
|
|
16597
|
+
hasBom: detected.hasBom
|
|
16598
|
+
};
|
|
16599
|
+
}
|
|
16600
|
+
function createLineCollector(offset, limit) {
|
|
16601
|
+
const lines = [];
|
|
16602
|
+
let pending = "";
|
|
16603
|
+
let totalLines = 0;
|
|
16604
|
+
let crlfCount = 0;
|
|
16605
|
+
let lfCount = 0;
|
|
16606
|
+
const emitLine = (line) => {
|
|
16607
|
+
totalLines += 1;
|
|
16608
|
+
if (totalLines >= offset && lines.length < limit) {
|
|
16609
|
+
lines.push(`${totalLines}: ${line}`);
|
|
16610
|
+
}
|
|
16611
|
+
};
|
|
16612
|
+
return {
|
|
16613
|
+
push(text) {
|
|
16614
|
+
if (text.length === 0) {
|
|
16615
|
+
return;
|
|
16616
|
+
}
|
|
16617
|
+
const combined = pending + text;
|
|
16618
|
+
let start = 0;
|
|
16619
|
+
while (true) {
|
|
16620
|
+
const newlineIndex = combined.indexOf("\n", start);
|
|
16621
|
+
if (newlineIndex === -1) {
|
|
16622
|
+
break;
|
|
16623
|
+
}
|
|
16624
|
+
let line = combined.slice(start, newlineIndex);
|
|
16625
|
+
if (line.endsWith("\r")) {
|
|
16626
|
+
line = line.slice(0, -1);
|
|
16627
|
+
crlfCount += 1;
|
|
16628
|
+
} else {
|
|
16629
|
+
lfCount += 1;
|
|
16630
|
+
}
|
|
16631
|
+
emitLine(line);
|
|
16632
|
+
start = newlineIndex + 1;
|
|
16633
|
+
}
|
|
16634
|
+
pending = combined.slice(start);
|
|
16635
|
+
},
|
|
16636
|
+
finish() {
|
|
16637
|
+
emitLine(pending);
|
|
16638
|
+
return {
|
|
16639
|
+
startLine: offset,
|
|
16640
|
+
endLine: lines.length === 0 ? totalLines : offset + lines.length - 1,
|
|
16641
|
+
totalLines,
|
|
16642
|
+
content: lines.join("\n"),
|
|
16643
|
+
tail: false,
|
|
16644
|
+
truncated: (lines.length === 0 ? totalLines : offset + lines.length - 1) < totalLines,
|
|
16645
|
+
newlineStyle: finalizeNewlineStyle(crlfCount, lfCount)
|
|
16646
|
+
};
|
|
16647
|
+
}
|
|
16648
|
+
};
|
|
16649
|
+
}
|
|
16650
|
+
function createTailCollector(limit) {
|
|
16651
|
+
assertPositiveInteger(limit, "limit");
|
|
16652
|
+
const lines = [];
|
|
16653
|
+
let pending = "";
|
|
16654
|
+
let totalLines = 0;
|
|
16655
|
+
let crlfCount = 0;
|
|
16656
|
+
let lfCount = 0;
|
|
16657
|
+
const emitLine = (line) => {
|
|
16658
|
+
totalLines += 1;
|
|
16659
|
+
lines.push(line);
|
|
16660
|
+
if (lines.length > limit) {
|
|
16661
|
+
lines.shift();
|
|
16662
|
+
}
|
|
16663
|
+
};
|
|
16664
|
+
return {
|
|
16665
|
+
push(text) {
|
|
16666
|
+
if (text.length === 0) {
|
|
16667
|
+
return;
|
|
16668
|
+
}
|
|
16669
|
+
const combined = pending + text;
|
|
16670
|
+
let start = 0;
|
|
16671
|
+
while (true) {
|
|
16672
|
+
const newlineIndex = combined.indexOf("\n", start);
|
|
16673
|
+
if (newlineIndex === -1) {
|
|
16674
|
+
break;
|
|
16675
|
+
}
|
|
16676
|
+
let line = combined.slice(start, newlineIndex);
|
|
16677
|
+
if (line.endsWith("\r")) {
|
|
16678
|
+
line = line.slice(0, -1);
|
|
16679
|
+
crlfCount += 1;
|
|
16680
|
+
} else {
|
|
16681
|
+
lfCount += 1;
|
|
16682
|
+
}
|
|
16683
|
+
emitLine(line);
|
|
16684
|
+
start = newlineIndex + 1;
|
|
16685
|
+
}
|
|
16686
|
+
pending = combined.slice(start);
|
|
16687
|
+
},
|
|
16688
|
+
finish() {
|
|
16689
|
+
emitLine(pending);
|
|
16690
|
+
const startLine = Math.max(1, totalLines - lines.length + 1);
|
|
16691
|
+
return {
|
|
16692
|
+
startLine,
|
|
16693
|
+
endLine: totalLines,
|
|
16694
|
+
totalLines,
|
|
16695
|
+
content: lines.map((line, index) => `${startLine + index}: ${line}`).join("\n"),
|
|
16696
|
+
truncated: startLine > 1,
|
|
16697
|
+
tail: true,
|
|
16698
|
+
newlineStyle: finalizeNewlineStyle(crlfCount, lfCount)
|
|
16699
|
+
};
|
|
16700
|
+
}
|
|
16701
|
+
};
|
|
16702
|
+
}
|
|
16703
|
+
async function visitDecodedTextChunks(resolved, visitor) {
|
|
16704
|
+
const stream = createReadStream2(resolved.filePath);
|
|
16705
|
+
const bomLength = getBomPrefix(resolved.encoding, resolved.hasBom).length;
|
|
16706
|
+
const decoderEncoding = resolved.encoding === "utf8-bom" ? "utf8" : resolved.encoding;
|
|
16707
|
+
const decoder = import_iconv_lite2.default.getDecoder(decoderEncoding);
|
|
16708
|
+
let skippedBom = false;
|
|
16709
|
+
try {
|
|
16710
|
+
for await (const chunk of stream) {
|
|
16711
|
+
let buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
16712
|
+
if (!skippedBom && bomLength > 0) {
|
|
16713
|
+
buffer = buffer.subarray(Math.min(bomLength, buffer.length));
|
|
16714
|
+
skippedBom = true;
|
|
16715
|
+
} else {
|
|
16716
|
+
skippedBom = true;
|
|
16717
|
+
}
|
|
16718
|
+
if (buffer.length === 0) {
|
|
16719
|
+
continue;
|
|
16720
|
+
}
|
|
16721
|
+
const text = decoder.write(buffer);
|
|
16722
|
+
if (text.length > 0) {
|
|
16723
|
+
await visitor(text);
|
|
16724
|
+
}
|
|
16725
|
+
}
|
|
16726
|
+
const trailingText = decoder.end() ?? "";
|
|
16727
|
+
if (trailingText.length > 0) {
|
|
16728
|
+
await visitor(trailingText);
|
|
16729
|
+
}
|
|
16730
|
+
} catch (error45) {
|
|
16731
|
+
if (error45 instanceof Error && "code" in error45) {
|
|
16732
|
+
throw error45;
|
|
16733
|
+
}
|
|
16734
|
+
throw createTextError("GBK_IO_ERROR", `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${resolved.filePath}`, error45);
|
|
16735
|
+
} finally {
|
|
16736
|
+
stream.destroy();
|
|
16737
|
+
}
|
|
16738
|
+
}
|
|
16546
16739
|
async function readWholeTextFile(input) {
|
|
16547
16740
|
const resolved = await resolveReadableTextFile(input);
|
|
16548
16741
|
try {
|
|
@@ -16588,26 +16781,46 @@ async function readTextFile(input) {
|
|
|
16588
16781
|
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
16589
16782
|
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
16590
16783
|
const tail = input.tail ?? false;
|
|
16591
|
-
const
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
tail:
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
|
|
16784
|
+
const resolved = await detectReadableTextFile(input);
|
|
16785
|
+
if (resolved.stat.size < TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
|
|
16786
|
+
const loaded = await readWholeTextFile(input);
|
|
16787
|
+
const lineWindow2 = tail ? collectTailLines(loaded.content, limit) : {
|
|
16788
|
+
...splitLinesWithNumbers(loaded.content, offset, limit),
|
|
16789
|
+
tail: false,
|
|
16790
|
+
truncated: false
|
|
16791
|
+
};
|
|
16792
|
+
if (!tail) {
|
|
16793
|
+
lineWindow2.truncated = lineWindow2.endLine < lineWindow2.totalLines;
|
|
16794
|
+
}
|
|
16795
|
+
return {
|
|
16796
|
+
filePath: loaded.filePath,
|
|
16797
|
+
encoding: loaded.encoding,
|
|
16798
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
16799
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
16800
|
+
confidence: loaded.confidence,
|
|
16801
|
+
hasBom: loaded.hasBom,
|
|
16802
|
+
bytesRead: loaded.bytesRead,
|
|
16803
|
+
fileSize: loaded.fileSize,
|
|
16804
|
+
streamed: false,
|
|
16805
|
+
newlineStyle: loaded.newlineStyle,
|
|
16806
|
+
...lineWindow2
|
|
16807
|
+
};
|
|
16599
16808
|
}
|
|
16809
|
+
const collector = tail ? createTailCollector(limit) : createLineCollector(offset, limit);
|
|
16810
|
+
await visitDecodedTextChunks(resolved, (text) => {
|
|
16811
|
+
collector.push(text);
|
|
16812
|
+
});
|
|
16813
|
+
const lineWindow = collector.finish();
|
|
16600
16814
|
return {
|
|
16601
|
-
filePath:
|
|
16602
|
-
encoding:
|
|
16603
|
-
requestedEncoding:
|
|
16604
|
-
detectedEncoding:
|
|
16605
|
-
confidence:
|
|
16606
|
-
hasBom:
|
|
16607
|
-
bytesRead:
|
|
16608
|
-
fileSize:
|
|
16609
|
-
streamed:
|
|
16610
|
-
newlineStyle: loaded.newlineStyle,
|
|
16815
|
+
filePath: resolved.filePath,
|
|
16816
|
+
encoding: resolved.encoding,
|
|
16817
|
+
requestedEncoding: resolved.requestedEncoding,
|
|
16818
|
+
detectedEncoding: resolved.detectedEncoding,
|
|
16819
|
+
confidence: resolved.confidence,
|
|
16820
|
+
hasBom: resolved.hasBom,
|
|
16821
|
+
bytesRead: resolved.stat.size,
|
|
16822
|
+
fileSize: resolved.stat.size,
|
|
16823
|
+
streamed: true,
|
|
16611
16824
|
...lineWindow
|
|
16612
16825
|
};
|
|
16613
16826
|
}
|
|
@@ -16240,6 +16240,8 @@ tool.schema = external_exports;
|
|
|
16240
16240
|
|
|
16241
16241
|
// src/lib/text-file.ts
|
|
16242
16242
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
16243
|
+
import crypto2 from "crypto";
|
|
16244
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
16243
16245
|
import fs3 from "fs/promises";
|
|
16244
16246
|
import path3 from "path";
|
|
16245
16247
|
|
|
@@ -16326,12 +16328,32 @@ function detectNewlineStyle(text) {
|
|
|
16326
16328
|
|
|
16327
16329
|
// src/lib/text-file.ts
|
|
16328
16330
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16331
|
+
var TEXT_DETECTION_SAMPLE_BYTES = 64 * 1024;
|
|
16329
16332
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16330
16333
|
function assertStringArgument(value, name) {
|
|
16331
16334
|
if (typeof value !== "string") {
|
|
16332
16335
|
throw createTextError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
16333
16336
|
}
|
|
16334
16337
|
}
|
|
16338
|
+
function resolveExplicitTextEncoding(value, fallback) {
|
|
16339
|
+
const requested = value ?? "auto";
|
|
16340
|
+
return requested === "auto" ? fallback : requested;
|
|
16341
|
+
}
|
|
16342
|
+
function getBomPrefix(encoding, hasBom) {
|
|
16343
|
+
if (!hasBom) {
|
|
16344
|
+
return Buffer.alloc(0);
|
|
16345
|
+
}
|
|
16346
|
+
if (encoding === "utf8-bom") {
|
|
16347
|
+
return Buffer.from([239, 187, 191]);
|
|
16348
|
+
}
|
|
16349
|
+
if (encoding === "utf16le") {
|
|
16350
|
+
return Buffer.from([255, 254]);
|
|
16351
|
+
}
|
|
16352
|
+
if (encoding === "utf16be") {
|
|
16353
|
+
return Buffer.from([254, 255]);
|
|
16354
|
+
}
|
|
16355
|
+
return Buffer.alloc(0);
|
|
16356
|
+
}
|
|
16335
16357
|
function isSupportedEncoding(value) {
|
|
16336
16358
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
16337
16359
|
}
|
|
@@ -16343,23 +16365,23 @@ function assertTextEncodingSupported(encoding) {
|
|
|
16343
16365
|
function decodeUtf16be(buffer) {
|
|
16344
16366
|
return import_iconv_lite2.default.decode(buffer, "utf16be");
|
|
16345
16367
|
}
|
|
16346
|
-
function
|
|
16347
|
-
if (encoding === "utf8") {
|
|
16368
|
+
function encodeTextBody(content, encoding) {
|
|
16369
|
+
if (encoding === "utf8" || encoding === "utf8-bom") {
|
|
16348
16370
|
return Buffer.from(content, "utf8");
|
|
16349
16371
|
}
|
|
16350
|
-
if (encoding === "utf8-bom") {
|
|
16351
|
-
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(content, "utf8")]);
|
|
16352
|
-
}
|
|
16353
16372
|
if (encoding === "utf16le") {
|
|
16354
|
-
|
|
16355
|
-
return hasBom ? Buffer.concat([Buffer.from([255, 254]), body]) : body;
|
|
16373
|
+
return Buffer.from(content, "utf16le");
|
|
16356
16374
|
}
|
|
16357
16375
|
if (encoding === "utf16be") {
|
|
16358
|
-
|
|
16359
|
-
return hasBom ? Buffer.concat([Buffer.from([254, 255]), body]) : body;
|
|
16376
|
+
return import_iconv_lite2.default.encode(content, "utf16be");
|
|
16360
16377
|
}
|
|
16361
16378
|
return import_iconv_lite2.default.encode(content, encoding);
|
|
16362
16379
|
}
|
|
16380
|
+
function encodeText(content, encoding, hasBom = encoding === "utf8-bom") {
|
|
16381
|
+
const bom = getBomPrefix(encoding, hasBom);
|
|
16382
|
+
const body = encodeTextBody(content, encoding);
|
|
16383
|
+
return bom.length === 0 ? body : Buffer.concat([bom, body]);
|
|
16384
|
+
}
|
|
16363
16385
|
function decodeText(buffer, encoding) {
|
|
16364
16386
|
if (encoding === "utf8") {
|
|
16365
16387
|
return buffer.toString("utf8");
|
|
@@ -16603,7 +16625,10 @@ async function writeTextFile(input) {
|
|
|
16603
16625
|
if (existing && !append && !overwrite) {
|
|
16604
16626
|
throw createTextError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728: ${candidatePath}`);
|
|
16605
16627
|
}
|
|
16606
|
-
|
|
16628
|
+
if (!existing && requestedEncoding === "auto") {
|
|
16629
|
+
throw createTextError("TEXT_UNKNOWN_ENCODING", "\u65B0\u6587\u4EF6\u5728 encoding=auto \u4E0B\u65E0\u6CD5\u786E\u5B9A\u7F16\u7801\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding");
|
|
16630
|
+
}
|
|
16631
|
+
const targetEncoding = existing && preserveEncoding ? existing.encoding : resolveExplicitTextEncoding(requestedEncoding, existing?.encoding ?? "utf8");
|
|
16607
16632
|
const targetHasBom = targetEncoding === "utf8-bom" ? true : existing && preserveEncoding ? existing.hasBom : targetEncoding === "utf16le" || targetEncoding === "utf16be" ? true : false;
|
|
16608
16633
|
const baseContent = append ? existing?.content ?? "" : "";
|
|
16609
16634
|
const rawContent = `${baseContent}${input.content}`;
|
|
@@ -16631,7 +16656,7 @@ var text_write_default = tool({
|
|
|
16631
16656
|
description: `Write text files while preserving detected encoding and newline style by default.
|
|
16632
16657
|
|
|
16633
16658
|
- Existing files keep their original encoding when encoding=auto.
|
|
16634
|
-
- New files
|
|
16659
|
+
- New files require explicit encoding when encoding=auto.
|
|
16635
16660
|
- Supports append and overwrite modes.`,
|
|
16636
16661
|
args: {
|
|
16637
16662
|
filePath: tool.schema.string().describe("Target file path"),
|
package/dist/plugin/index.js
CHANGED
|
@@ -17409,6 +17409,7 @@ var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
|
17409
17409
|
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
17410
17410
|
"- \u5904\u7406\u6587\u672C\u6587\u4EF6\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 text_read\u3001text_write\u3001text_edit\u3002",
|
|
17411
17411
|
"- text_* \u9ED8\u8BA4\u4F1A\u81EA\u52A8\u8BC6\u522B\u73B0\u6709\u6587\u4EF6\u7F16\u7801\uFF0C\u5E76\u5728\u4FEE\u6539\u65F6\u5C3D\u91CF\u4FDD\u6301\u539F\u7F16\u7801\u3001BOM \u548C\u6362\u884C\u98CE\u683C\u3002",
|
|
17412
|
+
"- \u65B0\u6587\u4EF6\u5728 encoding=auto \u4E0B\u4E0D\u4F1A\u9759\u9ED8\u9ED8\u8BA4\u6210 utf8\uFF1B\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
17412
17413
|
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
17413
17414
|
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
17414
17415
|
"- \u82E5\u68C0\u6D4B\u7F6E\u4FE1\u5EA6\u4E0D\u8DB3\u6216\u51FA\u73B0 TEXT_UNKNOWN_ENCODING\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding \u540E\u91CD\u8BD5\u3002",
|
|
@@ -17423,10 +17424,25 @@ function appendTextToolSystemPrompt(system) {
|
|
|
17423
17424
|
|
|
17424
17425
|
// src/lib/text-file.ts
|
|
17425
17426
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
17427
|
+
import crypto2 from "crypto";
|
|
17428
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
17426
17429
|
import fs3 from "fs/promises";
|
|
17427
17430
|
import path3 from "path";
|
|
17428
17431
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
17432
|
+
var TEXT_DETECTION_SAMPLE_BYTES = 64 * 1024;
|
|
17429
17433
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
17434
|
+
function finalizeNewlineStyle2(crlfCount, lfCount) {
|
|
17435
|
+
if (crlfCount > 0 && lfCount === 0) {
|
|
17436
|
+
return "crlf";
|
|
17437
|
+
}
|
|
17438
|
+
if (lfCount > 0 && crlfCount === 0) {
|
|
17439
|
+
return "lf";
|
|
17440
|
+
}
|
|
17441
|
+
if (crlfCount > 0 && lfCount > 0) {
|
|
17442
|
+
return "mixed";
|
|
17443
|
+
}
|
|
17444
|
+
return "none";
|
|
17445
|
+
}
|
|
17430
17446
|
function assertStringArgument2(value, name) {
|
|
17431
17447
|
if (typeof value !== "string") {
|
|
17432
17448
|
throw createTextError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
@@ -17446,6 +17462,21 @@ function resolveExplicitTextEncoding(value, fallback) {
|
|
|
17446
17462
|
const requested = value ?? "auto";
|
|
17447
17463
|
return requested === "auto" ? fallback : requested;
|
|
17448
17464
|
}
|
|
17465
|
+
function getBomPrefix(encoding, hasBom) {
|
|
17466
|
+
if (!hasBom) {
|
|
17467
|
+
return Buffer.alloc(0);
|
|
17468
|
+
}
|
|
17469
|
+
if (encoding === "utf8-bom") {
|
|
17470
|
+
return Buffer.from([239, 187, 191]);
|
|
17471
|
+
}
|
|
17472
|
+
if (encoding === "utf16le") {
|
|
17473
|
+
return Buffer.from([255, 254]);
|
|
17474
|
+
}
|
|
17475
|
+
if (encoding === "utf16be") {
|
|
17476
|
+
return Buffer.from([254, 255]);
|
|
17477
|
+
}
|
|
17478
|
+
return Buffer.alloc(0);
|
|
17479
|
+
}
|
|
17449
17480
|
function isSupportedEncoding(value) {
|
|
17450
17481
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
17451
17482
|
}
|
|
@@ -17457,23 +17488,23 @@ function assertTextEncodingSupported(encoding) {
|
|
|
17457
17488
|
function decodeUtf16be(buffer) {
|
|
17458
17489
|
return import_iconv_lite2.default.decode(buffer, "utf16be");
|
|
17459
17490
|
}
|
|
17460
|
-
function
|
|
17461
|
-
if (encoding === "utf8") {
|
|
17491
|
+
function encodeTextBody(content, encoding) {
|
|
17492
|
+
if (encoding === "utf8" || encoding === "utf8-bom") {
|
|
17462
17493
|
return Buffer.from(content, "utf8");
|
|
17463
17494
|
}
|
|
17464
|
-
if (encoding === "utf8-bom") {
|
|
17465
|
-
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(content, "utf8")]);
|
|
17466
|
-
}
|
|
17467
17495
|
if (encoding === "utf16le") {
|
|
17468
|
-
|
|
17469
|
-
return hasBom ? Buffer.concat([Buffer.from([255, 254]), body]) : body;
|
|
17496
|
+
return Buffer.from(content, "utf16le");
|
|
17470
17497
|
}
|
|
17471
17498
|
if (encoding === "utf16be") {
|
|
17472
|
-
|
|
17473
|
-
return hasBom ? Buffer.concat([Buffer.from([254, 255]), body]) : body;
|
|
17499
|
+
return import_iconv_lite2.default.encode(content, "utf16be");
|
|
17474
17500
|
}
|
|
17475
17501
|
return import_iconv_lite2.default.encode(content, encoding);
|
|
17476
17502
|
}
|
|
17503
|
+
function encodeText(content, encoding, hasBom = encoding === "utf8-bom") {
|
|
17504
|
+
const bom = getBomPrefix(encoding, hasBom);
|
|
17505
|
+
const body = encodeTextBody(content, encoding);
|
|
17506
|
+
return bom.length === 0 ? body : Buffer.concat([bom, body]);
|
|
17507
|
+
}
|
|
17477
17508
|
function decodeText(buffer, encoding) {
|
|
17478
17509
|
if (encoding === "utf8") {
|
|
17479
17510
|
return buffer.toString("utf8");
|
|
@@ -17749,6 +17780,169 @@ async function resolveReadableTextFile(input) {
|
|
|
17749
17780
|
}
|
|
17750
17781
|
return { filePath: candidatePath, stat };
|
|
17751
17782
|
}
|
|
17783
|
+
async function readDetectionBuffer(filePath, sampleSize = TEXT_DETECTION_SAMPLE_BYTES) {
|
|
17784
|
+
const handle = await fs3.open(filePath, "r");
|
|
17785
|
+
try {
|
|
17786
|
+
const buffer = Buffer.alloc(sampleSize);
|
|
17787
|
+
const { bytesRead } = await handle.read(buffer, 0, sampleSize, 0);
|
|
17788
|
+
return buffer.subarray(0, bytesRead);
|
|
17789
|
+
} finally {
|
|
17790
|
+
await handle.close();
|
|
17791
|
+
}
|
|
17792
|
+
}
|
|
17793
|
+
async function detectReadableTextFile(input) {
|
|
17794
|
+
const resolved = await resolveReadableTextFile(input);
|
|
17795
|
+
const sample = await readDetectionBuffer(resolved.filePath);
|
|
17796
|
+
assertLikelyTextBuffer(sample);
|
|
17797
|
+
const detected = detectTextEncodingFromBuffer(sample, input.encoding ?? "auto");
|
|
17798
|
+
return {
|
|
17799
|
+
...resolved,
|
|
17800
|
+
encoding: detected.detectedEncoding,
|
|
17801
|
+
requestedEncoding: detected.requestedEncoding,
|
|
17802
|
+
detectedEncoding: detected.detectedEncoding,
|
|
17803
|
+
confidence: detected.confidence,
|
|
17804
|
+
hasBom: detected.hasBom
|
|
17805
|
+
};
|
|
17806
|
+
}
|
|
17807
|
+
function createLineCollector2(offset, limit) {
|
|
17808
|
+
const lines = [];
|
|
17809
|
+
let pending = "";
|
|
17810
|
+
let totalLines = 0;
|
|
17811
|
+
let crlfCount = 0;
|
|
17812
|
+
let lfCount = 0;
|
|
17813
|
+
const emitLine = (line) => {
|
|
17814
|
+
totalLines += 1;
|
|
17815
|
+
if (totalLines >= offset && lines.length < limit) {
|
|
17816
|
+
lines.push(`${totalLines}: ${line}`);
|
|
17817
|
+
}
|
|
17818
|
+
};
|
|
17819
|
+
return {
|
|
17820
|
+
push(text) {
|
|
17821
|
+
if (text.length === 0) {
|
|
17822
|
+
return;
|
|
17823
|
+
}
|
|
17824
|
+
const combined = pending + text;
|
|
17825
|
+
let start = 0;
|
|
17826
|
+
while (true) {
|
|
17827
|
+
const newlineIndex = combined.indexOf("\n", start);
|
|
17828
|
+
if (newlineIndex === -1) {
|
|
17829
|
+
break;
|
|
17830
|
+
}
|
|
17831
|
+
let line = combined.slice(start, newlineIndex);
|
|
17832
|
+
if (line.endsWith("\r")) {
|
|
17833
|
+
line = line.slice(0, -1);
|
|
17834
|
+
crlfCount += 1;
|
|
17835
|
+
} else {
|
|
17836
|
+
lfCount += 1;
|
|
17837
|
+
}
|
|
17838
|
+
emitLine(line);
|
|
17839
|
+
start = newlineIndex + 1;
|
|
17840
|
+
}
|
|
17841
|
+
pending = combined.slice(start);
|
|
17842
|
+
},
|
|
17843
|
+
finish() {
|
|
17844
|
+
emitLine(pending);
|
|
17845
|
+
return {
|
|
17846
|
+
startLine: offset,
|
|
17847
|
+
endLine: lines.length === 0 ? totalLines : offset + lines.length - 1,
|
|
17848
|
+
totalLines,
|
|
17849
|
+
content: lines.join("\n"),
|
|
17850
|
+
tail: false,
|
|
17851
|
+
truncated: (lines.length === 0 ? totalLines : offset + lines.length - 1) < totalLines,
|
|
17852
|
+
newlineStyle: finalizeNewlineStyle2(crlfCount, lfCount)
|
|
17853
|
+
};
|
|
17854
|
+
}
|
|
17855
|
+
};
|
|
17856
|
+
}
|
|
17857
|
+
function createTailCollector2(limit) {
|
|
17858
|
+
assertPositiveInteger(limit, "limit");
|
|
17859
|
+
const lines = [];
|
|
17860
|
+
let pending = "";
|
|
17861
|
+
let totalLines = 0;
|
|
17862
|
+
let crlfCount = 0;
|
|
17863
|
+
let lfCount = 0;
|
|
17864
|
+
const emitLine = (line) => {
|
|
17865
|
+
totalLines += 1;
|
|
17866
|
+
lines.push(line);
|
|
17867
|
+
if (lines.length > limit) {
|
|
17868
|
+
lines.shift();
|
|
17869
|
+
}
|
|
17870
|
+
};
|
|
17871
|
+
return {
|
|
17872
|
+
push(text) {
|
|
17873
|
+
if (text.length === 0) {
|
|
17874
|
+
return;
|
|
17875
|
+
}
|
|
17876
|
+
const combined = pending + text;
|
|
17877
|
+
let start = 0;
|
|
17878
|
+
while (true) {
|
|
17879
|
+
const newlineIndex = combined.indexOf("\n", start);
|
|
17880
|
+
if (newlineIndex === -1) {
|
|
17881
|
+
break;
|
|
17882
|
+
}
|
|
17883
|
+
let line = combined.slice(start, newlineIndex);
|
|
17884
|
+
if (line.endsWith("\r")) {
|
|
17885
|
+
line = line.slice(0, -1);
|
|
17886
|
+
crlfCount += 1;
|
|
17887
|
+
} else {
|
|
17888
|
+
lfCount += 1;
|
|
17889
|
+
}
|
|
17890
|
+
emitLine(line);
|
|
17891
|
+
start = newlineIndex + 1;
|
|
17892
|
+
}
|
|
17893
|
+
pending = combined.slice(start);
|
|
17894
|
+
},
|
|
17895
|
+
finish() {
|
|
17896
|
+
emitLine(pending);
|
|
17897
|
+
const startLine = Math.max(1, totalLines - lines.length + 1);
|
|
17898
|
+
return {
|
|
17899
|
+
startLine,
|
|
17900
|
+
endLine: totalLines,
|
|
17901
|
+
totalLines,
|
|
17902
|
+
content: lines.map((line, index) => `${startLine + index}: ${line}`).join("\n"),
|
|
17903
|
+
truncated: startLine > 1,
|
|
17904
|
+
tail: true,
|
|
17905
|
+
newlineStyle: finalizeNewlineStyle2(crlfCount, lfCount)
|
|
17906
|
+
};
|
|
17907
|
+
}
|
|
17908
|
+
};
|
|
17909
|
+
}
|
|
17910
|
+
async function visitDecodedTextChunks2(resolved, visitor) {
|
|
17911
|
+
const stream = createReadStream2(resolved.filePath);
|
|
17912
|
+
const bomLength = getBomPrefix(resolved.encoding, resolved.hasBom).length;
|
|
17913
|
+
const decoderEncoding = resolved.encoding === "utf8-bom" ? "utf8" : resolved.encoding;
|
|
17914
|
+
const decoder = import_iconv_lite2.default.getDecoder(decoderEncoding);
|
|
17915
|
+
let skippedBom = false;
|
|
17916
|
+
try {
|
|
17917
|
+
for await (const chunk of stream) {
|
|
17918
|
+
let buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
17919
|
+
if (!skippedBom && bomLength > 0) {
|
|
17920
|
+
buffer = buffer.subarray(Math.min(bomLength, buffer.length));
|
|
17921
|
+
skippedBom = true;
|
|
17922
|
+
} else {
|
|
17923
|
+
skippedBom = true;
|
|
17924
|
+
}
|
|
17925
|
+
if (buffer.length === 0) {
|
|
17926
|
+
continue;
|
|
17927
|
+
}
|
|
17928
|
+
const text = decoder.write(buffer);
|
|
17929
|
+
if (text.length > 0) {
|
|
17930
|
+
await visitor(text);
|
|
17931
|
+
}
|
|
17932
|
+
}
|
|
17933
|
+
const trailingText = decoder.end() ?? "";
|
|
17934
|
+
if (trailingText.length > 0) {
|
|
17935
|
+
await visitor(trailingText);
|
|
17936
|
+
}
|
|
17937
|
+
} catch (error45) {
|
|
17938
|
+
if (error45 instanceof Error && "code" in error45) {
|
|
17939
|
+
throw error45;
|
|
17940
|
+
}
|
|
17941
|
+
throw createTextError("GBK_IO_ERROR", `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${resolved.filePath}`, error45);
|
|
17942
|
+
} finally {
|
|
17943
|
+
stream.destroy();
|
|
17944
|
+
}
|
|
17945
|
+
}
|
|
17752
17946
|
async function readWholeTextFile(input) {
|
|
17753
17947
|
const resolved = await resolveReadableTextFile(input);
|
|
17754
17948
|
try {
|
|
@@ -17914,26 +18108,46 @@ async function readTextFile(input) {
|
|
|
17914
18108
|
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
17915
18109
|
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
17916
18110
|
const tail = input.tail ?? false;
|
|
17917
|
-
const
|
|
17918
|
-
|
|
17919
|
-
|
|
17920
|
-
tail:
|
|
17921
|
-
|
|
17922
|
-
|
|
17923
|
-
|
|
17924
|
-
|
|
18111
|
+
const resolved = await detectReadableTextFile(input);
|
|
18112
|
+
if (resolved.stat.size < TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
|
|
18113
|
+
const loaded = await readWholeTextFile(input);
|
|
18114
|
+
const lineWindow2 = tail ? collectTailLines2(loaded.content, limit) : {
|
|
18115
|
+
...splitLinesWithNumbers(loaded.content, offset, limit),
|
|
18116
|
+
tail: false,
|
|
18117
|
+
truncated: false
|
|
18118
|
+
};
|
|
18119
|
+
if (!tail) {
|
|
18120
|
+
lineWindow2.truncated = lineWindow2.endLine < lineWindow2.totalLines;
|
|
18121
|
+
}
|
|
18122
|
+
return {
|
|
18123
|
+
filePath: loaded.filePath,
|
|
18124
|
+
encoding: loaded.encoding,
|
|
18125
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
18126
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
18127
|
+
confidence: loaded.confidence,
|
|
18128
|
+
hasBom: loaded.hasBom,
|
|
18129
|
+
bytesRead: loaded.bytesRead,
|
|
18130
|
+
fileSize: loaded.fileSize,
|
|
18131
|
+
streamed: false,
|
|
18132
|
+
newlineStyle: loaded.newlineStyle,
|
|
18133
|
+
...lineWindow2
|
|
18134
|
+
};
|
|
17925
18135
|
}
|
|
18136
|
+
const collector = tail ? createTailCollector2(limit) : createLineCollector2(offset, limit);
|
|
18137
|
+
await visitDecodedTextChunks2(resolved, (text) => {
|
|
18138
|
+
collector.push(text);
|
|
18139
|
+
});
|
|
18140
|
+
const lineWindow = collector.finish();
|
|
17926
18141
|
return {
|
|
17927
|
-
filePath:
|
|
17928
|
-
encoding:
|
|
17929
|
-
requestedEncoding:
|
|
17930
|
-
detectedEncoding:
|
|
17931
|
-
confidence:
|
|
17932
|
-
hasBom:
|
|
17933
|
-
bytesRead:
|
|
17934
|
-
fileSize:
|
|
17935
|
-
streamed:
|
|
17936
|
-
newlineStyle: loaded.newlineStyle,
|
|
18142
|
+
filePath: resolved.filePath,
|
|
18143
|
+
encoding: resolved.encoding,
|
|
18144
|
+
requestedEncoding: resolved.requestedEncoding,
|
|
18145
|
+
detectedEncoding: resolved.detectedEncoding,
|
|
18146
|
+
confidence: resolved.confidence,
|
|
18147
|
+
hasBom: resolved.hasBom,
|
|
18148
|
+
bytesRead: resolved.stat.size,
|
|
18149
|
+
fileSize: resolved.stat.size,
|
|
18150
|
+
streamed: true,
|
|
17937
18151
|
...lineWindow
|
|
17938
18152
|
};
|
|
17939
18153
|
}
|
|
@@ -17965,7 +18179,10 @@ async function writeTextFile(input) {
|
|
|
17965
18179
|
if (existing && !append && !overwrite) {
|
|
17966
18180
|
throw createTextError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728: ${candidatePath}`);
|
|
17967
18181
|
}
|
|
17968
|
-
|
|
18182
|
+
if (!existing && requestedEncoding === "auto") {
|
|
18183
|
+
throw createTextError("TEXT_UNKNOWN_ENCODING", "\u65B0\u6587\u4EF6\u5728 encoding=auto \u4E0B\u65E0\u6CD5\u786E\u5B9A\u7F16\u7801\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding");
|
|
18184
|
+
}
|
|
18185
|
+
const targetEncoding = existing && preserveEncoding ? existing.encoding : resolveExplicitTextEncoding(requestedEncoding, existing?.encoding ?? "utf8");
|
|
17969
18186
|
const targetHasBom = targetEncoding === "utf8-bom" ? true : existing && preserveEncoding ? existing.hasBom : targetEncoding === "utf16le" || targetEncoding === "utf16be" ? true : false;
|
|
17970
18187
|
const baseContent = append ? existing?.content ?? "" : "";
|
|
17971
18188
|
const rawContent = `${baseContent}${input.content}`;
|
|
@@ -17994,70 +18211,84 @@ async function replaceTextFileText(input) {
|
|
|
17994
18211
|
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
17995
18212
|
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
17996
18213
|
};
|
|
17997
|
-
const loaded = await readWholeTextFile({
|
|
17998
|
-
filePath: input.filePath,
|
|
17999
|
-
encoding: input.encoding ?? "auto",
|
|
18000
|
-
allowExternal: input.allowExternal,
|
|
18001
|
-
context: input.context
|
|
18002
|
-
});
|
|
18003
18214
|
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
18215
|
+
const loaded2 = await readWholeTextFile({
|
|
18216
|
+
filePath: input.filePath,
|
|
18217
|
+
encoding: input.encoding ?? "auto",
|
|
18218
|
+
allowExternal: input.allowExternal,
|
|
18219
|
+
context: input.context
|
|
18220
|
+
});
|
|
18004
18221
|
assertInsertArguments2(normalizedInput);
|
|
18005
18222
|
const occurrence = normalizeOptionalPositiveInteger(normalizedInput.occurrence, "occurrence") ?? 1;
|
|
18006
18223
|
const insertResult = buildInsertOutput(
|
|
18007
|
-
|
|
18224
|
+
loaded2.content,
|
|
18008
18225
|
mode,
|
|
18009
18226
|
normalizedInput.anchor,
|
|
18010
18227
|
normalizedInput.content,
|
|
18011
18228
|
occurrence,
|
|
18012
18229
|
resolveInsertIfExists(normalizedInput.ifExists),
|
|
18013
|
-
|
|
18230
|
+
loaded2.newlineStyle
|
|
18014
18231
|
);
|
|
18015
18232
|
if (insertResult.skipped) {
|
|
18016
18233
|
return {
|
|
18017
18234
|
mode,
|
|
18018
|
-
filePath:
|
|
18019
|
-
encoding:
|
|
18020
|
-
requestedEncoding:
|
|
18021
|
-
detectedEncoding:
|
|
18022
|
-
confidence:
|
|
18023
|
-
hasBom:
|
|
18235
|
+
filePath: loaded2.filePath,
|
|
18236
|
+
encoding: loaded2.encoding,
|
|
18237
|
+
requestedEncoding: loaded2.requestedEncoding,
|
|
18238
|
+
detectedEncoding: loaded2.detectedEncoding,
|
|
18239
|
+
confidence: loaded2.confidence,
|
|
18240
|
+
hasBom: loaded2.hasBom,
|
|
18024
18241
|
anchor: insertResult.anchor,
|
|
18025
18242
|
occurrence: insertResult.occurrence,
|
|
18026
18243
|
anchorMatches: insertResult.anchorMatches,
|
|
18027
18244
|
inserted: false,
|
|
18028
18245
|
skipped: true,
|
|
18029
|
-
bytesRead:
|
|
18246
|
+
bytesRead: loaded2.bytesRead,
|
|
18030
18247
|
bytesWritten: 0,
|
|
18031
|
-
newlineStyle:
|
|
18248
|
+
newlineStyle: loaded2.newlineStyle,
|
|
18249
|
+
diffPreview: buildLineDiffPreview(loaded2.filePath, loaded2.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
18032
18250
|
};
|
|
18033
18251
|
}
|
|
18034
|
-
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ?
|
|
18035
|
-
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" :
|
|
18252
|
+
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ? loaded2.encoding : resolveExplicitTextEncoding(normalizedInput.encoding, loaded2.encoding);
|
|
18253
|
+
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" : loaded2.hasBom;
|
|
18036
18254
|
ensureLossless(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
18037
18255
|
const buffer2 = encodeText(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
18038
|
-
await fs3.writeFile(
|
|
18256
|
+
await fs3.writeFile(loaded2.filePath, buffer2);
|
|
18039
18257
|
return {
|
|
18040
18258
|
mode,
|
|
18041
|
-
filePath:
|
|
18259
|
+
filePath: loaded2.filePath,
|
|
18042
18260
|
encoding: targetEncoding2,
|
|
18043
|
-
requestedEncoding:
|
|
18044
|
-
detectedEncoding:
|
|
18045
|
-
confidence:
|
|
18261
|
+
requestedEncoding: loaded2.requestedEncoding,
|
|
18262
|
+
detectedEncoding: loaded2.detectedEncoding,
|
|
18263
|
+
confidence: loaded2.confidence,
|
|
18046
18264
|
hasBom: targetHasBom2,
|
|
18047
18265
|
anchor: insertResult.anchor,
|
|
18048
18266
|
occurrence: insertResult.occurrence,
|
|
18049
18267
|
anchorMatches: insertResult.anchorMatches,
|
|
18050
18268
|
inserted: true,
|
|
18051
18269
|
skipped: false,
|
|
18052
|
-
bytesRead:
|
|
18270
|
+
bytesRead: loaded2.bytesRead,
|
|
18053
18271
|
bytesWritten: buffer2.byteLength,
|
|
18054
|
-
newlineStyle: detectNewlineStyle(insertResult.outputText)
|
|
18272
|
+
newlineStyle: detectNewlineStyle(insertResult.outputText),
|
|
18273
|
+
diffPreview: buildLineDiffPreview(loaded2.filePath, targetEncoding2, insertResult.previewBefore, insertResult.previewAfter)
|
|
18055
18274
|
};
|
|
18056
18275
|
}
|
|
18057
18276
|
assertReplaceArguments2(input);
|
|
18058
18277
|
if (input.oldString.length === 0) {
|
|
18059
18278
|
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
18060
18279
|
}
|
|
18280
|
+
const resolved = await detectReadableTextFile({
|
|
18281
|
+
filePath: input.filePath,
|
|
18282
|
+
encoding: input.encoding ?? "auto",
|
|
18283
|
+
allowExternal: input.allowExternal,
|
|
18284
|
+
context: input.context
|
|
18285
|
+
});
|
|
18286
|
+
const loaded = await readWholeTextFile({
|
|
18287
|
+
filePath: input.filePath,
|
|
18288
|
+
encoding: input.encoding ?? "auto",
|
|
18289
|
+
allowExternal: input.allowExternal,
|
|
18290
|
+
context: input.context
|
|
18291
|
+
});
|
|
18061
18292
|
const scope = resolveEditScope2(loaded.content, normalizedInput);
|
|
18062
18293
|
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
18063
18294
|
const preserveEncoding = normalizedInput.preserveEncoding ?? true;
|
|
@@ -18092,43 +18323,8 @@ async function replaceTextFileText(input) {
|
|
|
18092
18323
|
occurrencesBefore,
|
|
18093
18324
|
bytesRead: loaded.bytesRead,
|
|
18094
18325
|
bytesWritten: buffer.byteLength,
|
|
18095
|
-
newlineStyle: detectNewlineStyle(outputText)
|
|
18096
|
-
|
|
18097
|
-
}
|
|
18098
|
-
async function createTextDiffPreview(input) {
|
|
18099
|
-
const mode = resolveEditMode(input.mode);
|
|
18100
|
-
const loaded = await readWholeTextFile({
|
|
18101
|
-
filePath: input.filePath,
|
|
18102
|
-
encoding: input.encoding ?? "auto",
|
|
18103
|
-
allowExternal: input.allowExternal,
|
|
18104
|
-
context: input.context
|
|
18105
|
-
});
|
|
18106
|
-
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
18107
|
-
assertInsertArguments2(input);
|
|
18108
|
-
const occurrence = normalizeOptionalPositiveInteger(input.occurrence, "occurrence") ?? 1;
|
|
18109
|
-
const insertResult = buildInsertOutput(
|
|
18110
|
-
loaded.content,
|
|
18111
|
-
mode,
|
|
18112
|
-
input.anchor,
|
|
18113
|
-
input.content,
|
|
18114
|
-
occurrence,
|
|
18115
|
-
resolveInsertIfExists(input.ifExists),
|
|
18116
|
-
loaded.newlineStyle
|
|
18117
|
-
);
|
|
18118
|
-
return {
|
|
18119
|
-
filePath: loaded.filePath,
|
|
18120
|
-
encoding: loaded.encoding,
|
|
18121
|
-
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
18122
|
-
};
|
|
18123
|
-
}
|
|
18124
|
-
assertReplaceArguments2(input);
|
|
18125
|
-
const scope = resolveEditScope2(loaded.content, input);
|
|
18126
|
-
const alignedNewString = alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
18127
|
-
const replaced = input.replaceAll ?? false ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
18128
|
-
return {
|
|
18129
|
-
filePath: loaded.filePath,
|
|
18130
|
-
encoding: loaded.encoding,
|
|
18131
|
-
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, scope.selectedText, replaced)
|
|
18326
|
+
newlineStyle: detectNewlineStyle(outputText),
|
|
18327
|
+
diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
|
|
18132
18328
|
};
|
|
18133
18329
|
}
|
|
18134
18330
|
|
|
@@ -18161,7 +18357,6 @@ var text_edit_default = tool({
|
|
|
18161
18357
|
},
|
|
18162
18358
|
async execute(args, context) {
|
|
18163
18359
|
const result = await replaceTextFileText({ ...args, context });
|
|
18164
|
-
const preview = await createTextDiffPreview({ ...args, context });
|
|
18165
18360
|
const title = result.mode === "replace" ? `\u6587\u672C\u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}` : result.inserted ? `\u6587\u672C\u63D2\u5165 ${result.encoding.toUpperCase()} #${result.occurrence}` : `\u6587\u672C\u8DF3\u8FC7 ${result.encoding.toUpperCase()} #${result.occurrence}`;
|
|
18166
18361
|
context.metadata({
|
|
18167
18362
|
title,
|
|
@@ -18180,7 +18375,7 @@ var text_edit_default = tool({
|
|
|
18180
18375
|
inserted: result.mode === "replace" ? void 0 : result.inserted,
|
|
18181
18376
|
skipped: result.mode === "replace" ? void 0 : result.skipped,
|
|
18182
18377
|
newlineStyle: result.newlineStyle,
|
|
18183
|
-
diffPreview:
|
|
18378
|
+
diffPreview: result.diffPreview ?? ""
|
|
18184
18379
|
}
|
|
18185
18380
|
});
|
|
18186
18381
|
return JSON.stringify(result, null, 2);
|
|
@@ -18228,7 +18423,7 @@ var text_write_default = tool({
|
|
|
18228
18423
|
description: `Write text files while preserving detected encoding and newline style by default.
|
|
18229
18424
|
|
|
18230
18425
|
- Existing files keep their original encoding when encoding=auto.
|
|
18231
|
-
- New files
|
|
18426
|
+
- New files require explicit encoding when encoding=auto.
|
|
18232
18427
|
- Supports append and overwrite modes.`,
|
|
18233
18428
|
args: {
|
|
18234
18429
|
filePath: tool.schema.string().describe("Target file path"),
|
|
@@ -5,6 +5,7 @@ var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
|
5
5
|
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
6
6
|
"- \u5904\u7406\u6587\u672C\u6587\u4EF6\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 text_read\u3001text_write\u3001text_edit\u3002",
|
|
7
7
|
"- text_* \u9ED8\u8BA4\u4F1A\u81EA\u52A8\u8BC6\u522B\u73B0\u6709\u6587\u4EF6\u7F16\u7801\uFF0C\u5E76\u5728\u4FEE\u6539\u65F6\u5C3D\u91CF\u4FDD\u6301\u539F\u7F16\u7801\u3001BOM \u548C\u6362\u884C\u98CE\u683C\u3002",
|
|
8
|
+
"- \u65B0\u6587\u4EF6\u5728 encoding=auto \u4E0B\u4E0D\u4F1A\u9759\u9ED8\u9ED8\u8BA4\u6210 utf8\uFF1B\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
8
9
|
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
9
10
|
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
10
11
|
"- \u82E5\u68C0\u6D4B\u7F6E\u4FE1\u5EA6\u4E0D\u8DB3\u6216\u51FA\u73B0 TEXT_UNKNOWN_ENCODING\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding \u540E\u91CD\u8BD5\u3002",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
3
|
"packageName": "opencode-gbk-tools",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.13",
|
|
5
5
|
"artifacts": [
|
|
6
6
|
{
|
|
7
7
|
"relativePath": "tools/gbk_edit.js",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
{
|
|
31
31
|
"relativePath": "tools/text_edit.js",
|
|
32
32
|
"kind": "tool",
|
|
33
|
-
"expectedHash": "
|
|
33
|
+
"expectedHash": "28e22f96cb6e738c55a3c90b8ffae9ef5d5d702649353373d0d04e882c60a5ee",
|
|
34
34
|
"hashAlgorithm": "sha256"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"relativePath": "tools/text_read.js",
|
|
38
38
|
"kind": "tool",
|
|
39
|
-
"expectedHash": "
|
|
39
|
+
"expectedHash": "09de6b032a6c622471e00701be56274e86a6019406f1bf4e45016b90ffabc4d9",
|
|
40
40
|
"hashAlgorithm": "sha256"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"relativePath": "tools/text_write.js",
|
|
44
44
|
"kind": "tool",
|
|
45
|
-
"expectedHash": "
|
|
45
|
+
"expectedHash": "d9b941bb88ecad271a9865eac6595ead964357a04e278ecd43941d4212ab69f3",
|
|
46
46
|
"hashAlgorithm": "sha256"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
{
|
|
55
55
|
"relativePath": "plugins/opencode-gbk-tools.js",
|
|
56
56
|
"kind": "plugin",
|
|
57
|
-
"expectedHash": "
|
|
57
|
+
"expectedHash": "efde5f79ebaa4393a276aa9da0be2f172e90127de7ccfbe16a186eb04f02975a",
|
|
58
58
|
"hashAlgorithm": "sha256"
|
|
59
59
|
}
|
|
60
60
|
]
|