@kodocagent/cli 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1609 -1204
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9739,9 +9739,9 @@ var require_load = __commonJS({
|
|
|
9739
9739
|
var require_lib3 = __commonJS({
|
|
9740
9740
|
"../../node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/lib/index.js"(exports, module) {
|
|
9741
9741
|
"use strict";
|
|
9742
|
-
function
|
|
9743
|
-
if (!(this instanceof
|
|
9744
|
-
return new
|
|
9742
|
+
function JSZip7() {
|
|
9743
|
+
if (!(this instanceof JSZip7)) {
|
|
9744
|
+
return new JSZip7();
|
|
9745
9745
|
}
|
|
9746
9746
|
if (arguments.length) {
|
|
9747
9747
|
throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
|
|
@@ -9750,7 +9750,7 @@ var require_lib3 = __commonJS({
|
|
|
9750
9750
|
this.comment = null;
|
|
9751
9751
|
this.root = "";
|
|
9752
9752
|
this.clone = function() {
|
|
9753
|
-
var newObj = new
|
|
9753
|
+
var newObj = new JSZip7();
|
|
9754
9754
|
for (var i in this) {
|
|
9755
9755
|
if (typeof this[i] !== "function") {
|
|
9756
9756
|
newObj[i] = this[i];
|
|
@@ -9759,16 +9759,16 @@ var require_lib3 = __commonJS({
|
|
|
9759
9759
|
return newObj;
|
|
9760
9760
|
};
|
|
9761
9761
|
}
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
return new
|
|
9762
|
+
JSZip7.prototype = require_object();
|
|
9763
|
+
JSZip7.prototype.loadAsync = require_load();
|
|
9764
|
+
JSZip7.support = require_support();
|
|
9765
|
+
JSZip7.defaults = require_defaults();
|
|
9766
|
+
JSZip7.version = "3.10.1";
|
|
9767
|
+
JSZip7.loadAsync = function(content, options) {
|
|
9768
|
+
return new JSZip7().loadAsync(content, options);
|
|
9769
9769
|
};
|
|
9770
|
-
|
|
9771
|
-
module.exports =
|
|
9770
|
+
JSZip7.external = require_external();
|
|
9771
|
+
module.exports = JSZip7;
|
|
9772
9772
|
}
|
|
9773
9773
|
});
|
|
9774
9774
|
|
|
@@ -9894,6 +9894,15 @@ function detectPii(text3) {
|
|
|
9894
9894
|
function summarizePii(findings) {
|
|
9895
9895
|
return findings.map((f) => `${f.type} ${f.count}\uAC74`).join(", ");
|
|
9896
9896
|
}
|
|
9897
|
+
function redactText(text3) {
|
|
9898
|
+
if (!text3) return { text: text3 ?? "", findings: [] };
|
|
9899
|
+
let result = text3;
|
|
9900
|
+
const findings = detectPii(text3);
|
|
9901
|
+
for (const { re, mask } of PATTERNS) {
|
|
9902
|
+
result = result.replace(new RegExp(re.source, re.flags), (m) => mask(m));
|
|
9903
|
+
}
|
|
9904
|
+
return { text: result, findings };
|
|
9905
|
+
}
|
|
9897
9906
|
|
|
9898
9907
|
// ../core/dist/index.js
|
|
9899
9908
|
import { stepCountIs, streamText } from "ai";
|
|
@@ -9948,7 +9957,7 @@ var EDIT_SAFETY_SECTION = `## \uD3B8\uC9D1 \uC548\uC804 \uADDC\uCE59
|
|
|
9948
9957
|
4. \uC804\uBB38 \uC6A9\uC5B4\uB294 \uC784\uC758\uB85C \uB2E4\uB978 \uD45C\uD604\uC73C\uB85C \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uBB38\uC11C \uC804\uCCB4\uC5D0\uC11C \uC77C\uAD00\uB418\uAC8C \uC720\uC9C0\uD569\uB2C8\uB2E4.
|
|
9949
9958
|
5. \uACC4\uC57D\uC11C\xB7\uC57D\uAD00\xB7\uACF5\uC2DC\xB7\uB17C\uBB38 \uB4F1 \uBC95\uC801\xB7\uACF5\uC2DD \uBB38\uC11C\uB294 \uB2E8\uC5B4 \uD558\uB098\uAC00 \uC758\uBBF8\uB97C \uBC14\uAFC0 \uC218 \uC788\uC73C\uBBC0\uB85C, \uD45C\uD604\uC744 \uC790\uB3D9\uC73C\uB85C \uB2E4\uB4EC\uC9C0 \uB9D0\uACE0 \uC0AC\uC6A9\uC790\uAC00 \uC9C0\uC815\uD55C \uBCC0\uACBD\uB9CC \uC218\uD589\uD569\uB2C8\uB2E4.
|
|
9950
9959
|
6. \uC758\uBBF8\uAC00 \uBC14\uB014 \uC218 \uC788\uB294 \uC218\uC815\uC740 \uC2E4\uD589 \uC804\uC5D0 \uADF8 \uC0AC\uC2E4\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9AC\uACE0, \uC218\uC815 \uC81C\uC548\uC758 \uC694\uC57D(summary)\uC5D0\uB294 \uBB34\uC5C7\uC744\xB7\uC65C \uBC14\uAFB8\uB294\uC9C0 \uD568\uAED8 \uC801\uC2B5\uB2C8\uB2E4.
|
|
9951
|
-
7. \uAC1C\uC778\uC815\uBCF4(\uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638\xB7\uC804\uD654\uBC88\uD638\xB7\uC774\uBA54\uC77C\xB7\uACC4\uC88C\xB7\uCE74\uB4DC\uBC88\uD638 \uB4F1)
|
|
9960
|
+
7. \uAC1C\uC778\uC815\uBCF4(\uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638\xB7\uC804\uD654\uBC88\uD638\xB7\uC774\uBA54\uC77C\xB7\uACC4\uC88C\xB7\uCE74\uB4DC\uBC88\uD638 \uB4F1) \uCC98\uB9AC: \uC0AC\uC6A9\uC790\uAC00 **\uD655\uC778\xB7\uC810\uAC80\uB9CC** \uC6D0\uD558\uBA74 \`scan_pii\`(\uC77D\uAE30 \uC804\uC6A9)\uB85C \uBCF4\uC5EC\uC8FC\uACE0, **\uAC00\uB9AC\uAE30\xB7\uB9C8\uC2A4\uD0B9\xB7\uBE44\uC2DD\uBCC4\xB7\uAC1C\uC778\uC815\uBCF4 \uC0AD\uC81C**\uB97C \uC6D0\uD558\uBA74 \`scan_pii\`\uC5D0\uC11C \uBA48\uCD94\uC9C0 \uB9D0\uACE0 \`propose_redact_pii\`\uB85C \uBC14\uB85C \uC218\uC815\uC548\uC744 \uC81C\uC548\uD558\uC138\uC694(\uC774 \uB3C4\uAD6C\uB3C4 \uC2B9\uC778\uC744 \uAC70\uCE58\uBBC0\uB85C \uC548\uC804\uD569\uB2C8\uB2E4).`;
|
|
9952
9961
|
var LAW_RULES_SECTION = `## \uBC95\uB839 \uADDC\uCE59
|
|
9953
9962
|
|
|
9954
9963
|
1. \uBC95\uB839 \uC778\uC6A9 \uD615\uC2DD: \u300C\uBC95\uB839\uBA85\u300D \uC81CN\uC870 \uC81CN\uD56D \uC81CN\uD638
|
|
@@ -10919,23 +10928,27 @@ import { basename as basename3, extname as extname2, join as join32 } from "path
|
|
|
10919
10928
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
10920
10929
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
10921
10930
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
10931
|
+
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10922
10932
|
import { z as z3 } from "zod";
|
|
10923
10933
|
import { readFile as readFile32 } from "fs/promises";
|
|
10924
10934
|
import { blocksToMarkdown, compare } from "@clazic/kordoc";
|
|
10925
10935
|
import { z as z22 } from "zod";
|
|
10936
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
10937
|
+
import { extname as extname4 } from "path";
|
|
10938
|
+
import { z as z4 } from "zod";
|
|
10926
10939
|
import { readFile as readFile4 } from "fs/promises";
|
|
10927
10940
|
import { extname as extname3 } from "path";
|
|
10928
10941
|
import { z as z32 } from "zod";
|
|
10929
|
-
import {
|
|
10930
|
-
import { extname as extname4, join as join4, relative as relative2 } from "path";
|
|
10931
|
-
import { z as z4 } from "zod";
|
|
10932
|
-
import { readFile as readFile5 } from "fs/promises";
|
|
10942
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
10933
10943
|
import { extname as extname5 } from "path";
|
|
10934
10944
|
import { z as z5 } from "zod";
|
|
10935
|
-
import {
|
|
10936
|
-
import { extname as extname6 } from "path";
|
|
10937
|
-
import { compare as compare2, markdownToHwpx, parse } from "@clazic/kordoc";
|
|
10945
|
+
import { readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
10946
|
+
import { extname as extname6, join as join4, relative as relative2 } from "path";
|
|
10938
10947
|
import { z as z6 } from "zod";
|
|
10948
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
10949
|
+
import { extname as extname7 } from "path";
|
|
10950
|
+
import { compare as compare2, markdownToHwpx, parse } from "@clazic/kordoc";
|
|
10951
|
+
import { z as z7 } from "zod";
|
|
10939
10952
|
import {
|
|
10940
10953
|
Document,
|
|
10941
10954
|
HeadingLevel,
|
|
@@ -10947,41 +10960,45 @@ import {
|
|
|
10947
10960
|
TextRun,
|
|
10948
10961
|
WidthType
|
|
10949
10962
|
} from "docx";
|
|
10950
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
10951
|
-
import { extname as extname7 } from "path";
|
|
10952
|
-
import { parse as parse2 } from "@clazic/kordoc";
|
|
10953
|
-
import { z as z7 } from "zod";
|
|
10954
10963
|
import { readFile as readFile8 } from "fs/promises";
|
|
10955
10964
|
import { extname as extname8 } from "path";
|
|
10956
|
-
import {
|
|
10957
|
-
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10965
|
+
import { parse as parse2 } from "@clazic/kordoc";
|
|
10958
10966
|
import { z as z8 } from "zod";
|
|
10959
10967
|
import { readFile as readFile9 } from "fs/promises";
|
|
10960
10968
|
import { extname as extname9 } from "path";
|
|
10961
|
-
import
|
|
10969
|
+
import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
|
|
10962
10970
|
import { z as z9 } from "zod";
|
|
10963
10971
|
import { readFile as readFile10 } from "fs/promises";
|
|
10964
|
-
import { extname as extname10 } from "path";
|
|
10965
|
-
|
|
10972
|
+
import { basename as basename4, extname as extname10 } from "path";
|
|
10973
|
+
var import_jszip5 = __toESM(require_lib3(), 1);
|
|
10974
|
+
var import_jszip6 = __toESM(require_lib3(), 1);
|
|
10966
10975
|
import { z as z10 } from "zod";
|
|
10967
|
-
import { readFile as readFile11
|
|
10976
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
10968
10977
|
import { extname as extname11 } from "path";
|
|
10969
|
-
import
|
|
10978
|
+
import ExcelJS from "exceljs";
|
|
10970
10979
|
import { z as z11 } from "zod";
|
|
10971
|
-
import { readFile as fsReadFile, stat as stat5 } from "fs/promises";
|
|
10972
|
-
import { z as z12 } from "zod";
|
|
10973
10980
|
import { readFile as readFile12 } from "fs/promises";
|
|
10974
10981
|
import { extname as extname12 } from "path";
|
|
10975
|
-
import { parse as
|
|
10982
|
+
import { parse as parse4 } from "@clazic/kordoc";
|
|
10983
|
+
import { z as z12 } from "zod";
|
|
10984
|
+
import { readFile as readFile13, stat as stat4 } from "fs/promises";
|
|
10985
|
+
import { extname as extname13 } from "path";
|
|
10986
|
+
import { parse as parse5 } from "@clazic/kordoc";
|
|
10976
10987
|
import { z as z13 } from "zod";
|
|
10988
|
+
import { readFile as fsReadFile, stat as stat5 } from "fs/promises";
|
|
10989
|
+
import { z as z14 } from "zod";
|
|
10990
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
10991
|
+
import { extname as extname14 } from "path";
|
|
10992
|
+
import { parse as parse6 } from "@clazic/kordoc";
|
|
10993
|
+
import { z as z15 } from "zod";
|
|
10977
10994
|
import { stat as stat6 } from "fs/promises";
|
|
10978
|
-
import { extname as
|
|
10995
|
+
import { extname as extname15 } from "path";
|
|
10979
10996
|
import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
|
|
10980
|
-
import { z as
|
|
10997
|
+
import { z as z16 } from "zod";
|
|
10981
10998
|
import { stat as stat7 } from "fs/promises";
|
|
10982
|
-
import { extname as
|
|
10999
|
+
import { extname as extname16 } from "path";
|
|
10983
11000
|
import ExcelJS2 from "exceljs";
|
|
10984
|
-
import { z as
|
|
11001
|
+
import { z as z17 } from "zod";
|
|
10985
11002
|
async function resolveSafePath(cwd, p) {
|
|
10986
11003
|
const normalizedCwd = normalize(cwd).normalize("NFC");
|
|
10987
11004
|
const normalizedP = p.normalize("NFC");
|
|
@@ -11481,458 +11498,357 @@ var compareDocumentsTool = {
|
|
|
11481
11498
|
return output;
|
|
11482
11499
|
}
|
|
11483
11500
|
};
|
|
11484
|
-
|
|
11485
|
-
|
|
11501
|
+
var cellEditItemSchema = z32.object({
|
|
11502
|
+
tableIndex: z32.number().int().nonnegative().optional().describe(
|
|
11503
|
+
"\uC88C\uD45C \uBAA8\uB4DC: 0-based \uD45C \uC778\uB371\uC2A4(read_document\uC758 kordoc \uBE14\uB85D \uC21C\uC11C, \uC911\uCCA9\uD45C \uC81C\uC678). \uB808\uC774\uBE14 \uBAA8\uB4DC\uC5D0\uC120 \uD0D0\uC0C9 \uBC94\uC704 \uC81C\uD55C\uC6A9(\uC120\uD0DD)."
|
|
11504
|
+
),
|
|
11505
|
+
row: z32.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
|
|
11506
|
+
col: z32.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
|
|
11507
|
+
label: z32.string().optional().describe(
|
|
11508
|
+
"\uB808\uC774\uBE14 \uBAA8\uB4DC: \uAE30\uC900 \uC140 \uD14D\uC2A4\uD2B8(\uD2B8\uB9BC \uBE44\uAD50). \uC774 \uC140\uC758 direction \uBC29\uD5A5 \uC778\uC811 \uC140\uC5D0 newText\uB97C \uAE30\uB85D. \uC88C\uD45C \uBAA8\uB4DC\uBA74 \uC0DD\uB7B5."
|
|
11509
|
+
),
|
|
11510
|
+
direction: z32.enum(["right", "below"]).optional().describe("\uB808\uC774\uBE14 \uBAA8\uB4DC \uBC29\uD5A5. \uAE30\uBCF8 right(\uC624\uB978\uCABD \uC140), below(\uC544\uB798 \uC140). \uBCD1\uD569 span \uACE0\uB824."),
|
|
11511
|
+
newText: z32.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
|
|
11512
|
+
expectedText: z32.string().optional().describe(
|
|
11513
|
+
"\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8(\uC548\uC804 \uAC80\uC99D\uC6A9). \uBD88\uC77C\uCE58 \uC2DC \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815 \uBC29\uC9C0\uB97C \uC704\uD574 \uAD8C\uC7A5."
|
|
11514
|
+
)
|
|
11515
|
+
}).describe(
|
|
11516
|
+
"\uD3B8\uC9D1 \uD56D\uBAA9. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label[+direction]) \uC911 \uD558\uB098\uB97C \uC0AC\uC6A9\uD558\uC138\uC694. \uB458 \uB2E4 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC624\uB958\uC785\uB2C8\uB2E4."
|
|
11517
|
+
);
|
|
11518
|
+
var proposeCellEditSchema = z32.object({
|
|
11519
|
+
path: z32.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
11520
|
+
edits: z32.array(cellEditItemSchema).min(1).describe(
|
|
11521
|
+
"\uD3B8\uC9D1 \uBAA9\uB85D. \uAC01 \uD56D\uBAA9\uC740 \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label+direction) \uC911 \uD558\uB098"
|
|
11522
|
+
),
|
|
11523
|
+
summary: z32.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
11524
|
+
});
|
|
11525
|
+
function tokenizeHwpxXml(xml) {
|
|
11526
|
+
const tokens = [];
|
|
11527
|
+
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*/g;
|
|
11528
|
+
let startPos = 0;
|
|
11529
|
+
let m = re.exec(xml);
|
|
11530
|
+
while (m !== null) {
|
|
11531
|
+
const raw = m[0];
|
|
11532
|
+
const pos = m.index;
|
|
11533
|
+
if (raw.startsWith("<hp:tbl")) {
|
|
11534
|
+
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
11535
|
+
} else if (raw === "</hp:tbl>") {
|
|
11536
|
+
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
11537
|
+
} else if (raw.startsWith("<hp:tc")) {
|
|
11538
|
+
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
11539
|
+
} else if (raw === "</hp:tc>") {
|
|
11540
|
+
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
11541
|
+
} else if (raw === "<hp:t/>") {
|
|
11542
|
+
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
11543
|
+
} else if (raw === "<hp:t>") {
|
|
11544
|
+
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
11545
|
+
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
11546
|
+
const colM = raw.match(/colAddr="(\d+)"/);
|
|
11547
|
+
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
11548
|
+
if (colM && rowM) {
|
|
11549
|
+
const selfClose = xml.indexOf("/>", pos);
|
|
11550
|
+
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11551
|
+
tokens.push({
|
|
11552
|
+
kind: "cell_addr",
|
|
11553
|
+
pos,
|
|
11554
|
+
end,
|
|
11555
|
+
colAddr: Number(colM[1]),
|
|
11556
|
+
rowAddr: Number(rowM[1])
|
|
11557
|
+
});
|
|
11558
|
+
}
|
|
11559
|
+
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
11560
|
+
const colM = raw.match(/colSpan="(\d+)"/);
|
|
11561
|
+
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
11562
|
+
const selfClose = xml.indexOf("/>", pos);
|
|
11563
|
+
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11564
|
+
tokens.push({
|
|
11565
|
+
kind: "cell_span",
|
|
11566
|
+
pos,
|
|
11567
|
+
end,
|
|
11568
|
+
colSpan: colM ? Number(colM[1]) : 1,
|
|
11569
|
+
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
11570
|
+
});
|
|
11571
|
+
}
|
|
11572
|
+
startPos = re.lastIndex;
|
|
11573
|
+
m = re.exec(xml);
|
|
11574
|
+
}
|
|
11575
|
+
void startPos;
|
|
11576
|
+
return tokens;
|
|
11486
11577
|
}
|
|
11487
|
-
function
|
|
11488
|
-
return text3.replace(
|
|
11578
|
+
function escapeXml(text3) {
|
|
11579
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11489
11580
|
}
|
|
11490
|
-
function
|
|
11491
|
-
const
|
|
11492
|
-
const
|
|
11493
|
-
|
|
11581
|
+
function collectDirectTcRanges(tokens, tblStart, tblEnd) {
|
|
11582
|
+
const tblTokens = tokens.filter((t) => t.pos > tblStart && t.pos < tblEnd);
|
|
11583
|
+
const tcRanges = [];
|
|
11584
|
+
const tcStack = [];
|
|
11585
|
+
let innerDepth = 0;
|
|
11586
|
+
for (const tok of tblTokens) {
|
|
11587
|
+
if (tok.kind === "tbl_open") {
|
|
11588
|
+
innerDepth++;
|
|
11589
|
+
} else if (tok.kind === "tbl_close") {
|
|
11590
|
+
innerDepth--;
|
|
11591
|
+
} else if (tok.kind === "tc_open") {
|
|
11592
|
+
tcStack.push({ pos: tok.pos, depth: innerDepth });
|
|
11593
|
+
} else if (tok.kind === "tc_close") {
|
|
11594
|
+
const entry = tcStack.pop();
|
|
11595
|
+
if (entry !== void 0) {
|
|
11596
|
+
tcRanges.push({ start: entry.pos, end: tok.end, depth: entry.depth });
|
|
11597
|
+
}
|
|
11598
|
+
}
|
|
11599
|
+
}
|
|
11600
|
+
return tcRanges.filter((tc) => tc.depth === 0);
|
|
11494
11601
|
}
|
|
11495
|
-
function
|
|
11496
|
-
const
|
|
11497
|
-
let
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
for (let m = re.exec(xml); m !== null; m = re.exec(xml)) {
|
|
11509
|
-
hits.push({ pos: m.index, xmlTag: spec.xmlTag, type: spec.type });
|
|
11602
|
+
function readOwnTextFromTc(xml, tokens, tcStart, tcEnd) {
|
|
11603
|
+
const tcTokens = tokens.filter((t) => t.pos >= tcStart && t.pos < tcEnd);
|
|
11604
|
+
let innerDepth = 0;
|
|
11605
|
+
let text3 = "";
|
|
11606
|
+
for (const t of tcTokens) {
|
|
11607
|
+
if (t.kind === "tbl_open") innerDepth++;
|
|
11608
|
+
else if (t.kind === "tbl_close") innerDepth--;
|
|
11609
|
+
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11610
|
+
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11611
|
+
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11612
|
+
if (closePos >= 0) {
|
|
11613
|
+
text3 += xml.substring(t.end, closePos);
|
|
11614
|
+
}
|
|
11510
11615
|
}
|
|
11511
11616
|
}
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
const name = getAttr(openTag, "name");
|
|
11524
|
-
let currentValue;
|
|
11525
|
-
let comboItems;
|
|
11526
|
-
switch (type) {
|
|
11527
|
-
case "button":
|
|
11528
|
-
currentValue = getAttr(openTag, "caption");
|
|
11529
|
-
break;
|
|
11530
|
-
case "checkBox":
|
|
11531
|
-
case "radioButton":
|
|
11532
|
-
currentValue = getAttr(openTag, "value") === "CHECKED";
|
|
11533
|
-
break;
|
|
11534
|
-
case "comboBox": {
|
|
11535
|
-
currentValue = getAttr(openTag, "selectedValue");
|
|
11536
|
-
const listItemRe = /<hp:listItem\b[^>]*/g;
|
|
11537
|
-
const items = [];
|
|
11538
|
-
for (let lm = listItemRe.exec(elementXml); lm !== null; lm = listItemRe.exec(elementXml)) {
|
|
11539
|
-
const itemValue = getAttr(lm[0] ?? "", "value");
|
|
11540
|
-
items.push(itemValue);
|
|
11617
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11618
|
+
}
|
|
11619
|
+
function findTopLevelTableRange(tokens, tableIndex) {
|
|
11620
|
+
let topLevelCount = 0;
|
|
11621
|
+
let depth = 0;
|
|
11622
|
+
let targetStart = -1;
|
|
11623
|
+
for (const tok of tokens) {
|
|
11624
|
+
if (tok.kind === "tbl_open") {
|
|
11625
|
+
if (depth === 0) {
|
|
11626
|
+
if (topLevelCount === tableIndex) {
|
|
11627
|
+
targetStart = tok.pos;
|
|
11541
11628
|
}
|
|
11542
|
-
|
|
11543
|
-
break;
|
|
11629
|
+
topLevelCount++;
|
|
11544
11630
|
}
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
currentValue = "";
|
|
11551
|
-
} else {
|
|
11552
|
-
const tOpenIdx = elementXml.search(textOpen);
|
|
11553
|
-
if (tOpenIdx >= 0) {
|
|
11554
|
-
const afterOpen = elementXml.indexOf(">", tOpenIdx) + 1;
|
|
11555
|
-
const closePos = elementXml.indexOf(textClose, afterOpen);
|
|
11556
|
-
currentValue = closePos >= 0 ? decodeXml(elementXml.slice(afterOpen, closePos)) : "";
|
|
11557
|
-
} else {
|
|
11558
|
-
currentValue = "";
|
|
11559
|
-
}
|
|
11560
|
-
}
|
|
11561
|
-
break;
|
|
11631
|
+
depth++;
|
|
11632
|
+
} else if (tok.kind === "tbl_close") {
|
|
11633
|
+
depth--;
|
|
11634
|
+
if (depth === 0 && topLevelCount === tableIndex + 1 && targetStart >= 0) {
|
|
11635
|
+
return { start: targetStart, end: tok.end };
|
|
11562
11636
|
}
|
|
11563
11637
|
}
|
|
11564
|
-
results.push({
|
|
11565
|
-
index: idx++,
|
|
11566
|
-
name,
|
|
11567
|
-
type,
|
|
11568
|
-
currentValue,
|
|
11569
|
-
comboItems,
|
|
11570
|
-
sectionFile,
|
|
11571
|
-
posInSection: pos
|
|
11572
|
-
});
|
|
11573
11638
|
}
|
|
11574
|
-
return
|
|
11639
|
+
return null;
|
|
11575
11640
|
}
|
|
11576
|
-
function
|
|
11577
|
-
const
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
if (openTagEnd < 0) {
|
|
11586
|
-
results[ei] = {
|
|
11587
|
-
success: false,
|
|
11588
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uC5EC\uB294 \uD0DC\uADF8 \uB05D\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11589
|
-
};
|
|
11590
|
-
continue;
|
|
11641
|
+
function readCellAddrSpan(tokens, tcStart, tcEnd) {
|
|
11642
|
+
const tcTokens = tokens.filter((t) => t.pos >= tcStart && t.pos < tcEnd);
|
|
11643
|
+
let addr = null;
|
|
11644
|
+
let span = { colSpan: 1, rowSpan: 1 };
|
|
11645
|
+
for (const t of tcTokens) {
|
|
11646
|
+
if (t.kind === "cell_addr" && t.colAddr !== void 0 && t.rowAddr !== void 0) {
|
|
11647
|
+
addr = { colAddr: t.colAddr, rowAddr: t.rowAddr };
|
|
11648
|
+
} else if (t.kind === "cell_span" && t.colSpan !== void 0 && t.rowSpan !== void 0) {
|
|
11649
|
+
span = { colSpan: t.colSpan, rowSpan: t.rowSpan };
|
|
11591
11650
|
}
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11651
|
+
}
|
|
11652
|
+
if (!addr) return null;
|
|
11653
|
+
return { ...addr, ...span };
|
|
11654
|
+
}
|
|
11655
|
+
function applyCellEditsToSectionXml(xml, edits) {
|
|
11656
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
11657
|
+
const results = edits.map(() => ({ success: false }));
|
|
11658
|
+
let totalTopLevelTbls = 0;
|
|
11659
|
+
{
|
|
11660
|
+
let d = 0;
|
|
11661
|
+
for (const tok of tokens) {
|
|
11662
|
+
if (tok.kind === "tbl_open") {
|
|
11663
|
+
if (d === 0) totalTopLevelTbls++;
|
|
11664
|
+
d++;
|
|
11665
|
+
} else if (tok.kind === "tbl_close") {
|
|
11666
|
+
d--;
|
|
11667
|
+
}
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
const replacements = [];
|
|
11671
|
+
for (let ei = 0; ei < edits.length; ei++) {
|
|
11672
|
+
const edit = edits[ei];
|
|
11673
|
+
const tblRange = findTopLevelTableRange(tokens, edit.tableIndex);
|
|
11674
|
+
if (!tblRange) {
|
|
11596
11675
|
results[ei] = {
|
|
11597
11676
|
success: false,
|
|
11598
|
-
error: `\
|
|
11677
|
+
error: `\uD45C ${edit.tableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 \uC139\uC158\uC5D0 \uCD1D ${totalTopLevelTbls}\uAC1C\uC758 \uCD5C\uC0C1\uC704 \uD45C\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`
|
|
11599
11678
|
};
|
|
11600
11679
|
continue;
|
|
11601
11680
|
}
|
|
11602
|
-
const
|
|
11603
|
-
const
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
const items = target.comboItems ?? [];
|
|
11625
|
-
if (!items.includes(set.selected)) {
|
|
11626
|
-
const validList = items.map((v) => `"${v}"`).join(", ");
|
|
11627
|
-
results[ei] = {
|
|
11628
|
-
success: false,
|
|
11629
|
-
error: `comboBox "${target.name}"\uC758 selected \uAC12 "${set.selected}"\uC774 \uC720\uD6A8\uD55C \uD56D\uBAA9\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uC720\uD6A8\uD55C \uD56D\uBAA9: ${validList || "(\uC5C6\uC74C)"}`
|
|
11630
|
-
};
|
|
11631
|
-
continue;
|
|
11681
|
+
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11682
|
+
const tblTokens = tokens.filter((t) => t.pos >= tblRange.start && t.pos < tblRange.end);
|
|
11683
|
+
let found = false;
|
|
11684
|
+
for (const tc of directTcs) {
|
|
11685
|
+
const tcTokens = tblTokens.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11686
|
+
const hasAddr = tcTokens.some(
|
|
11687
|
+
(t) => t.kind === "cell_addr" && t.colAddr === edit.col && t.rowAddr === edit.row
|
|
11688
|
+
);
|
|
11689
|
+
if (!hasAddr) continue;
|
|
11690
|
+
let innerDepth = 0;
|
|
11691
|
+
const ownTRuns = [];
|
|
11692
|
+
for (const t of tokens.filter((x) => x.pos >= tc.start && x.pos < tc.end)) {
|
|
11693
|
+
if (t.kind === "tbl_open") innerDepth++;
|
|
11694
|
+
else if (t.kind === "tbl_close") innerDepth--;
|
|
11695
|
+
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11696
|
+
ownTRuns.push({ isEmpty: true, tagPos: t.pos, tagEnd: t.end });
|
|
11697
|
+
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11698
|
+
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11699
|
+
if (closePos >= 0) {
|
|
11700
|
+
ownTRuns.push({ isEmpty: false, openEnd: t.end, closePos });
|
|
11701
|
+
}
|
|
11702
|
+
}
|
|
11632
11703
|
}
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
if (set.caption !== void 0) {
|
|
11636
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption", escapeXml(set.caption));
|
|
11637
|
-
if (!patch) {
|
|
11704
|
+
const currentText = ownTRuns.map((r) => r.isEmpty ? "" : xml.substring(r.openEnd, r.closePos)).join("").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11705
|
+
if (edit.expectedText !== void 0 && edit.expectedText !== currentText) {
|
|
11638
11706
|
results[ei] = {
|
|
11639
11707
|
success: false,
|
|
11640
|
-
|
|
11708
|
+
oldText: currentText,
|
|
11709
|
+
error: `\uC140 (\uD45C ${edit.tableIndex}, \uD589 ${edit.row}, \uC5F4 ${edit.col})\uC758 \uD604\uC7AC \uD14D\uC2A4\uD2B8\uAC00 \uC608\uC0C1\uAC12\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uC608\uC0C1: "${edit.expectedText}", \uC2E4\uC81C: "${currentText}". \uC218\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
|
|
11641
11710
|
};
|
|
11642
|
-
|
|
11711
|
+
found = true;
|
|
11712
|
+
break;
|
|
11643
11713
|
}
|
|
11644
|
-
|
|
11645
|
-
patchCreated = true;
|
|
11646
|
-
} else if (set.checked !== void 0) {
|
|
11647
|
-
const newValue = set.checked ? "CHECKED" : "UNCHECKED";
|
|
11648
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "value", newValue);
|
|
11649
|
-
if (!patch) {
|
|
11714
|
+
if (ownTRuns.length === 0) {
|
|
11650
11715
|
results[ei] = {
|
|
11651
11716
|
success: false,
|
|
11652
|
-
|
|
11717
|
+
oldText: currentText,
|
|
11718
|
+
error: `\uC140 (\uD45C ${edit.tableIndex}, \uD589 ${edit.row}, \uC5F4 ${edit.col})\uC5D0 \uD14D\uC2A4\uD2B8 \uB7F0\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. <hp:t> \uB610\uB294 <hp:t/> \uB7F0\uC774 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC140 \uAD6C\uC870\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
11653
11719
|
};
|
|
11654
|
-
|
|
11720
|
+
found = true;
|
|
11721
|
+
break;
|
|
11655
11722
|
}
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
results[ei] = {
|
|
11668
|
-
success: false,
|
|
11669
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 selectedValue \uC18D\uC131\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11670
|
-
};
|
|
11671
|
-
continue;
|
|
11723
|
+
const escapedNew = escapeXml(edit.newText);
|
|
11724
|
+
const patches = [];
|
|
11725
|
+
const firstRun = ownTRuns[0];
|
|
11726
|
+
if (firstRun.isEmpty) {
|
|
11727
|
+
patches.push({
|
|
11728
|
+
from: firstRun.tagPos,
|
|
11729
|
+
to: firstRun.tagEnd,
|
|
11730
|
+
text: `<hp:t>${escapedNew}</hp:t>`
|
|
11731
|
+
});
|
|
11732
|
+
} else {
|
|
11733
|
+
patches.push({ from: firstRun.openEnd, to: firstRun.closePos, text: escapedNew });
|
|
11672
11734
|
}
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
if (!textPatch) {
|
|
11679
|
-
results[ei] = {
|
|
11680
|
-
success: false,
|
|
11681
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 <hp:text> \uC694\uC18C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11682
|
-
};
|
|
11683
|
-
continue;
|
|
11735
|
+
for (let ri = 1; ri < ownTRuns.length; ri++) {
|
|
11736
|
+
const run = ownTRuns[ri];
|
|
11737
|
+
if (!run.isEmpty) {
|
|
11738
|
+
patches.push({ from: run.openEnd, to: run.closePos, text: "" });
|
|
11739
|
+
}
|
|
11684
11740
|
}
|
|
11685
|
-
|
|
11686
|
-
|
|
11741
|
+
replacements.push({ editIdx: ei, patches });
|
|
11742
|
+
results[ei] = { success: true, oldText: currentText };
|
|
11743
|
+
found = true;
|
|
11744
|
+
break;
|
|
11687
11745
|
}
|
|
11688
|
-
if (!
|
|
11689
|
-
results[ei] = {
|
|
11690
|
-
|
|
11746
|
+
if (!found) {
|
|
11747
|
+
results[ei] = {
|
|
11748
|
+
success: false,
|
|
11749
|
+
error: `\uD45C ${edit.tableIndex}\uC5D0\uC11C \uC140 (\uD589 ${edit.row}, \uC5F4 ${edit.col})\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. cellAddr colAddr="${edit.col}" rowAddr="${edit.row}"\uC5D0 \uD574\uB2F9\uD558\uB294 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11750
|
+
};
|
|
11691
11751
|
}
|
|
11692
|
-
results[ei] = { success: true, oldValue: currentRaw };
|
|
11693
11752
|
}
|
|
11694
11753
|
if (results.some((r) => !r.success)) {
|
|
11695
11754
|
return { newXml: xml, results };
|
|
11696
11755
|
}
|
|
11697
|
-
const
|
|
11756
|
+
const allPatches = replacements.flatMap((r) => r.patches).sort((a, b) => b.from - a.from);
|
|
11698
11757
|
let result = xml;
|
|
11699
|
-
for (const patch of
|
|
11700
|
-
result = result.
|
|
11758
|
+
for (const patch of allPatches) {
|
|
11759
|
+
result = result.substring(0, patch.from) + patch.text + result.substring(patch.to);
|
|
11701
11760
|
}
|
|
11702
11761
|
return { newXml: result, results };
|
|
11703
11762
|
}
|
|
11704
|
-
function
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
button: "caption",
|
|
11723
|
-
checkBox: "checked",
|
|
11724
|
-
radioButton: "checked",
|
|
11725
|
-
comboBox: "selected",
|
|
11726
|
-
edit: "text"
|
|
11727
|
-
};
|
|
11728
|
-
if (key !== allowed[type]) {
|
|
11729
|
-
const typeKo = {
|
|
11730
|
-
button: "PushButton",
|
|
11731
|
-
checkBox: "CheckBox",
|
|
11732
|
-
radioButton: "RadioButton",
|
|
11733
|
-
comboBox: "ComboBox",
|
|
11734
|
-
edit: "Edit"
|
|
11735
|
-
};
|
|
11736
|
-
return `\uD0C0\uC785 \uBD88\uC77C\uCE58: ${typeKo[type]} \uC591\uC2DD \uAC1C\uCCB4\uC5D0\uB294 "${allowed[type]}" \uD544\uB4DC\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5\uD558\uC9C0\uB9CC "${key}"\uC774(\uAC00) \uC9C0\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`;
|
|
11737
|
-
}
|
|
11738
|
-
return null;
|
|
11739
|
-
}
|
|
11740
|
-
function readCurrentValue(xml, pos, openTagEnd, _afterOpen, elementEnd, type) {
|
|
11741
|
-
const openTag = xml.slice(pos, openTagEnd + 1);
|
|
11742
|
-
switch (type) {
|
|
11743
|
-
case "button":
|
|
11744
|
-
return decodeXml(getAttr(openTag, "caption"));
|
|
11745
|
-
case "checkBox":
|
|
11746
|
-
case "radioButton":
|
|
11747
|
-
return getAttr(openTag, "value") === "CHECKED";
|
|
11748
|
-
case "comboBox":
|
|
11749
|
-
return decodeXml(getAttr(openTag, "selectedValue"));
|
|
11750
|
-
case "edit": {
|
|
11751
|
-
const elementContent = xml.slice(pos, elementEnd);
|
|
11752
|
-
if (/<hp:text\s*\/>/.test(elementContent)) return "";
|
|
11753
|
-
const tOpenIdx = elementContent.search(/<hp:text>/);
|
|
11754
|
-
if (tOpenIdx >= 0) {
|
|
11755
|
-
const afterOpen = elementContent.indexOf(">", tOpenIdx) + 1;
|
|
11756
|
-
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
11757
|
-
return closePos >= 0 ? decodeXml(elementContent.slice(afterOpen, closePos)) : "";
|
|
11763
|
+
async function applyEditsToHwpx(hwpxBuffer, edits) {
|
|
11764
|
+
const zip = await import_jszip2.default.loadAsync(hwpxBuffer);
|
|
11765
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
11766
|
+
const sectionXmls = [];
|
|
11767
|
+
const sectionTblCounts = [];
|
|
11768
|
+
for (const sf of sectionFiles) {
|
|
11769
|
+
const entry = zip.file(sf);
|
|
11770
|
+
const xml = entry ? await entry.async("string") : "";
|
|
11771
|
+
sectionXmls.push(xml);
|
|
11772
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
11773
|
+
let count = 0;
|
|
11774
|
+
let depth = 0;
|
|
11775
|
+
for (const tok of tokens) {
|
|
11776
|
+
if (tok.kind === "tbl_open") {
|
|
11777
|
+
if (depth === 0) count++;
|
|
11778
|
+
depth++;
|
|
11779
|
+
} else if (tok.kind === "tbl_close") {
|
|
11780
|
+
depth--;
|
|
11758
11781
|
}
|
|
11759
|
-
return "";
|
|
11760
11782
|
}
|
|
11783
|
+
sectionTblCounts.push(count);
|
|
11761
11784
|
}
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
let
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
from: tagStart + relFrom,
|
|
11785
|
-
to: tagStart + relTo,
|
|
11786
|
-
text: newValue
|
|
11787
|
-
};
|
|
11788
|
-
}
|
|
11789
|
-
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
11790
|
-
const escaped = escapeXml(newText);
|
|
11791
|
-
const selfCloseRe = /<hp:text\s*\/>/;
|
|
11792
|
-
const scm = selfCloseRe.exec(elementContent);
|
|
11793
|
-
if (scm) {
|
|
11794
|
-
return {
|
|
11795
|
-
from: pos + scm.index,
|
|
11796
|
-
to: pos + scm.index + scm[0].length,
|
|
11797
|
-
text: `<hp:text>${escaped}</hp:text>`
|
|
11798
|
-
};
|
|
11799
|
-
}
|
|
11800
|
-
const openRe = /<hp:text>/;
|
|
11801
|
-
const om = openRe.exec(elementContent);
|
|
11802
|
-
if (om) {
|
|
11803
|
-
const afterOpen = om.index + om[0].length;
|
|
11804
|
-
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
11805
|
-
if (closePos < 0) return null;
|
|
11806
|
-
return {
|
|
11807
|
-
from: pos + afterOpen,
|
|
11808
|
-
to: pos + closePos,
|
|
11809
|
-
text: escaped
|
|
11810
|
-
};
|
|
11811
|
-
}
|
|
11812
|
-
return null;
|
|
11813
|
-
}
|
|
11814
|
-
async function listFormObjectsFromZip(zip) {
|
|
11815
|
-
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
11816
|
-
const sectionXmls = [];
|
|
11817
|
-
const objects = [];
|
|
11818
|
-
let globalIndex = 0;
|
|
11819
|
-
for (const sf of sectionFiles) {
|
|
11820
|
-
const entry = zip.file(sf);
|
|
11821
|
-
const xml = entry ? await entry.async("string") : "";
|
|
11822
|
-
sectionXmls.push(xml);
|
|
11823
|
-
const parsed = parseFormObjects(xml, sf, globalIndex);
|
|
11824
|
-
objects.push(...parsed);
|
|
11825
|
-
globalIndex += parsed.length;
|
|
11785
|
+
const sectionEdits = sectionFiles.map(() => []);
|
|
11786
|
+
let offset = 0;
|
|
11787
|
+
for (let si = 0; si < sectionFiles.length; si++) {
|
|
11788
|
+
const count = sectionTblCounts[si] ?? 0;
|
|
11789
|
+
for (let ei = 0; ei < edits.length; ei++) {
|
|
11790
|
+
const edit = edits[ei];
|
|
11791
|
+
if (edit.tableIndex >= offset && edit.tableIndex < offset + count) {
|
|
11792
|
+
const secEdits = sectionEdits[si];
|
|
11793
|
+
if (secEdits) {
|
|
11794
|
+
secEdits.push({
|
|
11795
|
+
tableIndex: edit.tableIndex - offset,
|
|
11796
|
+
// 섹션 내 상대 인덱스
|
|
11797
|
+
row: edit.row,
|
|
11798
|
+
col: edit.col,
|
|
11799
|
+
newText: edit.newText,
|
|
11800
|
+
expectedText: edit.expectedText,
|
|
11801
|
+
originalEditIdx: ei
|
|
11802
|
+
});
|
|
11803
|
+
}
|
|
11804
|
+
}
|
|
11805
|
+
}
|
|
11806
|
+
offset += count;
|
|
11826
11807
|
}
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11808
|
+
const totalTables = sectionTblCounts.reduce((a, b) => a + b, 0);
|
|
11809
|
+
const allResults = edits.map((edit, ei) => ({
|
|
11810
|
+
success: false,
|
|
11811
|
+
error: `\uD45C ${edit?.tableIndex ?? ei}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uC5D0 \uCD1D ${totalTables}\uAC1C\uC758 \uCD5C\uC0C1\uC704 \uD45C\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`
|
|
11812
|
+
}));
|
|
11813
|
+
const newSectionXmls = [...sectionXmls];
|
|
11814
|
+
for (let si = 0; si < sectionFiles.length; si++) {
|
|
11815
|
+
const sEdits = sectionEdits[si] ?? [];
|
|
11816
|
+
if (sEdits.length === 0) continue;
|
|
11817
|
+
const srcXml = sectionXmls[si] ?? "";
|
|
11818
|
+
const { newXml, results } = applyCellEditsToSectionXml(srcXml, sEdits);
|
|
11819
|
+
newSectionXmls[si] = newXml;
|
|
11820
|
+
for (let i = 0; i < sEdits.length; i++) {
|
|
11821
|
+
const sEdit = sEdits[i];
|
|
11822
|
+
const res = results[i];
|
|
11823
|
+
if (sEdit && res) {
|
|
11824
|
+
allResults[sEdit.originalEditIdx] = res;
|
|
11825
|
+
}
|
|
11826
|
+
}
|
|
11832
11827
|
}
|
|
11833
|
-
if (
|
|
11834
|
-
return
|
|
11828
|
+
if (allResults.some((r) => !r.success)) {
|
|
11829
|
+
return { buffer: hwpxBuffer, results: allResults };
|
|
11835
11830
|
}
|
|
11836
|
-
|
|
11837
|
-
|
|
11831
|
+
const out = new import_jszip2.default();
|
|
11832
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
11833
|
+
if (mimetypeEntry) {
|
|
11834
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
11838
11835
|
}
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
checked: z32.boolean().optional().describe("CheckBox/RadioButton \uCCB4\uD06C \uC0C1\uD0DC (true=CHECKED)"),
|
|
11847
|
-
selected: z32.string().optional().describe("ComboBox \uC120\uD0DD \uAC12 (listItem \uC911 \uD558\uB098\uC5EC\uC57C \uD568)"),
|
|
11848
|
-
text: z32.string().optional().describe("Edit \uD14D\uC2A4\uD2B8 \uB0B4\uC6A9")
|
|
11849
|
-
}).refine(
|
|
11850
|
-
(v) => {
|
|
11851
|
-
const keys = ["caption", "checked", "selected", "text"].filter(
|
|
11852
|
-
(k) => v[k] !== void 0
|
|
11853
|
-
);
|
|
11854
|
-
return keys.length === 1;
|
|
11855
|
-
},
|
|
11856
|
-
{ message: "set \uD544\uB4DC\uB294 caption/checked/selected/text \uC911 \uC815\uD655\uD788 \uD558\uB098\uB9CC \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4." }
|
|
11857
|
-
);
|
|
11858
|
-
var formEditExpectedSchema = z32.object({
|
|
11859
|
-
caption: z32.string().optional(),
|
|
11860
|
-
checked: z32.boolean().optional(),
|
|
11861
|
-
selected: z32.string().optional(),
|
|
11862
|
-
text: z32.string().optional()
|
|
11863
|
-
}).optional();
|
|
11864
|
-
var formEditItemSchema = z32.object({
|
|
11865
|
-
name: z32.string().describe("\uC591\uC2DD \uAC1C\uCCB4\uC758 name \uC18D\uC131 \uAC12"),
|
|
11866
|
-
index: z32.number().int().nonnegative().optional().describe("\uB3D9\uC77C name\uC774 \uC5EC\uB7FF\uC778 \uACBD\uC6B0 \uBB38\uC11C \uC804\uCCB4 0-based \uC778\uB371\uC2A4\uB85C \uAD6C\uBD84"),
|
|
11867
|
-
set: formEditSetSchema.describe("\uBCC0\uACBD\uD560 \uAC12 (caption/checked/selected/text \uC911 \uD558\uB098)"),
|
|
11868
|
-
expected: formEditExpectedSchema.describe(
|
|
11869
|
-
"\uD604\uC7AC \uAC12 \uC0AC\uC804 \uAC80\uC99D (\uC548\uC804 \uC635\uC158). \uC2E4\uC81C \uAC12\uC774 \uB2E4\uB974\uBA74 \uC774 \uD3B8\uC9D1\uC744 \uCDE8\uC18C\uD558\uACE0 \uC624\uB958 \uBC18\uD658."
|
|
11870
|
-
)
|
|
11871
|
-
});
|
|
11872
|
-
var proposeFormObjectSchema = z32.object({
|
|
11873
|
-
path: z32.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C"),
|
|
11874
|
-
edits: z32.array(formEditItemSchema).min(1).describe("\uC591\uC2DD \uAC1C\uCCB4 \uD3B8\uC9D1 \uBAA9\uB85D"),
|
|
11875
|
-
summary: z32.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
11876
|
-
});
|
|
11877
|
-
var listFormObjectsTool = {
|
|
11878
|
-
name: "list_form_objects",
|
|
11879
|
-
description: "HWPX \uBB38\uC11C\uC758 \uC591\uC2DD \uAC1C\uCCB4(form object) \uBAA9\uB85D\uC744 \uC5F4\uAC70\uD569\uB2C8\uB2E4. PushButton, CheckBox, RadioButton, ComboBox, Edit \uB2E4\uC12F \uAC00\uC9C0 \uD0C0\uC785\uC744 \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uAC01 \uC591\uC2DD \uAC1C\uCCB4\uC758 \uC774\uB984(name), \uD0C0\uC785, \uD604\uC7AC \uAC12, ComboBox\uC758 \uACBD\uC6B0 \uC120\uD0DD \uAC00\uB2A5\uD55C \uD56D\uBAA9 \uBAA9\uB85D\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. propose_form_object\uB85C \uAC12\uC744 \uC218\uC815\uD558\uAE30 \uC804\uC5D0 \uC774 \uD234\uB85C \uBA3C\uC800 \uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. .hwpx \uD30C\uC77C \uC804\uC6A9\uC785\uB2C8\uB2E4.",
|
|
11880
|
-
inputSchema: listFormObjectsSchema,
|
|
11881
|
-
requiresApproval: false,
|
|
11882
|
-
execute: async ({
|
|
11883
|
-
input,
|
|
11884
|
-
ctx
|
|
11885
|
-
}) => {
|
|
11886
|
-
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11887
|
-
const ext = extname3(safePath).toLowerCase();
|
|
11888
|
-
let buffer;
|
|
11889
|
-
try {
|
|
11890
|
-
buffer = await readFile4(safePath);
|
|
11891
|
-
} catch {
|
|
11892
|
-
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
11893
|
-
}
|
|
11894
|
-
const validationError = validateHwpxBuffer(ext, new Uint8Array(buffer.buffer));
|
|
11895
|
-
if (validationError) return validationError;
|
|
11896
|
-
let zip;
|
|
11897
|
-
try {
|
|
11898
|
-
zip = await import_jszip.default.loadAsync(new Uint8Array(buffer.buffer));
|
|
11899
|
-
} catch {
|
|
11900
|
-
return "\uC624\uB958: .hwpx(ZIP) \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
11901
|
-
}
|
|
11902
|
-
const { objects } = await listFormObjectsFromZip(zip);
|
|
11903
|
-
if (objects.length === 0) {
|
|
11904
|
-
return "\uC774 \uBB38\uC11C\uC5D0\uB294 \uC591\uC2DD \uAC1C\uCCB4(form object)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
11905
|
-
}
|
|
11906
|
-
const lines = [`\uCD1D ${objects.length}\uAC1C\uC758 \uC591\uC2DD \uAC1C\uCCB4\uAC00 \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
11907
|
-
`];
|
|
11908
|
-
const typeKo = {
|
|
11909
|
-
button: "PushButton",
|
|
11910
|
-
checkBox: "CheckBox",
|
|
11911
|
-
radioButton: "RadioButton",
|
|
11912
|
-
comboBox: "ComboBox",
|
|
11913
|
-
edit: "Edit"
|
|
11914
|
-
};
|
|
11915
|
-
for (const obj of objects) {
|
|
11916
|
-
let valueStr;
|
|
11917
|
-
if (typeof obj.currentValue === "boolean") {
|
|
11918
|
-
valueStr = obj.currentValue ? "\uCCB4\uD06C\uB428 (CHECKED)" : "\uCCB4\uD06C \uD574\uC81C\uB428 (UNCHECKED)";
|
|
11919
|
-
} else {
|
|
11920
|
-
valueStr = `"${obj.currentValue}"`;
|
|
11921
|
-
}
|
|
11922
|
-
let line = `[${obj.index}] name="${obj.name}" | \uD0C0\uC785: ${typeKo[obj.type]} | \uD604\uC7AC \uAC12: ${valueStr}`;
|
|
11923
|
-
if (obj.type === "comboBox" && obj.comboItems) {
|
|
11924
|
-
const itemList = obj.comboItems.map((v) => `"${v}"`).join(", ");
|
|
11925
|
-
line += ` | \uD56D\uBAA9: [${itemList}]`;
|
|
11926
|
-
}
|
|
11927
|
-
lines.push(line);
|
|
11836
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
11837
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
11838
|
+
const sectionIdx = sectionFiles.indexOf(name);
|
|
11839
|
+
if (sectionIdx >= 0) {
|
|
11840
|
+
out.file(name, newSectionXmls[sectionIdx] ?? "");
|
|
11841
|
+
} else {
|
|
11842
|
+
out.file(name, await entry.async("uint8array"));
|
|
11928
11843
|
}
|
|
11929
|
-
return lines.join("\n");
|
|
11930
11844
|
}
|
|
11931
|
-
};
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11845
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
11846
|
+
return { buffer: new Uint8Array(buf), results: allResults };
|
|
11847
|
+
}
|
|
11848
|
+
var proposeCellEditTool = {
|
|
11849
|
+
name: "propose_cell_edit",
|
|
11850
|
+
description: "HWPX \uBB38\uC11C\uC758 \uD45C \uC140 \uB0B4\uC6A9\uC744 XML \uC9C1\uC811 \uD328\uCE58 \uBC29\uC2DD\uC73C\uB85C \uC218\uC815\uD569\uB2C8\uB2E4. \uBCD1\uD569 \uC140(cellSpan/rowSpan)\uC774 \uC788\uB294 \uD45C\uC5D0\uC11C\uB3C4 \uBCD1\uD569 \uAD6C\uC870\uB97C \uC644\uC804\uD788 \uBCF4\uC874\uD569\uB2C8\uB2E4. \uBE48 \uC140(<hp:t/> self-closing \uB7F0)\uB3C4 \uCC44\uC6B8 \uC218 \uC788\uC5B4 \uC591\uC2DD(form) \uD3B8\uC9D1\uC5D0 \uC801\uD569\uD569\uB2C8\uB2E4. \uC140 \uC8FC\uC18C \uC9C0\uC815 \uBC29\uBC95: (1) \uC88C\uD45C \uBAA8\uB4DC \u2014 tableIndex + row + col \uC9C1\uC811 \uC9C0\uC815. (2) \uB808\uC774\uBE14 \uBAA8\uB4DC \u2014 label(\uC778\uC811 \uB808\uC774\uBE14 \uC140 \uD14D\uC2A4\uD2B8) + direction(right/below, \uAE30\uBCF8 right) + \uC120\uD0DD\uC801 tableIndex\uB85C \uB808\uC774\uBE14 \uC606/\uC544\uB798 \uC140\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uD3B8\uC9D1. \uBCD1\uD569 \uB808\uC774\uBE14 \uC140\uB3C4 colSpan/rowSpan\uC744 \uBC18\uC601\uD558\uC5EC \uB300\uC0C1 \uC140\uC744 \uACC4\uC0B0\uD569\uB2C8\uB2E4. propose_edit/propose_form_fill\uC740 \uB9C8\uD06C\uB2E4\uC6B4 \uB77C\uC6B4\uB4DC\uD2B8\uB9BD\uC73C\uB85C \uBCD1\uD569 \uC140\uC744 \uC18C\uC2E4\uC2DC\uD0A4\uBBC0\uB85C, \uBCD1\uD569 \uC140\uC774 \uC788\uB294 \uD45C\uB97C \uC218\uC815\uD560 \uB54C\uB294 \uC774 \uD234\uC744 \uC0AC\uC6A9\uD558\uC138\uC694. .hwpx \uD30C\uC77C \uC804\uC6A9\uC785\uB2C8\uB2E4. .hwp\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70 Hancom\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. \uC218\uC815 \uC804\uC5D0 \uBC18\uB4DC\uC2DC read_document\uB85C \uC6D0\uBCF8\uC744 \uC77D\uACE0, expectedText\uB97C \uC9C0\uC815\uD558\uC5EC \uC548\uC804\uD558\uAC8C \uC218\uC815\uD558\uC138\uC694. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
11851
|
+
inputSchema: proposeCellEditSchema,
|
|
11936
11852
|
requiresApproval: true,
|
|
11937
11853
|
propose: async ({
|
|
11938
11854
|
input,
|
|
@@ -11940,145 +11856,196 @@ var proposeFormObjectTool = {
|
|
|
11940
11856
|
}) => {
|
|
11941
11857
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11942
11858
|
const ext = extname3(safePath).toLowerCase();
|
|
11859
|
+
if (ext === ".hwp") {
|
|
11860
|
+
return "\uC624\uB958: propose_cell_edit\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uB610\uB294 propose_edit\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC73C\uB098, \uBCD1\uD569 \uC140\uC774 \uC18C\uC2E4\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
11861
|
+
}
|
|
11862
|
+
if (ext !== ".hwpx") {
|
|
11863
|
+
return `\uC624\uB958: propose_cell_edit\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}. \uD45C \uC140 \uC9C1\uC811 \uD3B8\uC9D1\uC740 .hwpx \uD3EC\uB9F7\uC5D0\uC11C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4.`;
|
|
11864
|
+
}
|
|
11943
11865
|
let originalBuffer;
|
|
11944
11866
|
try {
|
|
11945
11867
|
originalBuffer = await readFile4(safePath);
|
|
11946
11868
|
} catch {
|
|
11947
|
-
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}
|
|
11869
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uAC70\uB098 read_document\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
|
|
11948
11870
|
}
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
if (validationError) return validationError;
|
|
11952
|
-
let zip;
|
|
11953
|
-
try {
|
|
11954
|
-
zip = await import_jszip.default.loadAsync(bufArray);
|
|
11955
|
-
} catch {
|
|
11956
|
-
return "\uC624\uB958: .hwpx(ZIP) \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
11871
|
+
if (originalBuffer[0] !== 80 || originalBuffer[1] !== 75) {
|
|
11872
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
11957
11873
|
}
|
|
11958
|
-
const
|
|
11959
|
-
const
|
|
11960
|
-
const
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
const
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11874
|
+
const zipForLabel = await import_jszip2.default.loadAsync(new Uint8Array(originalBuffer.buffer));
|
|
11875
|
+
const sectionFilesForLabel = Object.keys(zipForLabel.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
11876
|
+
const sectionInfos = [];
|
|
11877
|
+
let globalTblOffset = 0;
|
|
11878
|
+
for (const sf of sectionFilesForLabel) {
|
|
11879
|
+
const entry = zipForLabel.file(sf);
|
|
11880
|
+
const xml = entry ? await entry.async("string") : "";
|
|
11881
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
11882
|
+
let count = 0;
|
|
11883
|
+
let d = 0;
|
|
11884
|
+
for (const tok of tokens) {
|
|
11885
|
+
if (tok.kind === "tbl_open") {
|
|
11886
|
+
if (d === 0) count++;
|
|
11887
|
+
d++;
|
|
11888
|
+
} else if (tok.kind === "tbl_close") {
|
|
11889
|
+
d--;
|
|
11890
|
+
}
|
|
11970
11891
|
}
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11892
|
+
sectionInfos.push({ xml, tblCount: count, globalOffset: globalTblOffset });
|
|
11893
|
+
globalTblOffset += count;
|
|
11894
|
+
}
|
|
11895
|
+
function resolveLabelAcrossSections(label, direction, scopedTableIndex) {
|
|
11896
|
+
const trimmedLabel = label.trim();
|
|
11897
|
+
const allMatches = [];
|
|
11898
|
+
for (const si of sectionInfos) {
|
|
11899
|
+
const tokens = tokenizeHwpxXml(si.xml);
|
|
11900
|
+
let localStart = 0;
|
|
11901
|
+
let localEnd = si.tblCount - 1;
|
|
11902
|
+
if (scopedTableIndex !== void 0) {
|
|
11903
|
+
const localIdx2 = scopedTableIndex - si.globalOffset;
|
|
11904
|
+
if (localIdx2 < 0 || localIdx2 >= si.tblCount) continue;
|
|
11905
|
+
localStart = localIdx2;
|
|
11906
|
+
localEnd = localIdx2;
|
|
11979
11907
|
}
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
)
|
|
11985
|
-
|
|
11908
|
+
for (let li = localStart; li <= localEnd; li++) {
|
|
11909
|
+
const tblRange = findTopLevelTableRange(tokens, li);
|
|
11910
|
+
if (!tblRange) continue;
|
|
11911
|
+
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11912
|
+
for (const tc of directTcs) {
|
|
11913
|
+
const cellText = readOwnTextFromTc(si.xml, tokens, tc.start, tc.end).trim();
|
|
11914
|
+
if (cellText === trimmedLabel) {
|
|
11915
|
+
const addrSpan2 = readCellAddrSpan(tokens, tc.start, tc.end);
|
|
11916
|
+
if (addrSpan2) {
|
|
11917
|
+
allMatches.push({
|
|
11918
|
+
globalTableIndex: si.globalOffset + li,
|
|
11919
|
+
addrSpan: addrSpan2,
|
|
11920
|
+
sectionXml: si.xml,
|
|
11921
|
+
sectionOffset: si.globalOffset
|
|
11922
|
+
});
|
|
11923
|
+
}
|
|
11924
|
+
}
|
|
11925
|
+
}
|
|
11986
11926
|
}
|
|
11987
|
-
|
|
11927
|
+
}
|
|
11928
|
+
if (allMatches.length === 0) {
|
|
11929
|
+
const scope = scopedTableIndex !== void 0 ? `\uD45C ${scopedTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
11930
|
+
return {
|
|
11931
|
+
error: `\uB808\uC774\uBE14 "${label}"\uC744(\uB97C) ${scope}\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694.`
|
|
11932
|
+
};
|
|
11933
|
+
}
|
|
11934
|
+
if (allMatches.length > 1) {
|
|
11935
|
+
const locs = allMatches.map(
|
|
11936
|
+
(m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.addrSpan.rowAddr}, \uC5F4 ${m.addrSpan.colAddr})`
|
|
11937
|
+
).join(", ");
|
|
11938
|
+
return {
|
|
11939
|
+
error: `\uB808\uC774\uBE14 "${label}"\uC774(\uAC00) \uC5EC\uB7EC \uC140\uC5D0\uC11C \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${locs}. tableIndex\uB85C \uD0D0\uC0C9 \uBC94\uC704\uB97C \uC881\uD788\uAC70\uB098 \uC88C\uD45C(row/col)\uB97C \uC9C1\uC811 \uC9C0\uC815\uD558\uC138\uC694.`
|
|
11940
|
+
};
|
|
11941
|
+
}
|
|
11942
|
+
const match = allMatches[0];
|
|
11943
|
+
const { addrSpan, globalTableIndex, sectionXml, sectionOffset } = match;
|
|
11944
|
+
let targetRow;
|
|
11945
|
+
let targetCol;
|
|
11946
|
+
if (direction === "right") {
|
|
11947
|
+
targetRow = addrSpan.rowAddr;
|
|
11948
|
+
targetCol = addrSpan.colAddr + addrSpan.colSpan;
|
|
11988
11949
|
} else {
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11950
|
+
targetRow = addrSpan.rowAddr + addrSpan.rowSpan;
|
|
11951
|
+
targetCol = addrSpan.colAddr;
|
|
11952
|
+
}
|
|
11953
|
+
const localIdx = globalTableIndex - sectionOffset;
|
|
11954
|
+
const tokens2 = tokenizeHwpxXml(sectionXml);
|
|
11955
|
+
const tblRange2 = findTopLevelTableRange(tokens2, localIdx);
|
|
11956
|
+
if (!tblRange2) {
|
|
11957
|
+
return { error: `\uD45C ${globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
11958
|
+
}
|
|
11959
|
+
const directTcs2 = collectDirectTcRanges(tokens2, tblRange2.start, tblRange2.end);
|
|
11960
|
+
const tblTokens2 = tokens2.filter((t) => t.pos >= tblRange2.start && t.pos < tblRange2.end);
|
|
11961
|
+
let targetExists = false;
|
|
11962
|
+
for (const tc of directTcs2) {
|
|
11963
|
+
const tcTokens = tblTokens2.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11964
|
+
if (tcTokens.some(
|
|
11965
|
+
(t) => t.kind === "cell_addr" && t.colAddr === targetCol && t.rowAddr === targetRow
|
|
11966
|
+
)) {
|
|
11967
|
+
targetExists = true;
|
|
11968
|
+
break;
|
|
11995
11969
|
}
|
|
11996
11970
|
}
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
}
|
|
11971
|
+
if (!targetExists) {
|
|
11972
|
+
const dirLabel = direction === "right" ? "\uC624\uB978\uCABD" : "\uC544\uB798";
|
|
11973
|
+
return {
|
|
11974
|
+
error: `\uB808\uC774\uBE14 "${label}" (\uD45C ${globalTableIndex}, \uD589 ${addrSpan.rowAddr}, \uC5F4 ${addrSpan.colAddr})\uC758 ${dirLabel} \uC140 (\uD589 ${targetRow}, \uC5F4 ${targetCol})\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. direction \uB610\uB294 \uC88C\uD45C\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
11975
|
+
};
|
|
11976
|
+
}
|
|
11977
|
+
return { tableIndex: globalTableIndex, row: targetRow, col: targetCol };
|
|
11978
|
+
}
|
|
11979
|
+
const resolvedEdits = [];
|
|
11980
|
+
const resolveErrors = [];
|
|
11981
|
+
for (let i = 0; i < input.edits.length; i++) {
|
|
11982
|
+
const e = input.edits[i];
|
|
11983
|
+
if (!e) continue;
|
|
11984
|
+
if (e.label !== void 0 && (e.row !== void 0 || e.col !== void 0)) {
|
|
11985
|
+
resolveErrors.push(
|
|
11986
|
+
`\uD3B8\uC9D1 #${i + 1}: label\uACFC row/col\uC744 \uB3D9\uC2DC\uC5D0 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694.`
|
|
11987
|
+
);
|
|
11988
|
+
} else if (e.label !== void 0) {
|
|
11989
|
+
const direction = e.direction ?? "right";
|
|
11990
|
+
const resolved = resolveLabelAcrossSections(e.label, direction, e.tableIndex);
|
|
11991
|
+
if ("error" in resolved) {
|
|
11992
|
+
resolveErrors.push(`\uD3B8\uC9D1 #${i + 1} (\uB808\uC774\uBE14 "${e.label}"): ${resolved.error}`);
|
|
11993
|
+
} else {
|
|
11994
|
+
resolvedEdits.push({
|
|
11995
|
+
tableIndex: resolved.tableIndex,
|
|
11996
|
+
row: resolved.row,
|
|
11997
|
+
col: resolved.col,
|
|
11998
|
+
newText: e.newText,
|
|
11999
|
+
expectedText: e.expectedText,
|
|
12000
|
+
label: e.label
|
|
12001
|
+
});
|
|
12002
|
+
}
|
|
12003
|
+
} else if (e.tableIndex !== void 0 && e.row !== void 0 && e.col !== void 0) {
|
|
12004
|
+
resolvedEdits.push({
|
|
12005
|
+
tableIndex: e.tableIndex,
|
|
12006
|
+
row: e.row,
|
|
12007
|
+
col: e.col,
|
|
12008
|
+
newText: e.newText,
|
|
12009
|
+
expectedText: e.expectedText
|
|
12010
|
+
});
|
|
12011
|
+
} else {
|
|
12012
|
+
resolveErrors.push(
|
|
12013
|
+
`\uD3B8\uC9D1 #${i + 1}: \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB97C \uC644\uC804\uD788 \uC9C0\uC815\uD558\uC138\uC694. read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`
|
|
12014
|
+
);
|
|
12015
|
+
}
|
|
12003
12016
|
}
|
|
12004
12017
|
if (resolveErrors.length > 0) {
|
|
12005
|
-
return `\uC624\uB958: \uB2E4\uC74C \
|
|
12018
|
+
return `\uC624\uB958: \uB2E4\uC74C \uB808\uC774\uBE14\uC744 \uD574\uC11D\uD560 \uC218 \uC5C6\uC5B4 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
|
|
12006
12019
|
${resolveErrors.join("\n")}`;
|
|
12007
12020
|
}
|
|
12008
|
-
const
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12021
|
+
const editRequests = resolvedEdits.map((e) => ({
|
|
12022
|
+
tableIndex: e.tableIndex,
|
|
12023
|
+
row: e.row,
|
|
12024
|
+
col: e.col,
|
|
12025
|
+
newText: e.newText,
|
|
12026
|
+
expectedText: e.expectedText
|
|
12027
|
+
}));
|
|
12028
|
+
const { buffer: newBuffer, results } = await applyEditsToHwpx(
|
|
12029
|
+
new Uint8Array(originalBuffer.buffer),
|
|
12030
|
+
editRequests
|
|
12031
|
+
);
|
|
12032
|
+
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
12033
|
+
if (failedResults.length > 0) {
|
|
12034
|
+
const messages = failedResults.map(({ r, i }) => {
|
|
12035
|
+
const e = resolvedEdits[i];
|
|
12036
|
+
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
12037
|
+
return `\uD3B8\uC9D1 #${i + 1} (${label}\uD45C ${e?.tableIndex ?? "?"}, \uD589 ${e?.row ?? "?"}, \uC5F4 ${e?.col ?? "?"}): ${r.error}`;
|
|
12017
12038
|
});
|
|
12018
|
-
}
|
|
12019
|
-
const sectionResults = [];
|
|
12020
|
-
const newSectionXmls = [...sectionXmls];
|
|
12021
|
-
for (const [si, edits] of sectionEditMap) {
|
|
12022
|
-
const xml = sectionXmls[si] ?? "";
|
|
12023
|
-
const { newXml, results } = applyFormObjectEdits(xml, edits);
|
|
12024
|
-
newSectionXmls[si] = newXml;
|
|
12025
|
-
sectionResults.push({ si, results, edits });
|
|
12026
|
-
}
|
|
12027
|
-
const failMessages = [];
|
|
12028
|
-
const successMap = /* @__PURE__ */ new Map();
|
|
12029
|
-
for (const sr of sectionResults) {
|
|
12030
|
-
for (let i = 0; i < sr.edits.length; i++) {
|
|
12031
|
-
const editReq = sr.edits[i];
|
|
12032
|
-
const result = sr.results[i];
|
|
12033
|
-
const resolved = resolvedEdits.find((r) => r.target === editReq.target);
|
|
12034
|
-
if (resolved) {
|
|
12035
|
-
successMap.set(resolved.editIdx, result);
|
|
12036
|
-
if (!result.success) {
|
|
12037
|
-
failMessages.push(
|
|
12038
|
-
`\uD3B8\uC9D1 #${resolved.editIdx + 1} (name="${editReq.target.name}"): ${result.error}`
|
|
12039
|
-
);
|
|
12040
|
-
}
|
|
12041
|
-
}
|
|
12042
|
-
}
|
|
12043
|
-
}
|
|
12044
|
-
if (failMessages.length > 0) {
|
|
12045
12039
|
return `\uC624\uB958: \uB2E4\uC74C \uD3B8\uC9D1\uC744 \uC801\uC6A9\uD560 \uC218 \uC5C6\uC5B4 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
|
|
12046
|
-
${
|
|
12047
|
-
}
|
|
12048
|
-
const out = new import_jszip.default();
|
|
12049
|
-
const mimetypeEntry = zip.file("mimetype");
|
|
12050
|
-
if (mimetypeEntry) {
|
|
12051
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
12052
|
-
}
|
|
12053
|
-
for (const [name, entry] of Object.entries(zip.files)) {
|
|
12054
|
-
if (name === "mimetype" || entry.dir) continue;
|
|
12055
|
-
const si = sectionFiles.indexOf(name);
|
|
12056
|
-
if (si >= 0) {
|
|
12057
|
-
out.file(name, newSectionXmls[si] ?? "");
|
|
12058
|
-
} else {
|
|
12059
|
-
out.file(name, await entry.async("uint8array"));
|
|
12060
|
-
}
|
|
12040
|
+
${messages.join("\n")}`;
|
|
12061
12041
|
}
|
|
12062
|
-
const
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
edit: "Edit"
|
|
12070
|
-
};
|
|
12071
|
-
const diffLines = ["| \uC591\uC2DD \uAC1C\uCCB4 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12072
|
-
for (const re of resolvedEdits) {
|
|
12073
|
-
const result = successMap.get(re.editIdx);
|
|
12074
|
-
const oldVal = result?.oldValue ?? "";
|
|
12075
|
-
const oldStr = typeof oldVal === "boolean" ? oldVal ? "CHECKED" : "UNCHECKED" : String(oldVal);
|
|
12076
|
-
let newStr;
|
|
12077
|
-
if (re.set.caption !== void 0) newStr = re.set.caption;
|
|
12078
|
-
else if (re.set.checked !== void 0) newStr = re.set.checked ? "CHECKED" : "UNCHECKED";
|
|
12079
|
-
else if (re.set.selected !== void 0) newStr = re.set.selected;
|
|
12080
|
-
else newStr = re.set.text ?? "";
|
|
12081
|
-
diffLines.push(`| ${re.target.name}(${typeKo[re.target.type]}) | ${oldStr} | ${newStr} |`);
|
|
12042
|
+
const diffLines = ["| \uD45C\xB7\uC140 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12043
|
+
for (let i = 0; i < resolvedEdits.length; i++) {
|
|
12044
|
+
const e = resolvedEdits[i];
|
|
12045
|
+
if (!e) continue;
|
|
12046
|
+
const oldText = results[i]?.oldText ?? "";
|
|
12047
|
+
const addr = e.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 #${e.tableIndex} (${e.row},${e.col})` : `#${e.tableIndex} (${e.row},${e.col})`;
|
|
12048
|
+
diffLines.push(`| ${addr} | ${oldText} | ${e.newText} |`);
|
|
12082
12049
|
}
|
|
12083
12050
|
const diff = diffLines.join("\n");
|
|
12084
12051
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
@@ -12087,7 +12054,7 @@ ${failMessages.join("\n")}`;
|
|
|
12087
12054
|
return {
|
|
12088
12055
|
proposal: {
|
|
12089
12056
|
id: proposalId,
|
|
12090
|
-
kind: "
|
|
12057
|
+
kind: "cell-edit",
|
|
12091
12058
|
targetPath: outputPath,
|
|
12092
12059
|
stagedPath,
|
|
12093
12060
|
summary: input.summary,
|
|
@@ -12104,443 +12071,644 @@ ${failMessages.join("\n")}`;
|
|
|
12104
12071
|
};
|
|
12105
12072
|
}
|
|
12106
12073
|
};
|
|
12107
|
-
var
|
|
12108
|
-
".
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
".docx",
|
|
12112
|
-
".doc",
|
|
12113
|
-
".xlsx",
|
|
12114
|
-
".xls",
|
|
12115
|
-
".pdf",
|
|
12116
|
-
".pptx",
|
|
12117
|
-
".ppt",
|
|
12118
|
-
".md",
|
|
12119
|
-
".txt"
|
|
12120
|
-
]);
|
|
12121
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12122
|
-
"node_modules",
|
|
12123
|
-
".git",
|
|
12124
|
-
".DS_Store",
|
|
12125
|
-
"__pycache__",
|
|
12126
|
-
"dist",
|
|
12127
|
-
"build",
|
|
12128
|
-
".next"
|
|
12129
|
-
]);
|
|
12130
|
-
var MAX_DEPTH = 4;
|
|
12131
|
-
var MAX_FILES = 500;
|
|
12132
|
-
var listFilesSchema = z4.object({
|
|
12133
|
-
dir: z4.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
|
|
12074
|
+
var findInDocumentSchema = z4.object({
|
|
12075
|
+
path: z4.string().describe("\uAC80\uC0C9\uD560 .hwpx \uBB38\uC11C \uACBD\uB85C"),
|
|
12076
|
+
query: z4.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
12077
|
+
caseSensitive: z4.boolean().optional().describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8 false)")
|
|
12134
12078
|
});
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12079
|
+
function unescapeXml(text3) {
|
|
12080
|
+
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
12081
|
+
}
|
|
12082
|
+
function windowText(text3, query, caseSensitive, maxLen = 60) {
|
|
12083
|
+
const compare3 = caseSensitive ? text3 : text3.toLowerCase();
|
|
12084
|
+
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12085
|
+
const idx = compare3.indexOf(compareQuery);
|
|
12086
|
+
if (idx < 0) return text3.slice(0, maxLen);
|
|
12087
|
+
const half = Math.floor((maxLen - query.length) / 2);
|
|
12088
|
+
const start = Math.max(0, idx - half);
|
|
12089
|
+
const end = Math.min(text3.length, idx + query.length + half);
|
|
12090
|
+
const prefix = start > 0 ? "\u2026" : "";
|
|
12091
|
+
const suffix = end < text3.length ? "\u2026" : "";
|
|
12092
|
+
return prefix + text3.slice(start, end) + suffix;
|
|
12093
|
+
}
|
|
12094
|
+
function findInSectionXmls(xmls, query, caseSensitive) {
|
|
12095
|
+
const hits = [];
|
|
12096
|
+
let globalOffset = 0;
|
|
12097
|
+
for (let si = 0; si < xmls.length; si++) {
|
|
12098
|
+
const xml = xmls[si] ?? "";
|
|
12099
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
12100
|
+
let localCount = 0;
|
|
12101
|
+
{
|
|
12102
|
+
let depth = 0;
|
|
12103
|
+
for (const tok of tokens) {
|
|
12104
|
+
if (tok.kind === "tbl_open") {
|
|
12105
|
+
if (depth === 0) localCount++;
|
|
12106
|
+
depth++;
|
|
12107
|
+
} else if (tok.kind === "tbl_close") {
|
|
12108
|
+
depth--;
|
|
12109
|
+
}
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
for (let ti = 0; ti < localCount; ti++) {
|
|
12113
|
+
const range = findTopLevelTableRange(tokens, ti);
|
|
12114
|
+
if (!range) continue;
|
|
12115
|
+
const cells = collectDirectTcRanges(tokens, range.start, range.end);
|
|
12116
|
+
const tblTokens = tokens.filter((t) => t.pos >= range.start && t.pos < range.end);
|
|
12117
|
+
for (const cell of cells) {
|
|
12118
|
+
const cellText = readOwnTextFromTc(xml, tokens, cell.start, cell.end);
|
|
12119
|
+
const compare3 = caseSensitive ? cellText : cellText.toLowerCase();
|
|
12120
|
+
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12121
|
+
if (!compare3.includes(compareQuery)) continue;
|
|
12122
|
+
const cellTokens = tblTokens.filter((t) => t.pos >= cell.start && t.pos < cell.end);
|
|
12123
|
+
const addrTok = cellTokens.find((t) => t.kind === "cell_addr");
|
|
12124
|
+
if (addrTok === void 0) continue;
|
|
12125
|
+
const row = addrTok.rowAddr ?? 0;
|
|
12126
|
+
const col = addrTok.colAddr ?? 0;
|
|
12127
|
+
const truncated = cellText.length > 60 ? `${cellText.slice(0, 57)}\u2026` : cellText;
|
|
12128
|
+
hits.push({
|
|
12129
|
+
kind: "\uD45C",
|
|
12130
|
+
tableIndex: globalOffset + ti,
|
|
12131
|
+
row,
|
|
12132
|
+
col,
|
|
12133
|
+
text: truncated
|
|
12134
|
+
});
|
|
12135
|
+
}
|
|
12153
12136
|
}
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12137
|
+
{
|
|
12138
|
+
const bodySegments = [];
|
|
12139
|
+
let tblDepth = 0;
|
|
12140
|
+
let segStart = 0;
|
|
12141
|
+
for (const tok of tokens) {
|
|
12142
|
+
if (tok.kind === "tbl_open") {
|
|
12143
|
+
if (tblDepth === 0) {
|
|
12144
|
+
if (tok.pos > segStart) {
|
|
12145
|
+
bodySegments.push({ start: segStart, end: tok.pos });
|
|
12146
|
+
}
|
|
12147
|
+
}
|
|
12148
|
+
tblDepth++;
|
|
12149
|
+
} else if (tok.kind === "tbl_close") {
|
|
12150
|
+
tblDepth--;
|
|
12151
|
+
if (tblDepth === 0) {
|
|
12152
|
+
segStart = tok.end;
|
|
12153
|
+
}
|
|
12154
|
+
}
|
|
12155
|
+
}
|
|
12156
|
+
if (segStart < xml.length) {
|
|
12157
|
+
bodySegments.push({ start: segStart, end: xml.length });
|
|
12158
|
+
}
|
|
12159
|
+
const tRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
12160
|
+
for (const seg of bodySegments) {
|
|
12161
|
+
const slice = xml.slice(seg.start, seg.end);
|
|
12162
|
+
tRe.lastIndex = 0;
|
|
12163
|
+
let m = tRe.exec(slice);
|
|
12164
|
+
while (m !== null) {
|
|
12165
|
+
const rawText = m[1] ?? "";
|
|
12166
|
+
const decodedText = unescapeXml(rawText);
|
|
12167
|
+
const compare3 = caseSensitive ? decodedText : decodedText.toLowerCase();
|
|
12168
|
+
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12169
|
+
if (compare3.includes(compareQuery)) {
|
|
12170
|
+
hits.push({
|
|
12171
|
+
kind: "\uBCF8\uBB38",
|
|
12172
|
+
section: si,
|
|
12173
|
+
text: windowText(decodedText, query, caseSensitive)
|
|
12174
|
+
});
|
|
12175
|
+
}
|
|
12176
|
+
m = tRe.exec(slice);
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12162
12179
|
}
|
|
12180
|
+
globalOffset += localCount;
|
|
12163
12181
|
}
|
|
12182
|
+
return hits;
|
|
12164
12183
|
}
|
|
12165
|
-
var
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12184
|
+
var MAX_HITS = 50;
|
|
12185
|
+
function formatHits(hits, query) {
|
|
12186
|
+
const shown = hits.slice(0, MAX_HITS);
|
|
12187
|
+
const lines = shown.map((h) => {
|
|
12188
|
+
if (h.kind === "\uD45C") {
|
|
12189
|
+
return `[\uD45C] tableIndex=${h.tableIndex}, row=${h.row}, col=${h.col} \u2014 "${h.text}" (propose_cell_edit\uC73C\uB85C \uC218\uC815 \uAC00\uB2A5)`;
|
|
12190
|
+
}
|
|
12191
|
+
return `[\uBCF8\uBB38] \uC139\uC158 ${h.section} \u2014 "\u2026${h.text}\u2026" (propose_find_replace\uB85C \uC218\uC815 \uAC00\uB2A5)`;
|
|
12192
|
+
});
|
|
12193
|
+
const notice = hits.length > MAX_HITS ? `
|
|
12194
|
+
(\uCD1D ${hits.length}\uAC1C \uB9E4\uCE6D \uC911 ${MAX_HITS}\uAC1C\uB9CC \uD45C\uC2DC\uB429\uB2C8\uB2E4.)` : "";
|
|
12195
|
+
return `"${query}" \uAC80\uC0C9 \uACB0\uACFC: ${hits.length}\uAC1C
|
|
12196
|
+
|
|
12197
|
+
${lines.join("\n")}${notice}`;
|
|
12198
|
+
}
|
|
12199
|
+
var findInDocumentTool = {
|
|
12200
|
+
name: "find_in_document",
|
|
12201
|
+
description: "\uBB38\uC11C\uC5D0\uC11C \uD2B9\uC815 \uD14D\uC2A4\uD2B8\uAC00 **\uC5B4\uB514\uC5D0 \uC788\uB294\uC9C0** \uC704\uCE58\uB97C \uCC3E\uC544 \uC90D\uB2C8\uB2E4. \uD45C \uC548\uC758 \uC140\uC774\uBA74 propose_cell_edit\uC5D0\uC11C \uBC14\uB85C \uC4F8 \uC218 \uC788\uB294 tableIndex\xB7row\xB7col \uC88C\uD45C\uB97C, \uBCF8\uBB38\uC774\uBA74 \uC8FC\uBCC0 \uB9E5\uB77D\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. \uD070 \uBB38\uC11C\uC5D0\uC11C \uC804\uCCB4\uB97C \uC77D\uC9C0 \uC54A\uACE0 \uC218\uC815 \uB300\uC0C1\uC744 \uC815\uD655\uD788 \uC9C0\uC815\uD560 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. .hwpx \uC804\uC6A9.",
|
|
12202
|
+
inputSchema: findInDocumentSchema,
|
|
12169
12203
|
requiresApproval: false,
|
|
12170
12204
|
execute: async ({
|
|
12171
12205
|
input,
|
|
12172
12206
|
ctx
|
|
12173
12207
|
}) => {
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
return
|
|
12208
|
+
let safePath;
|
|
12209
|
+
try {
|
|
12210
|
+
safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12211
|
+
} catch (err) {
|
|
12212
|
+
return `\uC624\uB958: \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. ${String(err)}`;
|
|
12179
12213
|
}
|
|
12180
|
-
const
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
}
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
}
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
"\uD3B8\uC9D1 \uD56D\uBAA9. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label[+direction]) \uC911 \uD558\uB098\uB97C \uC0AC\uC6A9\uD558\uC138\uC694. \uB458 \uB2E4 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC624\uB958\uC785\uB2C8\uB2E4."
|
|
12209
|
-
);
|
|
12210
|
-
var proposeCellEditSchema = z5.object({
|
|
12211
|
-
path: z5.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
12212
|
-
edits: z5.array(cellEditItemSchema).min(1).describe(
|
|
12213
|
-
"\uD3B8\uC9D1 \uBAA9\uB85D. \uAC01 \uD56D\uBAA9\uC740 \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label+direction) \uC911 \uD558\uB098"
|
|
12214
|
-
),
|
|
12215
|
-
summary: z5.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12216
|
-
});
|
|
12217
|
-
function tokenizeHwpxXml(xml) {
|
|
12218
|
-
const tokens = [];
|
|
12219
|
-
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*/g;
|
|
12220
|
-
let startPos = 0;
|
|
12221
|
-
let m = re.exec(xml);
|
|
12222
|
-
while (m !== null) {
|
|
12223
|
-
const raw = m[0];
|
|
12224
|
-
const pos = m.index;
|
|
12225
|
-
if (raw.startsWith("<hp:tbl")) {
|
|
12226
|
-
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
12227
|
-
} else if (raw === "</hp:tbl>") {
|
|
12228
|
-
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
12229
|
-
} else if (raw.startsWith("<hp:tc")) {
|
|
12230
|
-
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
12231
|
-
} else if (raw === "</hp:tc>") {
|
|
12232
|
-
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
12233
|
-
} else if (raw === "<hp:t/>") {
|
|
12234
|
-
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
12235
|
-
} else if (raw === "<hp:t>") {
|
|
12236
|
-
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
12237
|
-
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
12238
|
-
const colM = raw.match(/colAddr="(\d+)"/);
|
|
12239
|
-
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
12240
|
-
if (colM && rowM) {
|
|
12241
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
12242
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
12243
|
-
tokens.push({
|
|
12244
|
-
kind: "cell_addr",
|
|
12245
|
-
pos,
|
|
12246
|
-
end,
|
|
12247
|
-
colAddr: Number(colM[1]),
|
|
12248
|
-
rowAddr: Number(rowM[1])
|
|
12249
|
-
});
|
|
12214
|
+
const ext = extname4(safePath).toLowerCase();
|
|
12215
|
+
if (ext !== ".hwpx") {
|
|
12216
|
+
return "find_in_document\uB294 .hwpx \uC804\uC6A9\uC785\uB2C8\uB2E4. \uBCF8\uBB38 \uD14D\uC2A4\uD2B8 \uAC80\uC0C9\uC740 read_document\uC758 search \uBAA8\uB4DC\uB97C \uC0AC\uC6A9\uD558\uC138\uC694. (.hwp \uD30C\uC77C\uC740 \uD55C\uAE00\uC5D0\uC11C \uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx\uB85C \uBCC0\uD658 \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694.)";
|
|
12217
|
+
}
|
|
12218
|
+
let bytes;
|
|
12219
|
+
try {
|
|
12220
|
+
bytes = await readFile5(safePath);
|
|
12221
|
+
} catch {
|
|
12222
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12223
|
+
}
|
|
12224
|
+
if (bytes[0] !== 80 || bytes[1] !== 75) {
|
|
12225
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
12226
|
+
}
|
|
12227
|
+
let zip;
|
|
12228
|
+
try {
|
|
12229
|
+
zip = await import_jszip.default.loadAsync(bytes);
|
|
12230
|
+
} catch (err) {
|
|
12231
|
+
return `\uC624\uB958: .hwpx ZIP\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${String(err)}`;
|
|
12232
|
+
}
|
|
12233
|
+
const sectionNames = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12234
|
+
const xmls = [];
|
|
12235
|
+
for (const name of sectionNames) {
|
|
12236
|
+
try {
|
|
12237
|
+
const entry = zip.file(name);
|
|
12238
|
+
const xml = entry ? await entry.async("string") : "";
|
|
12239
|
+
xmls.push(xml);
|
|
12240
|
+
} catch {
|
|
12241
|
+
xmls.push("");
|
|
12250
12242
|
}
|
|
12251
|
-
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
12252
|
-
const colM = raw.match(/colSpan="(\d+)"/);
|
|
12253
|
-
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
12254
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
12255
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
12256
|
-
tokens.push({
|
|
12257
|
-
kind: "cell_span",
|
|
12258
|
-
pos,
|
|
12259
|
-
end,
|
|
12260
|
-
colSpan: colM ? Number(colM[1]) : 1,
|
|
12261
|
-
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
12262
|
-
});
|
|
12263
12243
|
}
|
|
12264
|
-
|
|
12265
|
-
|
|
12244
|
+
if (xmls.length === 0) {
|
|
12245
|
+
return `\uC624\uB958: .hwpx \uD30C\uC77C\uC5D0\uC11C \uC139\uC158 XML\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
12246
|
+
}
|
|
12247
|
+
const caseSensitive = input.caseSensitive ?? false;
|
|
12248
|
+
let hits;
|
|
12249
|
+
try {
|
|
12250
|
+
hits = findInSectionXmls(xmls, input.query, caseSensitive);
|
|
12251
|
+
} catch (err) {
|
|
12252
|
+
return `\uC624\uB958: \uBB38\uC11C \uD0D0\uC0C9 \uC911 \uC608\uC678\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${String(err)}`;
|
|
12253
|
+
}
|
|
12254
|
+
if (hits.length === 0) {
|
|
12255
|
+
return `\uBB38\uC11C\uC5D0\uC11C "${input.query}"\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
12256
|
+
}
|
|
12257
|
+
return formatHits(hits, input.query);
|
|
12266
12258
|
}
|
|
12267
|
-
|
|
12268
|
-
return tokens;
|
|
12269
|
-
}
|
|
12259
|
+
};
|
|
12270
12260
|
function escapeXml2(text3) {
|
|
12271
|
-
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12261
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
12272
12262
|
}
|
|
12273
|
-
function
|
|
12274
|
-
|
|
12275
|
-
const tcRanges = [];
|
|
12276
|
-
const tcStack = [];
|
|
12277
|
-
let innerDepth = 0;
|
|
12278
|
-
for (const tok of tblTokens) {
|
|
12279
|
-
if (tok.kind === "tbl_open") {
|
|
12280
|
-
innerDepth++;
|
|
12281
|
-
} else if (tok.kind === "tbl_close") {
|
|
12282
|
-
innerDepth--;
|
|
12283
|
-
} else if (tok.kind === "tc_open") {
|
|
12284
|
-
tcStack.push({ pos: tok.pos, depth: innerDepth });
|
|
12285
|
-
} else if (tok.kind === "tc_close") {
|
|
12286
|
-
const entry = tcStack.pop();
|
|
12287
|
-
if (entry !== void 0) {
|
|
12288
|
-
tcRanges.push({ start: entry.pos, end: tok.end, depth: entry.depth });
|
|
12289
|
-
}
|
|
12290
|
-
}
|
|
12291
|
-
}
|
|
12292
|
-
return tcRanges.filter((tc) => tc.depth === 0);
|
|
12263
|
+
function decodeXml(text3) {
|
|
12264
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
12293
12265
|
}
|
|
12294
|
-
function
|
|
12295
|
-
const
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
for (const t of tcTokens) {
|
|
12299
|
-
if (t.kind === "tbl_open") innerDepth++;
|
|
12300
|
-
else if (t.kind === "tbl_close") innerDepth--;
|
|
12301
|
-
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
12302
|
-
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
12303
|
-
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
12304
|
-
if (closePos >= 0) {
|
|
12305
|
-
text3 += xml.substring(t.end, closePos);
|
|
12306
|
-
}
|
|
12307
|
-
}
|
|
12308
|
-
}
|
|
12309
|
-
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12266
|
+
function getAttr(openTag, attr) {
|
|
12267
|
+
const re = new RegExp(`\\b${attr}="([^"]*)"`, "");
|
|
12268
|
+
const m = re.exec(openTag);
|
|
12269
|
+
return m ? decodeXml(m[1] ?? "") : "";
|
|
12310
12270
|
}
|
|
12311
|
-
function
|
|
12312
|
-
|
|
12313
|
-
let
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
if (depth === 0 && topLevelCount === tableIndex + 1 && targetStart >= 0) {
|
|
12327
|
-
return { start: targetStart, end: tok.end };
|
|
12328
|
-
}
|
|
12271
|
+
function parseFormObjects(xml, sectionFile, startIndex = 0) {
|
|
12272
|
+
const results = [];
|
|
12273
|
+
let idx = startIndex;
|
|
12274
|
+
const tagSpecs = [
|
|
12275
|
+
{ xmlTag: "hp:btn", type: "button" },
|
|
12276
|
+
{ xmlTag: "hp:checkBtn", type: "checkBox" },
|
|
12277
|
+
{ xmlTag: "hp:radioBtn", type: "radioButton" },
|
|
12278
|
+
{ xmlTag: "hp:comboBox", type: "comboBox" },
|
|
12279
|
+
{ xmlTag: "hp:edit", type: "edit" }
|
|
12280
|
+
];
|
|
12281
|
+
const hits = [];
|
|
12282
|
+
for (const spec of tagSpecs) {
|
|
12283
|
+
const re = new RegExp(`<${spec.xmlTag}\\b`, "g");
|
|
12284
|
+
for (let m = re.exec(xml); m !== null; m = re.exec(xml)) {
|
|
12285
|
+
hits.push({ pos: m.index, xmlTag: spec.xmlTag, type: spec.type });
|
|
12329
12286
|
}
|
|
12330
12287
|
}
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12288
|
+
hits.sort((a, b) => a.pos - b.pos);
|
|
12289
|
+
for (const hit of hits) {
|
|
12290
|
+
const { pos, xmlTag, type } = hit;
|
|
12291
|
+
const openTagEnd = xml.indexOf(">", pos);
|
|
12292
|
+
if (openTagEnd < 0) continue;
|
|
12293
|
+
const openTag = xml.slice(pos, openTagEnd + 1);
|
|
12294
|
+
const closeTag = `</${xmlTag}>`;
|
|
12295
|
+
const closeIdx = xml.indexOf(closeTag, openTagEnd);
|
|
12296
|
+
if (closeIdx < 0) continue;
|
|
12297
|
+
const elementEnd = closeIdx + closeTag.length;
|
|
12298
|
+
const elementXml = xml.slice(pos, elementEnd);
|
|
12299
|
+
const name = getAttr(openTag, "name");
|
|
12300
|
+
let currentValue;
|
|
12301
|
+
let comboItems;
|
|
12302
|
+
switch (type) {
|
|
12303
|
+
case "button":
|
|
12304
|
+
currentValue = getAttr(openTag, "caption");
|
|
12305
|
+
break;
|
|
12306
|
+
case "checkBox":
|
|
12307
|
+
case "radioButton":
|
|
12308
|
+
currentValue = getAttr(openTag, "value") === "CHECKED";
|
|
12309
|
+
break;
|
|
12310
|
+
case "comboBox": {
|
|
12311
|
+
currentValue = getAttr(openTag, "selectedValue");
|
|
12312
|
+
const listItemRe = /<hp:listItem\b[^>]*/g;
|
|
12313
|
+
const items = [];
|
|
12314
|
+
for (let lm = listItemRe.exec(elementXml); lm !== null; lm = listItemRe.exec(elementXml)) {
|
|
12315
|
+
const itemValue = getAttr(lm[0] ?? "", "value");
|
|
12316
|
+
items.push(itemValue);
|
|
12317
|
+
}
|
|
12318
|
+
comboItems = items;
|
|
12319
|
+
break;
|
|
12320
|
+
}
|
|
12321
|
+
case "edit": {
|
|
12322
|
+
const textSelfClose = /<hp:text\s*\/>/;
|
|
12323
|
+
const textOpen = /<hp:text>/;
|
|
12324
|
+
const textClose = "</hp:text>";
|
|
12325
|
+
if (textSelfClose.test(elementXml)) {
|
|
12326
|
+
currentValue = "";
|
|
12327
|
+
} else {
|
|
12328
|
+
const tOpenIdx = elementXml.search(textOpen);
|
|
12329
|
+
if (tOpenIdx >= 0) {
|
|
12330
|
+
const afterOpen = elementXml.indexOf(">", tOpenIdx) + 1;
|
|
12331
|
+
const closePos = elementXml.indexOf(textClose, afterOpen);
|
|
12332
|
+
currentValue = closePos >= 0 ? decodeXml(elementXml.slice(afterOpen, closePos)) : "";
|
|
12333
|
+
} else {
|
|
12334
|
+
currentValue = "";
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
break;
|
|
12338
|
+
}
|
|
12342
12339
|
}
|
|
12340
|
+
results.push({
|
|
12341
|
+
index: idx++,
|
|
12342
|
+
name,
|
|
12343
|
+
type,
|
|
12344
|
+
currentValue,
|
|
12345
|
+
comboItems,
|
|
12346
|
+
sectionFile,
|
|
12347
|
+
posInSection: pos
|
|
12348
|
+
});
|
|
12343
12349
|
}
|
|
12344
|
-
|
|
12345
|
-
return { ...addr, ...span };
|
|
12350
|
+
return results;
|
|
12346
12351
|
}
|
|
12347
|
-
function
|
|
12348
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
12352
|
+
function applyFormObjectEdits(xml, edits) {
|
|
12349
12353
|
const results = edits.map(() => ({ success: false }));
|
|
12350
|
-
|
|
12351
|
-
{
|
|
12352
|
-
let d = 0;
|
|
12353
|
-
for (const tok of tokens) {
|
|
12354
|
-
if (tok.kind === "tbl_open") {
|
|
12355
|
-
if (d === 0) totalTopLevelTbls++;
|
|
12356
|
-
d++;
|
|
12357
|
-
} else if (tok.kind === "tbl_close") {
|
|
12358
|
-
d--;
|
|
12359
|
-
}
|
|
12360
|
-
}
|
|
12361
|
-
}
|
|
12362
|
-
const replacements = [];
|
|
12354
|
+
const patches = [];
|
|
12363
12355
|
for (let ei = 0; ei < edits.length; ei++) {
|
|
12364
12356
|
const edit = edits[ei];
|
|
12365
|
-
const
|
|
12366
|
-
|
|
12357
|
+
const { target, set, expected } = edit;
|
|
12358
|
+
const pos = target.posInSection;
|
|
12359
|
+
const xmlTag = typeToXmlTag(target.type);
|
|
12360
|
+
const openTagEnd = xml.indexOf(">", pos);
|
|
12361
|
+
if (openTagEnd < 0) {
|
|
12367
12362
|
results[ei] = {
|
|
12368
12363
|
success: false,
|
|
12369
|
-
error: `\
|
|
12364
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uC5EC\uB294 \uD0DC\uADF8 \uB05D\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12370
12365
|
};
|
|
12371
12366
|
continue;
|
|
12372
12367
|
}
|
|
12373
|
-
const
|
|
12374
|
-
const
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12368
|
+
const _openTag = xml.slice(pos, openTagEnd + 1);
|
|
12369
|
+
const closeTag = `</${xmlTag}>`;
|
|
12370
|
+
const closeIdx = xml.indexOf(closeTag, openTagEnd);
|
|
12371
|
+
if (closeIdx < 0) {
|
|
12372
|
+
results[ei] = {
|
|
12373
|
+
success: false,
|
|
12374
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uB2EB\uB294 \uD0DC\uADF8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12375
|
+
};
|
|
12376
|
+
continue;
|
|
12377
|
+
}
|
|
12378
|
+
const elementEnd = closeIdx + closeTag.length;
|
|
12379
|
+
const typeError = validateSetForType(target.type, set);
|
|
12380
|
+
if (typeError) {
|
|
12381
|
+
results[ei] = { success: false, error: typeError };
|
|
12382
|
+
continue;
|
|
12383
|
+
}
|
|
12384
|
+
const currentRaw = readCurrentValue(
|
|
12385
|
+
xml,
|
|
12386
|
+
pos,
|
|
12387
|
+
openTagEnd,
|
|
12388
|
+
openTagEnd + 1,
|
|
12389
|
+
elementEnd,
|
|
12390
|
+
target.type
|
|
12391
|
+
);
|
|
12392
|
+
if (expected !== void 0) {
|
|
12393
|
+
const mismatch = checkExpected(expected, currentRaw, target);
|
|
12394
|
+
if (mismatch) {
|
|
12395
|
+
results[ei] = { success: false, error: mismatch };
|
|
12396
|
+
continue;
|
|
12395
12397
|
}
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
+
}
|
|
12399
|
+
if (target.type === "comboBox" && set.selected !== void 0) {
|
|
12400
|
+
const items = target.comboItems ?? [];
|
|
12401
|
+
if (!items.includes(set.selected)) {
|
|
12402
|
+
const validList = items.map((v) => `"${v}"`).join(", ");
|
|
12398
12403
|
results[ei] = {
|
|
12399
12404
|
success: false,
|
|
12400
|
-
|
|
12401
|
-
error: `\uC140 (\uD45C ${edit.tableIndex}, \uD589 ${edit.row}, \uC5F4 ${edit.col})\uC758 \uD604\uC7AC \uD14D\uC2A4\uD2B8\uAC00 \uC608\uC0C1\uAC12\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uC608\uC0C1: "${edit.expectedText}", \uC2E4\uC81C: "${currentText}". \uC218\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
|
|
12405
|
+
error: `comboBox "${target.name}"\uC758 selected \uAC12 "${set.selected}"\uC774 \uC720\uD6A8\uD55C \uD56D\uBAA9\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uC720\uD6A8\uD55C \uD56D\uBAA9: ${validList || "(\uC5C6\uC74C)"}`
|
|
12402
12406
|
};
|
|
12403
|
-
|
|
12404
|
-
break;
|
|
12407
|
+
continue;
|
|
12405
12408
|
}
|
|
12406
|
-
|
|
12409
|
+
}
|
|
12410
|
+
let patchCreated = false;
|
|
12411
|
+
if (set.caption !== void 0) {
|
|
12412
|
+
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption", escapeXml2(set.caption));
|
|
12413
|
+
if (!patch) {
|
|
12407
12414
|
results[ei] = {
|
|
12408
12415
|
success: false,
|
|
12409
|
-
|
|
12410
|
-
error: `\uC140 (\uD45C ${edit.tableIndex}, \uD589 ${edit.row}, \uC5F4 ${edit.col})\uC5D0 \uD14D\uC2A4\uD2B8 \uB7F0\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. <hp:t> \uB610\uB294 <hp:t/> \uB7F0\uC774 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC140 \uAD6C\uC870\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
12416
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 caption \uC18D\uC131\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12411
12417
|
};
|
|
12412
|
-
|
|
12413
|
-
break;
|
|
12418
|
+
continue;
|
|
12414
12419
|
}
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12420
|
+
patches.push(patch);
|
|
12421
|
+
patchCreated = true;
|
|
12422
|
+
} else if (set.checked !== void 0) {
|
|
12423
|
+
const newValue = set.checked ? "CHECKED" : "UNCHECKED";
|
|
12424
|
+
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "value", newValue);
|
|
12425
|
+
if (!patch) {
|
|
12426
|
+
results[ei] = {
|
|
12427
|
+
success: false,
|
|
12428
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 value \uC18D\uC131\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12429
|
+
};
|
|
12430
|
+
continue;
|
|
12426
12431
|
}
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
+
patches.push(patch);
|
|
12433
|
+
patchCreated = true;
|
|
12434
|
+
} else if (set.selected !== void 0) {
|
|
12435
|
+
const patch = replaceAttrInOpenTag(
|
|
12436
|
+
xml,
|
|
12437
|
+
pos,
|
|
12438
|
+
openTagEnd,
|
|
12439
|
+
"selectedValue",
|
|
12440
|
+
escapeXml2(set.selected)
|
|
12441
|
+
);
|
|
12442
|
+
if (!patch) {
|
|
12443
|
+
results[ei] = {
|
|
12444
|
+
success: false,
|
|
12445
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 selectedValue \uC18D\uC131\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12446
|
+
};
|
|
12447
|
+
continue;
|
|
12432
12448
|
}
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12449
|
+
patches.push(patch);
|
|
12450
|
+
patchCreated = true;
|
|
12451
|
+
} else if (set.text !== void 0) {
|
|
12452
|
+
const elementContent = xml.slice(pos, elementEnd);
|
|
12453
|
+
const textPatch = replaceEditText(xml, pos, elementContent, set.text);
|
|
12454
|
+
if (!textPatch) {
|
|
12455
|
+
results[ei] = {
|
|
12456
|
+
success: false,
|
|
12457
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 <hp:text> \uC694\uC18C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12458
|
+
};
|
|
12459
|
+
continue;
|
|
12460
|
+
}
|
|
12461
|
+
patches.push(textPatch);
|
|
12462
|
+
patchCreated = true;
|
|
12437
12463
|
}
|
|
12438
|
-
if (!
|
|
12439
|
-
results[ei] = {
|
|
12440
|
-
|
|
12441
|
-
error: `\uD45C ${edit.tableIndex}\uC5D0\uC11C \uC140 (\uD589 ${edit.row}, \uC5F4 ${edit.col})\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. cellAddr colAddr="${edit.col}" rowAddr="${edit.row}"\uC5D0 \uD574\uB2F9\uD558\uB294 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12442
|
-
};
|
|
12464
|
+
if (!patchCreated) {
|
|
12465
|
+
results[ei] = { success: false, error: `\uD3B8\uC9D1 #${ei + 1}: set \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.` };
|
|
12466
|
+
continue;
|
|
12443
12467
|
}
|
|
12468
|
+
results[ei] = { success: true, oldValue: currentRaw };
|
|
12444
12469
|
}
|
|
12445
12470
|
if (results.some((r) => !r.success)) {
|
|
12446
12471
|
return { newXml: xml, results };
|
|
12447
12472
|
}
|
|
12448
|
-
const
|
|
12473
|
+
const sortedPatches = [...patches].sort((a, b) => b.from - a.from);
|
|
12449
12474
|
let result = xml;
|
|
12450
|
-
for (const patch of
|
|
12451
|
-
result = result.
|
|
12475
|
+
for (const patch of sortedPatches) {
|
|
12476
|
+
result = result.slice(0, patch.from) + patch.text + result.slice(patch.to);
|
|
12452
12477
|
}
|
|
12453
12478
|
return { newXml: result, results };
|
|
12454
12479
|
}
|
|
12455
|
-
|
|
12456
|
-
|
|
12480
|
+
function typeToXmlTag(type) {
|
|
12481
|
+
switch (type) {
|
|
12482
|
+
case "button":
|
|
12483
|
+
return "hp:btn";
|
|
12484
|
+
case "checkBox":
|
|
12485
|
+
return "hp:checkBtn";
|
|
12486
|
+
case "radioButton":
|
|
12487
|
+
return "hp:radioBtn";
|
|
12488
|
+
case "comboBox":
|
|
12489
|
+
return "hp:comboBox";
|
|
12490
|
+
case "edit":
|
|
12491
|
+
return "hp:edit";
|
|
12492
|
+
}
|
|
12493
|
+
}
|
|
12494
|
+
function validateSetForType(type, set) {
|
|
12495
|
+
const key = Object.keys(set).find((k) => set[k] !== void 0);
|
|
12496
|
+
if (!key) return "set \uD544\uB4DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. caption/checked/selected/text \uC911 \uD558\uB098\uB97C \uC9C0\uC815\uD558\uC138\uC694.";
|
|
12497
|
+
const allowed = {
|
|
12498
|
+
button: "caption",
|
|
12499
|
+
checkBox: "checked",
|
|
12500
|
+
radioButton: "checked",
|
|
12501
|
+
comboBox: "selected",
|
|
12502
|
+
edit: "text"
|
|
12503
|
+
};
|
|
12504
|
+
if (key !== allowed[type]) {
|
|
12505
|
+
const typeKo = {
|
|
12506
|
+
button: "PushButton",
|
|
12507
|
+
checkBox: "CheckBox",
|
|
12508
|
+
radioButton: "RadioButton",
|
|
12509
|
+
comboBox: "ComboBox",
|
|
12510
|
+
edit: "Edit"
|
|
12511
|
+
};
|
|
12512
|
+
return `\uD0C0\uC785 \uBD88\uC77C\uCE58: ${typeKo[type]} \uC591\uC2DD \uAC1C\uCCB4\uC5D0\uB294 "${allowed[type]}" \uD544\uB4DC\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5\uD558\uC9C0\uB9CC "${key}"\uC774(\uAC00) \uC9C0\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`;
|
|
12513
|
+
}
|
|
12514
|
+
return null;
|
|
12515
|
+
}
|
|
12516
|
+
function readCurrentValue(xml, pos, openTagEnd, _afterOpen, elementEnd, type) {
|
|
12517
|
+
const openTag = xml.slice(pos, openTagEnd + 1);
|
|
12518
|
+
switch (type) {
|
|
12519
|
+
case "button":
|
|
12520
|
+
return decodeXml(getAttr(openTag, "caption"));
|
|
12521
|
+
case "checkBox":
|
|
12522
|
+
case "radioButton":
|
|
12523
|
+
return getAttr(openTag, "value") === "CHECKED";
|
|
12524
|
+
case "comboBox":
|
|
12525
|
+
return decodeXml(getAttr(openTag, "selectedValue"));
|
|
12526
|
+
case "edit": {
|
|
12527
|
+
const elementContent = xml.slice(pos, elementEnd);
|
|
12528
|
+
if (/<hp:text\s*\/>/.test(elementContent)) return "";
|
|
12529
|
+
const tOpenIdx = elementContent.search(/<hp:text>/);
|
|
12530
|
+
if (tOpenIdx >= 0) {
|
|
12531
|
+
const afterOpen = elementContent.indexOf(">", tOpenIdx) + 1;
|
|
12532
|
+
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
12533
|
+
return closePos >= 0 ? decodeXml(elementContent.slice(afterOpen, closePos)) : "";
|
|
12534
|
+
}
|
|
12535
|
+
return "";
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
function checkExpected(expected, currentRaw, target) {
|
|
12540
|
+
let expectedVal;
|
|
12541
|
+
if (expected.caption !== void 0) expectedVal = expected.caption;
|
|
12542
|
+
else if (expected.checked !== void 0) expectedVal = expected.checked;
|
|
12543
|
+
else if (expected.selected !== void 0) expectedVal = expected.selected;
|
|
12544
|
+
else if (expected.text !== void 0) expectedVal = expected.text;
|
|
12545
|
+
if (expectedVal === void 0) return null;
|
|
12546
|
+
const matches = typeof expectedVal === "boolean" ? currentRaw === expectedVal : String(currentRaw) === String(expectedVal);
|
|
12547
|
+
if (!matches) {
|
|
12548
|
+
return `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uD604\uC7AC \uAC12\uC774 \uC608\uC0C1\uAC12\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uC608\uC0C1: ${JSON.stringify(expectedVal)}, \uC2E4\uC81C: ${JSON.stringify(currentRaw)}. \uC218\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`;
|
|
12549
|
+
}
|
|
12550
|
+
return null;
|
|
12551
|
+
}
|
|
12552
|
+
function replaceAttrInOpenTag(xml, tagStart, tagEnd, attr, newValue) {
|
|
12553
|
+
const tagStr = xml.slice(tagStart, tagEnd + 1);
|
|
12554
|
+
const re = new RegExp(`\\b(${attr}=")([^"]*)(")`, "");
|
|
12555
|
+
const m = re.exec(tagStr);
|
|
12556
|
+
if (!m) return null;
|
|
12557
|
+
const relFrom = m.index + (m[1]?.length ?? 0);
|
|
12558
|
+
const relTo = relFrom + (m[2]?.length ?? 0);
|
|
12559
|
+
return {
|
|
12560
|
+
from: tagStart + relFrom,
|
|
12561
|
+
to: tagStart + relTo,
|
|
12562
|
+
text: newValue
|
|
12563
|
+
};
|
|
12564
|
+
}
|
|
12565
|
+
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
12566
|
+
const escaped = escapeXml2(newText);
|
|
12567
|
+
const selfCloseRe = /<hp:text\s*\/>/;
|
|
12568
|
+
const scm = selfCloseRe.exec(elementContent);
|
|
12569
|
+
if (scm) {
|
|
12570
|
+
return {
|
|
12571
|
+
from: pos + scm.index,
|
|
12572
|
+
to: pos + scm.index + scm[0].length,
|
|
12573
|
+
text: `<hp:text>${escaped}</hp:text>`
|
|
12574
|
+
};
|
|
12575
|
+
}
|
|
12576
|
+
const openRe = /<hp:text>/;
|
|
12577
|
+
const om = openRe.exec(elementContent);
|
|
12578
|
+
if (om) {
|
|
12579
|
+
const afterOpen = om.index + om[0].length;
|
|
12580
|
+
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
12581
|
+
if (closePos < 0) return null;
|
|
12582
|
+
return {
|
|
12583
|
+
from: pos + afterOpen,
|
|
12584
|
+
to: pos + closePos,
|
|
12585
|
+
text: escaped
|
|
12586
|
+
};
|
|
12587
|
+
}
|
|
12588
|
+
return null;
|
|
12589
|
+
}
|
|
12590
|
+
async function listFormObjectsFromZip(zip) {
|
|
12457
12591
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12458
12592
|
const sectionXmls = [];
|
|
12459
|
-
const
|
|
12593
|
+
const objects = [];
|
|
12594
|
+
let globalIndex = 0;
|
|
12460
12595
|
for (const sf of sectionFiles) {
|
|
12461
12596
|
const entry = zip.file(sf);
|
|
12462
12597
|
const xml = entry ? await entry.async("string") : "";
|
|
12463
12598
|
sectionXmls.push(xml);
|
|
12464
|
-
const
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
for (const tok of tokens) {
|
|
12468
|
-
if (tok.kind === "tbl_open") {
|
|
12469
|
-
if (depth === 0) count++;
|
|
12470
|
-
depth++;
|
|
12471
|
-
} else if (tok.kind === "tbl_close") {
|
|
12472
|
-
depth--;
|
|
12473
|
-
}
|
|
12474
|
-
}
|
|
12475
|
-
sectionTblCounts.push(count);
|
|
12476
|
-
}
|
|
12477
|
-
const sectionEdits = sectionFiles.map(() => []);
|
|
12478
|
-
let offset = 0;
|
|
12479
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
12480
|
-
const count = sectionTblCounts[si] ?? 0;
|
|
12481
|
-
for (let ei = 0; ei < edits.length; ei++) {
|
|
12482
|
-
const edit = edits[ei];
|
|
12483
|
-
if (edit.tableIndex >= offset && edit.tableIndex < offset + count) {
|
|
12484
|
-
const secEdits = sectionEdits[si];
|
|
12485
|
-
if (secEdits) {
|
|
12486
|
-
secEdits.push({
|
|
12487
|
-
tableIndex: edit.tableIndex - offset,
|
|
12488
|
-
// 섹션 내 상대 인덱스
|
|
12489
|
-
row: edit.row,
|
|
12490
|
-
col: edit.col,
|
|
12491
|
-
newText: edit.newText,
|
|
12492
|
-
expectedText: edit.expectedText,
|
|
12493
|
-
originalEditIdx: ei
|
|
12494
|
-
});
|
|
12495
|
-
}
|
|
12496
|
-
}
|
|
12497
|
-
}
|
|
12498
|
-
offset += count;
|
|
12599
|
+
const parsed = parseFormObjects(xml, sf, globalIndex);
|
|
12600
|
+
objects.push(...parsed);
|
|
12601
|
+
globalIndex += parsed.length;
|
|
12499
12602
|
}
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
const newSectionXmls = [...sectionXmls];
|
|
12506
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
12507
|
-
const sEdits = sectionEdits[si] ?? [];
|
|
12508
|
-
if (sEdits.length === 0) continue;
|
|
12509
|
-
const srcXml = sectionXmls[si] ?? "";
|
|
12510
|
-
const { newXml, results } = applyCellEditsToSectionXml(srcXml, sEdits);
|
|
12511
|
-
newSectionXmls[si] = newXml;
|
|
12512
|
-
for (let i = 0; i < sEdits.length; i++) {
|
|
12513
|
-
const sEdit = sEdits[i];
|
|
12514
|
-
const res = results[i];
|
|
12515
|
-
if (sEdit && res) {
|
|
12516
|
-
allResults[sEdit.originalEditIdx] = res;
|
|
12517
|
-
}
|
|
12518
|
-
}
|
|
12603
|
+
return { objects, sectionFiles, sectionXmls };
|
|
12604
|
+
}
|
|
12605
|
+
function validateHwpxBuffer(ext, buffer) {
|
|
12606
|
+
if (ext === ".hwp") {
|
|
12607
|
+
return "\uC624\uB958: \uC774 \uD234\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
12519
12608
|
}
|
|
12520
|
-
if (
|
|
12521
|
-
return
|
|
12609
|
+
if (ext !== ".hwpx") {
|
|
12610
|
+
return `\uC624\uB958: \uC774 \uD234\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}`;
|
|
12522
12611
|
}
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
if (mimetypeEntry) {
|
|
12526
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
12612
|
+
if (buffer[0] !== 80 || buffer[1] !== 75) {
|
|
12613
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4.";
|
|
12527
12614
|
}
|
|
12528
|
-
|
|
12529
|
-
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12615
|
+
return null;
|
|
12616
|
+
}
|
|
12617
|
+
var listFormObjectsSchema = z5.object({
|
|
12618
|
+
path: z5.string().describe("\uC77D\uC744 .hwpx \uD30C\uC77C \uACBD\uB85C")
|
|
12619
|
+
});
|
|
12620
|
+
var formEditSetSchema = z5.object({
|
|
12621
|
+
caption: z5.string().optional().describe("PushButton \uCEA1\uC158 \uD14D\uC2A4\uD2B8"),
|
|
12622
|
+
checked: z5.boolean().optional().describe("CheckBox/RadioButton \uCCB4\uD06C \uC0C1\uD0DC (true=CHECKED)"),
|
|
12623
|
+
selected: z5.string().optional().describe("ComboBox \uC120\uD0DD \uAC12 (listItem \uC911 \uD558\uB098\uC5EC\uC57C \uD568)"),
|
|
12624
|
+
text: z5.string().optional().describe("Edit \uD14D\uC2A4\uD2B8 \uB0B4\uC6A9")
|
|
12625
|
+
}).refine(
|
|
12626
|
+
(v) => {
|
|
12627
|
+
const keys = ["caption", "checked", "selected", "text"].filter(
|
|
12628
|
+
(k) => v[k] !== void 0
|
|
12629
|
+
);
|
|
12630
|
+
return keys.length === 1;
|
|
12631
|
+
},
|
|
12632
|
+
{ message: "set \uD544\uB4DC\uB294 caption/checked/selected/text \uC911 \uC815\uD655\uD788 \uD558\uB098\uB9CC \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4." }
|
|
12633
|
+
);
|
|
12634
|
+
var formEditExpectedSchema = z5.object({
|
|
12635
|
+
caption: z5.string().optional(),
|
|
12636
|
+
checked: z5.boolean().optional(),
|
|
12637
|
+
selected: z5.string().optional(),
|
|
12638
|
+
text: z5.string().optional()
|
|
12639
|
+
}).optional();
|
|
12640
|
+
var formEditItemSchema = z5.object({
|
|
12641
|
+
name: z5.string().describe("\uC591\uC2DD \uAC1C\uCCB4\uC758 name \uC18D\uC131 \uAC12"),
|
|
12642
|
+
index: z5.number().int().nonnegative().optional().describe("\uB3D9\uC77C name\uC774 \uC5EC\uB7FF\uC778 \uACBD\uC6B0 \uBB38\uC11C \uC804\uCCB4 0-based \uC778\uB371\uC2A4\uB85C \uAD6C\uBD84"),
|
|
12643
|
+
set: formEditSetSchema.describe("\uBCC0\uACBD\uD560 \uAC12 (caption/checked/selected/text \uC911 \uD558\uB098)"),
|
|
12644
|
+
expected: formEditExpectedSchema.describe(
|
|
12645
|
+
"\uD604\uC7AC \uAC12 \uC0AC\uC804 \uAC80\uC99D (\uC548\uC804 \uC635\uC158). \uC2E4\uC81C \uAC12\uC774 \uB2E4\uB974\uBA74 \uC774 \uD3B8\uC9D1\uC744 \uCDE8\uC18C\uD558\uACE0 \uC624\uB958 \uBC18\uD658."
|
|
12646
|
+
)
|
|
12647
|
+
});
|
|
12648
|
+
var proposeFormObjectSchema = z5.object({
|
|
12649
|
+
path: z5.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C"),
|
|
12650
|
+
edits: z5.array(formEditItemSchema).min(1).describe("\uC591\uC2DD \uAC1C\uCCB4 \uD3B8\uC9D1 \uBAA9\uB85D"),
|
|
12651
|
+
summary: z5.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12652
|
+
});
|
|
12653
|
+
var listFormObjectsTool = {
|
|
12654
|
+
name: "list_form_objects",
|
|
12655
|
+
description: "HWPX \uBB38\uC11C\uC758 \uC591\uC2DD \uAC1C\uCCB4(form object) \uBAA9\uB85D\uC744 \uC5F4\uAC70\uD569\uB2C8\uB2E4. PushButton, CheckBox, RadioButton, ComboBox, Edit \uB2E4\uC12F \uAC00\uC9C0 \uD0C0\uC785\uC744 \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uAC01 \uC591\uC2DD \uAC1C\uCCB4\uC758 \uC774\uB984(name), \uD0C0\uC785, \uD604\uC7AC \uAC12, ComboBox\uC758 \uACBD\uC6B0 \uC120\uD0DD \uAC00\uB2A5\uD55C \uD56D\uBAA9 \uBAA9\uB85D\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. propose_form_object\uB85C \uAC12\uC744 \uC218\uC815\uD558\uAE30 \uC804\uC5D0 \uC774 \uD234\uB85C \uBA3C\uC800 \uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. .hwpx \uD30C\uC77C \uC804\uC6A9\uC785\uB2C8\uB2E4.",
|
|
12656
|
+
inputSchema: listFormObjectsSchema,
|
|
12657
|
+
requiresApproval: false,
|
|
12658
|
+
execute: async ({
|
|
12659
|
+
input,
|
|
12660
|
+
ctx
|
|
12661
|
+
}) => {
|
|
12662
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12663
|
+
const ext = extname5(safePath).toLowerCase();
|
|
12664
|
+
let buffer;
|
|
12665
|
+
try {
|
|
12666
|
+
buffer = await readFile6(safePath);
|
|
12667
|
+
} catch {
|
|
12668
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
12669
|
+
}
|
|
12670
|
+
const validationError = validateHwpxBuffer(ext, new Uint8Array(buffer.buffer));
|
|
12671
|
+
if (validationError) return validationError;
|
|
12672
|
+
let zip;
|
|
12673
|
+
try {
|
|
12674
|
+
zip = await import_jszip3.default.loadAsync(new Uint8Array(buffer.buffer));
|
|
12675
|
+
} catch {
|
|
12676
|
+
return "\uC624\uB958: .hwpx(ZIP) \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
12677
|
+
}
|
|
12678
|
+
const { objects } = await listFormObjectsFromZip(zip);
|
|
12679
|
+
if (objects.length === 0) {
|
|
12680
|
+
return "\uC774 \uBB38\uC11C\uC5D0\uB294 \uC591\uC2DD \uAC1C\uCCB4(form object)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
12681
|
+
}
|
|
12682
|
+
const lines = [`\uCD1D ${objects.length}\uAC1C\uC758 \uC591\uC2DD \uAC1C\uCCB4\uAC00 \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
12683
|
+
`];
|
|
12684
|
+
const typeKo = {
|
|
12685
|
+
button: "PushButton",
|
|
12686
|
+
checkBox: "CheckBox",
|
|
12687
|
+
radioButton: "RadioButton",
|
|
12688
|
+
comboBox: "ComboBox",
|
|
12689
|
+
edit: "Edit"
|
|
12690
|
+
};
|
|
12691
|
+
for (const obj of objects) {
|
|
12692
|
+
let valueStr;
|
|
12693
|
+
if (typeof obj.currentValue === "boolean") {
|
|
12694
|
+
valueStr = obj.currentValue ? "\uCCB4\uD06C\uB428 (CHECKED)" : "\uCCB4\uD06C \uD574\uC81C\uB428 (UNCHECKED)";
|
|
12695
|
+
} else {
|
|
12696
|
+
valueStr = `"${obj.currentValue}"`;
|
|
12697
|
+
}
|
|
12698
|
+
let line = `[${obj.index}] name="${obj.name}" | \uD0C0\uC785: ${typeKo[obj.type]} | \uD604\uC7AC \uAC12: ${valueStr}`;
|
|
12699
|
+
if (obj.type === "comboBox" && obj.comboItems) {
|
|
12700
|
+
const itemList = obj.comboItems.map((v) => `"${v}"`).join(", ");
|
|
12701
|
+
line += ` | \uD56D\uBAA9: [${itemList}]`;
|
|
12702
|
+
}
|
|
12703
|
+
lines.push(line);
|
|
12535
12704
|
}
|
|
12705
|
+
return lines.join("\n");
|
|
12536
12706
|
}
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
description: "HWPX \uBB38\uC11C\uC758 \uD45C \uC140 \uB0B4\uC6A9\uC744 XML \uC9C1\uC811 \uD328\uCE58 \uBC29\uC2DD\uC73C\uB85C \uC218\uC815\uD569\uB2C8\uB2E4. \uBCD1\uD569 \uC140(cellSpan/rowSpan)\uC774 \uC788\uB294 \uD45C\uC5D0\uC11C\uB3C4 \uBCD1\uD569 \uAD6C\uC870\uB97C \uC644\uC804\uD788 \uBCF4\uC874\uD569\uB2C8\uB2E4. \uBE48 \uC140(<hp:t/> self-closing \uB7F0)\uB3C4 \uCC44\uC6B8 \uC218 \uC788\uC5B4 \uC591\uC2DD(form) \uD3B8\uC9D1\uC5D0 \uC801\uD569\uD569\uB2C8\uB2E4. \uC140 \uC8FC\uC18C \uC9C0\uC815 \uBC29\uBC95: (1) \uC88C\uD45C \uBAA8\uB4DC \u2014 tableIndex + row + col \uC9C1\uC811 \uC9C0\uC815. (2) \uB808\uC774\uBE14 \uBAA8\uB4DC \u2014 label(\uC778\uC811 \uB808\uC774\uBE14 \uC140 \uD14D\uC2A4\uD2B8) + direction(right/below, \uAE30\uBCF8 right) + \uC120\uD0DD\uC801 tableIndex\uB85C \uB808\uC774\uBE14 \uC606/\uC544\uB798 \uC140\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uD3B8\uC9D1. \uBCD1\uD569 \uB808\uC774\uBE14 \uC140\uB3C4 colSpan/rowSpan\uC744 \uBC18\uC601\uD558\uC5EC \uB300\uC0C1 \uC140\uC744 \uACC4\uC0B0\uD569\uB2C8\uB2E4. propose_edit/propose_form_fill\uC740 \uB9C8\uD06C\uB2E4\uC6B4 \uB77C\uC6B4\uB4DC\uD2B8\uB9BD\uC73C\uB85C \uBCD1\uD569 \uC140\uC744 \uC18C\uC2E4\uC2DC\uD0A4\uBBC0\uB85C, \uBCD1\uD569 \uC140\uC774 \uC788\uB294 \uD45C\uB97C \uC218\uC815\uD560 \uB54C\uB294 \uC774 \uD234\uC744 \uC0AC\uC6A9\uD558\uC138\uC694. .hwpx \uD30C\uC77C \uC804\uC6A9\uC785\uB2C8\uB2E4. .hwp\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70 Hancom\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. \uC218\uC815 \uC804\uC5D0 \uBC18\uB4DC\uC2DC read_document\uB85C \uC6D0\uBCF8\uC744 \uC77D\uACE0, expectedText\uB97C \uC9C0\uC815\uD558\uC5EC \uC548\uC804\uD558\uAC8C \uC218\uC815\uD558\uC138\uC694. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
12543
|
-
inputSchema: proposeCellEditSchema,
|
|
12707
|
+
};
|
|
12708
|
+
var proposeFormObjectTool = {
|
|
12709
|
+
name: "propose_form_object",
|
|
12710
|
+
description: "HWPX \uBB38\uC11C\uC758 \uC591\uC2DD \uAC1C\uCCB4(form object) \uAC12\uC744 XML \uC9C1\uC811 \uD328\uCE58 \uBC29\uC2DD\uC73C\uB85C \uC218\uC815\uD569\uB2C8\uB2E4. PushButton(caption), CheckBox/RadioButton(checked), ComboBox(selected), Edit(text)\uB97C \uC218\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. kordoc IR\uC5D0 \uC591\uC2DD \uAC1C\uCCB4 \uD0C0\uC785\uC774 \uC5C6\uC73C\uBBC0\uB85C section XML\uC744 \uC9C1\uC811 \uD328\uCE58\uD569\uB2C8\uB2E4. \uC218\uC815 \uC804\uC5D0 list_form_objects\uB85C \uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. name\uC774 \uC911\uBCF5\uB41C \uACBD\uC6B0 index\uB85C \uB300\uC0C1\uC744 \uC9C0\uC815\uD558\uC138\uC694. \uBCC0\uACBD \uC0AC\uD56D\uC740 \uC0AC\uC6A9\uC790 \uC2B9\uC778 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4. .hwpx \uD30C\uC77C \uC804\uC6A9\uC785\uB2C8\uB2E4.",
|
|
12711
|
+
inputSchema: proposeFormObjectSchema,
|
|
12544
12712
|
requiresApproval: true,
|
|
12545
12713
|
propose: async ({
|
|
12546
12714
|
input,
|
|
@@ -12548,196 +12716,145 @@ var proposeCellEditTool = {
|
|
|
12548
12716
|
}) => {
|
|
12549
12717
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12550
12718
|
const ext = extname5(safePath).toLowerCase();
|
|
12551
|
-
if (ext === ".hwp") {
|
|
12552
|
-
return "\uC624\uB958: propose_cell_edit\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uB610\uB294 propose_edit\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC73C\uB098, \uBCD1\uD569 \uC140\uC774 \uC18C\uC2E4\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
12553
|
-
}
|
|
12554
|
-
if (ext !== ".hwpx") {
|
|
12555
|
-
return `\uC624\uB958: propose_cell_edit\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}. \uD45C \uC140 \uC9C1\uC811 \uD3B8\uC9D1\uC740 .hwpx \uD3EC\uB9F7\uC5D0\uC11C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4.`;
|
|
12556
|
-
}
|
|
12557
12719
|
let originalBuffer;
|
|
12558
12720
|
try {
|
|
12559
|
-
originalBuffer = await
|
|
12721
|
+
originalBuffer = await readFile6(safePath);
|
|
12560
12722
|
} catch {
|
|
12561
|
-
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}
|
|
12562
|
-
}
|
|
12563
|
-
if (originalBuffer[0] !== 80 || originalBuffer[1] !== 75) {
|
|
12564
|
-
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
12565
|
-
}
|
|
12566
|
-
const zipForLabel = await import_jszip2.default.loadAsync(new Uint8Array(originalBuffer.buffer));
|
|
12567
|
-
const sectionFilesForLabel = Object.keys(zipForLabel.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12568
|
-
const sectionInfos = [];
|
|
12569
|
-
let globalTblOffset = 0;
|
|
12570
|
-
for (const sf of sectionFilesForLabel) {
|
|
12571
|
-
const entry = zipForLabel.file(sf);
|
|
12572
|
-
const xml = entry ? await entry.async("string") : "";
|
|
12573
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
12574
|
-
let count = 0;
|
|
12575
|
-
let d = 0;
|
|
12576
|
-
for (const tok of tokens) {
|
|
12577
|
-
if (tok.kind === "tbl_open") {
|
|
12578
|
-
if (d === 0) count++;
|
|
12579
|
-
d++;
|
|
12580
|
-
} else if (tok.kind === "tbl_close") {
|
|
12581
|
-
d--;
|
|
12582
|
-
}
|
|
12583
|
-
}
|
|
12584
|
-
sectionInfos.push({ xml, tblCount: count, globalOffset: globalTblOffset });
|
|
12585
|
-
globalTblOffset += count;
|
|
12723
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
12586
12724
|
}
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
const localIdx2 = scopedTableIndex - si.globalOffset;
|
|
12596
|
-
if (localIdx2 < 0 || localIdx2 >= si.tblCount) continue;
|
|
12597
|
-
localStart = localIdx2;
|
|
12598
|
-
localEnd = localIdx2;
|
|
12599
|
-
}
|
|
12600
|
-
for (let li = localStart; li <= localEnd; li++) {
|
|
12601
|
-
const tblRange = findTopLevelTableRange(tokens, li);
|
|
12602
|
-
if (!tblRange) continue;
|
|
12603
|
-
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
12604
|
-
for (const tc of directTcs) {
|
|
12605
|
-
const cellText = readOwnTextFromTc(si.xml, tokens, tc.start, tc.end).trim();
|
|
12606
|
-
if (cellText === trimmedLabel) {
|
|
12607
|
-
const addrSpan2 = readCellAddrSpan(tokens, tc.start, tc.end);
|
|
12608
|
-
if (addrSpan2) {
|
|
12609
|
-
allMatches.push({
|
|
12610
|
-
globalTableIndex: si.globalOffset + li,
|
|
12611
|
-
addrSpan: addrSpan2,
|
|
12612
|
-
sectionXml: si.xml,
|
|
12613
|
-
sectionOffset: si.globalOffset
|
|
12614
|
-
});
|
|
12615
|
-
}
|
|
12616
|
-
}
|
|
12617
|
-
}
|
|
12618
|
-
}
|
|
12619
|
-
}
|
|
12620
|
-
if (allMatches.length === 0) {
|
|
12621
|
-
const scope = scopedTableIndex !== void 0 ? `\uD45C ${scopedTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
12622
|
-
return {
|
|
12623
|
-
error: `\uB808\uC774\uBE14 "${label}"\uC744(\uB97C) ${scope}\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694.`
|
|
12624
|
-
};
|
|
12625
|
-
}
|
|
12626
|
-
if (allMatches.length > 1) {
|
|
12627
|
-
const locs = allMatches.map(
|
|
12628
|
-
(m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.addrSpan.rowAddr}, \uC5F4 ${m.addrSpan.colAddr})`
|
|
12629
|
-
).join(", ");
|
|
12630
|
-
return {
|
|
12631
|
-
error: `\uB808\uC774\uBE14 "${label}"\uC774(\uAC00) \uC5EC\uB7EC \uC140\uC5D0\uC11C \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${locs}. tableIndex\uB85C \uD0D0\uC0C9 \uBC94\uC704\uB97C \uC881\uD788\uAC70\uB098 \uC88C\uD45C(row/col)\uB97C \uC9C1\uC811 \uC9C0\uC815\uD558\uC138\uC694.`
|
|
12632
|
-
};
|
|
12633
|
-
}
|
|
12634
|
-
const match = allMatches[0];
|
|
12635
|
-
const { addrSpan, globalTableIndex, sectionXml, sectionOffset } = match;
|
|
12636
|
-
let targetRow;
|
|
12637
|
-
let targetCol;
|
|
12638
|
-
if (direction === "right") {
|
|
12639
|
-
targetRow = addrSpan.rowAddr;
|
|
12640
|
-
targetCol = addrSpan.colAddr + addrSpan.colSpan;
|
|
12641
|
-
} else {
|
|
12642
|
-
targetRow = addrSpan.rowAddr + addrSpan.rowSpan;
|
|
12643
|
-
targetCol = addrSpan.colAddr;
|
|
12644
|
-
}
|
|
12645
|
-
const localIdx = globalTableIndex - sectionOffset;
|
|
12646
|
-
const tokens2 = tokenizeHwpxXml(sectionXml);
|
|
12647
|
-
const tblRange2 = findTopLevelTableRange(tokens2, localIdx);
|
|
12648
|
-
if (!tblRange2) {
|
|
12649
|
-
return { error: `\uD45C ${globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
12650
|
-
}
|
|
12651
|
-
const directTcs2 = collectDirectTcRanges(tokens2, tblRange2.start, tblRange2.end);
|
|
12652
|
-
const tblTokens2 = tokens2.filter((t) => t.pos >= tblRange2.start && t.pos < tblRange2.end);
|
|
12653
|
-
let targetExists = false;
|
|
12654
|
-
for (const tc of directTcs2) {
|
|
12655
|
-
const tcTokens = tblTokens2.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
12656
|
-
if (tcTokens.some(
|
|
12657
|
-
(t) => t.kind === "cell_addr" && t.colAddr === targetCol && t.rowAddr === targetRow
|
|
12658
|
-
)) {
|
|
12659
|
-
targetExists = true;
|
|
12660
|
-
break;
|
|
12661
|
-
}
|
|
12662
|
-
}
|
|
12663
|
-
if (!targetExists) {
|
|
12664
|
-
const dirLabel = direction === "right" ? "\uC624\uB978\uCABD" : "\uC544\uB798";
|
|
12665
|
-
return {
|
|
12666
|
-
error: `\uB808\uC774\uBE14 "${label}" (\uD45C ${globalTableIndex}, \uD589 ${addrSpan.rowAddr}, \uC5F4 ${addrSpan.colAddr})\uC758 ${dirLabel} \uC140 (\uD589 ${targetRow}, \uC5F4 ${targetCol})\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. direction \uB610\uB294 \uC88C\uD45C\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
12667
|
-
};
|
|
12668
|
-
}
|
|
12669
|
-
return { tableIndex: globalTableIndex, row: targetRow, col: targetCol };
|
|
12725
|
+
const bufArray = new Uint8Array(originalBuffer.buffer);
|
|
12726
|
+
const validationError = validateHwpxBuffer(ext, bufArray);
|
|
12727
|
+
if (validationError) return validationError;
|
|
12728
|
+
let zip;
|
|
12729
|
+
try {
|
|
12730
|
+
zip = await import_jszip3.default.loadAsync(bufArray);
|
|
12731
|
+
} catch {
|
|
12732
|
+
return "\uC624\uB958: .hwpx(ZIP) \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
12670
12733
|
}
|
|
12734
|
+
const { objects, sectionFiles, sectionXmls } = await listFormObjectsFromZip(zip);
|
|
12671
12735
|
const resolvedEdits = [];
|
|
12672
12736
|
const resolveErrors = [];
|
|
12673
|
-
for (let
|
|
12674
|
-
const
|
|
12675
|
-
if (!
|
|
12676
|
-
|
|
12737
|
+
for (let ei = 0; ei < input.edits.length; ei++) {
|
|
12738
|
+
const edit = input.edits[ei];
|
|
12739
|
+
if (!edit) continue;
|
|
12740
|
+
const candidates = objects.filter((o) => o.name === edit.name);
|
|
12741
|
+
if (candidates.length === 0) {
|
|
12677
12742
|
resolveErrors.push(
|
|
12678
|
-
`\uD3B8\uC9D1 #${
|
|
12743
|
+
`\uD3B8\uC9D1 #${ei + 1}: name="${edit.name}"\uC778 \uC591\uC2DD \uAC1C\uCCB4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. list_form_objects\uB85C \uBB38\uC11C\uC758 \uC591\uC2DD \uAC1C\uCCB4 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694.`
|
|
12679
12744
|
);
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
newText: e.newText,
|
|
12691
|
-
expectedText: e.expectedText,
|
|
12692
|
-
label: e.label
|
|
12693
|
-
});
|
|
12745
|
+
continue;
|
|
12746
|
+
}
|
|
12747
|
+
let target;
|
|
12748
|
+
if (candidates.length > 1) {
|
|
12749
|
+
if (edit.index === void 0) {
|
|
12750
|
+
const indices = candidates.map((c) => c.index).join(", ");
|
|
12751
|
+
resolveErrors.push(
|
|
12752
|
+
`\uD3B8\uC9D1 #${ei + 1}: name="${edit.name}"\uC778 \uC591\uC2DD \uAC1C\uCCB4\uAC00 ${candidates.length}\uAC1C \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (\uBB38\uC11C \uC778\uB371\uC2A4: ${indices}). index \uD544\uB4DC\uB85C \uB300\uC0C1\uC744 \uC9C0\uC815\uD558\uC138\uC694.`
|
|
12753
|
+
);
|
|
12754
|
+
continue;
|
|
12694
12755
|
}
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12756
|
+
const byIndex = candidates.find((c) => c.index === edit.index);
|
|
12757
|
+
if (!byIndex) {
|
|
12758
|
+
resolveErrors.push(
|
|
12759
|
+
`\uD3B8\uC9D1 #${ei + 1}: name="${edit.name}", index=${edit.index}\uC778 \uC591\uC2DD \uAC1C\uCCB4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12760
|
+
);
|
|
12761
|
+
continue;
|
|
12762
|
+
}
|
|
12763
|
+
target = byIndex;
|
|
12703
12764
|
} else {
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12765
|
+
target = candidates[0];
|
|
12766
|
+
if (edit.index !== void 0 && edit.index !== target.index) {
|
|
12767
|
+
resolveErrors.push(
|
|
12768
|
+
`\uD3B8\uC9D1 #${ei + 1}: name="${edit.name}"\uC758 \uBB38\uC11C \uC778\uB371\uC2A4\uB294 ${target.index}\uC774\uC9C0\uB9CC index=${edit.index}\uAC00 \uC9C0\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`
|
|
12769
|
+
);
|
|
12770
|
+
continue;
|
|
12771
|
+
}
|
|
12707
12772
|
}
|
|
12773
|
+
resolvedEdits.push({
|
|
12774
|
+
target,
|
|
12775
|
+
set: edit.set,
|
|
12776
|
+
expected: edit.expected,
|
|
12777
|
+
editIdx: ei
|
|
12778
|
+
});
|
|
12708
12779
|
}
|
|
12709
12780
|
if (resolveErrors.length > 0) {
|
|
12710
|
-
return `\uC624\uB958: \uB2E4\uC74C \
|
|
12781
|
+
return `\uC624\uB958: \uB2E4\uC74C \uD3B8\uC9D1 \uB300\uC0C1\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
|
|
12711
12782
|
${resolveErrors.join("\n")}`;
|
|
12712
12783
|
}
|
|
12713
|
-
const
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
editRequests
|
|
12723
|
-
);
|
|
12724
|
-
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
12725
|
-
if (failedResults.length > 0) {
|
|
12726
|
-
const messages = failedResults.map(({ r, i }) => {
|
|
12727
|
-
const e = resolvedEdits[i];
|
|
12728
|
-
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
12729
|
-
return `\uD3B8\uC9D1 #${i + 1} (${label}\uD45C ${e?.tableIndex ?? "?"}, \uD589 ${e?.row ?? "?"}, \uC5F4 ${e?.col ?? "?"}): ${r.error}`;
|
|
12784
|
+
const sectionEditMap = /* @__PURE__ */ new Map();
|
|
12785
|
+
for (const re of resolvedEdits) {
|
|
12786
|
+
const si = sectionFiles.indexOf(re.target.sectionFile);
|
|
12787
|
+
if (si < 0) continue;
|
|
12788
|
+
if (!sectionEditMap.has(si)) sectionEditMap.set(si, []);
|
|
12789
|
+
sectionEditMap.get(si).push({
|
|
12790
|
+
target: re.target,
|
|
12791
|
+
set: re.set,
|
|
12792
|
+
expected: re.expected
|
|
12730
12793
|
});
|
|
12794
|
+
}
|
|
12795
|
+
const sectionResults = [];
|
|
12796
|
+
const newSectionXmls = [...sectionXmls];
|
|
12797
|
+
for (const [si, edits] of sectionEditMap) {
|
|
12798
|
+
const xml = sectionXmls[si] ?? "";
|
|
12799
|
+
const { newXml, results } = applyFormObjectEdits(xml, edits);
|
|
12800
|
+
newSectionXmls[si] = newXml;
|
|
12801
|
+
sectionResults.push({ si, results, edits });
|
|
12802
|
+
}
|
|
12803
|
+
const failMessages = [];
|
|
12804
|
+
const successMap = /* @__PURE__ */ new Map();
|
|
12805
|
+
for (const sr of sectionResults) {
|
|
12806
|
+
for (let i = 0; i < sr.edits.length; i++) {
|
|
12807
|
+
const editReq = sr.edits[i];
|
|
12808
|
+
const result = sr.results[i];
|
|
12809
|
+
const resolved = resolvedEdits.find((r) => r.target === editReq.target);
|
|
12810
|
+
if (resolved) {
|
|
12811
|
+
successMap.set(resolved.editIdx, result);
|
|
12812
|
+
if (!result.success) {
|
|
12813
|
+
failMessages.push(
|
|
12814
|
+
`\uD3B8\uC9D1 #${resolved.editIdx + 1} (name="${editReq.target.name}"): ${result.error}`
|
|
12815
|
+
);
|
|
12816
|
+
}
|
|
12817
|
+
}
|
|
12818
|
+
}
|
|
12819
|
+
}
|
|
12820
|
+
if (failMessages.length > 0) {
|
|
12731
12821
|
return `\uC624\uB958: \uB2E4\uC74C \uD3B8\uC9D1\uC744 \uC801\uC6A9\uD560 \uC218 \uC5C6\uC5B4 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.
|
|
12732
|
-
${
|
|
12822
|
+
${failMessages.join("\n")}`;
|
|
12733
12823
|
}
|
|
12734
|
-
const
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12824
|
+
const out = new import_jszip3.default();
|
|
12825
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
12826
|
+
if (mimetypeEntry) {
|
|
12827
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
12828
|
+
}
|
|
12829
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
12830
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
12831
|
+
const si = sectionFiles.indexOf(name);
|
|
12832
|
+
if (si >= 0) {
|
|
12833
|
+
out.file(name, newSectionXmls[si] ?? "");
|
|
12834
|
+
} else {
|
|
12835
|
+
out.file(name, await entry.async("uint8array"));
|
|
12836
|
+
}
|
|
12837
|
+
}
|
|
12838
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
12839
|
+
const newBuffer = new Uint8Array(buf);
|
|
12840
|
+
const typeKo = {
|
|
12841
|
+
button: "PushButton",
|
|
12842
|
+
checkBox: "CheckBox",
|
|
12843
|
+
radioButton: "RadioButton",
|
|
12844
|
+
comboBox: "ComboBox",
|
|
12845
|
+
edit: "Edit"
|
|
12846
|
+
};
|
|
12847
|
+
const diffLines = ["| \uC591\uC2DD \uAC1C\uCCB4 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12848
|
+
for (const re of resolvedEdits) {
|
|
12849
|
+
const result = successMap.get(re.editIdx);
|
|
12850
|
+
const oldVal = result?.oldValue ?? "";
|
|
12851
|
+
const oldStr = typeof oldVal === "boolean" ? oldVal ? "CHECKED" : "UNCHECKED" : String(oldVal);
|
|
12852
|
+
let newStr;
|
|
12853
|
+
if (re.set.caption !== void 0) newStr = re.set.caption;
|
|
12854
|
+
else if (re.set.checked !== void 0) newStr = re.set.checked ? "CHECKED" : "UNCHECKED";
|
|
12855
|
+
else if (re.set.selected !== void 0) newStr = re.set.selected;
|
|
12856
|
+
else newStr = re.set.text ?? "";
|
|
12857
|
+
diffLines.push(`| ${re.target.name}(${typeKo[re.target.type]}) | ${oldStr} | ${newStr} |`);
|
|
12741
12858
|
}
|
|
12742
12859
|
const diff = diffLines.join("\n");
|
|
12743
12860
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
@@ -12746,7 +12863,7 @@ ${messages.join("\n")}`;
|
|
|
12746
12863
|
return {
|
|
12747
12864
|
proposal: {
|
|
12748
12865
|
id: proposalId,
|
|
12749
|
-
kind: "
|
|
12866
|
+
kind: "form-object",
|
|
12750
12867
|
targetPath: outputPath,
|
|
12751
12868
|
stagedPath,
|
|
12752
12869
|
summary: input.summary,
|
|
@@ -12763,6 +12880,92 @@ ${messages.join("\n")}`;
|
|
|
12763
12880
|
};
|
|
12764
12881
|
}
|
|
12765
12882
|
};
|
|
12883
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
12884
|
+
".hwp",
|
|
12885
|
+
".hwpx",
|
|
12886
|
+
".hwpml",
|
|
12887
|
+
".docx",
|
|
12888
|
+
".doc",
|
|
12889
|
+
".xlsx",
|
|
12890
|
+
".xls",
|
|
12891
|
+
".pdf",
|
|
12892
|
+
".pptx",
|
|
12893
|
+
".ppt",
|
|
12894
|
+
".md",
|
|
12895
|
+
".txt"
|
|
12896
|
+
]);
|
|
12897
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12898
|
+
"node_modules",
|
|
12899
|
+
".git",
|
|
12900
|
+
".DS_Store",
|
|
12901
|
+
"__pycache__",
|
|
12902
|
+
"dist",
|
|
12903
|
+
"build",
|
|
12904
|
+
".next"
|
|
12905
|
+
]);
|
|
12906
|
+
var MAX_DEPTH = 4;
|
|
12907
|
+
var MAX_FILES = 500;
|
|
12908
|
+
var listFilesSchema = z6.object({
|
|
12909
|
+
dir: z6.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
|
|
12910
|
+
});
|
|
12911
|
+
async function collectFiles(dir, cwd, depth, entries) {
|
|
12912
|
+
if (depth > MAX_DEPTH || entries.length >= MAX_FILES) return;
|
|
12913
|
+
let items;
|
|
12914
|
+
try {
|
|
12915
|
+
items = await readdir3(dir);
|
|
12916
|
+
} catch {
|
|
12917
|
+
return;
|
|
12918
|
+
}
|
|
12919
|
+
for (const item of items) {
|
|
12920
|
+
if (entries.length >= MAX_FILES) break;
|
|
12921
|
+
if (item.startsWith(".")) continue;
|
|
12922
|
+
if (SKIP_DIRS.has(item)) continue;
|
|
12923
|
+
const fullPath = join4(dir, item);
|
|
12924
|
+
let info;
|
|
12925
|
+
try {
|
|
12926
|
+
info = await stat3(fullPath);
|
|
12927
|
+
} catch {
|
|
12928
|
+
continue;
|
|
12929
|
+
}
|
|
12930
|
+
const relPath = relative2(cwd, fullPath);
|
|
12931
|
+
const ext = extname6(item).toLowerCase();
|
|
12932
|
+
const isDoc = DOC_EXTENSIONS.has(ext);
|
|
12933
|
+
if (info.isDirectory()) {
|
|
12934
|
+
entries.push({ path: relPath + "/", isDir: true, isDoc: false });
|
|
12935
|
+
await collectFiles(fullPath, cwd, depth + 1, entries);
|
|
12936
|
+
} else if (info.isFile()) {
|
|
12937
|
+
entries.push({ path: relPath, isDir: false, isDoc });
|
|
12938
|
+
}
|
|
12939
|
+
}
|
|
12940
|
+
}
|
|
12941
|
+
var listFilesTool = {
|
|
12942
|
+
name: "list_files",
|
|
12943
|
+
description: "\uD604\uC7AC \uC791\uC5C5 \uB514\uB809\uD130\uB9AC(cwd) \uC774\uD558\uC758 \uD30C\uC77C \uBAA9\uB85D\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. \uBB38\uC11C \uD30C\uC77C(.hwp/.hwpx/.docx/.xlsx/.pdf \uB4F1)\uC774 \uCD5C\uC0C1\uB2E8\uC5D0 \uD45C\uC2DC\uB429\uB2C8\uB2E4. \uAE4A\uC774\uB294 \uCD5C\uB300 4\uB2E8\uACC4\uAE4C\uC9C0\uC785\uB2C8\uB2E4.",
|
|
12944
|
+
inputSchema: listFilesSchema,
|
|
12945
|
+
requiresApproval: false,
|
|
12946
|
+
execute: async ({
|
|
12947
|
+
input,
|
|
12948
|
+
ctx
|
|
12949
|
+
}) => {
|
|
12950
|
+
const targetDir = input.dir ? await resolveSafePath(ctx.cwd, input.dir) : ctx.cwd;
|
|
12951
|
+
const entries = [];
|
|
12952
|
+
await collectFiles(targetDir, ctx.cwd, 0, entries);
|
|
12953
|
+
if (entries.length === 0) {
|
|
12954
|
+
return "\uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
12955
|
+
}
|
|
12956
|
+
const docs = entries.filter((e) => e.isDoc);
|
|
12957
|
+
const dirs = entries.filter((e) => e.isDir);
|
|
12958
|
+
const others = entries.filter((e) => !e.isDoc && !e.isDir);
|
|
12959
|
+
const allSorted = [...docs, ...dirs, ...others];
|
|
12960
|
+
const lines = allSorted.map((e) => {
|
|
12961
|
+
const icon = e.isDir ? "\u{1F4C1}" : e.isDoc ? "\u{1F4C4}" : " ";
|
|
12962
|
+
return `${icon} ${e.path}`;
|
|
12963
|
+
});
|
|
12964
|
+
const truncateNotice = entries.length >= MAX_FILES ? `
|
|
12965
|
+
(\uCD5C\uB300 ${MAX_FILES}\uAC1C\uAE4C\uC9C0 \uD45C\uC2DC\uB429\uB2C8\uB2E4. \uB354 \uC881\uC740 \uBC94\uC704\uB97C \uC9C0\uC815\uD558\uC138\uC694.)` : "";
|
|
12966
|
+
return lines.join("\n") + truncateNotice;
|
|
12967
|
+
}
|
|
12968
|
+
};
|
|
12766
12969
|
var HEADING_LEVELS = {
|
|
12767
12970
|
1: HeadingLevel.HEADING_1,
|
|
12768
12971
|
2: HeadingLevel.HEADING_2,
|
|
@@ -12885,10 +13088,10 @@ async function markdownToDocx(markdown) {
|
|
|
12885
13088
|
});
|
|
12886
13089
|
return Packer.toBuffer(doc);
|
|
12887
13090
|
}
|
|
12888
|
-
var proposeEditSchema =
|
|
12889
|
-
path:
|
|
12890
|
-
newMarkdown:
|
|
12891
|
-
summary:
|
|
13091
|
+
var proposeEditSchema = z7.object({
|
|
13092
|
+
path: z7.string().describe("\uC218\uC815\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13093
|
+
newMarkdown: z7.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD). read_document\uB85C \uC6D0\uBCF8\uC744 \uBA3C\uC800 \uC77D\uC5B4\uC57C \uD568"),
|
|
13094
|
+
summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12892
13095
|
});
|
|
12893
13096
|
var proposeEditTool = {
|
|
12894
13097
|
name: "propose_edit",
|
|
@@ -12900,10 +13103,10 @@ var proposeEditTool = {
|
|
|
12900
13103
|
ctx
|
|
12901
13104
|
}) => {
|
|
12902
13105
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12903
|
-
const ext =
|
|
13106
|
+
const ext = extname7(safePath).toLowerCase();
|
|
12904
13107
|
let originalBuffer;
|
|
12905
13108
|
try {
|
|
12906
|
-
originalBuffer = await
|
|
13109
|
+
originalBuffer = await readFile7(safePath);
|
|
12907
13110
|
} catch {
|
|
12908
13111
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uAC70\uB098 read_document\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
|
|
12909
13112
|
}
|
|
@@ -12974,19 +13177,19 @@ ${diff}`;
|
|
|
12974
13177
|
};
|
|
12975
13178
|
}
|
|
12976
13179
|
};
|
|
12977
|
-
var proposeFindReplaceSchema =
|
|
12978
|
-
path:
|
|
12979
|
-
find:
|
|
12980
|
-
replace:
|
|
12981
|
-
caseSensitive:
|
|
12982
|
-
all:
|
|
12983
|
-
summary:
|
|
13180
|
+
var proposeFindReplaceSchema = z8.object({
|
|
13181
|
+
path: z8.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13182
|
+
find: z8.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
13183
|
+
replace: z8.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
|
|
13184
|
+
caseSensitive: z8.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
|
|
13185
|
+
all: z8.boolean().optional().default(true).describe("\uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uAD50\uCCB4\uD560\uC9C0 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true). false\uC774\uBA74 \uCCAB \uBC88\uC9F8 \uB9E4\uCE58\uB9CC \uAD50\uCCB4"),
|
|
13186
|
+
summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12984
13187
|
});
|
|
12985
13188
|
var MAX_DIFF_SAMPLES = 20;
|
|
12986
13189
|
function escapeXml3(text3) {
|
|
12987
13190
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12988
13191
|
}
|
|
12989
|
-
function
|
|
13192
|
+
function unescapeXml2(text3) {
|
|
12990
13193
|
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
12991
13194
|
}
|
|
12992
13195
|
function makeChangeSnippet(before, after, ctx = 24) {
|
|
@@ -13010,7 +13213,7 @@ function collectChangedSnippets(beforeXml, afterXml, maxSamples) {
|
|
|
13010
13213
|
const n = Math.min(beforeNodes.length, afterNodes.length);
|
|
13011
13214
|
for (let i = 0; i < n && out.length < maxSamples; i++) {
|
|
13012
13215
|
if (beforeNodes[i] !== afterNodes[i]) {
|
|
13013
|
-
const snip = makeChangeSnippet(
|
|
13216
|
+
const snip = makeChangeSnippet(unescapeXml2(beforeNodes[i]), unescapeXml2(afterNodes[i]));
|
|
13014
13217
|
out.push(snip);
|
|
13015
13218
|
}
|
|
13016
13219
|
}
|
|
@@ -13093,7 +13296,7 @@ function countOccurrences(str, sub) {
|
|
|
13093
13296
|
return count;
|
|
13094
13297
|
}
|
|
13095
13298
|
async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
|
|
13096
|
-
const zip = await
|
|
13299
|
+
const zip = await import_jszip4.default.loadAsync(hwpxBuffer);
|
|
13097
13300
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13098
13301
|
const sectionXmls = [];
|
|
13099
13302
|
for (const sf of sectionFiles) {
|
|
@@ -13137,7 +13340,7 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13137
13340
|
samples.push(snip);
|
|
13138
13341
|
}
|
|
13139
13342
|
}
|
|
13140
|
-
const out = new
|
|
13343
|
+
const out = new import_jszip4.default();
|
|
13141
13344
|
const mimetypeEntry = zip.file("mimetype");
|
|
13142
13345
|
if (mimetypeEntry) {
|
|
13143
13346
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -13168,7 +13371,7 @@ var proposeFindReplaceTool = {
|
|
|
13168
13371
|
ctx
|
|
13169
13372
|
}) => {
|
|
13170
13373
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13171
|
-
const ext =
|
|
13374
|
+
const ext = extname8(safePath).toLowerCase();
|
|
13172
13375
|
if (ext === ".hwp") {
|
|
13173
13376
|
return "\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
13174
13377
|
}
|
|
@@ -13177,7 +13380,7 @@ var proposeFindReplaceTool = {
|
|
|
13177
13380
|
}
|
|
13178
13381
|
let originalBuf;
|
|
13179
13382
|
try {
|
|
13180
|
-
originalBuf = await
|
|
13383
|
+
originalBuf = await readFile8(safePath);
|
|
13181
13384
|
} catch {
|
|
13182
13385
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13183
13386
|
}
|
|
@@ -13270,10 +13473,10 @@ var proposeFindReplaceTool = {
|
|
|
13270
13473
|
};
|
|
13271
13474
|
}
|
|
13272
13475
|
};
|
|
13273
|
-
var proposeFormFillSchema =
|
|
13274
|
-
path:
|
|
13275
|
-
fields:
|
|
13276
|
-
summary:
|
|
13476
|
+
var proposeFormFillSchema = z9.object({
|
|
13477
|
+
path: z9.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13478
|
+
fields: z9.record(z9.string(), z9.string()).describe("\uCC44\uC6B8 \uD544\uB4DC \uB9E4\uD551: { \uB77C\uBCA8: \uAC12 }. read_document\uB85C \uBA3C\uC800 \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694"),
|
|
13479
|
+
summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13277
13480
|
});
|
|
13278
13481
|
var proposeFormFillTool = {
|
|
13279
13482
|
name: "propose_form_fill",
|
|
@@ -13285,13 +13488,13 @@ var proposeFormFillTool = {
|
|
|
13285
13488
|
ctx
|
|
13286
13489
|
}) => {
|
|
13287
13490
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13288
|
-
const ext =
|
|
13491
|
+
const ext = extname9(safePath).toLowerCase();
|
|
13289
13492
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13290
13493
|
return `\uC624\uB958: propose_form_fill\uC740 .hwp/.hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}. .docx \uD30C\uC77C\uC740 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
13291
13494
|
}
|
|
13292
13495
|
let originalBuffer;
|
|
13293
13496
|
try {
|
|
13294
|
-
originalBuffer = await
|
|
13497
|
+
originalBuffer = await readFile9(safePath);
|
|
13295
13498
|
} catch {
|
|
13296
13499
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13297
13500
|
}
|
|
@@ -13359,16 +13562,216 @@ var proposeFormFillTool = {
|
|
|
13359
13562
|
};
|
|
13360
13563
|
}
|
|
13361
13564
|
};
|
|
13362
|
-
var
|
|
13363
|
-
path:
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13565
|
+
var proposeRedactPiiSchema = z10.object({
|
|
13566
|
+
path: z10.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uBE44\uC2DD\uBCC4 \uCC98\uB9AC\uD560 \uBB38\uC11C \uACBD\uB85C"),
|
|
13567
|
+
summary: z10.string().optional().describe("\uBCC0\uACBD \uC694\uC57D")
|
|
13568
|
+
});
|
|
13569
|
+
function mergeFindings(allFindings) {
|
|
13570
|
+
const map = /* @__PURE__ */ new Map();
|
|
13571
|
+
for (const findings of allFindings) {
|
|
13572
|
+
for (const f of findings) {
|
|
13573
|
+
const existing = map.get(f.type);
|
|
13574
|
+
if (existing) {
|
|
13575
|
+
existing.count += f.count;
|
|
13576
|
+
for (const m of f.masked) {
|
|
13577
|
+
if (!existing.masked.includes(m) && existing.masked.length < 5) {
|
|
13578
|
+
existing.masked.push(m);
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
} else {
|
|
13582
|
+
map.set(f.type, { type: f.type, count: f.count, masked: [...f.masked] });
|
|
13583
|
+
}
|
|
13584
|
+
}
|
|
13585
|
+
}
|
|
13586
|
+
return [...map.values()];
|
|
13587
|
+
}
|
|
13588
|
+
async function applyRedactToHwpx(hwpxBuffer) {
|
|
13589
|
+
const zip = await import_jszip5.default.loadAsync(hwpxBuffer);
|
|
13590
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13591
|
+
const sectionXmls = [];
|
|
13592
|
+
for (const sf of sectionFiles) {
|
|
13593
|
+
const entry = zip.file(sf);
|
|
13594
|
+
const xml = entry ? await entry.async("string") : "";
|
|
13595
|
+
sectionXmls.push(xml);
|
|
13596
|
+
}
|
|
13597
|
+
const newSectionXmls = [];
|
|
13598
|
+
const allFindings = [];
|
|
13599
|
+
let anyChanged = false;
|
|
13600
|
+
for (const srcXml of sectionXmls) {
|
|
13601
|
+
const tNodeRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
13602
|
+
const sectionFindings = [];
|
|
13603
|
+
let offset = 0;
|
|
13604
|
+
let result = srcXml;
|
|
13605
|
+
let m = tNodeRe.exec(srcXml);
|
|
13606
|
+
while (m !== null) {
|
|
13607
|
+
const content = m[1];
|
|
13608
|
+
if (content.length === 0) {
|
|
13609
|
+
m = tNodeRe.exec(srcXml);
|
|
13610
|
+
continue;
|
|
13611
|
+
}
|
|
13612
|
+
const { text: redacted, findings } = redactText(content);
|
|
13613
|
+
if (redacted !== content) {
|
|
13614
|
+
const openTagLen = "<hp:t>".length;
|
|
13615
|
+
const contentStart = m.index + offset + openTagLen;
|
|
13616
|
+
const contentEnd = contentStart + content.length;
|
|
13617
|
+
result = result.substring(0, contentStart) + redacted + result.substring(contentEnd);
|
|
13618
|
+
offset += redacted.length - content.length;
|
|
13619
|
+
anyChanged = true;
|
|
13620
|
+
}
|
|
13621
|
+
if (findings.length > 0) {
|
|
13622
|
+
sectionFindings.push(findings);
|
|
13623
|
+
}
|
|
13624
|
+
m = tNodeRe.exec(srcXml);
|
|
13625
|
+
}
|
|
13626
|
+
newSectionXmls.push(result);
|
|
13627
|
+
allFindings.push(...sectionFindings);
|
|
13628
|
+
}
|
|
13629
|
+
if (!anyChanged) {
|
|
13630
|
+
return { buffer: hwpxBuffer, findings: mergeFindings(allFindings), changed: false };
|
|
13631
|
+
}
|
|
13632
|
+
const out = new import_jszip5.default();
|
|
13633
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
13634
|
+
if (mimetypeEntry) {
|
|
13635
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
13636
|
+
}
|
|
13637
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
13638
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
13639
|
+
const sectionIdx = sectionFiles.indexOf(name);
|
|
13640
|
+
if (sectionIdx >= 0) {
|
|
13641
|
+
out.file(name, newSectionXmls[sectionIdx] ?? "");
|
|
13642
|
+
} else {
|
|
13643
|
+
out.file(name, await entry.async("uint8array"));
|
|
13644
|
+
}
|
|
13645
|
+
}
|
|
13646
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
13647
|
+
return {
|
|
13648
|
+
buffer: new Uint8Array(buf),
|
|
13649
|
+
findings: mergeFindings(allFindings),
|
|
13650
|
+
changed: true
|
|
13651
|
+
};
|
|
13652
|
+
}
|
|
13653
|
+
function buildDiff(findings) {
|
|
13654
|
+
const total = findings.reduce((s, f) => s + f.count, 0);
|
|
13655
|
+
const lines = [`\uAC1C\uC778\uC815\uBCF4 ${total}\uAC74 \uBE44\uC2DD\uBCC4 \uCC98\uB9AC`];
|
|
13656
|
+
for (const f of findings) {
|
|
13657
|
+
const examples = f.masked.slice(0, 3).join(", ");
|
|
13658
|
+
lines.push(`- ${f.type}: ${f.count}\uAC74 \u2192 ${examples}`);
|
|
13659
|
+
}
|
|
13660
|
+
return lines.join("\n");
|
|
13661
|
+
}
|
|
13662
|
+
var proposeRedactPiiTool = {
|
|
13663
|
+
name: "propose_redact_pii",
|
|
13664
|
+
description: "\uBB38\uC11C\uC758 \uAC1C\uC778\uC815\uBCF4(\uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638\xB7\uC804\uD654\uBC88\uD638\xB7\uC774\uBA54\uC77C\xB7\uC2E0\uC6A9\uCE74\uB4DC\uBC88\uD638)\uB97C \uAC00\uB9AC\uAE30/\uB9C8\uC2A4\uD0B9/\uBE44\uC2DD\uBCC4 \uCC98\uB9AC\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 '\uAC1C\uC778\uC815\uBCF4 \uAC00\uB824\uC918/\uC9C0\uC6CC\uC918/\uBE44\uC2DD\uBCC4 \uCC98\uB9AC\uD574\uC918/\uB9C8\uC2A4\uD0B9\uD574\uC918'\uB77C\uACE0 \uD558\uBA74 \uC774 \uB3C4\uAD6C\uB85C \uC218\uC815\uC548\uC744 \uC81C\uC548\uD558\uC138\uC694(scan_pii\uB294 \uD655\uC778\uC6A9\uC77C \uBFD0 \uC218\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). .hwpx\uB294 XML \uC9C1\uC811 \uD328\uCE58\uB85C \uAD6C\uC870(\uD45C\xB7\uC774\uBBF8\uC9C0\xB7\uC11C\uC2DD)\uB97C \uBCF4\uC874\uD558\uBA70, .md/.txt\uB3C4 \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uBCC0\uACBD\uC740 \uC2B9\uC778 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4. \uC6D0\uBB38 \uAC12\uC740 \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
13665
|
+
inputSchema: proposeRedactPiiSchema,
|
|
13666
|
+
requiresApproval: true,
|
|
13667
|
+
propose: async ({
|
|
13668
|
+
input,
|
|
13669
|
+
ctx
|
|
13670
|
+
}) => {
|
|
13671
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13672
|
+
const ext = extname10(safePath).toLowerCase();
|
|
13673
|
+
if (ext === ".hwp") {
|
|
13674
|
+
return "\uC624\uB958: propose_redact_pii\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
13675
|
+
}
|
|
13676
|
+
if (ext !== ".hwpx" && ext !== ".md" && ext !== ".txt") {
|
|
13677
|
+
const hint = ext === ".docx" || ext === ".xlsx" ? " .hwpx/.md/.txt\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4." : " .hwpx/.md/.txt\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.";
|
|
13678
|
+
return `\uC624\uB958: propose_redact_pii\uB294 .hwpx/.md/.txt \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}.${hint}`;
|
|
13679
|
+
}
|
|
13680
|
+
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
13681
|
+
if (ext === ".hwpx") {
|
|
13682
|
+
let originalBuf;
|
|
13683
|
+
try {
|
|
13684
|
+
originalBuf = await readFile10(safePath);
|
|
13685
|
+
} catch {
|
|
13686
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13687
|
+
}
|
|
13688
|
+
if (originalBuf[0] !== 80 || originalBuf[1] !== 75) {
|
|
13689
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
13690
|
+
}
|
|
13691
|
+
const originalBytes = new Uint8Array(
|
|
13692
|
+
originalBuf.buffer,
|
|
13693
|
+
originalBuf.byteOffset,
|
|
13694
|
+
originalBuf.byteLength
|
|
13695
|
+
);
|
|
13696
|
+
let patchResult;
|
|
13697
|
+
try {
|
|
13698
|
+
patchResult = await applyRedactToHwpx(originalBytes);
|
|
13699
|
+
} catch (e) {
|
|
13700
|
+
return `\uC624\uB958: \uBE44\uC2DD\uBCC4 \uCC98\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
|
|
13701
|
+
}
|
|
13702
|
+
const { buffer: newBytes, findings: findings2, changed } = patchResult;
|
|
13703
|
+
const totalCount2 = findings2.reduce((s, f) => s + f.count, 0);
|
|
13704
|
+
if (!changed || totalCount2 === 0) {
|
|
13705
|
+
return `\uAC1C\uC778\uC815\uBCF4\uAC00 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC544 \uBCC0\uACBD\uD560 \uB0B4\uC6A9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13706
|
+
}
|
|
13707
|
+
const stagedPath2 = await stageFile(ctx.sessionId, outputPath, newBytes);
|
|
13708
|
+
const proposalId2 = crypto.randomUUID();
|
|
13709
|
+
const diff2 = buildDiff(findings2);
|
|
13710
|
+
const summaryText2 = input.summary ?? `\uAC1C\uC778\uC815\uBCF4 \uBE44\uC2DD\uBCC4 \uCC98\uB9AC(\uB9C8\uC2A4\uD0B9): ${basename4(safePath)}`;
|
|
13711
|
+
return {
|
|
13712
|
+
proposal: {
|
|
13713
|
+
id: proposalId2,
|
|
13714
|
+
kind: "redact-pii",
|
|
13715
|
+
targetPath: outputPath,
|
|
13716
|
+
stagedPath: stagedPath2,
|
|
13717
|
+
summary: summaryText2,
|
|
13718
|
+
diff: diff2,
|
|
13719
|
+
warnings: [],
|
|
13720
|
+
willConvertFormat
|
|
13721
|
+
},
|
|
13722
|
+
commit: async () => {
|
|
13723
|
+
const backupPath = await backupFile(outputPath);
|
|
13724
|
+
await commitStaged(stagedPath2, outputPath);
|
|
13725
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
13726
|
+
return `\uAC1C\uC778\uC815\uBCF4 \uBE44\uC2DD\uBCC4 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
13727
|
+
}
|
|
13728
|
+
};
|
|
13729
|
+
}
|
|
13730
|
+
let originalText;
|
|
13731
|
+
try {
|
|
13732
|
+
originalText = await readFile10(safePath, "utf-8");
|
|
13733
|
+
} catch {
|
|
13734
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13735
|
+
}
|
|
13736
|
+
const { text: redacted, findings } = redactText(originalText);
|
|
13737
|
+
const totalCount = findings.reduce((s, f) => s + f.count, 0);
|
|
13738
|
+
if (totalCount === 0) {
|
|
13739
|
+
return `\uAC1C\uC778\uC815\uBCF4\uAC00 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC544 \uBCC0\uACBD\uD560 \uB0B4\uC6A9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13740
|
+
}
|
|
13741
|
+
const stagedPath = await stageFile(ctx.sessionId, outputPath, redacted);
|
|
13742
|
+
const proposalId = crypto.randomUUID();
|
|
13743
|
+
const diff = buildDiff(findings);
|
|
13744
|
+
const summaryText = input.summary ?? `\uAC1C\uC778\uC815\uBCF4 \uBE44\uC2DD\uBCC4 \uCC98\uB9AC(\uB9C8\uC2A4\uD0B9): ${basename4(safePath)}`;
|
|
13745
|
+
return {
|
|
13746
|
+
proposal: {
|
|
13747
|
+
id: proposalId,
|
|
13748
|
+
kind: "redact-pii",
|
|
13749
|
+
targetPath: outputPath,
|
|
13750
|
+
stagedPath,
|
|
13751
|
+
summary: summaryText,
|
|
13752
|
+
diff,
|
|
13753
|
+
warnings: [],
|
|
13754
|
+
willConvertFormat
|
|
13755
|
+
},
|
|
13756
|
+
commit: async () => {
|
|
13757
|
+
const backupPath = await backupFile(outputPath);
|
|
13758
|
+
await commitStaged(stagedPath, outputPath);
|
|
13759
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
13760
|
+
return `\uAC1C\uC778\uC815\uBCF4 \uBE44\uC2DD\uBCC4 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
13761
|
+
}
|
|
13762
|
+
};
|
|
13763
|
+
}
|
|
13764
|
+
};
|
|
13765
|
+
var proposeSheetEditSchema = z11.object({
|
|
13766
|
+
path: z11.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13767
|
+
updates: z11.array(
|
|
13768
|
+
z11.object({
|
|
13769
|
+
sheet: z11.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
13770
|
+
cell: z11.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
|
|
13771
|
+
value: z11.union([z11.string(), z11.number()]).describe("\uC0C8 \uAC12")
|
|
13369
13772
|
})
|
|
13370
13773
|
).min(1).describe("\uC218\uC815\uD560 \uC140 \uBAA9\uB85D. read_document\uB85C \uC6D0\uBCF8 \uC2DC\uD2B8 \uAD6C\uC870\uB97C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694"),
|
|
13371
|
-
summary:
|
|
13774
|
+
summary: z11.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13372
13775
|
});
|
|
13373
13776
|
var proposeSheetEditTool = {
|
|
13374
13777
|
name: "propose_sheet_edit",
|
|
@@ -13380,13 +13783,13 @@ var proposeSheetEditTool = {
|
|
|
13380
13783
|
ctx
|
|
13381
13784
|
}) => {
|
|
13382
13785
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13383
|
-
const ext =
|
|
13786
|
+
const ext = extname11(safePath).toLowerCase();
|
|
13384
13787
|
if (ext !== ".xlsx" && ext !== ".xls") {
|
|
13385
13788
|
return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
|
|
13386
13789
|
}
|
|
13387
13790
|
let originalBuffer;
|
|
13388
13791
|
try {
|
|
13389
|
-
originalBuffer = await
|
|
13792
|
+
originalBuffer = await readFile11(safePath);
|
|
13390
13793
|
} catch {
|
|
13391
13794
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13392
13795
|
}
|
|
@@ -13479,47 +13882,47 @@ function detectStructuralLoss(beforeBlocks, afterBlocks) {
|
|
|
13479
13882
|
}
|
|
13480
13883
|
return { lost: false, detail: "" };
|
|
13481
13884
|
}
|
|
13482
|
-
var insertRowOpSchema =
|
|
13483
|
-
type:
|
|
13484
|
-
row:
|
|
13485
|
-
position:
|
|
13885
|
+
var insertRowOpSchema = z12.object({
|
|
13886
|
+
type: z12.literal("insertRow"),
|
|
13887
|
+
row: z12.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
|
|
13888
|
+
position: z12.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
|
|
13486
13889
|
});
|
|
13487
|
-
var deleteRowOpSchema =
|
|
13488
|
-
type:
|
|
13489
|
-
row:
|
|
13890
|
+
var deleteRowOpSchema = z12.object({
|
|
13891
|
+
type: z12.literal("deleteRow"),
|
|
13892
|
+
row: z12.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
|
|
13490
13893
|
});
|
|
13491
|
-
var insertColumnOpSchema =
|
|
13492
|
-
type:
|
|
13493
|
-
col:
|
|
13494
|
-
position:
|
|
13894
|
+
var insertColumnOpSchema = z12.object({
|
|
13895
|
+
type: z12.literal("insertColumn"),
|
|
13896
|
+
col: z12.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
|
|
13897
|
+
position: z12.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
|
|
13495
13898
|
});
|
|
13496
|
-
var deleteColumnOpSchema =
|
|
13497
|
-
type:
|
|
13498
|
-
col:
|
|
13899
|
+
var deleteColumnOpSchema = z12.object({
|
|
13900
|
+
type: z12.literal("deleteColumn"),
|
|
13901
|
+
col: z12.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
|
|
13499
13902
|
});
|
|
13500
|
-
var mergeCellsOpSchema =
|
|
13501
|
-
type:
|
|
13502
|
-
startRow:
|
|
13503
|
-
startCol:
|
|
13504
|
-
endRow:
|
|
13505
|
-
endCol:
|
|
13903
|
+
var mergeCellsOpSchema = z12.object({
|
|
13904
|
+
type: z12.literal("mergeCells"),
|
|
13905
|
+
startRow: z12.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
|
|
13906
|
+
startCol: z12.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
|
|
13907
|
+
endRow: z12.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
|
|
13908
|
+
endCol: z12.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
|
|
13506
13909
|
});
|
|
13507
|
-
var operationSchema =
|
|
13910
|
+
var operationSchema = z12.discriminatedUnion("type", [
|
|
13508
13911
|
insertRowOpSchema,
|
|
13509
13912
|
deleteRowOpSchema,
|
|
13510
13913
|
insertColumnOpSchema,
|
|
13511
13914
|
deleteColumnOpSchema,
|
|
13512
13915
|
mergeCellsOpSchema
|
|
13513
13916
|
]).describe("\uD45C \uAD6C\uC870 \uC5F0\uC0B0. \uB098\uC911 \uC5F0\uC0B0\uC758 row/col \uC778\uB371\uC2A4\uB294 \uC55E \uC5F0\uC0B0 \uC801\uC6A9 \uD6C4 \uC0C1\uD0DC\uB97C \uAE30\uC900\uC73C\uB85C \uD55C\uB2E4.");
|
|
13514
|
-
var proposeTableStructureSchema =
|
|
13515
|
-
path:
|
|
13516
|
-
anchor:
|
|
13917
|
+
var proposeTableStructureSchema = z12.object({
|
|
13918
|
+
path: z12.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13919
|
+
anchor: z12.string().min(1).describe(
|
|
13517
13920
|
"\uB300\uC0C1 \uD45C\uB97C \uC2DD\uBCC4\uD558\uB294 \uC575\uCEE4 \uD14D\uC2A4\uD2B8. \uD45C \uC548\uC5D0\uB9CC \uC788\uB294 \uB3C5\uD2B9\uD55C \uC140 \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694. (\uBD80\uBD84 \uC77C\uCE58, \uACF5\uBC31 \uD2B8\uB9BC) \u2014 read_document\uB85C \uD655\uC778 \uD6C4 \uC0AC\uC6A9 \uAD8C\uC7A5."
|
|
13518
13921
|
),
|
|
13519
|
-
operations:
|
|
13922
|
+
operations: z12.array(operationSchema).min(1).describe(
|
|
13520
13923
|
"\uC801\uC6A9\uD560 \uD45C \uAD6C\uC870 \uC5F0\uC0B0 \uBAA9\uB85D (\uC21C\uC11C\uB300\uB85C \uC2E4\uD589). \uAC01 \uC5F0\uC0B0\uC740 \uC774\uC804 \uC5F0\uC0B0\uC774 \uC801\uC6A9\uB41C \uD6C4\uC758 \uD45C \uC0C1\uD0DC \uAE30\uC900\uC73C\uB85C row/col\uC744 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."
|
|
13521
13924
|
),
|
|
13522
|
-
summary:
|
|
13925
|
+
summary: z12.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13523
13926
|
});
|
|
13524
13927
|
function tokenizeHwpxXml2(xml) {
|
|
13525
13928
|
const tokens = [];
|
|
@@ -14076,7 +14479,7 @@ function getTblDims(tblXml) {
|
|
|
14076
14479
|
return parseTblDimensions(tblXml, 0);
|
|
14077
14480
|
}
|
|
14078
14481
|
async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
14079
|
-
const zip = await
|
|
14482
|
+
const zip = await import_jszip6.default.loadAsync(hwpxBuffer);
|
|
14080
14483
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
14081
14484
|
let targetSectionIdx = -1;
|
|
14082
14485
|
let targetRange = null;
|
|
@@ -14135,7 +14538,7 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14135
14538
|
const afterDims = getTblDims(tblBlock);
|
|
14136
14539
|
const newSectionXml = sectionXml.substring(0, targetRange.start) + tblBlock + sectionXml.substring(targetRange.end);
|
|
14137
14540
|
sectionXmls[targetSectionIdx] = newSectionXml;
|
|
14138
|
-
const out = new
|
|
14541
|
+
const out = new import_jszip6.default();
|
|
14139
14542
|
const mimetypeEntry = zip.file("mimetype");
|
|
14140
14543
|
if (mimetypeEntry) {
|
|
14141
14544
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -14159,7 +14562,7 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14159
14562
|
};
|
|
14160
14563
|
}
|
|
14161
14564
|
async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt) {
|
|
14162
|
-
const zip = await
|
|
14565
|
+
const zip = await import_jszip6.default.loadAsync(newBytes);
|
|
14163
14566
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
14164
14567
|
for (const sf of sectionFiles) {
|
|
14165
14568
|
const entry = zip.file(sf);
|
|
@@ -14194,7 +14597,7 @@ var proposeTableStructureTool = {
|
|
|
14194
14597
|
ctx
|
|
14195
14598
|
}) => {
|
|
14196
14599
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14197
|
-
const ext =
|
|
14600
|
+
const ext = extname12(safePath).toLowerCase();
|
|
14198
14601
|
if (ext === ".hwp") {
|
|
14199
14602
|
return "\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
14200
14603
|
}
|
|
@@ -14203,7 +14606,7 @@ var proposeTableStructureTool = {
|
|
|
14203
14606
|
}
|
|
14204
14607
|
let originalBuf;
|
|
14205
14608
|
try {
|
|
14206
|
-
originalBuf = await
|
|
14609
|
+
originalBuf = await readFile12(safePath);
|
|
14207
14610
|
} catch {
|
|
14208
14611
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
14209
14612
|
}
|
|
@@ -14399,11 +14802,11 @@ function searchExcerpts(markdown, query) {
|
|
|
14399
14802
|
}
|
|
14400
14803
|
return result;
|
|
14401
14804
|
}
|
|
14402
|
-
var readDocumentSchema =
|
|
14403
|
-
path:
|
|
14404
|
-
pages:
|
|
14405
|
-
outline:
|
|
14406
|
-
search:
|
|
14805
|
+
var readDocumentSchema = z13.object({
|
|
14806
|
+
path: z13.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
14807
|
+
pages: z13.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
|
|
14808
|
+
outline: z13.boolean().optional().describe("\uD5E4\uB529(\uC81C\uBAA9) \uAD6C\uC870\uB9CC \uBC18\uD658\uD574 \uBB38\uC11C \uAC1C\uC694 \uD30C\uC545 (\uB300\uD615 \uBB38\uC11C \uD0D0\uC0C9\uC6A9)"),
|
|
14809
|
+
search: z13.string().optional().describe("\uD0A4\uC6CC\uB4DC\uAC00 \uD3EC\uD568\uB41C \uBD80\uBD84\uACFC \uC8FC\uBCC0 \uB9E5\uB77D\uB9CC \uBC18\uD658 (\uB300\uD615 \uBB38\uC11C\uC5D0\uC11C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uAE30)")
|
|
14407
14810
|
});
|
|
14408
14811
|
function applyReadMode(body, outline, search) {
|
|
14409
14812
|
if (outline === true) {
|
|
@@ -14432,11 +14835,11 @@ var readDocumentTool = {
|
|
|
14432
14835
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14433
14836
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
14434
14837
|
}
|
|
14435
|
-
const ext =
|
|
14838
|
+
const ext = extname13(safePath).toLowerCase();
|
|
14436
14839
|
if (PLAIN_TEXT_EXTS.has(ext)) {
|
|
14437
14840
|
let raw;
|
|
14438
14841
|
try {
|
|
14439
|
-
raw = await
|
|
14842
|
+
raw = await readFile13(safePath, "utf-8");
|
|
14440
14843
|
} catch (e) {
|
|
14441
14844
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14442
14845
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -14536,8 +14939,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
14536
14939
|
".csv",
|
|
14537
14940
|
".log"
|
|
14538
14941
|
]);
|
|
14539
|
-
var readFileSchema =
|
|
14540
|
-
path:
|
|
14942
|
+
var readFileSchema = z14.object({
|
|
14943
|
+
path: z14.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
|
|
14541
14944
|
});
|
|
14542
14945
|
var readFileTool = {
|
|
14543
14946
|
name: "read_file",
|
|
@@ -14561,8 +14964,8 @@ var readFileTool = {
|
|
|
14561
14964
|
if (info.size > MAX_SIZE) {
|
|
14562
14965
|
return `\uC624\uB958: \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${Math.round(info.size / 1024)}KB). \uCD5C\uB300 256KB\uAE4C\uC9C0 \uC77D\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`;
|
|
14563
14966
|
}
|
|
14564
|
-
const { extname:
|
|
14565
|
-
const ext =
|
|
14967
|
+
const { extname: extname17 } = await import("path");
|
|
14968
|
+
const ext = extname17(safePath).toLowerCase();
|
|
14566
14969
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
14567
14970
|
return `\uC624\uB958: '${ext}' \uD615\uC2DD\uC740 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uB85C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C \uD30C\uC77C\uC774\uB77C\uBA74 read_document \uD234\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
14568
14971
|
}
|
|
@@ -14577,8 +14980,8 @@ var readFileTool = {
|
|
|
14577
14980
|
}
|
|
14578
14981
|
};
|
|
14579
14982
|
var PLAIN_TEXT_EXTS2 = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
|
|
14580
|
-
var scanPiiSchema =
|
|
14581
|
-
path:
|
|
14983
|
+
var scanPiiSchema = z15.object({
|
|
14984
|
+
path: z15.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uC810\uAC80\uD560 \uBB38\uC11C \uACBD\uB85C")
|
|
14582
14985
|
});
|
|
14583
14986
|
var scanPiiTool = {
|
|
14584
14987
|
name: "scan_pii",
|
|
@@ -14596,11 +14999,11 @@ var scanPiiTool = {
|
|
|
14596
14999
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14597
15000
|
return `\uC624\uB958: \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
14598
15001
|
}
|
|
14599
|
-
const ext =
|
|
15002
|
+
const ext = extname14(safePath).toLowerCase();
|
|
14600
15003
|
let text3;
|
|
14601
15004
|
if (PLAIN_TEXT_EXTS2.has(ext)) {
|
|
14602
15005
|
try {
|
|
14603
|
-
text3 = await
|
|
15006
|
+
text3 = await readFile14(safePath, "utf-8");
|
|
14604
15007
|
} catch (e) {
|
|
14605
15008
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14606
15009
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -14631,9 +15034,9 @@ var scanPiiTool = {
|
|
|
14631
15034
|
}
|
|
14632
15035
|
};
|
|
14633
15036
|
var MAX_PREVIEW_CHARS = 1e4;
|
|
14634
|
-
var writeNewDocumentSchema =
|
|
14635
|
-
path:
|
|
14636
|
-
markdown:
|
|
15037
|
+
var writeNewDocumentSchema = z16.object({
|
|
15038
|
+
path: z16.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
15039
|
+
markdown: z16.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
|
|
14637
15040
|
});
|
|
14638
15041
|
var writeNewDocumentTool = {
|
|
14639
15042
|
name: "write_new_document",
|
|
@@ -14645,7 +15048,7 @@ var writeNewDocumentTool = {
|
|
|
14645
15048
|
ctx
|
|
14646
15049
|
}) => {
|
|
14647
15050
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14648
|
-
const ext =
|
|
15051
|
+
const ext = extname15(safePath).toLowerCase();
|
|
14649
15052
|
try {
|
|
14650
15053
|
await stat6(safePath);
|
|
14651
15054
|
return `\uC624\uB958: \uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${input.path}. \uAE30\uC874 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uB824\uBA74 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
@@ -14695,12 +15098,12 @@ ${preview}`,
|
|
|
14695
15098
|
};
|
|
14696
15099
|
}
|
|
14697
15100
|
};
|
|
14698
|
-
var writeNewSpreadsheetSchema =
|
|
14699
|
-
path:
|
|
14700
|
-
sheets:
|
|
14701
|
-
|
|
14702
|
-
name:
|
|
14703
|
-
rows:
|
|
15101
|
+
var writeNewSpreadsheetSchema = z17.object({
|
|
15102
|
+
path: z17.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
15103
|
+
sheets: z17.array(
|
|
15104
|
+
z17.object({
|
|
15105
|
+
name: z17.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
15106
|
+
rows: z17.array(z17.array(z17.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
|
|
14704
15107
|
})
|
|
14705
15108
|
).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
|
|
14706
15109
|
});
|
|
@@ -14714,7 +15117,7 @@ var writeNewSpreadsheetTool = {
|
|
|
14714
15117
|
ctx
|
|
14715
15118
|
}) => {
|
|
14716
15119
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14717
|
-
const ext =
|
|
15120
|
+
const ext = extname16(safePath).toLowerCase();
|
|
14718
15121
|
if (ext !== ".xlsx") {
|
|
14719
15122
|
return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
|
|
14720
15123
|
}
|
|
@@ -14775,10 +15178,12 @@ function createDocTools(_ctx) {
|
|
|
14775
15178
|
listBackupsTool,
|
|
14776
15179
|
readFileTool,
|
|
14777
15180
|
scanPiiTool,
|
|
15181
|
+
findInDocumentTool,
|
|
14778
15182
|
proposeEditTool,
|
|
14779
15183
|
proposeFormFillTool,
|
|
14780
15184
|
proposeCellEditTool,
|
|
14781
15185
|
proposeFindReplaceTool,
|
|
15186
|
+
proposeRedactPiiTool,
|
|
14782
15187
|
proposeSheetEditTool,
|
|
14783
15188
|
proposeTableStructureTool,
|
|
14784
15189
|
listFormObjectsTool,
|
|
@@ -15602,7 +16007,7 @@ async function runOnboarding() {
|
|
|
15602
16007
|
|
|
15603
16008
|
// src/update.ts
|
|
15604
16009
|
import { spawn } from "child_process";
|
|
15605
|
-
import { mkdir as mkdir4, readFile as
|
|
16010
|
+
import { mkdir as mkdir4, readFile as readFile15, writeFile as writeFile3 } from "fs/promises";
|
|
15606
16011
|
import { dirname as dirname3 } from "path";
|
|
15607
16012
|
function compareSemver(a, b) {
|
|
15608
16013
|
const parse7 = (v) => {
|
|
@@ -15623,7 +16028,7 @@ function compareSemver(a, b) {
|
|
|
15623
16028
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
15624
16029
|
async function readCache(cachePath) {
|
|
15625
16030
|
try {
|
|
15626
|
-
const raw = await
|
|
16031
|
+
const raw = await readFile15(cachePath, "utf-8");
|
|
15627
16032
|
const parsed = JSON.parse(raw);
|
|
15628
16033
|
if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
|
|
15629
16034
|
return parsed;
|