kordoc 4.0.7 → 4.0.8

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 (111) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +823 -806
  3. package/dist/{-6HWEFXVV.js → -LE4RLXVA.js} +9 -9
  4. package/dist/{chunk-BKWHX3RC.js → chunk-2DZQF6YJ.js} +3 -3
  5. package/dist/chunk-2DZQF6YJ.js.map +1 -0
  6. package/dist/{chunk-6GBLFQMA.js → chunk-5RPYBC2Q.js} +1 -1
  7. package/dist/chunk-5RPYBC2Q.js.map +1 -0
  8. package/dist/{chunk-U25XGCNH.cjs → chunk-6XAAYAWL.cjs} +142 -3
  9. package/dist/chunk-6XAAYAWL.cjs.map +1 -0
  10. package/dist/{chunk-TJJTLM76.js → chunk-7S3M4N4E.js} +666 -742
  11. package/dist/chunk-7S3M4N4E.js.map +1 -0
  12. package/dist/{chunk-Q7EN4EUJ.js → chunk-BNU5QGIZ.js} +2 -2
  13. package/dist/chunk-BNU5QGIZ.js.map +1 -0
  14. package/dist/{chunk-NGSHDBQR.js → chunk-D35VBACN.js} +3 -3
  15. package/dist/chunk-D35VBACN.js.map +1 -0
  16. package/dist/{chunk-X34YWRL5.cjs → chunk-DCZVOIEO.cjs} +1 -1
  17. package/dist/chunk-DCZVOIEO.cjs.map +1 -0
  18. package/dist/{chunk-AIQ3ISQU.js → chunk-GE43BE46.js} +1 -1
  19. package/dist/chunk-GE43BE46.js.map +1 -0
  20. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  21. package/dist/{chunk-CNM75ZSX.js → chunk-HMUEU7D7.js} +2 -2
  22. package/dist/chunk-HMUEU7D7.js.map +1 -0
  23. package/dist/{chunk-CFQLK3LM.js → chunk-MEPHGCPQ.js} +1 -1
  24. package/dist/chunk-MEPHGCPQ.js.map +1 -0
  25. package/dist/{chunk-3TBUDJDE.js → chunk-MOL7MDBG.js} +1 -1
  26. package/dist/chunk-MOL7MDBG.js.map +1 -0
  27. package/dist/{chunk-5OKHAJ62.js → chunk-OQPILS7B.js} +142 -3
  28. package/dist/chunk-OQPILS7B.js.map +1 -0
  29. package/dist/{chunk-ONXVBURQ.js → chunk-RROH5WHO.js} +2 -2
  30. package/dist/chunk-RROH5WHO.js.map +1 -0
  31. package/dist/{chunk-2SN3CKME.js → chunk-UEIYETCQ.js} +142 -3
  32. package/dist/chunk-UEIYETCQ.js.map +1 -0
  33. package/dist/cli.js +18 -18
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{detect-NTR2FUEG.js → detect-RI2MQ33K.js} +2 -2
  36. package/dist/{formula-ZYUDQHD3.cjs → formula-5NKVS2LR.cjs} +1 -1
  37. package/dist/formula-5NKVS2LR.cjs.map +1 -0
  38. package/dist/{formula-KOQV353G.js → formula-JCNF43NE.js} +1 -1
  39. package/dist/formula-JCNF43NE.js.map +1 -0
  40. package/dist/{formula-MEDGYQXS.js → formula-RXVSQPXI.js} +1 -1
  41. package/dist/formula-RXVSQPXI.js.map +1 -0
  42. package/dist/index.cjs +1018 -1094
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +1 -1
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.js +780 -856
  47. package/dist/index.js.map +1 -1
  48. package/dist/mcp.js +14 -14
  49. package/dist/mcp.js.map +1 -1
  50. package/dist/page-range-737B4EZW.js +8 -0
  51. package/dist/page-range-LNMXJU6W.js +7 -0
  52. package/dist/page-range-P7SDW6LR.cjs +8 -0
  53. package/dist/page-range-P7SDW6LR.cjs.map +1 -0
  54. package/dist/{parser-E6U5TNAU.cjs → parser-ITMU2WN5.cjs} +214 -38
  55. package/dist/parser-ITMU2WN5.cjs.map +1 -0
  56. package/dist/{parser-UGB3NIVH.js → parser-KMMUS73Q.js} +183 -7
  57. package/dist/parser-KMMUS73Q.js.map +1 -0
  58. package/dist/{parser-5EHWYJMC.js → parser-TCTCSBYZ.js} +185 -9
  59. package/dist/parser-TCTCSBYZ.js.map +1 -0
  60. package/dist/{profile-io-CAKRPQRD.js → profile-io-SLN4P76T.js} +3 -3
  61. package/dist/{provider-H4WWSPOV.js → provider-4ZJKV3DC.js} +1 -1
  62. package/dist/provider-4ZJKV3DC.js.map +1 -0
  63. package/dist/{provider-7H4CPZYS.js → provider-AKROB7WQ.js} +1 -1
  64. package/dist/provider-AKROB7WQ.js.map +1 -0
  65. package/dist/{provider-5K7O3Z2O.cjs → provider-G4C2V2PD.cjs} +1 -1
  66. package/dist/provider-G4C2V2PD.cjs.map +1 -0
  67. package/dist/render-25BT623I.js +10 -0
  68. package/dist/seal-R4TETA4C.js +10 -0
  69. package/dist/{setup-QSJ2INNS.js → setup-57FB3LSP.js} +1 -1
  70. package/dist/setup-57FB3LSP.js.map +1 -0
  71. package/dist/{watch-RMX427YR.js → watch-OHQWSVPE.js} +9 -9
  72. package/dist/watch-OHQWSVPE.js.map +1 -0
  73. package/package.json +97 -97
  74. package/dist/chunk-2SN3CKME.js.map +0 -1
  75. package/dist/chunk-3TBUDJDE.js.map +0 -1
  76. package/dist/chunk-5OKHAJ62.js.map +0 -1
  77. package/dist/chunk-6GBLFQMA.js.map +0 -1
  78. package/dist/chunk-AIQ3ISQU.js.map +0 -1
  79. package/dist/chunk-BKWHX3RC.js.map +0 -1
  80. package/dist/chunk-CFQLK3LM.js.map +0 -1
  81. package/dist/chunk-CNM75ZSX.js.map +0 -1
  82. package/dist/chunk-NGSHDBQR.js.map +0 -1
  83. package/dist/chunk-ONXVBURQ.js.map +0 -1
  84. package/dist/chunk-Q7EN4EUJ.js.map +0 -1
  85. package/dist/chunk-TJJTLM76.js.map +0 -1
  86. package/dist/chunk-U25XGCNH.cjs.map +0 -1
  87. package/dist/chunk-X34YWRL5.cjs.map +0 -1
  88. package/dist/formula-KOQV353G.js.map +0 -1
  89. package/dist/formula-MEDGYQXS.js.map +0 -1
  90. package/dist/formula-ZYUDQHD3.cjs.map +0 -1
  91. package/dist/page-range-CIRRJPXJ.js +0 -7
  92. package/dist/page-range-OF5I4PQY.js +0 -8
  93. package/dist/page-range-TPIRSA3J.cjs +0 -8
  94. package/dist/page-range-TPIRSA3J.cjs.map +0 -1
  95. package/dist/parser-5EHWYJMC.js.map +0 -1
  96. package/dist/parser-E6U5TNAU.cjs.map +0 -1
  97. package/dist/parser-UGB3NIVH.js.map +0 -1
  98. package/dist/provider-5K7O3Z2O.cjs.map +0 -1
  99. package/dist/provider-7H4CPZYS.js.map +0 -1
  100. package/dist/provider-H4WWSPOV.js.map +0 -1
  101. package/dist/render-P6663O4I.js +0 -10
  102. package/dist/seal-7PPTXKKV.js +0 -10
  103. package/dist/setup-QSJ2INNS.js.map +0 -1
  104. package/dist/watch-RMX427YR.js.map +0 -1
  105. /package/dist/{-6HWEFXVV.js.map → -LE4RLXVA.js.map} +0 -0
  106. /package/dist/{detect-NTR2FUEG.js.map → detect-RI2MQ33K.js.map} +0 -0
  107. /package/dist/{page-range-CIRRJPXJ.js.map → page-range-737B4EZW.js.map} +0 -0
  108. /package/dist/{page-range-OF5I4PQY.js.map → page-range-LNMXJU6W.js.map} +0 -0
  109. /package/dist/{profile-io-CAKRPQRD.js.map → profile-io-SLN4P76T.js.map} +0 -0
  110. /package/dist/{render-P6663O4I.js.map → render-25BT623I.js.map} +0 -0
  111. /package/dist/{seal-7PPTXKKV.js.map → seal-R4TETA4C.js.map} +0 -0
package/dist/mcp.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  SIZE_KEYS,
15
15
  SIZE_PT_RANGE,
16
16
  buildGongmunOptions
