kordoc 3.16.2 → 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.
Files changed (56) hide show
  1. package/README.md +37 -12
  2. package/dist/{-C4FVXHIP.js → -OUCPI6B3.js} +9 -7
  3. package/dist/{chunk-HECUYZZK.js → chunk-4YEZTZAR.js} +79 -3
  4. package/dist/chunk-4YEZTZAR.js.map +1 -0
  5. package/dist/{chunk-72LLOLFX.cjs → chunk-5HDBLRVP.cjs} +45 -9
  6. package/dist/chunk-5HDBLRVP.cjs.map +1 -0
  7. package/dist/{chunk-4WI7QXEG.js → chunk-6Y4KKPC3.js} +2 -2
  8. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  9. package/dist/{chunk-EJ2ZIYTO.js → chunk-GIXXV52X.js} +39 -3
  10. package/dist/chunk-GIXXV52X.js.map +1 -0
  11. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  12. package/dist/{chunk-EUEKDZ7O.js → chunk-PWHM3KAA.js} +535 -83
  13. package/dist/chunk-PWHM3KAA.js.map +1 -0
  14. package/dist/{chunk-WR5LHRXO.js → chunk-VP34RO24.js} +3 -3
  15. package/dist/{chunk-IS3IK7UT.js → chunk-YWIYGXLI.js} +39 -3
  16. package/dist/chunk-YWIYGXLI.js.map +1 -0
  17. package/dist/{chunk-E4Y73XRH.js → chunk-ZTUQYITH.js} +127 -68
  18. package/dist/chunk-ZTUQYITH.js.map +1 -0
  19. package/dist/cli.js +18 -15
  20. package/dist/cli.js.map +1 -1
  21. package/dist/formula-5NKVS2LR.cjs.map +1 -1
  22. package/dist/index.cjs +1045 -459
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +121 -3
  25. package/dist/index.d.ts +121 -3
  26. package/dist/index.js +784 -198
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp.js +22 -10
  29. package/dist/mcp.js.map +1 -1
  30. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  31. package/dist/{parser-GWL72XYS.js → parser-5D6BQYTX.js} +2 -2
  32. package/dist/{parser-CMPNM6ZL.js → parser-NZAE7PBE.js} +3 -3
  33. package/dist/{parser-YVRTKGKO.cjs → parser-S34YR2SH.cjs} +14 -14
  34. package/dist/parser-S34YR2SH.cjs.map +1 -0
  35. package/dist/provider-G4C2V2PD.cjs.map +1 -1
  36. package/dist/render-HRAW7ASR.js +10 -0
  37. package/dist/seal-CK64CXWO.js +10 -0
  38. package/dist/{watch-WLI6K3IR.js → watch-2EFXBRYK.js} +7 -7
  39. package/package.json +1 -1
  40. package/dist/chunk-72LLOLFX.cjs.map +0 -1
  41. package/dist/chunk-E4Y73XRH.js.map +0 -1
  42. package/dist/chunk-EJ2ZIYTO.js.map +0 -1
  43. package/dist/chunk-EUEKDZ7O.js.map +0 -1
  44. package/dist/chunk-HECUYZZK.js.map +0 -1
  45. package/dist/chunk-IS3IK7UT.js.map +0 -1
  46. package/dist/parser-YVRTKGKO.cjs.map +0 -1
  47. package/dist/render-A2LFMJCP.js +0 -10
  48. package/dist/seal-KA4VAV2Z.js +0 -10
  49. /package/dist/{-C4FVXHIP.js.map → -OUCPI6B3.js.map} +0 -0
  50. /package/dist/{chunk-4WI7QXEG.js.map → chunk-6Y4KKPC3.js.map} +0 -0
  51. /package/dist/{chunk-WR5LHRXO.js.map → chunk-VP34RO24.js.map} +0 -0
  52. /package/dist/{parser-GWL72XYS.js.map → parser-5D6BQYTX.js.map} +0 -0
  53. /package/dist/{parser-CMPNM6ZL.js.map → parser-NZAE7PBE.js.map} +0 -0
  54. /package/dist/{render-A2LFMJCP.js.map → render-HRAW7ASR.js.map} +0 -0
  55. /package/dist/{seal-KA4VAV2Z.js.map → seal-CK64CXWO.js.map} +0 -0
  56. /package/dist/{watch-WLI6K3IR.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-EUEKDZ7O.js";
15
+ } from "./chunk-PWHM3KAA.js";
15
16
  import {
16
17
  blocksToMarkdown
17
- } from "./chunk-IS3IK7UT.js";
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-WR5LHRXO.js";
24
- import "./chunk-E4Y73XRH.js";
25
- import "./chunk-HECUYZZK.js";
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-4WI7QXEG.js";
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
- ${result.markdown}`);
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-CMPNM6ZL.js");
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-KA4VAV2Z.js");
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"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/chris_gomdori/workspace/kordoc/dist/page-range-P7SDW6LR.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,0DAAC","file":"/Users/chris_gomdori/workspace/kordoc/dist/page-range-P7SDW6LR.cjs"}
1
+ {"version":3,"sources":["/Users/mong-e/workspace/kordoc/dist/page-range-P7SDW6LR.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,0DAAC","file":"/Users/mong-e/workspace/kordoc/dist/page-range-P7SDW6LR.cjs"}
@@ -6,7 +6,7 @@ import {
6
6
  blocksToMarkdown,
7
7
  safeMax,
8
8
  safeMin
9
- } from "./chunk-EJ2ZIYTO.js";
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-GWL72XYS.js.map
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-IS3IK7UT.js";
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-4WI7QXEG.js";
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-CMPNM6ZL.js.map
3590
+ //# sourceMappingURL=parser-NZAE7PBE.js.map
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
 
9
- var _chunk72LLOLFXcjs = require('./chunk-72LLOLFX.cjs');
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 = _chunk72LLOLFXcjs.safeMax.call(void 0, gaps);
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 = _chunk72LLOLFXcjs.safeMax.call(void 0, allItems.map((i) => i.x + i.w)) - _chunk72LLOLFXcjs.safeMin.call(void 0, allItems.map((i) => i.x));
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 >= _chunk72LLOLFXcjs.HEADING_RATIO_H1) level = 1;
2380
- else if (ratio >= _chunk72LLOLFXcjs.HEADING_RATIO_H2) level = 2;
2381
- else if (ratio >= _chunk72LLOLFXcjs.HEADING_RATIO_H3) level = 3;
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 = _chunk72LLOLFXcjs.safeMax.call(void 0, allY) - _chunk72LLOLFXcjs.safeMin.call(void 0, allY);
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 = _chunk72LLOLFXcjs.safeMax.call(void 0, allY) - _chunk72LLOLFXcjs.safeMin.call(void 0, allY);
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, _chunk72LLOLFXcjs.KordocError)("PDF \uB85C\uB529 \uD0C0\uC784\uC544\uC6C3 (30\uCD08 \uCD08\uACFC)"));
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, _chunk72LLOLFXcjs.KordocError)("PDF\uC5D0 \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
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, _chunk72LLOLFXcjs.KordocError)("\uD14D\uC2A4\uD2B8 \uCD94\uCD9C \uD06C\uAE30 \uCD08\uACFC");
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 _chunk72LLOLFXcjs.KordocError) throw pageErr;
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(_chunk72LLOLFXcjs.blocksToMarkdown.call(void 0, blocks));
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-YVRTKGKO.cjs.map
3590
+ //# sourceMappingURL=parser-S34YR2SH.cjs.map