opencode-gbk-tools 0.1.9 → 0.1.10
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 +31 -4
- package/dist/cli/index.js +3 -3
- package/dist/opencode-tools/gbk_edit.js +11 -0
- package/dist/opencode-tools/gbk_read.js +13 -0
- package/dist/opencode-tools/gbk_search.js +9 -0
- package/dist/opencode-tools/gbk_write.js +12 -0
- package/dist/opencode-tools/text_edit.js +16816 -0
- package/dist/opencode-tools/text_read.js +16652 -0
- package/dist/opencode-tools/text_write.js +16662 -0
- package/dist/plugin/index.js +744 -27
- package/dist/plugins/opencode-gbk-tools.js +28 -0
- package/dist/release-manifest.json +29 -5
- package/package.json +2 -2
package/dist/plugin/index.js
CHANGED
|
@@ -182,7 +182,7 @@ var require_internal = __commonJS({
|
|
|
182
182
|
// Codec.
|
|
183
183
|
_internal: InternalCodec
|
|
184
184
|
};
|
|
185
|
-
function InternalCodec(codecOptions,
|
|
185
|
+
function InternalCodec(codecOptions, iconv3) {
|
|
186
186
|
this.enc = codecOptions.encodingName;
|
|
187
187
|
this.bomAware = codecOptions.bomAware;
|
|
188
188
|
if (this.enc === "base64") {
|
|
@@ -194,7 +194,7 @@ var require_internal = __commonJS({
|
|
|
194
194
|
this.encoder = InternalEncoderCesu8;
|
|
195
195
|
if (Buffer2.from("eda0bdedb2a9", "hex").toString() !== "\u{1F4A9}") {
|
|
196
196
|
this.decoder = InternalDecoderCesu8;
|
|
197
|
-
this.defaultCharUnicode =
|
|
197
|
+
this.defaultCharUnicode = iconv3.defaultCharUnicode;
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
@@ -351,8 +351,8 @@ var require_utf32 = __commonJS({
|
|
|
351
351
|
"use strict";
|
|
352
352
|
var Buffer2 = require_safer().Buffer;
|
|
353
353
|
exports._utf32 = Utf32Codec;
|
|
354
|
-
function Utf32Codec(codecOptions,
|
|
355
|
-
this.iconv =
|
|
354
|
+
function Utf32Codec(codecOptions, iconv3) {
|
|
355
|
+
this.iconv = iconv3;
|
|
356
356
|
this.bomAware = true;
|
|
357
357
|
this.isLE = codecOptions.isLE;
|
|
358
358
|
}
|
|
@@ -476,8 +476,8 @@ var require_utf32 = __commonJS({
|
|
|
476
476
|
};
|
|
477
477
|
exports.utf32 = Utf32AutoCodec;
|
|
478
478
|
exports.ucs4 = "utf32";
|
|
479
|
-
function Utf32AutoCodec(options,
|
|
480
|
-
this.iconv =
|
|
479
|
+
function Utf32AutoCodec(options, iconv3) {
|
|
480
|
+
this.iconv = iconv3;
|
|
481
481
|
}
|
|
482
482
|
Utf32AutoCodec.prototype.encoder = Utf32AutoEncoder;
|
|
483
483
|
Utf32AutoCodec.prototype.decoder = Utf32AutoDecoder;
|
|
@@ -627,8 +627,8 @@ var require_utf16 = __commonJS({
|
|
|
627
627
|
this.overflowByte = -1;
|
|
628
628
|
};
|
|
629
629
|
exports.utf16 = Utf16Codec;
|
|
630
|
-
function Utf16Codec(codecOptions,
|
|
631
|
-
this.iconv =
|
|
630
|
+
function Utf16Codec(codecOptions, iconv3) {
|
|
631
|
+
this.iconv = iconv3;
|
|
632
632
|
}
|
|
633
633
|
Utf16Codec.prototype.encoder = Utf16Encoder;
|
|
634
634
|
Utf16Codec.prototype.decoder = Utf16Decoder;
|
|
@@ -726,8 +726,8 @@ var require_utf7 = __commonJS({
|
|
|
726
726
|
var Buffer2 = require_safer().Buffer;
|
|
727
727
|
exports.utf7 = Utf7Codec;
|
|
728
728
|
exports.unicode11utf7 = "utf7";
|
|
729
|
-
function Utf7Codec(codecOptions,
|
|
730
|
-
this.iconv =
|
|
729
|
+
function Utf7Codec(codecOptions, iconv3) {
|
|
730
|
+
this.iconv = iconv3;
|
|
731
731
|
}
|
|
732
732
|
Utf7Codec.prototype.encoder = Utf7Encoder;
|
|
733
733
|
Utf7Codec.prototype.decoder = Utf7Decoder;
|
|
@@ -809,8 +809,8 @@ var require_utf7 = __commonJS({
|
|
|
809
809
|
return res;
|
|
810
810
|
};
|
|
811
811
|
exports.utf7imap = Utf7IMAPCodec;
|
|
812
|
-
function Utf7IMAPCodec(codecOptions,
|
|
813
|
-
this.iconv =
|
|
812
|
+
function Utf7IMAPCodec(codecOptions, iconv3) {
|
|
813
|
+
this.iconv = iconv3;
|
|
814
814
|
}
|
|
815
815
|
Utf7IMAPCodec.prototype.encoder = Utf7IMAPEncoder;
|
|
816
816
|
Utf7IMAPCodec.prototype.decoder = Utf7IMAPDecoder;
|
|
@@ -943,7 +943,7 @@ var require_sbcs_codec = __commonJS({
|
|
|
943
943
|
"use strict";
|
|
944
944
|
var Buffer2 = require_safer().Buffer;
|
|
945
945
|
exports._sbcs = SBCSCodec;
|
|
946
|
-
function SBCSCodec(codecOptions,
|
|
946
|
+
function SBCSCodec(codecOptions, iconv3) {
|
|
947
947
|
if (!codecOptions) {
|
|
948
948
|
throw new Error("SBCS codec is called without the data.");
|
|
949
949
|
}
|
|
@@ -958,7 +958,7 @@ var require_sbcs_codec = __commonJS({
|
|
|
958
958
|
codecOptions.chars = asciiString + codecOptions.chars;
|
|
959
959
|
}
|
|
960
960
|
this.decodeBuf = Buffer2.from(codecOptions.chars, "ucs2");
|
|
961
|
-
var encodeBuf = Buffer2.alloc(65536,
|
|
961
|
+
var encodeBuf = Buffer2.alloc(65536, iconv3.defaultCharSingleByte.charCodeAt(0));
|
|
962
962
|
for (var i = 0; i < codecOptions.chars.length; i++) {
|
|
963
963
|
encodeBuf[codecOptions.chars.charCodeAt(i)] = i;
|
|
964
964
|
}
|
|
@@ -1623,7 +1623,7 @@ var require_dbcs_codec = __commonJS({
|
|
|
1623
1623
|
UNASSIGNED_NODE[i] = UNASSIGNED;
|
|
1624
1624
|
}
|
|
1625
1625
|
var i;
|
|
1626
|
-
function DBCSCodec(codecOptions,
|
|
1626
|
+
function DBCSCodec(codecOptions, iconv3) {
|
|
1627
1627
|
this.encodingName = codecOptions.encodingName;
|
|
1628
1628
|
if (!codecOptions) {
|
|
1629
1629
|
throw new Error("DBCS codec is called without the data.");
|
|
@@ -1672,7 +1672,7 @@ var require_dbcs_codec = __commonJS({
|
|
|
1672
1672
|
}
|
|
1673
1673
|
}
|
|
1674
1674
|
}
|
|
1675
|
-
this.defaultCharUnicode =
|
|
1675
|
+
this.defaultCharUnicode = iconv3.defaultCharUnicode;
|
|
1676
1676
|
this.encodeTable = [];
|
|
1677
1677
|
this.encodeTableSeq = [];
|
|
1678
1678
|
var skipEncodeChars = {};
|
|
@@ -1696,7 +1696,7 @@ var require_dbcs_codec = __commonJS({
|
|
|
1696
1696
|
}
|
|
1697
1697
|
}
|
|
1698
1698
|
}
|
|
1699
|
-
this.defCharSB = this.encodeTable[0][
|
|
1699
|
+
this.defCharSB = this.encodeTable[0][iconv3.defaultCharSingleByte.charCodeAt(0)];
|
|
1700
1700
|
if (this.defCharSB === UNASSIGNED) this.defCharSB = this.encodeTable[0]["?"];
|
|
1701
1701
|
if (this.defCharSB === UNASSIGNED) this.defCharSB = "?".charCodeAt(0);
|
|
1702
1702
|
}
|
|
@@ -4547,10 +4547,10 @@ function mergeDefs(...defs) {
|
|
|
4547
4547
|
function cloneDef(schema) {
|
|
4548
4548
|
return mergeDefs(schema._zod.def);
|
|
4549
4549
|
}
|
|
4550
|
-
function getElementAtPath(obj,
|
|
4551
|
-
if (!
|
|
4550
|
+
function getElementAtPath(obj, path4) {
|
|
4551
|
+
if (!path4)
|
|
4552
4552
|
return obj;
|
|
4553
|
-
return
|
|
4553
|
+
return path4.reduce((acc, key) => acc?.[key], obj);
|
|
4554
4554
|
}
|
|
4555
4555
|
function promiseAllObject(promisesObj) {
|
|
4556
4556
|
const keys = Object.keys(promisesObj);
|
|
@@ -4911,11 +4911,11 @@ function aborted(x, startIndex = 0) {
|
|
|
4911
4911
|
}
|
|
4912
4912
|
return false;
|
|
4913
4913
|
}
|
|
4914
|
-
function prefixIssues(
|
|
4914
|
+
function prefixIssues(path4, issues) {
|
|
4915
4915
|
return issues.map((iss) => {
|
|
4916
4916
|
var _a;
|
|
4917
4917
|
(_a = iss).path ?? (_a.path = []);
|
|
4918
|
-
iss.path.unshift(
|
|
4918
|
+
iss.path.unshift(path4);
|
|
4919
4919
|
return iss;
|
|
4920
4920
|
});
|
|
4921
4921
|
}
|
|
@@ -5083,7 +5083,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5083
5083
|
return issue2.message;
|
|
5084
5084
|
};
|
|
5085
5085
|
const result = { errors: [] };
|
|
5086
|
-
const processError = (error46,
|
|
5086
|
+
const processError = (error46, path4 = []) => {
|
|
5087
5087
|
var _a, _b;
|
|
5088
5088
|
for (const issue2 of error46.issues) {
|
|
5089
5089
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -5093,7 +5093,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5093
5093
|
} else if (issue2.code === "invalid_element") {
|
|
5094
5094
|
processError({ issues: issue2.issues }, issue2.path);
|
|
5095
5095
|
} else {
|
|
5096
|
-
const fullpath = [...
|
|
5096
|
+
const fullpath = [...path4, ...issue2.path];
|
|
5097
5097
|
if (fullpath.length === 0) {
|
|
5098
5098
|
result.errors.push(mapper(issue2));
|
|
5099
5099
|
continue;
|
|
@@ -5125,8 +5125,8 @@ function treeifyError(error45, _mapper) {
|
|
|
5125
5125
|
}
|
|
5126
5126
|
function toDotPath(_path) {
|
|
5127
5127
|
const segs = [];
|
|
5128
|
-
const
|
|
5129
|
-
for (const seg of
|
|
5128
|
+
const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5129
|
+
for (const seg of path4) {
|
|
5130
5130
|
if (typeof seg === "number")
|
|
5131
5131
|
segs.push(`[${seg}]`);
|
|
5132
5132
|
else if (typeof seg === "symbol")
|
|
@@ -16259,6 +16259,7 @@ var GbkToolError = class extends Error {
|
|
|
16259
16259
|
function createGbkError(code, message, cause) {
|
|
16260
16260
|
return new GbkToolError(code, message, cause);
|
|
16261
16261
|
}
|
|
16262
|
+
var createTextError = createGbkError;
|
|
16262
16263
|
|
|
16263
16264
|
// src/lib/path-sandbox.ts
|
|
16264
16265
|
import fs from "fs/promises";
|
|
@@ -17119,6 +17120,17 @@ and avoid false matches. Scoped edits also improve performance on very large fil
|
|
|
17119
17120
|
},
|
|
17120
17121
|
async execute(args, context) {
|
|
17121
17122
|
const result = await replaceGbkFileText({ ...args, context });
|
|
17123
|
+
context.metadata({
|
|
17124
|
+
title: `GBK \u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}`,
|
|
17125
|
+
metadata: {
|
|
17126
|
+
filePath: result.filePath,
|
|
17127
|
+
encoding: result.encoding,
|
|
17128
|
+
replacements: result.replacements,
|
|
17129
|
+
occurrencesBefore: result.occurrencesBefore,
|
|
17130
|
+
bytesRead: result.bytesRead,
|
|
17131
|
+
bytesWritten: result.bytesWritten
|
|
17132
|
+
}
|
|
17133
|
+
});
|
|
17122
17134
|
return JSON.stringify(result, null, 2);
|
|
17123
17135
|
}
|
|
17124
17136
|
});
|
|
@@ -17153,6 +17165,19 @@ Workflow when truncated=true:
|
|
|
17153
17165
|
},
|
|
17154
17166
|
async execute(args, context) {
|
|
17155
17167
|
const result = await readGbkFile({ ...args, context });
|
|
17168
|
+
const lineRange = `${result.startLine}-${result.endLine}`;
|
|
17169
|
+
context.metadata({
|
|
17170
|
+
title: `GBK \u8BFB\u53D6 ${result.encoding.toUpperCase()} ${lineRange}`,
|
|
17171
|
+
metadata: {
|
|
17172
|
+
filePath: result.filePath,
|
|
17173
|
+
encoding: result.encoding,
|
|
17174
|
+
lineRange,
|
|
17175
|
+
totalLines: result.totalLines,
|
|
17176
|
+
newlineStyle: result.newlineStyle,
|
|
17177
|
+
truncated: result.truncated,
|
|
17178
|
+
tail: result.tail
|
|
17179
|
+
}
|
|
17180
|
+
});
|
|
17156
17181
|
return JSON.stringify(result, null, 2);
|
|
17157
17182
|
}
|
|
17158
17183
|
});
|
|
@@ -17180,6 +17205,15 @@ Workflow for large files:
|
|
|
17180
17205
|
},
|
|
17181
17206
|
async execute(args, context) {
|
|
17182
17207
|
const result = await searchGbkFile({ ...args, context });
|
|
17208
|
+
context.metadata({
|
|
17209
|
+
title: `GBK \u641C\u7D22 ${result.encoding.toUpperCase()} ${result.matchCount} \u547D\u4E2D`,
|
|
17210
|
+
metadata: {
|
|
17211
|
+
filePath: result.filePath,
|
|
17212
|
+
encoding: result.encoding,
|
|
17213
|
+
totalLines: result.totalLines,
|
|
17214
|
+
matchCount: result.matchCount
|
|
17215
|
+
}
|
|
17216
|
+
});
|
|
17183
17217
|
return JSON.stringify(result, null, 2);
|
|
17184
17218
|
}
|
|
17185
17219
|
});
|
|
@@ -17208,6 +17242,683 @@ var gbk_write_default = tool({
|
|
|
17208
17242
|
},
|
|
17209
17243
|
async execute(args, context) {
|
|
17210
17244
|
const result = await writeGbkFile({ ...args, context });
|
|
17245
|
+
const action = result.appended ? "\u8FFD\u52A0" : result.overwritten ? "\u8986\u76D6" : "\u5199\u5165";
|
|
17246
|
+
context.metadata({
|
|
17247
|
+
title: `GBK ${action} ${result.encoding.toUpperCase()}`,
|
|
17248
|
+
metadata: {
|
|
17249
|
+
filePath: result.filePath,
|
|
17250
|
+
encoding: result.encoding,
|
|
17251
|
+
created: result.created,
|
|
17252
|
+
overwritten: result.overwritten,
|
|
17253
|
+
appended: result.appended ?? false,
|
|
17254
|
+
bytesWritten: result.bytesWritten
|
|
17255
|
+
}
|
|
17256
|
+
});
|
|
17257
|
+
return JSON.stringify(result, null, 2);
|
|
17258
|
+
}
|
|
17259
|
+
});
|
|
17260
|
+
|
|
17261
|
+
// src/plugin/text-guidance.ts
|
|
17262
|
+
var TEXT_TOOL_SYSTEM_MARKER = "[opencode-gbk-tools:text-rules]";
|
|
17263
|
+
var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
17264
|
+
TEXT_TOOL_SYSTEM_MARKER,
|
|
17265
|
+
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
17266
|
+
"- \u5904\u7406\u6587\u672C\u6587\u4EF6\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 text_read\u3001text_write\u3001text_edit\u3002",
|
|
17267
|
+
"- 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",
|
|
17268
|
+
"- \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",
|
|
17269
|
+
"- \u5904\u7406\u660E\u786E\u7684 GBK/GB18030 \u6587\u4EF6\u65F6\uFF0C\u4E5F\u53EF\u7EE7\u7EED\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\u3002"
|
|
17270
|
+
].join("\n");
|
|
17271
|
+
function appendTextToolSystemPrompt(system) {
|
|
17272
|
+
if (system.some((item) => item.includes(TEXT_TOOL_SYSTEM_MARKER))) {
|
|
17273
|
+
return;
|
|
17274
|
+
}
|
|
17275
|
+
system.push(TEXT_TOOL_SYSTEM_PROMPT);
|
|
17276
|
+
}
|
|
17277
|
+
|
|
17278
|
+
// src/lib/text-file.ts
|
|
17279
|
+
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
17280
|
+
import fs3 from "fs/promises";
|
|
17281
|
+
import path3 from "path";
|
|
17282
|
+
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
17283
|
+
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
17284
|
+
function isSupportedEncoding(value) {
|
|
17285
|
+
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
17286
|
+
}
|
|
17287
|
+
function assertTextEncodingSupported(encoding) {
|
|
17288
|
+
if (encoding !== "auto" && !isSupportedEncoding(encoding)) {
|
|
17289
|
+
throw createTextError("GBK_INVALID_ENCODING", `\u4E0D\u652F\u6301\u7684\u7F16\u7801: ${encoding}`);
|
|
17290
|
+
}
|
|
17291
|
+
}
|
|
17292
|
+
function decodeUtf16be(buffer) {
|
|
17293
|
+
return import_iconv_lite2.default.decode(buffer, "utf16be");
|
|
17294
|
+
}
|
|
17295
|
+
function encodeText(content, encoding, hasBom = encoding === "utf8-bom") {
|
|
17296
|
+
if (encoding === "utf8") {
|
|
17297
|
+
return Buffer.from(content, "utf8");
|
|
17298
|
+
}
|
|
17299
|
+
if (encoding === "utf8-bom") {
|
|
17300
|
+
return Buffer.concat([Buffer.from([239, 187, 191]), Buffer.from(content, "utf8")]);
|
|
17301
|
+
}
|
|
17302
|
+
if (encoding === "utf16le") {
|
|
17303
|
+
const body = Buffer.from(content, "utf16le");
|
|
17304
|
+
return hasBom ? Buffer.concat([Buffer.from([255, 254]), body]) : body;
|
|
17305
|
+
}
|
|
17306
|
+
if (encoding === "utf16be") {
|
|
17307
|
+
const body = import_iconv_lite2.default.encode(content, "utf16be");
|
|
17308
|
+
return hasBom ? Buffer.concat([Buffer.from([254, 255]), body]) : body;
|
|
17309
|
+
}
|
|
17310
|
+
return import_iconv_lite2.default.encode(content, encoding);
|
|
17311
|
+
}
|
|
17312
|
+
function decodeText(buffer, encoding) {
|
|
17313
|
+
if (encoding === "utf8") {
|
|
17314
|
+
return buffer.toString("utf8");
|
|
17315
|
+
}
|
|
17316
|
+
if (encoding === "utf8-bom") {
|
|
17317
|
+
return buffer.subarray(3).toString("utf8");
|
|
17318
|
+
}
|
|
17319
|
+
if (encoding === "utf16le") {
|
|
17320
|
+
const withoutBom = buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254 ? buffer.subarray(2) : buffer;
|
|
17321
|
+
return withoutBom.toString("utf16le");
|
|
17322
|
+
}
|
|
17323
|
+
if (encoding === "utf16be") {
|
|
17324
|
+
const withoutBom = buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255 ? buffer.subarray(2) : buffer;
|
|
17325
|
+
return decodeUtf16be(withoutBom);
|
|
17326
|
+
}
|
|
17327
|
+
return import_iconv_lite2.default.decode(buffer, encoding);
|
|
17328
|
+
}
|
|
17329
|
+
function detectBom(buffer) {
|
|
17330
|
+
if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
|
|
17331
|
+
return {
|
|
17332
|
+
requestedEncoding: "auto",
|
|
17333
|
+
detectedEncoding: "utf8-bom",
|
|
17334
|
+
confidence: "high",
|
|
17335
|
+
hasBom: true
|
|
17336
|
+
};
|
|
17337
|
+
}
|
|
17338
|
+
if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
|
|
17339
|
+
return {
|
|
17340
|
+
requestedEncoding: "auto",
|
|
17341
|
+
detectedEncoding: "utf16le",
|
|
17342
|
+
confidence: "high",
|
|
17343
|
+
hasBom: true
|
|
17344
|
+
};
|
|
17345
|
+
}
|
|
17346
|
+
if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
|
|
17347
|
+
return {
|
|
17348
|
+
requestedEncoding: "auto",
|
|
17349
|
+
detectedEncoding: "utf16be",
|
|
17350
|
+
confidence: "high",
|
|
17351
|
+
hasBom: true
|
|
17352
|
+
};
|
|
17353
|
+
}
|
|
17354
|
+
return null;
|
|
17355
|
+
}
|
|
17356
|
+
function isValidUtf8(buffer) {
|
|
17357
|
+
try {
|
|
17358
|
+
UTF8_DECODER.decode(buffer);
|
|
17359
|
+
return true;
|
|
17360
|
+
} catch {
|
|
17361
|
+
return false;
|
|
17362
|
+
}
|
|
17363
|
+
}
|
|
17364
|
+
function hasUtf16Pattern(buffer) {
|
|
17365
|
+
const sample = buffer.subarray(0, Math.min(buffer.length, 512));
|
|
17366
|
+
let evenZeros = 0;
|
|
17367
|
+
let oddZeros = 0;
|
|
17368
|
+
for (let index = 0; index < sample.length; index += 1) {
|
|
17369
|
+
if (sample[index] === 0) {
|
|
17370
|
+
if (index % 2 === 0) {
|
|
17371
|
+
evenZeros += 1;
|
|
17372
|
+
} else {
|
|
17373
|
+
oddZeros += 1;
|
|
17374
|
+
}
|
|
17375
|
+
}
|
|
17376
|
+
}
|
|
17377
|
+
return {
|
|
17378
|
+
utf16be: evenZeros >= 2 && evenZeros >= oddZeros * 2,
|
|
17379
|
+
utf16le: oddZeros >= 2 && oddZeros >= evenZeros * 2
|
|
17380
|
+
};
|
|
17381
|
+
}
|
|
17382
|
+
function looksLossyRoundTrip(buffer, encoding) {
|
|
17383
|
+
const decoded = decodeText(buffer, encoding);
|
|
17384
|
+
const reencoded = encodeText(decoded, encoding, false);
|
|
17385
|
+
return !buffer.equals(reencoded);
|
|
17386
|
+
}
|
|
17387
|
+
function assertLikelyTextBuffer(buffer) {
|
|
17388
|
+
if (!buffer.includes(0)) {
|
|
17389
|
+
return;
|
|
17390
|
+
}
|
|
17391
|
+
const bom = detectBom(buffer);
|
|
17392
|
+
if (bom?.detectedEncoding === "utf16le" || bom?.detectedEncoding === "utf16be") {
|
|
17393
|
+
return;
|
|
17394
|
+
}
|
|
17395
|
+
const utf16Pattern = hasUtf16Pattern(buffer);
|
|
17396
|
+
if (utf16Pattern.utf16be || utf16Pattern.utf16le) {
|
|
17397
|
+
return;
|
|
17398
|
+
}
|
|
17399
|
+
throw createTextError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309\u6587\u672C\u5904\u7406");
|
|
17400
|
+
}
|
|
17401
|
+
function getNearestContext2(content, oldString) {
|
|
17402
|
+
const lines = content.split(/\r?\n/);
|
|
17403
|
+
const oldLines = oldString.replace(/\r\n/g, "\n").split("\n").filter(Boolean);
|
|
17404
|
+
const firstToken = oldLines[0] || oldString.trim();
|
|
17405
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
17406
|
+
if (lines[index].includes(firstToken)) {
|
|
17407
|
+
return lines.slice(index, index + 4).join("\n");
|
|
17408
|
+
}
|
|
17409
|
+
}
|
|
17410
|
+
return lines.slice(0, 4).join("\n");
|
|
17411
|
+
}
|
|
17412
|
+
function buildNoMatchMessage2(content, oldString) {
|
|
17413
|
+
return [
|
|
17414
|
+
"\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
|
|
17415
|
+
"oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\u3002",
|
|
17416
|
+
"\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFF1A",
|
|
17417
|
+
getNearestContext2(content, oldString)
|
|
17418
|
+
].join("\n");
|
|
17419
|
+
}
|
|
17420
|
+
function detectTextEncodingFromBuffer(buffer, requestedEncoding = "auto") {
|
|
17421
|
+
if (requestedEncoding !== "auto") {
|
|
17422
|
+
assertTextEncodingSupported(requestedEncoding);
|
|
17423
|
+
return {
|
|
17424
|
+
requestedEncoding,
|
|
17425
|
+
detectedEncoding: requestedEncoding,
|
|
17426
|
+
confidence: "high",
|
|
17427
|
+
hasBom: requestedEncoding === "utf8-bom" || requestedEncoding === "utf16le" || requestedEncoding === "utf16be"
|
|
17428
|
+
};
|
|
17429
|
+
}
|
|
17430
|
+
const bom = detectBom(buffer);
|
|
17431
|
+
if (bom) {
|
|
17432
|
+
return bom;
|
|
17433
|
+
}
|
|
17434
|
+
const utf16Pattern = hasUtf16Pattern(buffer);
|
|
17435
|
+
if (utf16Pattern.utf16be) {
|
|
17436
|
+
return {
|
|
17437
|
+
requestedEncoding,
|
|
17438
|
+
detectedEncoding: "utf16be",
|
|
17439
|
+
confidence: "medium",
|
|
17440
|
+
hasBom: false
|
|
17441
|
+
};
|
|
17442
|
+
}
|
|
17443
|
+
if (utf16Pattern.utf16le) {
|
|
17444
|
+
return {
|
|
17445
|
+
requestedEncoding,
|
|
17446
|
+
detectedEncoding: "utf16le",
|
|
17447
|
+
confidence: "medium",
|
|
17448
|
+
hasBom: false
|
|
17449
|
+
};
|
|
17450
|
+
}
|
|
17451
|
+
if (isValidUtf8(buffer)) {
|
|
17452
|
+
return {
|
|
17453
|
+
requestedEncoding,
|
|
17454
|
+
detectedEncoding: "utf8",
|
|
17455
|
+
confidence: "high",
|
|
17456
|
+
hasBom: false
|
|
17457
|
+
};
|
|
17458
|
+
}
|
|
17459
|
+
const gbkLossy = looksLossyRoundTrip(buffer, "gbk");
|
|
17460
|
+
const gb18030Lossy = looksLossyRoundTrip(buffer, "gb18030");
|
|
17461
|
+
if (gb18030Lossy && gbkLossy) {
|
|
17462
|
+
throw createTextError("TEXT_UNKNOWN_ENCODING", "\u65E0\u6CD5\u53EF\u9760\u8BC6\u522B\u6587\u4EF6\u7F16\u7801\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding");
|
|
17463
|
+
}
|
|
17464
|
+
return {
|
|
17465
|
+
requestedEncoding,
|
|
17466
|
+
detectedEncoding: gbkLossy ? "gb18030" : "gbk",
|
|
17467
|
+
confidence: gbkLossy || gb18030Lossy ? "medium" : "low",
|
|
17468
|
+
hasBom: false
|
|
17469
|
+
};
|
|
17470
|
+
}
|
|
17471
|
+
async function resolveReadableTextFile(input) {
|
|
17472
|
+
const { candidatePath } = await assertPathAllowed(input.filePath, input.context, input.allowExternal ?? false);
|
|
17473
|
+
let stat;
|
|
17474
|
+
try {
|
|
17475
|
+
stat = await fs3.stat(candidatePath);
|
|
17476
|
+
} catch (error45) {
|
|
17477
|
+
throw createTextError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\u5728: ${candidatePath}`, error45);
|
|
17478
|
+
}
|
|
17479
|
+
if (stat.isDirectory()) {
|
|
17480
|
+
throw createTextError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\u5F55: ${candidatePath}`);
|
|
17481
|
+
}
|
|
17482
|
+
return { filePath: candidatePath, stat };
|
|
17483
|
+
}
|
|
17484
|
+
async function readWholeTextFile(input) {
|
|
17485
|
+
const resolved = await resolveReadableTextFile(input);
|
|
17486
|
+
try {
|
|
17487
|
+
const buffer = await fs3.readFile(resolved.filePath);
|
|
17488
|
+
assertLikelyTextBuffer(buffer);
|
|
17489
|
+
const detected = detectTextEncodingFromBuffer(buffer, input.encoding ?? "auto");
|
|
17490
|
+
const content = decodeText(buffer, detected.detectedEncoding);
|
|
17491
|
+
return {
|
|
17492
|
+
filePath: resolved.filePath,
|
|
17493
|
+
bytesRead: buffer.byteLength,
|
|
17494
|
+
fileSize: buffer.byteLength,
|
|
17495
|
+
content,
|
|
17496
|
+
encoding: detected.detectedEncoding,
|
|
17497
|
+
requestedEncoding: detected.requestedEncoding,
|
|
17498
|
+
detectedEncoding: detected.detectedEncoding,
|
|
17499
|
+
confidence: detected.confidence,
|
|
17500
|
+
hasBom: detected.hasBom,
|
|
17501
|
+
newlineStyle: detectNewlineStyle(content)
|
|
17502
|
+
};
|
|
17503
|
+
} catch (error45) {
|
|
17504
|
+
if (error45 instanceof Error && "code" in error45) {
|
|
17505
|
+
throw error45;
|
|
17506
|
+
}
|
|
17507
|
+
throw createTextError("GBK_IO_ERROR", `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${resolved.filePath}`, error45);
|
|
17508
|
+
}
|
|
17509
|
+
}
|
|
17510
|
+
function collectTailLines2(text, limit) {
|
|
17511
|
+
assertPositiveInteger(limit, "limit");
|
|
17512
|
+
const lines = text.split(/\r?\n/);
|
|
17513
|
+
const totalLines = lines.length;
|
|
17514
|
+
const startLine = Math.max(1, totalLines - limit + 1);
|
|
17515
|
+
const selected = lines.slice(startLine - 1);
|
|
17516
|
+
return {
|
|
17517
|
+
startLine,
|
|
17518
|
+
endLine: totalLines,
|
|
17519
|
+
totalLines,
|
|
17520
|
+
content: selected.map((line, index) => `${startLine + index}: ${line}`).join("\n"),
|
|
17521
|
+
truncated: startLine > 1,
|
|
17522
|
+
tail: true
|
|
17523
|
+
};
|
|
17524
|
+
}
|
|
17525
|
+
function normalizeNewlines2(text) {
|
|
17526
|
+
return text.replace(/\r\n/g, "\n");
|
|
17527
|
+
}
|
|
17528
|
+
function applyLineRange2(text, startLine, endLine) {
|
|
17529
|
+
if (startLine === void 0 && endLine === void 0) {
|
|
17530
|
+
return {
|
|
17531
|
+
selectedText: text,
|
|
17532
|
+
rangeStart: 0,
|
|
17533
|
+
rangeEnd: text.length
|
|
17534
|
+
};
|
|
17535
|
+
}
|
|
17536
|
+
const lines = text.split(/\r?\n/);
|
|
17537
|
+
const actualStartLine = startLine ?? 1;
|
|
17538
|
+
const actualEndLine = endLine ?? lines.length;
|
|
17539
|
+
assertPositiveInteger(actualStartLine, "startLine");
|
|
17540
|
+
assertPositiveInteger(actualEndLine, "endLine");
|
|
17541
|
+
if (actualEndLine < actualStartLine) {
|
|
17542
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "endLine \u4E0D\u80FD\u5C0F\u4E8E startLine");
|
|
17543
|
+
}
|
|
17544
|
+
let cursor = 0;
|
|
17545
|
+
let rangeStart = 0;
|
|
17546
|
+
let rangeEnd = text.length;
|
|
17547
|
+
for (let lineNumber = 1; lineNumber <= lines.length; lineNumber += 1) {
|
|
17548
|
+
if (lineNumber === actualStartLine) {
|
|
17549
|
+
rangeStart = cursor;
|
|
17550
|
+
}
|
|
17551
|
+
cursor += lines[lineNumber - 1].length;
|
|
17552
|
+
if (lineNumber < lines.length) {
|
|
17553
|
+
const newlineLength = text.startsWith("\r\n", cursor) ? 2 : 1;
|
|
17554
|
+
cursor += newlineLength;
|
|
17555
|
+
}
|
|
17556
|
+
if (lineNumber === actualEndLine) {
|
|
17557
|
+
rangeEnd = cursor;
|
|
17558
|
+
break;
|
|
17559
|
+
}
|
|
17560
|
+
}
|
|
17561
|
+
return {
|
|
17562
|
+
selectedText: text.slice(rangeStart, rangeEnd),
|
|
17563
|
+
rangeStart,
|
|
17564
|
+
rangeEnd
|
|
17565
|
+
};
|
|
17566
|
+
}
|
|
17567
|
+
function applyAnchors2(text, startAnchor, endAnchor) {
|
|
17568
|
+
if (!startAnchor && !endAnchor) {
|
|
17569
|
+
return {
|
|
17570
|
+
selectedText: text,
|
|
17571
|
+
rangeStart: 0,
|
|
17572
|
+
rangeEnd: text.length
|
|
17573
|
+
};
|
|
17574
|
+
}
|
|
17575
|
+
let rangeStart = 0;
|
|
17576
|
+
let rangeEnd = text.length;
|
|
17577
|
+
if (startAnchor) {
|
|
17578
|
+
const found = text.indexOf(startAnchor);
|
|
17579
|
+
if (found === -1) {
|
|
17580
|
+
throw createTextError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\u70B9: ${startAnchor}`);
|
|
17581
|
+
}
|
|
17582
|
+
rangeStart = found + startAnchor.length;
|
|
17583
|
+
}
|
|
17584
|
+
if (endAnchor) {
|
|
17585
|
+
const found = text.indexOf(endAnchor, rangeStart);
|
|
17586
|
+
if (found === -1) {
|
|
17587
|
+
throw createTextError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\u70B9: ${endAnchor}`);
|
|
17588
|
+
}
|
|
17589
|
+
rangeEnd = found;
|
|
17590
|
+
}
|
|
17591
|
+
if (rangeEnd < rangeStart) {
|
|
17592
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u951A\u70B9\u8303\u56F4\u65E0\u6548");
|
|
17593
|
+
}
|
|
17594
|
+
return {
|
|
17595
|
+
selectedText: text.slice(rangeStart, rangeEnd),
|
|
17596
|
+
rangeStart,
|
|
17597
|
+
rangeEnd
|
|
17598
|
+
};
|
|
17599
|
+
}
|
|
17600
|
+
function resolveEditScope2(text, input) {
|
|
17601
|
+
const anchored = applyAnchors2(text, input.startAnchor, input.endAnchor);
|
|
17602
|
+
const lineRanged = applyLineRange2(anchored.selectedText, input.startLine, input.endLine);
|
|
17603
|
+
return {
|
|
17604
|
+
selectedText: lineRanged.selectedText,
|
|
17605
|
+
rangeStart: anchored.rangeStart + lineRanged.rangeStart,
|
|
17606
|
+
rangeEnd: anchored.rangeStart + lineRanged.rangeEnd
|
|
17607
|
+
};
|
|
17608
|
+
}
|
|
17609
|
+
function alignTextToNewlineStyle(text, newlineStyle) {
|
|
17610
|
+
const normalized = text.replace(/\r\n/g, "\n");
|
|
17611
|
+
if (newlineStyle === "crlf") {
|
|
17612
|
+
return normalized.replace(/\n/g, "\r\n");
|
|
17613
|
+
}
|
|
17614
|
+
if (newlineStyle === "lf") {
|
|
17615
|
+
return normalized;
|
|
17616
|
+
}
|
|
17617
|
+
return text;
|
|
17618
|
+
}
|
|
17619
|
+
function ensureLossless(input, encoding, hasBom = encoding === "utf8-bom") {
|
|
17620
|
+
const buffer = encodeText(input, encoding, hasBom);
|
|
17621
|
+
const roundTrip = decodeText(buffer, encoding);
|
|
17622
|
+
if (roundTrip !== input) {
|
|
17623
|
+
throw createTextError("TEXT_ENCODING_LOSSY", `\u5185\u5BB9\u65E0\u6CD5\u65E0\u635F\u5199\u5165\u7F16\u7801 ${encoding}`);
|
|
17624
|
+
}
|
|
17625
|
+
}
|
|
17626
|
+
async function ensureParentDirectory(parent, createDirectories) {
|
|
17627
|
+
try {
|
|
17628
|
+
const parentStat = await fs3.stat(parent);
|
|
17629
|
+
if (!parentStat.isDirectory()) {
|
|
17630
|
+
throw createTextError("GBK_PARENT_DIRECTORY_MISSING", `\u7236\u76EE\u5F55\u4E0D\u5B58\u5728: ${parent}`);
|
|
17631
|
+
}
|
|
17632
|
+
} catch (error45) {
|
|
17633
|
+
if (error45 instanceof Error && "code" in error45 && error45.code === "ENOENT") {
|
|
17634
|
+
if (!createDirectories) {
|
|
17635
|
+
throw createTextError("GBK_PARENT_DIRECTORY_MISSING", `\u7236\u76EE\u5F55\u4E0D\u5B58\u5728: ${parent}`);
|
|
17636
|
+
}
|
|
17637
|
+
await fs3.mkdir(parent, { recursive: true });
|
|
17638
|
+
return;
|
|
17639
|
+
}
|
|
17640
|
+
throw error45;
|
|
17641
|
+
}
|
|
17642
|
+
}
|
|
17643
|
+
async function readTextFile(input) {
|
|
17644
|
+
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
17645
|
+
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
17646
|
+
const tail = input.tail ?? false;
|
|
17647
|
+
const loaded = await readWholeTextFile(input);
|
|
17648
|
+
const lineWindow = tail ? collectTailLines2(loaded.content, limit) : {
|
|
17649
|
+
...splitLinesWithNumbers(loaded.content, offset, limit),
|
|
17650
|
+
tail: false,
|
|
17651
|
+
truncated: false
|
|
17652
|
+
};
|
|
17653
|
+
if (!tail) {
|
|
17654
|
+
lineWindow.truncated = lineWindow.endLine < lineWindow.totalLines;
|
|
17655
|
+
}
|
|
17656
|
+
return {
|
|
17657
|
+
filePath: loaded.filePath,
|
|
17658
|
+
encoding: loaded.encoding,
|
|
17659
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
17660
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
17661
|
+
confidence: loaded.confidence,
|
|
17662
|
+
hasBom: loaded.hasBom,
|
|
17663
|
+
bytesRead: loaded.bytesRead,
|
|
17664
|
+
fileSize: loaded.fileSize,
|
|
17665
|
+
streamed: false,
|
|
17666
|
+
newlineStyle: loaded.newlineStyle,
|
|
17667
|
+
...lineWindow
|
|
17668
|
+
};
|
|
17669
|
+
}
|
|
17670
|
+
async function writeTextFile(input) {
|
|
17671
|
+
const requestedEncoding = input.encoding ?? "auto";
|
|
17672
|
+
assertTextEncodingSupported(requestedEncoding);
|
|
17673
|
+
const preserveEncoding = input.preserveEncoding ?? true;
|
|
17674
|
+
const preserveNewlineStyle = input.preserveNewlineStyle ?? true;
|
|
17675
|
+
const createDirectories = input.createDirectories ?? true;
|
|
17676
|
+
const overwrite = input.overwrite ?? false;
|
|
17677
|
+
const append = input.append ?? false;
|
|
17678
|
+
const { candidatePath } = await assertPathAllowed(input.filePath, input.context, input.allowExternal ?? false);
|
|
17679
|
+
const parent = path3.dirname(candidatePath);
|
|
17680
|
+
await ensureParentDirectory(parent, createDirectories);
|
|
17681
|
+
let existing = null;
|
|
17682
|
+
try {
|
|
17683
|
+
existing = await readWholeTextFile({
|
|
17684
|
+
filePath: candidatePath,
|
|
17685
|
+
encoding: requestedEncoding,
|
|
17686
|
+
allowExternal: input.allowExternal,
|
|
17687
|
+
context: input.context
|
|
17688
|
+
});
|
|
17689
|
+
} catch (error45) {
|
|
17690
|
+
if (!(error45 instanceof Error && "code" in error45 && error45.code === "GBK_FILE_NOT_FOUND")) {
|
|
17691
|
+
throw error45;
|
|
17692
|
+
}
|
|
17693
|
+
}
|
|
17694
|
+
if (existing && !append && !overwrite) {
|
|
17695
|
+
throw createTextError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728: ${candidatePath}`);
|
|
17696
|
+
}
|
|
17697
|
+
const targetEncoding = existing && preserveEncoding ? existing.encoding : requestedEncoding === "auto" ? "utf8" : requestedEncoding;
|
|
17698
|
+
const targetHasBom = targetEncoding === "utf8-bom" ? true : existing && preserveEncoding ? existing.hasBom : targetEncoding === "utf16le" || targetEncoding === "utf16be" ? true : false;
|
|
17699
|
+
const baseContent = append ? existing?.content ?? "" : "";
|
|
17700
|
+
const rawContent = `${baseContent}${input.content}`;
|
|
17701
|
+
const outputContent = existing && preserveNewlineStyle ? alignTextToNewlineStyle(rawContent, existing.newlineStyle) : rawContent;
|
|
17702
|
+
ensureLossless(outputContent, targetEncoding, targetHasBom);
|
|
17703
|
+
const buffer = encodeText(outputContent, targetEncoding, targetHasBom);
|
|
17704
|
+
await fs3.writeFile(candidatePath, buffer);
|
|
17705
|
+
return {
|
|
17706
|
+
filePath: candidatePath,
|
|
17707
|
+
encoding: targetEncoding,
|
|
17708
|
+
requestedEncoding,
|
|
17709
|
+
detectedEncoding: existing?.encoding ?? null,
|
|
17710
|
+
confidence: existing?.confidence ?? null,
|
|
17711
|
+
hasBom: targetHasBom,
|
|
17712
|
+
bytesWritten: buffer.byteLength,
|
|
17713
|
+
created: !existing,
|
|
17714
|
+
overwritten: Boolean(existing) && !append,
|
|
17715
|
+
appended: append,
|
|
17716
|
+
newlineStyle: detectNewlineStyle(outputContent)
|
|
17717
|
+
};
|
|
17718
|
+
}
|
|
17719
|
+
async function replaceTextFileText(input) {
|
|
17720
|
+
const normalizedInput = {
|
|
17721
|
+
...input,
|
|
17722
|
+
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
17723
|
+
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
17724
|
+
};
|
|
17725
|
+
if (input.oldString.length === 0) {
|
|
17726
|
+
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17727
|
+
}
|
|
17728
|
+
const loaded = await readWholeTextFile({
|
|
17729
|
+
filePath: input.filePath,
|
|
17730
|
+
encoding: input.encoding ?? "auto",
|
|
17731
|
+
allowExternal: input.allowExternal,
|
|
17732
|
+
context: input.context
|
|
17733
|
+
});
|
|
17734
|
+
const scope = resolveEditScope2(loaded.content, normalizedInput);
|
|
17735
|
+
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
17736
|
+
const preserveEncoding = normalizedInput.preserveEncoding ?? true;
|
|
17737
|
+
const requestedEncoding = normalizedInput.encoding ?? "auto";
|
|
17738
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, normalizedInput.oldString);
|
|
17739
|
+
if (replaceAll) {
|
|
17740
|
+
if (occurrencesBefore === 0) {
|
|
17741
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText, normalizedInput.oldString));
|
|
17742
|
+
}
|
|
17743
|
+
} else if (occurrencesBefore === 0) {
|
|
17744
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText, normalizedInput.oldString));
|
|
17745
|
+
} else if (occurrencesBefore > 1) {
|
|
17746
|
+
throw createTextError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${normalizedInput.oldString}`);
|
|
17747
|
+
}
|
|
17748
|
+
const alignedNewString = normalizedInput.preserveNewlineStyle === false ? normalizedInput.newString : alignTextToNewlineStyle(normalizedInput.newString, loaded.newlineStyle);
|
|
17749
|
+
const replaced = replaceAll ? scope.selectedText.split(normalizedInput.oldString).join(alignedNewString) : scope.selectedText.replace(normalizedInput.oldString, alignedNewString);
|
|
17750
|
+
const outputText = `${loaded.content.slice(0, scope.rangeStart)}${replaced}${loaded.content.slice(scope.rangeEnd)}`;
|
|
17751
|
+
const targetEncoding = preserveEncoding || requestedEncoding === "auto" ? loaded.encoding : requestedEncoding;
|
|
17752
|
+
const targetHasBom = preserveEncoding ? loaded.hasBom : targetEncoding === "utf8-bom" || targetEncoding === "utf16le" || targetEncoding === "utf16be";
|
|
17753
|
+
ensureLossless(outputText, targetEncoding, targetHasBom);
|
|
17754
|
+
const buffer = encodeText(outputText, targetEncoding, targetHasBom);
|
|
17755
|
+
await fs3.writeFile(loaded.filePath, buffer);
|
|
17756
|
+
return {
|
|
17757
|
+
filePath: loaded.filePath,
|
|
17758
|
+
encoding: targetEncoding,
|
|
17759
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
17760
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
17761
|
+
confidence: loaded.confidence,
|
|
17762
|
+
hasBom: targetHasBom,
|
|
17763
|
+
replacements: replaceAll ? occurrencesBefore : 1,
|
|
17764
|
+
occurrencesBefore,
|
|
17765
|
+
bytesRead: loaded.bytesRead,
|
|
17766
|
+
bytesWritten: buffer.byteLength,
|
|
17767
|
+
newlineStyle: detectNewlineStyle(outputText)
|
|
17768
|
+
};
|
|
17769
|
+
}
|
|
17770
|
+
async function createTextDiffPreview(input) {
|
|
17771
|
+
const loaded = await readWholeTextFile({
|
|
17772
|
+
filePath: input.filePath,
|
|
17773
|
+
encoding: input.encoding ?? "auto",
|
|
17774
|
+
allowExternal: input.allowExternal,
|
|
17775
|
+
context: input.context
|
|
17776
|
+
});
|
|
17777
|
+
const scope = resolveEditScope2(loaded.content, input);
|
|
17778
|
+
const alignedNewString = alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
17779
|
+
const replaced = input.replaceAll ?? false ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
17780
|
+
const beforeLines = normalizeNewlines2(scope.selectedText).split("\n");
|
|
17781
|
+
const afterLines = normalizeNewlines2(replaced).split("\n");
|
|
17782
|
+
const maxLines = Math.max(beforeLines.length, afterLines.length);
|
|
17783
|
+
const lines = [
|
|
17784
|
+
`--- ${path3.basename(loaded.filePath)} (${loaded.encoding})`,
|
|
17785
|
+
`+++ ${path3.basename(loaded.filePath)} (${loaded.encoding})`
|
|
17786
|
+
];
|
|
17787
|
+
for (let index = 0; index < maxLines; index += 1) {
|
|
17788
|
+
const before = beforeLines[index];
|
|
17789
|
+
const after = afterLines[index];
|
|
17790
|
+
if (before !== void 0 && after !== void 0 && before === after) {
|
|
17791
|
+
lines.push(` ${before}`);
|
|
17792
|
+
continue;
|
|
17793
|
+
}
|
|
17794
|
+
if (before !== void 0) {
|
|
17795
|
+
lines.push(`-${before}`);
|
|
17796
|
+
}
|
|
17797
|
+
if (after !== void 0) {
|
|
17798
|
+
lines.push(`+${after}`);
|
|
17799
|
+
}
|
|
17800
|
+
}
|
|
17801
|
+
return {
|
|
17802
|
+
filePath: loaded.filePath,
|
|
17803
|
+
encoding: loaded.encoding,
|
|
17804
|
+
preview: lines.join("\n")
|
|
17805
|
+
};
|
|
17806
|
+
}
|
|
17807
|
+
|
|
17808
|
+
// src/tools/text_edit.ts
|
|
17809
|
+
var text_edit_default = tool({
|
|
17810
|
+
description: `Edit text files with automatic encoding detection and preservation.
|
|
17811
|
+
|
|
17812
|
+
- Existing files keep their original encoding and BOM by default.
|
|
17813
|
+
- Existing newline style is preserved by default.
|
|
17814
|
+
- Use startLine/endLine or anchors to narrow the edit scope.`,
|
|
17815
|
+
args: {
|
|
17816
|
+
filePath: tool.schema.string().describe("Target file path"),
|
|
17817
|
+
oldString: tool.schema.string().describe("Exact text to replace"),
|
|
17818
|
+
newString: tool.schema.string().describe("Replacement text"),
|
|
17819
|
+
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
|
|
17820
|
+
startLine: tool.schema.number().int().positive().optional().describe("Start line for scoped edit"),
|
|
17821
|
+
endLine: tool.schema.number().int().positive().optional().describe("End line for scoped edit"),
|
|
17822
|
+
startAnchor: tool.schema.string().optional().describe("Start anchor for scoped edit"),
|
|
17823
|
+
endAnchor: tool.schema.string().optional().describe("End anchor for scoped edit"),
|
|
17824
|
+
encoding: tool.schema.enum(["auto", "utf8", "utf8-bom", "utf16le", "utf16be", "gbk", "gb18030"]).optional().describe("Encoding override, default auto"),
|
|
17825
|
+
preserveEncoding: tool.schema.boolean().optional().describe("Preserve detected file encoding"),
|
|
17826
|
+
preserveNewlineStyle: tool.schema.boolean().optional().describe("Preserve detected file newline style"),
|
|
17827
|
+
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
|
17828
|
+
},
|
|
17829
|
+
async execute(args, context) {
|
|
17830
|
+
const result = await replaceTextFileText({ ...args, context });
|
|
17831
|
+
const preview = await createTextDiffPreview({ ...args, context });
|
|
17832
|
+
context.metadata({
|
|
17833
|
+
title: `\u6587\u672C\u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}`,
|
|
17834
|
+
metadata: {
|
|
17835
|
+
filePath: result.filePath,
|
|
17836
|
+
encoding: result.encoding,
|
|
17837
|
+
requestedEncoding: result.requestedEncoding,
|
|
17838
|
+
confidence: result.confidence,
|
|
17839
|
+
hasBom: result.hasBom,
|
|
17840
|
+
replacements: result.replacements,
|
|
17841
|
+
occurrencesBefore: result.occurrencesBefore,
|
|
17842
|
+
newlineStyle: result.newlineStyle,
|
|
17843
|
+
diffPreview: preview.preview
|
|
17844
|
+
}
|
|
17845
|
+
});
|
|
17846
|
+
return JSON.stringify(result, null, 2);
|
|
17847
|
+
}
|
|
17848
|
+
});
|
|
17849
|
+
|
|
17850
|
+
// src/tools/text_read.ts
|
|
17851
|
+
var text_read_default = tool({
|
|
17852
|
+
description: `Read text files with automatic encoding detection and preservation.
|
|
17853
|
+
|
|
17854
|
+
- Default encoding=auto detects the existing file encoding.
|
|
17855
|
+
- Supported encodings: utf8, utf8-bom, utf16le, utf16be, gbk, gb18030.
|
|
17856
|
+
- Returns line-numbered content and detected encoding metadata.`,
|
|
17857
|
+
args: {
|
|
17858
|
+
filePath: tool.schema.string().describe("Target file path"),
|
|
17859
|
+
offset: tool.schema.number().int().positive().optional().describe("1-based start line"),
|
|
17860
|
+
limit: tool.schema.number().int().positive().optional().describe("Maximum lines to return"),
|
|
17861
|
+
tail: tool.schema.boolean().optional().describe("Read from file end instead of start"),
|
|
17862
|
+
encoding: tool.schema.enum(["auto", "utf8", "utf8-bom", "utf16le", "utf16be", "gbk", "gb18030"]).optional().describe("Encoding override, default auto"),
|
|
17863
|
+
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
|
17864
|
+
},
|
|
17865
|
+
async execute(args, context) {
|
|
17866
|
+
const result = await readTextFile({ ...args, context });
|
|
17867
|
+
const lineRange = `${result.startLine}-${result.endLine}`;
|
|
17868
|
+
context.metadata({
|
|
17869
|
+
title: `\u6587\u672C\u8BFB\u53D6 ${result.encoding.toUpperCase()} ${lineRange}`,
|
|
17870
|
+
metadata: {
|
|
17871
|
+
filePath: result.filePath,
|
|
17872
|
+
encoding: result.encoding,
|
|
17873
|
+
requestedEncoding: result.requestedEncoding,
|
|
17874
|
+
confidence: result.confidence,
|
|
17875
|
+
hasBom: result.hasBom,
|
|
17876
|
+
lineRange,
|
|
17877
|
+
totalLines: result.totalLines,
|
|
17878
|
+
newlineStyle: result.newlineStyle,
|
|
17879
|
+
truncated: result.truncated
|
|
17880
|
+
}
|
|
17881
|
+
});
|
|
17882
|
+
return JSON.stringify(result, null, 2);
|
|
17883
|
+
}
|
|
17884
|
+
});
|
|
17885
|
+
|
|
17886
|
+
// src/tools/text_write.ts
|
|
17887
|
+
var text_write_default = tool({
|
|
17888
|
+
description: `Write text files while preserving detected encoding and newline style by default.
|
|
17889
|
+
|
|
17890
|
+
- Existing files keep their original encoding when encoding=auto.
|
|
17891
|
+
- New files default to utf8 unless encoding is specified.
|
|
17892
|
+
- Supports append and overwrite modes.`,
|
|
17893
|
+
args: {
|
|
17894
|
+
filePath: tool.schema.string().describe("Target file path"),
|
|
17895
|
+
content: tool.schema.string().describe("Text content to write or append"),
|
|
17896
|
+
encoding: tool.schema.enum(["auto", "utf8", "utf8-bom", "utf16le", "utf16be", "gbk", "gb18030"]).optional().describe("Encoding override, default auto"),
|
|
17897
|
+
createDirectories: tool.schema.boolean().optional().describe("Create parent directories"),
|
|
17898
|
+
overwrite: tool.schema.boolean().optional().describe("Overwrite existing file"),
|
|
17899
|
+
append: tool.schema.boolean().optional().describe("Append content to existing file"),
|
|
17900
|
+
preserveEncoding: tool.schema.boolean().optional().describe("Preserve detected file encoding when file exists"),
|
|
17901
|
+
preserveNewlineStyle: tool.schema.boolean().optional().describe("Preserve detected file newline style when file exists"),
|
|
17902
|
+
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
|
17903
|
+
},
|
|
17904
|
+
async execute(args, context) {
|
|
17905
|
+
const result = await writeTextFile({ ...args, context });
|
|
17906
|
+
const action = result.appended ? "\u8FFD\u52A0" : result.overwritten ? "\u8986\u76D6" : "\u5199\u5165";
|
|
17907
|
+
context.metadata({
|
|
17908
|
+
title: `\u6587\u672C${action} ${result.encoding.toUpperCase()}`,
|
|
17909
|
+
metadata: {
|
|
17910
|
+
filePath: result.filePath,
|
|
17911
|
+
encoding: result.encoding,
|
|
17912
|
+
requestedEncoding: result.requestedEncoding,
|
|
17913
|
+
detectedEncoding: result.detectedEncoding,
|
|
17914
|
+
confidence: result.confidence,
|
|
17915
|
+
hasBom: result.hasBom,
|
|
17916
|
+
created: result.created,
|
|
17917
|
+
overwritten: result.overwritten,
|
|
17918
|
+
appended: result.appended,
|
|
17919
|
+
newlineStyle: result.newlineStyle
|
|
17920
|
+
}
|
|
17921
|
+
});
|
|
17211
17922
|
return JSON.stringify(result, null, 2);
|
|
17212
17923
|
}
|
|
17213
17924
|
});
|
|
@@ -17221,7 +17932,13 @@ var pluginModule = {
|
|
|
17221
17932
|
gbk_read: gbk_read_default,
|
|
17222
17933
|
gbk_write: gbk_write_default,
|
|
17223
17934
|
gbk_edit: gbk_edit_default,
|
|
17224
|
-
gbk_search: gbk_search_default
|
|
17935
|
+
gbk_search: gbk_search_default,
|
|
17936
|
+
text_read: text_read_default,
|
|
17937
|
+
text_write: text_write_default,
|
|
17938
|
+
text_edit: text_edit_default
|
|
17939
|
+
},
|
|
17940
|
+
async "experimental.chat.system.transform"(_input, output) {
|
|
17941
|
+
appendTextToolSystemPrompt(output.system);
|
|
17225
17942
|
}
|
|
17226
17943
|
};
|
|
17227
17944
|
}
|