17
- } from "./chunk-6GBLFQMA.js";
17
+ } from "./chunk-5RPYBC2Q.js";
18
18
  import {
19
19
  PRESET_ALIAS,
20
20
  compare,
@@ -31,24 +31,24 @@ import {
31
31
  patchHwp,
32
32
  patchHwpx,
33
33
  unknownFontWarnings
34
- } from "./chunk-TJJTLM76.js";
34
+ } from "./chunk-7S3M4N4E.js";
35
35
  import {
36
36
  blocksToMarkdown
37
- } from "./chunk-2SN3CKME.js";
37
+ } from "./chunk-UEIYETCQ.js";
38
38
  import {
39
39
  detectFormat,
40
40
  detectZipFormat
41
- } from "./chunk-CFQLK3LM.js";
42
- import "./chunk-3TBUDJDE.js";
43
- import "./chunk-NGSHDBQR.js";
44
- import "./chunk-BKWHX3RC.js";
45
- import "./chunk-Q7EN4EUJ.js";
41
+ } from "./chunk-MEPHGCPQ.js";
42
+ import "./chunk-MOL7MDBG.js";
43
+ import "./chunk-D35VBACN.js";
44
+ import "./chunk-2DZQF6YJ.js";
45
+ import "./chunk-BNU5QGIZ.js";
46
46
  import {
47
47
  KordocError,
48
48
  VERSION,
49
49
  sanitizeError,
50
50
  toArrayBuffer
51
- } from "./chunk-CNM75ZSX.js";
51
+ } from "./chunk-HMUEU7D7.js";
52
52
 
53
53
  // src/mcp.ts
