@kodocagent/cli 0.6.2 → 0.7.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 +1351 -1115
- 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 JSZip8() {
|
|
9743
|
+
if (!(this instanceof JSZip8)) {
|
|
9744
|
+
return new JSZip8();
|
|
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 JSZip8();
|
|
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
|
+
JSZip8.prototype = require_object();
|
|
9763
|
+
JSZip8.prototype.loadAsync = require_load();
|
|
9764
|
+
JSZip8.support = require_support();
|
|
9765
|
+
JSZip8.defaults = require_defaults();
|
|
9766
|
+
JSZip8.version = "3.10.1";
|
|
9767
|
+
JSZip8.loadAsync = function(content, options) {
|
|
9768
|
+
return new JSZip8().loadAsync(content, options);
|
|
9769
9769
|
};
|
|
9770
|
-
|
|
9771
|
-
module.exports =
|
|
9770
|
+
JSZip8.external = require_external();
|
|
9771
|
+
module.exports = JSZip8;
|
|
9772
9772
|
}
|
|
9773
9773
|
});
|
|
9774
9774
|
|
|
@@ -9903,6 +9903,31 @@ function redactText(text3) {
|
|
|
9903
9903
|
}
|
|
9904
9904
|
return { text: result, findings };
|
|
9905
9905
|
}
|
|
9906
|
+
function redactRanges(text3) {
|
|
9907
|
+
if (!text3) return [];
|
|
9908
|
+
const ranges = [];
|
|
9909
|
+
for (const { type, re, mask } of PATTERNS) {
|
|
9910
|
+
for (const m of text3.matchAll(new RegExp(re.source, re.flags))) {
|
|
9911
|
+
if (m.index === void 0) continue;
|
|
9912
|
+
ranges.push({
|
|
9913
|
+
start: m.index,
|
|
9914
|
+
end: m.index + m[0].length,
|
|
9915
|
+
replacement: mask(m[0]),
|
|
9916
|
+
type
|
|
9917
|
+
});
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
ranges.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
9921
|
+
const out = [];
|
|
9922
|
+
let lastEnd = -1;
|
|
9923
|
+
for (const r of ranges) {
|
|
9924
|
+
if (r.start >= lastEnd) {
|
|
9925
|
+
out.push(r);
|
|
9926
|
+
lastEnd = r.end;
|
|
9927
|
+
}
|
|
9928
|
+
}
|
|
9929
|
+
return out;
|
|
9930
|
+
}
|
|
9906
9931
|
|
|
9907
9932
|
// ../core/dist/index.js
|
|
9908
9933
|
import { stepCountIs, streamText } from "ai";
|
|
@@ -9977,6 +10002,12 @@ var LAW_RULES_SECTION = `## \uBC95\uB839 \uADDC\uCE59
|
|
|
9977
10002
|
2. \uBC95\uB839 \uD604\uD589 \uC5EC\uBD80\uB294 MCP \uBC95\uB839 \uD234(\`mcp__korean-law__*\`)\uB85C \uD655\uC778\uD55C \uD6C4 \uC778\uC6A9\uD558\uC138\uC694.
|
|
9978
10003
|
3. MCP \uBC95\uB839 \uD234\uB85C \uD655\uC778\uD558\uC9C0 \uBABB\uD55C \uBC95\uB839 \uB0B4\uC6A9\uC740 \uBC18\uB4DC\uC2DC "\u203B \uD604\uD589 \uC5EC\uBD80\uB97C \uD655\uC778\uD558\uC9C0 \uC54A\uC740 \uB0B4\uC6A9\uC785\uB2C8\uB2E4"\uB77C\uACE0 \uBA85\uC2DC\uD558\uC138\uC694.
|
|
9979
10004
|
4. \uBC95\uB839 \uD574\uC11D\uC740 \uCC38\uACE0\uC6A9\uC774\uBA70 \uBC95\uC801 \uD6A8\uB825\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC911\uC694\uD55C \uC0AC\uD56D\uC740 \uC804\uBB38\uAC00\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.`;
|
|
10005
|
+
var SELF_VERIFY_PROMPT = `[\uC790\uB3D9 \uAC80\uC99D \uB2E8\uACC4] \uBC29\uAE08 \uBB38\uC11C\uB97C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4. \uC791\uC5C5\uC744 \uB9C8\uCE58\uAE30 \uC804\uC5D0 \uBC18\uB4DC\uC2DC \uB2E4\uC74C\uC744 \uC218\uD589\uD558\uC138\uC694.
|
|
10006
|
+
|
|
10007
|
+
1. \`read_document\`\uB85C \uBCC0\uACBD\uB41C \uBB38\uC11C(\uB610\uB294 \uAD00\uB828 \uBD80\uBD84)\uB97C **\uB2E4\uC2DC \uC77D\uC73C\uC138\uC694**.
|
|
10008
|
+
2. \uC0AC\uC6A9\uC790\uC758 **\uC6D0\uB798 \uC694\uCCAD**\uC744 \uD55C \uD56D\uBAA9\uC529 \uB300\uC870\uD558\uC138\uC694 \u2014 "\uBAA8\uB450/\uC804\uBD80/\uC77C\uAD04"\uC774\uBA74 \uBAA8\uB4E0 \uD56D\uBAA9\uC774 \uCC98\uB9AC\uB410\uB294\uC9C0, \uAC12\xB7\uD615\uC2DD\xB7\uB300\uC0C1 \uC704\uCE58\uAC00 \uC815\uD655\uD55C\uC9C0, \uBE60\uC9C0\uAC70\uB098 \uC798\uBABB \uBC18\uC601\uB41C \uBD80\uBD84\uC774 \uC5C6\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.
|
|
10009
|
+
3. \uB204\uB77D\xB7\uC624\uB958\uAC00 \uC788\uC73C\uBA74 \uC989\uC2DC \`propose_*\` \uB3C4\uAD6C\uB85C **\uCD94\uAC00 \uC218\uC815\uC548\uC744 \uC81C\uC548**\uD558\uC138\uC694. \uB2E8, \uC0AC\uC6A9\uC790\uAC00 \uC774\uBBF8 \uAC70\uC808\uD55C \uC81C\uC548\uC740 \uB2E4\uC2DC \uC62C\uB9AC\uC9C0 \uB9C8\uC138\uC694.
|
|
10010
|
+
4. \uBAA8\uB4E0 \uD56D\uBAA9\uC774 \uC815\uD655\uD788 \uBC18\uC601\uB410\uC73C\uBA74 \uCD94\uAC00 \uB3C4\uAD6C \uD638\uCD9C \uC5C6\uC774 \`\uAC80\uC99D \uC644\uB8CC: <\uD55C \uC904 \uC694\uC57D>\` \uD615\uC2DD\uC73C\uB85C\uB9CC \uB2F5\uD558\uC138\uC694.`;
|
|
9980
10011
|
function buildDynamicContext(ctx) {
|
|
9981
10012
|
const lines = ["## \uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8"];
|
|
9982
10013
|
lines.push(`- **\uC791\uC5C5 \uB514\uB809\uD130\uB9AC**: \`${ctx.cwd}\``);
|
|
@@ -10077,6 +10108,20 @@ function compactMessages(messages, maxTokens) {
|
|
|
10077
10108
|
}
|
|
10078
10109
|
return result;
|
|
10079
10110
|
}
|
|
10111
|
+
var EDITING_TOOLS = /* @__PURE__ */ new Set([
|
|
10112
|
+
"propose_edit",
|
|
10113
|
+
"propose_find_replace",
|
|
10114
|
+
"propose_cell_edit",
|
|
10115
|
+
"propose_redact_pii",
|
|
10116
|
+
"propose_form_fill",
|
|
10117
|
+
"propose_table_structure",
|
|
10118
|
+
"propose_form_object",
|
|
10119
|
+
"propose_sheet_edit",
|
|
10120
|
+
"write_new_document",
|
|
10121
|
+
"write_new_spreadsheet",
|
|
10122
|
+
"restore_backup"
|
|
10123
|
+
]);
|
|
10124
|
+
var MAX_SELF_VERIFY_ROUNDS = 1;
|
|
10080
10125
|
var AgentSession = class {
|
|
10081
10126
|
constructor(opts) {
|
|
10082
10127
|
this.opts = opts;
|
|
@@ -10136,110 +10181,129 @@ var AgentSession = class {
|
|
|
10136
10181
|
await store.appendUser(userMessage);
|
|
10137
10182
|
const userMsg = { role: "user", content: userMessage };
|
|
10138
10183
|
this.messages.push(userMsg);
|
|
10139
|
-
const system = buildSystemPrompt({
|
|
10140
|
-
cwd: this.opts.cwd,
|
|
10141
|
-
mcpServers: this.opts.mcpServers ?? [],
|
|
10142
|
-
openDocuments: this.openDocuments,
|
|
10143
|
-
toolNames: tools.toolNames
|
|
10144
|
-
});
|
|
10145
10184
|
const aiSdkTools = tools.toAiSdkTools();
|
|
10146
|
-
|
|
10185
|
+
const selfVerifyEnabled = process.env.KODOC_SELF_VERIFY !== "0";
|
|
10186
|
+
let verifyRounds = 0;
|
|
10187
|
+
let totalInputTokens = 0;
|
|
10188
|
+
let totalOutputTokens = 0;
|
|
10189
|
+
let sawFinish = false;
|
|
10147
10190
|
try {
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
case "text-delta": {
|
|
10168
|
-
yield { type: "text-delta", text: part.text };
|
|
10169
|
-
break;
|
|
10191
|
+
while (true) {
|
|
10192
|
+
const system = buildSystemPrompt({
|
|
10193
|
+
cwd: this.opts.cwd,
|
|
10194
|
+
mcpServers: this.opts.mcpServers ?? [],
|
|
10195
|
+
openDocuments: this.openDocuments,
|
|
10196
|
+
toolNames: tools.toolNames
|
|
10197
|
+
});
|
|
10198
|
+
this.messages = compactMessages(this.messages, config.maxContextTokens);
|
|
10199
|
+
let editedThisRound = false;
|
|
10200
|
+
const result = streamText({
|
|
10201
|
+
model,
|
|
10202
|
+
system,
|
|
10203
|
+
messages: this.messages,
|
|
10204
|
+
tools: aiSdkTools,
|
|
10205
|
+
// 멀티스텝 정지 조건: maxSteps번 툴콜 후 중단 (AI SDK v6 실제 API)
|
|
10206
|
+
stopWhen: stepCountIs(config.maxSteps),
|
|
10207
|
+
abortSignal: signal,
|
|
10208
|
+
// 샘플링 파라미터 미설정 (SPEC §3, §5 불변 원칙)
|
|
10209
|
+
onError: () => {
|
|
10170
10210
|
}
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10211
|
+
});
|
|
10212
|
+
for await (const part of result.fullStream) {
|
|
10213
|
+
if (signal.aborted) break;
|
|
10214
|
+
while (this.pendingApprovalEvents.length > 0) {
|
|
10215
|
+
const proposal = this.pendingApprovalEvents.shift();
|
|
10216
|
+
yield { type: "approval-required", proposal };
|
|
10217
|
+
}
|
|
10218
|
+
switch (part.type) {
|
|
10219
|
+
case "text-delta": {
|
|
10220
|
+
yield { type: "text-delta", text: part.text };
|
|
10221
|
+
break;
|
|
10222
|
+
}
|
|
10223
|
+
case "tool-call": {
|
|
10224
|
+
yield {
|
|
10225
|
+
type: "tool-call",
|
|
10226
|
+
toolName: part.toolName,
|
|
10227
|
+
args: part.input,
|
|
10228
|
+
callId: part.toolCallId
|
|
10229
|
+
};
|
|
10230
|
+
if (EDITING_TOOLS.has(part.toolName)) editedThisRound = true;
|
|
10231
|
+
try {
|
|
10232
|
+
const inp = part.input;
|
|
10233
|
+
if (part.toolName === "read_document") {
|
|
10234
|
+
this.recordOpenDocument(inp.path);
|
|
10235
|
+
} else if (part.toolName === "compare_documents") {
|
|
10236
|
+
this.recordOpenDocument(inp.pathA);
|
|
10237
|
+
this.recordOpenDocument(inp.pathB);
|
|
10238
|
+
} else if (part.toolName === "write_new_document" || part.toolName === "write_new_spreadsheet") {
|
|
10239
|
+
this.recordOpenDocument(inp.path);
|
|
10240
|
+
}
|
|
10241
|
+
} catch {
|
|
10187
10242
|
}
|
|
10188
|
-
|
|
10243
|
+
break;
|
|
10189
10244
|
}
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
isError
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10245
|
+
case "tool-result": {
|
|
10246
|
+
const isError = false;
|
|
10247
|
+
yield {
|
|
10248
|
+
type: "tool-result",
|
|
10249
|
+
callId: part.toolCallId,
|
|
10250
|
+
result: part.output,
|
|
10251
|
+
isError
|
|
10252
|
+
};
|
|
10253
|
+
await store.appendToolResult(part.toolCallId, part.output, isError);
|
|
10254
|
+
break;
|
|
10255
|
+
}
|
|
10256
|
+
case "tool-error": {
|
|
10257
|
+
yield {
|
|
10258
|
+
type: "tool-result",
|
|
10259
|
+
callId: part.toolCallId,
|
|
10260
|
+
result: String(part.error),
|
|
10261
|
+
isError: true
|
|
10262
|
+
};
|
|
10263
|
+
await store.appendToolResult(part.toolCallId, String(part.error), true);
|
|
10264
|
+
break;
|
|
10265
|
+
}
|
|
10266
|
+
case "finish": {
|
|
10267
|
+
const usage = part.totalUsage;
|
|
10268
|
+
if (usage) {
|
|
10269
|
+
totalInputTokens += usage.inputTokens ?? 0;
|
|
10270
|
+
totalOutputTokens += usage.outputTokens ?? 0;
|
|
10271
|
+
}
|
|
10272
|
+
sawFinish = true;
|
|
10273
|
+
break;
|
|
10274
|
+
}
|
|
10275
|
+
case "error": {
|
|
10276
|
+
const errPart = part;
|
|
10277
|
+
const message = errPart.error instanceof Error ? errPart.error.message : String(errPart.error);
|
|
10278
|
+
yield { type: "error", message, recoverable: false };
|
|
10279
|
+
break;
|
|
10280
|
+
}
|
|
10281
|
+
default:
|
|
10282
|
+
break;
|
|
10223
10283
|
}
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10284
|
+
}
|
|
10285
|
+
try {
|
|
10286
|
+
const response = await result.response;
|
|
10287
|
+
for (const msg of response.messages) {
|
|
10288
|
+
const modelMsg = msg;
|
|
10289
|
+
this.messages.push(modelMsg);
|
|
10290
|
+
await store.appendAssistant(modelMsg);
|
|
10229
10291
|
}
|
|
10230
|
-
|
|
10231
|
-
break;
|
|
10292
|
+
} catch {
|
|
10232
10293
|
}
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
await store.appendAssistant(modelMsg);
|
|
10294
|
+
if (selfVerifyEnabled && editedThisRound && verifyRounds < MAX_SELF_VERIFY_ROUNDS && !signal.aborted) {
|
|
10295
|
+
verifyRounds++;
|
|
10296
|
+
const verifyMsg = { role: "user", content: SELF_VERIFY_PROMPT };
|
|
10297
|
+
this.messages.push(verifyMsg);
|
|
10298
|
+
await store.appendUser(SELF_VERIFY_PROMPT);
|
|
10299
|
+
continue;
|
|
10240
10300
|
}
|
|
10241
|
-
|
|
10301
|
+
break;
|
|
10242
10302
|
}
|
|
10303
|
+
yield {
|
|
10304
|
+
type: "turn-complete",
|
|
10305
|
+
usage: sawFinish ? { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } : void 0
|
|
10306
|
+
};
|
|
10243
10307
|
} catch (err) {
|
|
10244
10308
|
if (signal.aborted) {
|
|
10245
10309
|
return;
|
|
@@ -10934,25 +10998,30 @@ var ToolRegistry = class {
|
|
|
10934
10998
|
// ../doc-tools/dist/index.js
|
|
10935
10999
|
import { realpath } from "fs/promises";
|
|
10936
11000
|
import { basename, dirname as dirname2, isAbsolute, join as join3, normalize, relative, resolve } from "path";
|
|
11001
|
+
import { isOldHwpFile, isZipFile } from "kordoc";
|
|
10937
11002
|
import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile3, rename, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
10938
11003
|
import { basename as basename2, dirname as dirname22, extname, join as join22 } from "path";
|
|
10939
11004
|
import { createTwoFilesPatch } from "diff";
|
|
10940
11005
|
import { readdir as readdir22, readFile as readFile22, stat as stat22 } from "fs/promises";
|
|
10941
11006
|
import { basename as basename3, extname as extname2, join as join32 } from "path";
|
|
10942
|
-
var import_jszip = __toESM(require_lib3(), 1);
|
|
10943
|
-
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
10944
|
-
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
10945
|
-
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10946
11007
|
import { z as z3 } from "zod";
|
|
10947
11008
|
import { readFile as readFile32 } from "fs/promises";
|
|
10948
11009
|
import { blocksToMarkdown, compare } from "kordoc";
|
|
10949
11010
|
import { z as z22 } from "zod";
|
|
11011
|
+
import { extname as extname3 } from "path";
|
|
11012
|
+
var import_jszip = __toESM(require_lib3(), 1);
|
|
11013
|
+
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
11014
|
+
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
11015
|
+
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
11016
|
+
var import_jszip5 = __toESM(require_lib3(), 1);
|
|
11017
|
+
import { markdownToPdf, renderHtml } from "kordoc";
|
|
11018
|
+
import { z as z32 } from "zod";
|
|
11019
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
11020
|
+
import { isZipFile as isZipFile2, parse as kordocParse } from "kordoc";
|
|
10950
11021
|
import { readFile as readFile5 } from "fs/promises";
|
|
10951
11022
|
import { extname as extname4 } from "path";
|
|
11023
|
+
import { scanSectionXml } from "kordoc";
|
|
10952
11024
|
import { z as z4 } from "zod";
|
|
10953
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
10954
|
-
import { extname as extname3 } from "path";
|
|
10955
|
-
import { z as z32 } from "zod";
|
|
10956
11025
|
import { readFile as readFile6 } from "fs/promises";
|
|
10957
11026
|
import { extname as extname5 } from "path";
|
|
10958
11027
|
import { z as z5 } from "zod";
|
|
@@ -10961,8 +11030,12 @@ import { extname as extname6, join as join4, relative as relative2 } from "path"
|
|
|
10961
11030
|
import { z as z6 } from "zod";
|
|
10962
11031
|
import { readFile as readFile7 } from "fs/promises";
|
|
10963
11032
|
import { extname as extname7 } from "path";
|
|
10964
|
-
import {
|
|
11033
|
+
import { applySplices, buildParagraphSplices, scanSectionXml as scanSectionXml2 } from "kordoc";
|
|
10965
11034
|
import { z as z7 } from "zod";
|
|
11035
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
11036
|
+
import { extname as extname8 } from "path";
|
|
11037
|
+
import { compare as compare2, patchHwp, patchHwpx } from "kordoc";
|
|
11038
|
+
import { z as z8 } from "zod";
|
|
10966
11039
|
import {
|
|
10967
11040
|
Document,
|
|
10968
11041
|
HeadingLevel,
|
|
@@ -10974,57 +11047,60 @@ import {
|
|
|
10974
11047
|
TextRun,
|
|
10975
11048
|
WidthType
|
|
10976
11049
|
} from "docx";
|
|
10977
|
-
import { readFile as readFile8 } from "fs/promises";
|
|
10978
|
-
import { extname as extname8 } from "path";
|
|
10979
|
-
import { parse as parse2 } from "kordoc";
|
|
10980
|
-
import { z as z8 } from "zod";
|
|
10981
11050
|
import { readFile as readFile9 } from "fs/promises";
|
|
10982
11051
|
import { extname as extname9 } from "path";
|
|
10983
|
-
import {
|
|
11052
|
+
import { applySplices as applySplices3, buildRangeSplices as buildRangeSplices2, scanSectionXml as scanSectionXml4 } from "kordoc";
|
|
10984
11053
|
import { z as z9 } from "zod";
|
|
11054
|
+
import { applySplices as applySplices2, buildRangeSplices, scanSectionXml as scanSectionXml3 } from "kordoc";
|
|
10985
11055
|
import { readFile as readFile10 } from "fs/promises";
|
|
10986
|
-
import {
|
|
10987
|
-
|
|
10988
|
-
var import_jszip6 = __toESM(require_lib3(), 1);
|
|
11056
|
+
import { extname as extname10 } from "path";
|
|
11057
|
+
import { extractFormSchema, fillHwpx } from "kordoc";
|
|
10989
11058
|
import { z as z10 } from "zod";
|
|
10990
11059
|
import { readFile as readFile11 } from "fs/promises";
|
|
10991
|
-
import { extname as extname11 } from "path";
|
|
10992
|
-
|
|
11060
|
+
import { basename as basename4, extname as extname11 } from "path";
|
|
11061
|
+
var import_jszip6 = __toESM(require_lib3(), 1);
|
|
11062
|
+
var import_jszip7 = __toESM(require_lib3(), 1);
|
|
11063
|
+
import { scanSectionXml as scanSectionXml5 } from "kordoc";
|
|
10993
11064
|
import { z as z11 } from "zod";
|
|
10994
11065
|
import { readFile as readFile12 } from "fs/promises";
|
|
10995
11066
|
import { extname as extname12 } from "path";
|
|
10996
|
-
import
|
|
11067
|
+
import ExcelJS from "exceljs";
|
|
10997
11068
|
import { z as z12 } from "zod";
|
|
10998
|
-
import { readFile as readFile13
|
|
11069
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
10999
11070
|
import { extname as extname13 } from "path";
|
|
11000
|
-
import { parse as parse5 } from "kordoc";
|
|
11001
11071
|
import { z as z13 } from "zod";
|
|
11002
|
-
import { readFile as
|
|
11003
|
-
import { z as z14 } from "zod";
|
|
11004
|
-
import { readFile as readFile14 } from "fs/promises";
|
|
11072
|
+
import { readFile as readFile14, stat as stat4 } from "fs/promises";
|
|
11005
11073
|
import { extname as extname14 } from "path";
|
|
11006
|
-
import {
|
|
11074
|
+
import { z as z14 } from "zod";
|
|
11075
|
+
import { readFile as fsReadFile, stat as stat5 } from "fs/promises";
|
|
11007
11076
|
import { z as z15 } from "zod";
|
|
11008
|
-
import {
|
|
11077
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
11009
11078
|
import { extname as extname15 } from "path";
|
|
11010
|
-
import { markdownToHwpx } from "kordoc";
|
|
11011
11079
|
import { z as z16 } from "zod";
|
|
11012
|
-
import { stat as
|
|
11080
|
+
import { stat as stat6 } from "fs/promises";
|
|
11013
11081
|
import { extname as extname16 } from "path";
|
|
11014
|
-
import
|
|
11082
|
+
import { markdownToHwpx } from "kordoc";
|
|
11015
11083
|
import { z as z17 } from "zod";
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11084
|
+
import { stat as stat7 } from "fs/promises";
|
|
11085
|
+
import { extname as extname17 } from "path";
|
|
11086
|
+
import ExcelJS2 from "exceljs";
|
|
11087
|
+
import { z as z18 } from "zod";
|
|
11088
|
+
function headerBuffer(bytes, n = 64) {
|
|
11089
|
+
return new Uint8Array(bytes.subarray(0, Math.min(n, bytes.length))).buffer;
|
|
11090
|
+
}
|
|
11019
11091
|
function isOle2Binary(bytes) {
|
|
11020
|
-
if (bytes.length <
|
|
11021
|
-
return
|
|
11092
|
+
if (bytes.length < 8) return false;
|
|
11093
|
+
return isOldHwpFile(headerBuffer(bytes));
|
|
11094
|
+
}
|
|
11095
|
+
function isZipBinary(bytes) {
|
|
11096
|
+
if (bytes.length < 4) return false;
|
|
11097
|
+
return isZipFile(headerBuffer(bytes));
|
|
11022
11098
|
}
|
|
11023
11099
|
function hwpStructuralGuard(ext, bytes) {
|
|
11024
11100
|
const isHwpExt = ext === ".hwp";
|
|
11025
11101
|
const isOle2 = isOle2Binary(bytes);
|
|
11026
11102
|
if (!isHwpExt && !isOle2) return null;
|
|
11027
|
-
if (!isHwpExt && bytes
|
|
11103
|
+
if (!isHwpExt && isZipBinary(bytes)) return null;
|
|
11028
11104
|
return "\uC774 \uC791\uC5C5(\uD45C\xB7\uC140\xB7\uC591\uC2DD\xB7\uCC3E\uAE30\uBC14\uAFB8\uAE30 \uB4F1 \uAD6C\uC870 \uD3B8\uC9D1)\uC740 `.hwpx` \uBB38\uC11C\uC5D0\uC11C \uC9C0\uC6D0\uB429\uB2C8\uB2E4. `.hwp`\uB294 OLE \uBC14\uC774\uB108\uB9AC\uB77C \uAD6C\uC870\uB97C \uBB34\uC190\uC2E4\uB85C \uD328\uCE58\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD574\uACB0: (1) \uBCF8\uBB38 \uD14D\uC2A4\uD2B8\uB9CC \uACE0\uCE58\uB824\uBA74 `propose_edit`\uC744 \uC0AC\uC6A9\uD558\uC138\uC694(.hwp \uC81C\uC790\uB9AC \uD3B8\uC9D1). (2) \uD45C\xB7\uC140\xB7\uC591\uC2DD \uD3B8\uC9D1\uC774 \uD544\uC694\uD558\uBA74 \uD55C\uAE00\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 HWPX(.hwpx)'\uB85C \uBCC0\uD658\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
11029
11105
|
}
|
|
11030
11106
|
async function resolveSafePath(cwd, p) {
|
|
@@ -11526,577 +11602,272 @@ var compareDocumentsTool = {
|
|
|
11526
11602
|
return output;
|
|
11527
11603
|
}
|
|
11528
11604
|
};
|
|
11529
|
-
var
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
"\
|
|
11605
|
+
var SHAPE_ALT_KEYWORDS = [
|
|
11606
|
+
"\uC0AC\uAC01\uD615",
|
|
11607
|
+
"\uC9C1\uC0AC\uAC01\uD615",
|
|
11608
|
+
"\uC815\uC0AC\uAC01\uD615",
|
|
11609
|
+
"\uC6D0",
|
|
11610
|
+
"\uD0C0\uC6D0",
|
|
11611
|
+
"\uC0BC\uAC01\uD615",
|
|
11612
|
+
"\uC774\uB4F1\uBCC0 \uC0BC\uAC01\uD615",
|
|
11613
|
+
"\uC9C1\uAC01 \uC0BC\uAC01\uD615",
|
|
11614
|
+
"\uC120",
|
|
11615
|
+
"\uC9C1\uC120",
|
|
11616
|
+
"\uACE1\uC120",
|
|
11617
|
+
"\uD654\uC0B4\uD45C",
|
|
11618
|
+
"\uAD75\uC740 \uD654\uC0B4\uD45C",
|
|
11619
|
+
"\uC774\uC911 \uD654\uC0B4\uD45C",
|
|
11620
|
+
"\uC624\uAC01\uD615",
|
|
11621
|
+
"\uC721\uAC01\uD615",
|
|
11622
|
+
"\uD314\uAC01\uD615",
|
|
11623
|
+
"\uBCC4",
|
|
11624
|
+
"[4-8]\uC810\uBCC4",
|
|
11625
|
+
"\uC2ED\uC790",
|
|
11626
|
+
"\uC2ED\uC790\uD615",
|
|
11627
|
+
"\uAD6C\uB984",
|
|
11628
|
+
"\uAD6C\uB984\uD615",
|
|
11629
|
+
"\uB9C8\uB984\uBAA8",
|
|
11630
|
+
"\uB3C4\uB11B",
|
|
11631
|
+
"\uD3C9\uD589\uC0AC\uBCC0\uD615",
|
|
11632
|
+
"\uC0AC\uB2E4\uB9AC\uAF34",
|
|
11633
|
+
"\uBD80\uCC44\uAF34",
|
|
11634
|
+
"\uD638",
|
|
11635
|
+
"\uBC18\uC6D0",
|
|
11636
|
+
"\uBB3C\uACB0",
|
|
11637
|
+
"\uBC88\uAC1C",
|
|
11638
|
+
"\uD558\uD2B8",
|
|
11639
|
+
"\uBE57\uAE08",
|
|
11640
|
+
"\uBE14\uB85D \uD654\uC0B4\uD45C",
|
|
11641
|
+
"\uC218\uC2DD",
|
|
11642
|
+
"\uD45C",
|
|
11643
|
+
"\uADF8\uB9BC",
|
|
11644
|
+
"\uAC1C\uCCB4",
|
|
11645
|
+
"\uADF8\uB9AC\uAE30\\s?\uAC1C\uCCB4",
|
|
11646
|
+
"\uBB36\uC74C\\s?\uAC1C\uCCB4",
|
|
11647
|
+
"\uAE00\uC0C1\uC790",
|
|
11648
|
+
"\uC218\uC2DD\\s?\uAC1C\uCCB4",
|
|
11649
|
+
"OLE\\s?\uAC1C\uCCB4"
|
|
11650
|
+
].join("|");
|
|
11651
|
+
var BUGGY_SHAPE_STRIP = new RegExp(
|
|
11652
|
+
`(?:\uBAA8\uC11C\uB9AC\uAC00 \uB465\uADFC |\uB465\uADFC )?(?:${SHAPE_ALT_KEYWORDS})\\s?\uC785\uB2C8\uB2E4\\.?`,
|
|
11653
|
+
"g"
|
|
11545
11654
|
);
|
|
11546
|
-
var
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
function tokenizeHwpxXml(xml) {
|
|
11554
|
-
const tokens = [];
|
|
11555
|
-
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*/g;
|
|
11556
|
-
let startPos = 0;
|
|
11557
|
-
let m = re.exec(xml);
|
|
11558
|
-
while (m !== null) {
|
|
11559
|
-
const raw = m[0];
|
|
11560
|
-
const pos = m.index;
|
|
11561
|
-
if (raw.startsWith("<hp:tbl")) {
|
|
11562
|
-
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
11563
|
-
} else if (raw === "</hp:tbl>") {
|
|
11564
|
-
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
11565
|
-
} else if (raw.startsWith("<hp:tc")) {
|
|
11566
|
-
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
11567
|
-
} else if (raw === "</hp:tc>") {
|
|
11568
|
-
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
11569
|
-
} else if (raw === "<hp:t/>") {
|
|
11570
|
-
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
11571
|
-
} else if (raw === "<hp:t>") {
|
|
11572
|
-
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
11573
|
-
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
11574
|
-
const colM = raw.match(/colAddr="(\d+)"/);
|
|
11575
|
-
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
11576
|
-
if (colM && rowM) {
|
|
11577
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
11578
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11579
|
-
tokens.push({
|
|
11580
|
-
kind: "cell_addr",
|
|
11581
|
-
pos,
|
|
11582
|
-
end,
|
|
11583
|
-
colAddr: Number(colM[1]),
|
|
11584
|
-
rowAddr: Number(rowM[1])
|
|
11585
|
-
});
|
|
11586
|
-
}
|
|
11587
|
-
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
11588
|
-
const colM = raw.match(/colSpan="(\d+)"/);
|
|
11589
|
-
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
11590
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
11591
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11592
|
-
tokens.push({
|
|
11593
|
-
kind: "cell_span",
|
|
11594
|
-
pos,
|
|
11595
|
-
end,
|
|
11596
|
-
colSpan: colM ? Number(colM[1]) : 1,
|
|
11597
|
-
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
11598
|
-
});
|
|
11599
|
-
}
|
|
11600
|
-
startPos = re.lastIndex;
|
|
11601
|
-
m = re.exec(xml);
|
|
11602
|
-
}
|
|
11603
|
-
void startPos;
|
|
11604
|
-
return tokens;
|
|
11655
|
+
var FIXED_SHAPE_STRIP = new RegExp(
|
|
11656
|
+
`(?<![\uAC00-\uD7A3])(?:\uBAA8\uC11C\uB9AC\uAC00 \uB465\uADFC |\uB465\uADFC )?(?:${SHAPE_ALT_KEYWORDS})\\s?\uC785\uB2C8\uB2E4\\.?`,
|
|
11657
|
+
"g"
|
|
11658
|
+
);
|
|
11659
|
+
function applyStrip(raw, re) {
|
|
11660
|
+
const normalized = raw.replace(/[ \t]+/g, " ").trim();
|
|
11661
|
+
return normalized.replace(re, "").trim();
|
|
11605
11662
|
}
|
|
11606
|
-
function
|
|
11607
|
-
return
|
|
11663
|
+
function decodeXmlEntities(s) {
|
|
11664
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(Number.parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number.parseInt(d, 10))).replace(/&/g, "&");
|
|
11608
11665
|
}
|
|
11609
|
-
function
|
|
11610
|
-
const
|
|
11611
|
-
const
|
|
11612
|
-
const
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
if (
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
} else if (tok.kind === "tc_close") {
|
|
11622
|
-
const entry = tcStack.pop();
|
|
11623
|
-
if (entry !== void 0) {
|
|
11624
|
-
tcRanges.push({ start: entry.pos, end: tok.end, depth: entry.depth });
|
|
11666
|
+
async function extractHwpxParagraphTexts(bytes) {
|
|
11667
|
+
const zip = await import_jszip.default.loadAsync(bytes);
|
|
11668
|
+
const out = [];
|
|
11669
|
+
for (const name of Object.keys(zip.files)) {
|
|
11670
|
+
if (!/^Contents\/section\d*\.xml$/i.test(name)) continue;
|
|
11671
|
+
const entry = zip.files[name];
|
|
11672
|
+
if (!entry) continue;
|
|
11673
|
+
const xml = await entry.async("string");
|
|
11674
|
+
for (const seg of xml.split(/(?=<hp:p\b)/)) {
|
|
11675
|
+
let para = "";
|
|
11676
|
+
for (const m of seg.matchAll(/<hp:t(?:\s[^>]*)?>([\s\S]*?)<\/hp:t>/g)) {
|
|
11677
|
+
para += decodeXmlEntities((m[1] ?? "").replace(/<[^>]+>/g, ""));
|
|
11625
11678
|
}
|
|
11679
|
+
if (para.trim()) out.push(para);
|
|
11626
11680
|
}
|
|
11627
11681
|
}
|
|
11628
|
-
return
|
|
11682
|
+
return out;
|
|
11629
11683
|
}
|
|
11630
|
-
function
|
|
11631
|
-
const
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
for (const t of tcTokens) {
|
|
11635
|
-
if (t.kind === "tbl_open") innerDepth++;
|
|
11636
|
-
else if (t.kind === "tbl_close") innerDepth--;
|
|
11637
|
-
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11638
|
-
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11639
|
-
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11640
|
-
if (closePos >= 0) {
|
|
11641
|
-
text3 += xml.substring(t.end, closePos);
|
|
11642
|
-
}
|
|
11643
|
-
}
|
|
11644
|
-
}
|
|
11645
|
-
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11684
|
+
function splitDecoration(line) {
|
|
11685
|
+
const m = line.match(/^(\s*(?:#{1,6}\s+|[-*+]\s+|\d+\.\s+|>\s+)?)([\s\S]*)$/);
|
|
11686
|
+
if (!m) return { prefix: "", content: line };
|
|
11687
|
+
return { prefix: m[1] ?? "", content: m[2] ?? "" };
|
|
11646
11688
|
}
|
|
11647
|
-
function
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
let
|
|
11651
|
-
for (const
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11689
|
+
function restoreOverStrippedShapeText(markdown, paragraphTexts) {
|
|
11690
|
+
const lines = markdown.split("\n");
|
|
11691
|
+
const used = /* @__PURE__ */ new Set();
|
|
11692
|
+
let changed = false;
|
|
11693
|
+
for (const raw of paragraphTexts) {
|
|
11694
|
+
const buggy = applyStrip(raw, BUGGY_SHAPE_STRIP);
|
|
11695
|
+
const fixed = applyStrip(raw, FIXED_SHAPE_STRIP);
|
|
11696
|
+
if (buggy === fixed) continue;
|
|
11697
|
+
if (!fixed) continue;
|
|
11698
|
+
if (!buggy) continue;
|
|
11699
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11700
|
+
if (used.has(i)) continue;
|
|
11701
|
+
const { prefix, content } = splitDecoration(lines[i] ?? "");
|
|
11702
|
+
if (content.trim() === buggy && content.trim() !== fixed) {
|
|
11703
|
+
lines[i] = prefix + fixed;
|
|
11704
|
+
used.add(i);
|
|
11705
|
+
changed = true;
|
|
11706
|
+
break;
|
|
11664
11707
|
}
|
|
11665
11708
|
}
|
|
11666
11709
|
}
|
|
11667
|
-
return
|
|
11710
|
+
return changed ? lines.join("\n") : markdown;
|
|
11668
11711
|
}
|
|
11669
|
-
function
|
|
11670
|
-
const
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
}
|
|
11679
|
-
}
|
|
11680
|
-
if (!addr) return null;
|
|
11681
|
-
return { ...addr, ...span };
|
|
11682
|
-
}
|
|
11683
|
-
function applyCellEditsToSectionXml(xml, edits) {
|
|
11684
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
11685
|
-
const results = edits.map(() => ({ success: false }));
|
|
11686
|
-
let totalTopLevelTbls = 0;
|
|
11687
|
-
{
|
|
11688
|
-
let d = 0;
|
|
11689
|
-
for (const tok of tokens) {
|
|
11690
|
-
if (tok.kind === "tbl_open") {
|
|
11691
|
-
if (d === 0) totalTopLevelTbls++;
|
|
11692
|
-
d++;
|
|
11693
|
-
} else if (tok.kind === "tbl_close") {
|
|
11694
|
-
d--;
|
|
11695
|
-
}
|
|
11696
|
-
}
|
|
11697
|
-
}
|
|
11698
|
-
const replacements = [];
|
|
11699
|
-
for (let ei = 0; ei < edits.length; ei++) {
|
|
11700
|
-
const edit = edits[ei];
|
|
11701
|
-
const tblRange = findTopLevelTableRange(tokens, edit.tableIndex);
|
|
11702
|
-
if (!tblRange) {
|
|
11703
|
-
results[ei] = {
|
|
11704
|
-
success: false,
|
|
11705
|
-
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.`
|
|
11706
|
-
};
|
|
11707
|
-
continue;
|
|
11708
|
-
}
|
|
11709
|
-
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11710
|
-
const tblTokens = tokens.filter((t) => t.pos >= tblRange.start && t.pos < tblRange.end);
|
|
11711
|
-
let found = false;
|
|
11712
|
-
for (const tc of directTcs) {
|
|
11713
|
-
const tcTokens = tblTokens.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11714
|
-
const hasAddr = tcTokens.some(
|
|
11715
|
-
(t) => t.kind === "cell_addr" && t.colAddr === edit.col && t.rowAddr === edit.row
|
|
11716
|
-
);
|
|
11717
|
-
if (!hasAddr) continue;
|
|
11718
|
-
let innerDepth = 0;
|
|
11719
|
-
const ownTRuns = [];
|
|
11720
|
-
for (const t of tokens.filter((x) => x.pos >= tc.start && x.pos < tc.end)) {
|
|
11721
|
-
if (t.kind === "tbl_open") innerDepth++;
|
|
11722
|
-
else if (t.kind === "tbl_close") innerDepth--;
|
|
11723
|
-
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11724
|
-
ownTRuns.push({ isEmpty: true, tagPos: t.pos, tagEnd: t.end });
|
|
11725
|
-
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11726
|
-
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11727
|
-
if (closePos >= 0) {
|
|
11728
|
-
ownTRuns.push({ isEmpty: false, openEnd: t.end, closePos });
|
|
11712
|
+
function collectBlockTextSlots(blocks) {
|
|
11713
|
+
const slots = [];
|
|
11714
|
+
const walk = (bs) => {
|
|
11715
|
+
for (const b of bs) {
|
|
11716
|
+
if (typeof b.text === "string") {
|
|
11717
|
+
slots.push({
|
|
11718
|
+
get: () => b.text ?? "",
|
|
11719
|
+
set: (v) => {
|
|
11720
|
+
b.text = v;
|
|
11729
11721
|
}
|
|
11730
|
-
}
|
|
11731
|
-
}
|
|
11732
|
-
const currentText = ownTRuns.map((r) => r.isEmpty ? "" : xml.substring(r.openEnd, r.closePos)).join("").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11733
|
-
if (edit.expectedText !== void 0 && edit.expectedText !== currentText) {
|
|
11734
|
-
results[ei] = {
|
|
11735
|
-
success: false,
|
|
11736
|
-
oldText: currentText,
|
|
11737
|
-
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.`
|
|
11738
|
-
};
|
|
11739
|
-
found = true;
|
|
11740
|
-
break;
|
|
11741
|
-
}
|
|
11742
|
-
if (ownTRuns.length === 0) {
|
|
11743
|
-
results[ei] = {
|
|
11744
|
-
success: false,
|
|
11745
|
-
oldText: currentText,
|
|
11746
|
-
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.`
|
|
11747
|
-
};
|
|
11748
|
-
found = true;
|
|
11749
|
-
break;
|
|
11750
|
-
}
|
|
11751
|
-
const escapedNew = escapeXml(edit.newText);
|
|
11752
|
-
const patches = [];
|
|
11753
|
-
const firstRun = ownTRuns[0];
|
|
11754
|
-
if (firstRun.isEmpty) {
|
|
11755
|
-
patches.push({
|
|
11756
|
-
from: firstRun.tagPos,
|
|
11757
|
-
to: firstRun.tagEnd,
|
|
11758
|
-
text: `<hp:t>${escapedNew}</hp:t>`
|
|
11759
11722
|
});
|
|
11760
|
-
} else {
|
|
11761
|
-
patches.push({ from: firstRun.openEnd, to: firstRun.closePos, text: escapedNew });
|
|
11762
11723
|
}
|
|
11763
|
-
|
|
11764
|
-
const
|
|
11765
|
-
|
|
11766
|
-
|
|
11724
|
+
if (b.table) {
|
|
11725
|
+
for (const row of b.table.cells) {
|
|
11726
|
+
for (const cell of row) {
|
|
11727
|
+
slots.push({
|
|
11728
|
+
get: () => cell.text,
|
|
11729
|
+
set: (v) => {
|
|
11730
|
+
cell.text = v;
|
|
11731
|
+
}
|
|
11732
|
+
});
|
|
11733
|
+
if (cell.blocks) walk(cell.blocks);
|
|
11734
|
+
}
|
|
11767
11735
|
}
|
|
11768
11736
|
}
|
|
11769
|
-
|
|
11770
|
-
results[ei] = { success: true, oldText: currentText };
|
|
11771
|
-
found = true;
|
|
11772
|
-
break;
|
|
11737
|
+
if (b.children) walk(b.children);
|
|
11773
11738
|
}
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11739
|
+
};
|
|
11740
|
+
walk(blocks);
|
|
11741
|
+
return slots;
|
|
11742
|
+
}
|
|
11743
|
+
function restoreOverStrippedBlocks(blocks, paragraphTexts) {
|
|
11744
|
+
const slots = collectBlockTextSlots(blocks);
|
|
11745
|
+
const used = /* @__PURE__ */ new Set();
|
|
11746
|
+
let changed = false;
|
|
11747
|
+
for (const raw of paragraphTexts) {
|
|
11748
|
+
const buggy = applyStrip(raw, BUGGY_SHAPE_STRIP);
|
|
11749
|
+
const fixed = applyStrip(raw, FIXED_SHAPE_STRIP);
|
|
11750
|
+
if (buggy === fixed) continue;
|
|
11751
|
+
if (!fixed || !buggy) continue;
|
|
11752
|
+
for (let i = 0; i < slots.length; i++) {
|
|
11753
|
+
if (used.has(i)) continue;
|
|
11754
|
+
const cur = slots[i]?.get().trim() ?? "";
|
|
11755
|
+
if (cur === buggy && cur !== fixed) {
|
|
11756
|
+
slots[i]?.set(fixed);
|
|
11757
|
+
used.add(i);
|
|
11758
|
+
changed = true;
|
|
11759
|
+
break;
|
|
11760
|
+
}
|
|
11779
11761
|
}
|
|
11780
11762
|
}
|
|
11781
|
-
|
|
11782
|
-
return { newXml: xml, results };
|
|
11783
|
-
}
|
|
11784
|
-
const allPatches = replacements.flatMap((r) => r.patches).sort((a, b) => b.from - a.from);
|
|
11785
|
-
let result = xml;
|
|
11786
|
-
for (const patch of allPatches) {
|
|
11787
|
-
result = result.substring(0, patch.from) + patch.text + result.substring(patch.to);
|
|
11788
|
-
}
|
|
11789
|
-
return { newXml: result, results };
|
|
11763
|
+
return changed;
|
|
11790
11764
|
}
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
11801
|
-
let count = 0;
|
|
11802
|
-
let depth = 0;
|
|
11803
|
-
for (const tok of tokens) {
|
|
11804
|
-
if (tok.kind === "tbl_open") {
|
|
11805
|
-
if (depth === 0) count++;
|
|
11806
|
-
depth++;
|
|
11807
|
-
} else if (tok.kind === "tbl_close") {
|
|
11808
|
-
depth--;
|
|
11809
|
-
}
|
|
11765
|
+
function isZip(bytes) {
|
|
11766
|
+
if (bytes.length < 4) return false;
|
|
11767
|
+
return isZipFile2(new Uint8Array(bytes.subarray(0, 4)).buffer);
|
|
11768
|
+
}
|
|
11769
|
+
async function resolveHwpxBytes(input) {
|
|
11770
|
+
try {
|
|
11771
|
+
if (typeof input === "string") {
|
|
11772
|
+
if (!/\.hwpx$/i.test(input)) return null;
|
|
11773
|
+
return new Uint8Array(await readFile4(input));
|
|
11810
11774
|
}
|
|
11811
|
-
|
|
11775
|
+
const u8 = input instanceof Uint8Array ? input : new Uint8Array(input instanceof ArrayBuffer ? input : input);
|
|
11776
|
+
return isZip(u8) ? u8 : null;
|
|
11777
|
+
} catch {
|
|
11778
|
+
return null;
|
|
11812
11779
|
}
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
});
|
|
11831
|
-
}
|
|
11832
|
-
}
|
|
11780
|
+
}
|
|
11781
|
+
async function parse(input, options) {
|
|
11782
|
+
const result = await kordocParse(input, options);
|
|
11783
|
+
if (!result.success || typeof result.markdown !== "string" || !result.markdown) {
|
|
11784
|
+
return result;
|
|
11785
|
+
}
|
|
11786
|
+
const bytes = await resolveHwpxBytes(input);
|
|
11787
|
+
if (!bytes) return result;
|
|
11788
|
+
try {
|
|
11789
|
+
const paragraphTexts = await extractHwpxParagraphTexts(bytes);
|
|
11790
|
+
if (paragraphTexts.length === 0) return result;
|
|
11791
|
+
const repaired = restoreOverStrippedShapeText(result.markdown, paragraphTexts);
|
|
11792
|
+
if (Array.isArray(result.blocks)) {
|
|
11793
|
+
restoreOverStrippedBlocks(result.blocks, paragraphTexts);
|
|
11794
|
+
}
|
|
11795
|
+
if (repaired !== result.markdown) {
|
|
11796
|
+
return { ...result, markdown: repaired };
|
|
11833
11797
|
}
|
|
11834
|
-
|
|
11798
|
+
} catch {
|
|
11835
11799
|
}
|
|
11836
|
-
|
|
11837
|
-
const allResults = edits.map((edit, ei) => ({
|
|
11838
|
-
success: false,
|
|
11839
|
-
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.`
|
|
11840
|
-
}));
|
|
11841
|
-
const newSectionXmls = [...sectionXmls];
|
|
11842
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
11843
|
-
const sEdits = sectionEdits[si] ?? [];
|
|
11844
|
-
if (sEdits.length === 0) continue;
|
|
11845
|
-
const srcXml = sectionXmls[si] ?? "";
|
|
11846
|
-
const { newXml, results } = applyCellEditsToSectionXml(srcXml, sEdits);
|
|
11847
|
-
newSectionXmls[si] = newXml;
|
|
11848
|
-
for (let i = 0; i < sEdits.length; i++) {
|
|
11849
|
-
const sEdit = sEdits[i];
|
|
11850
|
-
const res = results[i];
|
|
11851
|
-
if (sEdit && res) {
|
|
11852
|
-
allResults[sEdit.originalEditIdx] = res;
|
|
11853
|
-
}
|
|
11854
|
-
}
|
|
11855
|
-
}
|
|
11856
|
-
if (allResults.some((r) => !r.success)) {
|
|
11857
|
-
return { buffer: hwpxBuffer, results: allResults };
|
|
11858
|
-
}
|
|
11859
|
-
const out = new import_jszip2.default();
|
|
11860
|
-
const mimetypeEntry = zip.file("mimetype");
|
|
11861
|
-
if (mimetypeEntry) {
|
|
11862
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
11863
|
-
}
|
|
11864
|
-
for (const [name, entry] of Object.entries(zip.files)) {
|
|
11865
|
-
if (name === "mimetype" || entry.dir) continue;
|
|
11866
|
-
const sectionIdx = sectionFiles.indexOf(name);
|
|
11867
|
-
if (sectionIdx >= 0) {
|
|
11868
|
-
out.file(name, newSectionXmls[sectionIdx] ?? "");
|
|
11869
|
-
} else {
|
|
11870
|
-
out.file(name, await entry.async("uint8array"));
|
|
11871
|
-
}
|
|
11872
|
-
}
|
|
11873
|
-
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
11874
|
-
return { buffer: new Uint8Array(buf), results: allResults };
|
|
11800
|
+
return result;
|
|
11875
11801
|
}
|
|
11876
|
-
var
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11802
|
+
var MAX_PREVIEW_CHARS = 4e3;
|
|
11803
|
+
var exportDocumentSchema = z32.object({
|
|
11804
|
+
path: z32.string().describe("\uBCC0\uD658\uD560 \uC6D0\uBCF8 \uBB38\uC11C \uACBD\uB85C (.hwp/.hwpx/.docx \uB4F1 \u2014 cwd \uAE30\uC900 \uC0C1\uB300/\uC808\uB300 \uACBD\uB85C)"),
|
|
11805
|
+
outputPath: z32.string().describe("\uCD9C\uB825 \uD30C\uC77C \uACBD\uB85C \u2014 \uD655\uC7A5\uC790\uB85C \uD615\uC2DD \uACB0\uC815(.html/.htm \uB610\uB294 .pdf)"),
|
|
11806
|
+
summary: z32.string().optional().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
11807
|
+
});
|
|
11808
|
+
var exportDocumentTool = {
|
|
11809
|
+
name: "export_document",
|
|
11810
|
+
description: "\uBB38\uC11C(.hwp/.hwpx/.docx \uB4F1)\uB97C HTML \uB610\uB294 PDF\uB85C \uB0B4\uBCF4\uB0C5\uB2C8\uB2E4. \uCD9C\uB825 \uACBD\uB85C \uD655\uC7A5\uC790\uB85C \uD615\uC2DD\uC744 \uACB0\uC815\uD569\uB2C8\uB2E4(.html/.htm \uB610\uB294 .pdf). \uC6D0\uBCF8\uC740 \uBCC0\uACBD\uD558\uC9C0 \uC54A\uACE0 \uC0C8 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4. HTML\uC740 \uD56D\uC0C1 \uAC00\uB2A5\uD558\uBA70, PDF\uB294 puppeteer-core\uAC00 \uC124\uCE58\uB41C \uD658\uACBD\uC5D0\uC11C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4(\uBBF8\uC124\uCE58 \uC2DC \uC548\uB0B4). \uBCC0\uACBD \uC0AC\uD56D\uC740 \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
11811
|
+
inputSchema: exportDocumentSchema,
|
|
11880
11812
|
requiresApproval: true,
|
|
11881
11813
|
propose: async ({
|
|
11882
11814
|
input,
|
|
11883
11815
|
ctx
|
|
11884
11816
|
}) => {
|
|
11885
11817
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11886
|
-
const
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
try {
|
|
11892
|
-
originalBuffer = await readFile4(safePath);
|
|
11893
|
-
} catch {
|
|
11894
|
-
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.`;
|
|
11895
|
-
}
|
|
11896
|
-
const originalBytes = new Uint8Array(originalBuffer.buffer);
|
|
11897
|
-
const structuralGuard = hwpStructuralGuard(ext, originalBytes);
|
|
11898
|
-
if (structuralGuard !== null) {
|
|
11899
|
-
return structuralGuard;
|
|
11818
|
+
const outPath = await resolveSafePath(ctx.cwd, input.outputPath);
|
|
11819
|
+
const outExt = extname3(outPath).toLowerCase();
|
|
11820
|
+
const format = outExt === ".html" || outExt === ".htm" ? "html" : outExt === ".pdf" ? "pdf" : null;
|
|
11821
|
+
if (format === null) {
|
|
11822
|
+
return `\uC624\uB958: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uCD9C\uB825 \uD615\uC2DD\uC785\uB2C8\uB2E4: ${outExt || "(\uD655\uC7A5\uC790 \uC5C6\uC74C)"}. \uCD9C\uB825 \uACBD\uB85C\uB97C .html \uB610\uB294 .pdf \uB85C \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
11900
11823
|
}
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
for (const sf of sectionFilesForLabel) {
|
|
11909
|
-
const entry = zipForLabel.file(sf);
|
|
11910
|
-
const xml = entry ? await entry.async("string") : "";
|
|
11911
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
11912
|
-
let count = 0;
|
|
11913
|
-
let d = 0;
|
|
11914
|
-
for (const tok of tokens) {
|
|
11915
|
-
if (tok.kind === "tbl_open") {
|
|
11916
|
-
if (d === 0) count++;
|
|
11917
|
-
d++;
|
|
11918
|
-
} else if (tok.kind === "tbl_close") {
|
|
11919
|
-
d--;
|
|
11920
|
-
}
|
|
11921
|
-
}
|
|
11922
|
-
sectionInfos.push({ xml, tblCount: count, globalOffset: globalTblOffset });
|
|
11923
|
-
globalTblOffset += count;
|
|
11924
|
-
}
|
|
11925
|
-
function resolveLabelAcrossSections(label, direction, scopedTableIndex) {
|
|
11926
|
-
const trimmedLabel = label.trim();
|
|
11927
|
-
const allMatches = [];
|
|
11928
|
-
for (const si of sectionInfos) {
|
|
11929
|
-
const tokens = tokenizeHwpxXml(si.xml);
|
|
11930
|
-
let localStart = 0;
|
|
11931
|
-
let localEnd = si.tblCount - 1;
|
|
11932
|
-
if (scopedTableIndex !== void 0) {
|
|
11933
|
-
const localIdx2 = scopedTableIndex - si.globalOffset;
|
|
11934
|
-
if (localIdx2 < 0 || localIdx2 >= si.tblCount) continue;
|
|
11935
|
-
localStart = localIdx2;
|
|
11936
|
-
localEnd = localIdx2;
|
|
11937
|
-
}
|
|
11938
|
-
for (let li = localStart; li <= localEnd; li++) {
|
|
11939
|
-
const tblRange = findTopLevelTableRange(tokens, li);
|
|
11940
|
-
if (!tblRange) continue;
|
|
11941
|
-
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11942
|
-
for (const tc of directTcs) {
|
|
11943
|
-
const cellText = readOwnTextFromTc(si.xml, tokens, tc.start, tc.end).trim();
|
|
11944
|
-
if (cellText === trimmedLabel) {
|
|
11945
|
-
const addrSpan2 = readCellAddrSpan(tokens, tc.start, tc.end);
|
|
11946
|
-
if (addrSpan2) {
|
|
11947
|
-
allMatches.push({
|
|
11948
|
-
globalTableIndex: si.globalOffset + li,
|
|
11949
|
-
addrSpan: addrSpan2,
|
|
11950
|
-
sectionXml: si.xml,
|
|
11951
|
-
sectionOffset: si.globalOffset
|
|
11952
|
-
});
|
|
11953
|
-
}
|
|
11954
|
-
}
|
|
11955
|
-
}
|
|
11956
|
-
}
|
|
11957
|
-
}
|
|
11958
|
-
if (allMatches.length === 0) {
|
|
11959
|
-
const scope = scopedTableIndex !== void 0 ? `\uD45C ${scopedTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
11960
|
-
return {
|
|
11961
|
-
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.`
|
|
11962
|
-
};
|
|
11963
|
-
}
|
|
11964
|
-
if (allMatches.length > 1) {
|
|
11965
|
-
const locs = allMatches.map(
|
|
11966
|
-
(m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.addrSpan.rowAddr}, \uC5F4 ${m.addrSpan.colAddr})`
|
|
11967
|
-
).join(", ");
|
|
11968
|
-
return {
|
|
11969
|
-
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.`
|
|
11970
|
-
};
|
|
11971
|
-
}
|
|
11972
|
-
const match = allMatches[0];
|
|
11973
|
-
const { addrSpan, globalTableIndex, sectionXml, sectionOffset } = match;
|
|
11974
|
-
let targetRow;
|
|
11975
|
-
let targetCol;
|
|
11976
|
-
if (direction === "right") {
|
|
11977
|
-
targetRow = addrSpan.rowAddr;
|
|
11978
|
-
targetCol = addrSpan.colAddr + addrSpan.colSpan;
|
|
11979
|
-
} else {
|
|
11980
|
-
targetRow = addrSpan.rowAddr + addrSpan.rowSpan;
|
|
11981
|
-
targetCol = addrSpan.colAddr;
|
|
11982
|
-
}
|
|
11983
|
-
const localIdx = globalTableIndex - sectionOffset;
|
|
11984
|
-
const tokens2 = tokenizeHwpxXml(sectionXml);
|
|
11985
|
-
const tblRange2 = findTopLevelTableRange(tokens2, localIdx);
|
|
11986
|
-
if (!tblRange2) {
|
|
11987
|
-
return { error: `\uD45C ${globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
11988
|
-
}
|
|
11989
|
-
const directTcs2 = collectDirectTcRanges(tokens2, tblRange2.start, tblRange2.end);
|
|
11990
|
-
const tblTokens2 = tokens2.filter((t) => t.pos >= tblRange2.start && t.pos < tblRange2.end);
|
|
11991
|
-
let targetExists = false;
|
|
11992
|
-
for (const tc of directTcs2) {
|
|
11993
|
-
const tcTokens = tblTokens2.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11994
|
-
if (tcTokens.some(
|
|
11995
|
-
(t) => t.kind === "cell_addr" && t.colAddr === targetCol && t.rowAddr === targetRow
|
|
11996
|
-
)) {
|
|
11997
|
-
targetExists = true;
|
|
11998
|
-
break;
|
|
11999
|
-
}
|
|
12000
|
-
}
|
|
12001
|
-
if (!targetExists) {
|
|
12002
|
-
const dirLabel = direction === "right" ? "\uC624\uB978\uCABD" : "\uC544\uB798";
|
|
12003
|
-
return {
|
|
12004
|
-
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.`
|
|
12005
|
-
};
|
|
12006
|
-
}
|
|
12007
|
-
return { tableIndex: globalTableIndex, row: targetRow, col: targetCol };
|
|
11824
|
+
const parseResult = await parse(safePath);
|
|
11825
|
+
if (!parseResult.success) {
|
|
11826
|
+
const msg = kordocErrorMessage(
|
|
11827
|
+
parseResult.code,
|
|
11828
|
+
`\uC6D0\uBCF8 \uBB38\uC11C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${parseResult.error}`
|
|
11829
|
+
);
|
|
11830
|
+
return `\uC624\uB958: ${msg}`;
|
|
12008
11831
|
}
|
|
12009
|
-
const
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
);
|
|
12018
|
-
}
|
|
12019
|
-
const
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
resolveErrors.push(`\uD3B8\uC9D1 #${i + 1} (\uB808\uC774\uBE14 "${e.label}"): ${resolved.error}`);
|
|
12023
|
-
} else {
|
|
12024
|
-
resolvedEdits.push({
|
|
12025
|
-
tableIndex: resolved.tableIndex,
|
|
12026
|
-
row: resolved.row,
|
|
12027
|
-
col: resolved.col,
|
|
12028
|
-
newText: e.newText,
|
|
12029
|
-
expectedText: e.expectedText,
|
|
12030
|
-
label: e.label
|
|
12031
|
-
});
|
|
11832
|
+
const markdown = parseResult.markdown;
|
|
11833
|
+
let stagedData;
|
|
11834
|
+
const warnings = [];
|
|
11835
|
+
if (format === "html") {
|
|
11836
|
+
stagedData = new TextEncoder().encode(renderHtml(markdown));
|
|
11837
|
+
} else {
|
|
11838
|
+
try {
|
|
11839
|
+
const pdf = await markdownToPdf(markdown);
|
|
11840
|
+
stagedData = new Uint8Array(pdf);
|
|
11841
|
+
} catch (err) {
|
|
11842
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
11843
|
+
if (/puppeteer/i.test(m)) {
|
|
11844
|
+
return "\uC624\uB958: PDF \uB0B4\uBCF4\uB0B4\uAE30\uC5D0\uB294 puppeteer-core\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4(\uC774 \uD658\uACBD\uC5D0 \uBBF8\uC124\uCE58). \uD574\uACB0: (1) \uCD9C\uB825 \uACBD\uB85C\uB97C .html \uB85C \uC9C0\uC815\uD574 HTML\uB85C \uB0B4\uBCF4\uB0B4\uAC70\uB098, (2) `npm install -g puppeteer-core` \uD6C4 Chrome/Chromium \uC2E4\uD589 \uD30C\uC77C \uACBD\uB85C\uB97C \uC124\uC815\uD558\uC138\uC694.";
|
|
12032
11845
|
}
|
|
12033
|
-
|
|
12034
|
-
resolvedEdits.push({
|
|
12035
|
-
tableIndex: e.tableIndex,
|
|
12036
|
-
row: e.row,
|
|
12037
|
-
col: e.col,
|
|
12038
|
-
newText: e.newText,
|
|
12039
|
-
expectedText: e.expectedText
|
|
12040
|
-
});
|
|
12041
|
-
} else {
|
|
12042
|
-
resolveErrors.push(
|
|
12043
|
-
`\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.`
|
|
12044
|
-
);
|
|
11846
|
+
return `\uC624\uB958: PDF \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${m}`;
|
|
12045
11847
|
}
|
|
12046
11848
|
}
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
row: e.row,
|
|
12054
|
-
col: e.col,
|
|
12055
|
-
newText: e.newText,
|
|
12056
|
-
expectedText: e.expectedText
|
|
12057
|
-
}));
|
|
12058
|
-
const { buffer: newBuffer, results } = await applyEditsToHwpx(
|
|
12059
|
-
new Uint8Array(originalBuffer.buffer),
|
|
12060
|
-
editRequests
|
|
12061
|
-
);
|
|
12062
|
-
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
12063
|
-
if (failedResults.length > 0) {
|
|
12064
|
-
const messages = failedResults.map(({ r, i }) => {
|
|
12065
|
-
const e = resolvedEdits[i];
|
|
12066
|
-
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
12067
|
-
return `\uD3B8\uC9D1 #${i + 1} (${label}\uD45C ${e?.tableIndex ?? "?"}, \uD589 ${e?.row ?? "?"}, \uC5F4 ${e?.col ?? "?"}): ${r.error}`;
|
|
12068
|
-
});
|
|
12069
|
-
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.
|
|
12070
|
-
${messages.join("\n")}`;
|
|
12071
|
-
}
|
|
12072
|
-
const diffLines = ["| \uD45C\xB7\uC140 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12073
|
-
for (let i = 0; i < resolvedEdits.length; i++) {
|
|
12074
|
-
const e = resolvedEdits[i];
|
|
12075
|
-
if (!e) continue;
|
|
12076
|
-
const oldText = results[i]?.oldText ?? "";
|
|
12077
|
-
const addr = e.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 #${e.tableIndex} (${e.row},${e.col})` : `#${e.tableIndex} (${e.row},${e.col})`;
|
|
12078
|
-
diffLines.push(`| ${addr} | ${oldText} | ${e.newText} |`);
|
|
12079
|
-
}
|
|
12080
|
-
const diff = diffLines.join("\n");
|
|
12081
|
-
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
12082
|
-
const stagedPath = await stageFile(ctx.sessionId, safePath, newBuffer);
|
|
11849
|
+
const stagedPath = await stageFile(ctx.sessionId, outPath, stagedData);
|
|
11850
|
+
const preview = format === "html" ? `[HTML \uB0B4\uBCF4\uB0B4\uAE30 \uBBF8\uB9AC\uBCF4\uAE30 \u2014 \uBCF8\uBB38 \uD14D\uC2A4\uD2B8]
|
|
11851
|
+
|
|
11852
|
+
${markdown.length > MAX_PREVIEW_CHARS ? `${markdown.slice(0, MAX_PREVIEW_CHARS)}
|
|
11853
|
+
|
|
11854
|
+
...\uC774\uD558 \uC0DD\uB7B5` : markdown}` : `[PDF \uB0B4\uBCF4\uB0B4\uAE30] ${input.path} \u2192 ${input.outputPath} (${stagedData.byteLength.toLocaleString()} bytes)`;
|
|
12083
11855
|
const proposalId = crypto.randomUUID();
|
|
12084
11856
|
return {
|
|
12085
11857
|
proposal: {
|
|
12086
11858
|
id: proposalId,
|
|
12087
|
-
kind: "
|
|
12088
|
-
targetPath:
|
|
11859
|
+
kind: "export",
|
|
11860
|
+
targetPath: outPath,
|
|
12089
11861
|
stagedPath,
|
|
12090
|
-
summary: input.summary
|
|
12091
|
-
diff,
|
|
12092
|
-
warnings
|
|
12093
|
-
willConvertFormat
|
|
11862
|
+
summary: input.summary ?? `${input.path} \u2192 ${format.toUpperCase()} \uB0B4\uBCF4\uB0B4\uAE30`,
|
|
11863
|
+
diff: preview,
|
|
11864
|
+
warnings
|
|
12094
11865
|
},
|
|
12095
11866
|
commit: async () => {
|
|
12096
|
-
const backupPath = await backupFile(
|
|
12097
|
-
await commitStaged(stagedPath,
|
|
12098
|
-
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
12099
|
-
return `\
|
|
11867
|
+
const backupPath = await backupFile(outPath);
|
|
11868
|
+
await commitStaged(stagedPath, outPath);
|
|
11869
|
+
const backupInfo = backupPath ? ` (\uAE30\uC874 \uD30C\uC77C \uBC31\uC5C5: ${backupPath})` : "";
|
|
11870
|
+
return `\uB0B4\uBCF4\uB0B4\uAE30 \uC644\uB8CC: ${outPath}${backupInfo}`;
|
|
12100
11871
|
}
|
|
12101
11872
|
};
|
|
12102
11873
|
}
|
|
@@ -12106,9 +11877,6 @@ var findInDocumentSchema = z4.object({
|
|
|
12106
11877
|
query: z4.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
12107
11878
|
caseSensitive: z4.boolean().optional().describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8 false)")
|
|
12108
11879
|
});
|
|
12109
|
-
function unescapeXml(text3) {
|
|
12110
|
-
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
12111
|
-
}
|
|
12112
11880
|
function windowText(text3, query, caseSensitive, maxLen = 60) {
|
|
12113
11881
|
const compare3 = caseSensitive ? text3 : text3.toLowerCase();
|
|
12114
11882
|
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
@@ -12123,91 +11891,31 @@ function windowText(text3, query, caseSensitive, maxLen = 60) {
|
|
|
12123
11891
|
}
|
|
12124
11892
|
function findInSectionXmls(xmls, query, caseSensitive) {
|
|
12125
11893
|
const hits = [];
|
|
11894
|
+
const needle = caseSensitive ? query : query.toLowerCase();
|
|
11895
|
+
const includes = (text3) => (caseSensitive ? text3 : text3.toLowerCase()).includes(needle);
|
|
12126
11896
|
let globalOffset = 0;
|
|
12127
11897
|
for (let si = 0; si < xmls.length; si++) {
|
|
12128
11898
|
const xml = xmls[si] ?? "";
|
|
12129
|
-
const
|
|
12130
|
-
let
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
for (const
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
}
|
|
12140
|
-
}
|
|
12141
|
-
}
|
|
12142
|
-
for (let ti = 0; ti < localCount; ti++) {
|
|
12143
|
-
const range = findTopLevelTableRange(tokens, ti);
|
|
12144
|
-
if (!range) continue;
|
|
12145
|
-
const cells = collectDirectTcRanges(tokens, range.start, range.end);
|
|
12146
|
-
const tblTokens = tokens.filter((t) => t.pos >= range.start && t.pos < range.end);
|
|
12147
|
-
for (const cell of cells) {
|
|
12148
|
-
const cellText = readOwnTextFromTc(xml, tokens, cell.start, cell.end);
|
|
12149
|
-
const compare3 = caseSensitive ? cellText : cellText.toLowerCase();
|
|
12150
|
-
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12151
|
-
if (!compare3.includes(compareQuery)) continue;
|
|
12152
|
-
const cellTokens = tblTokens.filter((t) => t.pos >= cell.start && t.pos < cell.end);
|
|
12153
|
-
const addrTok = cellTokens.find((t) => t.kind === "cell_addr");
|
|
12154
|
-
if (addrTok === void 0) continue;
|
|
12155
|
-
const row = addrTok.rowAddr ?? 0;
|
|
12156
|
-
const col = addrTok.colAddr ?? 0;
|
|
11899
|
+
const scan = scanSectionXml(xml, 0);
|
|
11900
|
+
for (let ti = 0; ti < scan.tables.length; ti++) {
|
|
11901
|
+
const table = scan.tables[ti];
|
|
11902
|
+
if (table === void 0) continue;
|
|
11903
|
+
for (const [key, cell] of table.cellByAnchor) {
|
|
11904
|
+
const cellText = cell.paragraphs.map((p) => p.text).join("");
|
|
11905
|
+
if (!includes(cellText)) continue;
|
|
11906
|
+
const parts = key.split(",");
|
|
11907
|
+
const row = Number(parts[0]);
|
|
11908
|
+
const col = Number(parts[1]);
|
|
12157
11909
|
const truncated = cellText.length > 60 ? `${cellText.slice(0, 57)}\u2026` : cellText;
|
|
12158
|
-
hits.push({
|
|
12159
|
-
kind: "\uD45C",
|
|
12160
|
-
tableIndex: globalOffset + ti,
|
|
12161
|
-
row,
|
|
12162
|
-
col,
|
|
12163
|
-
text: truncated
|
|
12164
|
-
});
|
|
11910
|
+
hits.push({ kind: "\uD45C", tableIndex: globalOffset + ti, row, col, text: truncated });
|
|
12165
11911
|
}
|
|
12166
11912
|
}
|
|
12167
|
-
{
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
let segStart = 0;
|
|
12171
|
-
for (const tok of tokens) {
|
|
12172
|
-
if (tok.kind === "tbl_open") {
|
|
12173
|
-
if (tblDepth === 0) {
|
|
12174
|
-
if (tok.pos > segStart) {
|
|
12175
|
-
bodySegments.push({ start: segStart, end: tok.pos });
|
|
12176
|
-
}
|
|
12177
|
-
}
|
|
12178
|
-
tblDepth++;
|
|
12179
|
-
} else if (tok.kind === "tbl_close") {
|
|
12180
|
-
tblDepth--;
|
|
12181
|
-
if (tblDepth === 0) {
|
|
12182
|
-
segStart = tok.end;
|
|
12183
|
-
}
|
|
12184
|
-
}
|
|
12185
|
-
}
|
|
12186
|
-
if (segStart < xml.length) {
|
|
12187
|
-
bodySegments.push({ start: segStart, end: xml.length });
|
|
12188
|
-
}
|
|
12189
|
-
const tRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
12190
|
-
for (const seg of bodySegments) {
|
|
12191
|
-
const slice = xml.slice(seg.start, seg.end);
|
|
12192
|
-
tRe.lastIndex = 0;
|
|
12193
|
-
let m = tRe.exec(slice);
|
|
12194
|
-
while (m !== null) {
|
|
12195
|
-
const rawText = m[1] ?? "";
|
|
12196
|
-
const decodedText = unescapeXml(rawText);
|
|
12197
|
-
const compare3 = caseSensitive ? decodedText : decodedText.toLowerCase();
|
|
12198
|
-
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12199
|
-
if (compare3.includes(compareQuery)) {
|
|
12200
|
-
hits.push({
|
|
12201
|
-
kind: "\uBCF8\uBB38",
|
|
12202
|
-
section: si,
|
|
12203
|
-
text: windowText(decodedText, query, caseSensitive)
|
|
12204
|
-
});
|
|
12205
|
-
}
|
|
12206
|
-
m = tRe.exec(slice);
|
|
12207
|
-
}
|
|
11913
|
+
for (const p of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
11914
|
+
if (p.text.length > 0 && includes(p.text)) {
|
|
11915
|
+
hits.push({ kind: "\uBCF8\uBB38", section: si, text: windowText(p.text, query, caseSensitive) });
|
|
12208
11916
|
}
|
|
12209
11917
|
}
|
|
12210
|
-
globalOffset +=
|
|
11918
|
+
globalOffset += scan.tables.length;
|
|
12211
11919
|
}
|
|
12212
11920
|
return hits;
|
|
12213
11921
|
}
|
|
@@ -12251,12 +11959,12 @@ var findInDocumentTool = {
|
|
|
12251
11959
|
} catch {
|
|
12252
11960
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12253
11961
|
}
|
|
12254
|
-
if (bytes
|
|
11962
|
+
if (!isZipBinary(bytes)) {
|
|
12255
11963
|
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.";
|
|
12256
11964
|
}
|
|
12257
11965
|
let zip;
|
|
12258
11966
|
try {
|
|
12259
|
-
zip = await
|
|
11967
|
+
zip = await import_jszip2.default.loadAsync(bytes);
|
|
12260
11968
|
} catch (err) {
|
|
12261
11969
|
return `\uC624\uB958: .hwpx ZIP\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${String(err)}`;
|
|
12262
11970
|
}
|
|
@@ -12287,7 +11995,7 @@ var findInDocumentTool = {
|
|
|
12287
11995
|
return formatHits(hits, input.query);
|
|
12288
11996
|
}
|
|
12289
11997
|
};
|
|
12290
|
-
function
|
|
11998
|
+
function escapeXml(text3) {
|
|
12291
11999
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
12292
12000
|
}
|
|
12293
12001
|
function decodeXml(text3) {
|
|
@@ -12439,7 +12147,7 @@ function applyFormObjectEdits(xml, edits) {
|
|
|
12439
12147
|
}
|
|
12440
12148
|
let patchCreated = false;
|
|
12441
12149
|
if (set.caption !== void 0) {
|
|
12442
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption",
|
|
12150
|
+
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption", escapeXml(set.caption));
|
|
12443
12151
|
if (!patch) {
|
|
12444
12152
|
results[ei] = {
|
|
12445
12153
|
success: false,
|
|
@@ -12467,7 +12175,7 @@ function applyFormObjectEdits(xml, edits) {
|
|
|
12467
12175
|
pos,
|
|
12468
12176
|
openTagEnd,
|
|
12469
12177
|
"selectedValue",
|
|
12470
|
-
|
|
12178
|
+
escapeXml(set.selected)
|
|
12471
12179
|
);
|
|
12472
12180
|
if (!patch) {
|
|
12473
12181
|
results[ei] = {
|
|
@@ -12593,7 +12301,7 @@ function replaceAttrInOpenTag(xml, tagStart, tagEnd, attr, newValue) {
|
|
|
12593
12301
|
};
|
|
12594
12302
|
}
|
|
12595
12303
|
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
12596
|
-
const escaped =
|
|
12304
|
+
const escaped = escapeXml(newText);
|
|
12597
12305
|
const selfCloseRe = /<hp:text\s*\/>/;
|
|
12598
12306
|
const scm = selfCloseRe.exec(elementContent);
|
|
12599
12307
|
if (scm) {
|
|
@@ -12640,7 +12348,7 @@ function validateHwpxBuffer(ext, buffer) {
|
|
|
12640
12348
|
if (structuralGuard !== null) {
|
|
12641
12349
|
return structuralGuard;
|
|
12642
12350
|
}
|
|
12643
|
-
if (buffer
|
|
12351
|
+
if (!isZipBinary(buffer)) {
|
|
12644
12352
|
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.";
|
|
12645
12353
|
}
|
|
12646
12354
|
return null;
|
|
@@ -12866,26 +12574,483 @@ ${failMessages.join("\n")}`;
|
|
|
12866
12574
|
out.file(name, await entry.async("uint8array"));
|
|
12867
12575
|
}
|
|
12868
12576
|
}
|
|
12869
|
-
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
12870
|
-
const newBuffer = new Uint8Array(buf);
|
|
12871
|
-
const typeKo = {
|
|
12872
|
-
button: "PushButton",
|
|
12873
|
-
checkBox: "CheckBox",
|
|
12874
|
-
radioButton: "RadioButton",
|
|
12875
|
-
comboBox: "ComboBox",
|
|
12876
|
-
edit: "Edit"
|
|
12877
|
-
};
|
|
12878
|
-
const diffLines = ["| \uC591\uC2DD \uAC1C\uCCB4 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12879
|
-
for (const re of resolvedEdits) {
|
|
12880
|
-
const result = successMap.get(re.editIdx);
|
|
12881
|
-
const oldVal = result?.oldValue ?? "";
|
|
12882
|
-
const oldStr = typeof oldVal === "boolean" ? oldVal ? "CHECKED" : "UNCHECKED" : String(oldVal);
|
|
12883
|
-
let newStr;
|
|
12884
|
-
if (re.set.caption !== void 0) newStr = re.set.caption;
|
|
12885
|
-
else if (re.set.checked !== void 0) newStr = re.set.checked ? "CHECKED" : "UNCHECKED";
|
|
12886
|
-
else if (re.set.selected !== void 0) newStr = re.set.selected;
|
|
12887
|
-
else newStr = re.set.text ?? "";
|
|
12888
|
-
diffLines.push(`| ${re.target.name}(${typeKo[re.target.type]}) | ${oldStr} | ${newStr} |`);
|
|
12577
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
12578
|
+
const newBuffer = new Uint8Array(buf);
|
|
12579
|
+
const typeKo = {
|
|
12580
|
+
button: "PushButton",
|
|
12581
|
+
checkBox: "CheckBox",
|
|
12582
|
+
radioButton: "RadioButton",
|
|
12583
|
+
comboBox: "ComboBox",
|
|
12584
|
+
edit: "Edit"
|
|
12585
|
+
};
|
|
12586
|
+
const diffLines = ["| \uC591\uC2DD \uAC1C\uCCB4 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12587
|
+
for (const re of resolvedEdits) {
|
|
12588
|
+
const result = successMap.get(re.editIdx);
|
|
12589
|
+
const oldVal = result?.oldValue ?? "";
|
|
12590
|
+
const oldStr = typeof oldVal === "boolean" ? oldVal ? "CHECKED" : "UNCHECKED" : String(oldVal);
|
|
12591
|
+
let newStr;
|
|
12592
|
+
if (re.set.caption !== void 0) newStr = re.set.caption;
|
|
12593
|
+
else if (re.set.checked !== void 0) newStr = re.set.checked ? "CHECKED" : "UNCHECKED";
|
|
12594
|
+
else if (re.set.selected !== void 0) newStr = re.set.selected;
|
|
12595
|
+
else newStr = re.set.text ?? "";
|
|
12596
|
+
diffLines.push(`| ${re.target.name}(${typeKo[re.target.type]}) | ${oldStr} | ${newStr} |`);
|
|
12597
|
+
}
|
|
12598
|
+
const diff = diffLines.join("\n");
|
|
12599
|
+
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
12600
|
+
const stagedPath = await stageFile(ctx.sessionId, safePath, newBuffer);
|
|
12601
|
+
const proposalId = crypto.randomUUID();
|
|
12602
|
+
return {
|
|
12603
|
+
proposal: {
|
|
12604
|
+
id: proposalId,
|
|
12605
|
+
kind: "form-object",
|
|
12606
|
+
targetPath: outputPath,
|
|
12607
|
+
stagedPath,
|
|
12608
|
+
summary: input.summary,
|
|
12609
|
+
diff,
|
|
12610
|
+
warnings: [],
|
|
12611
|
+
willConvertFormat
|
|
12612
|
+
},
|
|
12613
|
+
commit: async () => {
|
|
12614
|
+
const backupPath = await backupFile(safePath);
|
|
12615
|
+
await commitStaged(stagedPath, outputPath);
|
|
12616
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
12617
|
+
return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
12618
|
+
}
|
|
12619
|
+
};
|
|
12620
|
+
}
|
|
12621
|
+
};
|
|
12622
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
12623
|
+
".hwp",
|
|
12624
|
+
".hwpx",
|
|
12625
|
+
".hwpml",
|
|
12626
|
+
".docx",
|
|
12627
|
+
".doc",
|
|
12628
|
+
".xlsx",
|
|
12629
|
+
".xls",
|
|
12630
|
+
".pdf",
|
|
12631
|
+
".pptx",
|
|
12632
|
+
".ppt",
|
|
12633
|
+
".md",
|
|
12634
|
+
".txt"
|
|
12635
|
+
]);
|
|
12636
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12637
|
+
"node_modules",
|
|
12638
|
+
".git",
|
|
12639
|
+
".DS_Store",
|
|
12640
|
+
"__pycache__",
|
|
12641
|
+
"dist",
|
|
12642
|
+
"build",
|
|
12643
|
+
".next"
|
|
12644
|
+
]);
|
|
12645
|
+
var MAX_DEPTH = 4;
|
|
12646
|
+
var MAX_FILES = 500;
|
|
12647
|
+
var listFilesSchema = z6.object({
|
|
12648
|
+
dir: z6.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
|
|
12649
|
+
});
|
|
12650
|
+
async function collectFiles(dir, cwd, depth, entries) {
|
|
12651
|
+
if (depth > MAX_DEPTH || entries.length >= MAX_FILES) return;
|
|
12652
|
+
let items;
|
|
12653
|
+
try {
|
|
12654
|
+
items = await readdir3(dir);
|
|
12655
|
+
} catch {
|
|
12656
|
+
return;
|
|
12657
|
+
}
|
|
12658
|
+
for (const item of items) {
|
|
12659
|
+
if (entries.length >= MAX_FILES) break;
|
|
12660
|
+
if (item.startsWith(".")) continue;
|
|
12661
|
+
if (SKIP_DIRS.has(item)) continue;
|
|
12662
|
+
const fullPath = join4(dir, item);
|
|
12663
|
+
let info;
|
|
12664
|
+
try {
|
|
12665
|
+
info = await stat3(fullPath);
|
|
12666
|
+
} catch {
|
|
12667
|
+
continue;
|
|
12668
|
+
}
|
|
12669
|
+
const relPath = relative2(cwd, fullPath);
|
|
12670
|
+
const ext = extname6(item).toLowerCase();
|
|
12671
|
+
const isDoc = DOC_EXTENSIONS.has(ext);
|
|
12672
|
+
if (info.isDirectory()) {
|
|
12673
|
+
entries.push({ path: relPath + "/", isDir: true, isDoc: false });
|
|
12674
|
+
await collectFiles(fullPath, cwd, depth + 1, entries);
|
|
12675
|
+
} else if (info.isFile()) {
|
|
12676
|
+
entries.push({ path: relPath, isDir: false, isDoc });
|
|
12677
|
+
}
|
|
12678
|
+
}
|
|
12679
|
+
}
|
|
12680
|
+
var listFilesTool = {
|
|
12681
|
+
name: "list_files",
|
|
12682
|
+
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.",
|
|
12683
|
+
inputSchema: listFilesSchema,
|
|
12684
|
+
requiresApproval: false,
|
|
12685
|
+
execute: async ({
|
|
12686
|
+
input,
|
|
12687
|
+
ctx
|
|
12688
|
+
}) => {
|
|
12689
|
+
const targetDir = input.dir ? await resolveSafePath(ctx.cwd, input.dir) : ctx.cwd;
|
|
12690
|
+
const entries = [];
|
|
12691
|
+
await collectFiles(targetDir, ctx.cwd, 0, entries);
|
|
12692
|
+
if (entries.length === 0) {
|
|
12693
|
+
return "\uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
12694
|
+
}
|
|
12695
|
+
const docs = entries.filter((e) => e.isDoc);
|
|
12696
|
+
const dirs = entries.filter((e) => e.isDir);
|
|
12697
|
+
const others = entries.filter((e) => !e.isDoc && !e.isDir);
|
|
12698
|
+
const allSorted = [...docs, ...dirs, ...others];
|
|
12699
|
+
const lines = allSorted.map((e) => {
|
|
12700
|
+
const icon = e.isDir ? "\u{1F4C1}" : e.isDoc ? "\u{1F4C4}" : " ";
|
|
12701
|
+
return `${icon} ${e.path}`;
|
|
12702
|
+
});
|
|
12703
|
+
const truncateNotice = entries.length >= MAX_FILES ? `
|
|
12704
|
+
(\uCD5C\uB300 ${MAX_FILES}\uAC1C\uAE4C\uC9C0 \uD45C\uC2DC\uB429\uB2C8\uB2E4. \uB354 \uC881\uC740 \uBC94\uC704\uB97C \uC9C0\uC815\uD558\uC138\uC694.)` : "";
|
|
12705
|
+
return lines.join("\n") + truncateNotice;
|
|
12706
|
+
}
|
|
12707
|
+
};
|
|
12708
|
+
var cellEditItemSchema = z7.object({
|
|
12709
|
+
tableIndex: z7.number().int().nonnegative().optional().describe(
|
|
12710
|
+
"\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)."
|
|
12711
|
+
),
|
|
12712
|
+
row: z7.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
|
|
12713
|
+
col: z7.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
|
|
12714
|
+
label: z7.string().optional().describe(
|
|
12715
|
+
"\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."
|
|
12716
|
+
),
|
|
12717
|
+
direction: z7.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."),
|
|
12718
|
+
newText: z7.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
|
|
12719
|
+
expectedText: z7.string().optional().describe(
|
|
12720
|
+
"\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."
|
|
12721
|
+
)
|
|
12722
|
+
}).describe(
|
|
12723
|
+
"\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."
|
|
12724
|
+
);
|
|
12725
|
+
var proposeCellEditSchema = z7.object({
|
|
12726
|
+
path: z7.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
12727
|
+
edits: z7.array(cellEditItemSchema).min(1).describe(
|
|
12728
|
+
"\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"
|
|
12729
|
+
),
|
|
12730
|
+
summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12731
|
+
});
|
|
12732
|
+
function cellOwnText(cell) {
|
|
12733
|
+
return cell.paragraphs.map((p) => p.text).join("");
|
|
12734
|
+
}
|
|
12735
|
+
function cellAt(table, row, col) {
|
|
12736
|
+
return table.cellByAnchor.get(`${row},${col}`);
|
|
12737
|
+
}
|
|
12738
|
+
function buildCellWriteSplices(cell, newText) {
|
|
12739
|
+
const paras = cell.paragraphs;
|
|
12740
|
+
if (paras.length === 0) return null;
|
|
12741
|
+
const firstParagraph = paras[0];
|
|
12742
|
+
if (firstParagraph === void 0) return null;
|
|
12743
|
+
const first = buildParagraphSplices(firstParagraph, newText, void 0);
|
|
12744
|
+
if (first === null) return null;
|
|
12745
|
+
const splices = [...first];
|
|
12746
|
+
for (let i = 1; i < paras.length; i++) {
|
|
12747
|
+
const para = paras[i];
|
|
12748
|
+
if (para === void 0) continue;
|
|
12749
|
+
const clear = buildParagraphSplices(para, "", void 0);
|
|
12750
|
+
if (clear !== null) splices.push(...clear);
|
|
12751
|
+
}
|
|
12752
|
+
return splices;
|
|
12753
|
+
}
|
|
12754
|
+
function applyCellEditsToSectionXml(xml, edits) {
|
|
12755
|
+
const scan = scanSectionXml2(xml, 0);
|
|
12756
|
+
const tables = scan.tables;
|
|
12757
|
+
const results = edits.map(() => ({ success: false }));
|
|
12758
|
+
const allSplices = [];
|
|
12759
|
+
for (let ei = 0; ei < edits.length; ei++) {
|
|
12760
|
+
const edit = edits[ei];
|
|
12761
|
+
if (edit === void 0) continue;
|
|
12762
|
+
const table = tables[edit.tableIndex];
|
|
12763
|
+
if (table === void 0) {
|
|
12764
|
+
results[ei] = {
|
|
12765
|
+
success: false,
|
|
12766
|
+
error: `\uD45C ${edit.tableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 \uC139\uC158\uC5D0 \uCD1D ${tables.length}\uAC1C\uC758 \uCD5C\uC0C1\uC704 \uD45C\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`
|
|
12767
|
+
};
|
|
12768
|
+
continue;
|
|
12769
|
+
}
|
|
12770
|
+
const cell = cellAt(table, edit.row, edit.col);
|
|
12771
|
+
if (cell === void 0) {
|
|
12772
|
+
results[ei] = {
|
|
12773
|
+
success: false,
|
|
12774
|
+
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.`
|
|
12775
|
+
};
|
|
12776
|
+
continue;
|
|
12777
|
+
}
|
|
12778
|
+
const currentText = cellOwnText(cell);
|
|
12779
|
+
if (edit.expectedText !== void 0 && edit.expectedText !== currentText) {
|
|
12780
|
+
results[ei] = {
|
|
12781
|
+
success: false,
|
|
12782
|
+
oldText: currentText,
|
|
12783
|
+
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.`
|
|
12784
|
+
};
|
|
12785
|
+
continue;
|
|
12786
|
+
}
|
|
12787
|
+
const splices = buildCellWriteSplices(cell, edit.newText);
|
|
12788
|
+
if (splices === null) {
|
|
12789
|
+
results[ei] = {
|
|
12790
|
+
success: false,
|
|
12791
|
+
oldText: currentText,
|
|
12792
|
+
error: `\uC140 (\uD45C ${edit.tableIndex}, \uD589 ${edit.row}, \uC5F4 ${edit.col})\uC5D0 \uD14D\uC2A4\uD2B8 \uB7F0\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uBB38\uB2E8/\uB7F0\uC774 \uC5C6\uC5B4 \uAC12\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC140 \uAD6C\uC870\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
12793
|
+
};
|
|
12794
|
+
continue;
|
|
12795
|
+
}
|
|
12796
|
+
allSplices.push(...splices);
|
|
12797
|
+
results[ei] = { success: true, oldText: currentText };
|
|
12798
|
+
}
|
|
12799
|
+
if (results.some((r) => !r.success)) {
|
|
12800
|
+
return { newXml: xml, results };
|
|
12801
|
+
}
|
|
12802
|
+
return { newXml: applySplices(xml, allSplices), results };
|
|
12803
|
+
}
|
|
12804
|
+
function parseAnchor(key) {
|
|
12805
|
+
const [r, c] = key.split(",");
|
|
12806
|
+
return { row: Number(r), col: Number(c) };
|
|
12807
|
+
}
|
|
12808
|
+
function findLabelMatchesInScan(tables, trimmedLabel, startIdx, endIdx) {
|
|
12809
|
+
const out = [];
|
|
12810
|
+
for (let ti = startIdx; ti <= endIdx; ti++) {
|
|
12811
|
+
const table = tables[ti];
|
|
12812
|
+
if (table === void 0) continue;
|
|
12813
|
+
for (const [key, cell] of table.cellByAnchor) {
|
|
12814
|
+
if (cellOwnText(cell).trim() === trimmedLabel) {
|
|
12815
|
+
const { row, col } = parseAnchor(key);
|
|
12816
|
+
out.push({ tableIndex: ti, row, col, colSpan: cell.colSpan, rowSpan: cell.rowSpan });
|
|
12817
|
+
}
|
|
12818
|
+
}
|
|
12819
|
+
}
|
|
12820
|
+
return out;
|
|
12821
|
+
}
|
|
12822
|
+
function targetFromLabel(m, direction) {
|
|
12823
|
+
return direction === "right" ? { row: m.row, col: m.col + m.colSpan } : { row: m.row + m.rowSpan, col: m.col };
|
|
12824
|
+
}
|
|
12825
|
+
var SECTION_RE = /^Contents\/section\d+\.xml$/;
|
|
12826
|
+
async function readSections(zip) {
|
|
12827
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => SECTION_RE.test(name)).sort();
|
|
12828
|
+
const out = [];
|
|
12829
|
+
let globalOffset = 0;
|
|
12830
|
+
for (const name of sectionFiles) {
|
|
12831
|
+
const entry = zip.file(name);
|
|
12832
|
+
const xml = entry ? await entry.async("string") : "";
|
|
12833
|
+
const tblCount = scanSectionXml2(xml, 0).tables.length;
|
|
12834
|
+
out.push({ name, xml, tblCount, globalOffset });
|
|
12835
|
+
globalOffset += tblCount;
|
|
12836
|
+
}
|
|
12837
|
+
return out;
|
|
12838
|
+
}
|
|
12839
|
+
async function applyEditsToHwpx(hwpxBuffer, edits) {
|
|
12840
|
+
const zip = await import_jszip4.default.loadAsync(hwpxBuffer);
|
|
12841
|
+
const sections = await readSections(zip);
|
|
12842
|
+
const totalTables = sections.reduce((a, s) => a + s.tblCount, 0);
|
|
12843
|
+
const sectionEdits = sections.map(() => []);
|
|
12844
|
+
for (let ei = 0; ei < edits.length; ei++) {
|
|
12845
|
+
const edit = edits[ei];
|
|
12846
|
+
if (edit === void 0) continue;
|
|
12847
|
+
for (let si = 0; si < sections.length; si++) {
|
|
12848
|
+
const s = sections[si];
|
|
12849
|
+
if (s === void 0) continue;
|
|
12850
|
+
if (edit.tableIndex >= s.globalOffset && edit.tableIndex < s.globalOffset + s.tblCount) {
|
|
12851
|
+
sectionEdits[si]?.push({
|
|
12852
|
+
tableIndex: edit.tableIndex - s.globalOffset,
|
|
12853
|
+
row: edit.row,
|
|
12854
|
+
col: edit.col,
|
|
12855
|
+
newText: edit.newText,
|
|
12856
|
+
expectedText: edit.expectedText,
|
|
12857
|
+
originalEditIdx: ei
|
|
12858
|
+
});
|
|
12859
|
+
break;
|
|
12860
|
+
}
|
|
12861
|
+
}
|
|
12862
|
+
}
|
|
12863
|
+
const allResults = edits.map((edit, ei) => ({
|
|
12864
|
+
success: false,
|
|
12865
|
+
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.`
|
|
12866
|
+
}));
|
|
12867
|
+
const newSectionXmls = sections.map((s) => s.xml);
|
|
12868
|
+
for (let si = 0; si < sections.length; si++) {
|
|
12869
|
+
const sEdits = sectionEdits[si] ?? [];
|
|
12870
|
+
if (sEdits.length === 0) continue;
|
|
12871
|
+
const srcXml = sections[si]?.xml ?? "";
|
|
12872
|
+
const { newXml, results } = applyCellEditsToSectionXml(srcXml, sEdits);
|
|
12873
|
+
newSectionXmls[si] = newXml;
|
|
12874
|
+
for (let i = 0; i < sEdits.length; i++) {
|
|
12875
|
+
const sEdit = sEdits[i];
|
|
12876
|
+
const res = results[i];
|
|
12877
|
+
if (sEdit && res) allResults[sEdit.originalEditIdx] = res;
|
|
12878
|
+
}
|
|
12879
|
+
}
|
|
12880
|
+
if (allResults.some((r) => !r.success)) {
|
|
12881
|
+
return { buffer: hwpxBuffer, results: allResults };
|
|
12882
|
+
}
|
|
12883
|
+
const out = new import_jszip4.default();
|
|
12884
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
12885
|
+
if (mimetypeEntry) {
|
|
12886
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
12887
|
+
}
|
|
12888
|
+
const nameToIdx = new Map(sections.map((s, i) => [s.name, i]));
|
|
12889
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
12890
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
12891
|
+
const idx = nameToIdx.get(name);
|
|
12892
|
+
if (idx !== void 0) {
|
|
12893
|
+
out.file(name, newSectionXmls[idx] ?? "");
|
|
12894
|
+
} else {
|
|
12895
|
+
out.file(name, await entry.async("uint8array"));
|
|
12896
|
+
}
|
|
12897
|
+
}
|
|
12898
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
12899
|
+
return { buffer: new Uint8Array(buf), results: allResults };
|
|
12900
|
+
}
|
|
12901
|
+
function resolveLabelAcrossSections(sections, label, direction, scopedGlobalTableIndex) {
|
|
12902
|
+
const trimmedLabel = label.trim();
|
|
12903
|
+
const matches = [];
|
|
12904
|
+
for (const s of sections) {
|
|
12905
|
+
const tables = scanSectionXml2(s.xml, 0).tables;
|
|
12906
|
+
let localStart = 0;
|
|
12907
|
+
let localEnd = s.tblCount - 1;
|
|
12908
|
+
if (scopedGlobalTableIndex !== void 0) {
|
|
12909
|
+
const localIdx2 = scopedGlobalTableIndex - s.globalOffset;
|
|
12910
|
+
if (localIdx2 < 0 || localIdx2 >= s.tblCount) continue;
|
|
12911
|
+
localStart = localIdx2;
|
|
12912
|
+
localEnd = localIdx2;
|
|
12913
|
+
}
|
|
12914
|
+
for (const m of findLabelMatchesInScan(tables, trimmedLabel, localStart, localEnd)) {
|
|
12915
|
+
matches.push({
|
|
12916
|
+
globalTableIndex: s.globalOffset + m.tableIndex,
|
|
12917
|
+
row: m.row,
|
|
12918
|
+
col: m.col,
|
|
12919
|
+
colSpan: m.colSpan,
|
|
12920
|
+
rowSpan: m.rowSpan
|
|
12921
|
+
});
|
|
12922
|
+
}
|
|
12923
|
+
}
|
|
12924
|
+
if (matches.length === 0) {
|
|
12925
|
+
const scope = scopedGlobalTableIndex !== void 0 ? `\uD45C ${scopedGlobalTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
12926
|
+
return {
|
|
12927
|
+
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.`
|
|
12928
|
+
};
|
|
12929
|
+
}
|
|
12930
|
+
if (matches.length > 1) {
|
|
12931
|
+
const locs = matches.map((m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.row}, \uC5F4 ${m.col})`).join(", ");
|
|
12932
|
+
return {
|
|
12933
|
+
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.`
|
|
12934
|
+
};
|
|
12935
|
+
}
|
|
12936
|
+
const match = matches[0];
|
|
12937
|
+
const target = targetFromLabel(match, direction);
|
|
12938
|
+
const section = sections.find(
|
|
12939
|
+
(s) => match.globalTableIndex >= s.globalOffset && match.globalTableIndex < s.globalOffset + s.tblCount
|
|
12940
|
+
);
|
|
12941
|
+
if (section === void 0) {
|
|
12942
|
+
return { error: `\uD45C ${match.globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
12943
|
+
}
|
|
12944
|
+
const localIdx = match.globalTableIndex - section.globalOffset;
|
|
12945
|
+
const table = scanSectionXml2(section.xml, 0).tables[localIdx];
|
|
12946
|
+
if (table === void 0 || cellAt(table, target.row, target.col) === void 0) {
|
|
12947
|
+
const dirLabel = direction === "right" ? "\uC624\uB978\uCABD" : "\uC544\uB798";
|
|
12948
|
+
return {
|
|
12949
|
+
error: `\uB808\uC774\uBE14 "${label}" (\uD45C ${match.globalTableIndex}, \uD589 ${match.row}, \uC5F4 ${match.col})\uC758 ${dirLabel} \uC140 (\uD589 ${target.row}, \uC5F4 ${target.col})\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. direction \uB610\uB294 \uC88C\uD45C\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
12950
|
+
};
|
|
12951
|
+
}
|
|
12952
|
+
return { tableIndex: match.globalTableIndex, row: target.row, col: target.col };
|
|
12953
|
+
}
|
|
12954
|
+
var proposeCellEditTool = {
|
|
12955
|
+
name: "propose_cell_edit",
|
|
12956
|
+
description: "HWPX \uBB38\uC11C\uC758 \uD45C \uC140 \uB0B4\uC6A9\uC744 \uC88C\uD45C/\uB808\uC774\uBE14 \uC9C0\uC815\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\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.",
|
|
12957
|
+
inputSchema: proposeCellEditSchema,
|
|
12958
|
+
requiresApproval: true,
|
|
12959
|
+
propose: async ({
|
|
12960
|
+
input,
|
|
12961
|
+
ctx
|
|
12962
|
+
}) => {
|
|
12963
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12964
|
+
const ext = extname7(safePath).toLowerCase();
|
|
12965
|
+
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
12966
|
+
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.`;
|
|
12967
|
+
}
|
|
12968
|
+
let originalBuffer;
|
|
12969
|
+
try {
|
|
12970
|
+
originalBuffer = await readFile7(safePath);
|
|
12971
|
+
} catch {
|
|
12972
|
+
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.`;
|
|
12973
|
+
}
|
|
12974
|
+
const originalBytes = new Uint8Array(originalBuffer.buffer);
|
|
12975
|
+
const structuralGuard = hwpStructuralGuard(ext, originalBytes);
|
|
12976
|
+
if (structuralGuard !== null) {
|
|
12977
|
+
return structuralGuard;
|
|
12978
|
+
}
|
|
12979
|
+
if (!isZipBinary(originalBytes)) {
|
|
12980
|
+
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.";
|
|
12981
|
+
}
|
|
12982
|
+
const zipForLabel = await import_jszip4.default.loadAsync(new Uint8Array(originalBuffer.buffer));
|
|
12983
|
+
const sections = await readSections(zipForLabel);
|
|
12984
|
+
const resolvedEdits = [];
|
|
12985
|
+
const resolveErrors = [];
|
|
12986
|
+
for (let i = 0; i < input.edits.length; i++) {
|
|
12987
|
+
const e = input.edits[i];
|
|
12988
|
+
if (!e) continue;
|
|
12989
|
+
if (e.label !== void 0 && (e.row !== void 0 || e.col !== void 0)) {
|
|
12990
|
+
resolveErrors.push(
|
|
12991
|
+
`\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.`
|
|
12992
|
+
);
|
|
12993
|
+
} else if (e.label !== void 0) {
|
|
12994
|
+
const direction = e.direction ?? "right";
|
|
12995
|
+
const resolved = resolveLabelAcrossSections(sections, e.label, direction, e.tableIndex);
|
|
12996
|
+
if ("error" in resolved) {
|
|
12997
|
+
resolveErrors.push(`\uD3B8\uC9D1 #${i + 1} (\uB808\uC774\uBE14 "${e.label}"): ${resolved.error}`);
|
|
12998
|
+
} else {
|
|
12999
|
+
resolvedEdits.push({
|
|
13000
|
+
tableIndex: resolved.tableIndex,
|
|
13001
|
+
row: resolved.row,
|
|
13002
|
+
col: resolved.col,
|
|
13003
|
+
newText: e.newText,
|
|
13004
|
+
expectedText: e.expectedText,
|
|
13005
|
+
label: e.label
|
|
13006
|
+
});
|
|
13007
|
+
}
|
|
13008
|
+
} else if (e.tableIndex !== void 0 && e.row !== void 0 && e.col !== void 0) {
|
|
13009
|
+
resolvedEdits.push({
|
|
13010
|
+
tableIndex: e.tableIndex,
|
|
13011
|
+
row: e.row,
|
|
13012
|
+
col: e.col,
|
|
13013
|
+
newText: e.newText,
|
|
13014
|
+
expectedText: e.expectedText
|
|
13015
|
+
});
|
|
13016
|
+
} else {
|
|
13017
|
+
resolveErrors.push(
|
|
13018
|
+
`\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.`
|
|
13019
|
+
);
|
|
13020
|
+
}
|
|
13021
|
+
}
|
|
13022
|
+
if (resolveErrors.length > 0) {
|
|
13023
|
+
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.
|
|
13024
|
+
${resolveErrors.join("\n")}`;
|
|
13025
|
+
}
|
|
13026
|
+
const editRequests = resolvedEdits.map((e) => ({
|
|
13027
|
+
tableIndex: e.tableIndex,
|
|
13028
|
+
row: e.row,
|
|
13029
|
+
col: e.col,
|
|
13030
|
+
newText: e.newText,
|
|
13031
|
+
expectedText: e.expectedText
|
|
13032
|
+
}));
|
|
13033
|
+
const { buffer: newBuffer, results } = await applyEditsToHwpx(
|
|
13034
|
+
new Uint8Array(originalBuffer.buffer),
|
|
13035
|
+
editRequests
|
|
13036
|
+
);
|
|
13037
|
+
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
13038
|
+
if (failedResults.length > 0) {
|
|
13039
|
+
const messages = failedResults.map(({ r, i }) => {
|
|
13040
|
+
const e = resolvedEdits[i];
|
|
13041
|
+
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
13042
|
+
return `\uD3B8\uC9D1 #${i + 1} (${label}\uD45C ${e?.tableIndex ?? "?"}, \uD589 ${e?.row ?? "?"}, \uC5F4 ${e?.col ?? "?"}): ${r.error}`;
|
|
13043
|
+
});
|
|
13044
|
+
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.
|
|
13045
|
+
${messages.join("\n")}`;
|
|
13046
|
+
}
|
|
13047
|
+
const diffLines = ["| \uD45C\xB7\uC140 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
13048
|
+
for (let i = 0; i < resolvedEdits.length; i++) {
|
|
13049
|
+
const e = resolvedEdits[i];
|
|
13050
|
+
if (!e) continue;
|
|
13051
|
+
const oldText = results[i]?.oldText ?? "";
|
|
13052
|
+
const addr = e.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 #${e.tableIndex} (${e.row},${e.col})` : `#${e.tableIndex} (${e.row},${e.col})`;
|
|
13053
|
+
diffLines.push(`| ${addr} | ${oldText} | ${e.newText} |`);
|
|
12889
13054
|
}
|
|
12890
13055
|
const diff = diffLines.join("\n");
|
|
12891
13056
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
@@ -12894,7 +13059,7 @@ ${failMessages.join("\n")}`;
|
|
|
12894
13059
|
return {
|
|
12895
13060
|
proposal: {
|
|
12896
13061
|
id: proposalId,
|
|
12897
|
-
kind: "
|
|
13062
|
+
kind: "cell-edit",
|
|
12898
13063
|
targetPath: outputPath,
|
|
12899
13064
|
stagedPath,
|
|
12900
13065
|
summary: input.summary,
|
|
@@ -12911,92 +13076,6 @@ ${failMessages.join("\n")}`;
|
|
|
12911
13076
|
};
|
|
12912
13077
|
}
|
|
12913
13078
|
};
|
|
12914
|
-
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
12915
|
-
".hwp",
|
|
12916
|
-
".hwpx",
|
|
12917
|
-
".hwpml",
|
|
12918
|
-
".docx",
|
|
12919
|
-
".doc",
|
|
12920
|
-
".xlsx",
|
|
12921
|
-
".xls",
|
|
12922
|
-
".pdf",
|
|
12923
|
-
".pptx",
|
|
12924
|
-
".ppt",
|
|
12925
|
-
".md",
|
|
12926
|
-
".txt"
|
|
12927
|
-
]);
|
|
12928
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12929
|
-
"node_modules",
|
|
12930
|
-
".git",
|
|
12931
|
-
".DS_Store",
|
|
12932
|
-
"__pycache__",
|
|
12933
|
-
"dist",
|
|
12934
|
-
"build",
|
|
12935
|
-
".next"
|
|
12936
|
-
]);
|
|
12937
|
-
var MAX_DEPTH = 4;
|
|
12938
|
-
var MAX_FILES = 500;
|
|
12939
|
-
var listFilesSchema = z6.object({
|
|
12940
|
-
dir: z6.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
|
|
12941
|
-
});
|
|
12942
|
-
async function collectFiles(dir, cwd, depth, entries) {
|
|
12943
|
-
if (depth > MAX_DEPTH || entries.length >= MAX_FILES) return;
|
|
12944
|
-
let items;
|
|
12945
|
-
try {
|
|
12946
|
-
items = await readdir3(dir);
|
|
12947
|
-
} catch {
|
|
12948
|
-
return;
|
|
12949
|
-
}
|
|
12950
|
-
for (const item of items) {
|
|
12951
|
-
if (entries.length >= MAX_FILES) break;
|
|
12952
|
-
if (item.startsWith(".")) continue;
|
|
12953
|
-
if (SKIP_DIRS.has(item)) continue;
|
|
12954
|
-
const fullPath = join4(dir, item);
|
|
12955
|
-
let info;
|
|
12956
|
-
try {
|
|
12957
|
-
info = await stat3(fullPath);
|
|
12958
|
-
} catch {
|
|
12959
|
-
continue;
|
|
12960
|
-
}
|
|
12961
|
-
const relPath = relative2(cwd, fullPath);
|
|
12962
|
-
const ext = extname6(item).toLowerCase();
|
|
12963
|
-
const isDoc = DOC_EXTENSIONS.has(ext);
|
|
12964
|
-
if (info.isDirectory()) {
|
|
12965
|
-
entries.push({ path: relPath + "/", isDir: true, isDoc: false });
|
|
12966
|
-
await collectFiles(fullPath, cwd, depth + 1, entries);
|
|
12967
|
-
} else if (info.isFile()) {
|
|
12968
|
-
entries.push({ path: relPath, isDir: false, isDoc });
|
|
12969
|
-
}
|
|
12970
|
-
}
|
|
12971
|
-
}
|
|
12972
|
-
var listFilesTool = {
|
|
12973
|
-
name: "list_files",
|
|
12974
|
-
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.",
|
|
12975
|
-
inputSchema: listFilesSchema,
|
|
12976
|
-
requiresApproval: false,
|
|
12977
|
-
execute: async ({
|
|
12978
|
-
input,
|
|
12979
|
-
ctx
|
|
12980
|
-
}) => {
|
|
12981
|
-
const targetDir = input.dir ? await resolveSafePath(ctx.cwd, input.dir) : ctx.cwd;
|
|
12982
|
-
const entries = [];
|
|
12983
|
-
await collectFiles(targetDir, ctx.cwd, 0, entries);
|
|
12984
|
-
if (entries.length === 0) {
|
|
12985
|
-
return "\uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
12986
|
-
}
|
|
12987
|
-
const docs = entries.filter((e) => e.isDoc);
|
|
12988
|
-
const dirs = entries.filter((e) => e.isDir);
|
|
12989
|
-
const others = entries.filter((e) => !e.isDoc && !e.isDir);
|
|
12990
|
-
const allSorted = [...docs, ...dirs, ...others];
|
|
12991
|
-
const lines = allSorted.map((e) => {
|
|
12992
|
-
const icon = e.isDir ? "\u{1F4C1}" : e.isDoc ? "\u{1F4C4}" : " ";
|
|
12993
|
-
return `${icon} ${e.path}`;
|
|
12994
|
-
});
|
|
12995
|
-
const truncateNotice = entries.length >= MAX_FILES ? `
|
|
12996
|
-
(\uCD5C\uB300 ${MAX_FILES}\uAC1C\uAE4C\uC9C0 \uD45C\uC2DC\uB429\uB2C8\uB2E4. \uB354 \uC881\uC740 \uBC94\uC704\uB97C \uC9C0\uC815\uD558\uC138\uC694.)` : "";
|
|
12997
|
-
return lines.join("\n") + truncateNotice;
|
|
12998
|
-
}
|
|
12999
|
-
};
|
|
13000
13079
|
var HEADING_LEVELS = {
|
|
13001
13080
|
1: HeadingLevel.HEADING_1,
|
|
13002
13081
|
2: HeadingLevel.HEADING_2,
|
|
@@ -13119,10 +13198,10 @@ async function markdownToDocx(markdown) {
|
|
|
13119
13198
|
});
|
|
13120
13199
|
return Packer.toBuffer(doc);
|
|
13121
13200
|
}
|
|
13122
|
-
var proposeEditSchema =
|
|
13123
|
-
path:
|
|
13124
|
-
newMarkdown:
|
|
13125
|
-
summary:
|
|
13201
|
+
var proposeEditSchema = z8.object({
|
|
13202
|
+
path: z8.string().describe("\uC218\uC815\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13203
|
+
newMarkdown: z8.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"),
|
|
13204
|
+
summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13126
13205
|
});
|
|
13127
13206
|
var proposeEditTool = {
|
|
13128
13207
|
name: "propose_edit",
|
|
@@ -13134,10 +13213,10 @@ var proposeEditTool = {
|
|
|
13134
13213
|
ctx
|
|
13135
13214
|
}) => {
|
|
13136
13215
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13137
|
-
const ext =
|
|
13216
|
+
const ext = extname8(safePath).toLowerCase();
|
|
13138
13217
|
let originalBuffer;
|
|
13139
13218
|
try {
|
|
13140
|
-
originalBuffer = await
|
|
13219
|
+
originalBuffer = await readFile8(safePath);
|
|
13141
13220
|
} catch {
|
|
13142
13221
|
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.`;
|
|
13143
13222
|
}
|
|
@@ -13215,19 +13294,52 @@ ${diff}`;
|
|
|
13215
13294
|
};
|
|
13216
13295
|
}
|
|
13217
13296
|
};
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13297
|
+
function collectParasInDocOrder(scan) {
|
|
13298
|
+
const out = [...scan.bodyParagraphs];
|
|
13299
|
+
const walkTable = (t) => {
|
|
13300
|
+
for (const row of t.rows) {
|
|
13301
|
+
for (const cell of row) {
|
|
13302
|
+
out.push(...cell.paragraphs);
|
|
13303
|
+
for (const nested of cell.tables) walkTable(nested);
|
|
13304
|
+
}
|
|
13305
|
+
}
|
|
13306
|
+
};
|
|
13307
|
+
for (const t of scan.tables) walkTable(t);
|
|
13308
|
+
out.push(...scan.excludedParagraphs);
|
|
13309
|
+
for (const t of scan.orphanTables) walkTable(t);
|
|
13310
|
+
return out.sort((a, b) => a.start - b.start);
|
|
13311
|
+
}
|
|
13312
|
+
function applyRangeSplicesToSection(xml, rangeFn) {
|
|
13313
|
+
const scan = scanSectionXml3(xml, 0);
|
|
13314
|
+
const paras = collectParasInDocOrder(scan);
|
|
13315
|
+
const splices = [];
|
|
13316
|
+
let count = 0;
|
|
13317
|
+
for (const p of paras) {
|
|
13318
|
+
const ranges = rangeFn(p.text);
|
|
13319
|
+
for (const r of ranges) {
|
|
13320
|
+
if (r.end <= r.start) continue;
|
|
13321
|
+
const s = buildRangeSplices(p, xml, r.start, r.end, r.replacement);
|
|
13322
|
+
if (s === null) return null;
|
|
13323
|
+
splices.push(...s);
|
|
13324
|
+
count++;
|
|
13325
|
+
}
|
|
13326
|
+
}
|
|
13327
|
+
if (count === 0) return { xml, count: 0 };
|
|
13328
|
+
return { xml: applySplices2(xml, splices), count };
|
|
13329
|
+
}
|
|
13330
|
+
var proposeFindReplaceSchema = z9.object({
|
|
13331
|
+
path: z9.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13332
|
+
find: z9.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
13333
|
+
replace: z9.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
|
|
13334
|
+
caseSensitive: z9.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
|
|
13335
|
+
all: z9.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"),
|
|
13336
|
+
summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13225
13337
|
});
|
|
13226
13338
|
var MAX_DIFF_SAMPLES = 20;
|
|
13227
|
-
function
|
|
13339
|
+
function escapeXml2(text3) {
|
|
13228
13340
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
13229
13341
|
}
|
|
13230
|
-
function
|
|
13342
|
+
function unescapeXml(text3) {
|
|
13231
13343
|
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
13232
13344
|
}
|
|
13233
13345
|
function makeChangeSnippet(before, after, ctx = 24) {
|
|
@@ -13251,7 +13363,7 @@ function collectChangedSnippets(beforeXml, afterXml, maxSamples) {
|
|
|
13251
13363
|
const n = Math.min(beforeNodes.length, afterNodes.length);
|
|
13252
13364
|
for (let i = 0; i < n && out.length < maxSamples; i++) {
|
|
13253
13365
|
if (beforeNodes[i] !== afterNodes[i]) {
|
|
13254
|
-
const snip = makeChangeSnippet(
|
|
13366
|
+
const snip = makeChangeSnippet(unescapeXml(beforeNodes[i]), unescapeXml(afterNodes[i]));
|
|
13255
13367
|
out.push(snip);
|
|
13256
13368
|
}
|
|
13257
13369
|
}
|
|
@@ -13261,8 +13373,8 @@ function replaceInSectionXml(xml, find, replace, caseSensitive, replaceAll, alre
|
|
|
13261
13373
|
if (!replaceAll && alreadyReplaced > 0) {
|
|
13262
13374
|
return { xml, count: 0 };
|
|
13263
13375
|
}
|
|
13264
|
-
const escapedFind =
|
|
13265
|
-
const escapedReplace =
|
|
13376
|
+
const escapedFind = escapeXml2(find);
|
|
13377
|
+
const escapedReplace = escapeXml2(replace);
|
|
13266
13378
|
if (escapedFind.length === 0) {
|
|
13267
13379
|
return { xml, count: 0 };
|
|
13268
13380
|
}
|
|
@@ -13333,8 +13445,33 @@ function countOccurrences(str, sub) {
|
|
|
13333
13445
|
}
|
|
13334
13446
|
return count;
|
|
13335
13447
|
}
|
|
13448
|
+
function replaceSectionViaSplices(xml, find, replace, caseSensitive, replaceAll, alreadyReplaced) {
|
|
13449
|
+
if (find.length === 0) return { xml, count: 0 };
|
|
13450
|
+
if (!replaceAll && alreadyReplaced > 0) return { xml, count: 0 };
|
|
13451
|
+
const scan = scanSectionXml4(xml, 0);
|
|
13452
|
+
const paras = collectParasInDocOrder(scan);
|
|
13453
|
+
const needle = caseSensitive ? find : find.toLowerCase();
|
|
13454
|
+
const splices = [];
|
|
13455
|
+
let count = 0;
|
|
13456
|
+
for (const p of paras) {
|
|
13457
|
+
if (!replaceAll && alreadyReplaced + count >= 1) break;
|
|
13458
|
+
const hay = caseSensitive ? p.text : p.text.toLowerCase();
|
|
13459
|
+
let idx = hay.indexOf(needle);
|
|
13460
|
+
while (idx !== -1) {
|
|
13461
|
+
if (!replaceAll && alreadyReplaced + count >= 1) break;
|
|
13462
|
+
const s = buildRangeSplices2(p, xml, idx, idx + find.length, replace);
|
|
13463
|
+
if (s === null) return null;
|
|
13464
|
+
splices.push(...s);
|
|
13465
|
+
count++;
|
|
13466
|
+
if (!replaceAll) break;
|
|
13467
|
+
idx = hay.indexOf(needle, idx + needle.length);
|
|
13468
|
+
}
|
|
13469
|
+
}
|
|
13470
|
+
if (count === 0) return { xml, count: 0 };
|
|
13471
|
+
return { xml: applySplices3(xml, splices), count };
|
|
13472
|
+
}
|
|
13336
13473
|
async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
|
|
13337
|
-
const zip = await
|
|
13474
|
+
const zip = await import_jszip5.default.loadAsync(hwpxBuffer);
|
|
13338
13475
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13339
13476
|
const sectionXmls = [];
|
|
13340
13477
|
for (const sf of sectionFiles) {
|
|
@@ -13346,7 +13483,9 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13346
13483
|
const newSectionXmls = [];
|
|
13347
13484
|
for (let si = 0; si < sectionFiles.length; si++) {
|
|
13348
13485
|
const srcXml = sectionXmls[si] ?? "";
|
|
13349
|
-
|
|
13486
|
+
let newXml;
|
|
13487
|
+
let count;
|
|
13488
|
+
const spliced = replaceSectionViaSplices(
|
|
13350
13489
|
srcXml,
|
|
13351
13490
|
find,
|
|
13352
13491
|
replace,
|
|
@@ -13354,6 +13493,21 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13354
13493
|
replaceAll,
|
|
13355
13494
|
totalCount
|
|
13356
13495
|
);
|
|
13496
|
+
if (spliced !== null) {
|
|
13497
|
+
newXml = spliced.xml;
|
|
13498
|
+
count = spliced.count;
|
|
13499
|
+
} else {
|
|
13500
|
+
const fallback = replaceInSectionXml(
|
|
13501
|
+
srcXml,
|
|
13502
|
+
find,
|
|
13503
|
+
replace,
|
|
13504
|
+
caseSensitive,
|
|
13505
|
+
replaceAll,
|
|
13506
|
+
totalCount
|
|
13507
|
+
);
|
|
13508
|
+
newXml = fallback.xml;
|
|
13509
|
+
count = fallback.count;
|
|
13510
|
+
}
|
|
13357
13511
|
newSectionXmls.push(newXml);
|
|
13358
13512
|
totalCount += count;
|
|
13359
13513
|
if (!replaceAll && totalCount >= 1) {
|
|
@@ -13378,7 +13532,7 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13378
13532
|
samples.push(snip);
|
|
13379
13533
|
}
|
|
13380
13534
|
}
|
|
13381
|
-
const out = new
|
|
13535
|
+
const out = new import_jszip5.default();
|
|
13382
13536
|
const mimetypeEntry = zip.file("mimetype");
|
|
13383
13537
|
if (mimetypeEntry) {
|
|
13384
13538
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -13409,13 +13563,13 @@ var proposeFindReplaceTool = {
|
|
|
13409
13563
|
ctx
|
|
13410
13564
|
}) => {
|
|
13411
13565
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13412
|
-
const ext =
|
|
13566
|
+
const ext = extname9(safePath).toLowerCase();
|
|
13413
13567
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13414
13568
|
return `\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
13415
13569
|
}
|
|
13416
13570
|
let originalBuf;
|
|
13417
13571
|
try {
|
|
13418
|
-
originalBuf = await
|
|
13572
|
+
originalBuf = await readFile9(safePath);
|
|
13419
13573
|
} catch {
|
|
13420
13574
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13421
13575
|
}
|
|
@@ -13428,7 +13582,7 @@ var proposeFindReplaceTool = {
|
|
|
13428
13582
|
if (structuralGuard !== null) {
|
|
13429
13583
|
return structuralGuard;
|
|
13430
13584
|
}
|
|
13431
|
-
if (
|
|
13585
|
+
if (!isZipBinary(originalBytes)) {
|
|
13432
13586
|
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. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
13433
13587
|
}
|
|
13434
13588
|
let newBytes;
|
|
@@ -13454,7 +13608,7 @@ var proposeFindReplaceTool = {
|
|
|
13454
13608
|
}
|
|
13455
13609
|
const warnings = [];
|
|
13456
13610
|
try {
|
|
13457
|
-
const exportedResult = await
|
|
13611
|
+
const exportedResult = await parse(newBytes.buffer);
|
|
13458
13612
|
if (exportedResult.success) {
|
|
13459
13613
|
const exportedMd = exportedResult.markdown;
|
|
13460
13614
|
const normAfter = input.caseSensitive ?? false ? exportedMd : exportedMd.toLowerCase();
|
|
@@ -13512,10 +13666,10 @@ var proposeFindReplaceTool = {
|
|
|
13512
13666
|
};
|
|
13513
13667
|
}
|
|
13514
13668
|
};
|
|
13515
|
-
var proposeFormFillSchema =
|
|
13516
|
-
path:
|
|
13517
|
-
fields:
|
|
13518
|
-
summary:
|
|
13669
|
+
var proposeFormFillSchema = z10.object({
|
|
13670
|
+
path: z10.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13671
|
+
fields: z10.record(z10.string(), z10.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"),
|
|
13672
|
+
summary: z10.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13519
13673
|
});
|
|
13520
13674
|
var proposeFormFillTool = {
|
|
13521
13675
|
name: "propose_form_fill",
|
|
@@ -13527,13 +13681,13 @@ var proposeFormFillTool = {
|
|
|
13527
13681
|
ctx
|
|
13528
13682
|
}) => {
|
|
13529
13683
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13530
|
-
const ext =
|
|
13684
|
+
const ext = extname10(safePath).toLowerCase();
|
|
13531
13685
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13532
13686
|
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.`;
|
|
13533
13687
|
}
|
|
13534
13688
|
let originalBuffer;
|
|
13535
13689
|
try {
|
|
13536
|
-
originalBuffer = await
|
|
13690
|
+
originalBuffer = await readFile10(safePath);
|
|
13537
13691
|
} catch {
|
|
13538
13692
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13539
13693
|
}
|
|
@@ -13544,7 +13698,7 @@ var proposeFormFillTool = {
|
|
|
13544
13698
|
if (structuralGuard !== null) {
|
|
13545
13699
|
return structuralGuard;
|
|
13546
13700
|
}
|
|
13547
|
-
const parseResult = await
|
|
13701
|
+
const parseResult = await parse(originalBuffer.buffer);
|
|
13548
13702
|
if (!parseResult.success) {
|
|
13549
13703
|
const msg = kordocErrorMessage(
|
|
13550
13704
|
parseResult.code,
|
|
@@ -13552,42 +13706,43 @@ var proposeFormFillTool = {
|
|
|
13552
13706
|
);
|
|
13553
13707
|
return `\uC624\uB958: ${msg}`;
|
|
13554
13708
|
}
|
|
13555
|
-
const
|
|
13556
|
-
const existingFields = new Map(
|
|
13557
|
-
const unknownLabels = Object.keys(input.fields).filter((label) => !existingFields.has(label));
|
|
13709
|
+
const formSchema = extractFormSchema(parseResult.blocks);
|
|
13710
|
+
const existingFields = new Map(formSchema.fields.map((f) => [f.label, f.value]));
|
|
13558
13711
|
const warnings = [];
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
|
|
13712
|
+
const fieldTypeKo = {
|
|
13713
|
+
text: "\uD14D\uC2A4\uD2B8",
|
|
13714
|
+
date: "\uB0A0\uC9DC",
|
|
13715
|
+
phone: "\uC804\uD654",
|
|
13716
|
+
email: "\uC774\uBA54\uC77C",
|
|
13717
|
+
amount: "\uAE08\uC561",
|
|
13718
|
+
checkbox: "\uCCB4\uD06C\uBC15\uC2A4",
|
|
13719
|
+
idnum: "\uC8FC\uBBFC\uBC88\uD638"
|
|
13720
|
+
};
|
|
13721
|
+
const availableHint = formSchema.fields.length > 0 ? ` \uCC44\uC6B8 \uC218 \uC788\uB294 \uD544\uB4DC: ${formSchema.fields.map((f) => `${f.label}(${fieldTypeKo[f.type] ?? f.type}${f.required ? ", \uD544\uC218" : ""})`).join(", ")}.` : "";
|
|
13565
13722
|
const diffLines = ["| \uB77C\uBCA8 | \uC774\uC804 \uAC12 | \uC0C8 \uAC12 |", "| --- | --- | --- |"];
|
|
13566
13723
|
for (const [label, newValue] of Object.entries(input.fields)) {
|
|
13567
13724
|
const oldValue = existingFields.get(label) ?? "(\uC5C6\uC74C)";
|
|
13568
13725
|
diffLines.push(`| ${label} | ${oldValue} | ${newValue} |`);
|
|
13569
|
-
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13570
|
-
const escapedOld = oldValue.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13571
|
-
if (oldValue !== "(\uC5C6\uC74C)") {
|
|
13572
|
-
newMarkdown = newMarkdown.replace(
|
|
13573
|
-
new RegExp(`(\\|\\s*${escapedLabel}\\s*\\|\\s*)${escapedOld}(\\s*\\|)`, "g"),
|
|
13574
|
-
`$1${newValue}$2`
|
|
13575
|
-
);
|
|
13576
|
-
}
|
|
13577
13726
|
}
|
|
13578
13727
|
const diff = diffLines.join("\n");
|
|
13579
|
-
const
|
|
13580
|
-
originalBuffer.buffer,
|
|
13728
|
+
const origAB = originalBuffer.buffer.slice(
|
|
13581
13729
|
originalBuffer.byteOffset,
|
|
13582
|
-
originalBuffer.byteLength
|
|
13730
|
+
originalBuffer.byteOffset + originalBuffer.byteLength
|
|
13583
13731
|
);
|
|
13584
|
-
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13732
|
+
let stagedData;
|
|
13733
|
+
try {
|
|
13734
|
+
const fillResult = await fillHwpx(origAB, input.fields);
|
|
13735
|
+
if (fillResult.unmatched.length > 0) {
|
|
13736
|
+
warnings.push(
|
|
13737
|
+
`\uB2E4\uC74C \uB77C\uBCA8\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${fillResult.unmatched.join(", ")}.${availableHint || " read_document\uB85C \uD604\uC7AC \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694."}`
|
|
13738
|
+
);
|
|
13739
|
+
}
|
|
13740
|
+
if (fillResult.filled.length === 0) {
|
|
13741
|
+
return `\uC624\uB958: \uCC44\uC6CC\uC9C4 \uC591\uC2DD \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uB77C\uBCA8\uC774 \uBB38\uC11C\uC640 \uC77C\uCE58\uD558\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694${fillResult.unmatched.length > 0 ? ` (\uBBF8\uB9E4\uCE6D: ${fillResult.unmatched.join(", ")})` : ""}.${availableHint || " read_document\uB85C \uD604\uC7AC \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694."}`;
|
|
13742
|
+
}
|
|
13743
|
+
stagedData = new Uint8Array(fillResult.buffer);
|
|
13744
|
+
} catch (err) {
|
|
13745
|
+
return `\uC624\uB958: \uC591\uC2DD \uCC44\uC6B0\uAE30\uB97C \uC801\uC6A9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${err instanceof Error ? err.message : String(err)}.`;
|
|
13591
13746
|
}
|
|
13592
13747
|
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
13593
13748
|
const stagedPath = await stageFile(ctx.sessionId, safePath, stagedData);
|
|
@@ -13612,9 +13767,9 @@ var proposeFormFillTool = {
|
|
|
13612
13767
|
};
|
|
13613
13768
|
}
|
|
13614
13769
|
};
|
|
13615
|
-
var proposeRedactPiiSchema =
|
|
13616
|
-
path:
|
|
13617
|
-
summary:
|
|
13770
|
+
var proposeRedactPiiSchema = z11.object({
|
|
13771
|
+
path: z11.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uBE44\uC2DD\uBCC4 \uCC98\uB9AC\uD560 \uBB38\uC11C \uACBD\uB85C"),
|
|
13772
|
+
summary: z11.string().optional().describe("\uBCC0\uACBD \uC694\uC57D")
|
|
13618
13773
|
});
|
|
13619
13774
|
function mergeFindings(allFindings) {
|
|
13620
13775
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -13635,8 +13790,31 @@ function mergeFindings(allFindings) {
|
|
|
13635
13790
|
}
|
|
13636
13791
|
return [...map.values()];
|
|
13637
13792
|
}
|
|
13793
|
+
function redactSectionViaNodes(srcXml) {
|
|
13794
|
+
const tNodeRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
13795
|
+
let offset = 0;
|
|
13796
|
+
let result = srcXml;
|
|
13797
|
+
let changed = false;
|
|
13798
|
+
let m = tNodeRe.exec(srcXml);
|
|
13799
|
+
while (m !== null) {
|
|
13800
|
+
const content = m[1];
|
|
13801
|
+
if (content.length > 0) {
|
|
13802
|
+
const { text: redacted } = redactText(content);
|
|
13803
|
+
if (redacted !== content) {
|
|
13804
|
+
const openTagLen = "<hp:t>".length;
|
|
13805
|
+
const contentStart = m.index + offset + openTagLen;
|
|
13806
|
+
const contentEnd = contentStart + content.length;
|
|
13807
|
+
result = result.substring(0, contentStart) + redacted + result.substring(contentEnd);
|
|
13808
|
+
offset += redacted.length - content.length;
|
|
13809
|
+
changed = true;
|
|
13810
|
+
}
|
|
13811
|
+
}
|
|
13812
|
+
m = tNodeRe.exec(srcXml);
|
|
13813
|
+
}
|
|
13814
|
+
return { xml: result, changed };
|
|
13815
|
+
}
|
|
13638
13816
|
async function applyRedactToHwpx(hwpxBuffer) {
|
|
13639
|
-
const zip = await
|
|
13817
|
+
const zip = await import_jszip6.default.loadAsync(hwpxBuffer);
|
|
13640
13818
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13641
13819
|
const sectionXmls = [];
|
|
13642
13820
|
for (const sf of sectionFiles) {
|
|
@@ -13648,38 +13826,28 @@ async function applyRedactToHwpx(hwpxBuffer) {
|
|
|
13648
13826
|
const allFindings = [];
|
|
13649
13827
|
let anyChanged = false;
|
|
13650
13828
|
for (const srcXml of sectionXmls) {
|
|
13651
|
-
const
|
|
13652
|
-
const
|
|
13653
|
-
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
result = result.substring(0, contentStart) + redacted + result.substring(contentEnd);
|
|
13668
|
-
offset += redacted.length - content.length;
|
|
13669
|
-
anyChanged = true;
|
|
13670
|
-
}
|
|
13671
|
-
if (findings.length > 0) {
|
|
13672
|
-
sectionFindings.push(findings);
|
|
13673
|
-
}
|
|
13674
|
-
m = tNodeRe.exec(srcXml);
|
|
13829
|
+
const scan = scanSectionXml5(srcXml, 0);
|
|
13830
|
+
for (const p of collectParasInDocOrder(scan)) {
|
|
13831
|
+
const findings = detectPii(p.text);
|
|
13832
|
+
if (findings.length > 0) allFindings.push(findings);
|
|
13833
|
+
}
|
|
13834
|
+
const spliced = applyRangeSplicesToSection(
|
|
13835
|
+
srcXml,
|
|
13836
|
+
(text3) => redactRanges(text3).map((r) => ({ start: r.start, end: r.end, replacement: r.replacement }))
|
|
13837
|
+
);
|
|
13838
|
+
if (spliced !== null) {
|
|
13839
|
+
newSectionXmls.push(spliced.xml);
|
|
13840
|
+
if (spliced.count > 0) anyChanged = true;
|
|
13841
|
+
} else {
|
|
13842
|
+
const fb = redactSectionViaNodes(srcXml);
|
|
13843
|
+
newSectionXmls.push(fb.xml);
|
|
13844
|
+
if (fb.changed) anyChanged = true;
|
|
13675
13845
|
}
|
|
13676
|
-
newSectionXmls.push(result);
|
|
13677
|
-
allFindings.push(...sectionFindings);
|
|
13678
13846
|
}
|
|
13679
13847
|
if (!anyChanged) {
|
|
13680
13848
|
return { buffer: hwpxBuffer, findings: mergeFindings(allFindings), changed: false };
|
|
13681
13849
|
}
|
|
13682
|
-
const out = new
|
|
13850
|
+
const out = new import_jszip6.default();
|
|
13683
13851
|
const mimetypeEntry = zip.file("mimetype");
|
|
13684
13852
|
if (mimetypeEntry) {
|
|
13685
13853
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -13719,7 +13887,7 @@ var proposeRedactPiiTool = {
|
|
|
13719
13887
|
ctx
|
|
13720
13888
|
}) => {
|
|
13721
13889
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13722
|
-
const ext =
|
|
13890
|
+
const ext = extname11(safePath).toLowerCase();
|
|
13723
13891
|
if (ext !== ".hwpx" && ext !== ".hwp" && ext !== ".md" && ext !== ".txt") {
|
|
13724
13892
|
const hint = ext === ".docx" || ext === ".xlsx" ? " .hwpx/.md/.txt\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4." : " .hwpx/.md/.txt\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.";
|
|
13725
13893
|
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}`;
|
|
@@ -13728,7 +13896,7 @@ var proposeRedactPiiTool = {
|
|
|
13728
13896
|
if (ext === ".hwpx" || ext === ".hwp") {
|
|
13729
13897
|
let originalBuf;
|
|
13730
13898
|
try {
|
|
13731
|
-
originalBuf = await
|
|
13899
|
+
originalBuf = await readFile11(safePath);
|
|
13732
13900
|
} catch {
|
|
13733
13901
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13734
13902
|
}
|
|
@@ -13741,7 +13909,7 @@ var proposeRedactPiiTool = {
|
|
|
13741
13909
|
if (structuralGuard !== null) {
|
|
13742
13910
|
return structuralGuard;
|
|
13743
13911
|
}
|
|
13744
|
-
if (
|
|
13912
|
+
if (!isZipBinary(originalBytes)) {
|
|
13745
13913
|
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.";
|
|
13746
13914
|
}
|
|
13747
13915
|
let patchResult;
|
|
@@ -13780,7 +13948,7 @@ var proposeRedactPiiTool = {
|
|
|
13780
13948
|
}
|
|
13781
13949
|
let originalText;
|
|
13782
13950
|
try {
|
|
13783
|
-
originalText = await
|
|
13951
|
+
originalText = await readFile11(safePath, "utf-8");
|
|
13784
13952
|
} catch {
|
|
13785
13953
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
|
|
13786
13954
|
}
|
|
@@ -13813,16 +13981,16 @@ var proposeRedactPiiTool = {
|
|
|
13813
13981
|
};
|
|
13814
13982
|
}
|
|
13815
13983
|
};
|
|
13816
|
-
var proposeSheetEditSchema =
|
|
13817
|
-
path:
|
|
13818
|
-
updates:
|
|
13819
|
-
|
|
13820
|
-
sheet:
|
|
13821
|
-
cell:
|
|
13822
|
-
value:
|
|
13984
|
+
var proposeSheetEditSchema = z12.object({
|
|
13985
|
+
path: z12.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13986
|
+
updates: z12.array(
|
|
13987
|
+
z12.object({
|
|
13988
|
+
sheet: z12.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
13989
|
+
cell: z12.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
|
|
13990
|
+
value: z12.union([z12.string(), z12.number()]).describe("\uC0C8 \uAC12")
|
|
13823
13991
|
})
|
|
13824
13992
|
).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"),
|
|
13825
|
-
summary:
|
|
13993
|
+
summary: z12.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13826
13994
|
});
|
|
13827
13995
|
var proposeSheetEditTool = {
|
|
13828
13996
|
name: "propose_sheet_edit",
|
|
@@ -13834,13 +14002,13 @@ var proposeSheetEditTool = {
|
|
|
13834
14002
|
ctx
|
|
13835
14003
|
}) => {
|
|
13836
14004
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13837
|
-
const ext =
|
|
14005
|
+
const ext = extname12(safePath).toLowerCase();
|
|
13838
14006
|
if (ext !== ".xlsx" && ext !== ".xls") {
|
|
13839
14007
|
return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
|
|
13840
14008
|
}
|
|
13841
14009
|
let originalBuffer;
|
|
13842
14010
|
try {
|
|
13843
|
-
originalBuffer = await
|
|
14011
|
+
originalBuffer = await readFile12(safePath);
|
|
13844
14012
|
} catch {
|
|
13845
14013
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13846
14014
|
}
|
|
@@ -13933,49 +14101,49 @@ function detectStructuralLoss(beforeBlocks, afterBlocks) {
|
|
|
13933
14101
|
}
|
|
13934
14102
|
return { lost: false, detail: "" };
|
|
13935
14103
|
}
|
|
13936
|
-
var insertRowOpSchema =
|
|
13937
|
-
type:
|
|
13938
|
-
row:
|
|
13939
|
-
position:
|
|
14104
|
+
var insertRowOpSchema = z13.object({
|
|
14105
|
+
type: z13.literal("insertRow"),
|
|
14106
|
+
row: z13.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
|
|
14107
|
+
position: z13.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
|
|
13940
14108
|
});
|
|
13941
|
-
var deleteRowOpSchema =
|
|
13942
|
-
type:
|
|
13943
|
-
row:
|
|
14109
|
+
var deleteRowOpSchema = z13.object({
|
|
14110
|
+
type: z13.literal("deleteRow"),
|
|
14111
|
+
row: z13.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
|
|
13944
14112
|
});
|
|
13945
|
-
var insertColumnOpSchema =
|
|
13946
|
-
type:
|
|
13947
|
-
col:
|
|
13948
|
-
position:
|
|
14113
|
+
var insertColumnOpSchema = z13.object({
|
|
14114
|
+
type: z13.literal("insertColumn"),
|
|
14115
|
+
col: z13.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
|
|
14116
|
+
position: z13.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
|
|
13949
14117
|
});
|
|
13950
|
-
var deleteColumnOpSchema =
|
|
13951
|
-
type:
|
|
13952
|
-
col:
|
|
14118
|
+
var deleteColumnOpSchema = z13.object({
|
|
14119
|
+
type: z13.literal("deleteColumn"),
|
|
14120
|
+
col: z13.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
|
|
13953
14121
|
});
|
|
13954
|
-
var mergeCellsOpSchema =
|
|
13955
|
-
type:
|
|
13956
|
-
startRow:
|
|
13957
|
-
startCol:
|
|
13958
|
-
endRow:
|
|
13959
|
-
endCol:
|
|
14122
|
+
var mergeCellsOpSchema = z13.object({
|
|
14123
|
+
type: z13.literal("mergeCells"),
|
|
14124
|
+
startRow: z13.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
|
|
14125
|
+
startCol: z13.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
|
|
14126
|
+
endRow: z13.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
|
|
14127
|
+
endCol: z13.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
|
|
13960
14128
|
});
|
|
13961
|
-
var operationSchema =
|
|
14129
|
+
var operationSchema = z13.discriminatedUnion("type", [
|
|
13962
14130
|
insertRowOpSchema,
|
|
13963
14131
|
deleteRowOpSchema,
|
|
13964
14132
|
insertColumnOpSchema,
|
|
13965
14133
|
deleteColumnOpSchema,
|
|
13966
14134
|
mergeCellsOpSchema
|
|
13967
14135
|
]).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.");
|
|
13968
|
-
var proposeTableStructureSchema =
|
|
13969
|
-
path:
|
|
13970
|
-
anchor:
|
|
14136
|
+
var proposeTableStructureSchema = z13.object({
|
|
14137
|
+
path: z13.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
14138
|
+
anchor: z13.string().min(1).describe(
|
|
13971
14139
|
"\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."
|
|
13972
14140
|
),
|
|
13973
|
-
operations:
|
|
14141
|
+
operations: z13.array(operationSchema).min(1).describe(
|
|
13974
14142
|
"\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."
|
|
13975
14143
|
),
|
|
13976
|
-
summary:
|
|
14144
|
+
summary: z13.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13977
14145
|
});
|
|
13978
|
-
function
|
|
14146
|
+
function tokenizeHwpxXml(xml) {
|
|
13979
14147
|
const tokens = [];
|
|
13980
14148
|
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tr[\s>]|<\/hp:tr>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<\/hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*|<hp:cellSz[^/>]*/g;
|
|
13981
14149
|
let m = re.exec(xml);
|
|
@@ -14165,7 +14333,7 @@ function setCellSpan(tcXml, colSpan, rowSpan) {
|
|
|
14165
14333
|
return tcXml.replace(/(colSpan=")(\d+)(")/, `$1${colSpan}$3`).replace(/(rowSpan=")(\d+)(")/, `$1${rowSpan}$3`);
|
|
14166
14334
|
}
|
|
14167
14335
|
function insertRowInTbl(tblXml, row, below) {
|
|
14168
|
-
const tokens =
|
|
14336
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14169
14337
|
const tblStart = 0;
|
|
14170
14338
|
const tblEnd = tblXml.length;
|
|
14171
14339
|
const rows = parseTableRows(tblXml, tokens, tblStart, tblEnd);
|
|
@@ -14207,7 +14375,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14207
14375
|
modifiedTr = modifiedTr.substring(0, tcLocalStart) + tcXml + modifiedTr.substring(tcLocalEnd);
|
|
14208
14376
|
}
|
|
14209
14377
|
let result = tblXml;
|
|
14210
|
-
const allTokens =
|
|
14378
|
+
const allTokens = tokenizeHwpxXml(result);
|
|
14211
14379
|
const addrPatches = [];
|
|
14212
14380
|
for (const tok of allTokens) {
|
|
14213
14381
|
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr >= newRowAddr) {
|
|
@@ -14220,7 +14388,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14220
14388
|
const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
14221
14389
|
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
14222
14390
|
}
|
|
14223
|
-
const resultTokens =
|
|
14391
|
+
const resultTokens = tokenizeHwpxXml(result);
|
|
14224
14392
|
const resultRows = parseTableRows(result, resultTokens, 0, result.length);
|
|
14225
14393
|
let insertAfterPos;
|
|
14226
14394
|
let insertBeforePos;
|
|
@@ -14252,7 +14420,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14252
14420
|
return { ok: true, xml: result };
|
|
14253
14421
|
}
|
|
14254
14422
|
function deleteRowInTbl(tblXml, row) {
|
|
14255
|
-
const tokens =
|
|
14423
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14256
14424
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14257
14425
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14258
14426
|
if (rows.length === 0) {
|
|
@@ -14289,7 +14457,7 @@ function deleteRowInTbl(tblXml, row) {
|
|
|
14289
14457
|
}
|
|
14290
14458
|
let result = tblXml;
|
|
14291
14459
|
result = result.substring(0, targetRow.trStart) + result.substring(targetRow.trEnd);
|
|
14292
|
-
const afterTokens =
|
|
14460
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14293
14461
|
const addrPatches = [];
|
|
14294
14462
|
for (const tok of afterTokens) {
|
|
14295
14463
|
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr > row) {
|
|
@@ -14306,7 +14474,7 @@ function deleteRowInTbl(tblXml, row) {
|
|
|
14306
14474
|
return { ok: true, xml: result };
|
|
14307
14475
|
}
|
|
14308
14476
|
function insertColumnInTbl(tblXml, col, right) {
|
|
14309
|
-
const tokens =
|
|
14477
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14310
14478
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14311
14479
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14312
14480
|
if (rows.length === 0) {
|
|
@@ -14327,7 +14495,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14327
14495
|
}
|
|
14328
14496
|
}
|
|
14329
14497
|
let result = tblXml;
|
|
14330
|
-
const allTokens =
|
|
14498
|
+
const allTokens = tokenizeHwpxXml(result);
|
|
14331
14499
|
const addrPatches = [];
|
|
14332
14500
|
for (const tok of allTokens) {
|
|
14333
14501
|
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr >= newColAddr) {
|
|
@@ -14340,7 +14508,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14340
14508
|
const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
14341
14509
|
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
14342
14510
|
}
|
|
14343
|
-
const resultTokens2 =
|
|
14511
|
+
const resultTokens2 = tokenizeHwpxXml(result);
|
|
14344
14512
|
const resultRows = parseTableRows(result, resultTokens2, 0, result.length);
|
|
14345
14513
|
const sortedRows = [...resultRows].sort((a, b) => b.trStart - a.trStart);
|
|
14346
14514
|
for (const r of sortedRows) {
|
|
@@ -14367,7 +14535,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14367
14535
|
return { ok: true, xml: result };
|
|
14368
14536
|
}
|
|
14369
14537
|
function deleteColumnInTbl(tblXml, col) {
|
|
14370
|
-
const tokens =
|
|
14538
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14371
14539
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14372
14540
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14373
14541
|
if (rows.length === 0) {
|
|
@@ -14401,7 +14569,7 @@ function deleteColumnInTbl(tblXml, col) {
|
|
|
14401
14569
|
result = result.substring(0, cell.tcStart) + result.substring(cell.tcEnd);
|
|
14402
14570
|
}
|
|
14403
14571
|
}
|
|
14404
|
-
const afterTokens =
|
|
14572
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14405
14573
|
const addrPatches = [];
|
|
14406
14574
|
for (const tok of afterTokens) {
|
|
14407
14575
|
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr > col) {
|
|
@@ -14424,7 +14592,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14424
14592
|
if (startRow > endRow || startCol > endCol) {
|
|
14425
14593
|
return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (start > end)." };
|
|
14426
14594
|
}
|
|
14427
|
-
const tokens =
|
|
14595
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14428
14596
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14429
14597
|
const rangeCells = [];
|
|
14430
14598
|
for (const r of rows) {
|
|
@@ -14480,7 +14648,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14480
14648
|
for (const c of sortedRemove) {
|
|
14481
14649
|
result = result.substring(0, c.tcStart) + result.substring(c.tcEnd);
|
|
14482
14650
|
}
|
|
14483
|
-
const afterTokens =
|
|
14651
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14484
14652
|
const afterRows = parseTableRows(result, afterTokens, 0, result.length);
|
|
14485
14653
|
const afterTopLeft = afterRows.flatMap((r) => r.cells).find((c) => c.rowAddr === startRow && c.colAddr === startCol);
|
|
14486
14654
|
if (!afterTopLeft) {
|
|
@@ -14492,7 +14660,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14492
14660
|
return { ok: true, xml: result };
|
|
14493
14661
|
}
|
|
14494
14662
|
function findTableByAnchorInXml(xml, anchor) {
|
|
14495
|
-
const tokens =
|
|
14663
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
14496
14664
|
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
14497
14665
|
const trimmedAnchor = anchor.trim();
|
|
14498
14666
|
const matched = [];
|
|
@@ -14530,7 +14698,7 @@ function getTblDims(tblXml) {
|
|
|
14530
14698
|
return parseTblDimensions(tblXml, 0);
|
|
14531
14699
|
}
|
|
14532
14700
|
async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
14533
|
-
const zip = await
|
|
14701
|
+
const zip = await import_jszip7.default.loadAsync(hwpxBuffer);
|
|
14534
14702
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
14535
14703
|
let targetSectionIdx = -1;
|
|
14536
14704
|
let targetRange = null;
|
|
@@ -14589,7 +14757,7 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14589
14757
|
const afterDims = getTblDims(tblBlock);
|
|
14590
14758
|
const newSectionXml = sectionXml.substring(0, targetRange.start) + tblBlock + sectionXml.substring(targetRange.end);
|
|
14591
14759
|
sectionXmls[targetSectionIdx] = newSectionXml;
|
|
14592
|
-
const out = new
|
|
14760
|
+
const out = new import_jszip7.default();
|
|
14593
14761
|
const mimetypeEntry = zip.file("mimetype");
|
|
14594
14762
|
if (mimetypeEntry) {
|
|
14595
14763
|
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
@@ -14613,12 +14781,12 @@ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
|
14613
14781
|
};
|
|
14614
14782
|
}
|
|
14615
14783
|
async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt) {
|
|
14616
|
-
const zip = await
|
|
14784
|
+
const zip = await import_jszip7.default.loadAsync(newBytes);
|
|
14617
14785
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
14618
14786
|
for (const sf of sectionFiles) {
|
|
14619
14787
|
const entry = zip.file(sf);
|
|
14620
14788
|
const xml = entry ? await entry.async("string") : "";
|
|
14621
|
-
const tokens =
|
|
14789
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
14622
14790
|
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
14623
14791
|
const trimmedAnchor = anchor.trim();
|
|
14624
14792
|
for (const r of allRanges) {
|
|
@@ -14648,13 +14816,13 @@ var proposeTableStructureTool = {
|
|
|
14648
14816
|
ctx
|
|
14649
14817
|
}) => {
|
|
14650
14818
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14651
|
-
const ext =
|
|
14819
|
+
const ext = extname13(safePath).toLowerCase();
|
|
14652
14820
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
14653
14821
|
return `\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
14654
14822
|
}
|
|
14655
14823
|
let originalBuf;
|
|
14656
14824
|
try {
|
|
14657
|
-
originalBuf = await
|
|
14825
|
+
originalBuf = await readFile13(safePath);
|
|
14658
14826
|
} catch {
|
|
14659
14827
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
14660
14828
|
}
|
|
@@ -14667,12 +14835,12 @@ var proposeTableStructureTool = {
|
|
|
14667
14835
|
if (structuralGuard !== null) {
|
|
14668
14836
|
return structuralGuard;
|
|
14669
14837
|
}
|
|
14670
|
-
if (
|
|
14838
|
+
if (!isZipBinary(originalBytes)) {
|
|
14671
14839
|
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.";
|
|
14672
14840
|
}
|
|
14673
14841
|
let originalBlocks = null;
|
|
14674
14842
|
try {
|
|
14675
|
-
const origResult = await
|
|
14843
|
+
const origResult = await parse(originalBuf.buffer);
|
|
14676
14844
|
if (origResult.success) {
|
|
14677
14845
|
originalBlocks = origResult.blocks;
|
|
14678
14846
|
}
|
|
@@ -14693,7 +14861,7 @@ var proposeTableStructureTool = {
|
|
|
14693
14861
|
let kordocOk = false;
|
|
14694
14862
|
let exportedBlocks = null;
|
|
14695
14863
|
try {
|
|
14696
|
-
const exportedResult = await
|
|
14864
|
+
const exportedResult = await parse(newBytes.buffer);
|
|
14697
14865
|
if (exportedResult.success) {
|
|
14698
14866
|
exportedMd = exportedResult.markdown;
|
|
14699
14867
|
exportedBlocks = exportedResult.blocks;
|
|
@@ -14854,11 +15022,11 @@ function searchExcerpts(markdown, query) {
|
|
|
14854
15022
|
}
|
|
14855
15023
|
return result;
|
|
14856
15024
|
}
|
|
14857
|
-
var readDocumentSchema =
|
|
14858
|
-
path:
|
|
14859
|
-
pages:
|
|
14860
|
-
outline:
|
|
14861
|
-
search:
|
|
15025
|
+
var readDocumentSchema = z14.object({
|
|
15026
|
+
path: z14.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
15027
|
+
pages: z14.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
|
|
15028
|
+
outline: z14.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)"),
|
|
15029
|
+
search: z14.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)")
|
|
14862
15030
|
});
|
|
14863
15031
|
function applyReadMode(body, outline, search) {
|
|
14864
15032
|
if (outline === true) {
|
|
@@ -14887,11 +15055,11 @@ var readDocumentTool = {
|
|
|
14887
15055
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14888
15056
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
14889
15057
|
}
|
|
14890
|
-
const ext =
|
|
15058
|
+
const ext = extname14(safePath).toLowerCase();
|
|
14891
15059
|
if (PLAIN_TEXT_EXTS.has(ext)) {
|
|
14892
15060
|
let raw;
|
|
14893
15061
|
try {
|
|
14894
|
-
raw = await
|
|
15062
|
+
raw = await readFile14(safePath, "utf-8");
|
|
14895
15063
|
} catch (e) {
|
|
14896
15064
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14897
15065
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -14915,7 +15083,7 @@ var readDocumentTool = {
|
|
|
14915
15083
|
\u26A0\uFE0F \uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE38\uC5B4 \uC57D 80,000\uC790\uC5D0\uC11C \uC798\uB838\uC2B5\uB2C8\uB2E4. (\uD3C9\uBB38 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774\uB77C \uD398\uC774\uC9C0 \uB2E8\uC704 \uBD84\uD560 \uC77D\uAE30\uB294 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)` : "";
|
|
14916
15084
|
return `${meta2}${body2}${truncationNotice2}`;
|
|
14917
15085
|
}
|
|
14918
|
-
const result = await
|
|
15086
|
+
const result = await parse(safePath, input.pages ? { pages: input.pages } : void 0);
|
|
14919
15087
|
if (!result.success) {
|
|
14920
15088
|
const msg = kordocErrorMessage(result.code, `\uBB38\uC11C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${result.error}`);
|
|
14921
15089
|
return `\uC624\uB958: ${msg}`;
|
|
@@ -14991,8 +15159,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
14991
15159
|
".csv",
|
|
14992
15160
|
".log"
|
|
14993
15161
|
]);
|
|
14994
|
-
var readFileSchema =
|
|
14995
|
-
path:
|
|
15162
|
+
var readFileSchema = z15.object({
|
|
15163
|
+
path: z15.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
|
|
14996
15164
|
});
|
|
14997
15165
|
var readFileTool = {
|
|
14998
15166
|
name: "read_file",
|
|
@@ -15016,8 +15184,8 @@ var readFileTool = {
|
|
|
15016
15184
|
if (info.size > MAX_SIZE) {
|
|
15017
15185
|
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.`;
|
|
15018
15186
|
}
|
|
15019
|
-
const { extname:
|
|
15020
|
-
const ext =
|
|
15187
|
+
const { extname: extname18 } = await import("path");
|
|
15188
|
+
const ext = extname18(safePath).toLowerCase();
|
|
15021
15189
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
15022
15190
|
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.`;
|
|
15023
15191
|
}
|
|
@@ -15032,8 +15200,8 @@ var readFileTool = {
|
|
|
15032
15200
|
}
|
|
15033
15201
|
};
|
|
15034
15202
|
var PLAIN_TEXT_EXTS2 = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
|
|
15035
|
-
var scanPiiSchema =
|
|
15036
|
-
path:
|
|
15203
|
+
var scanPiiSchema = z16.object({
|
|
15204
|
+
path: z16.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uC810\uAC80\uD560 \uBB38\uC11C \uACBD\uB85C")
|
|
15037
15205
|
});
|
|
15038
15206
|
var scanPiiTool = {
|
|
15039
15207
|
name: "scan_pii",
|
|
@@ -15051,11 +15219,11 @@ var scanPiiTool = {
|
|
|
15051
15219
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15052
15220
|
return `\uC624\uB958: \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15053
15221
|
}
|
|
15054
|
-
const ext =
|
|
15222
|
+
const ext = extname15(safePath).toLowerCase();
|
|
15055
15223
|
let text3;
|
|
15056
15224
|
if (PLAIN_TEXT_EXTS2.has(ext)) {
|
|
15057
15225
|
try {
|
|
15058
|
-
text3 = await
|
|
15226
|
+
text3 = await readFile15(safePath, "utf-8");
|
|
15059
15227
|
} catch (e) {
|
|
15060
15228
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15061
15229
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -15063,7 +15231,7 @@ var scanPiiTool = {
|
|
|
15063
15231
|
} else {
|
|
15064
15232
|
let parseResult;
|
|
15065
15233
|
try {
|
|
15066
|
-
parseResult = await
|
|
15234
|
+
parseResult = await parse(safePath);
|
|
15067
15235
|
} catch (e) {
|
|
15068
15236
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15069
15237
|
return `\uC624\uB958: \uBB38\uC11C\uB97C \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -15085,10 +15253,10 @@ var scanPiiTool = {
|
|
|
15085
15253
|
return lines.join("\n");
|
|
15086
15254
|
}
|
|
15087
15255
|
};
|
|
15088
|
-
var
|
|
15089
|
-
var writeNewDocumentSchema =
|
|
15090
|
-
path:
|
|
15091
|
-
markdown:
|
|
15256
|
+
var MAX_PREVIEW_CHARS2 = 1e4;
|
|
15257
|
+
var writeNewDocumentSchema = z17.object({
|
|
15258
|
+
path: z17.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
15259
|
+
markdown: z17.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
|
|
15092
15260
|
});
|
|
15093
15261
|
var writeNewDocumentTool = {
|
|
15094
15262
|
name: "write_new_document",
|
|
@@ -15100,7 +15268,7 @@ var writeNewDocumentTool = {
|
|
|
15100
15268
|
ctx
|
|
15101
15269
|
}) => {
|
|
15102
15270
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
15103
|
-
const ext =
|
|
15271
|
+
const ext = extname16(safePath).toLowerCase();
|
|
15104
15272
|
try {
|
|
15105
15273
|
await stat6(safePath);
|
|
15106
15274
|
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.`;
|
|
@@ -15121,9 +15289,9 @@ var writeNewDocumentTool = {
|
|
|
15121
15289
|
return `\uC624\uB958: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4: ${ext}. .hwpx, .docx, .md, .txt \uC911 \uD558\uB098\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
15122
15290
|
}
|
|
15123
15291
|
const stagedPath = await stageFile(ctx.sessionId, safePath, stagedData);
|
|
15124
|
-
const preview = input.markdown.length >
|
|
15292
|
+
const preview = input.markdown.length > MAX_PREVIEW_CHARS2 ? `${input.markdown.slice(0, MAX_PREVIEW_CHARS2)}
|
|
15125
15293
|
|
|
15126
|
-
...\uC774\uD558 \uC0DD\uB7B5 (${input.markdown.length -
|
|
15294
|
+
...\uC774\uD558 \uC0DD\uB7B5 (${input.markdown.length - MAX_PREVIEW_CHARS2}\uC790 \uB354 \uC788\uC74C)` : input.markdown;
|
|
15127
15295
|
const proposalId = crypto.randomUUID();
|
|
15128
15296
|
return {
|
|
15129
15297
|
proposal: {
|
|
@@ -15144,12 +15312,12 @@ ${preview}`,
|
|
|
15144
15312
|
};
|
|
15145
15313
|
}
|
|
15146
15314
|
};
|
|
15147
|
-
var writeNewSpreadsheetSchema =
|
|
15148
|
-
path:
|
|
15149
|
-
sheets:
|
|
15150
|
-
|
|
15151
|
-
name:
|
|
15152
|
-
rows:
|
|
15315
|
+
var writeNewSpreadsheetSchema = z18.object({
|
|
15316
|
+
path: z18.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
15317
|
+
sheets: z18.array(
|
|
15318
|
+
z18.object({
|
|
15319
|
+
name: z18.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
15320
|
+
rows: z18.array(z18.array(z18.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
|
|
15153
15321
|
})
|
|
15154
15322
|
).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
|
|
15155
15323
|
});
|
|
@@ -15163,7 +15331,7 @@ var writeNewSpreadsheetTool = {
|
|
|
15163
15331
|
ctx
|
|
15164
15332
|
}) => {
|
|
15165
15333
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
15166
|
-
const ext =
|
|
15334
|
+
const ext = extname17(safePath).toLowerCase();
|
|
15167
15335
|
if (ext !== ".xlsx") {
|
|
15168
15336
|
return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
|
|
15169
15337
|
}
|
|
@@ -15225,6 +15393,7 @@ function createDocTools(_ctx) {
|
|
|
15225
15393
|
readFileTool,
|
|
15226
15394
|
scanPiiTool,
|
|
15227
15395
|
findInDocumentTool,
|
|
15396
|
+
exportDocumentTool,
|
|
15228
15397
|
proposeEditTool,
|
|
15229
15398
|
proposeFormFillTool,
|
|
15230
15399
|
proposeCellEditTool,
|
|
@@ -15241,13 +15410,13 @@ function createDocTools(_ctx) {
|
|
|
15241
15410
|
}
|
|
15242
15411
|
|
|
15243
15412
|
// src/index.ts
|
|
15244
|
-
import
|
|
15413
|
+
import chalk6 from "chalk";
|
|
15245
15414
|
import { Command } from "commander";
|
|
15246
15415
|
|
|
15247
15416
|
// src/chat.ts
|
|
15248
15417
|
import * as readline from "readline/promises";
|
|
15249
15418
|
import { isCancel as isCancel2, select as select2, spinner, text } from "@clack/prompts";
|
|
15250
|
-
import
|
|
15419
|
+
import chalk3 from "chalk";
|
|
15251
15420
|
|
|
15252
15421
|
// src/approve.ts
|
|
15253
15422
|
import { isCancel, select } from "@clack/prompts";
|
|
@@ -15323,6 +15492,45 @@ function createCliApprovalHandler() {
|
|
|
15323
15492
|
};
|
|
15324
15493
|
}
|
|
15325
15494
|
|
|
15495
|
+
// src/usage.ts
|
|
15496
|
+
import chalk2 from "chalk";
|
|
15497
|
+
var MODEL_PRICING = [
|
|
15498
|
+
// Anthropic Claude (USD per 1M tokens)
|
|
15499
|
+
{ prefix: "claude-opus-4", price: { input: 15, output: 75 } },
|
|
15500
|
+
{ prefix: "claude-sonnet-4", price: { input: 3, output: 15 } },
|
|
15501
|
+
{ prefix: "claude-haiku-4", price: { input: 1, output: 5 } }
|
|
15502
|
+
// OpenAI·Google: 정가 미확정 → 미등록(토큰만 표시)
|
|
15503
|
+
];
|
|
15504
|
+
var PROVIDER_DEFAULT_MODEL = {
|
|
15505
|
+
anthropic: "claude-opus-4-8"
|
|
15506
|
+
};
|
|
15507
|
+
function effectiveModelId(config) {
|
|
15508
|
+
return config.model ?? PROVIDER_DEFAULT_MODEL[config.provider] ?? "";
|
|
15509
|
+
}
|
|
15510
|
+
function resolveTokenPrice(model) {
|
|
15511
|
+
for (const { prefix, price } of MODEL_PRICING) {
|
|
15512
|
+
if (model.startsWith(prefix)) return price;
|
|
15513
|
+
}
|
|
15514
|
+
return null;
|
|
15515
|
+
}
|
|
15516
|
+
function estimateCostUsd(model, inputTokens, outputTokens) {
|
|
15517
|
+
const p = resolveTokenPrice(model);
|
|
15518
|
+
if (!p) return null;
|
|
15519
|
+
return inputTokens / 1e6 * p.input + outputTokens / 1e6 * p.output;
|
|
15520
|
+
}
|
|
15521
|
+
var fmtTok = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
15522
|
+
function fmtUsd(cost) {
|
|
15523
|
+
if (cost > 0 && cost < 0.01) return `$${cost.toFixed(5)}`;
|
|
15524
|
+
return `$${cost.toFixed(4)}`;
|
|
15525
|
+
}
|
|
15526
|
+
function formatCumulativeUsage(config, inputTokens, outputTokens) {
|
|
15527
|
+
const cost = estimateCostUsd(effectiveModelId(config), inputTokens, outputTokens);
|
|
15528
|
+
const costStr = cost !== null ? ` \xB7 \uCD94\uC815 \uB204\uC801 \uBE44\uC6A9 ${fmtUsd(cost)}` : " \xB7 (\uB2E8\uAC00 \uBBF8\uB4F1\uB85D \u2014 \uD1A0\uD070\uB9CC)";
|
|
15529
|
+
return chalk2.dim(
|
|
15530
|
+
`\uB204\uC801 API \uC0AC\uC6A9: \uC785\uB825 ${fmtTok(inputTokens)} \xB7 \uCD9C\uB825 ${fmtTok(outputTokens)} \uD1A0\uD070${costStr}`
|
|
15531
|
+
);
|
|
15532
|
+
}
|
|
15533
|
+
|
|
15326
15534
|
// src/chat.ts
|
|
15327
15535
|
var HELP_TEXT = `
|
|
15328
15536
|
\uD560 \uC218 \uC788\uB294 \uC77C:
|
|
@@ -15336,6 +15544,7 @@ var HELP_TEXT = `
|
|
|
15336
15544
|
\uC2AC\uB798\uC2DC \uBA85\uB839:
|
|
15337
15545
|
/model \u2014 \uD504\uB85C\uBC14\uC774\uB354/\uBAA8\uB378 \uC804\uD658
|
|
15338
15546
|
/context \u2014 \uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uC0AC\uC6A9\uB7C9 \uD45C\uC2DC
|
|
15547
|
+
/usage \u2014 \uB204\uC801 API \uC0AC\uC6A9\uB7C9\xB7\uCD94\uC815 \uBE44\uC6A9 \uD45C\uC2DC (/cost)
|
|
15339
15548
|
/clear \u2014 \uC0C8 \uC138\uC158 \uC2DC\uC791
|
|
15340
15549
|
/help \u2014 \uC774 \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC
|
|
15341
15550
|
/exit \u2014 \uC885\uB8CC
|
|
@@ -15368,44 +15577,44 @@ async function runChat(opts) {
|
|
|
15368
15577
|
const okCount = mcpManager.connectedServerNames.length;
|
|
15369
15578
|
s.stop(okCount > 0 ? `MCP \uC11C\uBC84 ${okCount}\uAC1C \uC5F0\uACB0\uB428` : "MCP \uC5F0\uACB0 \uC644\uB8CC");
|
|
15370
15579
|
} else {
|
|
15371
|
-
process.stdout.write(
|
|
15580
|
+
process.stdout.write(chalk3.dim(`${connectMsg}
|
|
15372
15581
|
`));
|
|
15373
15582
|
await mcpManager.connect(servers);
|
|
15374
15583
|
const okCount = mcpManager.connectedServerNames.length;
|
|
15375
15584
|
process.stdout.write(
|
|
15376
|
-
|
|
15585
|
+
chalk3.dim(`${okCount > 0 ? `MCP \uC11C\uBC84 ${okCount}\uAC1C \uC5F0\uACB0\uB428` : "MCP \uC5F0\uACB0 \uC644\uB8CC"}
|
|
15377
15586
|
`)
|
|
15378
15587
|
);
|
|
15379
15588
|
}
|
|
15380
15589
|
}
|
|
15381
15590
|
for (const s of mcpManager.status()) {
|
|
15382
15591
|
if (s.state === "failed") {
|
|
15383
|
-
process.stdout.write(
|
|
15592
|
+
process.stdout.write(chalk3.dim(`MCP [${s.name}] \uC5F0\uACB0 \uC2E4\uD328: ${s.reason ?? ""}
|
|
15384
15593
|
`));
|
|
15385
15594
|
} else if (s.state === "skipped") {
|
|
15386
|
-
process.stdout.write(
|
|
15595
|
+
process.stdout.write(chalk3.dim(`MCP [${s.name}] \uC2A4\uD0B5: ${s.reason ?? ""}
|
|
15387
15596
|
`));
|
|
15388
15597
|
}
|
|
15389
15598
|
}
|
|
15390
15599
|
for (const w of mcpManager.warnings) {
|
|
15391
|
-
process.stdout.write(
|
|
15600
|
+
process.stdout.write(chalk3.yellow(`\u26A0 ${w}
|
|
15392
15601
|
`));
|
|
15393
15602
|
}
|
|
15394
15603
|
}
|
|
15395
15604
|
let store;
|
|
15396
15605
|
if (opts.resumeId) {
|
|
15397
15606
|
store = await SessionStore.load(opts.resumeId);
|
|
15398
|
-
process.stdout.write(
|
|
15607
|
+
process.stdout.write(chalk3.dim(`\uC138\uC158 \uC7AC\uAC1C: ${opts.resumeId}
|
|
15399
15608
|
`));
|
|
15400
15609
|
} else if (opts.continueLatest) {
|
|
15401
15610
|
const sessions = await listSessions();
|
|
15402
15611
|
const latest = sessions[0];
|
|
15403
15612
|
if (!latest) {
|
|
15404
|
-
process.stdout.write(
|
|
15613
|
+
process.stdout.write(chalk3.dim("\uC774\uC804 \uC138\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC0C8 \uC138\uC158\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4.\n"));
|
|
15405
15614
|
store = await createNewStore(config, cwd);
|
|
15406
15615
|
} else {
|
|
15407
15616
|
store = await SessionStore.load(latest.id);
|
|
15408
|
-
process.stdout.write(
|
|
15617
|
+
process.stdout.write(chalk3.dim(`\uC774\uC804 \uC138\uC158 \uC7AC\uAC1C: ${latest.id}
|
|
15409
15618
|
`));
|
|
15410
15619
|
}
|
|
15411
15620
|
} else {
|
|
@@ -15419,6 +15628,8 @@ async function runChat(opts) {
|
|
|
15419
15628
|
let ctrlCCount = 0;
|
|
15420
15629
|
let currentController = null;
|
|
15421
15630
|
let lastContextTokens = 0;
|
|
15631
|
+
let cumulativeInputTokens = 0;
|
|
15632
|
+
let cumulativeOutputTokens = 0;
|
|
15422
15633
|
let sharedActiveInterval = null;
|
|
15423
15634
|
function clearSharedSpinner() {
|
|
15424
15635
|
if (!sharedActiveInterval) return;
|
|
@@ -15433,30 +15644,30 @@ async function runChat(opts) {
|
|
|
15433
15644
|
clearSharedSpinner();
|
|
15434
15645
|
currentController.abort();
|
|
15435
15646
|
currentController = null;
|
|
15436
|
-
process.stdout.write(
|
|
15647
|
+
process.stdout.write(chalk3.yellow("\n\uD604\uC7AC \uC751\uB2F5\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n"));
|
|
15437
15648
|
ctrlCCount = 0;
|
|
15438
15649
|
} else {
|
|
15439
15650
|
ctrlCCount++;
|
|
15440
15651
|
if (ctrlCCount >= 2) {
|
|
15441
|
-
process.stdout.write(
|
|
15652
|
+
process.stdout.write(chalk3.yellow("\n\uC885\uB8CC\uD569\uB2C8\uB2E4.\n"));
|
|
15442
15653
|
rl.close();
|
|
15443
15654
|
cleanSessionStaging(store.id).catch(() => {
|
|
15444
15655
|
});
|
|
15445
15656
|
mcpManager.disconnect().finally(() => process.exit(0));
|
|
15446
15657
|
} else {
|
|
15447
|
-
process.stdout.write(
|
|
15658
|
+
process.stdout.write(chalk3.dim("\n(\uD55C \uBC88 \uB354 Ctrl+C\uB97C \uB204\uB974\uBA74 \uC885\uB8CC\uB429\uB2C8\uB2E4)\n"));
|
|
15448
15659
|
}
|
|
15449
15660
|
}
|
|
15450
15661
|
});
|
|
15451
|
-
process.stdout.write(
|
|
15662
|
+
process.stdout.write(chalk3.bold(`kodocagent \uCC44\uD305 \uC2DC\uC791 (/help\uB85C \uB3C4\uC6C0\uB9D0)
|
|
15452
15663
|
`));
|
|
15453
15664
|
process.stdout.write(
|
|
15454
|
-
|
|
15665
|
+
chalk3.dim(`\uD504\uB85C\uBC14\uC774\uB354: ${config.provider}, \uBAA8\uB378: ${config.model ?? "(\uAE30\uBCF8\uAC12)"}
|
|
15455
15666
|
|
|
15456
15667
|
`)
|
|
15457
15668
|
);
|
|
15458
15669
|
process.stdout.write(
|
|
15459
|
-
|
|
15670
|
+
chalk3.dim(
|
|
15460
15671
|
'\uBB38\uC11C\uB97C \uC77D\uACE0 \uD45C\xB7\uC591\uC2DD \uC218\uC815, \uCC3E\uAE30\xB7\uBC14\uAFB8\uAE30, \uB418\uB3CC\uB9AC\uAE30\uAE4C\uC9C0 \uC790\uC5F0\uC5B4\uB85C \uC694\uCCAD\uD558\uC138\uC694. \uC608: "\uC774 \uD45C\uC758 \uD569\uACC4\uB97C \uB2E4\uC2DC \uACC4\uC0B0\uD574\uC918", "\uBC29\uAE08 \uC218\uC815 \uB418\uB3CC\uB824\uC918". \uC790\uC138\uD788: /help\n'
|
|
15461
15672
|
)
|
|
15462
15673
|
);
|
|
@@ -15474,7 +15685,7 @@ async function runChat(opts) {
|
|
|
15474
15685
|
var clearActiveSpinner = clearActiveSpinner2;
|
|
15475
15686
|
let userInput;
|
|
15476
15687
|
try {
|
|
15477
|
-
userInput = await rl.question(
|
|
15688
|
+
userInput = await rl.question(chalk3.green("You: "));
|
|
15478
15689
|
} catch {
|
|
15479
15690
|
break;
|
|
15480
15691
|
}
|
|
@@ -15486,15 +15697,28 @@ async function runChat(opts) {
|
|
|
15486
15697
|
printContextUsage(lastContextTokens, config.maxContextTokens);
|
|
15487
15698
|
continue;
|
|
15488
15699
|
}
|
|
15700
|
+
if (trimmed.toLowerCase() === "/usage" || trimmed.toLowerCase() === "/cost") {
|
|
15701
|
+
if (cumulativeInputTokens === 0 && cumulativeOutputTokens === 0) {
|
|
15702
|
+
process.stdout.write(
|
|
15703
|
+
chalk3.dim("\uC544\uC9C1 API \uC0AC\uC6A9 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uBA74 \uB204\uC801\uB429\uB2C8\uB2E4.\n")
|
|
15704
|
+
);
|
|
15705
|
+
} else {
|
|
15706
|
+
process.stdout.write(
|
|
15707
|
+
`${formatCumulativeUsage(config, cumulativeInputTokens, cumulativeOutputTokens)}
|
|
15708
|
+
`
|
|
15709
|
+
);
|
|
15710
|
+
}
|
|
15711
|
+
continue;
|
|
15712
|
+
}
|
|
15489
15713
|
const handled = await handleSlashCommand(trimmed, config, store, cwd, rl);
|
|
15490
15714
|
if (handled === "exit") break;
|
|
15491
15715
|
if (handled === "new-session") {
|
|
15492
15716
|
store = await createNewStore(config, cwd);
|
|
15493
|
-
process.stdout.write(
|
|
15717
|
+
process.stdout.write(chalk3.dim("\uC0C8 \uC138\uC158\uC774 \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n"));
|
|
15494
15718
|
} else if (handled && typeof handled === "object" && "config" in handled) {
|
|
15495
15719
|
config = handled.config;
|
|
15496
15720
|
process.stdout.write(
|
|
15497
|
-
|
|
15721
|
+
chalk3.dim(`\uBAA8\uB378 \uC804\uD658: ${config.provider} / ${config.model ?? "(\uAE30\uBCF8\uAC12)"}
|
|
15498
15722
|
`)
|
|
15499
15723
|
);
|
|
15500
15724
|
}
|
|
@@ -15506,7 +15730,7 @@ async function runChat(opts) {
|
|
|
15506
15730
|
model = createModel(config);
|
|
15507
15731
|
} catch (err) {
|
|
15508
15732
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15509
|
-
process.stderr.write(
|
|
15733
|
+
process.stderr.write(chalk3.red(`\uC624\uB958: ${msg}
|
|
15510
15734
|
`));
|
|
15511
15735
|
continue;
|
|
15512
15736
|
}
|
|
@@ -15528,7 +15752,7 @@ async function runChat(opts) {
|
|
|
15528
15752
|
});
|
|
15529
15753
|
await session.loadHistory();
|
|
15530
15754
|
currentController = new AbortController();
|
|
15531
|
-
process.stdout.write(
|
|
15755
|
+
process.stdout.write(chalk3.bold("Assistant: "));
|
|
15532
15756
|
const isTTY = process.stdout.isTTY === true;
|
|
15533
15757
|
const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
15534
15758
|
let activeToolLabel = "";
|
|
@@ -15543,7 +15767,7 @@ async function runChat(opts) {
|
|
|
15543
15767
|
process.stdout.write(event.text);
|
|
15544
15768
|
hasOutput = true;
|
|
15545
15769
|
} else if (event.type === "tool-call") {
|
|
15546
|
-
process.stdout.write(
|
|
15770
|
+
process.stdout.write(chalk3.dim(`
|
|
15547
15771
|
\u2699 ${formatToolCall(event.toolName, event.args)}
|
|
15548
15772
|
`));
|
|
15549
15773
|
if (isTTY) {
|
|
@@ -15555,7 +15779,7 @@ async function runChat(opts) {
|
|
|
15555
15779
|
const frame = SPINNER_FRAMES[spinnerFrameIdx % SPINNER_FRAMES.length] ?? "\u280B";
|
|
15556
15780
|
spinnerFrameIdx++;
|
|
15557
15781
|
const plainText = `${frame} ${activeToolLabel} \uC2E4\uD589 \uC911\u2026`;
|
|
15558
|
-
process.stdout.write(`\r${
|
|
15782
|
+
process.stdout.write(`\r${chalk3.dim(plainText)}`);
|
|
15559
15783
|
lastSpinnerWidth = plainText.length + 1;
|
|
15560
15784
|
}, 80);
|
|
15561
15785
|
}
|
|
@@ -15565,23 +15789,29 @@ async function runChat(opts) {
|
|
|
15565
15789
|
clearActiveSpinner2();
|
|
15566
15790
|
const statusLabel = event.isError ? "\uC2E4\uD328" : "\uC644\uB8CC";
|
|
15567
15791
|
process.stdout.write(
|
|
15568
|
-
|
|
15792
|
+
chalk3.dim(` \u2514 ${activeToolLabel} ${statusLabel} (${elapsedSec}s)
|
|
15569
15793
|
`)
|
|
15570
15794
|
);
|
|
15571
15795
|
} else if (event.type === "approval-required") {
|
|
15572
15796
|
} else if (event.type === "turn-complete") {
|
|
15573
15797
|
if (event.usage) {
|
|
15574
15798
|
lastContextTokens = event.usage.inputTokens;
|
|
15799
|
+
cumulativeInputTokens += event.usage.inputTokens;
|
|
15800
|
+
cumulativeOutputTokens += event.usage.outputTokens;
|
|
15575
15801
|
process.stdout.write(
|
|
15576
15802
|
`
|
|
15577
|
-
${formatContextUsage(event.usage.inputTokens, config.maxContextTokens)} ${
|
|
15578
|
-
`\uCD9C\uB825 ${event.usage.outputTokens} \uD1A0\uD070`
|
|
15803
|
+
${formatContextUsage(event.usage.inputTokens, config.maxContextTokens)} ${chalk3.dim(
|
|
15804
|
+
`\uC774\uBC88 \uD134 \uCD9C\uB825 ${event.usage.outputTokens} \uD1A0\uD070`
|
|
15579
15805
|
)}
|
|
15806
|
+
`
|
|
15807
|
+
);
|
|
15808
|
+
process.stdout.write(
|
|
15809
|
+
`${formatCumulativeUsage(config, cumulativeInputTokens, cumulativeOutputTokens)}
|
|
15580
15810
|
`
|
|
15581
15811
|
);
|
|
15582
15812
|
}
|
|
15583
15813
|
} else if (event.type === "error") {
|
|
15584
|
-
process.stderr.write(
|
|
15814
|
+
process.stderr.write(chalk3.red(`
|
|
15585
15815
|
\uC624\uB958: ${event.message}
|
|
15586
15816
|
`));
|
|
15587
15817
|
}
|
|
@@ -15595,6 +15825,12 @@ ${formatContextUsage(event.usage.inputTokens, config.maxContextTokens)} ${chalk
|
|
|
15595
15825
|
}
|
|
15596
15826
|
}
|
|
15597
15827
|
rl.close();
|
|
15828
|
+
if (cumulativeInputTokens > 0 || cumulativeOutputTokens > 0) {
|
|
15829
|
+
process.stdout.write(
|
|
15830
|
+
`${formatCumulativeUsage(config, cumulativeInputTokens, cumulativeOutputTokens)}
|
|
15831
|
+
`
|
|
15832
|
+
);
|
|
15833
|
+
}
|
|
15598
15834
|
await cleanSessionStaging(store.id).catch(() => {
|
|
15599
15835
|
});
|
|
15600
15836
|
await mcpManager.disconnect();
|
|
@@ -15636,14 +15872,14 @@ function formatContextUsage(used, budget) {
|
|
|
15636
15872
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
15637
15873
|
const pct = budget > 0 ? Math.round(used / budget * 100) : 0;
|
|
15638
15874
|
const text3 = `\uCEE8\uD14D\uC2A4\uD2B8: ${fmt(used)} / ${fmt(budget)} \uD1A0\uD070 (${pct}%)`;
|
|
15639
|
-
if (pct >= 90) return
|
|
15640
|
-
if (pct >= 70) return
|
|
15641
|
-
return
|
|
15875
|
+
if (pct >= 90) return chalk3.red(text3);
|
|
15876
|
+
if (pct >= 70) return chalk3.yellow(text3);
|
|
15877
|
+
return chalk3.dim(text3);
|
|
15642
15878
|
}
|
|
15643
15879
|
function printContextUsage(used, budget) {
|
|
15644
15880
|
if (used <= 0) {
|
|
15645
15881
|
process.stdout.write(
|
|
15646
|
-
|
|
15882
|
+
chalk3.dim("\uC544\uC9C1 \uCE21\uC815\uB41C \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uBA74 \uD45C\uC2DC\uB429\uB2C8\uB2E4.\n")
|
|
15647
15883
|
);
|
|
15648
15884
|
return;
|
|
15649
15885
|
}
|
|
@@ -15664,7 +15900,7 @@ async function handleSlashCommand(cmd, config, _store, _cwd, rl) {
|
|
|
15664
15900
|
switch (command) {
|
|
15665
15901
|
case "/exit":
|
|
15666
15902
|
case "/quit":
|
|
15667
|
-
process.stdout.write(
|
|
15903
|
+
process.stdout.write(chalk3.yellow("\uC885\uB8CC\uD569\uB2C8\uB2E4.\n"));
|
|
15668
15904
|
rl.close();
|
|
15669
15905
|
return "exit";
|
|
15670
15906
|
case "/help":
|
|
@@ -15675,7 +15911,7 @@ async function handleSlashCommand(cmd, config, _store, _cwd, rl) {
|
|
|
15675
15911
|
case "/model":
|
|
15676
15912
|
return handleModelSwitch(config);
|
|
15677
15913
|
default:
|
|
15678
|
-
process.stdout.write(
|
|
15914
|
+
process.stdout.write(chalk3.yellow(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}. /help\uB85C \uB3C4\uC6C0\uB9D0\uC744 \uD655\uC778\uD558\uC138\uC694.
|
|
15679
15915
|
`));
|
|
15680
15916
|
return null;
|
|
15681
15917
|
}
|
|
@@ -15697,7 +15933,7 @@ async function handleModelSwitch(_config) {
|
|
|
15697
15933
|
options.push({ value: "__custom__", label: "\uC9C1\uC811 \uC785\uB825..." });
|
|
15698
15934
|
if (options.length <= 1) {
|
|
15699
15935
|
process.stdout.write(
|
|
15700
|
-
|
|
15936
|
+
chalk3.yellow("API \uD0A4\uAC00 \uC124\uC815\uB41C \uD504\uB85C\uBC14\uC774\uB354\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uD0A4\uB97C \uC124\uC815\uD558\uC138\uC694.\n")
|
|
15701
15937
|
);
|
|
15702
15938
|
return null;
|
|
15703
15939
|
}
|
|
@@ -15733,7 +15969,7 @@ async function handleModelSwitch(_config) {
|
|
|
15733
15969
|
const isKnown = knownForProvider.includes(modelId2);
|
|
15734
15970
|
if (!isKnown) {
|
|
15735
15971
|
process.stdout.write(
|
|
15736
|
-
|
|
15972
|
+
chalk3.dim("\uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uBAA8\uB378 ID\uC785\uB2C8\uB2E4 \u2014 \uD504\uB85C\uBC14\uC774\uB354\uAC00 \uC9C0\uC6D0\uD558\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694\n")
|
|
15737
15973
|
);
|
|
15738
15974
|
}
|
|
15739
15975
|
const newConfig2 = { ...loadedConfig };
|
|
@@ -15755,7 +15991,7 @@ async function saveConfig2(config) {
|
|
|
15755
15991
|
}
|
|
15756
15992
|
|
|
15757
15993
|
// src/clean-cmd.ts
|
|
15758
|
-
import
|
|
15994
|
+
import chalk4 from "chalk";
|
|
15759
15995
|
async function runClean(opts) {
|
|
15760
15996
|
const stagingDeleted = await cleanAllStaging();
|
|
15761
15997
|
let backupDeleted;
|
|
@@ -15771,7 +16007,7 @@ async function runClean(opts) {
|
|
|
15771
16007
|
}
|
|
15772
16008
|
const backupNote = opts.all ? "" : ` (\uBCF4\uC874 ${backupKept}\uAC1C)`;
|
|
15773
16009
|
process.stdout.write(
|
|
15774
|
-
|
|
16010
|
+
chalk4.green(`\u2713 \uC2A4\uD14C\uC774\uC9D5 ${stagingDeleted}\uAC1C, \uBC31\uC5C5 ${backupDeleted}\uAC1C \uC815\uB9AC${backupNote}
|
|
15775
16011
|
`)
|
|
15776
16012
|
);
|
|
15777
16013
|
}
|
|
@@ -15874,7 +16110,7 @@ async function configShow() {
|
|
|
15874
16110
|
}
|
|
15875
16111
|
|
|
15876
16112
|
// src/mcp-cmd.ts
|
|
15877
|
-
import
|
|
16113
|
+
import chalk5 from "chalk";
|
|
15878
16114
|
async function mcpList() {
|
|
15879
16115
|
const config = await loadConfig();
|
|
15880
16116
|
const cwd = process.cwd();
|
|
@@ -15893,22 +16129,22 @@ async function mcpList() {
|
|
|
15893
16129
|
return;
|
|
15894
16130
|
}
|
|
15895
16131
|
process.stdout.write(
|
|
15896
|
-
|
|
16132
|
+
chalk5.bold("\uC774\uB984".padEnd(20)) + chalk5.bold("\uC0C1\uD0DC".padEnd(12)) + chalk5.bold("\uD234 \uC218".padEnd(8)) + chalk5.bold("\uC0AC\uC720") + "\n"
|
|
15897
16133
|
);
|
|
15898
16134
|
process.stdout.write("\u2500".repeat(70) + "\n");
|
|
15899
16135
|
for (const s of statuses) {
|
|
15900
|
-
const stateStr = s.state === "connected" ?
|
|
16136
|
+
const stateStr = s.state === "connected" ? chalk5.green("\uC5F0\uACB0\uB428") : s.state === "failed" ? chalk5.red("\uC2E4\uD328") : chalk5.yellow("\uC2A4\uD0B5");
|
|
15901
16137
|
const toolCount = s.state === "connected" ? String(s.toolCount) : "\u2014";
|
|
15902
16138
|
const reason = s.reason ?? "";
|
|
15903
16139
|
process.stdout.write(
|
|
15904
16140
|
s.name.padEnd(20) + stateStr.padEnd(20) + // chalk adds escape codes so pad more
|
|
15905
|
-
toolCount.padEnd(8) +
|
|
16141
|
+
toolCount.padEnd(8) + chalk5.dim(reason.slice(0, 50)) + "\n"
|
|
15906
16142
|
);
|
|
15907
16143
|
}
|
|
15908
16144
|
if (manager.warnings.length > 0) {
|
|
15909
16145
|
process.stdout.write("\n");
|
|
15910
16146
|
for (const w of manager.warnings) {
|
|
15911
|
-
process.stdout.write(
|
|
16147
|
+
process.stdout.write(chalk5.yellow(`\uACBD\uACE0: ${w}
|
|
15912
16148
|
`));
|
|
15913
16149
|
}
|
|
15914
16150
|
}
|
|
@@ -15921,7 +16157,7 @@ async function mcpTest(serverName) {
|
|
|
15921
16157
|
const isSkipped = skipped.find((s) => s.name === serverName);
|
|
15922
16158
|
if (isSkipped) {
|
|
15923
16159
|
process.stderr.write(
|
|
15924
|
-
|
|
16160
|
+
chalk5.yellow(`\uC11C\uBC84 '${serverName}'\uC774(\uAC00) \uC2A4\uD0B5\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${isSkipped.reason}
|
|
15925
16161
|
`)
|
|
15926
16162
|
);
|
|
15927
16163
|
return;
|
|
@@ -15929,7 +16165,7 @@ async function mcpTest(serverName) {
|
|
|
15929
16165
|
const serverConfig = servers.find((s) => s.name === serverName);
|
|
15930
16166
|
if (!serverConfig) {
|
|
15931
16167
|
process.stderr.write(
|
|
15932
|
-
|
|
16168
|
+
chalk5.red(
|
|
15933
16169
|
`\uC11C\uBC84 '${serverName}'\uC744(\uB97C) \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
15934
16170
|
'kodocagent mcp list'\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC11C\uBC84\uB97C \uD655\uC778\uD558\uC138\uC694.
|
|
15935
16171
|
`
|
|
@@ -15937,32 +16173,32 @@ async function mcpTest(serverName) {
|
|
|
15937
16173
|
);
|
|
15938
16174
|
return;
|
|
15939
16175
|
}
|
|
15940
|
-
process.stdout.write(
|
|
16176
|
+
process.stdout.write(chalk5.dim(`\uC11C\uBC84 '${serverName}' \uC5F0\uACB0 \uC911...
|
|
15941
16177
|
`));
|
|
15942
16178
|
if (serverConfig.type === "stdio") {
|
|
15943
|
-
process.stdout.write(
|
|
16179
|
+
process.stdout.write(chalk5.dim(" (\uCD5C\uCD08 \uC2E4\uD589 \uC2DC \uC11C\uBC84 \uB2E4\uC6B4\uB85C\uB4DC\uB85C \uC2DC\uAC04\uC774 \uAC78\uB9B4 \uC218 \uC788\uC2B5\uB2C8\uB2E4)\n"));
|
|
15944
16180
|
}
|
|
15945
16181
|
const manager = new McpManager();
|
|
15946
16182
|
await manager.connect([serverConfig]);
|
|
15947
16183
|
const statuses = manager.status();
|
|
15948
16184
|
const status = statuses[0];
|
|
15949
16185
|
if (!status || status.state !== "connected") {
|
|
15950
|
-
process.stderr.write(
|
|
16186
|
+
process.stderr.write(chalk5.red(`\uC5F0\uACB0 \uC2E4\uD328: ${status?.reason ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}
|
|
15951
16187
|
`));
|
|
15952
16188
|
await manager.disconnect();
|
|
15953
16189
|
return;
|
|
15954
16190
|
}
|
|
15955
|
-
process.stdout.write(
|
|
16191
|
+
process.stdout.write(chalk5.green(`\uC5F0\uACB0 \uC131\uACF5! \uD234 ${status.toolCount}\uAC1C
|
|
15956
16192
|
|
|
15957
16193
|
`));
|
|
15958
16194
|
const defs = manager.getToolDefinitions();
|
|
15959
16195
|
if (defs.length === 0) {
|
|
15960
16196
|
process.stdout.write("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD234\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n");
|
|
15961
16197
|
} else {
|
|
15962
|
-
process.stdout.write(
|
|
16198
|
+
process.stdout.write(chalk5.bold("\uD234 \uBAA9\uB85D:\n"));
|
|
15963
16199
|
for (const def of defs) {
|
|
15964
|
-
process.stdout.write(` ${
|
|
15965
|
-
${
|
|
16200
|
+
process.stdout.write(` ${chalk5.cyan(def.name)}
|
|
16201
|
+
${chalk5.dim(def.description)}
|
|
15966
16202
|
`);
|
|
15967
16203
|
}
|
|
15968
16204
|
}
|
|
@@ -16053,10 +16289,10 @@ async function runOnboarding() {
|
|
|
16053
16289
|
|
|
16054
16290
|
// src/update.ts
|
|
16055
16291
|
import { spawn } from "child_process";
|
|
16056
|
-
import { mkdir as mkdir4, readFile as
|
|
16292
|
+
import { mkdir as mkdir4, readFile as readFile16, writeFile as writeFile3 } from "fs/promises";
|
|
16057
16293
|
import { dirname as dirname3 } from "path";
|
|
16058
16294
|
function compareSemver(a, b) {
|
|
16059
|
-
const
|
|
16295
|
+
const parse2 = (v) => {
|
|
16060
16296
|
const clean = v.replace(/^v/, "").split("-")[0] ?? "";
|
|
16061
16297
|
const parts = clean.split(".").map((p) => {
|
|
16062
16298
|
const n = parseInt(p, 10);
|
|
@@ -16064,8 +16300,8 @@ function compareSemver(a, b) {
|
|
|
16064
16300
|
});
|
|
16065
16301
|
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
16066
16302
|
};
|
|
16067
|
-
const [aMaj, aMin, aPat] =
|
|
16068
|
-
const [bMaj, bMin, bPat] =
|
|
16303
|
+
const [aMaj, aMin, aPat] = parse2(a);
|
|
16304
|
+
const [bMaj, bMin, bPat] = parse2(b);
|
|
16069
16305
|
if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
|
|
16070
16306
|
if (aMin !== bMin) return aMin < bMin ? -1 : 1;
|
|
16071
16307
|
if (aPat !== bPat) return aPat < bPat ? -1 : 1;
|
|
@@ -16074,7 +16310,7 @@ function compareSemver(a, b) {
|
|
|
16074
16310
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
16075
16311
|
async function readCache(cachePath) {
|
|
16076
16312
|
try {
|
|
16077
|
-
const raw = await
|
|
16313
|
+
const raw = await readFile16(cachePath, "utf-8");
|
|
16078
16314
|
const parsed = JSON.parse(raw);
|
|
16079
16315
|
if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
|
|
16080
16316
|
return parsed;
|
|
@@ -16196,7 +16432,7 @@ program.option("-p, --print <prompt>", "\uB2E8\uBC1C \uC9C8\uC758 (\uBE44\uB300\
|
|
|
16196
16432
|
const newVersion = await checkForUpdate(cliVersion()).catch(() => null);
|
|
16197
16433
|
if (newVersion) {
|
|
16198
16434
|
process.stdout.write(
|
|
16199
|
-
|
|
16435
|
+
chalk6.yellow(`\uC0C8 \uBC84\uC804 v${newVersion} \uC0AC\uC6A9 \uAC00\uB2A5 \u2014 'kodocagent update'\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694
|
|
16200
16436
|
`)
|
|
16201
16437
|
);
|
|
16202
16438
|
}
|
|
@@ -16226,15 +16462,15 @@ program.command("sessions").description("\uC138\uC158 \uBAA9\uB85D \uD45C\uC2DC"
|
|
|
16226
16462
|
for (const s of sessions) {
|
|
16227
16463
|
const dateStr = s.mtime.toLocaleString("ko-KR");
|
|
16228
16464
|
process.stdout.write(
|
|
16229
|
-
`${
|
|
16465
|
+
`${chalk6.bold(s.id)} ${chalk6.dim(dateStr)} ${chalk6.cyan(s.meta.provider)}/${s.meta.model ?? "(\uAE30\uBCF8)"}
|
|
16230
16466
|
`
|
|
16231
16467
|
);
|
|
16232
16468
|
if (s.preview) {
|
|
16233
|
-
process.stdout.write(` ${
|
|
16234
|
-
process.stdout.write(` ${
|
|
16469
|
+
process.stdout.write(` ${chalk6.italic(`"${s.preview}"`)}`);
|
|
16470
|
+
process.stdout.write(` ${chalk6.dim(s.meta.cwd)}
|
|
16235
16471
|
`);
|
|
16236
16472
|
} else {
|
|
16237
|
-
process.stdout.write(` ${
|
|
16473
|
+
process.stdout.write(` ${chalk6.dim(s.meta.cwd)}
|
|
16238
16474
|
`);
|
|
16239
16475
|
}
|
|
16240
16476
|
}
|
|
@@ -16295,7 +16531,7 @@ async function pickSession() {
|
|
|
16295
16531
|
}
|
|
16296
16532
|
const sessions = await listSessions();
|
|
16297
16533
|
if (sessions.length === 0) {
|
|
16298
|
-
process.stdout.write(
|
|
16534
|
+
process.stdout.write(chalk6.dim("\uC7AC\uAC1C\uD560 \uC138\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n"));
|
|
16299
16535
|
return void 0;
|
|
16300
16536
|
}
|
|
16301
16537
|
const options = sessions.map((s) => {
|
|
@@ -16316,17 +16552,17 @@ async function pickSession() {
|
|
|
16316
16552
|
}
|
|
16317
16553
|
function handleError(err) {
|
|
16318
16554
|
if (err instanceof KodocError) {
|
|
16319
|
-
process.stderr.write(
|
|
16555
|
+
process.stderr.write(chalk6.red(`\uC624\uB958: ${err.message}
|
|
16320
16556
|
`));
|
|
16321
16557
|
if (err.hint) {
|
|
16322
|
-
process.stderr.write(
|
|
16558
|
+
process.stderr.write(chalk6.yellow(` \u2192 ${err.hint}
|
|
16323
16559
|
`));
|
|
16324
16560
|
}
|
|
16325
16561
|
} else if (err instanceof Error) {
|
|
16326
|
-
process.stderr.write(
|
|
16562
|
+
process.stderr.write(chalk6.red(`\uC624\uB958: ${err.message}
|
|
16327
16563
|
`));
|
|
16328
16564
|
} else {
|
|
16329
|
-
process.stderr.write(
|
|
16565
|
+
process.stderr.write(chalk6.red(`\uC54C \uC218 \uC5C6\uB294 \uC624\uB958: ${String(err)}
|
|
16330
16566
|
`));
|
|
16331
16567
|
}
|
|
16332
16568
|
process.exit(1);
|
|
@@ -16345,7 +16581,7 @@ async function runSingleTurn(prompt) {
|
|
|
16345
16581
|
await mcpManager.connect(servers);
|
|
16346
16582
|
}
|
|
16347
16583
|
for (const w of mcpManager.warnings) {
|
|
16348
|
-
process.stdout.write(
|
|
16584
|
+
process.stdout.write(chalk6.yellow(`\u26A0 ${w}
|
|
16349
16585
|
`));
|
|
16350
16586
|
}
|
|
16351
16587
|
}
|
|
@@ -16386,7 +16622,7 @@ async function runSingleTurn(prompt) {
|
|
|
16386
16622
|
if (event.type === "text-delta") {
|
|
16387
16623
|
process.stdout.write(event.text);
|
|
16388
16624
|
} else if (event.type === "error") {
|
|
16389
|
-
process.stderr.write(
|
|
16625
|
+
process.stderr.write(chalk6.red(`
|
|
16390
16626
|
\uC624\uB958: ${event.message}
|
|
16391
16627
|
`));
|
|
16392
16628
|
}
|