kordoc 3.17.0 → 3.18.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/README.md +5 -0
- package/dist/{-3KPDRR3A.js → -OUCPI6B3.js} +9 -7
- package/dist/{chunk-NKICMZ7F.js → chunk-4YEZTZAR.js} +2 -2
- package/dist/{chunk-EL2Y4BL2.cjs → chunk-5HDBLRVP.cjs} +45 -9
- package/dist/chunk-5HDBLRVP.cjs.map +1 -0
- package/dist/{chunk-HVEEFHN7.js → chunk-6Y4KKPC3.js} +2 -2
- package/dist/{chunk-E6NQWOFQ.js → chunk-GIXXV52X.js} +39 -3
- package/dist/chunk-GIXXV52X.js.map +1 -0
- package/dist/{chunk-VUS6LRVY.js → chunk-PWHM3KAA.js} +535 -83
- package/dist/chunk-PWHM3KAA.js.map +1 -0
- package/dist/{chunk-TDIDUEBH.js → chunk-VP34RO24.js} +3 -3
- package/dist/{chunk-WHMNPQXW.js → chunk-YWIYGXLI.js} +39 -3
- package/dist/chunk-YWIYGXLI.js.map +1 -0
- package/dist/{chunk-SADQWCWR.js → chunk-ZTUQYITH.js} +3 -3
- package/dist/cli.js +18 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +862 -411
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -1
- package/dist/index.d.ts +118 -1
- package/dist/index.js +592 -141
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +22 -10
- package/dist/mcp.js.map +1 -1
- package/dist/{parser-LHBXIKXX.js → parser-5D6BQYTX.js} +2 -2
- package/dist/{parser-CYJMOXUR.js → parser-NZAE7PBE.js} +3 -3
- package/dist/{parser-I5GC4QQ5.cjs → parser-S34YR2SH.cjs} +14 -14
- package/dist/{parser-I5GC4QQ5.cjs.map → parser-S34YR2SH.cjs.map} +1 -1
- package/dist/render-HRAW7ASR.js +10 -0
- package/dist/seal-CK64CXWO.js +10 -0
- package/dist/{watch-VT7BSLNI.js → watch-2EFXBRYK.js} +7 -7
- package/package.json +1 -1
- package/dist/chunk-E6NQWOFQ.js.map +0 -1
- package/dist/chunk-EL2Y4BL2.cjs.map +0 -1
- package/dist/chunk-VUS6LRVY.js.map +0 -1
- package/dist/chunk-WHMNPQXW.js.map +0 -1
- package/dist/render-HY7KKYPV.js +0 -10
- package/dist/seal-ITGYHENO.js +0 -10
- /package/dist/{-3KPDRR3A.js.map → -OUCPI6B3.js.map} +0 -0
- /package/dist/{chunk-NKICMZ7F.js.map → chunk-4YEZTZAR.js.map} +0 -0
- /package/dist/{chunk-HVEEFHN7.js.map → chunk-6Y4KKPC3.js.map} +0 -0
- /package/dist/{chunk-TDIDUEBH.js.map → chunk-VP34RO24.js.map} +0 -0
- /package/dist/{chunk-SADQWCWR.js.map → chunk-ZTUQYITH.js.map} +0 -0
- /package/dist/{parser-LHBXIKXX.js.map → parser-5D6BQYTX.js.map} +0 -0
- /package/dist/{parser-CYJMOXUR.js.map → parser-NZAE7PBE.js.map} +0 -0
- /package/dist/{render-HY7KKYPV.js.map → render-HRAW7ASR.js.map} +0 -0
- /package/dist/{seal-ITGYHENO.js.map → seal-CK64CXWO.js.map} +0 -0
- /package/dist/{watch-VT7BSLNI.js.map → watch-2EFXBRYK.js.map} +0 -0
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
MAX_INLINE_MD_BYTES,
|
|
3
4
|
compare,
|
|
4
5
|
extractFormFields,
|
|
5
6
|
extractHwp5MetadataOnly,
|
|
@@ -11,24 +12,24 @@ import {
|
|
|
11
12
|
parse,
|
|
12
13
|
patchHwp,
|
|
13
14
|
patchHwpx
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-PWHM3KAA.js";
|
|
15
16
|
import {
|
|
16
17
|
blocksToMarkdown
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YWIYGXLI.js";
|
|
18
19
|
import {
|
|
19
20
|
detectFormat,
|
|
20
21
|
detectZipFormat
|
|
21
22
|
} from "./chunk-MEPHGCPQ.js";
|
|
22
23
|
import "./chunk-MOL7MDBG.js";
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-VP34RO24.js";
|
|
25
|
+
import "./chunk-ZTUQYITH.js";
|
|
26
|
+
import "./chunk-4YEZTZAR.js";
|
|
26
27
|
import {
|
|
27
28
|
KordocError,
|
|
28
29
|
VERSION,
|
|
29
30
|
sanitizeError,
|
|
30
31
|
toArrayBuffer
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-6Y4KKPC3.js";
|
|
32
33
|
|
|
33
34
|
// src/mcp.ts
|
|
34
35
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -104,13 +105,23 @@ server.tool(
|
|
|
104
105
|
isError: true
|
|
105
106
|
};
|
|
106
107
|
}
|
|
107
|
-
const result = await parse(buffer);
|
|
108
|
+
const result = await parse(buffer, { inlineImages: true });
|
|
108
109
|
if (!result.success) {
|
|
109
110
|
return {
|
|
110
111
|
content: [{ type: "text", text: `\uD30C\uC2F1 \uC2E4\uD328 (${result.fileType}): ${result.error}` }],
|
|
111
112
|
isError: true
|
|
112
113
|
};
|
|
113
114
|
}
|
|
115
|
+
let markdown = result.markdown;
|
|
116
|
+
let omitNote = "";
|
|
117
|
+
const imgCount = result.images?.length ?? 0;
|
|
118
|
+
const didInline = result.fileType === "hwp" && imgCount > 0;
|
|
119
|
+
if (didInline && Buffer.byteLength(markdown, "utf8") > MAX_INLINE_MD_BYTES) {
|
|
120
|
+
markdown = blocksToMarkdown(result.blocks);
|
|
121
|
+
omitNote = `
|
|
122
|
+
|
|
123
|
+
\u26A0\uFE0F \uC774\uBBF8\uC9C0 ${imgCount}\uAC1C\uAC00 \uC778\uB77C\uC778 \uD06C\uAE30 \uC0C1\uD55C(${(MAX_INLINE_MD_BYTES / 1024 / 1024).toFixed(0)}MB)\uC744 \uCD08\uACFC\uD558\uC5EC \uBCF8\uBB38\uC5D0 \uC778\uB77C\uC778\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC774\uBBF8\uC9C0\uB294 \uD30C\uC77C \uCC38\uC870(image_NNN)\uB85C\uB9CC \uD45C\uC2DC\uB429\uB2C8\uB2E4.`;
|
|
124
|
+
}
|
|
114
125
|
const meta = [
|
|
115
126
|
`\uD3EC\uB9F7: ${result.fileType.toUpperCase()}`,
|
|
116
127
|
result.pageCount ? `\uD398\uC774\uC9C0: ${result.pageCount}` : null,
|
|
@@ -133,7 +144,8 @@ ${warnText}`);
|
|
|
133
144
|
}
|
|
134
145
|
parts.push(`
|
|
135
146
|
|
|
136
|
-
${
|
|
147
|
+
${markdown}`);
|
|
148
|
+
if (omitNote) parts.push(omitNote);
|
|
137
149
|
return {
|
|
138
150
|
content: [{ type: "text", text: parts.join("") }]
|
|
139
151
|
};
|
|
@@ -199,7 +211,7 @@ server.tool(
|
|
|
199
211
|
break;
|
|
200
212
|
case "pdf":
|
|
201
213
|
try {
|
|
202
|
-
const { extractPdfMetadataOnly } = await import("./parser-
|
|
214
|
+
const { extractPdfMetadataOnly } = await import("./parser-NZAE7PBE.js");
|
|
203
215
|
metadata = await extractPdfMetadataOnly(buffer);
|
|
204
216
|
} catch {
|
|
205
217
|
metadata = void 0;
|
|
@@ -551,7 +563,7 @@ server.tool(
|
|
|
551
563
|
}
|
|
552
564
|
const image = new Uint8Array(readFileSync(imgResolved));
|
|
553
565
|
const ext = extname(image_path).slice(1).toLowerCase() || "png";
|
|
554
|
-
const { placeSealHwpx } = await import("./seal-
|
|
566
|
+
const { placeSealHwpx } = await import("./seal-CK64CXWO.js");
|
|
555
567
|
const result = await placeSealHwpx(buffer, [{
|
|
556
568
|
anchor,
|
|
557
569
|
occurrence,
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/** kordoc MCP 서버 — Claude/Cursor에서 문서 파싱 도구로 사용 */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { readFileSync, writeFileSync, realpathSync, openSync, readSync, closeSync, statSync, mkdirSync } from \"fs\"\nimport { resolve, isAbsolute, extname, dirname } from \"path\"\nimport { parse, detectFormat, detectZipFormat, blocksToMarkdown, compare, extractFormFields, fillFormFields, markdownToHwpx, fillHwpx, patchHwpx, patchHwp } from \"./index.js\"\nimport { fillWithUniqueGuard, type FillInput } from \"./form/match.js\"\nimport type { GongmunOptions } from \"./index.js\"\nimport { VERSION, toArrayBuffer, sanitizeError, KordocError } from \"./utils.js\"\nimport { extractHwp5MetadataOnly } from \"./hwp5/parser.js\"\nimport { extractHwpxMetadataOnly } from \"./hwpx/parser.js\"\n// pdfjs-dist는 optional — dynamic import로 지연 로드\n// import { extractPdfMetadataOnly } from \"./pdf/parser.js\"\n\n/** 허용 파일 확장자 */\nconst ALLOWED_EXTENSIONS = new Set([\".hwp\", \".hwpx\", \".pdf\", \".xlsx\", \".docx\"])\n/** 최대 파일 크기 (500MB) */\nconst MAX_FILE_SIZE = 500 * 1024 * 1024\n\n/** 경로 정규화 및 보안 검증 */\nfunction safePath(filePath: string): string {\n if (!filePath) throw new KordocError(\"파일 경로가 비어있습니다\")\n const resolved = resolve(filePath)\n let real: string\n try {\n real = realpathSync(resolved)\n } catch (err: any) {\n if (err?.code === \"ENOENT\") throw new KordocError(`파일을 찾을 수 없습니다: ${resolved}`)\n if (err?.code === \"EACCES\" || err?.code === \"EPERM\") throw new KordocError(`파일 접근 권한이 없습니다: ${resolved}`)\n throw new KordocError(`경로 처리 오류 [${err?.code ?? \"UNKNOWN\"}]`)\n }\n if (!isAbsolute(real)) throw new KordocError(\"절대 경로만 허용됩니다\")\n const ext = extname(real).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) throw new KordocError(`지원하지 않는 확장자입니다: ${ext} (허용: ${[...ALLOWED_EXTENSIONS].join(\", \")})`)\n return real\n}\n\n/** 최대 파일 크기 — metadata 전용 (50MB, 전체 파싱보다 보수적) */\nconst MAX_METADATA_FILE_SIZE = 50 * 1024 * 1024\n\n/** 파일 읽기 + 크기 검증 공통 로직 */\nfunction readValidatedFile(filePath: string, maxSize = MAX_FILE_SIZE): { buffer: ArrayBuffer; resolved: string } {\n const resolved = safePath(filePath)\n let fileSize: number\n try {\n fileSize = statSync(resolved).size\n } catch (err: any) {\n throw new KordocError(`파일 상태 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\n }\n if (fileSize > maxSize) {\n throw new KordocError(`파일이 너무 큽니다: ${(fileSize / 1024 / 1024).toFixed(1)}MB (최대 ${maxSize / 1024 / 1024}MB)`)\n }\n let raw: Buffer\n try {\n raw = readFileSync(resolved)\n } catch (err: any) {\n throw new KordocError(`파일 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\n }\n return { buffer: toArrayBuffer(raw), resolved }\n}\n\n/** 파일 헤더(16바이트)만 읽어 포맷 감지 — 전체 파일 로드 불필요 */\nfunction detectFormatFromHeader(resolved: string): ReturnType<typeof detectFormat> {\n const fd = openSync(resolved, \"r\")\n try {\n const headerBuf = Buffer.alloc(16)\n readSync(fd, headerBuf, 0, 16, 0)\n return detectFormat(toArrayBuffer(headerBuf))\n } finally {\n closeSync(fd)\n }\n}\n\nconst server = new McpServer({\n name: \"kordoc\",\n version: VERSION,\n})\n\n// ─── 도구: parse_document ────────────────────────────\n\nserver.tool(\n \"parse_document\",\n \"한국 문서 파일(HWP, HWPX, PDF, XLSX, DOCX)을 마크다운으로 변환합니다. 파일 경로를 입력하면 포맷을 자동 감지하여 텍스트를 추출합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n result.metadata?.title ? `제목: ${result.metadata.title}` : null,\n result.metadata?.author ? `작성자: ${result.metadata.author}` : null,\n result.isImageBased ? \"이미지 기반 PDF (텍스트 추출 불가)\" : null,\n ].filter(Boolean).join(\" | \")\n\n // outline/warnings 부가 정보 추가\n const parts: string[] = [`[${meta}]`]\n\n if (result.outline && result.outline.length > 0) {\n const outlineText = result.outline.map(o => `${\" \".repeat(o.level - 1)}- ${o.text}`).join(\"\\n\")\n parts.push(`\\n📑 문서 구조:\\n${outlineText}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n const warnText = result.warnings.map(w => `- [p${w.page || \"?\"}] ${w.message}`).join(\"\\n\")\n parts.push(`\\n⚠️ 경고:\\n${warnText}`)\n }\n\n parts.push(`\\n\\n${result.markdown}`)\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: detect_format ─────────────────────────────\n\nserver.tool(\n \"detect_format\",\n \"파일의 포맷을 매직 바이트로 감지합니다 (hwpx, hwp, pdf, unknown).\",\n {\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n return {\n content: [{ type: \"text\", text: `${file_path}: ${format}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_metadata ────────────────────────────\n\nserver.tool(\n \"parse_metadata\",\n \"문서의 메타데이터(제목, 작성자, 날짜 등)만 빠르게 추출합니다. 전체 파싱 없이 헤더/매니페스트만 읽습니다.\",\n {\n file_path: z.string().min(1).describe(\"메타데이터를 추출할 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n // metadata 전용 크기 제한 (50MB)\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\n\n let metadata\n // ZIP 기반 포맷(hwpx)은 내부 구조로 세분화 (XLSX/DOCX 구분)\n let effectiveFormat = format\n if (format === \"hwpx\") {\n const { detectZipFormat } = await import(\"./detect.js\")\n const zipFormat = await detectZipFormat(buffer)\n if (zipFormat === \"xlsx\" || zipFormat === \"docx\") effectiveFormat = zipFormat as any\n }\n switch (effectiveFormat) {\n case \"hwp\":\n metadata = extractHwp5MetadataOnly(Buffer.from(buffer))\n break\n case \"hwpx\":\n metadata = await extractHwpxMetadataOnly(buffer)\n break\n case \"pdf\":\n try {\n const { extractPdfMetadataOnly } = await import(\"./pdf/parser.js\")\n metadata = await extractPdfMetadataOnly(buffer)\n } catch {\n metadata = undefined // pdfjs-dist 미설치 시 metadata 생략\n }\n break\n case \"xlsx\":\n case \"docx\": {\n // XLSX/DOCX는 전용 metadata 추출기가 없으므로 전체 파싱 후 metadata 반환\n const result = await parse(buffer)\n metadata = result.success ? result.metadata : undefined\n break\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ format, ...metadata }, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_pages ──────────────────────────────\n\nserver.tool(\n \"parse_pages\",\n \"문서의 특정 페이지/섹션 범위만 파싱합니다. PDF는 정확한 페이지, HWP/HWPX는 섹션 단위 근사치입니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n pages: z.string().min(1).describe(\"페이지 범위 (예: '1-3', '1,3,5-7')\"),\n },\n async ({ file_path, pages }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { pages })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n `범위: ${pages}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n ].filter(Boolean).join(\" | \")\n\n return {\n content: [{ type: \"text\", text: `[${meta}]\\n\\n${result.markdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_table ──────────────────────────────\n\nserver.tool(\n \"parse_table\",\n \"문서에서 N번째 테이블만 추출합니다 (0-based index). 테이블이 없거나 인덱스 범위를 초과하면 오류를 반환합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n table_index: z.number().int().min(0).describe(\"추출할 테이블 인덱스 (0부터 시작)\"),\n },\n async ({ file_path, table_index }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const tableBlocks = result.blocks.filter(b => b.type === \"table\" && b.table)\n if (tableBlocks.length === 0) {\n return {\n content: [{ type: \"text\", text: `문서에 테이블이 없습니다.` }],\n isError: true,\n }\n }\n\n if (table_index >= tableBlocks.length) {\n return {\n content: [{ type: \"text\", text: `테이블 인덱스 초과: ${table_index} (총 ${tableBlocks.length}개 테이블)` }],\n isError: true,\n }\n }\n\n const tableBlock = tableBlocks[table_index]\n const tableMarkdown = blocksToMarkdown([tableBlock])\n\n return {\n content: [{ type: \"text\", text: `[테이블 #${table_index} / 총 ${tableBlocks.length}개]\\n\\n${tableMarkdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: compare_documents ─────────────────────────\n\nserver.tool(\n \"compare_documents\",\n \"두 한국 문서 파일을 비교하여 추가/삭제/변경된 블록을 표시합니다. 신구대조표 생성에 활용됩니다. 크로스 포맷(HWP↔HWPX) 비교 가능.\",\n {\n file_path_a: z.string().min(1).describe(\"비교 원본 문서의 절대 경로\"),\n file_path_b: z.string().min(1).describe(\"비교 대상 문서의 절대 경로\"),\n },\n async ({ file_path_a, file_path_b }) => {\n try {\n const { buffer: bufA } = readValidatedFile(file_path_a)\n const { buffer: bufB } = readValidatedFile(file_path_b)\n\n const result = await compare(bufA, bufB)\n const { stats, diffs } = result\n\n const lines: string[] = [\n `## 문서 비교 결과`,\n `추가: ${stats.added} | 삭제: ${stats.removed} | 변경: ${stats.modified} | 동일: ${stats.unchanged}`,\n \"\",\n ]\n\n for (const d of diffs) {\n const prefix = d.type === \"added\" ? \"+\" : d.type === \"removed\" ? \"-\" : d.type === \"modified\" ? \"~\" : \" \"\n const text = d.after?.text || d.before?.text || (d.after?.table ? \"[테이블]\" : d.before?.table ? \"[테이블]\" : \"\")\n const sim = d.similarity !== undefined ? ` (${(d.similarity * 100).toFixed(0)}%)` : \"\"\n lines.push(`${prefix} ${text.substring(0, 200)}${sim}`)\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_form ───────────────────────────────\n\nserver.tool(\n \"parse_form\",\n \"한국 서식 문서에서 레이블-값 쌍을 구조화된 JSON으로 추출합니다. 양식/서식 문서에 최적화.\",\n {\n file_path: z.string().min(1).describe(\"서식 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const form = extractFormFields(result.blocks)\n return {\n content: [{ type: \"text\", text: JSON.stringify(form, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n/** fields + formats 를 FillInput 맵으로 결합 (formats의 라벨은 fields와 동일 표기 기준) */\nfunction buildFillInputs(fields: Record<string, string>, formats?: Record<string, string>): Record<string, FillInput> {\n const out: Record<string, FillInput> = {}\n for (const [k, v] of Object.entries(fields)) {\n const format = formats?.[k]\n out[k] = format ? { value: v, format } : v\n }\n return out\n}\n\n// ─── 도구: fill_form ───────────────────────────────\n\nserver.tool(\n \"fill_form\",\n \"한국 서식 문서의 빈칸을 채워서 새 문서로 출력합니다. hwpx-preserve를 사용하면 원본 서식(테두리, 폰트, 병합 등)을 100% 유지합니다.\",\n {\n file_path: z.string().min(1).describe(\"서식 템플릿 문서의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n fields: z.record(z.string(), z.string()).describe(\"채울 필드 맵 (라벨 → 값). 예: {\\\"성명\\\": \\\"홍길동\\\", \\\"전화번호\\\": \\\"010-1234-5678\\\"}\"),\n formats: z.record(z.string(), z.string()).optional().describe(\"필드별 값 서식 (라벨 → 포맷). 정준값 하나로 서식마다 다른 모양을 채울 때: date:yy.mm.dd / phone:hyphen·dot·digits / rrn:hyphen·masked / mask:###-## / 자유 패턴(yyyy년 m월 d일, ###-####-####)\"),\n require_unique: z.boolean().optional().describe(\"한 키가 서식의 2곳 이상에 매칭되면 채우지 않고 거부 — 반복 라벨 양식에서 남의 블록 오염 방지 (배열 값은 예외)\"),\n mask_values: z.boolean().optional().describe(\"응답에 값 대신 글자수만 표시 — 개인정보 채움 시 값이 대화 로그에 남지 않게\"),\n output_format: z.enum([\"markdown\", \"hwpx\", \"hwpx-preserve\"]).default(\"hwpx-preserve\").describe(\"출력 포맷: hwpx-preserve (원본 스타일 보존, HWPX 전용), hwpx (새 HWPX 생성), markdown\"),\n output_path: z.string().optional().describe(\"출력 파일 저장 경로 (선택). 지정 시 파일로 저장, 미지정 시 텍스트로 반환\"),\n },\n async ({ file_path, fields, formats, require_unique, mask_values, output_format, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n\n // ─── hwpx-preserve: 원본 ZIP 직접 수정 (스타일 보존) ───\n if (output_format === \"hwpx-preserve\") {\n const format = detectFormat(buffer)\n let isHwpx = format === \"hwpx\"\n if (isHwpx) {\n const zipFormat = await detectZipFormat(buffer)\n isHwpx = zipFormat === \"hwpx\"\n }\n if (!isHwpx) {\n return {\n content: [{ type: \"text\", text: `hwpx-preserve는 HWPX 파일만 지원합니다 (감지된 포맷: ${format}). hwpx 또는 markdown을 사용하세요.` }],\n isError: true,\n }\n }\n\n const inputs = buildFillInputs(fields, formats)\n const hwpxResult = require_unique\n ? await fillWithUniqueGuard(inputs, (vals, blocked) => fillHwpx(buffer, vals, blocked))\n : { ...(await fillHwpx(buffer, inputs)), rejected: [] as string[] }\n // 마스킹 verify — 채운 결과를 재파싱해 값이 실제 문서에 있는지만 확인 (값 미노출)\n let verifyLine: string | null = null\n if (mask_values && hwpxResult.filled.length > 0) {\n const reparsed = await parse(Buffer.from(hwpxResult.buffer))\n // 마크다운 이스케이프(\\*,\\|,\\~ 등)·개행/연속공백 정규화 후 비교 — rrn:masked\n // ('900315-1******')의 * 이스케이프로 생기던 결정적 false negative 방지.\n // 빈 값은 includes('')===true 로 항상 통과하던 것을 FILLED 에서 제외한다.\n const norm = (s: string): string => s.replace(/\\\\([\\\\`*_{}[\\]()#+.!|~>-])/g, \"$1\").replace(/\\s+/g, \" \")\n const normMd = reparsed.success ? norm(reparsed.markdown) : \"\"\n const okCount = reparsed.success\n ? hwpxResult.filled.filter(f => f.value !== \"\" && normMd.includes(norm(f.value))).length\n : 0\n verifyLine = `검증(마스킹): ${okCount}/${hwpxResult.filled.length} FILLED — 재파싱 대조, 값 미노출`\n }\n const summary = [\n `채워진 필드: ${hwpxResult.filled.length}개 (원본 스타일 보존)`,\n hwpxResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${hwpxResult.rejected.join(\", \")}` : null,\n hwpxResult.unmatched.length > 0 ? `매칭 실패: ${hwpxResult.unmatched.join(\", \")}` : null,\n verifyLine,\n ].filter(Boolean).join(\" | \")\n\n const filledList = hwpxResult.filled\n .map(f => ` - ${f.label}: ${mask_values ? `[${[...f.value].length}자]` : f.value}`).join(\"\\n\")\n\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(hwpxResult.buffer))\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\nHWPX 파일 저장 (원본 서식 유지): ${resolve(output_path)}` }],\n }\n }\n\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\n⚠️ output_path를 지정하면 원본 서식이 유지된 HWPX 파일로 저장됩니다.` }],\n }\n }\n\n // ─── 일반 경로: parse → fill → output ───\n const result = await parse(buffer)\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const formInfo = extractFormFields(result.blocks)\n const irInputs = buildFillInputs(fields, formats)\n const fillResult = require_unique\n ? await fillWithUniqueGuard(irInputs, (vals, blocked) => fillFormFields(result.blocks, vals, blocked))\n : { ...fillFormFields(result.blocks, irInputs), rejected: [] as string[] }\n\n if (fillResult.filled.length === 0 && formInfo.fields.length === 0) {\n return {\n content: [{ type: \"text\", text: `서식 필드를 찾을 수 없습니다. 일반 문서이거나 서식 패턴이 감지되지 않았습니다.` }],\n isError: true,\n }\n }\n\n const markdown = blocksToMarkdown(fillResult.blocks)\n // mask_values 시 채운 값(주민번호·연락처 등)이 응답(대화 로그)에 노출되지 않게\n // 본문 미리보기를 안내 문구로 대체 (sfill-8). 값은 output_path 파일에만 기록된다.\n const previewMd = mask_values\n ? \"⚠️ mask_values 활성 — 개인정보 노출 방지를 위해 본문을 응답에 포함하지 않습니다. output_path 로 파일 저장 후 확인하세요.\"\n : markdown\n const summary = [\n `채워진 필드: ${fillResult.filled.length}개`,\n fillResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${fillResult.rejected.join(\", \")}` : null,\n fillResult.unmatched.length > 0 ? `매칭 실패: ${fillResult.unmatched.join(\", \")}` : null,\n formInfo.fields.length > 0 ? `서식 필드: ${formInfo.fields.length}개 (확신도 ${(formInfo.confidence * 100).toFixed(0)}%)` : null,\n ].filter(Boolean).join(\" | \")\n\n if (output_format === \"hwpx\") {\n const hwpxBuffer = await markdownToHwpx(markdown)\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(hwpxBuffer))\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\nHWPX 파일 저장: ${resolve(output_path)}` }],\n }\n }\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n⚠️ output_path를 지정하면 HWPX 파일로 저장됩니다. 미리보기:\\n\\n${previewMd}` }],\n }\n }\n\n // markdown\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), markdown, \"utf-8\")\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n마크다운 파일 저장: ${resolve(output_path)}\\n\\n${previewMd}` }],\n }\n }\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n${previewMd}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: place_seal ─────────────────────────────\n\nserver.tool(\n \"place_seal\",\n \"도장/서명 이미지를 앵커 문구(\\\"(인)\\\"·\\\"서명 또는 인\\\" 등) 위에 부유(글 앞) 배치합니다. 표/페이지를 키우지 않습니다 (HWPX 전용).\",\n {\n file_path: z.string().min(1).describe(\"대상 HWPX 문서의 절대 경로\"),\n image_path: z.string().min(1).describe(\"도장/서명 이미지 절대 경로 (투명 배경 PNG 권장)\"),\n anchor: z.string().default(\"(인)\").describe(\"앵커 문구 — 이 문구 기준으로 배치\"),\n occurrence: z.number().int().min(0).default(0).describe(\"같은 앵커가 여럿일 때 0-based 선택\"),\n size_mm: z.number().positive().optional().describe(\"도장 한 변 크기 mm (기본: 줄높이×1.6, 7~18 클램프)\"),\n mode: z.enum([\"overlap\", \"right\", \"auto\"]).default(\"auto\").describe(\"overlap=문구 위 겹침, right=문구 오른쪽 옆, auto=공간 있으면 right\"),\n dx_mm: z.number().optional().describe(\"x 미세조정 mm\"),\n dy_mm: z.number().optional().describe(\"y 미세조정 mm\"),\n output_path: z.string().min(1).describe(\"출력 HWPX 저장 경로\"),\n },\n async ({ file_path, image_path, anchor, occurrence, size_mm, mode, dx_mm, dy_mm, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n if (format !== \"hwpx\") {\n return {\n content: [{ type: \"text\", text: `place_seal 은 HWPX 파일만 지원합니다 (감지된 포맷: ${format}).` }],\n isError: true,\n }\n }\n const imgResolved = resolve(image_path)\n if (statSync(imgResolved).size > 500 * 1024 * 1024) {\n return { content: [{ type: \"text\", text: `도장 이미지가 너무 큽니다 (${(statSync(imgResolved).size / 1024 / 1024).toFixed(0)}MB) — 500MB 이하여야 합니다.` }], isError: true }\n }\n const image = new Uint8Array(readFileSync(imgResolved))\n const ext = (extname(image_path).slice(1).toLowerCase() || \"png\") as \"png\" | \"jpg\" | \"jpeg\" | \"bmp\" | \"gif\"\n const { placeSealHwpx } = await import(\"./form/seal.js\")\n const result = await placeSealHwpx(buffer, [{\n anchor, occurrence, image, ext,\n sizeMm: size_mm, mode, dxMm: dx_mm, dyMm: dy_mm,\n }])\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(result.buffer))\n const p0 = result.placed[0]\n const warnLines = (p0.warnings ?? []).map(w => `\\n⚠️ ${w}`).join(\"\")\n return {\n content: [{\n type: \"text\",\n text: `도장 배치 완료: \"${p0.anchor}\" #${p0.occurrence} → ${p0.mode} (x ${p0.posXMm}mm, y ${p0.posYMm}mm, ${p0.sizeMm}mm각, ${p0.entry})\\n저장: ${resolve(output_path)}${warnLines}\\n표/페이지 불확장(글 앞 부유) — 한컴에서 위치 확인 후 dx_mm/dy_mm 로 미세조정 가능합니다.`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `도장 배치 실패: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n },\n)\n\n// ─── 도구: patch_document ────────────────────────────\n\nserver.tool(\n \"patch_document\",\n \"원본 HWPX/HWP의 서식(글꼴·표·도장칸·이미지)을 1바이트도 건드리지 않고, 편집된 마크다운의 바뀐 텍스트만 제자리 치환해 새 문서로 출력합니다. parse_document로 얻은 마크다운을 수정해 넘기세요 — 양식 빈칸 채우기·문구 수정에 적합하며 한컴 한글에서 변조 경고 없이 열립니다. (블록 추가/삭제·표 구조 변경은 미지원, 미적용 항목은 결과에 보고)\",\n {\n file_path: z.string().min(1).describe(\"원본 문서의 절대 경로 (HWPX 또는 HWP 5.x)\"),\n edited_markdown: z.string().min(1).describe(\"parse_document 출력 마크다운을 편집한 전체 마크다운. 바뀐 문단/셀 텍스트만 반영하고 블록 수·순서는 원본과 같게 유지하세요\"),\n output_path: z.string().min(1).describe(\"출력 파일 저장 절대 경로 (원본과 같은 확장자: .hwpx 또는 .hwp)\"),\n },\n async ({ file_path, edited_markdown, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n let isHwpx = format === \"hwpx\"\n if (isHwpx) {\n const zipFormat = await detectZipFormat(buffer)\n isHwpx = zipFormat === \"hwpx\"\n }\n if (!isHwpx && format !== \"hwp\") {\n return {\n content: [{ type: \"text\", text: `patch_document는 HWPX 또는 HWP 5.x만 지원합니다 (감지된 포맷: ${format}).` }],\n isError: true,\n }\n }\n\n const original = new Uint8Array(buffer)\n const result = isHwpx\n ? await patchHwpx(original, edited_markdown)\n : await patchHwp(original, edited_markdown)\n\n if (!result.success || !result.data) {\n return {\n content: [{ type: \"text\", text: `패치 실패: ${result.error ?? \"알 수 없는 오류\"}` }],\n isError: true,\n }\n }\n\n const out = resolve(output_path)\n mkdirSync(dirname(out), { recursive: true })\n writeFileSync(out, Buffer.from(result.data))\n\n const v = result.verification?.stats\n const lossless = v ? (v.modified === 0 && v.added === 0 && v.removed === 0) : undefined\n const lines = [\n `✓ ${result.applied}개 변경 적용 (${isHwpx ? \"HWPX\" : \"HWP\"}, 원본 서식 보존) → ${out}`,\n lossless === true ? \"검증: 편집 내용과 재파싱 결과 완전 일치\" :\n lossless === false ? `검증 잔차: 수정 ${v!.modified} · 추가 ${v!.added} · 삭제 ${v!.removed} (반영 안 된 편집 있음)` : null,\n result.skipped.length > 0\n ? `미적용 ${result.skipped.length}건:\\n` + result.skipped.map(s => ` - ${s.reason}`).join(\"\\n\")\n : null,\n ].filter(Boolean)\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: generate_document ─────────────────────────\n\nserver.tool(\n \"generate_document\",\n \"마크다운을 HWPX 한글 문서로 생성합니다. GFM 표(| 헤더 | … |)·헤딩·리스트·볼드를 한글 문서 요소로 변환하며, 공문서 프리셋 지정 시 행정 표준 서식(항목부호 8단계·공식 여백·명조 15pt)으로 렌더링합니다. 활용: 평문 문장을 표로 구조화해 새 한글파일로 만들거나, parse_document로 읽은 내용을 편집해 다시 HWPX로 출력. (원본 서식을 보존하며 일부 텍스트/표만 제자리 수정하려면 patch_document 사용)\",\n {\n markdown: z.string().min(1).describe(\"HWPX로 변환할 마크다운 전문. 표는 GFM 문법 사용 (예: '| 이름 | 부서 |\\\\n| --- | --- |\\\\n| 홍길동 | 기획팀 |')\"),\n output_path: z.string().min(1).describe(\"출력 HWPX 파일의 절대 경로 (.hwpx 권장)\"),\n preset: z.enum([\"기안문\", \"보고서\", \"계획서\", \"통지\", \"회의록\", \"official\", \"report\", \"plan\", \"notice\", \"minutes\"]).optional()\n .describe(\"공문서 프리셋 — 지정 시 한국 행정 공문서 표준 서식 적용. 미지정 시 범용 마크다운 변환\"),\n font: z.enum([\"myeongjo\", \"gothic\"]).optional().describe(\"본문 글꼴(공문서 모드): myeongjo=함초롬바탕(명조), gothic=맑은 고딕\"),\n body_pt: z.number().int().min(6).max(40).optional().describe(\"본문 글자 크기(pt, 공문서 모드). 기본 15\"),\n },\n async ({ markdown, output_path, preset, font, body_pt }) => {\n try {\n let gongmun: GongmunOptions | undefined\n if (preset) {\n gongmun = { preset }\n if (font) gongmun.bodyFont = font\n if (body_pt) gongmun.bodyPt = body_pt\n }\n const buf = await markdownToHwpx(markdown, gongmun ? { gongmun } : undefined)\n const out = resolve(output_path)\n mkdirSync(dirname(out), { recursive: true })\n writeFileSync(out, Buffer.from(buf))\n\n const mode = gongmun ? `공문서:${gongmun.preset}` : \"범용\"\n const tableCount = (markdown.match(/^\\s*\\|.*\\|\\s*$/gm) || []).length > 0\n ? `, 표 포함` : \"\"\n return {\n content: [{ type: \"text\", text: `✓ HWPX 생성 (${mode}${tableCount}) → ${out}\\n크기: ${(buf.byteLength / 1024).toFixed(1)}KB` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 서버 시작 ───────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => { console.error(err); process.exit(1) })\n"],"mappings":"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","names":["detectZipFormat","summary"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/** kordoc MCP 서버 — Claude/Cursor에서 문서 파싱 도구로 사용 */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { readFileSync, writeFileSync, realpathSync, openSync, readSync, closeSync, statSync, mkdirSync } from \"fs\"\nimport { resolve, isAbsolute, extname, dirname } from \"path\"\nimport { parse, detectFormat, detectZipFormat, blocksToMarkdown, compare, extractFormFields, fillFormFields, markdownToHwpx, fillHwpx, patchHwpx, patchHwp } from \"./index.js\"\nimport { fillWithUniqueGuard, type FillInput } from \"./form/match.js\"\nimport type { GongmunOptions } from \"./index.js\"\nimport { VERSION, toArrayBuffer, sanitizeError, KordocError } from \"./utils.js\"\nimport { extractHwp5MetadataOnly } from \"./hwp5/parser.js\"\nimport { extractHwpxMetadataOnly } from \"./hwpx/parser.js\"\nimport { MAX_INLINE_MD_BYTES } from \"./image/transcode.js\"\n// pdfjs-dist는 optional — dynamic import로 지연 로드\n// import { extractPdfMetadataOnly } from \"./pdf/parser.js\"\n\n/** 허용 파일 확장자 */\nconst ALLOWED_EXTENSIONS = new Set([\".hwp\", \".hwpx\", \".pdf\", \".xlsx\", \".docx\"])\n/** 최대 파일 크기 (500MB) */\nconst MAX_FILE_SIZE = 500 * 1024 * 1024\n\n/** 경로 정규화 및 보안 검증 */\nfunction safePath(filePath: string): string {\n if (!filePath) throw new KordocError(\"파일 경로가 비어있습니다\")\n const resolved = resolve(filePath)\n let real: string\n try {\n real = realpathSync(resolved)\n } catch (err: any) {\n if (err?.code === \"ENOENT\") throw new KordocError(`파일을 찾을 수 없습니다: ${resolved}`)\n if (err?.code === \"EACCES\" || err?.code === \"EPERM\") throw new KordocError(`파일 접근 권한이 없습니다: ${resolved}`)\n throw new KordocError(`경로 처리 오류 [${err?.code ?? \"UNKNOWN\"}]`)\n }\n if (!isAbsolute(real)) throw new KordocError(\"절대 경로만 허용됩니다\")\n const ext = extname(real).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) throw new KordocError(`지원하지 않는 확장자입니다: ${ext} (허용: ${[...ALLOWED_EXTENSIONS].join(\", \")})`)\n return real\n}\n\n/** 최대 파일 크기 — metadata 전용 (50MB, 전체 파싱보다 보수적) */\nconst MAX_METADATA_FILE_SIZE = 50 * 1024 * 1024\n\n/** 파일 읽기 + 크기 검증 공통 로직 */\nfunction readValidatedFile(filePath: string, maxSize = MAX_FILE_SIZE): { buffer: ArrayBuffer; resolved: string } {\n const resolved = safePath(filePath)\n let fileSize: number\n try {\n fileSize = statSync(resolved).size\n } catch (err: any) {\n throw new KordocError(`파일 상태 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\n }\n if (fileSize > maxSize) {\n throw new KordocError(`파일이 너무 큽니다: ${(fileSize / 1024 / 1024).toFixed(1)}MB (최대 ${maxSize / 1024 / 1024}MB)`)\n }\n let raw: Buffer\n try {\n raw = readFileSync(resolved)\n } catch (err: any) {\n throw new KordocError(`파일 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\n }\n return { buffer: toArrayBuffer(raw), resolved }\n}\n\n/** 파일 헤더(16바이트)만 읽어 포맷 감지 — 전체 파일 로드 불필요 */\nfunction detectFormatFromHeader(resolved: string): ReturnType<typeof detectFormat> {\n const fd = openSync(resolved, \"r\")\n try {\n const headerBuf = Buffer.alloc(16)\n readSync(fd, headerBuf, 0, 16, 0)\n return detectFormat(toArrayBuffer(headerBuf))\n } finally {\n closeSync(fd)\n }\n}\n\nconst server = new McpServer({\n name: \"kordoc\",\n version: VERSION,\n})\n\n// ─── 도구: parse_document ────────────────────────────\n\nserver.tool(\n \"parse_document\",\n \"한국 문서 파일(HWP, HWPX, PDF, XLSX, DOCX)을 마크다운으로 변환합니다. 파일 경로를 입력하면 포맷을 자동 감지하여 텍스트를 추출합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n // 에이전트가 별도 파일 없이 이미지를 해석하도록 자체 완결형 마크다운(이미지 인라인)을 기본 제공\n const result = await parse(buffer, { inlineImages: true })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n // 인라인 결과가 상한을 넘으면(이미지 다수 → base64 폭증) 에이전트 컨텍스트/전송 오버플로를\n // 막기 위해 비인라인(파일 참조) 마크다운으로 폴백하고, 생략 사실을 안내한다. (재파싱 대신\n // 이미 파싱된 blocks 로 마크다운만 재생성 — blocks 의 이미지 참조는 인라인으로 바뀌지 않음)\n // 인라인은 HWP5 이미지 경로에서만 일어나므로, 실제 인라인된 경우에 한해 폴백한다 — 그 외\n // 포맷의 대용량 '텍스트' 는 정상 출력이며 blocksToMarkdown 재생성은 포맷별 후처리(PDF\n // cleanPdfText 등)를 잃으므로 건드리지 않는다.\n let markdown = result.markdown\n let omitNote = \"\"\n const imgCount = result.images?.length ?? 0\n const didInline = result.fileType === \"hwp\" && imgCount > 0\n if (didInline && Buffer.byteLength(markdown, \"utf8\") > MAX_INLINE_MD_BYTES) {\n markdown = blocksToMarkdown(result.blocks)\n omitNote = `\\n\\n⚠️ 이미지 ${imgCount}개가 인라인 크기 상한(${(MAX_INLINE_MD_BYTES / 1024 / 1024).toFixed(0)}MB)을 초과하여 본문에 인라인하지 않았습니다. 이미지는 파일 참조(image_NNN)로만 표시됩니다.`\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n result.metadata?.title ? `제목: ${result.metadata.title}` : null,\n result.metadata?.author ? `작성자: ${result.metadata.author}` : null,\n result.isImageBased ? \"이미지 기반 PDF (텍스트 추출 불가)\" : null,\n ].filter(Boolean).join(\" | \")\n\n // outline/warnings 부가 정보 추가\n const parts: string[] = [`[${meta}]`]\n\n if (result.outline && result.outline.length > 0) {\n const outlineText = result.outline.map(o => `${\" \".repeat(o.level - 1)}- ${o.text}`).join(\"\\n\")\n parts.push(`\\n📑 문서 구조:\\n${outlineText}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n const warnText = result.warnings.map(w => `- [p${w.page || \"?\"}] ${w.message}`).join(\"\\n\")\n parts.push(`\\n⚠️ 경고:\\n${warnText}`)\n }\n\n parts.push(`\\n\\n${markdown}`)\n if (omitNote) parts.push(omitNote)\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: detect_format ─────────────────────────────\n\nserver.tool(\n \"detect_format\",\n \"파일의 포맷을 매직 바이트로 감지합니다 (hwpx, hwp, pdf, unknown).\",\n {\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n return {\n content: [{ type: \"text\", text: `${file_path}: ${format}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_metadata ────────────────────────────\n\nserver.tool(\n \"parse_metadata\",\n \"문서의 메타데이터(제목, 작성자, 날짜 등)만 빠르게 추출합니다. 전체 파싱 없이 헤더/매니페스트만 읽습니다.\",\n {\n file_path: z.string().min(1).describe(\"메타데이터를 추출할 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n // metadata 전용 크기 제한 (50MB)\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\n\n let metadata\n // ZIP 기반 포맷(hwpx)은 내부 구조로 세분화 (XLSX/DOCX 구분)\n let effectiveFormat = format\n if (format === \"hwpx\") {\n const { detectZipFormat } = await import(\"./detect.js\")\n const zipFormat = await detectZipFormat(buffer)\n if (zipFormat === \"xlsx\" || zipFormat === \"docx\") effectiveFormat = zipFormat as any\n }\n switch (effectiveFormat) {\n case \"hwp\":\n metadata = extractHwp5MetadataOnly(Buffer.from(buffer))\n break\n case \"hwpx\":\n metadata = await extractHwpxMetadataOnly(buffer)\n break\n case \"pdf\":\n try {\n const { extractPdfMetadataOnly } = await import(\"./pdf/parser.js\")\n metadata = await extractPdfMetadataOnly(buffer)\n } catch {\n metadata = undefined // pdfjs-dist 미설치 시 metadata 생략\n }\n break\n case \"xlsx\":\n case \"docx\": {\n // XLSX/DOCX는 전용 metadata 추출기가 없으므로 전체 파싱 후 metadata 반환\n const result = await parse(buffer)\n metadata = result.success ? result.metadata : undefined\n break\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ format, ...metadata }, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_pages ──────────────────────────────\n\nserver.tool(\n \"parse_pages\",\n \"문서의 특정 페이지/섹션 범위만 파싱합니다. PDF는 정확한 페이지, HWP/HWPX는 섹션 단위 근사치입니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n pages: z.string().min(1).describe(\"페이지 범위 (예: '1-3', '1,3,5-7')\"),\n },\n async ({ file_path, pages }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { pages })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n `범위: ${pages}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n ].filter(Boolean).join(\" | \")\n\n return {\n content: [{ type: \"text\", text: `[${meta}]\\n\\n${result.markdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_table ──────────────────────────────\n\nserver.tool(\n \"parse_table\",\n \"문서에서 N번째 테이블만 추출합니다 (0-based index). 테이블이 없거나 인덱스 범위를 초과하면 오류를 반환합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n table_index: z.number().int().min(0).describe(\"추출할 테이블 인덱스 (0부터 시작)\"),\n },\n async ({ file_path, table_index }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const tableBlocks = result.blocks.filter(b => b.type === \"table\" && b.table)\n if (tableBlocks.length === 0) {\n return {\n content: [{ type: \"text\", text: `문서에 테이블이 없습니다.` }],\n isError: true,\n }\n }\n\n if (table_index >= tableBlocks.length) {\n return {\n content: [{ type: \"text\", text: `테이블 인덱스 초과: ${table_index} (총 ${tableBlocks.length}개 테이블)` }],\n isError: true,\n }\n }\n\n const tableBlock = tableBlocks[table_index]\n const tableMarkdown = blocksToMarkdown([tableBlock])\n\n return {\n content: [{ type: \"text\", text: `[테이블 #${table_index} / 총 ${tableBlocks.length}개]\\n\\n${tableMarkdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: compare_documents ─────────────────────────\n\nserver.tool(\n \"compare_documents\",\n \"두 한국 문서 파일을 비교하여 추가/삭제/변경된 블록을 표시합니다. 신구대조표 생성에 활용됩니다. 크로스 포맷(HWP↔HWPX) 비교 가능.\",\n {\n file_path_a: z.string().min(1).describe(\"비교 원본 문서의 절대 경로\"),\n file_path_b: z.string().min(1).describe(\"비교 대상 문서의 절대 경로\"),\n },\n async ({ file_path_a, file_path_b }) => {\n try {\n const { buffer: bufA } = readValidatedFile(file_path_a)\n const { buffer: bufB } = readValidatedFile(file_path_b)\n\n const result = await compare(bufA, bufB)\n const { stats, diffs } = result\n\n const lines: string[] = [\n `## 문서 비교 결과`,\n `추가: ${stats.added} | 삭제: ${stats.removed} | 변경: ${stats.modified} | 동일: ${stats.unchanged}`,\n \"\",\n ]\n\n for (const d of diffs) {\n const prefix = d.type === \"added\" ? \"+\" : d.type === \"removed\" ? \"-\" : d.type === \"modified\" ? \"~\" : \" \"\n const text = d.after?.text || d.before?.text || (d.after?.table ? \"[테이블]\" : d.before?.table ? \"[테이블]\" : \"\")\n const sim = d.similarity !== undefined ? ` (${(d.similarity * 100).toFixed(0)}%)` : \"\"\n lines.push(`${prefix} ${text.substring(0, 200)}${sim}`)\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_form ───────────────────────────────\n\nserver.tool(\n \"parse_form\",\n \"한국 서식 문서에서 레이블-값 쌍을 구조화된 JSON으로 추출합니다. 양식/서식 문서에 최적화.\",\n {\n file_path: z.string().min(1).describe(\"서식 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const form = extractFormFields(result.blocks)\n return {\n content: [{ type: \"text\", text: JSON.stringify(form, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n/** fields + formats 를 FillInput 맵으로 결합 (formats의 라벨은 fields와 동일 표기 기준) */\nfunction buildFillInputs(fields: Record<string, string>, formats?: Record<string, string>): Record<string, FillInput> {\n const out: Record<string, FillInput> = {}\n for (const [k, v] of Object.entries(fields)) {\n const format = formats?.[k]\n out[k] = format ? { value: v, format } : v\n }\n return out\n}\n\n// ─── 도구: fill_form ───────────────────────────────\n\nserver.tool(\n \"fill_form\",\n \"한국 서식 문서의 빈칸을 채워서 새 문서로 출력합니다. hwpx-preserve를 사용하면 원본 서식(테두리, 폰트, 병합 등)을 100% 유지합니다.\",\n {\n file_path: z.string().min(1).describe(\"서식 템플릿 문서의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n fields: z.record(z.string(), z.string()).describe(\"채울 필드 맵 (라벨 → 값). 예: {\\\"성명\\\": \\\"홍길동\\\", \\\"전화번호\\\": \\\"010-1234-5678\\\"}\"),\n formats: z.record(z.string(), z.string()).optional().describe(\"필드별 값 서식 (라벨 → 포맷). 정준값 하나로 서식마다 다른 모양을 채울 때: date:yy.mm.dd / phone:hyphen·dot·digits / rrn:hyphen·masked / mask:###-## / 자유 패턴(yyyy년 m월 d일, ###-####-####)\"),\n require_unique: z.boolean().optional().describe(\"한 키가 서식의 2곳 이상에 매칭되면 채우지 않고 거부 — 반복 라벨 양식에서 남의 블록 오염 방지 (배열 값은 예외)\"),\n mask_values: z.boolean().optional().describe(\"응답에 값 대신 글자수만 표시 — 개인정보 채움 시 값이 대화 로그에 남지 않게\"),\n output_format: z.enum([\"markdown\", \"hwpx\", \"hwpx-preserve\"]).default(\"hwpx-preserve\").describe(\"출력 포맷: hwpx-preserve (원본 스타일 보존, HWPX 전용), hwpx (새 HWPX 생성), markdown\"),\n output_path: z.string().optional().describe(\"출력 파일 저장 경로 (선택). 지정 시 파일로 저장, 미지정 시 텍스트로 반환\"),\n },\n async ({ file_path, fields, formats, require_unique, mask_values, output_format, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n\n // ─── hwpx-preserve: 원본 ZIP 직접 수정 (스타일 보존) ───\n if (output_format === \"hwpx-preserve\") {\n const format = detectFormat(buffer)\n let isHwpx = format === \"hwpx\"\n if (isHwpx) {\n const zipFormat = await detectZipFormat(buffer)\n isHwpx = zipFormat === \"hwpx\"\n }\n if (!isHwpx) {\n return {\n content: [{ type: \"text\", text: `hwpx-preserve는 HWPX 파일만 지원합니다 (감지된 포맷: ${format}). hwpx 또는 markdown을 사용하세요.` }],\n isError: true,\n }\n }\n\n const inputs = buildFillInputs(fields, formats)\n const hwpxResult = require_unique\n ? await fillWithUniqueGuard(inputs, (vals, blocked) => fillHwpx(buffer, vals, blocked))\n : { ...(await fillHwpx(buffer, inputs)), rejected: [] as string[] }\n // 마스킹 verify — 채운 결과를 재파싱해 값이 실제 문서에 있는지만 확인 (값 미노출)\n let verifyLine: string | null = null\n if (mask_values && hwpxResult.filled.length > 0) {\n const reparsed = await parse(Buffer.from(hwpxResult.buffer))\n // 마크다운 이스케이프(\\*,\\|,\\~ 등)·개행/연속공백 정규화 후 비교 — rrn:masked\n // ('900315-1******')의 * 이스케이프로 생기던 결정적 false negative 방지.\n // 빈 값은 includes('')===true 로 항상 통과하던 것을 FILLED 에서 제외한다.\n const norm = (s: string): string => s.replace(/\\\\([\\\\`*_{}[\\]()#+.!|~>-])/g, \"$1\").replace(/\\s+/g, \" \")\n const normMd = reparsed.success ? norm(reparsed.markdown) : \"\"\n const okCount = reparsed.success\n ? hwpxResult.filled.filter(f => f.value !== \"\" && normMd.includes(norm(f.value))).length\n : 0\n verifyLine = `검증(마스킹): ${okCount}/${hwpxResult.filled.length} FILLED — 재파싱 대조, 값 미노출`\n }\n const summary = [\n `채워진 필드: ${hwpxResult.filled.length}개 (원본 스타일 보존)`,\n hwpxResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${hwpxResult.rejected.join(\", \")}` : null,\n hwpxResult.unmatched.length > 0 ? `매칭 실패: ${hwpxResult.unmatched.join(\", \")}` : null,\n verifyLine,\n ].filter(Boolean).join(\" | \")\n\n const filledList = hwpxResult.filled\n .map(f => ` - ${f.label}: ${mask_values ? `[${[...f.value].length}자]` : f.value}`).join(\"\\n\")\n\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(hwpxResult.buffer))\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\nHWPX 파일 저장 (원본 서식 유지): ${resolve(output_path)}` }],\n }\n }\n\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\n⚠️ output_path를 지정하면 원본 서식이 유지된 HWPX 파일로 저장됩니다.` }],\n }\n }\n\n // ─── 일반 경로: parse → fill → output ───\n const result = await parse(buffer)\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const formInfo = extractFormFields(result.blocks)\n const irInputs = buildFillInputs(fields, formats)\n const fillResult = require_unique\n ? await fillWithUniqueGuard(irInputs, (vals, blocked) => fillFormFields(result.blocks, vals, blocked))\n : { ...fillFormFields(result.blocks, irInputs), rejected: [] as string[] }\n\n if (fillResult.filled.length === 0 && formInfo.fields.length === 0) {\n return {\n content: [{ type: \"text\", text: `서식 필드를 찾을 수 없습니다. 일반 문서이거나 서식 패턴이 감지되지 않았습니다.` }],\n isError: true,\n }\n }\n\n const markdown = blocksToMarkdown(fillResult.blocks)\n // mask_values 시 채운 값(주민번호·연락처 등)이 응답(대화 로그)에 노출되지 않게\n // 본문 미리보기를 안내 문구로 대체 (sfill-8). 값은 output_path 파일에만 기록된다.\n const previewMd = mask_values\n ? \"⚠️ mask_values 활성 — 개인정보 노출 방지를 위해 본문을 응답에 포함하지 않습니다. output_path 로 파일 저장 후 확인하세요.\"\n : markdown\n const summary = [\n `채워진 필드: ${fillResult.filled.length}개`,\n fillResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${fillResult.rejected.join(\", \")}` : null,\n fillResult.unmatched.length > 0 ? `매칭 실패: ${fillResult.unmatched.join(\", \")}` : null,\n formInfo.fields.length > 0 ? `서식 필드: ${formInfo.fields.length}개 (확신도 ${(formInfo.confidence * 100).toFixed(0)}%)` : null,\n ].filter(Boolean).join(\" | \")\n\n if (output_format === \"hwpx\") {\n const hwpxBuffer = await markdownToHwpx(markdown)\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(hwpxBuffer))\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\nHWPX 파일 저장: ${resolve(output_path)}` }],\n }\n }\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n⚠️ output_path를 지정하면 HWPX 파일로 저장됩니다. 미리보기:\\n\\n${previewMd}` }],\n }\n }\n\n // markdown\n if (output_path) {\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), markdown, \"utf-8\")\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n마크다운 파일 저장: ${resolve(output_path)}\\n\\n${previewMd}` }],\n }\n }\n return {\n content: [{ type: \"text\", text: `[${summary}]\\n\\n${previewMd}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: place_seal ─────────────────────────────\n\nserver.tool(\n \"place_seal\",\n \"도장/서명 이미지를 앵커 문구(\\\"(인)\\\"·\\\"서명 또는 인\\\" 등) 위에 부유(글 앞) 배치합니다. 표/페이지를 키우지 않습니다 (HWPX 전용).\",\n {\n file_path: z.string().min(1).describe(\"대상 HWPX 문서의 절대 경로\"),\n image_path: z.string().min(1).describe(\"도장/서명 이미지 절대 경로 (투명 배경 PNG 권장)\"),\n anchor: z.string().default(\"(인)\").describe(\"앵커 문구 — 이 문구 기준으로 배치\"),\n occurrence: z.number().int().min(0).default(0).describe(\"같은 앵커가 여럿일 때 0-based 선택\"),\n size_mm: z.number().positive().optional().describe(\"도장 한 변 크기 mm (기본: 줄높이×1.6, 7~18 클램프)\"),\n mode: z.enum([\"overlap\", \"right\", \"auto\"]).default(\"auto\").describe(\"overlap=문구 위 겹침, right=문구 오른쪽 옆, auto=공간 있으면 right\"),\n dx_mm: z.number().optional().describe(\"x 미세조정 mm\"),\n dy_mm: z.number().optional().describe(\"y 미세조정 mm\"),\n output_path: z.string().min(1).describe(\"출력 HWPX 저장 경로\"),\n },\n async ({ file_path, image_path, anchor, occurrence, size_mm, mode, dx_mm, dy_mm, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n if (format !== \"hwpx\") {\n return {\n content: [{ type: \"text\", text: `place_seal 은 HWPX 파일만 지원합니다 (감지된 포맷: ${format}).` }],\n isError: true,\n }\n }\n const imgResolved = resolve(image_path)\n if (statSync(imgResolved).size > 500 * 1024 * 1024) {\n return { content: [{ type: \"text\", text: `도장 이미지가 너무 큽니다 (${(statSync(imgResolved).size / 1024 / 1024).toFixed(0)}MB) — 500MB 이하여야 합니다.` }], isError: true }\n }\n const image = new Uint8Array(readFileSync(imgResolved))\n const ext = (extname(image_path).slice(1).toLowerCase() || \"png\") as \"png\" | \"jpg\" | \"jpeg\" | \"bmp\" | \"gif\"\n const { placeSealHwpx } = await import(\"./form/seal.js\")\n const result = await placeSealHwpx(buffer, [{\n anchor, occurrence, image, ext,\n sizeMm: size_mm, mode, dxMm: dx_mm, dyMm: dy_mm,\n }])\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\n writeFileSync(resolve(output_path), Buffer.from(result.buffer))\n const p0 = result.placed[0]\n const warnLines = (p0.warnings ?? []).map(w => `\\n⚠️ ${w}`).join(\"\")\n return {\n content: [{\n type: \"text\",\n text: `도장 배치 완료: \"${p0.anchor}\" #${p0.occurrence} → ${p0.mode} (x ${p0.posXMm}mm, y ${p0.posYMm}mm, ${p0.sizeMm}mm각, ${p0.entry})\\n저장: ${resolve(output_path)}${warnLines}\\n표/페이지 불확장(글 앞 부유) — 한컴에서 위치 확인 후 dx_mm/dy_mm 로 미세조정 가능합니다.`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `도장 배치 실패: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n },\n)\n\n// ─── 도구: patch_document ────────────────────────────\n\nserver.tool(\n \"patch_document\",\n \"원본 HWPX/HWP의 서식(글꼴·표·도장칸·이미지)을 1바이트도 건드리지 않고, 편집된 마크다운의 바뀐 텍스트만 제자리 치환해 새 문서로 출력합니다. parse_document로 얻은 마크다운을 수정해 넘기세요 — 양식 빈칸 채우기·문구 수정에 적합하며 한컴 한글에서 변조 경고 없이 열립니다. (블록 추가/삭제·표 구조 변경은 미지원, 미적용 항목은 결과에 보고)\",\n {\n file_path: z.string().min(1).describe(\"원본 문서의 절대 경로 (HWPX 또는 HWP 5.x)\"),\n edited_markdown: z.string().min(1).describe(\"parse_document 출력 마크다운을 편집한 전체 마크다운. 바뀐 문단/셀 텍스트만 반영하고 블록 수·순서는 원본과 같게 유지하세요\"),\n output_path: z.string().min(1).describe(\"출력 파일 저장 절대 경로 (원본과 같은 확장자: .hwpx 또는 .hwp)\"),\n },\n async ({ file_path, edited_markdown, output_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n let isHwpx = format === \"hwpx\"\n if (isHwpx) {\n const zipFormat = await detectZipFormat(buffer)\n isHwpx = zipFormat === \"hwpx\"\n }\n if (!isHwpx && format !== \"hwp\") {\n return {\n content: [{ type: \"text\", text: `patch_document는 HWPX 또는 HWP 5.x만 지원합니다 (감지된 포맷: ${format}).` }],\n isError: true,\n }\n }\n\n const original = new Uint8Array(buffer)\n const result = isHwpx\n ? await patchHwpx(original, edited_markdown)\n : await patchHwp(original, edited_markdown)\n\n if (!result.success || !result.data) {\n return {\n content: [{ type: \"text\", text: `패치 실패: ${result.error ?? \"알 수 없는 오류\"}` }],\n isError: true,\n }\n }\n\n const out = resolve(output_path)\n mkdirSync(dirname(out), { recursive: true })\n writeFileSync(out, Buffer.from(result.data))\n\n const v = result.verification?.stats\n const lossless = v ? (v.modified === 0 && v.added === 0 && v.removed === 0) : undefined\n const lines = [\n `✓ ${result.applied}개 변경 적용 (${isHwpx ? \"HWPX\" : \"HWP\"}, 원본 서식 보존) → ${out}`,\n lossless === true ? \"검증: 편집 내용과 재파싱 결과 완전 일치\" :\n lossless === false ? `검증 잔차: 수정 ${v!.modified} · 추가 ${v!.added} · 삭제 ${v!.removed} (반영 안 된 편집 있음)` : null,\n result.skipped.length > 0\n ? `미적용 ${result.skipped.length}건:\\n` + result.skipped.map(s => ` - ${s.reason}`).join(\"\\n\")\n : null,\n ].filter(Boolean)\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: generate_document ─────────────────────────\n\nserver.tool(\n \"generate_document\",\n \"마크다운을 HWPX 한글 문서로 생성합니다. GFM 표(| 헤더 | … |)·헤딩·리스트·볼드를 한글 문서 요소로 변환하며, 공문서 프리셋 지정 시 행정 표준 서식(항목부호 8단계·공식 여백·명조 15pt)으로 렌더링합니다. 활용: 평문 문장을 표로 구조화해 새 한글파일로 만들거나, parse_document로 읽은 내용을 편집해 다시 HWPX로 출력. (원본 서식을 보존하며 일부 텍스트/표만 제자리 수정하려면 patch_document 사용)\",\n {\n markdown: z.string().min(1).describe(\"HWPX로 변환할 마크다운 전문. 표는 GFM 문법 사용 (예: '| 이름 | 부서 |\\\\n| --- | --- |\\\\n| 홍길동 | 기획팀 |')\"),\n output_path: z.string().min(1).describe(\"출력 HWPX 파일의 절대 경로 (.hwpx 권장)\"),\n preset: z.enum([\"기안문\", \"보고서\", \"계획서\", \"통지\", \"회의록\", \"official\", \"report\", \"plan\", \"notice\", \"minutes\"]).optional()\n .describe(\"공문서 프리셋 — 지정 시 한국 행정 공문서 표준 서식 적용. 미지정 시 범용 마크다운 변환\"),\n font: z.enum([\"myeongjo\", \"gothic\"]).optional().describe(\"본문 글꼴(공문서 모드): myeongjo=함초롬바탕(명조), gothic=맑은 고딕\"),\n body_pt: z.number().int().min(6).max(40).optional().describe(\"본문 글자 크기(pt, 공문서 모드). 기본 15\"),\n },\n async ({ markdown, output_path, preset, font, body_pt }) => {\n try {\n let gongmun: GongmunOptions | undefined\n if (preset) {\n gongmun = { preset }\n if (font) gongmun.bodyFont = font\n if (body_pt) gongmun.bodyPt = body_pt\n }\n const buf = await markdownToHwpx(markdown, gongmun ? { gongmun } : undefined)\n const out = resolve(output_path)\n mkdirSync(dirname(out), { recursive: true })\n writeFileSync(out, Buffer.from(buf))\n\n const mode = gongmun ? `공문서:${gongmun.preset}` : \"범용\"\n const tableCount = (markdown.match(/^\\s*\\|.*\\|\\s*$/gm) || []).length > 0\n ? `, 표 포함` : \"\"\n return {\n content: [{ type: \"text\", text: `✓ HWPX 생성 (${mode}${tableCount}) → ${out}\\n크기: ${(buf.byteLength / 1024).toFixed(1)}KB` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 서버 시작 ───────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => { console.error(err); process.exit(1) })\n"],"mappings":"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","names":["detectZipFormat","summary"]}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
blocksToMarkdown,
|
|
7
7
|
safeMax,
|
|
8
8
|
safeMin
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GIXXV52X.js";
|
|
10
10
|
import {
|
|
11
11
|
parsePageRange
|
|
12
12
|
} from "./chunk-GE43BE46.js";
|
|
@@ -3584,4 +3584,4 @@ export {
|
|
|
3584
3584
|
parsePdfDocument,
|
|
3585
3585
|
removeHeaderFooterBlocks
|
|
3586
3586
|
};
|
|
3587
|
-
//# sourceMappingURL=parser-
|
|
3587
|
+
//# sourceMappingURL=parser-5D6BQYTX.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
HEADING_RATIO_H2,
|
|
5
5
|
HEADING_RATIO_H3,
|
|
6
6
|
blocksToMarkdown
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YWIYGXLI.js";
|
|
8
8
|
import {
|
|
9
9
|
parsePageRange
|
|
10
10
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
KordocError,
|
|
13
13
|
safeMax,
|
|
14
14
|
safeMin
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6Y4KKPC3.js";
|
|
16
16
|
|
|
17
17
|
// src/pdf/line-extract.ts
|
|
18
18
|
import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
@@ -3587,4 +3587,4 @@ export {
|
|
|
3587
3587
|
parsePdfDocument,
|
|
3588
3588
|
removeHeaderFooterBlocks
|
|
3589
3589
|
};
|
|
3590
|
-
//# sourceMappingURL=parser-
|
|
3590
|
+
//# sourceMappingURL=parser-NZAE7PBE.js.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunk5HDBLRVPcjs = require('./chunk-5HDBLRVP.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
var _chunkDCZVOIEOcjs = require('./chunk-DCZVOIEO.cjs');
|
|
@@ -2147,14 +2147,14 @@ function isProseSpread(items) {
|
|
|
2147
2147
|
for (let i = 1; i < sorted.length; i++) {
|
|
2148
2148
|
gaps.push(sorted[i].x - (sorted[i - 1].x + sorted[i - 1].w));
|
|
2149
2149
|
}
|
|
2150
|
-
const maxGap =
|
|
2150
|
+
const maxGap = _chunk5HDBLRVPcjs.safeMax.call(void 0, gaps);
|
|
2151
2151
|
const avgLen = items.reduce((s, i) => s + i.text.length, 0) / items.length;
|
|
2152
2152
|
return maxGap < 40 && avgLen < 5;
|
|
2153
2153
|
}
|
|
2154
2154
|
function detectColumns(yLines) {
|
|
2155
2155
|
const allItems = yLines.flat();
|
|
2156
2156
|
if (allItems.length === 0) return null;
|
|
2157
|
-
const pageWidth =
|
|
2157
|
+
const pageWidth = _chunk5HDBLRVPcjs.safeMax.call(void 0, allItems.map((i) => i.x + i.w)) - _chunk5HDBLRVPcjs.safeMin.call(void 0, allItems.map((i) => i.x));
|
|
2158
2158
|
if (pageWidth < 100) return null;
|
|
2159
2159
|
let bigoLineIdx = -1;
|
|
2160
2160
|
for (let i = 0; i < yLines.length; i++) {
|
|
@@ -2376,9 +2376,9 @@ function detectHeadings(blocks, medianFontSize) {
|
|
|
2376
2376
|
if (/^\d+$/.test(text)) continue;
|
|
2377
2377
|
const ratio = block.style.fontSize / medianFontSize;
|
|
2378
2378
|
let level = 0;
|
|
2379
|
-
if (ratio >=
|
|
2380
|
-
else if (ratio >=
|
|
2381
|
-
else if (ratio >=
|
|
2379
|
+
if (ratio >= _chunk5HDBLRVPcjs.HEADING_RATIO_H1) level = 1;
|
|
2380
|
+
else if (ratio >= _chunk5HDBLRVPcjs.HEADING_RATIO_H2) level = 2;
|
|
2381
|
+
else if (ratio >= _chunk5HDBLRVPcjs.HEADING_RATIO_H3) level = 3;
|
|
2382
2382
|
if (level > 0) {
|
|
2383
2383
|
block.type = "heading";
|
|
2384
2384
|
block.level = level;
|
|
@@ -2954,7 +2954,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
|
|
|
2954
2954
|
}
|
|
2955
2955
|
if (remaining.length > 0) {
|
|
2956
2956
|
const allY = remaining.map((i) => i.y);
|
|
2957
|
-
const pageH =
|
|
2957
|
+
const pageH = _chunk5HDBLRVPcjs.safeMax.call(void 0, allY) - _chunk5HDBLRVPcjs.safeMin.call(void 0, allY);
|
|
2958
2958
|
const groups = xyCutOrder(remaining, Math.max(15, pageH * 0.03));
|
|
2959
2959
|
const textBlocks = [];
|
|
2960
2960
|
for (const group of groups) {
|
|
@@ -3130,7 +3130,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false) {
|
|
|
3130
3130
|
blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(items) });
|
|
3131
3131
|
} else {
|
|
3132
3132
|
const allY = items.map((i) => i.y);
|
|
3133
|
-
const pageHeight =
|
|
3133
|
+
const pageHeight = _chunk5HDBLRVPcjs.safeMax.call(void 0, allY) - _chunk5HDBLRVPcjs.safeMin.call(void 0, allY);
|
|
3134
3134
|
const gapThreshold = Math.max(15, pageHeight * 0.03);
|
|
3135
3135
|
const orderedGroups = proseCutX !== null ? splitTwoColumnProse(items, proseCutX) : xyCutOrder(items, gapThreshold);
|
|
3136
3136
|
for (const group of orderedGroups) {
|
|
@@ -3383,7 +3383,7 @@ async function loadPdfWithTimeout(buffer) {
|
|
|
3383
3383
|
new Promise((_, reject) => {
|
|
3384
3384
|
timer = setTimeout(() => {
|
|
3385
3385
|
loadingTask.destroy();
|
|
3386
|
-
reject(new (0,
|
|
3386
|
+
reject(new (0, _chunk5HDBLRVPcjs.KordocError)("PDF \uB85C\uB529 \uD0C0\uC784\uC544\uC6C3 (30\uCD08 \uCD08\uACFC)"));
|
|
3387
3387
|
}, PDF_LOAD_TIMEOUT_MS);
|
|
3388
3388
|
})
|
|
3389
3389
|
]);
|
|
@@ -3396,7 +3396,7 @@ async function parsePdfDocument(buffer, options) {
|
|
|
3396
3396
|
const doc = await loadPdfWithTimeout(buffer);
|
|
3397
3397
|
try {
|
|
3398
3398
|
const pageCount = doc.numPages;
|
|
3399
|
-
if (pageCount === 0) throw new (0,
|
|
3399
|
+
if (pageCount === 0) throw new (0, _chunk5HDBLRVPcjs.KordocError)("PDF\uC5D0 \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
3400
3400
|
const metadata = { pageCount };
|
|
3401
3401
|
await extractPdfMetadata(doc, metadata);
|
|
3402
3402
|
const blocks = [];
|
|
@@ -3456,11 +3456,11 @@ async function parsePdfDocument(buffer, options) {
|
|
|
3456
3456
|
pageText += pageText ? "\n" + t : t;
|
|
3457
3457
|
}
|
|
3458
3458
|
pageQuality.push(computePageQuality(i, pageText));
|
|
3459
|
-
if (totalTextBytes > MAX_TOTAL_TEXT) throw new (0,
|
|
3459
|
+
if (totalTextBytes > MAX_TOTAL_TEXT) throw new (0, _chunk5HDBLRVPcjs.KordocError)("\uD14D\uC2A4\uD2B8 \uCD94\uCD9C \uD06C\uAE30 \uCD08\uACFC");
|
|
3460
3460
|
parsedPages++;
|
|
3461
3461
|
_optionalChain([options, 'optionalAccess', _36 => _36.onProgress, 'optionalCall', _37 => _37(parsedPages, totalTarget)]);
|
|
3462
3462
|
} catch (pageErr) {
|
|
3463
|
-
if (pageErr instanceof
|
|
3463
|
+
if (pageErr instanceof _chunk5HDBLRVPcjs.KordocError) throw pageErr;
|
|
3464
3464
|
warnings.push({ page: i, message: `\uD398\uC774\uC9C0 ${i} \uD30C\uC2F1 \uC2E4\uD328: ${pageErr instanceof Error ? pageErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "PARTIAL_PARSE" });
|
|
3465
3465
|
}
|
|
3466
3466
|
}
|
|
@@ -3528,7 +3528,7 @@ async function parsePdfDocument(buffer, options) {
|
|
|
3528
3528
|
detectKoreanListBlocks(blocks);
|
|
3529
3529
|
const outline = blocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
|
|
3530
3530
|
sanitizeBlockControlChars(blocks);
|
|
3531
|
-
let markdown = cleanPdfText(
|
|
3531
|
+
let markdown = cleanPdfText(_chunk5HDBLRVPcjs.blocksToMarkdown.call(void 0, blocks));
|
|
3532
3532
|
return {
|
|
3533
3533
|
markdown,
|
|
3534
3534
|
blocks,
|
|
@@ -3587,4 +3587,4 @@ async function extractPdfMetadataOnly(buffer) {
|
|
|
3587
3587
|
|
|
3588
3588
|
|
|
3589
3589
|
exports.cleanPdfText = cleanPdfText; exports.detectKoreanListBlocks = detectKoreanListBlocks; exports.detectTableCaptions = detectTableCaptions; exports.extractPdfMetadataOnly = extractPdfMetadataOnly; exports.mergeCrossPageTables = mergeCrossPageTables; exports.parsePdfDocument = parsePdfDocument; exports.removeHeaderFooterBlocks = removeHeaderFooterBlocks;
|
|
3590
|
-
//# sourceMappingURL=parser-
|
|
3590
|
+
//# sourceMappingURL=parser-S34YR2SH.cjs.map
|