54
54
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -211,7 +211,7 @@ server.tool(
211
211
  let metadata;
212
212
  let effectiveFormat = format;
213
213
  if (format === "hwpx") {
214
- const { detectZipFormat: detectZipFormat2 } = await import("./detect-NTR2FUEG.js");
214
+ const { detectZipFormat: detectZipFormat2 } = await import("./detect-RI2MQ33K.js");
215
215
  const zipFormat = await detectZipFormat2(buffer);
216
216
  if (zipFormat === "xlsx" || zipFormat === "docx") effectiveFormat = zipFormat;
217
217
  }
@@ -224,7 +224,7 @@ server.tool(
224
224
  break;
225
225
  case "pdf":
226
226
  try {
227
- const { extractPdfMetadataOnly } = await import("./parser-5EHWYJMC.js");
227
+ const { extractPdfMetadataOnly } = await import("./parser-TCTCSBYZ.js");
228
228
  metadata = await extractPdfMetadataOnly(buffer);
229
229
  } catch {
230
230
  metadata = void 0;
@@ -576,7 +576,7 @@ server.tool(
576
576
  }
577
577
  const image = new Uint8Array(readFileSync(imgResolved));
578
578
  const ext = extname(image_path).slice(1).toLowerCase() || "png";
579
- const { placeSealHwpx } = await import("./seal-7PPTXKKV.js");
579
+ const { placeSealHwpx } = await import("./seal-R4TETA4C.js");
580
580
  const result = await placeSealHwpx(buffer, [{
581
581
  anchor,
582
582
  occurrence,
@@ -671,7 +671,7 @@ server.tool(
671
671
  async ({ hwpx_path, output_path }) => {
672
672
  try {
673
673
  const { buffer } = readValidatedFile(hwpx_path);
674
- const { hwpxToProfile } = await import("./-6HWEFXVV.js");
674
+ const { hwpxToProfile } = await import("./-LE4RLXVA.js");
675
675
  const profile = await hwpxToProfile(buffer);
676
676
  const out = resolve(output_path);
677
677
  mkdirSync(dirname(out), { recursive: true });
@@ -755,7 +755,7 @@ server.tool(
755
755
  }
756
756
  let profile;
757
757
  if (profile_path) {
758
- const { parseFormatProfileJson } = await import("./profile-io-CAKRPQRD.js");
758
+ const { parseFormatProfileJson } = await import("./profile-io-SLN4P76T.js");
759
759
  profile = parseFormatProfileJson(readFileSync(resolve(profile_path), "utf-8"));
760
760
  }
761
761
  const buf = await markdownToHwpx(markdown, gongmun || profile ? { ...gongmun ? { gongmun } : {}, ...profile ? { profile } : {} } : void 0);
package/dist/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/** kordoc MCP 서버 — Claude/Cursor에서 문서 파싱 도구로 사용 */\r\n\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\r\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\r\nimport { z } from \"zod\"\r\nimport { readFileSync, writeFileSync, realpathSync, openSync, readSync, closeSync, statSync, mkdirSync } from \"fs\"\r\nimport { resolve, isAbsolute, extname, dirname } from \"path\"\r\nimport { parse, detectFormat, detectZipFormat, blocksToMarkdown, compare, extractFormFields, fillFormFields, markdownToHwpx, fillHwpx, patchHwpx, patchHwp, unknownFontWarnings, incompatibleGongmunWarnings, gongmunLintWarnings, PRESET_ALIAS } from \"./index.js\"\r\nimport { fillWithUniqueGuard, type FillInput } from \"./form/match.js\"\r\nimport type { GongmunOptions } from \"./index.js\"\r\nimport {\r\n buildGongmunOptions, BODY_FONTS, H2_MARKERS, BULLET2_CHARS,\r\n FONT_ROLE_KEYS, SIZE_KEYS, DOC_HEAD_KEYS, DOC_FOOT_KEYS, NOTICE_HEAD_KEYS, PRESS_CONTACT_KEYS,\r\n BODY_PT_RANGE, LINE_SPACING_RANGE, SIZE_PT_RANGE, APPROVAL_MAX,\r\n} from \"./hwpx/gongmun-surface.js\"\r\nimport { VERSION, toArrayBuffer, sanitizeError, KordocError } from \"./utils.js\"\r\nimport { extractHwp5MetadataOnly } from \"./hwp5/parser.js\"\r\nimport { extractHwpxMetadataOnly } from \"./hwpx/parser.js\"\r\n// pdfjs-dist는 optional — dynamic import로 지연 로드\r\n// import { extractPdfMetadataOnly } from \"./pdf/parser.js\"\r\n\r\n/** 허용 파일 확장자 */\r\nconst ALLOWED_EXTENSIONS = new Set([\".hwp\", \".hwpx\", \".pdf\", \".xlsx\", \".docx\"])\r\n/** 최대 파일 크기 (500MB) */\r\nconst MAX_FILE_SIZE = 500 * 1024 * 1024\r\n\r\n/** 경로 정규화 및 보안 검증 */\r\nfunction safePath(filePath: string): string {\r\n if (!filePath) throw new KordocError(\"파일 경로가 비어있습니다\")\r\n const resolved = resolve(filePath)\r\n let real: string\r\n try {\r\n real = realpathSync(resolved)\r\n } catch (err: any) {\r\n if (err?.code === \"ENOENT\") throw new KordocError(`파일을 찾을 수 없습니다: ${resolved}`)\r\n if (err?.code === \"EACCES\" || err?.code === \"EPERM\") throw new KordocError(`파일 접근 권한이 없습니다: ${resolved}`)\r\n throw new KordocError(`경로 처리 오류 [${err?.code ?? \"UNKNOWN\"}]`)\r\n }\r\n if (!isAbsolute(real)) throw new KordocError(\"절대 경로만 허용됩니다\")\r\n const ext = extname(real).toLowerCase()\r\n if (!ALLOWED_EXTENSIONS.has(ext)) throw new KordocError(`지원하지 않는 확장자입니다: ${ext} (허용: ${[...ALLOWED_EXTENSIONS].join(\", \")})`)\r\n return real\r\n}\r\n\r\n/** 최대 파일 크기 — metadata 전용 (50MB, 전체 파싱보다 보수적) */\r\nconst MAX_METADATA_FILE_SIZE = 50 * 1024 * 1024\r\n\r\n/** 파일 읽기 + 크기 검증 공통 로직 */\r\nfunction readValidatedFile(filePath: string, maxSize = MAX_FILE_SIZE): { buffer: ArrayBuffer; resolved: string } {\r\n const resolved = safePath(filePath)\r\n let fileSize: number\r\n try {\r\n fileSize = statSync(resolved).size\r\n } catch (err: any) {\r\n throw new KordocError(`파일 상태 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\r\n }\r\n if (fileSize > maxSize) {\r\n throw new KordocError(`파일이 너무 큽니다: ${(fileSize / 1024 / 1024).toFixed(1)}MB (최대 ${maxSize / 1024 / 1024}MB)`)\r\n }\r\n let raw: Buffer\r\n try {\r\n raw = readFileSync(resolved)\r\n } catch (err: any) {\r\n throw new KordocError(`파일 읽기 실패 [${err?.code ?? \"UNKNOWN\"}]: ${resolved}`)\r\n }\r\n return { buffer: toArrayBuffer(raw), resolved }\r\n}\r\n\r\n/** 파일 헤더(16바이트)만 읽어 포맷 감지 — 전체 파일 로드 불필요 */\r\nfunction detectFormatFromHeader(resolved: string): ReturnType<typeof detectFormat> {\r\n const fd = openSync(resolved, \"r\")\r\n try {\r\n const headerBuf = Buffer.alloc(16)\r\n readSync(fd, headerBuf, 0, 16, 0)\r\n return detectFormat(toArrayBuffer(headerBuf))\r\n } finally {\r\n closeSync(fd)\r\n }\r\n}\r\n\r\nconst server = new McpServer({\r\n name: \"kordoc\",\r\n version: VERSION,\r\n})\r\n\r\n// ─── 도구: parse_document ────────────────────────────\r\n\r\nserver.tool(\r\n \"parse_document\",\r\n \"한국 문서 파일(HWP, HWPX, PDF, XLSX, DOCX)을 마크다운으로 변환합니다. 파일 경로를 입력하면 포맷을 자동 감지하여 텍스트를 추출합니다.\",\r\n {\r\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\r\n },\r\n async ({ file_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const format = detectFormat(buffer)\r\n\r\n if (format === \"unknown\") {\r\n return {\r\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n // 이미지는 파일 참조(image_NNN)로 둔다 — MCP 텍스트 응답에 base64 를 인라인해도\r\n // 모델은 data URI 를 이미지로 해석하지 못하고, 사진 한 장(≈100KB → base64 133KB)\r\n // 만으로 클라이언트 도구 응답 한도(Claude Code 기본 25k 토큰)를 넘겨 호출 자체가\r\n // 깨진다(v3.18.0 회귀). 자체 완결형 마크다운이 필요하면 CLI `--inline-images`.\r\n const result = await parse(buffer)\r\n\r\n if (!result.success) {\r\n return {\r\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const markdown = result.markdown\r\n\r\n const meta = [\r\n `포맷: ${result.fileType.toUpperCase()}`,\r\n result.pageCount ? `페이지: ${result.pageCount}` : null,\r\n result.metadata?.title ? `제목: ${result.metadata.title}` : null,\r\n result.metadata?.author ? `작성자: ${result.metadata.author}` : null,\r\n result.isImageBased ? \"이미지 기반 PDF (텍스트 추출 불가)\" : null,\r\n ].filter(Boolean).join(\" | \")\r\n\r\n // outline/warnings 부가 정보 추가\r\n const parts: string[] = [`[${meta}]`]\r\n\r\n if (result.outline && result.outline.length > 0) {\r\n const outlineText = result.outline.map(o => `${\" \".repeat(o.level - 1)}- ${o.text}`).join(\"\\n\")\r\n parts.push(`\\n📑 문서 구조:\\n${outlineText}`)\r\n }\r\n\r\n if (result.warnings && result.warnings.length > 0) {\r\n const warnText = result.warnings.map(w => `- [p${w.page || \"?\"}] ${w.message}`).join(\"\\n\")\r\n parts.push(`\\n⚠️ 경고:\\n${warnText}`)\r\n }\r\n\r\n parts.push(`\\n\\n${markdown}`)\r\n\r\n return {\r\n content: [{ type: \"text\", text: parts.join(\"\") }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: detect_format ─────────────────────────────\r\n\r\nserver.tool(\r\n \"detect_format\",\r\n \"파일의 포맷을 매직 바이트로 감지합니다 (hwpx, hwp, pdf, xlsx, docx, unknown).\",\r\n {\r\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\r\n },\r\n async ({ file_path }) => {\r\n try {\r\n const resolved = safePath(file_path)\r\n let format: string = detectFormatFromHeader(resolved)\r\n // 16바이트 헤더로는 모든 ZIP이 'hwpx'로 나온다 — 파일을 읽어 내부 구조로\r\n // hwpx/xlsx/docx 세분화 (parse_metadata와 판정 일치, v4.0.6)\r\n if (format === \"hwpx\") {\r\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\r\n format = await detectZipFormat(buffer)\r\n }\r\n return {\r\n content: [{ type: \"text\", text: `${file_path}: ${format}` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: parse_metadata ────────────────────────────\r\n\r\nserver.tool(\r\n \"parse_metadata\",\r\n \"문서의 메타데이터(제목, 작성자, 날짜 등)만 빠르게 추출합니다. 전체 파싱 없이 헤더/매니페스트만 읽습니다.\",\r\n {\r\n file_path: z.string().min(1).describe(\"메타데이터를 추출할 문서 파일의 절대 경로\"),\r\n },\r\n async ({ file_path }) => {\r\n try {\r\n const resolved = safePath(file_path)\r\n const format = detectFormatFromHeader(resolved)\r\n\r\n if (format === \"unknown\") {\r\n return {\r\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n // metadata 전용 크기 제한 (50MB)\r\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\r\n\r\n let metadata\r\n // ZIP 기반 포맷(hwpx)은 내부 구조로 세분화 (XLSX/DOCX 구분)\r\n let effectiveFormat = format\r\n if (format === \"hwpx\") {\r\n const { detectZipFormat } = await import(\"./detect.js\")\r\n const zipFormat = await detectZipFormat(buffer)\r\n if (zipFormat === \"xlsx\" || zipFormat === \"docx\") effectiveFormat = zipFormat as any\r\n }\r\n switch (effectiveFormat) {\r\n case \"hwp\":\r\n metadata = extractHwp5MetadataOnly(Buffer.from(buffer))\r\n break\r\n case \"hwpx\":\r\n metadata = await extractHwpxMetadataOnly(buffer)\r\n break\r\n case \"pdf\":\r\n try {\r\n const { extractPdfMetadataOnly } = await import(\"./pdf/parser.js\")\r\n metadata = await extractPdfMetadataOnly(buffer)\r\n } catch {\r\n metadata = undefined // pdfjs-dist 미설치 시 metadata 생략\r\n }\r\n break\r\n case \"xlsx\":\r\n case \"docx\": {\r\n // XLSX/DOCX는 전용 metadata 추출기가 없으므로 전체 파싱 후 metadata 반환\r\n const result = await parse(buffer)\r\n metadata = result.success ? result.metadata : undefined\r\n break\r\n }\r\n }\r\n\r\n return {\r\n content: [{ type: \"text\", text: JSON.stringify({ format, ...metadata }, null, 2) }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: parse_pages ──────────────────────────────\r\n\r\nserver.tool(\r\n \"parse_pages\",\r\n \"문서의 특정 페이지/섹션 범위만 파싱합니다. PDF는 정확한 페이지, HWP/HWPX는 섹션 단위 근사치입니다.\",\r\n {\r\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\r\n pages: z.string().min(1).describe(\"페이지 범위 (예: '1-3', '1,3,5-7')\"),\r\n },\r\n async ({ file_path, pages }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const format = detectFormat(buffer)\r\n\r\n if (format === \"unknown\") {\r\n return {\r\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const result = await parse(buffer, { pages })\r\n\r\n if (!result.success) {\r\n return {\r\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const meta = [\r\n `포맷: ${result.fileType.toUpperCase()}`,\r\n `범위: ${pages}`,\r\n result.pageCount ? `페이지: ${result.pageCount}` : null,\r\n ].filter(Boolean).join(\" | \")\r\n\r\n return {\r\n content: [{ type: \"text\", text: `[${meta}]\\n\\n${result.markdown}` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: parse_table ──────────────────────────────\r\n\r\nserver.tool(\r\n \"parse_table\",\r\n \"문서에서 N번째 테이블만 추출합니다 (0-based index). 테이블이 없거나 인덱스 범위를 초과하면 오류를 반환합니다.\",\r\n {\r\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\r\n table_index: z.number().int().min(0).describe(\"추출할 테이블 인덱스 (0부터 시작)\"),\r\n },\r\n async ({ file_path, table_index }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const format = detectFormat(buffer)\r\n\r\n if (format === \"unknown\") {\r\n return {\r\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const result = await parse(buffer)\r\n\r\n if (!result.success) {\r\n return {\r\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const tableBlocks = result.blocks.filter(b => b.type === \"table\" && b.table)\r\n if (tableBlocks.length === 0) {\r\n return {\r\n content: [{ type: \"text\", text: `문서에 테이블이 없습니다.` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n if (table_index >= tableBlocks.length) {\r\n return {\r\n content: [{ type: \"text\", text: `테이블 인덱스 초과: ${table_index} (총 ${tableBlocks.length}개 테이블)` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const tableBlock = tableBlocks[table_index]\r\n const tableMarkdown = blocksToMarkdown([tableBlock])\r\n\r\n return {\r\n content: [{ type: \"text\", text: `[테이블 #${table_index} / 총 ${tableBlocks.length}개]\\n\\n${tableMarkdown}` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: compare_documents ─────────────────────────\r\n\r\nserver.tool(\r\n \"compare_documents\",\r\n \"두 한국 문서 파일을 비교하여 추가/삭제/변경된 블록을 표시합니다. 신구대조표 생성에 활용됩니다. 크로스 포맷(HWP↔HWPX) 비교 가능.\",\r\n {\r\n file_path_a: z.string().min(1).describe(\"비교 원본 문서의 절대 경로\"),\r\n file_path_b: z.string().min(1).describe(\"비교 대상 문서의 절대 경로\"),\r\n },\r\n async ({ file_path_a, file_path_b }) => {\r\n try {\r\n const { buffer: bufA } = readValidatedFile(file_path_a)\r\n const { buffer: bufB } = readValidatedFile(file_path_b)\r\n\r\n const result = await compare(bufA, bufB)\r\n const { stats, diffs } = result\r\n\r\n const lines: string[] = [\r\n `## 문서 비교 결과`,\r\n `추가: ${stats.added} | 삭제: ${stats.removed} | 변경: ${stats.modified} | 동일: ${stats.unchanged}`,\r\n \"\",\r\n ]\r\n\r\n for (const d of diffs) {\r\n const prefix = d.type === \"added\" ? \"+\" : d.type === \"removed\" ? \"-\" : d.type === \"modified\" ? \"~\" : \" \"\r\n const text = d.after?.text || d.before?.text || (d.after?.table ? \"[테이블]\" : d.before?.table ? \"[테이블]\" : \"\")\r\n const sim = d.similarity !== undefined ? ` (${(d.similarity * 100).toFixed(0)}%)` : \"\"\r\n lines.push(`${prefix} ${text.substring(0, 200)}${sim}`)\r\n }\r\n\r\n return {\r\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: parse_form ───────────────────────────────\r\n\r\nserver.tool(\r\n \"parse_form\",\r\n \"한국 서식 문서에서 레이블-값 쌍을 구조화된 JSON으로 추출합니다. 양식/서식 문서에 최적화.\",\r\n {\r\n file_path: z.string().min(1).describe(\"서식 문서 파일의 절대 경로\"),\r\n },\r\n async ({ file_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const result = await parse(buffer)\r\n\r\n if (!result.success) {\r\n return {\r\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const form = extractFormFields(result.blocks)\r\n return {\r\n content: [{ type: \"text\", text: JSON.stringify(form, null, 2) }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n/** fields + formats 를 FillInput 맵으로 결합 (formats의 라벨은 fields와 동일 표기 기준) */\r\nfunction buildFillInputs(fields: Record<string, string>, formats?: Record<string, string>): Record<string, FillInput> {\r\n const out: Record<string, FillInput> = {}\r\n for (const [k, v] of Object.entries(fields)) {\r\n const format = formats?.[k]\r\n out[k] = format ? { value: v, format } : v\r\n }\r\n return out\r\n}\r\n\r\n// ─── 도구: fill_form ───────────────────────────────\r\n\r\nserver.tool(\r\n \"fill_form\",\r\n \"한국 서식 문서의 빈칸을 채워서 새 문서로 출력합니다. hwpx-preserve를 사용하면 원본 서식(테두리, 폰트, 병합 등)을 100% 유지합니다.\",\r\n {\r\n file_path: z.string().min(1).describe(\"서식 템플릿 문서의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\r\n fields: z.record(z.string(), z.string()).describe(\"채울 필드 맵 (라벨 → 값). 예: {\\\"성명\\\": \\\"홍길동\\\", \\\"전화번호\\\": \\\"010-1234-5678\\\"}\"),\r\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일, ###-####-####)\"),\r\n require_unique: z.boolean().optional().describe(\"한 키가 서식의 2곳 이상에 매칭되면 채우지 않고 거부 — 반복 라벨 양식에서 남의 블록 오염 방지 (배열 값은 예외)\"),\r\n mask_values: z.boolean().optional().describe(\"응답에 값 대신 글자수만 표시 — 개인정보 채움 시 값이 대화 로그에 남지 않게\"),\r\n output_format: z.enum([\"markdown\", \"hwpx\", \"hwpx-preserve\"]).default(\"hwpx-preserve\").describe(\"출력 포맷: hwpx-preserve (원본 스타일 보존, HWPX 전용), hwpx (새 HWPX 생성), markdown\"),\r\n output_path: z.string().optional().describe(\"출력 파일 저장 경로 (선택). 지정 시 파일로 저장, 미지정 시 텍스트로 반환\"),\r\n },\r\n async ({ file_path, fields, formats, require_unique, mask_values, output_format, output_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n\r\n // ─── hwpx-preserve: 원본 ZIP 직접 수정 (스타일 보존) ───\r\n if (output_format === \"hwpx-preserve\") {\r\n const format = detectFormat(buffer)\r\n let isHwpx = format === \"hwpx\"\r\n if (isHwpx) {\r\n const zipFormat = await detectZipFormat(buffer)\r\n isHwpx = zipFormat === \"hwpx\"\r\n }\r\n if (!isHwpx) {\r\n return {\r\n content: [{ type: \"text\", text: `hwpx-preserve는 HWPX 파일만 지원합니다 (감지된 포맷: ${format}). hwpx 또는 markdown을 사용하세요.` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const inputs = buildFillInputs(fields, formats)\r\n const hwpxResult = require_unique\r\n ? await fillWithUniqueGuard(inputs, (vals, blocked) => fillHwpx(buffer, vals, blocked))\r\n : { ...(await fillHwpx(buffer, inputs)), rejected: [] as string[] }\r\n // 마스킹 verify — 채운 결과를 재파싱해 값이 실제 문서에 있는지만 확인 (값 미노출)\r\n let verifyLine: string | null = null\r\n if (mask_values && hwpxResult.filled.length > 0) {\r\n const reparsed = await parse(Buffer.from(hwpxResult.buffer))\r\n // 마크다운 이스케이프(\\*,\\|,\\~ 등)·개행/연속공백 정규화 후 비교 — rrn:masked\r\n // ('900315-1******')의 * 이스케이프로 생기던 결정적 false negative 방지.\r\n // 빈 값은 includes('')===true 로 항상 통과하던 것을 FILLED 에서 제외한다.\r\n const norm = (s: string): string => s.replace(/\\\\([\\\\`*_{}[\\]()#+.!|~>-])/g, \"$1\").replace(/\\s+/g, \" \")\r\n const normMd = reparsed.success ? norm(reparsed.markdown) : \"\"\r\n const okCount = reparsed.success\r\n ? hwpxResult.filled.filter(f => f.value !== \"\" && normMd.includes(norm(f.value))).length\r\n : 0\r\n verifyLine = `검증(마스킹): ${okCount}/${hwpxResult.filled.length} FILLED — 재파싱 대조, 값 미노출`\r\n }\r\n const summary = [\r\n `채워진 필드: ${hwpxResult.filled.length}개 (원본 스타일 보존)`,\r\n hwpxResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${hwpxResult.rejected.join(\", \")}` : null,\r\n hwpxResult.unmatched.length > 0 ? `매칭 실패: ${hwpxResult.unmatched.join(\", \")}` : null,\r\n verifyLine,\r\n ].filter(Boolean).join(\" | \")\r\n\r\n const filledList = hwpxResult.filled\r\n .map(f => ` - ${f.label}: ${mask_values ? `[${[...f.value].length}자]` : f.value}`).join(\"\\n\")\r\n\r\n if (output_path) {\r\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\r\n writeFileSync(resolve(output_path), Buffer.from(hwpxResult.buffer))\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\nHWPX 파일 저장 (원본 서식 유지): ${resolve(output_path)}` }],\r\n }\r\n }\r\n\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\n채워진 필드:\\n${filledList}\\n\\n⚠️ output_path를 지정하면 원본 서식이 유지된 HWPX 파일로 저장됩니다.` }],\r\n }\r\n }\r\n\r\n // ─── 일반 경로: parse → fill → output ───\r\n const result = await parse(buffer)\r\n if (!result.success) {\r\n return {\r\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const formInfo = extractFormFields(result.blocks)\r\n const irInputs = buildFillInputs(fields, formats)\r\n const fillResult = require_unique\r\n ? await fillWithUniqueGuard(irInputs, (vals, blocked) => fillFormFields(result.blocks, vals, blocked))\r\n : { ...fillFormFields(result.blocks, irInputs), rejected: [] as string[] }\r\n\r\n if (fillResult.filled.length === 0 && formInfo.fields.length === 0) {\r\n return {\r\n content: [{ type: \"text\", text: `서식 필드를 찾을 수 없습니다. 일반 문서이거나 서식 패턴이 감지되지 않았습니다.` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const markdown = blocksToMarkdown(fillResult.blocks)\r\n // mask_values 시 채운 값(주민번호·연락처 등)이 응답(대화 로그)에 노출되지 않게\r\n // 본문 미리보기를 안내 문구로 대체 (sfill-8). 값은 output_path 파일에만 기록된다.\r\n const previewMd = mask_values\r\n ? \"⚠️ mask_values 활성 — 개인정보 노출 방지를 위해 본문을 응답에 포함하지 않습니다. output_path 로 파일 저장 후 확인하세요.\"\r\n : markdown\r\n const summary = [\r\n `채워진 필드: ${fillResult.filled.length}개`,\r\n fillResult.rejected.length > 0 ? `모호 라벨 거부(2곳+ 매칭): ${fillResult.rejected.join(\", \")}` : null,\r\n fillResult.unmatched.length > 0 ? `매칭 실패: ${fillResult.unmatched.join(\", \")}` : null,\r\n formInfo.fields.length > 0 ? `서식 필드: ${formInfo.fields.length}개 (확신도 ${(formInfo.confidence * 100).toFixed(0)}%)` : null,\r\n ].filter(Boolean).join(\" | \")\r\n\r\n if (output_format === \"hwpx\") {\r\n const hwpxBuffer = await markdownToHwpx(markdown)\r\n if (output_path) {\r\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\r\n writeFileSync(resolve(output_path), Buffer.from(hwpxBuffer))\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\nHWPX 파일 저장: ${resolve(output_path)}` }],\r\n }\r\n }\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\n⚠️ output_path를 지정하면 HWPX 파일로 저장됩니다. 미리보기:\\n\\n${previewMd}` }],\r\n }\r\n }\r\n\r\n // markdown\r\n if (output_path) {\r\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\r\n writeFileSync(resolve(output_path), markdown, \"utf-8\")\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\n마크다운 파일 저장: ${resolve(output_path)}\\n\\n${previewMd}` }],\r\n }\r\n }\r\n return {\r\n content: [{ type: \"text\", text: `[${summary}]\\n\\n${previewMd}` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: place_seal ─────────────────────────────\r\n\r\nserver.tool(\r\n \"place_seal\",\r\n \"도장/서명 이미지를 앵커 문구(\\\"(인)\\\"·\\\"서명 또는 인\\\" 등) 위에 부유(글 앞) 배치합니다. 표/페이지를 키우지 않습니다 (HWPX 전용).\",\r\n {\r\n file_path: z.string().min(1).describe(\"대상 HWPX 문서의 절대 경로\"),\r\n image_path: z.string().min(1).describe(\"도장/서명 이미지 절대 경로 (투명 배경 PNG 권장)\"),\r\n anchor: z.string().default(\"(인)\").describe(\"앵커 문구 — 이 문구 기준으로 배치\"),\r\n occurrence: z.number().int().min(0).default(0).describe(\"같은 앵커가 여럿일 때 0-based 선택\"),\r\n size_mm: z.number().positive().optional().describe(\"도장 한 변 크기 mm (기본: 줄높이×1.6, 7~18 클램프)\"),\r\n mode: z.enum([\"overlap\", \"right\", \"auto\"]).default(\"auto\").describe(\"overlap=문구 위 겹침, right=문구 오른쪽 옆, auto=공간 있으면 right\"),\r\n dx_mm: z.number().optional().describe(\"x 미세조정 mm\"),\r\n dy_mm: z.number().optional().describe(\"y 미세조정 mm\"),\r\n output_path: z.string().min(1).describe(\"출력 HWPX 저장 경로\"),\r\n },\r\n async ({ file_path, image_path, anchor, occurrence, size_mm, mode, dx_mm, dy_mm, output_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const format = detectFormat(buffer)\r\n if (format !== \"hwpx\") {\r\n return {\r\n content: [{ type: \"text\", text: `place_seal 은 HWPX 파일만 지원합니다 (감지된 포맷: ${format}).` }],\r\n isError: true,\r\n }\r\n }\r\n const imgResolved = resolve(image_path)\r\n if (statSync(imgResolved).size > 500 * 1024 * 1024) {\r\n return { content: [{ type: \"text\", text: `도장 이미지가 너무 큽니다 (${(statSync(imgResolved).size / 1024 / 1024).toFixed(0)}MB) — 500MB 이하여야 합니다.` }], isError: true }\r\n }\r\n const image = new Uint8Array(readFileSync(imgResolved))\r\n const ext = (extname(image_path).slice(1).toLowerCase() || \"png\") as \"png\" | \"jpg\" | \"jpeg\" | \"bmp\" | \"gif\"\r\n const { placeSealHwpx } = await import(\"./form/seal.js\")\r\n const result = await placeSealHwpx(buffer, [{\r\n anchor, occurrence, image, ext,\r\n sizeMm: size_mm, mode, dxMm: dx_mm, dyMm: dy_mm,\r\n }])\r\n mkdirSync(dirname(resolve(output_path)), { recursive: true })\r\n writeFileSync(resolve(output_path), Buffer.from(result.buffer))\r\n const p0 = result.placed[0]\r\n const warnLines = (p0.warnings ?? []).map(w => `\\n⚠️ ${w}`).join(\"\")\r\n return {\r\n content: [{\r\n type: \"text\",\r\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 로 미세조정 가능합니다.`,\r\n }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `도장 배치 실패: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n },\r\n)\r\n\r\n// ─── 도구: patch_document ────────────────────────────\r\n\r\nserver.tool(\r\n \"patch_document\",\r\n \"원본 HWPX/HWP의 서식(글꼴·표·도장칸·이미지)을 1바이트도 건드리지 않고, 편집된 마크다운의 바뀐 텍스트만 제자리 치환해 새 문서로 출력합니다. parse_document로 얻은 마크다운을 수정해 넘기세요 — 양식 빈칸 채우기·문구 수정에 적합하며 한컴 한글에서 변조 경고 없이 열립니다. (블록 추가/삭제·표 구조 변경은 미지원, 미적용 항목은 결과에 보고)\",\r\n {\r\n file_path: z.string().min(1).describe(\"원본 문서의 절대 경로 (HWPX 또는 HWP 5.x)\"),\r\n edited_markdown: z.string().min(1).describe(\"parse_document 출력 마크다운을 편집한 전체 마크다운. 바뀐 문단/셀 텍스트만 반영하고 블록 수·순서는 원본과 같게 유지하세요\"),\r\n output_path: z.string().min(1).describe(\"출력 파일 저장 절대 경로 (원본과 같은 확장자: .hwpx 또는 .hwp)\"),\r\n },\r\n async ({ file_path, edited_markdown, output_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(file_path)\r\n const format = detectFormat(buffer)\r\n let isHwpx = format === \"hwpx\"\r\n if (isHwpx) {\r\n const zipFormat = await detectZipFormat(buffer)\r\n isHwpx = zipFormat === \"hwpx\"\r\n }\r\n if (!isHwpx && format !== \"hwp\") {\r\n return {\r\n content: [{ type: \"text\", text: `patch_document는 HWPX 또는 HWP 5.x만 지원합니다 (감지된 포맷: ${format}).` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const original = new Uint8Array(buffer)\r\n const result = isHwpx\r\n ? await patchHwpx(original, edited_markdown)\r\n : await patchHwp(original, edited_markdown)\r\n\r\n if (!result.success || !result.data) {\r\n return {\r\n content: [{ type: \"text\", text: `패치 실패: ${result.error ?? \"알 수 없는 오류\"}` }],\r\n isError: true,\r\n }\r\n }\r\n\r\n const out = resolve(output_path)\r\n mkdirSync(dirname(out), { recursive: true })\r\n writeFileSync(out, Buffer.from(result.data))\r\n\r\n const v = result.verification?.stats\r\n const lossless = v ? (v.modified === 0 && v.added === 0 && v.removed === 0) : undefined\r\n const lines = [\r\n `✓ ${result.applied}개 변경 적용 (${isHwpx ? \"HWPX\" : \"HWP\"}, 원본 서식 보존) → ${out}`,\r\n lossless === true ? \"검증: 편집 내용과 재파싱 결과 완전 일치\" :\r\n lossless === false ? `검증 잔차: 수정 ${v!.modified} · 추가 ${v!.added} · 삭제 ${v!.removed} (반영 안 된 편집 있음)` : null,\r\n result.skipped.length > 0\r\n ? `미적용 ${result.skipped.length}건:\\n` + result.skipped.map(s => ` - ${s.reason}`).join(\"\\n\")\r\n : null,\r\n ].filter(Boolean)\r\n\r\n return {\r\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 도구: generate_document ─────────────────────────\r\n\r\n// ─── 도구: extract_profile ───────────────────────────\r\n\r\nserver.tool(\r\n \"extract_profile\",\r\n \"참조 HWPX 문서에서 표 서식 프로필(테두리·음영·열 너비·셀 글꼴)을 JSON으로 추출합니다. 추출한 프로필을 generate_document의 profile_path로 넘기면 원본 문서 없이 같은 표 서식을 재현합니다 — \\\"이 문서 표 서식 그대로 만들어줘\\\" 워크플로의 1단계.\",\r\n {\r\n hwpx_path: z.string().min(1).describe(\"서식을 추출할 참조 HWPX 파일의 절대 경로\"),\r\n output_path: z.string().min(1).describe(\"프로필 JSON 출력 절대 경로 (.json 권장)\"),\r\n },\r\n async ({ hwpx_path, output_path }) => {\r\n try {\r\n const { buffer } = readValidatedFile(hwpx_path)\r\n const { hwpxToProfile } = await import(\"./index.js\")\r\n const profile = await hwpxToProfile(buffer)\r\n const out = resolve(output_path)\r\n mkdirSync(dirname(out), { recursive: true })\r\n writeFileSync(out, JSON.stringify(profile, null, 2))\r\n return {\r\n content: [{ type: \"text\", text: `서식 프로필 추출 완료: 표 ${profile.tables.length}개 → ${out}\\n(generate_document의 profile_path로 사용)` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\nserver.tool(\r\n \"generate_document\",\r\n \"마크다운을 HWPX 한글 문서로 생성합니다. \\\"보고서로/공문서로/개조식으로/계획서로 뽑아줘·만들어줘\\\" 요청이 이 도구입니다. 프리셋 매핑: 정부 표준 보고서(표지·목차·로마숫자 장헤더 자동)='개조식', 기안문·시행문·알림공문='기안문', 1페이지 요약보고서='보고서', 추진계획='계획서'. 표는 실측 정부 서식(헤더 음영+이중선·외곽 굵은선·내용 비례 열폭), 쪽번호·결재란·'끝.' 표시 지원. ⚠ 생성 전 확인 권장: 문서종류(보고서/기안문)·제목·기관명(org)·날짜·목차 여부가 불명확하면 사용자에게 물어보세요 — 엉뚱한 프리셋 선택이 가장 흔한 오생성 원인. 마크다운 규칙: #(h1)=문서 제목(표지), ##(h2)=장(Ⅰ Ⅱ Ⅲ 자동), 리스트 깊이=□ ○ - ㆍ 부호, ※시작 문단=참고 스타일, <right>텍스트</right>=우측정렬 출처행. (원본 서식 보존 제자리 수정은 patch_document, 서식 빈칸 채우기는 fill_form)\",\r\n {\r\n markdown: z.string().min(1).describe(\"HWPX로 변환할 마크다운 전문. 표는 GFM 문법 사용 (예: '| 이름 | 부서 |\\\\n| --- | --- |\\\\n| 홍길동 | 기획팀 |')\"),\r\n output_path: z.string().min(1).describe(\"출력 HWPX 파일의 절대 경로 (.hwpx 권장)\"),\r\n profile_path: z.string().optional().describe(\"서식 프로필 JSON 경로 (extract_profile로 추출) — 참조 문서의 표 테두리·음영·열폭·셀 글꼴을 재현. 표 행·열 수와 첫 셀 텍스트가 일치하는 표에만 적용\"),\r\n // 값 집합·범위는 gongmun-surface SSOT에서 파생 (CLI와 드리프트 불가 — v4.0.4 영역1-1)\r\n preset: z.enum(Object.keys(PRESET_ALIAS) as [string, ...string[]]).optional()\r\n .describe(\"공문서 프리셋 — 지정 시 한국 행정 공문서 표준 서식 적용. '개조식'=정부 표준 개조식 보고서(표지·목차·로마숫자 장 헤더 자동 + □○-※ 부호별 폰트), '보도자료'=머리박스+제목 25pt+□→ㅇ→*(각주) 체계. 미지정 시 범용 마크다운 변환\"),\r\n font: z.enum(BODY_FONTS).optional().describe(\"본문 글꼴(공문서 모드): myeongjo=명조 계열(개조식·보고서·계획서는 실측 휴먼명조, 그 외 함초롬바탕), gothic=맑은 고딕\"),\r\n body_pt: z.number().int().min(BODY_PT_RANGE.min).max(BODY_PT_RANGE.max).optional().describe(\"본문 글자 크기(pt, 공문서 모드). 기본: 기안문 12, 보고서·계획서·통지 15\"),\r\n line_spacing: z.number().int().min(LINE_SPACING_RANGE.min).max(LINE_SPACING_RANGE.max).optional().describe(\"본문 줄간격(%, 공문서 모드). 기본: 프리셋별 실측값(기안문 160, 회의록 130 등)\"),\r\n org: z.string().optional().describe(\"표지 기관명 (표지를 켜는 모든 프리셋에서 사용 가능). 미지정 시 표지에 기관명 생략\"),\r\n date: z.string().optional().describe(\"표지 날짜 ('YYYY. M. D.' 표기 권장). 미지정 시 오늘 날짜\"),\r\n toc: z.boolean().optional().describe(\"목차 페이지 생성 여부 — h2 목록을 Ⅰ Ⅱ Ⅲ 장으로 자동 구성. 전 프리셋 사용 가능(보도자료 제외). 미지정 시 개조식 프리셋만 켜짐\"),\r\n cover: z.boolean().optional().describe(\"표지 페이지 생성 여부 — 첫 h1을 제목으로 파랑 장식 표지. 전 프리셋 사용 가능(보도자료 제외 — 머리박스 서식과 양립 불가). 미지정 시 개조식 프리셋만 켜짐 (org/date 지정 시 자동 켜짐)\"),\r\n approval: z.array(z.string()).max(APPROVAL_MAX).optional().describe(\"결재란 직위 라벨 (최대 6개, 예: ['담당','팀장','과장']) — 문서 최상단 우측에 서명 공란 결재 표 생성\"),\r\n page_numbers: z.boolean().optional().describe(\"쪽번호(하단 중앙 '- 1 -', 표지·목차 카운트 제외). 미지정 시 개조식·보고서·계획서 켜짐\"),\r\n end_mark: z.boolean().optional().describe(\"본문 끝 '끝.' 표시 (행정업무규정). 미지정 시 기안문만 켜짐, 본문이 이미 '끝.'으로 끝나면 중복 생성 안 함\"),\r\n body_title_box: z.boolean().optional().describe(\"본문 첫 페이지 제목 반복 박스 (개조식 실측 관행). 미지정 시 개조식+표지 조합에서 켜짐\"),\r\n h2_marker: z.enum(H2_MARKERS).optional().describe(\"h2 섹션 제목 말머리(비개조식): box='□ 제목'(실측 보고서 관행), number='1. 제목'(공고문 관행), none=말머리 없음. 미지정 시 보고서·계획서 box, 그 외 none\"),\r\n fonts: z.object(Object.fromEntries(FONT_ROLE_KEYS.map(k => [k, z.string().optional()])))\r\n .optional().describe(\"요소별 글꼴 오버라이드(공문서 모드) — body=본문(○·-)/heading=제목 계열(□·장헤더·표지·목차)/ref=※ 참고/table=표 셀. 개조식·보고서·계획서는 네 역할 전부, 그 외 프리셋은 body만 적용\"),\r\n sizes: z.object(Object.fromEntries(SIZE_KEYS.map(k => [k, z.number().min(SIZE_PT_RANGE.min).max(SIZE_PT_RANGE.max).optional()])))\r\n .optional().describe(\"개조식 요소별 글자 크기(pt) 오버라이드 — dae=□/cham=※/chapter=장헤더/coverTitle·coverSub=표지/tocLabel·tocRoman·tocItem=목차/table=표 셀/bodyTitle=본문 첫 페이지 제목 반복 박스. 미지정 요소는 body_pt 비례 기본값\"),\r\n bullet2: z.enum(BULLET2_CHARS).optional().describe(\"2단계 항목부호 — 'ㅇ'(이응, 전자결재 기안문·공고문 실측 지배) / '○'(원, 보고서 양식). 미지정 시 통지·보도자료 ㅇ, 그 외 ○\"),\r\n suppress_single: z.boolean().optional().describe(\"단일 형제 항목 부호 생략(편람 규정, 법정 번호 standard 전용 — 불릿 체계인 보고서·계획서·개조식·보도자료엔 무효). 기본 false — 하나뿐인 항목에도 부호(1. 가.)를 부여 (부호 없는 계단 들여쓰기가 실무 눈에 어색)\"),\r\n doc_head: z.object(Object.fromEntries(DOC_HEAD_KEYS.map(k => [k, z.string().optional()])))\r\n .optional().describe(\"기안문 두문(별지 제1호서식) — org=행정기관명(18pt bold 중앙)/to=수신/via=경유/title=제목. 기안문 프리셋 전용\"),\r\n doc_foot: z.object(Object.fromEntries(DOC_FOOT_KEYS.map(k => [k, z.string().optional()])))\r\n .optional().describe(\"기안문 결문 — sender=발신명의(22pt 중앙)/drafter·reviewer·approver=기안·검토·결재/docNum=시행/receive=접수/disclosure=공개구분 등. 기안문 프리셋 전용\"),\r\n report_info: z.string().optional().describe(\"업무보고 우상단 보고정보 행 — 예: '(2026. 7. 11., 과장 홍길동, ☎02-120)' (실측: 12pt 우측정렬)\"),\r\n notice_head: z.object(Object.fromEntries(NOTICE_HEAD_KEYS.map(k => [k, z.string().optional()])))\r\n .optional().describe(\"공고문 두문·결문 — no=공고번호(본문 위 bold)/date=날짜(본문 아래 우측)/sender=발신명의(우측 bold). 통지 프리셋 전용\"),\r\n press: z.object({\r\n release: z.string().optional(), distribute: z.string().optional(),\r\n sub: z.array(z.string()).optional(),\r\n contact: z.object(Object.fromEntries(PRESS_CONTACT_KEYS.map(k => [k, z.string().optional()]))).optional(),\r\n }).optional().describe(\"보도자료 옵션 — release=보도시점/distribute=배포일(머리박스)/sub=부제 배열('- … -')/contact=담당 부서·담당자·연락처 표\"),\r\n },\r\n async ({ markdown, output_path, profile_path, preset, font, body_pt, line_spacing, org, date, toc, cover, approval, page_numbers, end_mark, body_title_box, h2_marker, fonts, sizes, bullet2, suppress_single, doc_head, doc_foot, report_info, notice_head, press }) => {\r\n try {\r\n // 조립은 gongmun-surface SSOT(buildGongmunOptions) — CLI와 의미론 공유 (v4.0.4)\r\n let gongmun: GongmunOptions | undefined\r\n if (preset) {\r\n gongmun = buildGongmunOptions({\r\n preset: PRESET_ALIAS[preset], font, bodyPt: body_pt, lineSpacing: line_spacing,\r\n org, date, cover, toc, approval,\r\n pageNumbers: page_numbers, endMark: end_mark, bodyTitleBox: body_title_box,\r\n h2Marker: h2_marker, fonts, sizes, bullet2, suppressSingle: suppress_single,\r\n docHead: doc_head, docFoot: doc_foot, reportInfo: report_info,\r\n noticeHead: notice_head, press,\r\n })\r\n }\r\n // 서식 프로필 (이슈 #41) — 경계 zod 검증 (CLI --profile과 공유 스키마)\r\n let profile: import(\"./hwpx/gen-profile.js\").FormatProfile | undefined\r\n if (profile_path) {\r\n const { parseFormatProfileJson } = await import(\"./hwpx/profile-io.js\")\r\n profile = parseFormatProfileJson(readFileSync(resolve(profile_path), \"utf-8\"))\r\n }\r\n const buf = await markdownToHwpx(markdown, gongmun || profile ? { ...(gongmun ? { gongmun } : {}), ...(profile ? { profile } : {}) } : undefined)\r\n const out = resolve(output_path)\r\n mkdirSync(dirname(out), { recursive: true })\r\n writeFileSync(out, Buffer.from(buf))\r\n\r\n const mode = gongmun ? `공문서:${gongmun.preset}` : \"범용\"\r\n const tableCount = (markdown.match(/^\\s*\\|.*\\|\\s*$/gm) || []).length > 0\r\n ? `, 표 포함` : \"\"\r\n // 폰트 오버라이드 오타·미설치 경고 (A2) — 생성은 진행, 경고만 병기.\r\n // 프리셋 비호환 옵션(조용한 폐기)·편람 표기법 검수도 같은 채널로 병기\r\n const fontWarns = gongmun?.fonts ? unknownFontWarnings(gongmun.fonts) : []\r\n if (gongmun) fontWarns.push(...incompatibleGongmunWarnings(gongmun))\r\n if (gongmun) fontWarns.push(...gongmunLintWarnings(markdown, 5))\r\n const warnText = fontWarns.length ? `\\n⚠ ${fontWarns.join(\"\\n⚠ \")}` : \"\"\r\n return {\r\n content: [{ type: \"text\", text: `✓ HWPX 생성 (${mode}${tableCount}) → ${out}\\n크기: ${(buf.byteLength / 1024).toFixed(1)}KB${warnText}` }],\r\n }\r\n } catch (err) {\r\n return {\r\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\r\n isError: true,\r\n }\r\n }\r\n }\r\n)\r\n\r\n// ─── 서버 시작 ───────────────────────────────────────\r\n\r\nasync function main() {\r\n const transport = new StdioServerTransport()\r\n await server.connect(transport)\r\n}\r\n\r\nmain().catch((err) => { console.error(err); process.exit(1) })\r\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, unknownFontWarnings, incompatibleGongmunWarnings, gongmunLintWarnings, PRESET_ALIAS } from \"./index.js\"\nimport { fillWithUniqueGuard, type FillInput } from \"./form/match.js\"\nimport type { GongmunOptions } from \"./index.js\"\nimport {\n buildGongmunOptions, BODY_FONTS, H2_MARKERS, BULLET2_CHARS,\n FONT_ROLE_KEYS, SIZE_KEYS, DOC_HEAD_KEYS, DOC_FOOT_KEYS, NOTICE_HEAD_KEYS, PRESS_CONTACT_KEYS,\n BODY_PT_RANGE, LINE_SPACING_RANGE, SIZE_PT_RANGE, APPROVAL_MAX,\n} from \"./hwpx/gongmun-surface.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 // 이미지는 파일 참조(image_NNN)로 둔다 — MCP 텍스트 응답에 base64 를 인라인해도\n // 모델은 data URI 를 이미지로 해석하지 못하고, 사진 한 장(≈100KB → base64 133KB)\n // 만으로 클라이언트 도구 응답 한도(Claude Code 기본 25k 토큰)를 넘겨 호출 자체가\n // 깨진다(v3.18.0 회귀). 자체 완결형 마크다운이 필요하면 CLI `--inline-images`.\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 markdown = result.markdown\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\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, xlsx, docx, unknown).\",\n {\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n let format: string = detectFormatFromHeader(resolved)\n // 16바이트 헤더로는 모든 ZIP이 'hwpx'로 나온다 — 파일을 읽어 내부 구조로\n // hwpx/xlsx/docx 세분화 (parse_metadata와 판정 일치, v4.0.6)\n if (format === \"hwpx\") {\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\n format = await detectZipFormat(buffer)\n }\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\n// ─── 도구: extract_profile ───────────────────────────\n\nserver.tool(\n \"extract_profile\",\n \"참조 HWPX 문서에서 표 서식 프로필(테두리·음영·열 너비·셀 글꼴)을 JSON으로 추출합니다. 추출한 프로필을 generate_document의 profile_path로 넘기면 원본 문서 없이 같은 표 서식을 재현합니다 — \\\"이 문서 표 서식 그대로 만들어줘\\\" 워크플로의 1단계.\",\n {\n hwpx_path: z.string().min(1).describe(\"서식을 추출할 참조 HWPX 파일의 절대 경로\"),\n output_path: z.string().min(1).describe(\"프로필 JSON 출력 절대 경로 (.json 권장)\"),\n },\n async ({ hwpx_path, output_path }) => {\n try {\n const { buffer } = readValidatedFile(hwpx_path)\n const { hwpxToProfile } = await import(\"./index.js\")\n const profile = await hwpxToProfile(buffer)\n const out = resolve(output_path)\n mkdirSync(dirname(out), { recursive: true })\n writeFileSync(out, JSON.stringify(profile, null, 2))\n return {\n content: [{ type: \"text\", text: `서식 프로필 추출 완료: 표 ${profile.tables.length}개 → ${out}\\n(generate_document의 profile_path로 사용)` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\nserver.tool(\n \"generate_document\",\n \"마크다운을 HWPX 한글 문서로 생성합니다. \\\"보고서로/공문서로/개조식으로/계획서로 뽑아줘·만들어줘\\\" 요청이 이 도구입니다. 프리셋 매핑: 정부 표준 보고서(표지·목차·로마숫자 장헤더 자동)='개조식', 기안문·시행문·알림공문='기안문', 1페이지 요약보고서='보고서', 추진계획='계획서'. 표는 실측 정부 서식(헤더 음영+이중선·외곽 굵은선·내용 비례 열폭), 쪽번호·결재란·'끝.' 표시 지원. ⚠ 생성 전 확인 권장: 문서종류(보고서/기안문)·제목·기관명(org)·날짜·목차 여부가 불명확하면 사용자에게 물어보세요 — 엉뚱한 프리셋 선택이 가장 흔한 오생성 원인. 마크다운 규칙: #(h1)=문서 제목(표지), ##(h2)=장(Ⅰ Ⅱ Ⅲ 자동), 리스트 깊이=□ ○ - ㆍ 부호, ※시작 문단=참고 스타일, <right>텍스트</right>=우측정렬 출처행. (원본 서식 보존 제자리 수정은 patch_document, 서식 빈칸 채우기는 fill_form)\",\n {\n markdown: z.string().min(1).describe(\"HWPX로 변환할 마크다운 전문. 표는 GFM 문법 사용 (예: '| 이름 | 부서 |\\\\n| --- | --- |\\\\n| 홍길동 | 기획팀 |')\"),\n output_path: z.string().min(1).describe(\"출력 HWPX 파일의 절대 경로 (.hwpx 권장)\"),\n profile_path: z.string().optional().describe(\"서식 프로필 JSON 경로 (extract_profile로 추출) — 참조 문서의 표 테두리·음영·열폭·셀 글꼴을 재현. 표 행·열 수와 첫 셀 텍스트가 일치하는 표에만 적용\"),\n // 값 집합·범위는 gongmun-surface SSOT에서 파생 (CLI와 드리프트 불가 — v4.0.4 영역1-1)\n preset: z.enum(Object.keys(PRESET_ALIAS) as [string, ...string[]]).optional()\n .describe(\"공문서 프리셋 — 지정 시 한국 행정 공문서 표준 서식 적용. '개조식'=정부 표준 개조식 보고서(표지·목차·로마숫자 장 헤더 자동 + □○-※ 부호별 폰트), '보도자료'=머리박스+제목 25pt+□→ㅇ→*(각주) 체계. 미지정 시 범용 마크다운 변환\"),\n font: z.enum(BODY_FONTS).optional().describe(\"본문 글꼴(공문서 모드): myeongjo=명조 계열(개조식·보고서·계획서는 실측 휴먼명조, 그 외 함초롬바탕), gothic=맑은 고딕\"),\n body_pt: z.number().int().min(BODY_PT_RANGE.min).max(BODY_PT_RANGE.max).optional().describe(\"본문 글자 크기(pt, 공문서 모드). 기본: 기안문 12, 보고서·계획서·통지 15\"),\n line_spacing: z.number().int().min(LINE_SPACING_RANGE.min).max(LINE_SPACING_RANGE.max).optional().describe(\"본문 줄간격(%, 공문서 모드). 기본: 프리셋별 실측값(기안문 160, 회의록 130 등)\"),\n org: z.string().optional().describe(\"표지 기관명 (표지를 켜는 모든 프리셋에서 사용 가능). 미지정 시 표지에 기관명 생략\"),\n date: z.string().optional().describe(\"표지 날짜 ('YYYY. M. D.' 표기 권장). 미지정 시 오늘 날짜\"),\n toc: z.boolean().optional().describe(\"목차 페이지 생성 여부 — h2 목록을 Ⅰ Ⅱ Ⅲ 장으로 자동 구성. 전 프리셋 사용 가능(보도자료 제외). 미지정 시 개조식 프리셋만 켜짐\"),\n cover: z.boolean().optional().describe(\"표지 페이지 생성 여부 — 첫 h1을 제목으로 파랑 장식 표지. 전 프리셋 사용 가능(보도자료 제외 — 머리박스 서식과 양립 불가). 미지정 시 개조식 프리셋만 켜짐 (org/date 지정 시 자동 켜짐)\"),\n approval: z.array(z.string()).max(APPROVAL_MAX).optional().describe(\"결재란 직위 라벨 (최대 6개, 예: ['담당','팀장','과장']) — 문서 최상단 우측에 서명 공란 결재 표 생성\"),\n page_numbers: z.boolean().optional().describe(\"쪽번호(하단 중앙 '- 1 -', 표지·목차 카운트 제외). 미지정 시 개조식·보고서·계획서 켜짐\"),\n end_mark: z.boolean().optional().describe(\"본문 끝 '끝.' 표시 (행정업무규정). 미지정 시 기안문만 켜짐, 본문이 이미 '끝.'으로 끝나면 중복 생성 안 함\"),\n body_title_box: z.boolean().optional().describe(\"본문 첫 페이지 제목 반복 박스 (개조식 실측 관행). 미지정 시 개조식+표지 조합에서 켜짐\"),\n h2_marker: z.enum(H2_MARKERS).optional().describe(\"h2 섹션 제목 말머리(비개조식): box='□ 제목'(실측 보고서 관행), number='1. 제목'(공고문 관행), none=말머리 없음. 미지정 시 보고서·계획서 box, 그 외 none\"),\n fonts: z.object(Object.fromEntries(FONT_ROLE_KEYS.map(k => [k, z.string().optional()])))\n .optional().describe(\"요소별 글꼴 오버라이드(공문서 모드) — body=본문(○·-)/heading=제목 계열(□·장헤더·표지·목차)/ref=※ 참고/table=표 셀. 개조식·보고서·계획서는 네 역할 전부, 그 외 프리셋은 body만 적용\"),\n sizes: z.object(Object.fromEntries(SIZE_KEYS.map(k => [k, z.number().min(SIZE_PT_RANGE.min).max(SIZE_PT_RANGE.max).optional()])))\n .optional().describe(\"개조식 요소별 글자 크기(pt) 오버라이드 — dae=□/cham=※/chapter=장헤더/coverTitle·coverSub=표지/tocLabel·tocRoman·tocItem=목차/table=표 셀/bodyTitle=본문 첫 페이지 제목 반복 박스. 미지정 요소는 body_pt 비례 기본값\"),\n bullet2: z.enum(BULLET2_CHARS).optional().describe(\"2단계 항목부호 — 'ㅇ'(이응, 전자결재 기안문·공고문 실측 지배) / '○'(원, 보고서 양식). 미지정 시 통지·보도자료 ㅇ, 그 외 ○\"),\n suppress_single: z.boolean().optional().describe(\"단일 형제 항목 부호 생략(편람 규정, 법정 번호 standard 전용 — 불릿 체계인 보고서·계획서·개조식·보도자료엔 무효). 기본 false — 하나뿐인 항목에도 부호(1. 가.)를 부여 (부호 없는 계단 들여쓰기가 실무 눈에 어색)\"),\n doc_head: z.object(Object.fromEntries(DOC_HEAD_KEYS.map(k => [k, z.string().optional()])))\n .optional().describe(\"기안문 두문(별지 제1호서식) — org=행정기관명(18pt bold 중앙)/to=수신/via=경유/title=제목. 기안문 프리셋 전용\"),\n doc_foot: z.object(Object.fromEntries(DOC_FOOT_KEYS.map(k => [k, z.string().optional()])))\n .optional().describe(\"기안문 결문 — sender=발신명의(22pt 중앙)/drafter·reviewer·approver=기안·검토·결재/docNum=시행/receive=접수/disclosure=공개구분 등. 기안문 프리셋 전용\"),\n report_info: z.string().optional().describe(\"업무보고 우상단 보고정보 행 — 예: '(2026. 7. 11., 과장 홍길동, ☎02-120)' (실측: 12pt 우측정렬)\"),\n notice_head: z.object(Object.fromEntries(NOTICE_HEAD_KEYS.map(k => [k, z.string().optional()])))\n .optional().describe(\"공고문 두문·결문 — no=공고번호(본문 위 bold)/date=날짜(본문 아래 우측)/sender=발신명의(우측 bold). 통지 프리셋 전용\"),\n press: z.object({\n release: z.string().optional(), distribute: z.string().optional(),\n sub: z.array(z.string()).optional(),\n contact: z.object(Object.fromEntries(PRESS_CONTACT_KEYS.map(k => [k, z.string().optional()]))).optional(),\n }).optional().describe(\"보도자료 옵션 — release=보도시점/distribute=배포일(머리박스)/sub=부제 배열('- … -')/contact=담당 부서·담당자·연락처 표\"),\n },\n async ({ markdown, output_path, profile_path, preset, font, body_pt, line_spacing, org, date, toc, cover, approval, page_numbers, end_mark, body_title_box, h2_marker, fonts, sizes, bullet2, suppress_single, doc_head, doc_foot, report_info, notice_head, press }) => {\n try {\n // 조립은 gongmun-surface SSOT(buildGongmunOptions) — CLI와 의미론 공유 (v4.0.4)\n let gongmun: GongmunOptions | undefined\n if (preset) {\n gongmun = buildGongmunOptions({\n preset: PRESET_ALIAS[preset], font, bodyPt: body_pt, lineSpacing: line_spacing,\n org, date, cover, toc, approval,\n pageNumbers: page_numbers, endMark: end_mark, bodyTitleBox: body_title_box,\n h2Marker: h2_marker, fonts, sizes, bullet2, suppressSingle: suppress_single,\n docHead: doc_head, docFoot: doc_foot, reportInfo: report_info,\n noticeHead: notice_head, press,\n })\n }\n // 서식 프로필 (이슈 #41) — 경계 zod 검증 (CLI --profile과 공유 스키마)\n let profile: import(\"./hwpx/gen-profile.js\").FormatProfile | undefined\n if (profile_path) {\n const { parseFormatProfileJson } = await import(\"./hwpx/profile-io.js\")\n profile = parseFormatProfileJson(readFileSync(resolve(profile_path), \"utf-8\"))\n }\n const buf = await markdownToHwpx(markdown, gongmun || profile ? { ...(gongmun ? { gongmun } : {}), ...(profile ? { profile } : {}) } : 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 // 폰트 오버라이드 오타·미설치 경고 (A2) — 생성은 진행, 경고만 병기.\n // 프리셋 비호환 옵션(조용한 폐기)·편람 표기법 검수도 같은 채널로 병기\n const fontWarns = gongmun?.fonts ? unknownFontWarnings(gongmun.fonts) : []\n if (gongmun) fontWarns.push(...incompatibleGongmunWarnings(gongmun))\n if (gongmun) fontWarns.push(...gongmunLintWarnings(markdown, 5))\n const warnText = fontWarns.length ? `\\n⚠ ${fontWarns.join(\"\\n⚠ \")}` : \"\"\n return {\n content: [{ type: \"text\", text: `✓ HWPX 생성 (${mode}${tableCount}) → ${out}\\n크기: ${(buf.byteLength / 1024).toFixed(1)}KB${warnText}` }],\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"]}
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parsePageRange
4
+ } from "./chunk-MOL7MDBG.js";
5
+ export {
6
+ parsePageRange
7
+ };
8
+ //# sourceMappingURL=page-range-737B4EZW.js.map
@@ -0,0 +1,7 @@
1
+ import {
2
+ parsePageRange
3
+ } from "./chunk-GE43BE46.js";
4
+ export {
5
+ parsePageRange
6
+ };
7
+ //# sourceMappingURL=page-range-LNMXJU6W.js.map
@@ -0,0 +1,8 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunkDCZVOIEOcjs = require('./chunk-DCZVOIEO.cjs');
4
+ require('./chunk-GS7T56RP.cjs');
5
+
6
+
7
+ exports.parsePageRange = _chunkDCZVOIEOcjs.parsePageRange;
8
+ //# sourceMappingURL=page-range-P7SDW6LR.cjs.map
@@ -0,0 +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"}