@kodocagent/cli 0.4.4 → 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 +1791 -1227
- 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
|
|
|
@@ -9851,6 +9851,58 @@ var KODOC_PATHS = {
|
|
|
9851
9851
|
function projectMcpConfigPath(cwd) {
|
|
9852
9852
|
return join(cwd, ".kodocagent", "mcp.json");
|
|
9853
9853
|
}
|
|
9854
|
+
var PATTERNS = [
|
|
9855
|
+
{
|
|
9856
|
+
type: "\uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638",
|
|
9857
|
+
re: /\b\d{6}-[1-4]\d{6}\b/g,
|
|
9858
|
+
mask: (m) => `${m.slice(0, 8)}******`
|
|
9859
|
+
},
|
|
9860
|
+
{
|
|
9861
|
+
type: "\uC2E0\uC6A9\uCE74\uB4DC\uBC88\uD638",
|
|
9862
|
+
re: /\b\d{4}-\d{4}-\d{4}-\d{4}\b/g,
|
|
9863
|
+
mask: (m) => `${m.slice(0, 4)}-****-****-${m.slice(-4)}`
|
|
9864
|
+
},
|
|
9865
|
+
{
|
|
9866
|
+
type: "\uC804\uD654\uBC88\uD638",
|
|
9867
|
+
re: /\b0\d{1,2}-\d{3,4}-\d{4}\b/g,
|
|
9868
|
+
mask: (m) => {
|
|
9869
|
+
const p = m.split("-");
|
|
9870
|
+
return `${p[0]}-${"*".repeat((p[1] ?? "").length)}-${p[2]}`;
|
|
9871
|
+
}
|
|
9872
|
+
},
|
|
9873
|
+
{
|
|
9874
|
+
type: "\uC774\uBA54\uC77C",
|
|
9875
|
+
re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
9876
|
+
mask: (m) => {
|
|
9877
|
+
const [u, d] = m.split("@");
|
|
9878
|
+
return `${u?.[0] ?? ""}***@${d}`;
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
];
|
|
9882
|
+
function detectPii(text3) {
|
|
9883
|
+
if (!text3) return [];
|
|
9884
|
+
const out = [];
|
|
9885
|
+
for (const { type, re, mask } of PATTERNS) {
|
|
9886
|
+
const matches = text3.match(re);
|
|
9887
|
+
if (matches && matches.length > 0) {
|
|
9888
|
+
const uniq = [...new Set(matches)];
|
|
9889
|
+
out.push({ type, count: matches.length, masked: uniq.slice(0, 5).map(mask) });
|
|
9890
|
+
}
|
|
9891
|
+
}
|
|
9892
|
+
return out;
|
|
9893
|
+
}
|
|
9894
|
+
function summarizePii(findings) {
|
|
9895
|
+
return findings.map((f) => `${f.type} ${f.count}\uAC74`).join(", ");
|
|
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
|
+
}
|
|
9854
9906
|
|
|
9855
9907
|
// ../core/dist/index.js
|
|
9856
9908
|
import { stepCountIs, streamText } from "ai";
|
|
@@ -9869,7 +9921,12 @@ import { appendFile, mkdir as mkdir2, readdir, readFile as readFile2, stat } fro
|
|
|
9869
9921
|
import { join as join2 } from "path";
|
|
9870
9922
|
import { tool } from "ai";
|
|
9871
9923
|
function buildSystemPrompt(ctx) {
|
|
9872
|
-
const stable = [
|
|
9924
|
+
const stable = [
|
|
9925
|
+
ROLE_SECTION,
|
|
9926
|
+
DOCUMENT_RULES_SECTION,
|
|
9927
|
+
EDIT_SAFETY_SECTION,
|
|
9928
|
+
LAW_RULES_SECTION
|
|
9929
|
+
].join("\n\n");
|
|
9873
9930
|
const dynamic = buildDynamicContext(ctx);
|
|
9874
9931
|
return `${stable}
|
|
9875
9932
|
|
|
@@ -9892,6 +9949,15 @@ var DOCUMENT_RULES_SECTION = `## \uBB38\uC11C \uADDC\uCE59
|
|
|
9892
9949
|
6. \uC0AC\uC6A9\uC790\uAC00 \uC218\uC815\uC548\uC744 \uAC70\uC808\uD558\uBA74 \uAC19\uC740 \uC81C\uC548\uC744 \uC790\uB3D9\uC73C\uB85C \uBC18\uBCF5\uD558\uC9C0 \uB9D0\uACE0, \uC0AC\uC6A9\uC790\uC758 \uB2E4\uC74C \uC9C0\uC2DC\uB97C \uAE30\uB2E4\uB9AC\uC138\uC694.
|
|
9893
9950
|
7. \uD070 \uBB38\uC11C\uB294 \uBA3C\uC800 \`outline\`\uC73C\uB85C \uAD6C\uC870\uB97C \uD30C\uC545\uD558\uACE0, \`search\`\uB098 \`pages\`\uB85C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uC5B4 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uC544\uB07C\uC138\uC694.
|
|
9894
9951
|
8. \uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC804 \uBCC0\uACBD\uC744 \uB418\uB3CC\uB9AC\uAE38 \uC6D0\uD558\uBA74 \`list_backups\`\uB85C \uBC31\uC5C5\uC744 \uD655\uC778\uD558\uACE0 \`restore_backup\`\uC73C\uB85C \uBCF5\uC6D0\uD558\uC138\uC694. \uBCF5\uC6D0\uB3C4 \uC2B9\uC778\uC744 \uAC70\uCE58\uBA70, \uBCF5\uC6D0 \uC804 \uD604\uC7AC \uC0C1\uD0DC\uAC00 \uC790\uB3D9 \uBC31\uC5C5\uB429\uB2C8\uB2E4.`;
|
|
9952
|
+
var EDIT_SAFETY_SECTION = `## \uD3B8\uC9D1 \uC548\uC804 \uADDC\uCE59
|
|
9953
|
+
|
|
9954
|
+
1. \uC694\uCCAD\uBC1B\uC740 \uBD80\uBD84\uB9CC \uC218\uC815\uD558\uACE0, \uC694\uCCAD\uD558\uC9C0 \uC54A\uC740 \uBB38\uC7A5\xB7\uC11C\uC2DD\xB7\uAD6C\uC870\xB7\uD45C\uD604\uC740 \uADF8\uB300\uB85C \uB461\uB2C8\uB2E4. \uBB38\uC11C \uC804\uCCB4\uB97C \uC784\uC758\uB85C \uC7AC\uC791\uC131\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
|
|
9955
|
+
2. \uBB38\uC11C\uC5D0 \uC5C6\uB294 \uC815\uBCF4\uB97C \uB9CC\uB4E4\uC5B4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC218\uCE58\xB7\uAE08\uC561\xB7\uB0A0\uC9DC\xB7\uC778\uBA85\xB7\uAE30\uAD00\uBA85\xB7\uACC4\uC57D \uC870\uD56D \uB4F1\uC744 \uCD94\uCE21\uD574 \uC0C8\uB85C \uC4F0\uAC70\uB098 \uBC14\uAFB8\uC9C0 \uC54A\uC73C\uBA70, \uBD88\uD655\uC2E4\uD558\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uD569\uB2C8\uB2E4.
|
|
9956
|
+
3. \uC0AC\uC6A9\uC790\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C \uBC14\uAFB8\uB77C\uACE0 \uD558\uC9C0 \uC54A\uC740 \uD55C \uC22B\uC790\xB7\uAE08\uC561\xB7\uB0A0\uC9DC\xB7\uB2E8\uC704\xB7\uACE0\uC720\uBA85\uC0AC\xB7\uBC95\uB839 \uC870\uBB38 \uBC88\uD638\uC640 \uC778\uC6A9\uBD80\uD638(" ", ' ', \u300C \u300D) \uB0B4\uBD80 \uB0B4\uC6A9\uC740 \uBCF4\uC874\uD569\uB2C8\uB2E4.
|
|
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.
|
|
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.
|
|
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.
|
|
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).`;
|
|
9895
9961
|
var LAW_RULES_SECTION = `## \uBC95\uB839 \uADDC\uCE59
|
|
9896
9962
|
|
|
9897
9963
|
1. \uBC95\uB839 \uC778\uC6A9 \uD615\uC2DD: \u300C\uBC95\uB839\uBA85\u300D \uC81CN\uC870 \uC81CN\uD56D \uC81CN\uD638
|
|
@@ -10017,6 +10083,26 @@ var AgentSession = class {
|
|
|
10017
10083
|
this.openDocuments.push(p);
|
|
10018
10084
|
}
|
|
10019
10085
|
}
|
|
10086
|
+
/**
|
|
10087
|
+
* 저장된 메시지(어시스턴트 tool-call 파트)에서 열람·작성한 문서 경로를 도출해 기록한다.
|
|
10088
|
+
* 세션이 턴마다 재생성되므로, 시스템 프롬프트("열람한 문서")가 이전 턴의 열람 기록을
|
|
10089
|
+
* 반영하려면 히스토리에서 다시 복원해야 한다.
|
|
10090
|
+
*/
|
|
10091
|
+
recordOpenDocumentsFromMessage(msg) {
|
|
10092
|
+
const content = msg.content;
|
|
10093
|
+
if (!Array.isArray(content)) return;
|
|
10094
|
+
for (const part of content) {
|
|
10095
|
+
if (!part || typeof part !== "object") continue;
|
|
10096
|
+
const p = part;
|
|
10097
|
+
if (p.type !== "tool-call" || !p.input) continue;
|
|
10098
|
+
if (p.toolName === "read_document" || p.toolName === "write_new_document" || p.toolName === "write_new_spreadsheet") {
|
|
10099
|
+
this.recordOpenDocument(p.input.path);
|
|
10100
|
+
} else if (p.toolName === "compare_documents") {
|
|
10101
|
+
this.recordOpenDocument(p.input.pathA);
|
|
10102
|
+
this.recordOpenDocument(p.input.pathB);
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10020
10106
|
/** approval-required 이벤트를 run() 스트림에 전달하기 위한 큐 */
|
|
10021
10107
|
pendingApprovalEvents = [];
|
|
10022
10108
|
/**
|
|
@@ -10025,6 +10111,9 @@ var AgentSession = class {
|
|
|
10025
10111
|
async loadHistory() {
|
|
10026
10112
|
const msgs = await this.opts.store.loadMessages();
|
|
10027
10113
|
this.messages.push(...msgs);
|
|
10114
|
+
for (const msg of msgs) {
|
|
10115
|
+
this.recordOpenDocumentsFromMessage(msg);
|
|
10116
|
+
}
|
|
10028
10117
|
}
|
|
10029
10118
|
/**
|
|
10030
10119
|
* 사용자 메시지를 처리하고 에이전트 이벤트를 스트리밍한다.
|
|
@@ -10079,7 +10168,7 @@ var AgentSession = class {
|
|
|
10079
10168
|
} else if (part.toolName === "compare_documents") {
|
|
10080
10169
|
this.recordOpenDocument(inp.pathA);
|
|
10081
10170
|
this.recordOpenDocument(inp.pathB);
|
|
10082
|
-
} else if (part.toolName === "write_new_document") {
|
|
10171
|
+
} else if (part.toolName === "write_new_document" || part.toolName === "write_new_spreadsheet") {
|
|
10083
10172
|
this.recordOpenDocument(inp.path);
|
|
10084
10173
|
}
|
|
10085
10174
|
} catch {
|
|
@@ -10788,6 +10877,13 @@ var ToolRegistry = class {
|
|
|
10788
10877
|
return outcome;
|
|
10789
10878
|
}
|
|
10790
10879
|
const { proposal, commit } = outcome;
|
|
10880
|
+
const piiFindings = detectPii(proposal.diff ?? "");
|
|
10881
|
+
if (piiFindings.length > 0) {
|
|
10882
|
+
proposal.warnings = [
|
|
10883
|
+
...proposal.warnings ?? [],
|
|
10884
|
+
`\uAC1C\uC778\uC815\uBCF4 \uD3EC\uD568: \uC774\uBC88 \uBCC0\uACBD \uC601\uC5ED\uC5D0 ${summarizePii(piiFindings)}\uC774(\uAC00) \uC788\uC2B5\uB2C8\uB2E4. \uC678\uBD80 \uACF5\uC720 \uC2DC \uC8FC\uC758\uD558\uC138\uC694.`
|
|
10885
|
+
];
|
|
10886
|
+
}
|
|
10791
10887
|
getEventEmitter()?.(proposal);
|
|
10792
10888
|
const approvalResult = await approvalHandler(proposal);
|
|
10793
10889
|
if (!approvalResult.approved) {
|
|
@@ -10832,23 +10928,27 @@ import { basename as basename3, extname as extname2, join as join32 } from "path
|
|
|
10832
10928
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
10833
10929
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
10834
10930
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
10931
|
+
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10835
10932
|
import { z as z3 } from "zod";
|
|
10836
10933
|
import { readFile as readFile32 } from "fs/promises";
|
|
10837
10934
|
import { blocksToMarkdown, compare } from "@clazic/kordoc";
|
|
10838
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";
|
|
10839
10939
|
import { readFile as readFile4 } from "fs/promises";
|
|
10840
10940
|
import { extname as extname3 } from "path";
|
|
10841
10941
|
import { z as z32 } from "zod";
|
|
10842
|
-
import {
|
|
10843
|
-
import { extname as extname4, join as join4, relative as relative2 } from "path";
|
|
10844
|
-
import { z as z4 } from "zod";
|
|
10845
|
-
import { readFile as readFile5 } from "fs/promises";
|
|
10942
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
10846
10943
|
import { extname as extname5 } from "path";
|
|
10847
10944
|
import { z as z5 } from "zod";
|
|
10848
|
-
import {
|
|
10849
|
-
import { extname as extname6 } from "path";
|
|
10850
|
-
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";
|
|
10851
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";
|
|
10852
10952
|
import {
|
|
10853
10953
|
Document,
|
|
10854
10954
|
HeadingLevel,
|
|
@@ -10860,37 +10960,45 @@ import {
|
|
|
10860
10960
|
TextRun,
|
|
10861
10961
|
WidthType
|
|
10862
10962
|
} from "docx";
|
|
10863
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
10864
|
-
import { extname as extname7 } from "path";
|
|
10865
|
-
import { parse as parse2 } from "@clazic/kordoc";
|
|
10866
|
-
import { z as z7 } from "zod";
|
|
10867
10963
|
import { readFile as readFile8 } from "fs/promises";
|
|
10868
10964
|
import { extname as extname8 } from "path";
|
|
10869
|
-
import {
|
|
10870
|
-
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10965
|
+
import { parse as parse2 } from "@clazic/kordoc";
|
|
10871
10966
|
import { z as z8 } from "zod";
|
|
10872
10967
|
import { readFile as readFile9 } from "fs/promises";
|
|
10873
10968
|
import { extname as extname9 } from "path";
|
|
10874
|
-
import
|
|
10969
|
+
import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
|
|
10875
10970
|
import { z as z9 } from "zod";
|
|
10876
10971
|
import { readFile as readFile10 } from "fs/promises";
|
|
10877
|
-
import { extname as extname10 } from "path";
|
|
10878
|
-
|
|
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);
|
|
10879
10975
|
import { z as z10 } from "zod";
|
|
10880
|
-
import { readFile as readFile11
|
|
10976
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
10881
10977
|
import { extname as extname11 } from "path";
|
|
10882
|
-
import
|
|
10978
|
+
import ExcelJS from "exceljs";
|
|
10883
10979
|
import { z as z11 } from "zod";
|
|
10884
|
-
import { readFile as
|
|
10980
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
10981
|
+
import { extname as extname12 } from "path";
|
|
10982
|
+
import { parse as parse4 } from "@clazic/kordoc";
|
|
10885
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";
|
|
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";
|
|
10886
10994
|
import { stat as stat6 } from "fs/promises";
|
|
10887
|
-
import { extname as
|
|
10995
|
+
import { extname as extname15 } from "path";
|
|
10888
10996
|
import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
|
|
10889
|
-
import { z as
|
|
10997
|
+
import { z as z16 } from "zod";
|
|
10890
10998
|
import { stat as stat7 } from "fs/promises";
|
|
10891
|
-
import { extname as
|
|
10999
|
+
import { extname as extname16 } from "path";
|
|
10892
11000
|
import ExcelJS2 from "exceljs";
|
|
10893
|
-
import { z as
|
|
11001
|
+
import { z as z17 } from "zod";
|
|
10894
11002
|
async function resolveSafePath(cwd, p) {
|
|
10895
11003
|
const normalizedCwd = normalize(cwd).normalize("NFC");
|
|
10896
11004
|
const normalizedP = p.normalize("NFC");
|
|
@@ -11155,19 +11263,29 @@ var restoreBackupTool = {
|
|
|
11155
11263
|
if (candidates.length === 0) {
|
|
11156
11264
|
return `\uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetBase}. list_backups\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
|
|
11157
11265
|
}
|
|
11266
|
+
const byNewest = (a, b) => b.mtimeMs !== a.mtimeMs ? b.mtimeMs - a.mtimeMs : b.tsToken.localeCompare(a.tsToken);
|
|
11158
11267
|
let chosen;
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
);
|
|
11163
|
-
if (
|
|
11164
|
-
|
|
11268
|
+
let ambiguityNote = null;
|
|
11269
|
+
const requested = input.backup?.trim();
|
|
11270
|
+
if (requested) {
|
|
11271
|
+
const exact = candidates.find((c) => c.filename === requested);
|
|
11272
|
+
if (exact) {
|
|
11273
|
+
chosen = exact;
|
|
11274
|
+
} else {
|
|
11275
|
+
const matches = candidates.filter(
|
|
11276
|
+
(c) => c.filename.endsWith(requested) || c.fullPath.endsWith(requested)
|
|
11277
|
+
);
|
|
11278
|
+
if (matches.length === 0) {
|
|
11279
|
+
return `\uC9C0\uC815\uD55C \uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.backup}. list_backups\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5\uC758 \uC815\uD655\uD55C \uD30C\uC77C\uBA85\uC744 \uD655\uC778\uD558\uC138\uC694.`;
|
|
11280
|
+
}
|
|
11281
|
+
matches.sort(byNewest);
|
|
11282
|
+
chosen = matches[0];
|
|
11283
|
+
if (matches.length > 1) {
|
|
11284
|
+
ambiguityNote = `'${input.backup}'\uC640 \uC77C\uCE58\uD558\uB294 \uBC31\uC5C5\uC774 ${matches.length}\uAC1C\uC5EC\uC11C \uAC00\uC7A5 \uCD5C\uADFC(${formatTimestamp(chosen.tsToken)}) \uAC83\uC744 \uC120\uD0DD\uD588\uC2B5\uB2C8\uB2E4. \uD2B9\uC815 \uBC31\uC5C5\uC744 \uC6D0\uD558\uBA74 list_backups\uC758 \uC804\uCCB4 \uD30C\uC77C\uBA85\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
11285
|
+
}
|
|
11165
11286
|
}
|
|
11166
|
-
chosen = found;
|
|
11167
11287
|
} else {
|
|
11168
|
-
candidates.sort(
|
|
11169
|
-
(a, b) => b.mtimeMs !== a.mtimeMs ? b.mtimeMs - a.mtimeMs : b.tsToken.localeCompare(a.tsToken)
|
|
11170
|
-
);
|
|
11288
|
+
candidates.sort(byNewest);
|
|
11171
11289
|
chosen = candidates[0];
|
|
11172
11290
|
}
|
|
11173
11291
|
let backupBytes;
|
|
@@ -11208,12 +11326,15 @@ var restoreBackupTool = {
|
|
|
11208
11326
|
].join("\n");
|
|
11209
11327
|
}
|
|
11210
11328
|
const warnings = [];
|
|
11211
|
-
const autoSelected = !
|
|
11329
|
+
const autoSelected = !requested && candidates.length > 1;
|
|
11212
11330
|
if (autoSelected) {
|
|
11213
11331
|
warnings.push(
|
|
11214
11332
|
`\uBC31\uC5C5\uC744 \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544 \uAC00\uC7A5 \uCD5C\uADFC \uBC31\uC5C5(${formatTimestamp(chosen.tsToken)})\uC744 \uC790\uB3D9\uC73C\uB85C \uC120\uD0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uB978 \uBC31\uC5C5\uC744 \uC6D0\uD558\uBA74 list_backups\uB85C \uD655\uC778 \uD6C4 backup \uD30C\uB77C\uBBF8\uD130\uB97C \uC9C0\uC815\uD558\uC138\uC694.`
|
|
11215
11333
|
);
|
|
11216
11334
|
}
|
|
11335
|
+
if (ambiguityNote) {
|
|
11336
|
+
warnings.push(ambiguityNote);
|
|
11337
|
+
}
|
|
11217
11338
|
warnings.push(
|
|
11218
11339
|
"\uBCF5\uC6D0\uC744 \uC2E4\uD589\uD558\uBA74 \uD604\uC7AC \uD30C\uC77C\uB3C4 \uBC31\uC5C5\uB41C \uB4A4 \uB36E\uC5B4\uC4F0\uC5EC\uC9D1\uB2C8\uB2E4(\uBCF5\uC6D0 \uC790\uCCB4\uB3C4 \uB418\uB3CC\uB9B4 \uC218 \uC788\uC74C)."
|
|
11219
11340
|
);
|
|
@@ -11377,458 +11498,357 @@ var compareDocumentsTool = {
|
|
|
11377
11498
|
return output;
|
|
11378
11499
|
}
|
|
11379
11500
|
};
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
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;
|
|
11385
11577
|
}
|
|
11386
|
-
function
|
|
11387
|
-
|
|
11388
|
-
const m = re.exec(openTag);
|
|
11389
|
-
return m ? decodeXml(m[1] ?? "") : "";
|
|
11578
|
+
function escapeXml(text3) {
|
|
11579
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11390
11580
|
}
|
|
11391
|
-
function
|
|
11392
|
-
const
|
|
11393
|
-
|
|
11394
|
-
const
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
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
|
+
}
|
|
11406
11598
|
}
|
|
11407
11599
|
}
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
if (
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
switch (type) {
|
|
11423
|
-
case "button":
|
|
11424
|
-
currentValue = getAttr(openTag, "caption");
|
|
11425
|
-
break;
|
|
11426
|
-
case "checkBox":
|
|
11427
|
-
case "radioButton":
|
|
11428
|
-
currentValue = getAttr(openTag, "value") === "CHECKED";
|
|
11429
|
-
break;
|
|
11430
|
-
case "comboBox": {
|
|
11431
|
-
currentValue = getAttr(openTag, "selectedValue");
|
|
11432
|
-
const listItemRe = /<hp:listItem\b[^>]*/g;
|
|
11433
|
-
const items = [];
|
|
11434
|
-
for (let lm = listItemRe.exec(elementXml); lm !== null; lm = listItemRe.exec(elementXml)) {
|
|
11435
|
-
const itemValue = getAttr(lm[0] ?? "", "value");
|
|
11436
|
-
items.push(itemValue);
|
|
11437
|
-
}
|
|
11438
|
-
comboItems = items;
|
|
11439
|
-
break;
|
|
11440
|
-
}
|
|
11441
|
-
case "edit": {
|
|
11442
|
-
const textSelfClose = /<hp:text\s*\/>/;
|
|
11443
|
-
const textOpen = /<hp:text>/;
|
|
11444
|
-
const textClose = "</hp:text>";
|
|
11445
|
-
if (textSelfClose.test(elementXml)) {
|
|
11446
|
-
currentValue = "";
|
|
11447
|
-
} else {
|
|
11448
|
-
const tOpenIdx = elementXml.search(textOpen);
|
|
11449
|
-
if (tOpenIdx >= 0) {
|
|
11450
|
-
const afterOpen = elementXml.indexOf(">", tOpenIdx) + 1;
|
|
11451
|
-
const closePos = elementXml.indexOf(textClose, afterOpen);
|
|
11452
|
-
currentValue = closePos >= 0 ? decodeXml(elementXml.slice(afterOpen, closePos)) : "";
|
|
11453
|
-
} else {
|
|
11454
|
-
currentValue = "";
|
|
11455
|
-
}
|
|
11456
|
-
}
|
|
11457
|
-
break;
|
|
11600
|
+
return tcRanges.filter((tc) => tc.depth === 0);
|
|
11601
|
+
}
|
|
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);
|
|
11458
11614
|
}
|
|
11459
11615
|
}
|
|
11460
|
-
results.push({
|
|
11461
|
-
index: idx++,
|
|
11462
|
-
name,
|
|
11463
|
-
type,
|
|
11464
|
-
currentValue,
|
|
11465
|
-
comboItems,
|
|
11466
|
-
sectionFile,
|
|
11467
|
-
posInSection: pos
|
|
11468
|
-
});
|
|
11469
11616
|
}
|
|
11470
|
-
return
|
|
11617
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11471
11618
|
}
|
|
11472
|
-
function
|
|
11473
|
-
|
|
11474
|
-
|
|
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;
|
|
11628
|
+
}
|
|
11629
|
+
topLevelCount++;
|
|
11630
|
+
}
|
|
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 };
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
}
|
|
11639
|
+
return null;
|
|
11640
|
+
}
|
|
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 };
|
|
11650
|
+
}
|
|
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 = [];
|
|
11475
11671
|
for (let ei = 0; ei < edits.length; ei++) {
|
|
11476
11672
|
const edit = edits[ei];
|
|
11477
|
-
const
|
|
11478
|
-
|
|
11479
|
-
const xmlTag = typeToXmlTag(target.type);
|
|
11480
|
-
const openTagEnd = xml.indexOf(">", pos);
|
|
11481
|
-
if (openTagEnd < 0) {
|
|
11482
|
-
results[ei] = {
|
|
11483
|
-
success: false,
|
|
11484
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uC5EC\uB294 \uD0DC\uADF8 \uB05D\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11485
|
-
};
|
|
11486
|
-
continue;
|
|
11487
|
-
}
|
|
11488
|
-
const _openTag = xml.slice(pos, openTagEnd + 1);
|
|
11489
|
-
const closeTag = `</${xmlTag}>`;
|
|
11490
|
-
const closeIdx = xml.indexOf(closeTag, openTagEnd);
|
|
11491
|
-
if (closeIdx < 0) {
|
|
11673
|
+
const tblRange = findTopLevelTableRange(tokens, edit.tableIndex);
|
|
11674
|
+
if (!tblRange) {
|
|
11492
11675
|
results[ei] = {
|
|
11493
11676
|
success: false,
|
|
11494
|
-
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.`
|
|
11495
11678
|
};
|
|
11496
11679
|
continue;
|
|
11497
11680
|
}
|
|
11498
|
-
const
|
|
11499
|
-
const
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
const items = target.comboItems ?? [];
|
|
11521
|
-
if (!items.includes(set.selected)) {
|
|
11522
|
-
const validList = items.map((v) => `"${v}"`).join(", ");
|
|
11523
|
-
results[ei] = {
|
|
11524
|
-
success: false,
|
|
11525
|
-
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)"}`
|
|
11526
|
-
};
|
|
11527
|
-
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
|
+
}
|
|
11528
11703
|
}
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
if (set.caption !== void 0) {
|
|
11532
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption", escapeXml(set.caption));
|
|
11533
|
-
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) {
|
|
11534
11706
|
results[ei] = {
|
|
11535
11707
|
success: false,
|
|
11536
|
-
|
|
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.`
|
|
11537
11710
|
};
|
|
11538
|
-
|
|
11711
|
+
found = true;
|
|
11712
|
+
break;
|
|
11539
11713
|
}
|
|
11540
|
-
|
|
11541
|
-
patchCreated = true;
|
|
11542
|
-
} else if (set.checked !== void 0) {
|
|
11543
|
-
const newValue = set.checked ? "CHECKED" : "UNCHECKED";
|
|
11544
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "value", newValue);
|
|
11545
|
-
if (!patch) {
|
|
11714
|
+
if (ownTRuns.length === 0) {
|
|
11546
11715
|
results[ei] = {
|
|
11547
11716
|
success: false,
|
|
11548
|
-
|
|
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.`
|
|
11549
11719
|
};
|
|
11550
|
-
|
|
11720
|
+
found = true;
|
|
11721
|
+
break;
|
|
11551
11722
|
}
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
results[ei] = {
|
|
11564
|
-
success: false,
|
|
11565
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 selectedValue \uC18D\uC131\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11566
|
-
};
|
|
11567
|
-
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 });
|
|
11568
11734
|
}
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
if (!textPatch) {
|
|
11575
|
-
results[ei] = {
|
|
11576
|
-
success: false,
|
|
11577
|
-
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}"\uC758 <hp:text> \uC694\uC18C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
11578
|
-
};
|
|
11579
|
-
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
|
+
}
|
|
11580
11740
|
}
|
|
11581
|
-
|
|
11582
|
-
|
|
11741
|
+
replacements.push({ editIdx: ei, patches });
|
|
11742
|
+
results[ei] = { success: true, oldText: currentText };
|
|
11743
|
+
found = true;
|
|
11744
|
+
break;
|
|
11583
11745
|
}
|
|
11584
|
-
if (!
|
|
11585
|
-
results[ei] = {
|
|
11586
|
-
|
|
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
|
+
};
|
|
11587
11751
|
}
|
|
11588
|
-
results[ei] = { success: true, oldValue: currentRaw };
|
|
11589
11752
|
}
|
|
11590
11753
|
if (results.some((r) => !r.success)) {
|
|
11591
11754
|
return { newXml: xml, results };
|
|
11592
11755
|
}
|
|
11593
|
-
const
|
|
11756
|
+
const allPatches = replacements.flatMap((r) => r.patches).sort((a, b) => b.from - a.from);
|
|
11594
11757
|
let result = xml;
|
|
11595
|
-
for (const patch of
|
|
11596
|
-
result = result.
|
|
11758
|
+
for (const patch of allPatches) {
|
|
11759
|
+
result = result.substring(0, patch.from) + patch.text + result.substring(patch.to);
|
|
11597
11760
|
}
|
|
11598
11761
|
return { newXml: result, results };
|
|
11599
11762
|
}
|
|
11600
|
-
function
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
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--;
|
|
11781
|
+
}
|
|
11782
|
+
}
|
|
11783
|
+
sectionTblCounts.push(count);
|
|
11612
11784
|
}
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
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.`;
|
|
11633
|
-
}
|
|
11634
|
-
return null;
|
|
11635
|
-
}
|
|
11636
|
-
function readCurrentValue(xml, pos, openTagEnd, _afterOpen, elementEnd, type) {
|
|
11637
|
-
const openTag = xml.slice(pos, openTagEnd + 1);
|
|
11638
|
-
switch (type) {
|
|
11639
|
-
case "button":
|
|
11640
|
-
return decodeXml(getAttr(openTag, "caption"));
|
|
11641
|
-
case "checkBox":
|
|
11642
|
-
case "radioButton":
|
|
11643
|
-
return getAttr(openTag, "value") === "CHECKED";
|
|
11644
|
-
case "comboBox":
|
|
11645
|
-
return decodeXml(getAttr(openTag, "selectedValue"));
|
|
11646
|
-
case "edit": {
|
|
11647
|
-
const elementContent = xml.slice(pos, elementEnd);
|
|
11648
|
-
if (/<hp:text\s*\/>/.test(elementContent)) return "";
|
|
11649
|
-
const tOpenIdx = elementContent.search(/<hp:text>/);
|
|
11650
|
-
if (tOpenIdx >= 0) {
|
|
11651
|
-
const afterOpen = elementContent.indexOf(">", tOpenIdx) + 1;
|
|
11652
|
-
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
11653
|
-
return closePos >= 0 ? decodeXml(elementContent.slice(afterOpen, closePos)) : "";
|
|
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
|
+
}
|
|
11654
11804
|
}
|
|
11655
|
-
return "";
|
|
11656
11805
|
}
|
|
11806
|
+
offset += count;
|
|
11657
11807
|
}
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
const relFrom = m.index + (m[1]?.length ?? 0);
|
|
11678
|
-
const relTo = relFrom + (m[2]?.length ?? 0);
|
|
11679
|
-
return {
|
|
11680
|
-
from: tagStart + relFrom,
|
|
11681
|
-
to: tagStart + relTo,
|
|
11682
|
-
text: newValue
|
|
11683
|
-
};
|
|
11684
|
-
}
|
|
11685
|
-
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
11686
|
-
const escaped = escapeXml(newText);
|
|
11687
|
-
const selfCloseRe = /<hp:text\s*\/>/;
|
|
11688
|
-
const scm = selfCloseRe.exec(elementContent);
|
|
11689
|
-
if (scm) {
|
|
11690
|
-
return {
|
|
11691
|
-
from: pos + scm.index,
|
|
11692
|
-
to: pos + scm.index + scm[0].length,
|
|
11693
|
-
text: `<hp:text>${escaped}</hp:text>`
|
|
11694
|
-
};
|
|
11695
|
-
}
|
|
11696
|
-
const openRe = /<hp:text>/;
|
|
11697
|
-
const om = openRe.exec(elementContent);
|
|
11698
|
-
if (om) {
|
|
11699
|
-
const afterOpen = om.index + om[0].length;
|
|
11700
|
-
const closePos = elementContent.indexOf("</hp:text>", afterOpen);
|
|
11701
|
-
if (closePos < 0) return null;
|
|
11702
|
-
return {
|
|
11703
|
-
from: pos + afterOpen,
|
|
11704
|
-
to: pos + closePos,
|
|
11705
|
-
text: escaped
|
|
11706
|
-
};
|
|
11707
|
-
}
|
|
11708
|
-
return null;
|
|
11709
|
-
}
|
|
11710
|
-
async function listFormObjectsFromZip(zip) {
|
|
11711
|
-
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
11712
|
-
const sectionXmls = [];
|
|
11713
|
-
const objects = [];
|
|
11714
|
-
let globalIndex = 0;
|
|
11715
|
-
for (const sf of sectionFiles) {
|
|
11716
|
-
const entry = zip.file(sf);
|
|
11717
|
-
const xml = entry ? await entry.async("string") : "";
|
|
11718
|
-
sectionXmls.push(xml);
|
|
11719
|
-
const parsed = parseFormObjects(xml, sf, globalIndex);
|
|
11720
|
-
objects.push(...parsed);
|
|
11721
|
-
globalIndex += parsed.length;
|
|
11722
|
-
}
|
|
11723
|
-
return { objects, sectionFiles, sectionXmls };
|
|
11724
|
-
}
|
|
11725
|
-
function validateHwpxBuffer(ext, buffer) {
|
|
11726
|
-
if (ext === ".hwp") {
|
|
11727
|
-
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.";
|
|
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
|
+
}
|
|
11728
11827
|
}
|
|
11729
|
-
if (
|
|
11730
|
-
return
|
|
11828
|
+
if (allResults.some((r) => !r.success)) {
|
|
11829
|
+
return { buffer: hwpxBuffer, results: allResults };
|
|
11731
11830
|
}
|
|
11732
|
-
|
|
11733
|
-
|
|
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" });
|
|
11734
11835
|
}
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
checked: z32.boolean().optional().describe("CheckBox/RadioButton \uCCB4\uD06C \uC0C1\uD0DC (true=CHECKED)"),
|
|
11743
|
-
selected: z32.string().optional().describe("ComboBox \uC120\uD0DD \uAC12 (listItem \uC911 \uD558\uB098\uC5EC\uC57C \uD568)"),
|
|
11744
|
-
text: z32.string().optional().describe("Edit \uD14D\uC2A4\uD2B8 \uB0B4\uC6A9")
|
|
11745
|
-
}).refine(
|
|
11746
|
-
(v) => {
|
|
11747
|
-
const keys = ["caption", "checked", "selected", "text"].filter(
|
|
11748
|
-
(k) => v[k] !== void 0
|
|
11749
|
-
);
|
|
11750
|
-
return keys.length === 1;
|
|
11751
|
-
},
|
|
11752
|
-
{ message: "set \uD544\uB4DC\uB294 caption/checked/selected/text \uC911 \uC815\uD655\uD788 \uD558\uB098\uB9CC \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4." }
|
|
11753
|
-
);
|
|
11754
|
-
var formEditExpectedSchema = z32.object({
|
|
11755
|
-
caption: z32.string().optional(),
|
|
11756
|
-
checked: z32.boolean().optional(),
|
|
11757
|
-
selected: z32.string().optional(),
|
|
11758
|
-
text: z32.string().optional()
|
|
11759
|
-
}).optional();
|
|
11760
|
-
var formEditItemSchema = z32.object({
|
|
11761
|
-
name: z32.string().describe("\uC591\uC2DD \uAC1C\uCCB4\uC758 name \uC18D\uC131 \uAC12"),
|
|
11762
|
-
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"),
|
|
11763
|
-
set: formEditSetSchema.describe("\uBCC0\uACBD\uD560 \uAC12 (caption/checked/selected/text \uC911 \uD558\uB098)"),
|
|
11764
|
-
expected: formEditExpectedSchema.describe(
|
|
11765
|
-
"\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."
|
|
11766
|
-
)
|
|
11767
|
-
});
|
|
11768
|
-
var proposeFormObjectSchema = z32.object({
|
|
11769
|
-
path: z32.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C"),
|
|
11770
|
-
edits: z32.array(formEditItemSchema).min(1).describe("\uC591\uC2DD \uAC1C\uCCB4 \uD3B8\uC9D1 \uBAA9\uB85D"),
|
|
11771
|
-
summary: z32.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
11772
|
-
});
|
|
11773
|
-
var listFormObjectsTool = {
|
|
11774
|
-
name: "list_form_objects",
|
|
11775
|
-
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.",
|
|
11776
|
-
inputSchema: listFormObjectsSchema,
|
|
11777
|
-
requiresApproval: false,
|
|
11778
|
-
execute: async ({
|
|
11779
|
-
input,
|
|
11780
|
-
ctx
|
|
11781
|
-
}) => {
|
|
11782
|
-
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11783
|
-
const ext = extname3(safePath).toLowerCase();
|
|
11784
|
-
let buffer;
|
|
11785
|
-
try {
|
|
11786
|
-
buffer = await readFile4(safePath);
|
|
11787
|
-
} catch {
|
|
11788
|
-
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
11789
|
-
}
|
|
11790
|
-
const validationError = validateHwpxBuffer(ext, new Uint8Array(buffer.buffer));
|
|
11791
|
-
if (validationError) return validationError;
|
|
11792
|
-
let zip;
|
|
11793
|
-
try {
|
|
11794
|
-
zip = await import_jszip.default.loadAsync(new Uint8Array(buffer.buffer));
|
|
11795
|
-
} catch {
|
|
11796
|
-
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.";
|
|
11797
|
-
}
|
|
11798
|
-
const { objects } = await listFormObjectsFromZip(zip);
|
|
11799
|
-
if (objects.length === 0) {
|
|
11800
|
-
return "\uC774 \uBB38\uC11C\uC5D0\uB294 \uC591\uC2DD \uAC1C\uCCB4(form object)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
11801
|
-
}
|
|
11802
|
-
const lines = [`\uCD1D ${objects.length}\uAC1C\uC758 \uC591\uC2DD \uAC1C\uCCB4\uAC00 \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
|
|
11803
|
-
`];
|
|
11804
|
-
const typeKo = {
|
|
11805
|
-
button: "PushButton",
|
|
11806
|
-
checkBox: "CheckBox",
|
|
11807
|
-
radioButton: "RadioButton",
|
|
11808
|
-
comboBox: "ComboBox",
|
|
11809
|
-
edit: "Edit"
|
|
11810
|
-
};
|
|
11811
|
-
for (const obj of objects) {
|
|
11812
|
-
let valueStr;
|
|
11813
|
-
if (typeof obj.currentValue === "boolean") {
|
|
11814
|
-
valueStr = obj.currentValue ? "\uCCB4\uD06C\uB428 (CHECKED)" : "\uCCB4\uD06C \uD574\uC81C\uB428 (UNCHECKED)";
|
|
11815
|
-
} else {
|
|
11816
|
-
valueStr = `"${obj.currentValue}"`;
|
|
11817
|
-
}
|
|
11818
|
-
let line = `[${obj.index}] name="${obj.name}" | \uD0C0\uC785: ${typeKo[obj.type]} | \uD604\uC7AC \uAC12: ${valueStr}`;
|
|
11819
|
-
if (obj.type === "comboBox" && obj.comboItems) {
|
|
11820
|
-
const itemList = obj.comboItems.map((v) => `"${v}"`).join(", ");
|
|
11821
|
-
line += ` | \uD56D\uBAA9: [${itemList}]`;
|
|
11822
|
-
}
|
|
11823
|
-
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"));
|
|
11824
11843
|
}
|
|
11825
|
-
return lines.join("\n");
|
|
11826
11844
|
}
|
|
11827
|
-
};
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
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,
|
|
11832
11852
|
requiresApproval: true,
|
|
11833
11853
|
propose: async ({
|
|
11834
11854
|
input,
|
|
@@ -11836,145 +11856,196 @@ var proposeFormObjectTool = {
|
|
|
11836
11856
|
}) => {
|
|
11837
11857
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11838
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
|
+
}
|
|
11839
11865
|
let originalBuffer;
|
|
11840
11866
|
try {
|
|
11841
11867
|
originalBuffer = await readFile4(safePath);
|
|
11842
11868
|
} catch {
|
|
11843
|
-
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.`;
|
|
11844
11870
|
}
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
if (validationError) return validationError;
|
|
11848
|
-
let zip;
|
|
11849
|
-
try {
|
|
11850
|
-
zip = await import_jszip.default.loadAsync(bufArray);
|
|
11851
|
-
} catch {
|
|
11852
|
-
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.";
|
|
11853
11873
|
}
|
|
11854
|
-
const
|
|
11855
|
-
const
|
|
11856
|
-
const
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
const
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
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
|
+
}
|
|
11866
11891
|
}
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
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;
|
|
11875
11907
|
}
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
)
|
|
11881
|
-
|
|
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
|
+
}
|
|
11882
11926
|
}
|
|
11883
|
-
|
|
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;
|
|
11884
11949
|
} else {
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
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;
|
|
11891
11969
|
}
|
|
11892
11970
|
}
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
}
|
|
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
|
+
}
|
|
11899
12016
|
}
|
|
11900
12017
|
if (resolveErrors.length > 0) {
|
|
11901
|
-
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.
|
|
11902
12019
|
${resolveErrors.join("\n")}`;
|
|
11903
12020
|
}
|
|
11904
|
-
const
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
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}`;
|
|
11913
12038
|
});
|
|
11914
|
-
}
|
|
11915
|
-
const sectionResults = [];
|
|
11916
|
-
const newSectionXmls = [...sectionXmls];
|
|
11917
|
-
for (const [si, edits] of sectionEditMap) {
|
|
11918
|
-
const xml = sectionXmls[si] ?? "";
|
|
11919
|
-
const { newXml, results } = applyFormObjectEdits(xml, edits);
|
|
11920
|
-
newSectionXmls[si] = newXml;
|
|
11921
|
-
sectionResults.push({ si, results, edits });
|
|
11922
|
-
}
|
|
11923
|
-
const failMessages = [];
|
|
11924
|
-
const successMap = /* @__PURE__ */ new Map();
|
|
11925
|
-
for (const sr of sectionResults) {
|
|
11926
|
-
for (let i = 0; i < sr.edits.length; i++) {
|
|
11927
|
-
const editReq = sr.edits[i];
|
|
11928
|
-
const result = sr.results[i];
|
|
11929
|
-
const resolved = resolvedEdits.find((r) => r.target === editReq.target);
|
|
11930
|
-
if (resolved) {
|
|
11931
|
-
successMap.set(resolved.editIdx, result);
|
|
11932
|
-
if (!result.success) {
|
|
11933
|
-
failMessages.push(
|
|
11934
|
-
`\uD3B8\uC9D1 #${resolved.editIdx + 1} (name="${editReq.target.name}"): ${result.error}`
|
|
11935
|
-
);
|
|
11936
|
-
}
|
|
11937
|
-
}
|
|
11938
|
-
}
|
|
11939
|
-
}
|
|
11940
|
-
if (failMessages.length > 0) {
|
|
11941
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.
|
|
11942
|
-
${
|
|
11943
|
-
}
|
|
11944
|
-
const out = new import_jszip.default();
|
|
11945
|
-
const mimetypeEntry = zip.file("mimetype");
|
|
11946
|
-
if (mimetypeEntry) {
|
|
11947
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
11948
|
-
}
|
|
11949
|
-
for (const [name, entry] of Object.entries(zip.files)) {
|
|
11950
|
-
if (name === "mimetype" || entry.dir) continue;
|
|
11951
|
-
const si = sectionFiles.indexOf(name);
|
|
11952
|
-
if (si >= 0) {
|
|
11953
|
-
out.file(name, newSectionXmls[si] ?? "");
|
|
11954
|
-
} else {
|
|
11955
|
-
out.file(name, await entry.async("uint8array"));
|
|
11956
|
-
}
|
|
12040
|
+
${messages.join("\n")}`;
|
|
11957
12041
|
}
|
|
11958
|
-
const
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
edit: "Edit"
|
|
11966
|
-
};
|
|
11967
|
-
const diffLines = ["| \uC591\uC2DD \uAC1C\uCCB4 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
11968
|
-
for (const re of resolvedEdits) {
|
|
11969
|
-
const result = successMap.get(re.editIdx);
|
|
11970
|
-
const oldVal = result?.oldValue ?? "";
|
|
11971
|
-
const oldStr = typeof oldVal === "boolean" ? oldVal ? "CHECKED" : "UNCHECKED" : String(oldVal);
|
|
11972
|
-
let newStr;
|
|
11973
|
-
if (re.set.caption !== void 0) newStr = re.set.caption;
|
|
11974
|
-
else if (re.set.checked !== void 0) newStr = re.set.checked ? "CHECKED" : "UNCHECKED";
|
|
11975
|
-
else if (re.set.selected !== void 0) newStr = re.set.selected;
|
|
11976
|
-
else newStr = re.set.text ?? "";
|
|
11977
|
-
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} |`);
|
|
11978
12049
|
}
|
|
11979
12050
|
const diff = diffLines.join("\n");
|
|
11980
12051
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
@@ -11983,7 +12054,7 @@ ${failMessages.join("\n")}`;
|
|
|
11983
12054
|
return {
|
|
11984
12055
|
proposal: {
|
|
11985
12056
|
id: proposalId,
|
|
11986
|
-
kind: "
|
|
12057
|
+
kind: "cell-edit",
|
|
11987
12058
|
targetPath: outputPath,
|
|
11988
12059
|
stagedPath,
|
|
11989
12060
|
summary: input.summary,
|
|
@@ -12000,640 +12071,790 @@ ${failMessages.join("\n")}`;
|
|
|
12000
12071
|
};
|
|
12001
12072
|
}
|
|
12002
12073
|
};
|
|
12003
|
-
var
|
|
12004
|
-
".
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
".docx",
|
|
12008
|
-
".doc",
|
|
12009
|
-
".xlsx",
|
|
12010
|
-
".xls",
|
|
12011
|
-
".pdf",
|
|
12012
|
-
".pptx",
|
|
12013
|
-
".ppt",
|
|
12014
|
-
".md",
|
|
12015
|
-
".txt"
|
|
12016
|
-
]);
|
|
12017
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12018
|
-
"node_modules",
|
|
12019
|
-
".git",
|
|
12020
|
-
".DS_Store",
|
|
12021
|
-
"__pycache__",
|
|
12022
|
-
"dist",
|
|
12023
|
-
"build",
|
|
12024
|
-
".next"
|
|
12025
|
-
]);
|
|
12026
|
-
var MAX_DEPTH = 4;
|
|
12027
|
-
var MAX_FILES = 500;
|
|
12028
|
-
var listFilesSchema = z4.object({
|
|
12029
|
-
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)")
|
|
12030
12078
|
});
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
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
|
+
}
|
|
12049
12136
|
}
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
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
|
+
}
|
|
12058
12179
|
}
|
|
12180
|
+
globalOffset += localCount;
|
|
12059
12181
|
}
|
|
12182
|
+
return hits;
|
|
12060
12183
|
}
|
|
12061
|
-
var
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
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,
|
|
12065
12203
|
requiresApproval: false,
|
|
12066
12204
|
execute: async ({
|
|
12067
12205
|
input,
|
|
12068
12206
|
ctx
|
|
12069
12207
|
}) => {
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
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)}`;
|
|
12075
12213
|
}
|
|
12076
|
-
const
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
}
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
}
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
"\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."
|
|
12105
|
-
);
|
|
12106
|
-
var proposeCellEditSchema = z5.object({
|
|
12107
|
-
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)"),
|
|
12108
|
-
edits: z5.array(cellEditItemSchema).min(1).describe(
|
|
12109
|
-
"\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"
|
|
12110
|
-
),
|
|
12111
|
-
summary: z5.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12112
|
-
});
|
|
12113
|
-
function tokenizeHwpxXml(xml) {
|
|
12114
|
-
const tokens = [];
|
|
12115
|
-
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*/g;
|
|
12116
|
-
let startPos = 0;
|
|
12117
|
-
let m = re.exec(xml);
|
|
12118
|
-
while (m !== null) {
|
|
12119
|
-
const raw = m[0];
|
|
12120
|
-
const pos = m.index;
|
|
12121
|
-
if (raw.startsWith("<hp:tbl")) {
|
|
12122
|
-
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
12123
|
-
} else if (raw === "</hp:tbl>") {
|
|
12124
|
-
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
12125
|
-
} else if (raw.startsWith("<hp:tc")) {
|
|
12126
|
-
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
12127
|
-
} else if (raw === "</hp:tc>") {
|
|
12128
|
-
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
12129
|
-
} else if (raw === "<hp:t/>") {
|
|
12130
|
-
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
12131
|
-
} else if (raw === "<hp:t>") {
|
|
12132
|
-
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
12133
|
-
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
12134
|
-
const colM = raw.match(/colAddr="(\d+)"/);
|
|
12135
|
-
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
12136
|
-
if (colM && rowM) {
|
|
12137
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
12138
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
12139
|
-
tokens.push({
|
|
12140
|
-
kind: "cell_addr",
|
|
12141
|
-
pos,
|
|
12142
|
-
end,
|
|
12143
|
-
colAddr: Number(colM[1]),
|
|
12144
|
-
rowAddr: Number(rowM[1])
|
|
12145
|
-
});
|
|
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("");
|
|
12146
12242
|
}
|
|
12147
|
-
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
12148
|
-
const colM = raw.match(/colSpan="(\d+)"/);
|
|
12149
|
-
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
12150
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
12151
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
12152
|
-
tokens.push({
|
|
12153
|
-
kind: "cell_span",
|
|
12154
|
-
pos,
|
|
12155
|
-
end,
|
|
12156
|
-
colSpan: colM ? Number(colM[1]) : 1,
|
|
12157
|
-
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
12158
|
-
});
|
|
12159
12243
|
}
|
|
12160
|
-
|
|
12161
|
-
|
|
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);
|
|
12162
12258
|
}
|
|
12163
|
-
|
|
12164
|
-
return tokens;
|
|
12165
|
-
}
|
|
12259
|
+
};
|
|
12166
12260
|
function escapeXml2(text3) {
|
|
12167
|
-
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12261
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
12168
12262
|
}
|
|
12169
|
-
function
|
|
12170
|
-
|
|
12171
|
-
const tcRanges = [];
|
|
12172
|
-
const tcStack = [];
|
|
12173
|
-
let innerDepth = 0;
|
|
12174
|
-
for (const tok of tblTokens) {
|
|
12175
|
-
if (tok.kind === "tbl_open") {
|
|
12176
|
-
innerDepth++;
|
|
12177
|
-
} else if (tok.kind === "tbl_close") {
|
|
12178
|
-
innerDepth--;
|
|
12179
|
-
} else if (tok.kind === "tc_open") {
|
|
12180
|
-
tcStack.push({ pos: tok.pos, depth: innerDepth });
|
|
12181
|
-
} else if (tok.kind === "tc_close") {
|
|
12182
|
-
const entry = tcStack.pop();
|
|
12183
|
-
if (entry !== void 0) {
|
|
12184
|
-
tcRanges.push({ start: entry.pos, end: tok.end, depth: entry.depth });
|
|
12185
|
-
}
|
|
12186
|
-
}
|
|
12187
|
-
}
|
|
12188
|
-
return tcRanges.filter((tc) => tc.depth === 0);
|
|
12263
|
+
function decodeXml(text3) {
|
|
12264
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
12189
12265
|
}
|
|
12190
|
-
function
|
|
12191
|
-
const
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12266
|
+
function getAttr(openTag, attr) {
|
|
12267
|
+
const re = new RegExp(`\\b${attr}="([^"]*)"`, "");
|
|
12268
|
+
const m = re.exec(openTag);
|
|
12269
|
+
return m ? decodeXml(m[1] ?? "") : "";
|
|
12270
|
+
}
|
|
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 });
|
|
12203
12286
|
}
|
|
12204
12287
|
}
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
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);
|
|
12216
12317
|
}
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
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;
|
|
12224
12338
|
}
|
|
12225
12339
|
}
|
|
12340
|
+
results.push({
|
|
12341
|
+
index: idx++,
|
|
12342
|
+
name,
|
|
12343
|
+
type,
|
|
12344
|
+
currentValue,
|
|
12345
|
+
comboItems,
|
|
12346
|
+
sectionFile,
|
|
12347
|
+
posInSection: pos
|
|
12348
|
+
});
|
|
12226
12349
|
}
|
|
12227
|
-
return
|
|
12228
|
-
}
|
|
12229
|
-
function readCellAddrSpan(tokens, tcStart, tcEnd) {
|
|
12230
|
-
const tcTokens = tokens.filter((t) => t.pos >= tcStart && t.pos < tcEnd);
|
|
12231
|
-
let addr = null;
|
|
12232
|
-
let span = { colSpan: 1, rowSpan: 1 };
|
|
12233
|
-
for (const t of tcTokens) {
|
|
12234
|
-
if (t.kind === "cell_addr" && t.colAddr !== void 0 && t.rowAddr !== void 0) {
|
|
12235
|
-
addr = { colAddr: t.colAddr, rowAddr: t.rowAddr };
|
|
12236
|
-
} else if (t.kind === "cell_span" && t.colSpan !== void 0 && t.rowSpan !== void 0) {
|
|
12237
|
-
span = { colSpan: t.colSpan, rowSpan: t.rowSpan };
|
|
12238
|
-
}
|
|
12239
|
-
}
|
|
12240
|
-
if (!addr) return null;
|
|
12241
|
-
return { ...addr, ...span };
|
|
12350
|
+
return results;
|
|
12242
12351
|
}
|
|
12243
|
-
function
|
|
12244
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
12352
|
+
function applyFormObjectEdits(xml, edits) {
|
|
12245
12353
|
const results = edits.map(() => ({ success: false }));
|
|
12246
|
-
|
|
12247
|
-
{
|
|
12248
|
-
let d = 0;
|
|
12249
|
-
for (const tok of tokens) {
|
|
12250
|
-
if (tok.kind === "tbl_open") {
|
|
12251
|
-
if (d === 0) totalTopLevelTbls++;
|
|
12252
|
-
d++;
|
|
12253
|
-
} else if (tok.kind === "tbl_close") {
|
|
12254
|
-
d--;
|
|
12255
|
-
}
|
|
12256
|
-
}
|
|
12257
|
-
}
|
|
12258
|
-
const replacements = [];
|
|
12354
|
+
const patches = [];
|
|
12259
12355
|
for (let ei = 0; ei < edits.length; ei++) {
|
|
12260
12356
|
const edit = edits[ei];
|
|
12261
|
-
const
|
|
12262
|
-
|
|
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) {
|
|
12263
12362
|
results[ei] = {
|
|
12264
12363
|
success: false,
|
|
12265
|
-
error: `\
|
|
12364
|
+
error: `\uC591\uC2DD \uAC1C\uCCB4 "${target.name}" \uC5EC\uB294 \uD0DC\uADF8 \uB05D\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
12266
12365
|
};
|
|
12267
12366
|
continue;
|
|
12268
12367
|
}
|
|
12269
|
-
const
|
|
12270
|
-
const
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
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;
|
|
12291
12397
|
}
|
|
12292
|
-
|
|
12293
|
-
|
|
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(", ");
|
|
12294
12403
|
results[ei] = {
|
|
12295
12404
|
success: false,
|
|
12296
|
-
|
|
12297
|
-
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)"}`
|
|
12298
12406
|
};
|
|
12299
|
-
|
|
12300
|
-
break;
|
|
12407
|
+
continue;
|
|
12301
12408
|
}
|
|
12302
|
-
|
|
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) {
|
|
12303
12414
|
results[ei] = {
|
|
12304
12415
|
success: false,
|
|
12305
|
-
|
|
12306
|
-
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.`
|
|
12307
12417
|
};
|
|
12308
|
-
|
|
12309
|
-
break;
|
|
12418
|
+
continue;
|
|
12310
12419
|
}
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
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;
|
|
12322
12431
|
}
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
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;
|
|
12328
12448
|
}
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
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;
|
|
12333
12463
|
}
|
|
12334
|
-
if (!
|
|
12335
|
-
results[ei] = {
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12464
|
+
if (!patchCreated) {
|
|
12465
|
+
results[ei] = { success: false, error: `\uD3B8\uC9D1 #${ei + 1}: set \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.` };
|
|
12466
|
+
continue;
|
|
12467
|
+
}
|
|
12468
|
+
results[ei] = { success: true, oldValue: currentRaw };
|
|
12469
|
+
}
|
|
12470
|
+
if (results.some((r) => !r.success)) {
|
|
12471
|
+
return { newXml: xml, results };
|
|
12472
|
+
}
|
|
12473
|
+
const sortedPatches = [...patches].sort((a, b) => b.from - a.from);
|
|
12474
|
+
let result = xml;
|
|
12475
|
+
for (const patch of sortedPatches) {
|
|
12476
|
+
result = result.slice(0, patch.from) + patch.text + result.slice(patch.to);
|
|
12477
|
+
}
|
|
12478
|
+
return { newXml: result, results };
|
|
12479
|
+
}
|
|
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 "";
|
|
12339
12536
|
}
|
|
12340
12537
|
}
|
|
12341
|
-
|
|
12342
|
-
|
|
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.`;
|
|
12343
12549
|
}
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
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
|
+
};
|
|
12348
12575
|
}
|
|
12349
|
-
|
|
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;
|
|
12350
12589
|
}
|
|
12351
|
-
async function
|
|
12352
|
-
const zip = await import_jszip2.default.loadAsync(hwpxBuffer);
|
|
12590
|
+
async function listFormObjectsFromZip(zip) {
|
|
12353
12591
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12354
12592
|
const sectionXmls = [];
|
|
12355
|
-
const
|
|
12593
|
+
const objects = [];
|
|
12594
|
+
let globalIndex = 0;
|
|
12356
12595
|
for (const sf of sectionFiles) {
|
|
12357
12596
|
const entry = zip.file(sf);
|
|
12358
12597
|
const xml = entry ? await entry.async("string") : "";
|
|
12359
12598
|
sectionXmls.push(xml);
|
|
12360
|
-
const
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
for (const tok of tokens) {
|
|
12364
|
-
if (tok.kind === "tbl_open") {
|
|
12365
|
-
if (depth === 0) count++;
|
|
12366
|
-
depth++;
|
|
12367
|
-
} else if (tok.kind === "tbl_close") {
|
|
12368
|
-
depth--;
|
|
12369
|
-
}
|
|
12370
|
-
}
|
|
12371
|
-
sectionTblCounts.push(count);
|
|
12372
|
-
}
|
|
12373
|
-
const sectionEdits = sectionFiles.map(() => []);
|
|
12374
|
-
let offset = 0;
|
|
12375
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
12376
|
-
const count = sectionTblCounts[si] ?? 0;
|
|
12377
|
-
for (let ei = 0; ei < edits.length; ei++) {
|
|
12378
|
-
const edit = edits[ei];
|
|
12379
|
-
if (edit.tableIndex >= offset && edit.tableIndex < offset + count) {
|
|
12380
|
-
const secEdits = sectionEdits[si];
|
|
12381
|
-
if (secEdits) {
|
|
12382
|
-
secEdits.push({
|
|
12383
|
-
tableIndex: edit.tableIndex - offset,
|
|
12384
|
-
// 섹션 내 상대 인덱스
|
|
12385
|
-
row: edit.row,
|
|
12386
|
-
col: edit.col,
|
|
12387
|
-
newText: edit.newText,
|
|
12388
|
-
expectedText: edit.expectedText,
|
|
12389
|
-
originalEditIdx: ei
|
|
12390
|
-
});
|
|
12391
|
-
}
|
|
12392
|
-
}
|
|
12393
|
-
}
|
|
12394
|
-
offset += count;
|
|
12395
|
-
}
|
|
12396
|
-
const totalTables = sectionTblCounts.reduce((a, b) => a + b, 0);
|
|
12397
|
-
const allResults = edits.map((edit, ei) => ({
|
|
12398
|
-
success: false,
|
|
12399
|
-
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.`
|
|
12400
|
-
}));
|
|
12401
|
-
const newSectionXmls = [...sectionXmls];
|
|
12402
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
12403
|
-
const sEdits = sectionEdits[si] ?? [];
|
|
12404
|
-
if (sEdits.length === 0) continue;
|
|
12405
|
-
const srcXml = sectionXmls[si] ?? "";
|
|
12406
|
-
const { newXml, results } = applyCellEditsToSectionXml(srcXml, sEdits);
|
|
12407
|
-
newSectionXmls[si] = newXml;
|
|
12408
|
-
for (let i = 0; i < sEdits.length; i++) {
|
|
12409
|
-
const sEdit = sEdits[i];
|
|
12410
|
-
const res = results[i];
|
|
12411
|
-
if (sEdit && res) {
|
|
12412
|
-
allResults[sEdit.originalEditIdx] = res;
|
|
12413
|
-
}
|
|
12414
|
-
}
|
|
12599
|
+
const parsed = parseFormObjects(xml, sf, globalIndex);
|
|
12600
|
+
objects.push(...parsed);
|
|
12601
|
+
globalIndex += parsed.length;
|
|
12415
12602
|
}
|
|
12416
|
-
|
|
12417
|
-
|
|
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.";
|
|
12418
12608
|
}
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
if (mimetypeEntry) {
|
|
12422
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
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}`;
|
|
12423
12611
|
}
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
const sectionIdx = sectionFiles.indexOf(name);
|
|
12427
|
-
if (sectionIdx >= 0) {
|
|
12428
|
-
out.file(name, newSectionXmls[sectionIdx] ?? "");
|
|
12429
|
-
} else {
|
|
12430
|
-
out.file(name, await entry.async("uint8array"));
|
|
12431
|
-
}
|
|
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.";
|
|
12432
12614
|
}
|
|
12433
|
-
|
|
12434
|
-
return { buffer: new Uint8Array(buf), results: allResults };
|
|
12615
|
+
return null;
|
|
12435
12616
|
}
|
|
12436
|
-
var
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
const
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
}
|
|
12515
|
-
}
|
|
12516
|
-
if (allMatches.length === 0) {
|
|
12517
|
-
const scope = scopedTableIndex !== void 0 ? `\uD45C ${scopedTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
12518
|
-
return {
|
|
12519
|
-
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.`
|
|
12520
|
-
};
|
|
12521
|
-
}
|
|
12522
|
-
if (allMatches.length > 1) {
|
|
12523
|
-
const locs = allMatches.map(
|
|
12524
|
-
(m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.addrSpan.rowAddr}, \uC5F4 ${m.addrSpan.colAddr})`
|
|
12525
|
-
).join(", ");
|
|
12526
|
-
return {
|
|
12527
|
-
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.`
|
|
12528
|
-
};
|
|
12529
|
-
}
|
|
12530
|
-
const match = allMatches[0];
|
|
12531
|
-
const { addrSpan, globalTableIndex, sectionXml, sectionOffset } = match;
|
|
12532
|
-
let targetRow;
|
|
12533
|
-
let targetCol;
|
|
12534
|
-
if (direction === "right") {
|
|
12535
|
-
targetRow = addrSpan.rowAddr;
|
|
12536
|
-
targetCol = addrSpan.colAddr + addrSpan.colSpan;
|
|
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)";
|
|
12537
12695
|
} else {
|
|
12538
|
-
|
|
12539
|
-
targetCol = addrSpan.colAddr;
|
|
12540
|
-
}
|
|
12541
|
-
const localIdx = globalTableIndex - sectionOffset;
|
|
12542
|
-
const tokens2 = tokenizeHwpxXml(sectionXml);
|
|
12543
|
-
const tblRange2 = findTopLevelTableRange(tokens2, localIdx);
|
|
12544
|
-
if (!tblRange2) {
|
|
12545
|
-
return { error: `\uD45C ${globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
12546
|
-
}
|
|
12547
|
-
const directTcs2 = collectDirectTcRanges(tokens2, tblRange2.start, tblRange2.end);
|
|
12548
|
-
const tblTokens2 = tokens2.filter((t) => t.pos >= tblRange2.start && t.pos < tblRange2.end);
|
|
12549
|
-
let targetExists = false;
|
|
12550
|
-
for (const tc of directTcs2) {
|
|
12551
|
-
const tcTokens = tblTokens2.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
12552
|
-
if (tcTokens.some(
|
|
12553
|
-
(t) => t.kind === "cell_addr" && t.colAddr === targetCol && t.rowAddr === targetRow
|
|
12554
|
-
)) {
|
|
12555
|
-
targetExists = true;
|
|
12556
|
-
break;
|
|
12557
|
-
}
|
|
12696
|
+
valueStr = `"${obj.currentValue}"`;
|
|
12558
12697
|
}
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
};
|
|
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}]`;
|
|
12564
12702
|
}
|
|
12565
|
-
|
|
12703
|
+
lines.push(line);
|
|
12704
|
+
}
|
|
12705
|
+
return lines.join("\n");
|
|
12706
|
+
}
|
|
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,
|
|
12712
|
+
requiresApproval: true,
|
|
12713
|
+
propose: async ({
|
|
12714
|
+
input,
|
|
12715
|
+
ctx
|
|
12716
|
+
}) => {
|
|
12717
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12718
|
+
const ext = extname5(safePath).toLowerCase();
|
|
12719
|
+
let originalBuffer;
|
|
12720
|
+
try {
|
|
12721
|
+
originalBuffer = await readFile6(safePath);
|
|
12722
|
+
} catch {
|
|
12723
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
12724
|
+
}
|
|
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.";
|
|
12566
12733
|
}
|
|
12734
|
+
const { objects, sectionFiles, sectionXmls } = await listFormObjectsFromZip(zip);
|
|
12567
12735
|
const resolvedEdits = [];
|
|
12568
12736
|
const resolveErrors = [];
|
|
12569
|
-
for (let
|
|
12570
|
-
const
|
|
12571
|
-
if (!
|
|
12572
|
-
|
|
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) {
|
|
12573
12742
|
resolveErrors.push(
|
|
12574
|
-
`\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.`
|
|
12575
12744
|
);
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
newText: e.newText,
|
|
12587
|
-
expectedText: e.expectedText,
|
|
12588
|
-
label: e.label
|
|
12589
|
-
});
|
|
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;
|
|
12590
12755
|
}
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
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;
|
|
12599
12764
|
} else {
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
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
|
+
}
|
|
12603
12772
|
}
|
|
12773
|
+
resolvedEdits.push({
|
|
12774
|
+
target,
|
|
12775
|
+
set: edit.set,
|
|
12776
|
+
expected: edit.expected,
|
|
12777
|
+
editIdx: ei
|
|
12778
|
+
});
|
|
12604
12779
|
}
|
|
12605
12780
|
if (resolveErrors.length > 0) {
|
|
12606
|
-
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.
|
|
12607
12782
|
${resolveErrors.join("\n")}`;
|
|
12608
12783
|
}
|
|
12609
|
-
const
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
editRequests
|
|
12619
|
-
);
|
|
12620
|
-
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
12621
|
-
if (failedResults.length > 0) {
|
|
12622
|
-
const messages = failedResults.map(({ r, i }) => {
|
|
12623
|
-
const e = resolvedEdits[i];
|
|
12624
|
-
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
12625
|
-
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
|
|
12626
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) {
|
|
12627
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.
|
|
12628
|
-
${
|
|
12822
|
+
${failMessages.join("\n")}`;
|
|
12629
12823
|
}
|
|
12630
|
-
const
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
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} |`);
|
|
12637
12858
|
}
|
|
12638
12859
|
const diff = diffLines.join("\n");
|
|
12639
12860
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
@@ -12642,7 +12863,7 @@ ${messages.join("\n")}`;
|
|
|
12642
12863
|
return {
|
|
12643
12864
|
proposal: {
|
|
12644
12865
|
id: proposalId,
|
|
12645
|
-
kind: "
|
|
12866
|
+
kind: "form-object",
|
|
12646
12867
|
targetPath: outputPath,
|
|
12647
12868
|
stagedPath,
|
|
12648
12869
|
summary: input.summary,
|
|
@@ -12659,6 +12880,92 @@ ${messages.join("\n")}`;
|
|
|
12659
12880
|
};
|
|
12660
12881
|
}
|
|
12661
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
|
+
};
|
|
12662
12969
|
var HEADING_LEVELS = {
|
|
12663
12970
|
1: HeadingLevel.HEADING_1,
|
|
12664
12971
|
2: HeadingLevel.HEADING_2,
|
|
@@ -12781,10 +13088,10 @@ async function markdownToDocx(markdown) {
|
|
|
12781
13088
|
});
|
|
12782
13089
|
return Packer.toBuffer(doc);
|
|
12783
13090
|
}
|
|
12784
|
-
var proposeEditSchema =
|
|
12785
|
-
path:
|
|
12786
|
-
newMarkdown:
|
|
12787
|
-
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)")
|
|
12788
13095
|
});
|
|
12789
13096
|
var proposeEditTool = {
|
|
12790
13097
|
name: "propose_edit",
|
|
@@ -12796,10 +13103,10 @@ var proposeEditTool = {
|
|
|
12796
13103
|
ctx
|
|
12797
13104
|
}) => {
|
|
12798
13105
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12799
|
-
const ext =
|
|
13106
|
+
const ext = extname7(safePath).toLowerCase();
|
|
12800
13107
|
let originalBuffer;
|
|
12801
13108
|
try {
|
|
12802
|
-
originalBuffer = await
|
|
13109
|
+
originalBuffer = await readFile7(safePath);
|
|
12803
13110
|
} catch {
|
|
12804
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.`;
|
|
12805
13112
|
}
|
|
@@ -12870,19 +13177,19 @@ ${diff}`;
|
|
|
12870
13177
|
};
|
|
12871
13178
|
}
|
|
12872
13179
|
};
|
|
12873
|
-
var proposeFindReplaceSchema =
|
|
12874
|
-
path:
|
|
12875
|
-
find:
|
|
12876
|
-
replace:
|
|
12877
|
-
caseSensitive:
|
|
12878
|
-
all:
|
|
12879
|
-
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)")
|
|
12880
13187
|
});
|
|
12881
13188
|
var MAX_DIFF_SAMPLES = 20;
|
|
12882
13189
|
function escapeXml3(text3) {
|
|
12883
13190
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12884
13191
|
}
|
|
12885
|
-
function
|
|
13192
|
+
function unescapeXml2(text3) {
|
|
12886
13193
|
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
12887
13194
|
}
|
|
12888
13195
|
function makeChangeSnippet(before, after, ctx = 24) {
|
|
@@ -12906,7 +13213,7 @@ function collectChangedSnippets(beforeXml, afterXml, maxSamples) {
|
|
|
12906
13213
|
const n = Math.min(beforeNodes.length, afterNodes.length);
|
|
12907
13214
|
for (let i = 0; i < n && out.length < maxSamples; i++) {
|
|
12908
13215
|
if (beforeNodes[i] !== afterNodes[i]) {
|
|
12909
|
-
const snip = makeChangeSnippet(
|
|
13216
|
+
const snip = makeChangeSnippet(unescapeXml2(beforeNodes[i]), unescapeXml2(afterNodes[i]));
|
|
12910
13217
|
out.push(snip);
|
|
12911
13218
|
}
|
|
12912
13219
|
}
|
|
@@ -12989,7 +13296,7 @@ function countOccurrences(str, sub) {
|
|
|
12989
13296
|
return count;
|
|
12990
13297
|
}
|
|
12991
13298
|
async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
|
|
12992
|
-
const zip = await
|
|
13299
|
+
const zip = await import_jszip4.default.loadAsync(hwpxBuffer);
|
|
12993
13300
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12994
13301
|
const sectionXmls = [];
|
|
12995
13302
|
for (const sf of sectionFiles) {
|
|
@@ -13033,7 +13340,7 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13033
13340
|
samples.push(snip);
|
|
13034
13341
|
}
|
|
13035
13342
|
}
|
|
13036
|
-
const out = new
|
|
13343
|
+
const out = new import_jszip4.default();
|
|
13037
13344
|
const mimetypeEntry = zip.file("mimetype");
|
|
13038
13345
|
if (mimetypeEntry) {
|
|
13039
13346
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -13064,7 +13371,7 @@ var proposeFindReplaceTool = {
|
|
|
13064
13371
|
ctx
|
|
13065
13372
|
}) => {
|
|
13066
13373
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13067
|
-
const ext =
|
|
13374
|
+
const ext = extname8(safePath).toLowerCase();
|
|
13068
13375
|
if (ext === ".hwp") {
|
|
13069
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.";
|
|
13070
13377
|
}
|
|
@@ -13073,7 +13380,7 @@ var proposeFindReplaceTool = {
|
|
|
13073
13380
|
}
|
|
13074
13381
|
let originalBuf;
|
|
13075
13382
|
try {
|
|
13076
|
-
originalBuf = await
|
|
13383
|
+
originalBuf = await readFile8(safePath);
|
|
13077
13384
|
} catch {
|
|
13078
13385
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13079
13386
|
}
|
|
@@ -13166,10 +13473,10 @@ var proposeFindReplaceTool = {
|
|
|
13166
13473
|
};
|
|
13167
13474
|
}
|
|
13168
13475
|
};
|
|
13169
|
-
var proposeFormFillSchema =
|
|
13170
|
-
path:
|
|
13171
|
-
fields:
|
|
13172
|
-
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)")
|
|
13173
13480
|
});
|
|
13174
13481
|
var proposeFormFillTool = {
|
|
13175
13482
|
name: "propose_form_fill",
|
|
@@ -13181,13 +13488,13 @@ var proposeFormFillTool = {
|
|
|
13181
13488
|
ctx
|
|
13182
13489
|
}) => {
|
|
13183
13490
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13184
|
-
const ext =
|
|
13491
|
+
const ext = extname9(safePath).toLowerCase();
|
|
13185
13492
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13186
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.`;
|
|
13187
13494
|
}
|
|
13188
13495
|
let originalBuffer;
|
|
13189
13496
|
try {
|
|
13190
|
-
originalBuffer = await
|
|
13497
|
+
originalBuffer = await readFile9(safePath);
|
|
13191
13498
|
} catch {
|
|
13192
13499
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13193
13500
|
}
|
|
@@ -13255,16 +13562,216 @@ var proposeFormFillTool = {
|
|
|
13255
13562
|
};
|
|
13256
13563
|
}
|
|
13257
13564
|
};
|
|
13258
|
-
var
|
|
13259
|
-
path:
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
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")
|
|
13265
13772
|
})
|
|
13266
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"),
|
|
13267
|
-
summary:
|
|
13774
|
+
summary: z11.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13268
13775
|
});
|
|
13269
13776
|
var proposeSheetEditTool = {
|
|
13270
13777
|
name: "propose_sheet_edit",
|
|
@@ -13276,13 +13783,13 @@ var proposeSheetEditTool = {
|
|
|
13276
13783
|
ctx
|
|
13277
13784
|
}) => {
|
|
13278
13785
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13279
|
-
const ext =
|
|
13786
|
+
const ext = extname11(safePath).toLowerCase();
|
|
13280
13787
|
if (ext !== ".xlsx" && ext !== ".xls") {
|
|
13281
13788
|
return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
|
|
13282
13789
|
}
|
|
13283
13790
|
let originalBuffer;
|
|
13284
13791
|
try {
|
|
13285
|
-
originalBuffer = await
|
|
13792
|
+
originalBuffer = await readFile11(safePath);
|
|
13286
13793
|
} catch {
|
|
13287
13794
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13288
13795
|
}
|
|
@@ -13375,47 +13882,47 @@ function detectStructuralLoss(beforeBlocks, afterBlocks) {
|
|
|
13375
13882
|
}
|
|
13376
13883
|
return { lost: false, detail: "" };
|
|
13377
13884
|
}
|
|
13378
|
-
var insertRowOpSchema =
|
|
13379
|
-
type:
|
|
13380
|
-
row:
|
|
13381
|
-
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")
|
|
13382
13889
|
});
|
|
13383
|
-
var deleteRowOpSchema =
|
|
13384
|
-
type:
|
|
13385
|
-
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)")
|
|
13386
13893
|
});
|
|
13387
|
-
var insertColumnOpSchema =
|
|
13388
|
-
type:
|
|
13389
|
-
col:
|
|
13390
|
-
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")
|
|
13391
13898
|
});
|
|
13392
|
-
var deleteColumnOpSchema =
|
|
13393
|
-
type:
|
|
13394
|
-
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)")
|
|
13395
13902
|
});
|
|
13396
|
-
var mergeCellsOpSchema =
|
|
13397
|
-
type:
|
|
13398
|
-
startRow:
|
|
13399
|
-
startCol:
|
|
13400
|
-
endRow:
|
|
13401
|
-
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)")
|
|
13402
13909
|
});
|
|
13403
|
-
var operationSchema =
|
|
13910
|
+
var operationSchema = z12.discriminatedUnion("type", [
|
|
13404
13911
|
insertRowOpSchema,
|
|
13405
13912
|
deleteRowOpSchema,
|
|
13406
13913
|
insertColumnOpSchema,
|
|
13407
13914
|
deleteColumnOpSchema,
|
|
13408
13915
|
mergeCellsOpSchema
|
|
13409
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.");
|
|
13410
|
-
var proposeTableStructureSchema =
|
|
13411
|
-
path:
|
|
13412
|
-
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(
|
|
13413
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."
|
|
13414
13921
|
),
|
|
13415
|
-
operations:
|
|
13922
|
+
operations: z12.array(operationSchema).min(1).describe(
|
|
13416
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."
|
|
13417
13924
|
),
|
|
13418
|
-
summary:
|
|
13925
|
+
summary: z12.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13419
13926
|
});
|
|
13420
13927
|
function tokenizeHwpxXml2(xml) {
|
|
13421
13928
|
const tokens = [];
|
|
@@ -13972,7 +14479,7 @@ function getTblDims(tblXml) {
|
|
|
13972
14479
|
return parseTblDimensions(tblXml, 0);
|
|
13973
14480
|
}
|
|
13974
14481
|
async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
13975
|
-
const zip = await
|
|
14482
|
+
const zip = await import_jszip6.default.loadAsync(hwpxBuffer);
|
|
13976
14483
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13977
14484
|
let targetSectionIdx = -1;
|
|
13978
14485
|
let targetRange = null;
|
|
@@ -14031,7 +14538,7 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14031
14538
|
const afterDims = getTblDims(tblBlock);
|
|
14032
14539
|
const newSectionXml = sectionXml.substring(0, targetRange.start) + tblBlock + sectionXml.substring(targetRange.end);
|
|
14033
14540
|
sectionXmls[targetSectionIdx] = newSectionXml;
|
|
14034
|
-
const out = new
|
|
14541
|
+
const out = new import_jszip6.default();
|
|
14035
14542
|
const mimetypeEntry = zip.file("mimetype");
|
|
14036
14543
|
if (mimetypeEntry) {
|
|
14037
14544
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -14055,7 +14562,7 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14055
14562
|
};
|
|
14056
14563
|
}
|
|
14057
14564
|
async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt) {
|
|
14058
|
-
const zip = await
|
|
14565
|
+
const zip = await import_jszip6.default.loadAsync(newBytes);
|
|
14059
14566
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
14060
14567
|
for (const sf of sectionFiles) {
|
|
14061
14568
|
const entry = zip.file(sf);
|
|
@@ -14090,7 +14597,7 @@ var proposeTableStructureTool = {
|
|
|
14090
14597
|
ctx
|
|
14091
14598
|
}) => {
|
|
14092
14599
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14093
|
-
const ext =
|
|
14600
|
+
const ext = extname12(safePath).toLowerCase();
|
|
14094
14601
|
if (ext === ".hwp") {
|
|
14095
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.";
|
|
14096
14603
|
}
|
|
@@ -14099,7 +14606,7 @@ var proposeTableStructureTool = {
|
|
|
14099
14606
|
}
|
|
14100
14607
|
let originalBuf;
|
|
14101
14608
|
try {
|
|
14102
|
-
originalBuf = await
|
|
14609
|
+
originalBuf = await readFile12(safePath);
|
|
14103
14610
|
} catch {
|
|
14104
14611
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
14105
14612
|
}
|
|
@@ -14295,11 +14802,11 @@ function searchExcerpts(markdown, query) {
|
|
|
14295
14802
|
}
|
|
14296
14803
|
return result;
|
|
14297
14804
|
}
|
|
14298
|
-
var readDocumentSchema =
|
|
14299
|
-
path:
|
|
14300
|
-
pages:
|
|
14301
|
-
outline:
|
|
14302
|
-
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)")
|
|
14303
14810
|
});
|
|
14304
14811
|
function applyReadMode(body, outline, search) {
|
|
14305
14812
|
if (outline === true) {
|
|
@@ -14328,11 +14835,11 @@ var readDocumentTool = {
|
|
|
14328
14835
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14329
14836
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
14330
14837
|
}
|
|
14331
|
-
const ext =
|
|
14838
|
+
const ext = extname13(safePath).toLowerCase();
|
|
14332
14839
|
if (PLAIN_TEXT_EXTS.has(ext)) {
|
|
14333
14840
|
let raw;
|
|
14334
14841
|
try {
|
|
14335
|
-
raw = await
|
|
14842
|
+
raw = await readFile13(safePath, "utf-8");
|
|
14336
14843
|
} catch (e) {
|
|
14337
14844
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14338
14845
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -14432,8 +14939,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
14432
14939
|
".csv",
|
|
14433
14940
|
".log"
|
|
14434
14941
|
]);
|
|
14435
|
-
var readFileSchema =
|
|
14436
|
-
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)")
|
|
14437
14944
|
});
|
|
14438
14945
|
var readFileTool = {
|
|
14439
14946
|
name: "read_file",
|
|
@@ -14457,8 +14964,8 @@ var readFileTool = {
|
|
|
14457
14964
|
if (info.size > MAX_SIZE) {
|
|
14458
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.`;
|
|
14459
14966
|
}
|
|
14460
|
-
const { extname:
|
|
14461
|
-
const ext =
|
|
14967
|
+
const { extname: extname17 } = await import("path");
|
|
14968
|
+
const ext = extname17(safePath).toLowerCase();
|
|
14462
14969
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
14463
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.`;
|
|
14464
14971
|
}
|
|
@@ -14472,10 +14979,64 @@ var readFileTool = {
|
|
|
14472
14979
|
return content;
|
|
14473
14980
|
}
|
|
14474
14981
|
};
|
|
14982
|
+
var PLAIN_TEXT_EXTS2 = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
|
|
14983
|
+
var scanPiiSchema = z15.object({
|
|
14984
|
+
path: z15.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uC810\uAC80\uD560 \uBB38\uC11C \uACBD\uB85C")
|
|
14985
|
+
});
|
|
14986
|
+
var scanPiiTool = {
|
|
14987
|
+
name: "scan_pii",
|
|
14988
|
+
description: "\uBB38\uC11C\uC5D0\uC11C \uD55C\uAD6D \uAC1C\uC778\uC815\uBCF4(\uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638\xB7\uC2E0\uC6A9\uCE74\uB4DC\uBC88\uD638\xB7\uC804\uD654\uBC88\uD638\xB7\uC774\uBA54\uC77C)\uB97C \uD0D0\uC9C0\uD569\uB2C8\uB2E4. \uC6D0\uBB38 \uAC12\uC740 \uBC18\uD658\uD558\uC9C0 \uC54A\uACE0 \uB9C8\uC2A4\uD0B9\uB41C \uC608\uC2DC\uB9CC \uD45C\uC2DC\uD569\uB2C8\uB2E4. \uC678\uBD80 \uACF5\uC720 \uC804 \uAC80\uD1A0\uC5D0 \uD65C\uC6A9\uD558\uC138\uC694.",
|
|
14989
|
+
inputSchema: scanPiiSchema,
|
|
14990
|
+
requiresApproval: false,
|
|
14991
|
+
execute: async ({
|
|
14992
|
+
input,
|
|
14993
|
+
ctx
|
|
14994
|
+
}) => {
|
|
14995
|
+
let safePath;
|
|
14996
|
+
try {
|
|
14997
|
+
safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14998
|
+
} catch (e) {
|
|
14999
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15000
|
+
return `\uC624\uB958: \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15001
|
+
}
|
|
15002
|
+
const ext = extname14(safePath).toLowerCase();
|
|
15003
|
+
let text3;
|
|
15004
|
+
if (PLAIN_TEXT_EXTS2.has(ext)) {
|
|
15005
|
+
try {
|
|
15006
|
+
text3 = await readFile14(safePath, "utf-8");
|
|
15007
|
+
} catch (e) {
|
|
15008
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15009
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15010
|
+
}
|
|
15011
|
+
} else {
|
|
15012
|
+
let parseResult;
|
|
15013
|
+
try {
|
|
15014
|
+
parseResult = await parse6(safePath);
|
|
15015
|
+
} catch (e) {
|
|
15016
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15017
|
+
return `\uC624\uB958: \uBB38\uC11C\uB97C \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15018
|
+
}
|
|
15019
|
+
if (!parseResult.success) {
|
|
15020
|
+
return `\uC624\uB958: \uBB38\uC11C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${parseResult.error ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`;
|
|
15021
|
+
}
|
|
15022
|
+
text3 = parseResult.markdown;
|
|
15023
|
+
}
|
|
15024
|
+
const findings = detectPii(text3);
|
|
15025
|
+
if (findings.length === 0) {
|
|
15026
|
+
return `\uAC1C\uC778\uC815\uBCF4\uAC00 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
15027
|
+
}
|
|
15028
|
+
const lines = [`\uBC1C\uACAC\uB41C \uAC1C\uC778\uC815\uBCF4 (${input.path}):`];
|
|
15029
|
+
for (const f of findings) {
|
|
15030
|
+
lines.push(`- ${f.type}: ${f.count}\uAC74 (\uC608: ${f.masked.join(", ")})`);
|
|
15031
|
+
}
|
|
15032
|
+
lines.push("\u203B \uB9C8\uC2A4\uD0B9\uB41C \uC608\uC2DC\uC774\uBA70 \uC6D0\uBB38 \uAC12\uC740 \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC678\uBD80 \uACF5\uC720 \uC804 \uD655\uC778\uD558\uC138\uC694.");
|
|
15033
|
+
return lines.join("\n");
|
|
15034
|
+
}
|
|
15035
|
+
};
|
|
14475
15036
|
var MAX_PREVIEW_CHARS = 1e4;
|
|
14476
|
-
var writeNewDocumentSchema =
|
|
14477
|
-
path:
|
|
14478
|
-
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)")
|
|
14479
15040
|
});
|
|
14480
15041
|
var writeNewDocumentTool = {
|
|
14481
15042
|
name: "write_new_document",
|
|
@@ -14487,7 +15048,7 @@ var writeNewDocumentTool = {
|
|
|
14487
15048
|
ctx
|
|
14488
15049
|
}) => {
|
|
14489
15050
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14490
|
-
const ext =
|
|
15051
|
+
const ext = extname15(safePath).toLowerCase();
|
|
14491
15052
|
try {
|
|
14492
15053
|
await stat6(safePath);
|
|
14493
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.`;
|
|
@@ -14537,12 +15098,12 @@ ${preview}`,
|
|
|
14537
15098
|
};
|
|
14538
15099
|
}
|
|
14539
15100
|
};
|
|
14540
|
-
var writeNewSpreadsheetSchema =
|
|
14541
|
-
path:
|
|
14542
|
-
sheets:
|
|
14543
|
-
|
|
14544
|
-
name:
|
|
14545
|
-
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)")
|
|
14546
15107
|
})
|
|
14547
15108
|
).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
|
|
14548
15109
|
});
|
|
@@ -14556,7 +15117,7 @@ var writeNewSpreadsheetTool = {
|
|
|
14556
15117
|
ctx
|
|
14557
15118
|
}) => {
|
|
14558
15119
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14559
|
-
const ext =
|
|
15120
|
+
const ext = extname16(safePath).toLowerCase();
|
|
14560
15121
|
if (ext !== ".xlsx") {
|
|
14561
15122
|
return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
|
|
14562
15123
|
}
|
|
@@ -14616,10 +15177,13 @@ function createDocTools(_ctx) {
|
|
|
14616
15177
|
listFilesTool,
|
|
14617
15178
|
listBackupsTool,
|
|
14618
15179
|
readFileTool,
|
|
15180
|
+
scanPiiTool,
|
|
15181
|
+
findInDocumentTool,
|
|
14619
15182
|
proposeEditTool,
|
|
14620
15183
|
proposeFormFillTool,
|
|
14621
15184
|
proposeCellEditTool,
|
|
14622
15185
|
proposeFindReplaceTool,
|
|
15186
|
+
proposeRedactPiiTool,
|
|
14623
15187
|
proposeSheetEditTool,
|
|
14624
15188
|
proposeTableStructureTool,
|
|
14625
15189
|
listFormObjectsTool,
|
|
@@ -15443,10 +16007,10 @@ async function runOnboarding() {
|
|
|
15443
16007
|
|
|
15444
16008
|
// src/update.ts
|
|
15445
16009
|
import { spawn } from "child_process";
|
|
15446
|
-
import { mkdir as mkdir4, readFile as
|
|
16010
|
+
import { mkdir as mkdir4, readFile as readFile15, writeFile as writeFile3 } from "fs/promises";
|
|
15447
16011
|
import { dirname as dirname3 } from "path";
|
|
15448
16012
|
function compareSemver(a, b) {
|
|
15449
|
-
const
|
|
16013
|
+
const parse7 = (v) => {
|
|
15450
16014
|
const clean = v.replace(/^v/, "").split("-")[0] ?? "";
|
|
15451
16015
|
const parts = clean.split(".").map((p) => {
|
|
15452
16016
|
const n = parseInt(p, 10);
|
|
@@ -15454,8 +16018,8 @@ function compareSemver(a, b) {
|
|
|
15454
16018
|
});
|
|
15455
16019
|
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
15456
16020
|
};
|
|
15457
|
-
const [aMaj, aMin, aPat] =
|
|
15458
|
-
const [bMaj, bMin, bPat] =
|
|
16021
|
+
const [aMaj, aMin, aPat] = parse7(a);
|
|
16022
|
+
const [bMaj, bMin, bPat] = parse7(b);
|
|
15459
16023
|
if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
|
|
15460
16024
|
if (aMin !== bMin) return aMin < bMin ? -1 : 1;
|
|
15461
16025
|
if (aPat !== bPat) return aPat < bPat ? -1 : 1;
|
|
@@ -15464,7 +16028,7 @@ function compareSemver(a, b) {
|
|
|
15464
16028
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
15465
16029
|
async function readCache(cachePath) {
|
|
15466
16030
|
try {
|
|
15467
|
-
const raw = await
|
|
16031
|
+
const raw = await readFile15(cachePath, "utf-8");
|
|
15468
16032
|
const parsed = JSON.parse(raw);
|
|
15469
16033
|
if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
|
|
15470
16034
|
return parsed;
|