@kodocagent/cli 0.6.3 → 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 +1178 -1072
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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,38 +10998,44 @@ 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";
|
|
11007
|
+
import { z as z3 } from "zod";
|
|
11008
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
11009
|
+
import { blocksToMarkdown, compare } from "kordoc";
|
|
11010
|
+
import { z as z22 } from "zod";
|
|
11011
|
+
import { extname as extname3 } from "path";
|
|
10942
11012
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
10943
11013
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
10944
11014
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
10945
11015
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10946
11016
|
var import_jszip5 = __toESM(require_lib3(), 1);
|
|
10947
|
-
import {
|
|
10948
|
-
import {
|
|
10949
|
-
import {
|
|
10950
|
-
import {
|
|
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";
|
|
10951
11021
|
import { readFile as readFile5 } from "fs/promises";
|
|
10952
11022
|
import { extname as extname4 } from "path";
|
|
11023
|
+
import { scanSectionXml } from "kordoc";
|
|
10953
11024
|
import { z as z4 } from "zod";
|
|
10954
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
10955
|
-
import { extname as extname3 } from "path";
|
|
10956
|
-
import { z as z32 } from "zod";
|
|
10957
11025
|
import { readFile as readFile6 } from "fs/promises";
|
|
10958
11026
|
import { extname as extname5 } from "path";
|
|
10959
11027
|
import { z as z5 } from "zod";
|
|
10960
11028
|
import { readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
10961
11029
|
import { extname as extname6, join as join4, relative as relative2 } from "path";
|
|
10962
11030
|
import { z as z6 } from "zod";
|
|
10963
|
-
import { readFile as
|
|
11031
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
10964
11032
|
import { extname as extname7 } from "path";
|
|
10965
|
-
import {
|
|
11033
|
+
import { applySplices, buildParagraphSplices, scanSectionXml as scanSectionXml2 } from "kordoc";
|
|
10966
11034
|
import { z as z7 } from "zod";
|
|
10967
|
-
import { readFile as
|
|
10968
|
-
import {
|
|
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";
|
|
10969
11039
|
import {
|
|
10970
11040
|
Document,
|
|
10971
11041
|
HeadingLevel,
|
|
@@ -10978,52 +11048,59 @@ import {
|
|
|
10978
11048
|
WidthType
|
|
10979
11049
|
} from "docx";
|
|
10980
11050
|
import { readFile as readFile9 } from "fs/promises";
|
|
10981
|
-
import { extname as extname8 } from "path";
|
|
10982
|
-
import { z as z8 } from "zod";
|
|
10983
|
-
import { readFile as readFile10 } from "fs/promises";
|
|
10984
11051
|
import { extname as extname9 } from "path";
|
|
10985
|
-
import {
|
|
11052
|
+
import { applySplices as applySplices3, buildRangeSplices as buildRangeSplices2, scanSectionXml as scanSectionXml4 } from "kordoc";
|
|
10986
11053
|
import { z as z9 } from "zod";
|
|
11054
|
+
import { applySplices as applySplices2, buildRangeSplices, scanSectionXml as scanSectionXml3 } from "kordoc";
|
|
11055
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
11056
|
+
import { extname as extname10 } from "path";
|
|
11057
|
+
import { extractFormSchema, fillHwpx } from "kordoc";
|
|
11058
|
+
import { z as z10 } from "zod";
|
|
10987
11059
|
import { readFile as readFile11 } from "fs/promises";
|
|
10988
|
-
import { basename as basename4, extname as
|
|
11060
|
+
import { basename as basename4, extname as extname11 } from "path";
|
|
10989
11061
|
var import_jszip6 = __toESM(require_lib3(), 1);
|
|
10990
11062
|
var import_jszip7 = __toESM(require_lib3(), 1);
|
|
10991
|
-
import {
|
|
10992
|
-
import { readFile as readFile12 } from "fs/promises";
|
|
10993
|
-
import { extname as extname11 } from "path";
|
|
10994
|
-
import ExcelJS from "exceljs";
|
|
11063
|
+
import { scanSectionXml as scanSectionXml5 } from "kordoc";
|
|
10995
11064
|
import { z as z11 } from "zod";
|
|
10996
|
-
import { readFile as
|
|
11065
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
10997
11066
|
import { extname as extname12 } from "path";
|
|
11067
|
+
import ExcelJS from "exceljs";
|
|
10998
11068
|
import { z as z12 } from "zod";
|
|
10999
|
-
import { readFile as
|
|
11069
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
11000
11070
|
import { extname as extname13 } from "path";
|
|
11001
11071
|
import { z as z13 } from "zod";
|
|
11002
|
-
import { readFile as
|
|
11003
|
-
import { z as z14 } from "zod";
|
|
11004
|
-
import { readFile as readFile15 } from "fs/promises";
|
|
11072
|
+
import { readFile as readFile14, stat as stat4 } from "fs/promises";
|
|
11005
11073
|
import { extname as extname14 } from "path";
|
|
11074
|
+
import { z as z14 } from "zod";
|
|
11075
|
+
import { readFile as fsReadFile, stat as stat5 } from "fs/promises";
|
|
11006
11076
|
import { z as z15 } from "zod";
|
|
11007
|
-
import {
|
|
11077
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
11008
11078
|
import { extname as extname15 } from "path";
|
|
11009
|
-
import { markdownToHwpx } from "kordoc";
|
|
11010
11079
|
import { z as z16 } from "zod";
|
|
11011
|
-
import { stat as
|
|
11080
|
+
import { stat as stat6 } from "fs/promises";
|
|
11012
11081
|
import { extname as extname16 } from "path";
|
|
11013
|
-
import
|
|
11082
|
+
import { markdownToHwpx } from "kordoc";
|
|
11014
11083
|
import { z as z17 } from "zod";
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
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
|
+
}
|
|
11018
11091
|
function isOle2Binary(bytes) {
|
|
11019
|
-
if (bytes.length <
|
|
11020
|
-
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));
|
|
11021
11098
|
}
|
|
11022
11099
|
function hwpStructuralGuard(ext, bytes) {
|
|
11023
11100
|
const isHwpExt = ext === ".hwp";
|
|
11024
11101
|
const isOle2 = isOle2Binary(bytes);
|
|
11025
11102
|
if (!isHwpExt && !isOle2) return null;
|
|
11026
|
-
if (!isHwpExt && bytes
|
|
11103
|
+
if (!isHwpExt && isZipBinary(bytes)) return null;
|
|
11027
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.";
|
|
11028
11105
|
}
|
|
11029
11106
|
async function resolveSafePath(cwd, p) {
|
|
@@ -11525,577 +11602,272 @@ var compareDocumentsTool = {
|
|
|
11525
11602
|
return output;
|
|
11526
11603
|
}
|
|
11527
11604
|
};
|
|
11528
|
-
var
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
"\
|
|
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"
|
|
11544
11654
|
);
|
|
11545
|
-
var
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
function tokenizeHwpxXml(xml) {
|
|
11553
|
-
const tokens = [];
|
|
11554
|
-
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*/g;
|
|
11555
|
-
let startPos = 0;
|
|
11556
|
-
let m = re.exec(xml);
|
|
11557
|
-
while (m !== null) {
|
|
11558
|
-
const raw = m[0];
|
|
11559
|
-
const pos = m.index;
|
|
11560
|
-
if (raw.startsWith("<hp:tbl")) {
|
|
11561
|
-
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
11562
|
-
} else if (raw === "</hp:tbl>") {
|
|
11563
|
-
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
11564
|
-
} else if (raw.startsWith("<hp:tc")) {
|
|
11565
|
-
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
11566
|
-
} else if (raw === "</hp:tc>") {
|
|
11567
|
-
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
11568
|
-
} else if (raw === "<hp:t/>") {
|
|
11569
|
-
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
11570
|
-
} else if (raw === "<hp:t>") {
|
|
11571
|
-
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
11572
|
-
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
11573
|
-
const colM = raw.match(/colAddr="(\d+)"/);
|
|
11574
|
-
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
11575
|
-
if (colM && rowM) {
|
|
11576
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
11577
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11578
|
-
tokens.push({
|
|
11579
|
-
kind: "cell_addr",
|
|
11580
|
-
pos,
|
|
11581
|
-
end,
|
|
11582
|
-
colAddr: Number(colM[1]),
|
|
11583
|
-
rowAddr: Number(rowM[1])
|
|
11584
|
-
});
|
|
11585
|
-
}
|
|
11586
|
-
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
11587
|
-
const colM = raw.match(/colSpan="(\d+)"/);
|
|
11588
|
-
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
11589
|
-
const selfClose = xml.indexOf("/>", pos);
|
|
11590
|
-
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
11591
|
-
tokens.push({
|
|
11592
|
-
kind: "cell_span",
|
|
11593
|
-
pos,
|
|
11594
|
-
end,
|
|
11595
|
-
colSpan: colM ? Number(colM[1]) : 1,
|
|
11596
|
-
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
11597
|
-
});
|
|
11598
|
-
}
|
|
11599
|
-
startPos = re.lastIndex;
|
|
11600
|
-
m = re.exec(xml);
|
|
11601
|
-
}
|
|
11602
|
-
void startPos;
|
|
11603
|
-
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();
|
|
11604
11662
|
}
|
|
11605
|
-
function
|
|
11606
|
-
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, "&");
|
|
11607
11665
|
}
|
|
11608
|
-
function
|
|
11609
|
-
const
|
|
11610
|
-
const
|
|
11611
|
-
const
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
if (
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
} else if (tok.kind === "tc_close") {
|
|
11621
|
-
const entry = tcStack.pop();
|
|
11622
|
-
if (entry !== void 0) {
|
|
11623
|
-
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, ""));
|
|
11624
11678
|
}
|
|
11679
|
+
if (para.trim()) out.push(para);
|
|
11625
11680
|
}
|
|
11626
11681
|
}
|
|
11627
|
-
return
|
|
11682
|
+
return out;
|
|
11628
11683
|
}
|
|
11629
|
-
function
|
|
11630
|
-
const
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
for (const t of tcTokens) {
|
|
11634
|
-
if (t.kind === "tbl_open") innerDepth++;
|
|
11635
|
-
else if (t.kind === "tbl_close") innerDepth--;
|
|
11636
|
-
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11637
|
-
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11638
|
-
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11639
|
-
if (closePos >= 0) {
|
|
11640
|
-
text3 += xml.substring(t.end, closePos);
|
|
11641
|
-
}
|
|
11642
|
-
}
|
|
11643
|
-
}
|
|
11644
|
-
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] ?? "" };
|
|
11645
11688
|
}
|
|
11646
|
-
function
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
let
|
|
11650
|
-
for (const
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
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;
|
|
11663
11707
|
}
|
|
11664
11708
|
}
|
|
11665
11709
|
}
|
|
11666
|
-
return
|
|
11667
|
-
}
|
|
11668
|
-
function readCellAddrSpan(tokens, tcStart, tcEnd) {
|
|
11669
|
-
const tcTokens = tokens.filter((t) => t.pos >= tcStart && t.pos < tcEnd);
|
|
11670
|
-
let addr = null;
|
|
11671
|
-
let span = { colSpan: 1, rowSpan: 1 };
|
|
11672
|
-
for (const t of tcTokens) {
|
|
11673
|
-
if (t.kind === "cell_addr" && t.colAddr !== void 0 && t.rowAddr !== void 0) {
|
|
11674
|
-
addr = { colAddr: t.colAddr, rowAddr: t.rowAddr };
|
|
11675
|
-
} else if (t.kind === "cell_span" && t.colSpan !== void 0 && t.rowSpan !== void 0) {
|
|
11676
|
-
span = { colSpan: t.colSpan, rowSpan: t.rowSpan };
|
|
11677
|
-
}
|
|
11678
|
-
}
|
|
11679
|
-
if (!addr) return null;
|
|
11680
|
-
return { ...addr, ...span };
|
|
11710
|
+
return changed ? lines.join("\n") : markdown;
|
|
11681
11711
|
}
|
|
11682
|
-
function
|
|
11683
|
-
const
|
|
11684
|
-
const
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
d++;
|
|
11692
|
-
} else if (tok.kind === "tbl_close") {
|
|
11693
|
-
d--;
|
|
11694
|
-
}
|
|
11695
|
-
}
|
|
11696
|
-
}
|
|
11697
|
-
const replacements = [];
|
|
11698
|
-
for (let ei = 0; ei < edits.length; ei++) {
|
|
11699
|
-
const edit = edits[ei];
|
|
11700
|
-
const tblRange = findTopLevelTableRange(tokens, edit.tableIndex);
|
|
11701
|
-
if (!tblRange) {
|
|
11702
|
-
results[ei] = {
|
|
11703
|
-
success: false,
|
|
11704
|
-
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.`
|
|
11705
|
-
};
|
|
11706
|
-
continue;
|
|
11707
|
-
}
|
|
11708
|
-
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11709
|
-
const tblTokens = tokens.filter((t) => t.pos >= tblRange.start && t.pos < tblRange.end);
|
|
11710
|
-
let found = false;
|
|
11711
|
-
for (const tc of directTcs) {
|
|
11712
|
-
const tcTokens = tblTokens.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11713
|
-
const hasAddr = tcTokens.some(
|
|
11714
|
-
(t) => t.kind === "cell_addr" && t.colAddr === edit.col && t.rowAddr === edit.row
|
|
11715
|
-
);
|
|
11716
|
-
if (!hasAddr) continue;
|
|
11717
|
-
let innerDepth = 0;
|
|
11718
|
-
const ownTRuns = [];
|
|
11719
|
-
for (const t of tokens.filter((x) => x.pos >= tc.start && x.pos < tc.end)) {
|
|
11720
|
-
if (t.kind === "tbl_open") innerDepth++;
|
|
11721
|
-
else if (t.kind === "tbl_close") innerDepth--;
|
|
11722
|
-
else if (t.kind === "t_empty" && innerDepth === 0) {
|
|
11723
|
-
ownTRuns.push({ isEmpty: true, tagPos: t.pos, tagEnd: t.end });
|
|
11724
|
-
} else if (t.kind === "t_open" && innerDepth === 0) {
|
|
11725
|
-
const closePos = xml.indexOf("</hp:t>", t.end);
|
|
11726
|
-
if (closePos >= 0) {
|
|
11727
|
-
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;
|
|
11728
11721
|
}
|
|
11729
|
-
}
|
|
11730
|
-
}
|
|
11731
|
-
const currentText = ownTRuns.map((r) => r.isEmpty ? "" : xml.substring(r.openEnd, r.closePos)).join("").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11732
|
-
if (edit.expectedText !== void 0 && edit.expectedText !== currentText) {
|
|
11733
|
-
results[ei] = {
|
|
11734
|
-
success: false,
|
|
11735
|
-
oldText: currentText,
|
|
11736
|
-
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.`
|
|
11737
|
-
};
|
|
11738
|
-
found = true;
|
|
11739
|
-
break;
|
|
11740
|
-
}
|
|
11741
|
-
if (ownTRuns.length === 0) {
|
|
11742
|
-
results[ei] = {
|
|
11743
|
-
success: false,
|
|
11744
|
-
oldText: currentText,
|
|
11745
|
-
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.`
|
|
11746
|
-
};
|
|
11747
|
-
found = true;
|
|
11748
|
-
break;
|
|
11749
|
-
}
|
|
11750
|
-
const escapedNew = escapeXml(edit.newText);
|
|
11751
|
-
const patches = [];
|
|
11752
|
-
const firstRun = ownTRuns[0];
|
|
11753
|
-
if (firstRun.isEmpty) {
|
|
11754
|
-
patches.push({
|
|
11755
|
-
from: firstRun.tagPos,
|
|
11756
|
-
to: firstRun.tagEnd,
|
|
11757
|
-
text: `<hp:t>${escapedNew}</hp:t>`
|
|
11758
11722
|
});
|
|
11759
|
-
} else {
|
|
11760
|
-
patches.push({ from: firstRun.openEnd, to: firstRun.closePos, text: escapedNew });
|
|
11761
11723
|
}
|
|
11762
|
-
|
|
11763
|
-
const
|
|
11764
|
-
|
|
11765
|
-
|
|
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
|
+
}
|
|
11766
11735
|
}
|
|
11767
11736
|
}
|
|
11768
|
-
|
|
11769
|
-
results[ei] = { success: true, oldText: currentText };
|
|
11770
|
-
found = true;
|
|
11771
|
-
break;
|
|
11737
|
+
if (b.children) walk(b.children);
|
|
11772
11738
|
}
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
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.`
|
|
11777
|
-
};
|
|
11778
|
-
}
|
|
11779
|
-
}
|
|
11780
|
-
if (results.some((r) => !r.success)) {
|
|
11781
|
-
return { newXml: xml, results };
|
|
11782
|
-
}
|
|
11783
|
-
const allPatches = replacements.flatMap((r) => r.patches).sort((a, b) => b.from - a.from);
|
|
11784
|
-
let result = xml;
|
|
11785
|
-
for (const patch of allPatches) {
|
|
11786
|
-
result = result.substring(0, patch.from) + patch.text + result.substring(patch.to);
|
|
11787
|
-
}
|
|
11788
|
-
return { newXml: result, results };
|
|
11739
|
+
};
|
|
11740
|
+
walk(blocks);
|
|
11741
|
+
return slots;
|
|
11789
11742
|
}
|
|
11790
|
-
|
|
11791
|
-
const
|
|
11792
|
-
const
|
|
11793
|
-
|
|
11794
|
-
const
|
|
11795
|
-
|
|
11796
|
-
const
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
depth--;
|
|
11808
|
-
}
|
|
11809
|
-
}
|
|
11810
|
-
sectionTblCounts.push(count);
|
|
11811
|
-
}
|
|
11812
|
-
const sectionEdits = sectionFiles.map(() => []);
|
|
11813
|
-
let offset = 0;
|
|
11814
|
-
for (let si = 0; si < sectionFiles.length; si++) {
|
|
11815
|
-
const count = sectionTblCounts[si] ?? 0;
|
|
11816
|
-
for (let ei = 0; ei < edits.length; ei++) {
|
|
11817
|
-
const edit = edits[ei];
|
|
11818
|
-
if (edit.tableIndex >= offset && edit.tableIndex < offset + count) {
|
|
11819
|
-
const secEdits = sectionEdits[si];
|
|
11820
|
-
if (secEdits) {
|
|
11821
|
-
secEdits.push({
|
|
11822
|
-
tableIndex: edit.tableIndex - offset,
|
|
11823
|
-
// 섹션 내 상대 인덱스
|
|
11824
|
-
row: edit.row,
|
|
11825
|
-
col: edit.col,
|
|
11826
|
-
newText: edit.newText,
|
|
11827
|
-
expectedText: edit.expectedText,
|
|
11828
|
-
originalEditIdx: ei
|
|
11829
|
-
});
|
|
11830
|
-
}
|
|
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;
|
|
11831
11760
|
}
|
|
11832
11761
|
}
|
|
11833
|
-
offset += count;
|
|
11834
11762
|
}
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
if (
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
newSectionXmls[si] = newXml;
|
|
11847
|
-
for (let i = 0; i < sEdits.length; i++) {
|
|
11848
|
-
const sEdit = sEdits[i];
|
|
11849
|
-
const res = results[i];
|
|
11850
|
-
if (sEdit && res) {
|
|
11851
|
-
allResults[sEdit.originalEditIdx] = res;
|
|
11852
|
-
}
|
|
11763
|
+
return changed;
|
|
11764
|
+
}
|
|
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));
|
|
11853
11774
|
}
|
|
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;
|
|
11854
11779
|
}
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
if (mimetypeEntry) {
|
|
11861
|
-
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
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;
|
|
11862
11785
|
}
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
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 };
|
|
11870
11797
|
}
|
|
11798
|
+
} catch {
|
|
11871
11799
|
}
|
|
11872
|
-
|
|
11873
|
-
return { buffer: new Uint8Array(buf), results: allResults };
|
|
11800
|
+
return result;
|
|
11874
11801
|
}
|
|
11875
|
-
var
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
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,
|
|
11879
11812
|
requiresApproval: true,
|
|
11880
11813
|
propose: async ({
|
|
11881
11814
|
input,
|
|
11882
11815
|
ctx
|
|
11883
11816
|
}) => {
|
|
11884
11817
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
11885
|
-
const
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
try {
|
|
11891
|
-
originalBuffer = await readFile4(safePath);
|
|
11892
|
-
} catch {
|
|
11893
|
-
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.`;
|
|
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.`;
|
|
11894
11823
|
}
|
|
11895
|
-
const
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
return
|
|
11902
|
-
}
|
|
11903
|
-
const zipForLabel = await import_jszip2.default.loadAsync(new Uint8Array(originalBuffer.buffer));
|
|
11904
|
-
const sectionFilesForLabel = Object.keys(zipForLabel.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
11905
|
-
const sectionInfos = [];
|
|
11906
|
-
let globalTblOffset = 0;
|
|
11907
|
-
for (const sf of sectionFilesForLabel) {
|
|
11908
|
-
const entry = zipForLabel.file(sf);
|
|
11909
|
-
const xml = entry ? await entry.async("string") : "";
|
|
11910
|
-
const tokens = tokenizeHwpxXml(xml);
|
|
11911
|
-
let count = 0;
|
|
11912
|
-
let d = 0;
|
|
11913
|
-
for (const tok of tokens) {
|
|
11914
|
-
if (tok.kind === "tbl_open") {
|
|
11915
|
-
if (d === 0) count++;
|
|
11916
|
-
d++;
|
|
11917
|
-
} else if (tok.kind === "tbl_close") {
|
|
11918
|
-
d--;
|
|
11919
|
-
}
|
|
11920
|
-
}
|
|
11921
|
-
sectionInfos.push({ xml, tblCount: count, globalOffset: globalTblOffset });
|
|
11922
|
-
globalTblOffset += count;
|
|
11923
|
-
}
|
|
11924
|
-
function resolveLabelAcrossSections(label, direction, scopedTableIndex) {
|
|
11925
|
-
const trimmedLabel = label.trim();
|
|
11926
|
-
const allMatches = [];
|
|
11927
|
-
for (const si of sectionInfos) {
|
|
11928
|
-
const tokens = tokenizeHwpxXml(si.xml);
|
|
11929
|
-
let localStart = 0;
|
|
11930
|
-
let localEnd = si.tblCount - 1;
|
|
11931
|
-
if (scopedTableIndex !== void 0) {
|
|
11932
|
-
const localIdx2 = scopedTableIndex - si.globalOffset;
|
|
11933
|
-
if (localIdx2 < 0 || localIdx2 >= si.tblCount) continue;
|
|
11934
|
-
localStart = localIdx2;
|
|
11935
|
-
localEnd = localIdx2;
|
|
11936
|
-
}
|
|
11937
|
-
for (let li = localStart; li <= localEnd; li++) {
|
|
11938
|
-
const tblRange = findTopLevelTableRange(tokens, li);
|
|
11939
|
-
if (!tblRange) continue;
|
|
11940
|
-
const directTcs = collectDirectTcRanges(tokens, tblRange.start, tblRange.end);
|
|
11941
|
-
for (const tc of directTcs) {
|
|
11942
|
-
const cellText = readOwnTextFromTc(si.xml, tokens, tc.start, tc.end).trim();
|
|
11943
|
-
if (cellText === trimmedLabel) {
|
|
11944
|
-
const addrSpan2 = readCellAddrSpan(tokens, tc.start, tc.end);
|
|
11945
|
-
if (addrSpan2) {
|
|
11946
|
-
allMatches.push({
|
|
11947
|
-
globalTableIndex: si.globalOffset + li,
|
|
11948
|
-
addrSpan: addrSpan2,
|
|
11949
|
-
sectionXml: si.xml,
|
|
11950
|
-
sectionOffset: si.globalOffset
|
|
11951
|
-
});
|
|
11952
|
-
}
|
|
11953
|
-
}
|
|
11954
|
-
}
|
|
11955
|
-
}
|
|
11956
|
-
}
|
|
11957
|
-
if (allMatches.length === 0) {
|
|
11958
|
-
const scope = scopedTableIndex !== void 0 ? `\uD45C ${scopedTableIndex}` : "\uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uD45C";
|
|
11959
|
-
return {
|
|
11960
|
-
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.`
|
|
11961
|
-
};
|
|
11962
|
-
}
|
|
11963
|
-
if (allMatches.length > 1) {
|
|
11964
|
-
const locs = allMatches.map(
|
|
11965
|
-
(m) => `\uD45C ${m.globalTableIndex} (\uD589 ${m.addrSpan.rowAddr}, \uC5F4 ${m.addrSpan.colAddr})`
|
|
11966
|
-
).join(", ");
|
|
11967
|
-
return {
|
|
11968
|
-
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.`
|
|
11969
|
-
};
|
|
11970
|
-
}
|
|
11971
|
-
const match = allMatches[0];
|
|
11972
|
-
const { addrSpan, globalTableIndex, sectionXml, sectionOffset } = match;
|
|
11973
|
-
let targetRow;
|
|
11974
|
-
let targetCol;
|
|
11975
|
-
if (direction === "right") {
|
|
11976
|
-
targetRow = addrSpan.rowAddr;
|
|
11977
|
-
targetCol = addrSpan.colAddr + addrSpan.colSpan;
|
|
11978
|
-
} else {
|
|
11979
|
-
targetRow = addrSpan.rowAddr + addrSpan.rowSpan;
|
|
11980
|
-
targetCol = addrSpan.colAddr;
|
|
11981
|
-
}
|
|
11982
|
-
const localIdx = globalTableIndex - sectionOffset;
|
|
11983
|
-
const tokens2 = tokenizeHwpxXml(sectionXml);
|
|
11984
|
-
const tblRange2 = findTopLevelTableRange(tokens2, localIdx);
|
|
11985
|
-
if (!tblRange2) {
|
|
11986
|
-
return { error: `\uD45C ${globalTableIndex}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958).` };
|
|
11987
|
-
}
|
|
11988
|
-
const directTcs2 = collectDirectTcRanges(tokens2, tblRange2.start, tblRange2.end);
|
|
11989
|
-
const tblTokens2 = tokens2.filter((t) => t.pos >= tblRange2.start && t.pos < tblRange2.end);
|
|
11990
|
-
let targetExists = false;
|
|
11991
|
-
for (const tc of directTcs2) {
|
|
11992
|
-
const tcTokens = tblTokens2.filter((t) => t.pos >= tc.start && t.pos < tc.end);
|
|
11993
|
-
if (tcTokens.some(
|
|
11994
|
-
(t) => t.kind === "cell_addr" && t.colAddr === targetCol && t.rowAddr === targetRow
|
|
11995
|
-
)) {
|
|
11996
|
-
targetExists = true;
|
|
11997
|
-
break;
|
|
11998
|
-
}
|
|
11999
|
-
}
|
|
12000
|
-
if (!targetExists) {
|
|
12001
|
-
const dirLabel = direction === "right" ? "\uC624\uB978\uCABD" : "\uC544\uB798";
|
|
12002
|
-
return {
|
|
12003
|
-
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.`
|
|
12004
|
-
};
|
|
12005
|
-
}
|
|
12006
|
-
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}`;
|
|
12007
11831
|
}
|
|
12008
|
-
const
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
);
|
|
12017
|
-
}
|
|
12018
|
-
const
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
resolveErrors.push(`\uD3B8\uC9D1 #${i + 1} (\uB808\uC774\uBE14 "${e.label}"): ${resolved.error}`);
|
|
12022
|
-
} else {
|
|
12023
|
-
resolvedEdits.push({
|
|
12024
|
-
tableIndex: resolved.tableIndex,
|
|
12025
|
-
row: resolved.row,
|
|
12026
|
-
col: resolved.col,
|
|
12027
|
-
newText: e.newText,
|
|
12028
|
-
expectedText: e.expectedText,
|
|
12029
|
-
label: e.label
|
|
12030
|
-
});
|
|
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.";
|
|
12031
11845
|
}
|
|
12032
|
-
|
|
12033
|
-
resolvedEdits.push({
|
|
12034
|
-
tableIndex: e.tableIndex,
|
|
12035
|
-
row: e.row,
|
|
12036
|
-
col: e.col,
|
|
12037
|
-
newText: e.newText,
|
|
12038
|
-
expectedText: e.expectedText
|
|
12039
|
-
});
|
|
12040
|
-
} else {
|
|
12041
|
-
resolveErrors.push(
|
|
12042
|
-
`\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.`
|
|
12043
|
-
);
|
|
11846
|
+
return `\uC624\uB958: PDF \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${m}`;
|
|
12044
11847
|
}
|
|
12045
11848
|
}
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
row: e.row,
|
|
12053
|
-
col: e.col,
|
|
12054
|
-
newText: e.newText,
|
|
12055
|
-
expectedText: e.expectedText
|
|
12056
|
-
}));
|
|
12057
|
-
const { buffer: newBuffer, results } = await applyEditsToHwpx(
|
|
12058
|
-
new Uint8Array(originalBuffer.buffer),
|
|
12059
|
-
editRequests
|
|
12060
|
-
);
|
|
12061
|
-
const failedResults = results.map((r, i) => ({ r, i })).filter(({ r }) => !r.success);
|
|
12062
|
-
if (failedResults.length > 0) {
|
|
12063
|
-
const messages = failedResults.map(({ r, i }) => {
|
|
12064
|
-
const e = resolvedEdits[i];
|
|
12065
|
-
const label = e?.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 ` : "";
|
|
12066
|
-
return `\uD3B8\uC9D1 #${i + 1} (${label}\uD45C ${e?.tableIndex ?? "?"}, \uD589 ${e?.row ?? "?"}, \uC5F4 ${e?.col ?? "?"}): ${r.error}`;
|
|
12067
|
-
});
|
|
12068
|
-
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.
|
|
12069
|
-
${messages.join("\n")}`;
|
|
12070
|
-
}
|
|
12071
|
-
const diffLines = ["| \uD45C\xB7\uC140 | \uC774\uC804 | \uC774\uD6C4 |", "| --- | --- | --- |"];
|
|
12072
|
-
for (let i = 0; i < resolvedEdits.length; i++) {
|
|
12073
|
-
const e = resolvedEdits[i];
|
|
12074
|
-
if (!e) continue;
|
|
12075
|
-
const oldText = results[i]?.oldText ?? "";
|
|
12076
|
-
const addr = e.label ? `\uB808\uC774\uBE14 "${e.label}" \u2192 #${e.tableIndex} (${e.row},${e.col})` : `#${e.tableIndex} (${e.row},${e.col})`;
|
|
12077
|
-
diffLines.push(`| ${addr} | ${oldText} | ${e.newText} |`);
|
|
12078
|
-
}
|
|
12079
|
-
const diff = diffLines.join("\n");
|
|
12080
|
-
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
12081
|
-
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)`;
|
|
12082
11855
|
const proposalId = crypto.randomUUID();
|
|
12083
11856
|
return {
|
|
12084
11857
|
proposal: {
|
|
12085
11858
|
id: proposalId,
|
|
12086
|
-
kind: "
|
|
12087
|
-
targetPath:
|
|
11859
|
+
kind: "export",
|
|
11860
|
+
targetPath: outPath,
|
|
12088
11861
|
stagedPath,
|
|
12089
|
-
summary: input.summary
|
|
12090
|
-
diff,
|
|
12091
|
-
warnings
|
|
12092
|
-
willConvertFormat
|
|
11862
|
+
summary: input.summary ?? `${input.path} \u2192 ${format.toUpperCase()} \uB0B4\uBCF4\uB0B4\uAE30`,
|
|
11863
|
+
diff: preview,
|
|
11864
|
+
warnings
|
|
12093
11865
|
},
|
|
12094
11866
|
commit: async () => {
|
|
12095
|
-
const backupPath = await backupFile(
|
|
12096
|
-
await commitStaged(stagedPath,
|
|
12097
|
-
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
12098
|
-
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}`;
|
|
12099
11871
|
}
|
|
12100
11872
|
};
|
|
12101
11873
|
}
|
|
@@ -12105,9 +11877,6 @@ var findInDocumentSchema = z4.object({
|
|
|
12105
11877
|
query: z4.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
12106
11878
|
caseSensitive: z4.boolean().optional().describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8 false)")
|
|
12107
11879
|
});
|
|
12108
|
-
function unescapeXml(text3) {
|
|
12109
|
-
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
12110
|
-
}
|
|
12111
11880
|
function windowText(text3, query, caseSensitive, maxLen = 60) {
|
|
12112
11881
|
const compare3 = caseSensitive ? text3 : text3.toLowerCase();
|
|
12113
11882
|
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
@@ -12122,91 +11891,31 @@ function windowText(text3, query, caseSensitive, maxLen = 60) {
|
|
|
12122
11891
|
}
|
|
12123
11892
|
function findInSectionXmls(xmls, query, caseSensitive) {
|
|
12124
11893
|
const hits = [];
|
|
11894
|
+
const needle = caseSensitive ? query : query.toLowerCase();
|
|
11895
|
+
const includes = (text3) => (caseSensitive ? text3 : text3.toLowerCase()).includes(needle);
|
|
12125
11896
|
let globalOffset = 0;
|
|
12126
11897
|
for (let si = 0; si < xmls.length; si++) {
|
|
12127
11898
|
const xml = xmls[si] ?? "";
|
|
12128
|
-
const
|
|
12129
|
-
let
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
for (const
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
}
|
|
12139
|
-
}
|
|
12140
|
-
}
|
|
12141
|
-
for (let ti = 0; ti < localCount; ti++) {
|
|
12142
|
-
const range = findTopLevelTableRange(tokens, ti);
|
|
12143
|
-
if (!range) continue;
|
|
12144
|
-
const cells = collectDirectTcRanges(tokens, range.start, range.end);
|
|
12145
|
-
const tblTokens = tokens.filter((t) => t.pos >= range.start && t.pos < range.end);
|
|
12146
|
-
for (const cell of cells) {
|
|
12147
|
-
const cellText = readOwnTextFromTc(xml, tokens, cell.start, cell.end);
|
|
12148
|
-
const compare3 = caseSensitive ? cellText : cellText.toLowerCase();
|
|
12149
|
-
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12150
|
-
if (!compare3.includes(compareQuery)) continue;
|
|
12151
|
-
const cellTokens = tblTokens.filter((t) => t.pos >= cell.start && t.pos < cell.end);
|
|
12152
|
-
const addrTok = cellTokens.find((t) => t.kind === "cell_addr");
|
|
12153
|
-
if (addrTok === void 0) continue;
|
|
12154
|
-
const row = addrTok.rowAddr ?? 0;
|
|
12155
|
-
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]);
|
|
12156
11909
|
const truncated = cellText.length > 60 ? `${cellText.slice(0, 57)}\u2026` : cellText;
|
|
12157
|
-
hits.push({
|
|
12158
|
-
kind: "\uD45C",
|
|
12159
|
-
tableIndex: globalOffset + ti,
|
|
12160
|
-
row,
|
|
12161
|
-
col,
|
|
12162
|
-
text: truncated
|
|
12163
|
-
});
|
|
11910
|
+
hits.push({ kind: "\uD45C", tableIndex: globalOffset + ti, row, col, text: truncated });
|
|
12164
11911
|
}
|
|
12165
11912
|
}
|
|
12166
|
-
{
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
let segStart = 0;
|
|
12170
|
-
for (const tok of tokens) {
|
|
12171
|
-
if (tok.kind === "tbl_open") {
|
|
12172
|
-
if (tblDepth === 0) {
|
|
12173
|
-
if (tok.pos > segStart) {
|
|
12174
|
-
bodySegments.push({ start: segStart, end: tok.pos });
|
|
12175
|
-
}
|
|
12176
|
-
}
|
|
12177
|
-
tblDepth++;
|
|
12178
|
-
} else if (tok.kind === "tbl_close") {
|
|
12179
|
-
tblDepth--;
|
|
12180
|
-
if (tblDepth === 0) {
|
|
12181
|
-
segStart = tok.end;
|
|
12182
|
-
}
|
|
12183
|
-
}
|
|
12184
|
-
}
|
|
12185
|
-
if (segStart < xml.length) {
|
|
12186
|
-
bodySegments.push({ start: segStart, end: xml.length });
|
|
12187
|
-
}
|
|
12188
|
-
const tRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
12189
|
-
for (const seg of bodySegments) {
|
|
12190
|
-
const slice = xml.slice(seg.start, seg.end);
|
|
12191
|
-
tRe.lastIndex = 0;
|
|
12192
|
-
let m = tRe.exec(slice);
|
|
12193
|
-
while (m !== null) {
|
|
12194
|
-
const rawText = m[1] ?? "";
|
|
12195
|
-
const decodedText = unescapeXml(rawText);
|
|
12196
|
-
const compare3 = caseSensitive ? decodedText : decodedText.toLowerCase();
|
|
12197
|
-
const compareQuery = caseSensitive ? query : query.toLowerCase();
|
|
12198
|
-
if (compare3.includes(compareQuery)) {
|
|
12199
|
-
hits.push({
|
|
12200
|
-
kind: "\uBCF8\uBB38",
|
|
12201
|
-
section: si,
|
|
12202
|
-
text: windowText(decodedText, query, caseSensitive)
|
|
12203
|
-
});
|
|
12204
|
-
}
|
|
12205
|
-
m = tRe.exec(slice);
|
|
12206
|
-
}
|
|
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) });
|
|
12207
11916
|
}
|
|
12208
11917
|
}
|
|
12209
|
-
globalOffset +=
|
|
11918
|
+
globalOffset += scan.tables.length;
|
|
12210
11919
|
}
|
|
12211
11920
|
return hits;
|
|
12212
11921
|
}
|
|
@@ -12250,12 +11959,12 @@ var findInDocumentTool = {
|
|
|
12250
11959
|
} catch {
|
|
12251
11960
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12252
11961
|
}
|
|
12253
|
-
if (bytes
|
|
11962
|
+
if (!isZipBinary(bytes)) {
|
|
12254
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.";
|
|
12255
11964
|
}
|
|
12256
11965
|
let zip;
|
|
12257
11966
|
try {
|
|
12258
|
-
zip = await
|
|
11967
|
+
zip = await import_jszip2.default.loadAsync(bytes);
|
|
12259
11968
|
} catch (err) {
|
|
12260
11969
|
return `\uC624\uB958: .hwpx ZIP\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${String(err)}`;
|
|
12261
11970
|
}
|
|
@@ -12286,7 +11995,7 @@ var findInDocumentTool = {
|
|
|
12286
11995
|
return formatHits(hits, input.query);
|
|
12287
11996
|
}
|
|
12288
11997
|
};
|
|
12289
|
-
function
|
|
11998
|
+
function escapeXml(text3) {
|
|
12290
11999
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
12291
12000
|
}
|
|
12292
12001
|
function decodeXml(text3) {
|
|
@@ -12438,7 +12147,7 @@ function applyFormObjectEdits(xml, edits) {
|
|
|
12438
12147
|
}
|
|
12439
12148
|
let patchCreated = false;
|
|
12440
12149
|
if (set.caption !== void 0) {
|
|
12441
|
-
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption",
|
|
12150
|
+
const patch = replaceAttrInOpenTag(xml, pos, openTagEnd, "caption", escapeXml(set.caption));
|
|
12442
12151
|
if (!patch) {
|
|
12443
12152
|
results[ei] = {
|
|
12444
12153
|
success: false,
|
|
@@ -12466,7 +12175,7 @@ function applyFormObjectEdits(xml, edits) {
|
|
|
12466
12175
|
pos,
|
|
12467
12176
|
openTagEnd,
|
|
12468
12177
|
"selectedValue",
|
|
12469
|
-
|
|
12178
|
+
escapeXml(set.selected)
|
|
12470
12179
|
);
|
|
12471
12180
|
if (!patch) {
|
|
12472
12181
|
results[ei] = {
|
|
@@ -12592,7 +12301,7 @@ function replaceAttrInOpenTag(xml, tagStart, tagEnd, attr, newValue) {
|
|
|
12592
12301
|
};
|
|
12593
12302
|
}
|
|
12594
12303
|
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
12595
|
-
const escaped =
|
|
12304
|
+
const escaped = escapeXml(newText);
|
|
12596
12305
|
const selfCloseRe = /<hp:text\s*\/>/;
|
|
12597
12306
|
const scm = selfCloseRe.exec(elementContent);
|
|
12598
12307
|
if (scm) {
|
|
@@ -12639,7 +12348,7 @@ function validateHwpxBuffer(ext, buffer) {
|
|
|
12639
12348
|
if (structuralGuard !== null) {
|
|
12640
12349
|
return structuralGuard;
|
|
12641
12350
|
}
|
|
12642
|
-
if (buffer
|
|
12351
|
+
if (!isZipBinary(buffer)) {
|
|
12643
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.";
|
|
12644
12353
|
}
|
|
12645
12354
|
return null;
|
|
@@ -12996,146 +12705,377 @@ var listFilesTool = {
|
|
|
12996
12705
|
return lines.join("\n") + truncateNotice;
|
|
12997
12706
|
}
|
|
12998
12707
|
};
|
|
12999
|
-
var
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
"\
|
|
13004
|
-
"\
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
"\
|
|
13009
|
-
"\
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
"\uC624\
|
|
13015
|
-
"\uC721\uAC01\uD615",
|
|
13016
|
-
"\uD314\uAC01\uD615",
|
|
13017
|
-
"\uBCC4",
|
|
13018
|
-
"[4-8]\uC810\uBCC4",
|
|
13019
|
-
"\uC2ED\uC790",
|
|
13020
|
-
"\uC2ED\uC790\uD615",
|
|
13021
|
-
"\uAD6C\uB984",
|
|
13022
|
-
"\uAD6C\uB984\uD615",
|
|
13023
|
-
"\uB9C8\uB984\uBAA8",
|
|
13024
|
-
"\uB3C4\uB11B",
|
|
13025
|
-
"\uD3C9\uD589\uC0AC\uBCC0\uD615",
|
|
13026
|
-
"\uC0AC\uB2E4\uB9AC\uAF34",
|
|
13027
|
-
"\uBD80\uCC44\uAF34",
|
|
13028
|
-
"\uD638",
|
|
13029
|
-
"\uBC18\uC6D0",
|
|
13030
|
-
"\uBB3C\uACB0",
|
|
13031
|
-
"\uBC88\uAC1C",
|
|
13032
|
-
"\uD558\uD2B8",
|
|
13033
|
-
"\uBE57\uAE08",
|
|
13034
|
-
"\uBE14\uB85D \uD654\uC0B4\uD45C",
|
|
13035
|
-
"\uC218\uC2DD",
|
|
13036
|
-
"\uD45C",
|
|
13037
|
-
"\uADF8\uB9BC",
|
|
13038
|
-
"\uAC1C\uCCB4",
|
|
13039
|
-
"\uADF8\uB9AC\uAE30\\s?\uAC1C\uCCB4",
|
|
13040
|
-
"\uBB36\uC74C\\s?\uAC1C\uCCB4",
|
|
13041
|
-
"\uAE00\uC0C1\uC790",
|
|
13042
|
-
"\uC218\uC2DD\\s?\uAC1C\uCCB4",
|
|
13043
|
-
"OLE\\s?\uAC1C\uCCB4"
|
|
13044
|
-
].join("|");
|
|
13045
|
-
var BUGGY_SHAPE_STRIP = new RegExp(
|
|
13046
|
-
`(?:\uBAA8\uC11C\uB9AC\uAC00 \uB465\uADFC |\uB465\uADFC )?(?:${SHAPE_ALT_KEYWORDS})\\s?\uC785\uB2C8\uB2E4\\.?`,
|
|
13047
|
-
"g"
|
|
13048
|
-
);
|
|
13049
|
-
var FIXED_SHAPE_STRIP = new RegExp(
|
|
13050
|
-
`(?<![\uAC00-\uD7A3])(?:\uBAA8\uC11C\uB9AC\uAC00 \uB465\uADFC |\uB465\uADFC )?(?:${SHAPE_ALT_KEYWORDS})\\s?\uC785\uB2C8\uB2E4\\.?`,
|
|
13051
|
-
"g"
|
|
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."
|
|
13052
12724
|
);
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
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("");
|
|
13056
12734
|
}
|
|
13057
|
-
function
|
|
13058
|
-
return
|
|
12735
|
+
function cellAt(table, row, col) {
|
|
12736
|
+
return table.cellByAnchor.get(`${row},${col}`);
|
|
13059
12737
|
}
|
|
13060
|
-
|
|
13061
|
-
const
|
|
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) {
|
|
13062
12809
|
const out = [];
|
|
13063
|
-
for (
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
for (const m of seg.matchAll(/<hp:t(?:\s[^>]*)?>([\s\S]*?)<\/hp:t>/g)) {
|
|
13071
|
-
para += decodeXmlEntities((m[1] ?? "").replace(/<[^>]+>/g, ""));
|
|
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 });
|
|
13072
12817
|
}
|
|
13073
|
-
if (para.trim()) out.push(para);
|
|
13074
12818
|
}
|
|
13075
|
-
}
|
|
13076
|
-
return out;
|
|
13077
|
-
}
|
|
13078
|
-
function
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
const
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
const
|
|
13089
|
-
const
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
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
|
+
);
|
|
13101
13020
|
}
|
|
13102
13021
|
}
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
}
|
|
13106
|
-
function isZip(bytes) {
|
|
13107
|
-
return bytes.length >= 4 && bytes[0] === 80 && bytes[1] === 75;
|
|
13108
|
-
}
|
|
13109
|
-
async function resolveHwpxBytes(input) {
|
|
13110
|
-
try {
|
|
13111
|
-
if (typeof input === "string") {
|
|
13112
|
-
if (!/\.hwpx$/i.test(input)) return null;
|
|
13113
|
-
return new Uint8Array(await readFile7(input));
|
|
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")}`;
|
|
13114
13025
|
}
|
|
13115
|
-
const
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
|
|
13124
|
-
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
return
|
|
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")}`;
|
|
13134
13046
|
}
|
|
13135
|
-
|
|
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} |`);
|
|
13054
|
+
}
|
|
13055
|
+
const diff = diffLines.join("\n");
|
|
13056
|
+
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
13057
|
+
const stagedPath = await stageFile(ctx.sessionId, safePath, newBuffer);
|
|
13058
|
+
const proposalId = crypto.randomUUID();
|
|
13059
|
+
return {
|
|
13060
|
+
proposal: {
|
|
13061
|
+
id: proposalId,
|
|
13062
|
+
kind: "cell-edit",
|
|
13063
|
+
targetPath: outputPath,
|
|
13064
|
+
stagedPath,
|
|
13065
|
+
summary: input.summary,
|
|
13066
|
+
diff,
|
|
13067
|
+
warnings: [],
|
|
13068
|
+
willConvertFormat
|
|
13069
|
+
},
|
|
13070
|
+
commit: async () => {
|
|
13071
|
+
const backupPath = await backupFile(safePath);
|
|
13072
|
+
await commitStaged(stagedPath, outputPath);
|
|
13073
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
13074
|
+
return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
13075
|
+
}
|
|
13076
|
+
};
|
|
13136
13077
|
}
|
|
13137
|
-
|
|
13138
|
-
}
|
|
13078
|
+
};
|
|
13139
13079
|
var HEADING_LEVELS = {
|
|
13140
13080
|
1: HeadingLevel.HEADING_1,
|
|
13141
13081
|
2: HeadingLevel.HEADING_2,
|
|
@@ -13258,10 +13198,10 @@ async function markdownToDocx(markdown) {
|
|
|
13258
13198
|
});
|
|
13259
13199
|
return Packer.toBuffer(doc);
|
|
13260
13200
|
}
|
|
13261
|
-
var proposeEditSchema =
|
|
13262
|
-
path:
|
|
13263
|
-
newMarkdown:
|
|
13264
|
-
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)")
|
|
13265
13205
|
});
|
|
13266
13206
|
var proposeEditTool = {
|
|
13267
13207
|
name: "propose_edit",
|
|
@@ -13273,7 +13213,7 @@ var proposeEditTool = {
|
|
|
13273
13213
|
ctx
|
|
13274
13214
|
}) => {
|
|
13275
13215
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13276
|
-
const ext =
|
|
13216
|
+
const ext = extname8(safePath).toLowerCase();
|
|
13277
13217
|
let originalBuffer;
|
|
13278
13218
|
try {
|
|
13279
13219
|
originalBuffer = await readFile8(safePath);
|
|
@@ -13354,19 +13294,52 @@ ${diff}`;
|
|
|
13354
13294
|
};
|
|
13355
13295
|
}
|
|
13356
13296
|
};
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
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)")
|
|
13364
13337
|
});
|
|
13365
13338
|
var MAX_DIFF_SAMPLES = 20;
|
|
13366
|
-
function
|
|
13339
|
+
function escapeXml2(text3) {
|
|
13367
13340
|
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
13368
13341
|
}
|
|
13369
|
-
function
|
|
13342
|
+
function unescapeXml(text3) {
|
|
13370
13343
|
return text3.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
13371
13344
|
}
|
|
13372
13345
|
function makeChangeSnippet(before, after, ctx = 24) {
|
|
@@ -13390,7 +13363,7 @@ function collectChangedSnippets(beforeXml, afterXml, maxSamples) {
|
|
|
13390
13363
|
const n = Math.min(beforeNodes.length, afterNodes.length);
|
|
13391
13364
|
for (let i = 0; i < n && out.length < maxSamples; i++) {
|
|
13392
13365
|
if (beforeNodes[i] !== afterNodes[i]) {
|
|
13393
|
-
const snip = makeChangeSnippet(
|
|
13366
|
+
const snip = makeChangeSnippet(unescapeXml(beforeNodes[i]), unescapeXml(afterNodes[i]));
|
|
13394
13367
|
out.push(snip);
|
|
13395
13368
|
}
|
|
13396
13369
|
}
|
|
@@ -13400,8 +13373,8 @@ function replaceInSectionXml(xml, find, replace, caseSensitive, replaceAll, alre
|
|
|
13400
13373
|
if (!replaceAll && alreadyReplaced > 0) {
|
|
13401
13374
|
return { xml, count: 0 };
|
|
13402
13375
|
}
|
|
13403
|
-
const escapedFind =
|
|
13404
|
-
const escapedReplace =
|
|
13376
|
+
const escapedFind = escapeXml2(find);
|
|
13377
|
+
const escapedReplace = escapeXml2(replace);
|
|
13405
13378
|
if (escapedFind.length === 0) {
|
|
13406
13379
|
return { xml, count: 0 };
|
|
13407
13380
|
}
|
|
@@ -13472,6 +13445,31 @@ function countOccurrences(str, sub) {
|
|
|
13472
13445
|
}
|
|
13473
13446
|
return count;
|
|
13474
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
|
+
}
|
|
13475
13473
|
async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
|
|
13476
13474
|
const zip = await import_jszip5.default.loadAsync(hwpxBuffer);
|
|
13477
13475
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
@@ -13485,7 +13483,9 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13485
13483
|
const newSectionXmls = [];
|
|
13486
13484
|
for (let si = 0; si < sectionFiles.length; si++) {
|
|
13487
13485
|
const srcXml = sectionXmls[si] ?? "";
|
|
13488
|
-
|
|
13486
|
+
let newXml;
|
|
13487
|
+
let count;
|
|
13488
|
+
const spliced = replaceSectionViaSplices(
|
|
13489
13489
|
srcXml,
|
|
13490
13490
|
find,
|
|
13491
13491
|
replace,
|
|
@@ -13493,6 +13493,21 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
|
|
|
13493
13493
|
replaceAll,
|
|
13494
13494
|
totalCount
|
|
13495
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
|
+
}
|
|
13496
13511
|
newSectionXmls.push(newXml);
|
|
13497
13512
|
totalCount += count;
|
|
13498
13513
|
if (!replaceAll && totalCount >= 1) {
|
|
@@ -13548,7 +13563,7 @@ var proposeFindReplaceTool = {
|
|
|
13548
13563
|
ctx
|
|
13549
13564
|
}) => {
|
|
13550
13565
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13551
|
-
const ext =
|
|
13566
|
+
const ext = extname9(safePath).toLowerCase();
|
|
13552
13567
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13553
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.`;
|
|
13554
13569
|
}
|
|
@@ -13567,7 +13582,7 @@ var proposeFindReplaceTool = {
|
|
|
13567
13582
|
if (structuralGuard !== null) {
|
|
13568
13583
|
return structuralGuard;
|
|
13569
13584
|
}
|
|
13570
|
-
if (
|
|
13585
|
+
if (!isZipBinary(originalBytes)) {
|
|
13571
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.";
|
|
13572
13587
|
}
|
|
13573
13588
|
let newBytes;
|
|
@@ -13651,10 +13666,10 @@ var proposeFindReplaceTool = {
|
|
|
13651
13666
|
};
|
|
13652
13667
|
}
|
|
13653
13668
|
};
|
|
13654
|
-
var proposeFormFillSchema =
|
|
13655
|
-
path:
|
|
13656
|
-
fields:
|
|
13657
|
-
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)")
|
|
13658
13673
|
});
|
|
13659
13674
|
var proposeFormFillTool = {
|
|
13660
13675
|
name: "propose_form_fill",
|
|
@@ -13666,7 +13681,7 @@ var proposeFormFillTool = {
|
|
|
13666
13681
|
ctx
|
|
13667
13682
|
}) => {
|
|
13668
13683
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13669
|
-
const ext =
|
|
13684
|
+
const ext = extname10(safePath).toLowerCase();
|
|
13670
13685
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
13671
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.`;
|
|
13672
13687
|
}
|
|
@@ -13691,9 +13706,19 @@ var proposeFormFillTool = {
|
|
|
13691
13706
|
);
|
|
13692
13707
|
return `\uC624\uB958: ${msg}`;
|
|
13693
13708
|
}
|
|
13694
|
-
const
|
|
13695
|
-
const existingFields = new Map(
|
|
13709
|
+
const formSchema = extractFormSchema(parseResult.blocks);
|
|
13710
|
+
const existingFields = new Map(formSchema.fields.map((f) => [f.label, f.value]));
|
|
13696
13711
|
const warnings = [];
|
|
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(", ")}.` : "";
|
|
13697
13722
|
const diffLines = ["| \uB77C\uBCA8 | \uC774\uC804 \uAC12 | \uC0C8 \uAC12 |", "| --- | --- | --- |"];
|
|
13698
13723
|
for (const [label, newValue] of Object.entries(input.fields)) {
|
|
13699
13724
|
const oldValue = existingFields.get(label) ?? "(\uC5C6\uC74C)";
|
|
@@ -13709,11 +13734,11 @@ var proposeFormFillTool = {
|
|
|
13709
13734
|
const fillResult = await fillHwpx(origAB, input.fields);
|
|
13710
13735
|
if (fillResult.unmatched.length > 0) {
|
|
13711
13736
|
warnings.push(
|
|
13712
|
-
`\uB2E4\uC74C \uB77C\uBCA8\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${fillResult.unmatched.join(", ")}
|
|
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."}`
|
|
13713
13738
|
);
|
|
13714
13739
|
}
|
|
13715
13740
|
if (fillResult.filled.length === 0) {
|
|
13716
|
-
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
|
|
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."}`;
|
|
13717
13742
|
}
|
|
13718
13743
|
stagedData = new Uint8Array(fillResult.buffer);
|
|
13719
13744
|
} catch (err) {
|
|
@@ -13742,9 +13767,9 @@ var proposeFormFillTool = {
|
|
|
13742
13767
|
};
|
|
13743
13768
|
}
|
|
13744
13769
|
};
|
|
13745
|
-
var proposeRedactPiiSchema =
|
|
13746
|
-
path:
|
|
13747
|
-
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")
|
|
13748
13773
|
});
|
|
13749
13774
|
function mergeFindings(allFindings) {
|
|
13750
13775
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -13765,6 +13790,29 @@ function mergeFindings(allFindings) {
|
|
|
13765
13790
|
}
|
|
13766
13791
|
return [...map.values()];
|
|
13767
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
|
+
}
|
|
13768
13816
|
async function applyRedactToHwpx(hwpxBuffer) {
|
|
13769
13817
|
const zip = await import_jszip6.default.loadAsync(hwpxBuffer);
|
|
13770
13818
|
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
@@ -13778,33 +13826,23 @@ async function applyRedactToHwpx(hwpxBuffer) {
|
|
|
13778
13826
|
const allFindings = [];
|
|
13779
13827
|
let anyChanged = false;
|
|
13780
13828
|
for (const srcXml of sectionXmls) {
|
|
13781
|
-
const
|
|
13782
|
-
const
|
|
13783
|
-
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
13789
|
-
|
|
13790
|
-
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
result = result.substring(0, contentStart) + redacted + result.substring(contentEnd);
|
|
13798
|
-
offset += redacted.length - content.length;
|
|
13799
|
-
anyChanged = true;
|
|
13800
|
-
}
|
|
13801
|
-
if (findings.length > 0) {
|
|
13802
|
-
sectionFindings.push(findings);
|
|
13803
|
-
}
|
|
13804
|
-
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;
|
|
13805
13845
|
}
|
|
13806
|
-
newSectionXmls.push(result);
|
|
13807
|
-
allFindings.push(...sectionFindings);
|
|
13808
13846
|
}
|
|
13809
13847
|
if (!anyChanged) {
|
|
13810
13848
|
return { buffer: hwpxBuffer, findings: mergeFindings(allFindings), changed: false };
|
|
@@ -13849,7 +13887,7 @@ var proposeRedactPiiTool = {
|
|
|
13849
13887
|
ctx
|
|
13850
13888
|
}) => {
|
|
13851
13889
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13852
|
-
const ext =
|
|
13890
|
+
const ext = extname11(safePath).toLowerCase();
|
|
13853
13891
|
if (ext !== ".hwpx" && ext !== ".hwp" && ext !== ".md" && ext !== ".txt") {
|
|
13854
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.";
|
|
13855
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}`;
|
|
@@ -13871,7 +13909,7 @@ var proposeRedactPiiTool = {
|
|
|
13871
13909
|
if (structuralGuard !== null) {
|
|
13872
13910
|
return structuralGuard;
|
|
13873
13911
|
}
|
|
13874
|
-
if (
|
|
13912
|
+
if (!isZipBinary(originalBytes)) {
|
|
13875
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.";
|
|
13876
13914
|
}
|
|
13877
13915
|
let patchResult;
|
|
@@ -13943,16 +13981,16 @@ var proposeRedactPiiTool = {
|
|
|
13943
13981
|
};
|
|
13944
13982
|
}
|
|
13945
13983
|
};
|
|
13946
|
-
var proposeSheetEditSchema =
|
|
13947
|
-
path:
|
|
13948
|
-
updates:
|
|
13949
|
-
|
|
13950
|
-
sheet:
|
|
13951
|
-
cell:
|
|
13952
|
-
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")
|
|
13953
13991
|
})
|
|
13954
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"),
|
|
13955
|
-
summary:
|
|
13993
|
+
summary: z12.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13956
13994
|
});
|
|
13957
13995
|
var proposeSheetEditTool = {
|
|
13958
13996
|
name: "propose_sheet_edit",
|
|
@@ -13964,7 +14002,7 @@ var proposeSheetEditTool = {
|
|
|
13964
14002
|
ctx
|
|
13965
14003
|
}) => {
|
|
13966
14004
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13967
|
-
const ext =
|
|
14005
|
+
const ext = extname12(safePath).toLowerCase();
|
|
13968
14006
|
if (ext !== ".xlsx" && ext !== ".xls") {
|
|
13969
14007
|
return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
|
|
13970
14008
|
}
|
|
@@ -14063,49 +14101,49 @@ function detectStructuralLoss(beforeBlocks, afterBlocks) {
|
|
|
14063
14101
|
}
|
|
14064
14102
|
return { lost: false, detail: "" };
|
|
14065
14103
|
}
|
|
14066
|
-
var insertRowOpSchema =
|
|
14067
|
-
type:
|
|
14068
|
-
row:
|
|
14069
|
-
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")
|
|
14070
14108
|
});
|
|
14071
|
-
var deleteRowOpSchema =
|
|
14072
|
-
type:
|
|
14073
|
-
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)")
|
|
14074
14112
|
});
|
|
14075
|
-
var insertColumnOpSchema =
|
|
14076
|
-
type:
|
|
14077
|
-
col:
|
|
14078
|
-
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")
|
|
14079
14117
|
});
|
|
14080
|
-
var deleteColumnOpSchema =
|
|
14081
|
-
type:
|
|
14082
|
-
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)")
|
|
14083
14121
|
});
|
|
14084
|
-
var mergeCellsOpSchema =
|
|
14085
|
-
type:
|
|
14086
|
-
startRow:
|
|
14087
|
-
startCol:
|
|
14088
|
-
endRow:
|
|
14089
|
-
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)")
|
|
14090
14128
|
});
|
|
14091
|
-
var operationSchema =
|
|
14129
|
+
var operationSchema = z13.discriminatedUnion("type", [
|
|
14092
14130
|
insertRowOpSchema,
|
|
14093
14131
|
deleteRowOpSchema,
|
|
14094
14132
|
insertColumnOpSchema,
|
|
14095
14133
|
deleteColumnOpSchema,
|
|
14096
14134
|
mergeCellsOpSchema
|
|
14097
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.");
|
|
14098
|
-
var proposeTableStructureSchema =
|
|
14099
|
-
path:
|
|
14100
|
-
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(
|
|
14101
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."
|
|
14102
14140
|
),
|
|
14103
|
-
operations:
|
|
14141
|
+
operations: z13.array(operationSchema).min(1).describe(
|
|
14104
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."
|
|
14105
14143
|
),
|
|
14106
|
-
summary:
|
|
14144
|
+
summary: z13.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
14107
14145
|
});
|
|
14108
|
-
function
|
|
14146
|
+
function tokenizeHwpxXml(xml) {
|
|
14109
14147
|
const tokens = [];
|
|
14110
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;
|
|
14111
14149
|
let m = re.exec(xml);
|
|
@@ -14295,7 +14333,7 @@ function setCellSpan(tcXml, colSpan, rowSpan) {
|
|
|
14295
14333
|
return tcXml.replace(/(colSpan=")(\d+)(")/, `$1${colSpan}$3`).replace(/(rowSpan=")(\d+)(")/, `$1${rowSpan}$3`);
|
|
14296
14334
|
}
|
|
14297
14335
|
function insertRowInTbl(tblXml, row, below) {
|
|
14298
|
-
const tokens =
|
|
14336
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14299
14337
|
const tblStart = 0;
|
|
14300
14338
|
const tblEnd = tblXml.length;
|
|
14301
14339
|
const rows = parseTableRows(tblXml, tokens, tblStart, tblEnd);
|
|
@@ -14337,7 +14375,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14337
14375
|
modifiedTr = modifiedTr.substring(0, tcLocalStart) + tcXml + modifiedTr.substring(tcLocalEnd);
|
|
14338
14376
|
}
|
|
14339
14377
|
let result = tblXml;
|
|
14340
|
-
const allTokens =
|
|
14378
|
+
const allTokens = tokenizeHwpxXml(result);
|
|
14341
14379
|
const addrPatches = [];
|
|
14342
14380
|
for (const tok of allTokens) {
|
|
14343
14381
|
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr >= newRowAddr) {
|
|
@@ -14350,7 +14388,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14350
14388
|
const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
14351
14389
|
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
14352
14390
|
}
|
|
14353
|
-
const resultTokens =
|
|
14391
|
+
const resultTokens = tokenizeHwpxXml(result);
|
|
14354
14392
|
const resultRows = parseTableRows(result, resultTokens, 0, result.length);
|
|
14355
14393
|
let insertAfterPos;
|
|
14356
14394
|
let insertBeforePos;
|
|
@@ -14382,7 +14420,7 @@ function insertRowInTbl(tblXml, row, below) {
|
|
|
14382
14420
|
return { ok: true, xml: result };
|
|
14383
14421
|
}
|
|
14384
14422
|
function deleteRowInTbl(tblXml, row) {
|
|
14385
|
-
const tokens =
|
|
14423
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14386
14424
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14387
14425
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14388
14426
|
if (rows.length === 0) {
|
|
@@ -14419,7 +14457,7 @@ function deleteRowInTbl(tblXml, row) {
|
|
|
14419
14457
|
}
|
|
14420
14458
|
let result = tblXml;
|
|
14421
14459
|
result = result.substring(0, targetRow.trStart) + result.substring(targetRow.trEnd);
|
|
14422
|
-
const afterTokens =
|
|
14460
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14423
14461
|
const addrPatches = [];
|
|
14424
14462
|
for (const tok of afterTokens) {
|
|
14425
14463
|
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr > row) {
|
|
@@ -14436,7 +14474,7 @@ function deleteRowInTbl(tblXml, row) {
|
|
|
14436
14474
|
return { ok: true, xml: result };
|
|
14437
14475
|
}
|
|
14438
14476
|
function insertColumnInTbl(tblXml, col, right) {
|
|
14439
|
-
const tokens =
|
|
14477
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14440
14478
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14441
14479
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14442
14480
|
if (rows.length === 0) {
|
|
@@ -14457,7 +14495,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14457
14495
|
}
|
|
14458
14496
|
}
|
|
14459
14497
|
let result = tblXml;
|
|
14460
|
-
const allTokens =
|
|
14498
|
+
const allTokens = tokenizeHwpxXml(result);
|
|
14461
14499
|
const addrPatches = [];
|
|
14462
14500
|
for (const tok of allTokens) {
|
|
14463
14501
|
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr >= newColAddr) {
|
|
@@ -14470,7 +14508,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14470
14508
|
const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
14471
14509
|
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
14472
14510
|
}
|
|
14473
|
-
const resultTokens2 =
|
|
14511
|
+
const resultTokens2 = tokenizeHwpxXml(result);
|
|
14474
14512
|
const resultRows = parseTableRows(result, resultTokens2, 0, result.length);
|
|
14475
14513
|
const sortedRows = [...resultRows].sort((a, b) => b.trStart - a.trStart);
|
|
14476
14514
|
for (const r of sortedRows) {
|
|
@@ -14497,7 +14535,7 @@ function insertColumnInTbl(tblXml, col, right) {
|
|
|
14497
14535
|
return { ok: true, xml: result };
|
|
14498
14536
|
}
|
|
14499
14537
|
function deleteColumnInTbl(tblXml, col) {
|
|
14500
|
-
const tokens =
|
|
14538
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14501
14539
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14502
14540
|
const dims = parseTblDimensions(tblXml, 0);
|
|
14503
14541
|
if (rows.length === 0) {
|
|
@@ -14531,7 +14569,7 @@ function deleteColumnInTbl(tblXml, col) {
|
|
|
14531
14569
|
result = result.substring(0, cell.tcStart) + result.substring(cell.tcEnd);
|
|
14532
14570
|
}
|
|
14533
14571
|
}
|
|
14534
|
-
const afterTokens =
|
|
14572
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14535
14573
|
const addrPatches = [];
|
|
14536
14574
|
for (const tok of afterTokens) {
|
|
14537
14575
|
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr > col) {
|
|
@@ -14554,7 +14592,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14554
14592
|
if (startRow > endRow || startCol > endCol) {
|
|
14555
14593
|
return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (start > end)." };
|
|
14556
14594
|
}
|
|
14557
|
-
const tokens =
|
|
14595
|
+
const tokens = tokenizeHwpxXml(tblXml);
|
|
14558
14596
|
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
14559
14597
|
const rangeCells = [];
|
|
14560
14598
|
for (const r of rows) {
|
|
@@ -14610,7 +14648,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14610
14648
|
for (const c of sortedRemove) {
|
|
14611
14649
|
result = result.substring(0, c.tcStart) + result.substring(c.tcEnd);
|
|
14612
14650
|
}
|
|
14613
|
-
const afterTokens =
|
|
14651
|
+
const afterTokens = tokenizeHwpxXml(result);
|
|
14614
14652
|
const afterRows = parseTableRows(result, afterTokens, 0, result.length);
|
|
14615
14653
|
const afterTopLeft = afterRows.flatMap((r) => r.cells).find((c) => c.rowAddr === startRow && c.colAddr === startCol);
|
|
14616
14654
|
if (!afterTopLeft) {
|
|
@@ -14622,7 +14660,7 @@ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
|
14622
14660
|
return { ok: true, xml: result };
|
|
14623
14661
|
}
|
|
14624
14662
|
function findTableByAnchorInXml(xml, anchor) {
|
|
14625
|
-
const tokens =
|
|
14663
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
14626
14664
|
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
14627
14665
|
const trimmedAnchor = anchor.trim();
|
|
14628
14666
|
const matched = [];
|
|
@@ -14748,7 +14786,7 @@ async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt
|
|
|
14748
14786
|
for (const sf of sectionFiles) {
|
|
14749
14787
|
const entry = zip.file(sf);
|
|
14750
14788
|
const xml = entry ? await entry.async("string") : "";
|
|
14751
|
-
const tokens =
|
|
14789
|
+
const tokens = tokenizeHwpxXml(xml);
|
|
14752
14790
|
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
14753
14791
|
const trimmedAnchor = anchor.trim();
|
|
14754
14792
|
for (const r of allRanges) {
|
|
@@ -14778,7 +14816,7 @@ var proposeTableStructureTool = {
|
|
|
14778
14816
|
ctx
|
|
14779
14817
|
}) => {
|
|
14780
14818
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
14781
|
-
const ext =
|
|
14819
|
+
const ext = extname13(safePath).toLowerCase();
|
|
14782
14820
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
14783
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.`;
|
|
14784
14822
|
}
|
|
@@ -14797,7 +14835,7 @@ var proposeTableStructureTool = {
|
|
|
14797
14835
|
if (structuralGuard !== null) {
|
|
14798
14836
|
return structuralGuard;
|
|
14799
14837
|
}
|
|
14800
|
-
if (
|
|
14838
|
+
if (!isZipBinary(originalBytes)) {
|
|
14801
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.";
|
|
14802
14840
|
}
|
|
14803
14841
|
let originalBlocks = null;
|
|
@@ -14984,11 +15022,11 @@ function searchExcerpts(markdown, query) {
|
|
|
14984
15022
|
}
|
|
14985
15023
|
return result;
|
|
14986
15024
|
}
|
|
14987
|
-
var readDocumentSchema =
|
|
14988
|
-
path:
|
|
14989
|
-
pages:
|
|
14990
|
-
outline:
|
|
14991
|
-
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)")
|
|
14992
15030
|
});
|
|
14993
15031
|
function applyReadMode(body, outline, search) {
|
|
14994
15032
|
if (outline === true) {
|
|
@@ -15017,7 +15055,7 @@ var readDocumentTool = {
|
|
|
15017
15055
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15018
15056
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15019
15057
|
}
|
|
15020
|
-
const ext =
|
|
15058
|
+
const ext = extname14(safePath).toLowerCase();
|
|
15021
15059
|
if (PLAIN_TEXT_EXTS.has(ext)) {
|
|
15022
15060
|
let raw;
|
|
15023
15061
|
try {
|
|
@@ -15121,8 +15159,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
15121
15159
|
".csv",
|
|
15122
15160
|
".log"
|
|
15123
15161
|
]);
|
|
15124
|
-
var readFileSchema =
|
|
15125
|
-
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)")
|
|
15126
15164
|
});
|
|
15127
15165
|
var readFileTool = {
|
|
15128
15166
|
name: "read_file",
|
|
@@ -15146,8 +15184,8 @@ var readFileTool = {
|
|
|
15146
15184
|
if (info.size > MAX_SIZE) {
|
|
15147
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.`;
|
|
15148
15186
|
}
|
|
15149
|
-
const { extname:
|
|
15150
|
-
const ext =
|
|
15187
|
+
const { extname: extname18 } = await import("path");
|
|
15188
|
+
const ext = extname18(safePath).toLowerCase();
|
|
15151
15189
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
15152
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.`;
|
|
15153
15191
|
}
|
|
@@ -15162,8 +15200,8 @@ var readFileTool = {
|
|
|
15162
15200
|
}
|
|
15163
15201
|
};
|
|
15164
15202
|
var PLAIN_TEXT_EXTS2 = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
|
|
15165
|
-
var scanPiiSchema =
|
|
15166
|
-
path:
|
|
15203
|
+
var scanPiiSchema = z16.object({
|
|
15204
|
+
path: z16.string().describe("\uAC1C\uC778\uC815\uBCF4\uB97C \uC810\uAC80\uD560 \uBB38\uC11C \uACBD\uB85C")
|
|
15167
15205
|
});
|
|
15168
15206
|
var scanPiiTool = {
|
|
15169
15207
|
name: "scan_pii",
|
|
@@ -15181,7 +15219,7 @@ var scanPiiTool = {
|
|
|
15181
15219
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15182
15220
|
return `\uC624\uB958: \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
15183
15221
|
}
|
|
15184
|
-
const ext =
|
|
15222
|
+
const ext = extname15(safePath).toLowerCase();
|
|
15185
15223
|
let text3;
|
|
15186
15224
|
if (PLAIN_TEXT_EXTS2.has(ext)) {
|
|
15187
15225
|
try {
|
|
@@ -15215,10 +15253,10 @@ var scanPiiTool = {
|
|
|
15215
15253
|
return lines.join("\n");
|
|
15216
15254
|
}
|
|
15217
15255
|
};
|
|
15218
|
-
var
|
|
15219
|
-
var writeNewDocumentSchema =
|
|
15220
|
-
path:
|
|
15221
|
-
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)")
|
|
15222
15260
|
});
|
|
15223
15261
|
var writeNewDocumentTool = {
|
|
15224
15262
|
name: "write_new_document",
|
|
@@ -15230,7 +15268,7 @@ var writeNewDocumentTool = {
|
|
|
15230
15268
|
ctx
|
|
15231
15269
|
}) => {
|
|
15232
15270
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
15233
|
-
const ext =
|
|
15271
|
+
const ext = extname16(safePath).toLowerCase();
|
|
15234
15272
|
try {
|
|
15235
15273
|
await stat6(safePath);
|
|
15236
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.`;
|
|
@@ -15251,9 +15289,9 @@ var writeNewDocumentTool = {
|
|
|
15251
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.`;
|
|
15252
15290
|
}
|
|
15253
15291
|
const stagedPath = await stageFile(ctx.sessionId, safePath, stagedData);
|
|
15254
|
-
const preview = input.markdown.length >
|
|
15292
|
+
const preview = input.markdown.length > MAX_PREVIEW_CHARS2 ? `${input.markdown.slice(0, MAX_PREVIEW_CHARS2)}
|
|
15255
15293
|
|
|
15256
|
-
...\uC774\uD558 \uC0DD\uB7B5 (${input.markdown.length -
|
|
15294
|
+
...\uC774\uD558 \uC0DD\uB7B5 (${input.markdown.length - MAX_PREVIEW_CHARS2}\uC790 \uB354 \uC788\uC74C)` : input.markdown;
|
|
15257
15295
|
const proposalId = crypto.randomUUID();
|
|
15258
15296
|
return {
|
|
15259
15297
|
proposal: {
|
|
@@ -15274,12 +15312,12 @@ ${preview}`,
|
|
|
15274
15312
|
};
|
|
15275
15313
|
}
|
|
15276
15314
|
};
|
|
15277
|
-
var writeNewSpreadsheetSchema =
|
|
15278
|
-
path:
|
|
15279
|
-
sheets:
|
|
15280
|
-
|
|
15281
|
-
name:
|
|
15282
|
-
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)")
|
|
15283
15321
|
})
|
|
15284
15322
|
).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
|
|
15285
15323
|
});
|
|
@@ -15293,7 +15331,7 @@ var writeNewSpreadsheetTool = {
|
|
|
15293
15331
|
ctx
|
|
15294
15332
|
}) => {
|
|
15295
15333
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
15296
|
-
const ext =
|
|
15334
|
+
const ext = extname17(safePath).toLowerCase();
|
|
15297
15335
|
if (ext !== ".xlsx") {
|
|
15298
15336
|
return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
|
|
15299
15337
|
}
|
|
@@ -15355,6 +15393,7 @@ function createDocTools(_ctx) {
|
|
|
15355
15393
|
readFileTool,
|
|
15356
15394
|
scanPiiTool,
|
|
15357
15395
|
findInDocumentTool,
|
|
15396
|
+
exportDocumentTool,
|
|
15358
15397
|
proposeEditTool,
|
|
15359
15398
|
proposeFormFillTool,
|
|
15360
15399
|
proposeCellEditTool,
|
|
@@ -15371,13 +15410,13 @@ function createDocTools(_ctx) {
|
|
|
15371
15410
|
}
|
|
15372
15411
|
|
|
15373
15412
|
// src/index.ts
|
|
15374
|
-
import
|
|
15413
|
+
import chalk6 from "chalk";
|
|
15375
15414
|
import { Command } from "commander";
|
|
15376
15415
|
|
|
15377
15416
|
// src/chat.ts
|
|
15378
15417
|
import * as readline from "readline/promises";
|
|
15379
15418
|
import { isCancel as isCancel2, select as select2, spinner, text } from "@clack/prompts";
|
|
15380
|
-
import
|
|
15419
|
+
import chalk3 from "chalk";
|
|
15381
15420
|
|
|
15382
15421
|
// src/approve.ts
|
|
15383
15422
|
import { isCancel, select } from "@clack/prompts";
|
|
@@ -15453,6 +15492,45 @@ function createCliApprovalHandler() {
|
|
|
15453
15492
|
};
|
|
15454
15493
|
}
|
|
15455
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
|
+
|
|
15456
15534
|
// src/chat.ts
|
|
15457
15535
|
var HELP_TEXT = `
|
|
15458
15536
|
\uD560 \uC218 \uC788\uB294 \uC77C:
|
|
@@ -15466,6 +15544,7 @@ var HELP_TEXT = `
|
|
|
15466
15544
|
\uC2AC\uB798\uC2DC \uBA85\uB839:
|
|
15467
15545
|
/model \u2014 \uD504\uB85C\uBC14\uC774\uB354/\uBAA8\uB378 \uC804\uD658
|
|
15468
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)
|
|
15469
15548
|
/clear \u2014 \uC0C8 \uC138\uC158 \uC2DC\uC791
|
|
15470
15549
|
/help \u2014 \uC774 \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC
|
|
15471
15550
|
/exit \u2014 \uC885\uB8CC
|
|
@@ -15498,44 +15577,44 @@ async function runChat(opts) {
|
|
|
15498
15577
|
const okCount = mcpManager.connectedServerNames.length;
|
|
15499
15578
|
s.stop(okCount > 0 ? `MCP \uC11C\uBC84 ${okCount}\uAC1C \uC5F0\uACB0\uB428` : "MCP \uC5F0\uACB0 \uC644\uB8CC");
|
|
15500
15579
|
} else {
|
|
15501
|
-
process.stdout.write(
|
|
15580
|
+
process.stdout.write(chalk3.dim(`${connectMsg}
|
|
15502
15581
|
`));
|
|
15503
15582
|
await mcpManager.connect(servers);
|
|
15504
15583
|
const okCount = mcpManager.connectedServerNames.length;
|
|
15505
15584
|
process.stdout.write(
|
|
15506
|
-
|
|
15585
|
+
chalk3.dim(`${okCount > 0 ? `MCP \uC11C\uBC84 ${okCount}\uAC1C \uC5F0\uACB0\uB428` : "MCP \uC5F0\uACB0 \uC644\uB8CC"}
|
|
15507
15586
|
`)
|
|
15508
15587
|
);
|
|
15509
15588
|
}
|
|
15510
15589
|
}
|
|
15511
15590
|
for (const s of mcpManager.status()) {
|
|
15512
15591
|
if (s.state === "failed") {
|
|
15513
|
-
process.stdout.write(
|
|
15592
|
+
process.stdout.write(chalk3.dim(`MCP [${s.name}] \uC5F0\uACB0 \uC2E4\uD328: ${s.reason ?? ""}
|
|
15514
15593
|
`));
|
|
15515
15594
|
} else if (s.state === "skipped") {
|
|
15516
|
-
process.stdout.write(
|
|
15595
|
+
process.stdout.write(chalk3.dim(`MCP [${s.name}] \uC2A4\uD0B5: ${s.reason ?? ""}
|
|
15517
15596
|
`));
|
|
15518
15597
|
}
|
|
15519
15598
|
}
|
|
15520
15599
|
for (const w of mcpManager.warnings) {
|
|
15521
|
-
process.stdout.write(
|
|
15600
|
+
process.stdout.write(chalk3.yellow(`\u26A0 ${w}
|
|
15522
15601
|
`));
|
|
15523
15602
|
}
|
|
15524
15603
|
}
|
|
15525
15604
|
let store;
|
|
15526
15605
|
if (opts.resumeId) {
|
|
15527
15606
|
store = await SessionStore.load(opts.resumeId);
|
|
15528
|
-
process.stdout.write(
|
|
15607
|
+
process.stdout.write(chalk3.dim(`\uC138\uC158 \uC7AC\uAC1C: ${opts.resumeId}
|
|
15529
15608
|
`));
|
|
15530
15609
|
} else if (opts.continueLatest) {
|
|
15531
15610
|
const sessions = await listSessions();
|
|
15532
15611
|
const latest = sessions[0];
|
|
15533
15612
|
if (!latest) {
|
|
15534
|
-
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"));
|
|
15535
15614
|
store = await createNewStore(config, cwd);
|
|
15536
15615
|
} else {
|
|
15537
15616
|
store = await SessionStore.load(latest.id);
|
|
15538
|
-
process.stdout.write(
|
|
15617
|
+
process.stdout.write(chalk3.dim(`\uC774\uC804 \uC138\uC158 \uC7AC\uAC1C: ${latest.id}
|
|
15539
15618
|
`));
|
|
15540
15619
|
}
|
|
15541
15620
|
} else {
|
|
@@ -15549,6 +15628,8 @@ async function runChat(opts) {
|
|
|
15549
15628
|
let ctrlCCount = 0;
|
|
15550
15629
|
let currentController = null;
|
|
15551
15630
|
let lastContextTokens = 0;
|
|
15631
|
+
let cumulativeInputTokens = 0;
|
|
15632
|
+
let cumulativeOutputTokens = 0;
|
|
15552
15633
|
let sharedActiveInterval = null;
|
|
15553
15634
|
function clearSharedSpinner() {
|
|
15554
15635
|
if (!sharedActiveInterval) return;
|
|
@@ -15563,30 +15644,30 @@ async function runChat(opts) {
|
|
|
15563
15644
|
clearSharedSpinner();
|
|
15564
15645
|
currentController.abort();
|
|
15565
15646
|
currentController = null;
|
|
15566
|
-
process.stdout.write(
|
|
15647
|
+
process.stdout.write(chalk3.yellow("\n\uD604\uC7AC \uC751\uB2F5\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n"));
|
|
15567
15648
|
ctrlCCount = 0;
|
|
15568
15649
|
} else {
|
|
15569
15650
|
ctrlCCount++;
|
|
15570
15651
|
if (ctrlCCount >= 2) {
|
|
15571
|
-
process.stdout.write(
|
|
15652
|
+
process.stdout.write(chalk3.yellow("\n\uC885\uB8CC\uD569\uB2C8\uB2E4.\n"));
|
|
15572
15653
|
rl.close();
|
|
15573
15654
|
cleanSessionStaging(store.id).catch(() => {
|
|
15574
15655
|
});
|
|
15575
15656
|
mcpManager.disconnect().finally(() => process.exit(0));
|
|
15576
15657
|
} else {
|
|
15577
|
-
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"));
|
|
15578
15659
|
}
|
|
15579
15660
|
}
|
|
15580
15661
|
});
|
|
15581
|
-
process.stdout.write(
|
|
15662
|
+
process.stdout.write(chalk3.bold(`kodocagent \uCC44\uD305 \uC2DC\uC791 (/help\uB85C \uB3C4\uC6C0\uB9D0)
|
|
15582
15663
|
`));
|
|
15583
15664
|
process.stdout.write(
|
|
15584
|
-
|
|
15665
|
+
chalk3.dim(`\uD504\uB85C\uBC14\uC774\uB354: ${config.provider}, \uBAA8\uB378: ${config.model ?? "(\uAE30\uBCF8\uAC12)"}
|
|
15585
15666
|
|
|
15586
15667
|
`)
|
|
15587
15668
|
);
|
|
15588
15669
|
process.stdout.write(
|
|
15589
|
-
|
|
15670
|
+
chalk3.dim(
|
|
15590
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'
|
|
15591
15672
|
)
|
|
15592
15673
|
);
|
|
@@ -15604,7 +15685,7 @@ async function runChat(opts) {
|
|
|
15604
15685
|
var clearActiveSpinner = clearActiveSpinner2;
|
|
15605
15686
|
let userInput;
|
|
15606
15687
|
try {
|
|
15607
|
-
userInput = await rl.question(
|
|
15688
|
+
userInput = await rl.question(chalk3.green("You: "));
|
|
15608
15689
|
} catch {
|
|
15609
15690
|
break;
|
|
15610
15691
|
}
|
|
@@ -15616,15 +15697,28 @@ async function runChat(opts) {
|
|
|
15616
15697
|
printContextUsage(lastContextTokens, config.maxContextTokens);
|
|
15617
15698
|
continue;
|
|
15618
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
|
+
}
|
|
15619
15713
|
const handled = await handleSlashCommand(trimmed, config, store, cwd, rl);
|
|
15620
15714
|
if (handled === "exit") break;
|
|
15621
15715
|
if (handled === "new-session") {
|
|
15622
15716
|
store = await createNewStore(config, cwd);
|
|
15623
|
-
process.stdout.write(
|
|
15717
|
+
process.stdout.write(chalk3.dim("\uC0C8 \uC138\uC158\uC774 \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n"));
|
|
15624
15718
|
} else if (handled && typeof handled === "object" && "config" in handled) {
|
|
15625
15719
|
config = handled.config;
|
|
15626
15720
|
process.stdout.write(
|
|
15627
|
-
|
|
15721
|
+
chalk3.dim(`\uBAA8\uB378 \uC804\uD658: ${config.provider} / ${config.model ?? "(\uAE30\uBCF8\uAC12)"}
|
|
15628
15722
|
`)
|
|
15629
15723
|
);
|
|
15630
15724
|
}
|
|
@@ -15636,7 +15730,7 @@ async function runChat(opts) {
|
|
|
15636
15730
|
model = createModel(config);
|
|
15637
15731
|
} catch (err) {
|
|
15638
15732
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15639
|
-
process.stderr.write(
|
|
15733
|
+
process.stderr.write(chalk3.red(`\uC624\uB958: ${msg}
|
|
15640
15734
|
`));
|
|
15641
15735
|
continue;
|
|
15642
15736
|
}
|
|
@@ -15658,7 +15752,7 @@ async function runChat(opts) {
|
|
|
15658
15752
|
});
|
|
15659
15753
|
await session.loadHistory();
|
|
15660
15754
|
currentController = new AbortController();
|
|
15661
|
-
process.stdout.write(
|
|
15755
|
+
process.stdout.write(chalk3.bold("Assistant: "));
|
|
15662
15756
|
const isTTY = process.stdout.isTTY === true;
|
|
15663
15757
|
const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
15664
15758
|
let activeToolLabel = "";
|
|
@@ -15673,7 +15767,7 @@ async function runChat(opts) {
|
|
|
15673
15767
|
process.stdout.write(event.text);
|
|
15674
15768
|
hasOutput = true;
|
|
15675
15769
|
} else if (event.type === "tool-call") {
|
|
15676
|
-
process.stdout.write(
|
|
15770
|
+
process.stdout.write(chalk3.dim(`
|
|
15677
15771
|
\u2699 ${formatToolCall(event.toolName, event.args)}
|
|
15678
15772
|
`));
|
|
15679
15773
|
if (isTTY) {
|
|
@@ -15685,7 +15779,7 @@ async function runChat(opts) {
|
|
|
15685
15779
|
const frame = SPINNER_FRAMES[spinnerFrameIdx % SPINNER_FRAMES.length] ?? "\u280B";
|
|
15686
15780
|
spinnerFrameIdx++;
|
|
15687
15781
|
const plainText = `${frame} ${activeToolLabel} \uC2E4\uD589 \uC911\u2026`;
|
|
15688
|
-
process.stdout.write(`\r${
|
|
15782
|
+
process.stdout.write(`\r${chalk3.dim(plainText)}`);
|
|
15689
15783
|
lastSpinnerWidth = plainText.length + 1;
|
|
15690
15784
|
}, 80);
|
|
15691
15785
|
}
|
|
@@ -15695,23 +15789,29 @@ async function runChat(opts) {
|
|
|
15695
15789
|
clearActiveSpinner2();
|
|
15696
15790
|
const statusLabel = event.isError ? "\uC2E4\uD328" : "\uC644\uB8CC";
|
|
15697
15791
|
process.stdout.write(
|
|
15698
|
-
|
|
15792
|
+
chalk3.dim(` \u2514 ${activeToolLabel} ${statusLabel} (${elapsedSec}s)
|
|
15699
15793
|
`)
|
|
15700
15794
|
);
|
|
15701
15795
|
} else if (event.type === "approval-required") {
|
|
15702
15796
|
} else if (event.type === "turn-complete") {
|
|
15703
15797
|
if (event.usage) {
|
|
15704
15798
|
lastContextTokens = event.usage.inputTokens;
|
|
15799
|
+
cumulativeInputTokens += event.usage.inputTokens;
|
|
15800
|
+
cumulativeOutputTokens += event.usage.outputTokens;
|
|
15705
15801
|
process.stdout.write(
|
|
15706
15802
|
`
|
|
15707
|
-
${formatContextUsage(event.usage.inputTokens, config.maxContextTokens)} ${
|
|
15708
|
-
`\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`
|
|
15709
15805
|
)}
|
|
15806
|
+
`
|
|
15807
|
+
);
|
|
15808
|
+
process.stdout.write(
|
|
15809
|
+
`${formatCumulativeUsage(config, cumulativeInputTokens, cumulativeOutputTokens)}
|
|
15710
15810
|
`
|
|
15711
15811
|
);
|
|
15712
15812
|
}
|
|
15713
15813
|
} else if (event.type === "error") {
|
|
15714
|
-
process.stderr.write(
|
|
15814
|
+
process.stderr.write(chalk3.red(`
|
|
15715
15815
|
\uC624\uB958: ${event.message}
|
|
15716
15816
|
`));
|
|
15717
15817
|
}
|
|
@@ -15725,6 +15825,12 @@ ${formatContextUsage(event.usage.inputTokens, config.maxContextTokens)} ${chalk
|
|
|
15725
15825
|
}
|
|
15726
15826
|
}
|
|
15727
15827
|
rl.close();
|
|
15828
|
+
if (cumulativeInputTokens > 0 || cumulativeOutputTokens > 0) {
|
|
15829
|
+
process.stdout.write(
|
|
15830
|
+
`${formatCumulativeUsage(config, cumulativeInputTokens, cumulativeOutputTokens)}
|
|
15831
|
+
`
|
|
15832
|
+
);
|
|
15833
|
+
}
|
|
15728
15834
|
await cleanSessionStaging(store.id).catch(() => {
|
|
15729
15835
|
});
|
|
15730
15836
|
await mcpManager.disconnect();
|
|
@@ -15766,14 +15872,14 @@ function formatContextUsage(used, budget) {
|
|
|
15766
15872
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
15767
15873
|
const pct = budget > 0 ? Math.round(used / budget * 100) : 0;
|
|
15768
15874
|
const text3 = `\uCEE8\uD14D\uC2A4\uD2B8: ${fmt(used)} / ${fmt(budget)} \uD1A0\uD070 (${pct}%)`;
|
|
15769
|
-
if (pct >= 90) return
|
|
15770
|
-
if (pct >= 70) return
|
|
15771
|
-
return
|
|
15875
|
+
if (pct >= 90) return chalk3.red(text3);
|
|
15876
|
+
if (pct >= 70) return chalk3.yellow(text3);
|
|
15877
|
+
return chalk3.dim(text3);
|
|
15772
15878
|
}
|
|
15773
15879
|
function printContextUsage(used, budget) {
|
|
15774
15880
|
if (used <= 0) {
|
|
15775
15881
|
process.stdout.write(
|
|
15776
|
-
|
|
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")
|
|
15777
15883
|
);
|
|
15778
15884
|
return;
|
|
15779
15885
|
}
|
|
@@ -15794,7 +15900,7 @@ async function handleSlashCommand(cmd, config, _store, _cwd, rl) {
|
|
|
15794
15900
|
switch (command) {
|
|
15795
15901
|
case "/exit":
|
|
15796
15902
|
case "/quit":
|
|
15797
|
-
process.stdout.write(
|
|
15903
|
+
process.stdout.write(chalk3.yellow("\uC885\uB8CC\uD569\uB2C8\uB2E4.\n"));
|
|
15798
15904
|
rl.close();
|
|
15799
15905
|
return "exit";
|
|
15800
15906
|
case "/help":
|
|
@@ -15805,7 +15911,7 @@ async function handleSlashCommand(cmd, config, _store, _cwd, rl) {
|
|
|
15805
15911
|
case "/model":
|
|
15806
15912
|
return handleModelSwitch(config);
|
|
15807
15913
|
default:
|
|
15808
|
-
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.
|
|
15809
15915
|
`));
|
|
15810
15916
|
return null;
|
|
15811
15917
|
}
|
|
@@ -15827,7 +15933,7 @@ async function handleModelSwitch(_config) {
|
|
|
15827
15933
|
options.push({ value: "__custom__", label: "\uC9C1\uC811 \uC785\uB825..." });
|
|
15828
15934
|
if (options.length <= 1) {
|
|
15829
15935
|
process.stdout.write(
|
|
15830
|
-
|
|
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")
|
|
15831
15937
|
);
|
|
15832
15938
|
return null;
|
|
15833
15939
|
}
|
|
@@ -15863,7 +15969,7 @@ async function handleModelSwitch(_config) {
|
|
|
15863
15969
|
const isKnown = knownForProvider.includes(modelId2);
|
|
15864
15970
|
if (!isKnown) {
|
|
15865
15971
|
process.stdout.write(
|
|
15866
|
-
|
|
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")
|
|
15867
15973
|
);
|
|
15868
15974
|
}
|
|
15869
15975
|
const newConfig2 = { ...loadedConfig };
|
|
@@ -15885,7 +15991,7 @@ async function saveConfig2(config) {
|
|
|
15885
15991
|
}
|
|
15886
15992
|
|
|
15887
15993
|
// src/clean-cmd.ts
|
|
15888
|
-
import
|
|
15994
|
+
import chalk4 from "chalk";
|
|
15889
15995
|
async function runClean(opts) {
|
|
15890
15996
|
const stagingDeleted = await cleanAllStaging();
|
|
15891
15997
|
let backupDeleted;
|
|
@@ -15901,7 +16007,7 @@ async function runClean(opts) {
|
|
|
15901
16007
|
}
|
|
15902
16008
|
const backupNote = opts.all ? "" : ` (\uBCF4\uC874 ${backupKept}\uAC1C)`;
|
|
15903
16009
|
process.stdout.write(
|
|
15904
|
-
|
|
16010
|
+
chalk4.green(`\u2713 \uC2A4\uD14C\uC774\uC9D5 ${stagingDeleted}\uAC1C, \uBC31\uC5C5 ${backupDeleted}\uAC1C \uC815\uB9AC${backupNote}
|
|
15905
16011
|
`)
|
|
15906
16012
|
);
|
|
15907
16013
|
}
|
|
@@ -16004,7 +16110,7 @@ async function configShow() {
|
|
|
16004
16110
|
}
|
|
16005
16111
|
|
|
16006
16112
|
// src/mcp-cmd.ts
|
|
16007
|
-
import
|
|
16113
|
+
import chalk5 from "chalk";
|
|
16008
16114
|
async function mcpList() {
|
|
16009
16115
|
const config = await loadConfig();
|
|
16010
16116
|
const cwd = process.cwd();
|
|
@@ -16023,22 +16129,22 @@ async function mcpList() {
|
|
|
16023
16129
|
return;
|
|
16024
16130
|
}
|
|
16025
16131
|
process.stdout.write(
|
|
16026
|
-
|
|
16132
|
+
chalk5.bold("\uC774\uB984".padEnd(20)) + chalk5.bold("\uC0C1\uD0DC".padEnd(12)) + chalk5.bold("\uD234 \uC218".padEnd(8)) + chalk5.bold("\uC0AC\uC720") + "\n"
|
|
16027
16133
|
);
|
|
16028
16134
|
process.stdout.write("\u2500".repeat(70) + "\n");
|
|
16029
16135
|
for (const s of statuses) {
|
|
16030
|
-
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");
|
|
16031
16137
|
const toolCount = s.state === "connected" ? String(s.toolCount) : "\u2014";
|
|
16032
16138
|
const reason = s.reason ?? "";
|
|
16033
16139
|
process.stdout.write(
|
|
16034
16140
|
s.name.padEnd(20) + stateStr.padEnd(20) + // chalk adds escape codes so pad more
|
|
16035
|
-
toolCount.padEnd(8) +
|
|
16141
|
+
toolCount.padEnd(8) + chalk5.dim(reason.slice(0, 50)) + "\n"
|
|
16036
16142
|
);
|
|
16037
16143
|
}
|
|
16038
16144
|
if (manager.warnings.length > 0) {
|
|
16039
16145
|
process.stdout.write("\n");
|
|
16040
16146
|
for (const w of manager.warnings) {
|
|
16041
|
-
process.stdout.write(
|
|
16147
|
+
process.stdout.write(chalk5.yellow(`\uACBD\uACE0: ${w}
|
|
16042
16148
|
`));
|
|
16043
16149
|
}
|
|
16044
16150
|
}
|
|
@@ -16051,7 +16157,7 @@ async function mcpTest(serverName) {
|
|
|
16051
16157
|
const isSkipped = skipped.find((s) => s.name === serverName);
|
|
16052
16158
|
if (isSkipped) {
|
|
16053
16159
|
process.stderr.write(
|
|
16054
|
-
|
|
16160
|
+
chalk5.yellow(`\uC11C\uBC84 '${serverName}'\uC774(\uAC00) \uC2A4\uD0B5\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${isSkipped.reason}
|
|
16055
16161
|
`)
|
|
16056
16162
|
);
|
|
16057
16163
|
return;
|
|
@@ -16059,7 +16165,7 @@ async function mcpTest(serverName) {
|
|
|
16059
16165
|
const serverConfig = servers.find((s) => s.name === serverName);
|
|
16060
16166
|
if (!serverConfig) {
|
|
16061
16167
|
process.stderr.write(
|
|
16062
|
-
|
|
16168
|
+
chalk5.red(
|
|
16063
16169
|
`\uC11C\uBC84 '${serverName}'\uC744(\uB97C) \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
16064
16170
|
'kodocagent mcp list'\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC11C\uBC84\uB97C \uD655\uC778\uD558\uC138\uC694.
|
|
16065
16171
|
`
|
|
@@ -16067,32 +16173,32 @@ async function mcpTest(serverName) {
|
|
|
16067
16173
|
);
|
|
16068
16174
|
return;
|
|
16069
16175
|
}
|
|
16070
|
-
process.stdout.write(
|
|
16176
|
+
process.stdout.write(chalk5.dim(`\uC11C\uBC84 '${serverName}' \uC5F0\uACB0 \uC911...
|
|
16071
16177
|
`));
|
|
16072
16178
|
if (serverConfig.type === "stdio") {
|
|
16073
|
-
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"));
|
|
16074
16180
|
}
|
|
16075
16181
|
const manager = new McpManager();
|
|
16076
16182
|
await manager.connect([serverConfig]);
|
|
16077
16183
|
const statuses = manager.status();
|
|
16078
16184
|
const status = statuses[0];
|
|
16079
16185
|
if (!status || status.state !== "connected") {
|
|
16080
|
-
process.stderr.write(
|
|
16186
|
+
process.stderr.write(chalk5.red(`\uC5F0\uACB0 \uC2E4\uD328: ${status?.reason ?? "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}
|
|
16081
16187
|
`));
|
|
16082
16188
|
await manager.disconnect();
|
|
16083
16189
|
return;
|
|
16084
16190
|
}
|
|
16085
|
-
process.stdout.write(
|
|
16191
|
+
process.stdout.write(chalk5.green(`\uC5F0\uACB0 \uC131\uACF5! \uD234 ${status.toolCount}\uAC1C
|
|
16086
16192
|
|
|
16087
16193
|
`));
|
|
16088
16194
|
const defs = manager.getToolDefinitions();
|
|
16089
16195
|
if (defs.length === 0) {
|
|
16090
16196
|
process.stdout.write("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD234\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n");
|
|
16091
16197
|
} else {
|
|
16092
|
-
process.stdout.write(
|
|
16198
|
+
process.stdout.write(chalk5.bold("\uD234 \uBAA9\uB85D:\n"));
|
|
16093
16199
|
for (const def of defs) {
|
|
16094
|
-
process.stdout.write(` ${
|
|
16095
|
-
${
|
|
16200
|
+
process.stdout.write(` ${chalk5.cyan(def.name)}
|
|
16201
|
+
${chalk5.dim(def.description)}
|
|
16096
16202
|
`);
|
|
16097
16203
|
}
|
|
16098
16204
|
}
|
|
@@ -16326,7 +16432,7 @@ program.option("-p, --print <prompt>", "\uB2E8\uBC1C \uC9C8\uC758 (\uBE44\uB300\
|
|
|
16326
16432
|
const newVersion = await checkForUpdate(cliVersion()).catch(() => null);
|
|
16327
16433
|
if (newVersion) {
|
|
16328
16434
|
process.stdout.write(
|
|
16329
|
-
|
|
16435
|
+
chalk6.yellow(`\uC0C8 \uBC84\uC804 v${newVersion} \uC0AC\uC6A9 \uAC00\uB2A5 \u2014 'kodocagent update'\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694
|
|
16330
16436
|
`)
|
|
16331
16437
|
);
|
|
16332
16438
|
}
|
|
@@ -16356,15 +16462,15 @@ program.command("sessions").description("\uC138\uC158 \uBAA9\uB85D \uD45C\uC2DC"
|
|
|
16356
16462
|
for (const s of sessions) {
|
|
16357
16463
|
const dateStr = s.mtime.toLocaleString("ko-KR");
|
|
16358
16464
|
process.stdout.write(
|
|
16359
|
-
`${
|
|
16465
|
+
`${chalk6.bold(s.id)} ${chalk6.dim(dateStr)} ${chalk6.cyan(s.meta.provider)}/${s.meta.model ?? "(\uAE30\uBCF8)"}
|
|
16360
16466
|
`
|
|
16361
16467
|
);
|
|
16362
16468
|
if (s.preview) {
|
|
16363
|
-
process.stdout.write(` ${
|
|
16364
|
-
process.stdout.write(` ${
|
|
16469
|
+
process.stdout.write(` ${chalk6.italic(`"${s.preview}"`)}`);
|
|
16470
|
+
process.stdout.write(` ${chalk6.dim(s.meta.cwd)}
|
|
16365
16471
|
`);
|
|
16366
16472
|
} else {
|
|
16367
|
-
process.stdout.write(` ${
|
|
16473
|
+
process.stdout.write(` ${chalk6.dim(s.meta.cwd)}
|
|
16368
16474
|
`);
|
|
16369
16475
|
}
|
|
16370
16476
|
}
|
|
@@ -16425,7 +16531,7 @@ async function pickSession() {
|
|
|
16425
16531
|
}
|
|
16426
16532
|
const sessions = await listSessions();
|
|
16427
16533
|
if (sessions.length === 0) {
|
|
16428
|
-
process.stdout.write(
|
|
16534
|
+
process.stdout.write(chalk6.dim("\uC7AC\uAC1C\uD560 \uC138\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n"));
|
|
16429
16535
|
return void 0;
|
|
16430
16536
|
}
|
|
16431
16537
|
const options = sessions.map((s) => {
|
|
@@ -16446,17 +16552,17 @@ async function pickSession() {
|
|
|
16446
16552
|
}
|
|
16447
16553
|
function handleError(err) {
|
|
16448
16554
|
if (err instanceof KodocError) {
|
|
16449
|
-
process.stderr.write(
|
|
16555
|
+
process.stderr.write(chalk6.red(`\uC624\uB958: ${err.message}
|
|
16450
16556
|
`));
|
|
16451
16557
|
if (err.hint) {
|
|
16452
|
-
process.stderr.write(
|
|
16558
|
+
process.stderr.write(chalk6.yellow(` \u2192 ${err.hint}
|
|
16453
16559
|
`));
|
|
16454
16560
|
}
|
|
16455
16561
|
} else if (err instanceof Error) {
|
|
16456
|
-
process.stderr.write(
|
|
16562
|
+
process.stderr.write(chalk6.red(`\uC624\uB958: ${err.message}
|
|
16457
16563
|
`));
|
|
16458
16564
|
} else {
|
|
16459
|
-
process.stderr.write(
|
|
16565
|
+
process.stderr.write(chalk6.red(`\uC54C \uC218 \uC5C6\uB294 \uC624\uB958: ${String(err)}
|
|
16460
16566
|
`));
|
|
16461
16567
|
}
|
|
16462
16568
|
process.exit(1);
|
|
@@ -16475,7 +16581,7 @@ async function runSingleTurn(prompt) {
|
|
|
16475
16581
|
await mcpManager.connect(servers);
|
|
16476
16582
|
}
|
|
16477
16583
|
for (const w of mcpManager.warnings) {
|
|
16478
|
-
process.stdout.write(
|
|
16584
|
+
process.stdout.write(chalk6.yellow(`\u26A0 ${w}
|
|
16479
16585
|
`));
|
|
16480
16586
|
}
|
|
16481
16587
|
}
|
|
@@ -16516,7 +16622,7 @@ async function runSingleTurn(prompt) {
|
|
|
16516
16622
|
if (event.type === "text-delta") {
|
|
16517
16623
|
process.stdout.write(event.text);
|
|
16518
16624
|
} else if (event.type === "error") {
|
|
16519
|
-
process.stderr.write(
|
|
16625
|
+
process.stderr.write(chalk6.red(`
|
|
16520
16626
|
\uC624\uB958: ${event.message}
|
|
16521
16627
|
`));
|
|
16522
16628
|
}
|