@clazic/kordoc 2.5.0 → 2.5.2

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 (44) hide show
  1. package/dist/batch-provider-XRF6F26E.js +234 -0
  2. package/dist/batch-provider-XRF6F26E.js.map +1 -0
  3. package/dist/{chunk-NKUNXGWI.js → chunk-25ZYYLVP.js} +8 -6
  4. package/dist/chunk-25ZYYLVP.js.map +1 -0
  5. package/dist/{chunk-IJGNPAK2.js → chunk-5CILZHRW.js} +2 -2
  6. package/dist/chunk-S7BHLD2V.js +200 -0
  7. package/dist/{chunk-Y4WFKJ5P.js.map → chunk-S7BHLD2V.js.map} +1 -1
  8. package/dist/cli.js +6 -38
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index.cjs +473 -2816
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +4 -1
  13. package/dist/index.d.ts +4 -1
  14. package/dist/index.js +482 -2826
  15. package/dist/index.js.map +1 -1
  16. package/dist/mcp.js +3 -6
  17. package/dist/mcp.js.map +1 -1
  18. package/dist/{resolve-XWYJYKKH.js → resolve-ZSUEJK3E.js} +4 -4
  19. package/dist/{utils-RBXHHCLI.js → utils-H2BL5GNR.js} +2 -2
  20. package/dist/{watch-FRLS6FKE.js → watch-D6ODQLPJ.js} +4 -7
  21. package/dist/{watch-FRLS6FKE.js.map → watch-D6ODQLPJ.js.map} +1 -1
  22. package/package.json +3 -7
  23. package/dist/batch-provider-5BFJRKAZ.js +0 -190
  24. package/dist/batch-provider-5BFJRKAZ.js.map +0 -1
  25. package/dist/chunk-AEWWERJ5.js +0 -35
  26. package/dist/chunk-AEWWERJ5.js.map +0 -1
  27. package/dist/chunk-CPTOBJJD.js +0 -125
  28. package/dist/chunk-CPTOBJJD.js.map +0 -1
  29. package/dist/chunk-NKUNXGWI.js.map +0 -1
  30. package/dist/chunk-THBLCND6.js +0 -33
  31. package/dist/chunk-THBLCND6.js.map +0 -1
  32. package/dist/chunk-Y4WFKJ5P.js +0 -167
  33. package/dist/doctor-SJ7NYSXC.js +0 -126
  34. package/dist/doctor-SJ7NYSXC.js.map +0 -1
  35. package/dist/install-commands-P2KTEXQ4.js +0 -11
  36. package/dist/pm-7KGLH6MX.js +0 -9
  37. package/dist/pm-7KGLH6MX.js.map +0 -1
  38. package/dist/setup/doctor.cjs +0 -308
  39. package/dist/setup/doctor.js +0 -288
  40. package/dist/utils-RBXHHCLI.js.map +0 -1
  41. package/scripts/postinstall.cjs +0 -27
  42. /package/dist/{chunk-IJGNPAK2.js.map → chunk-5CILZHRW.js.map} +0 -0
  43. /package/dist/{resolve-XWYJYKKH.js.map → resolve-ZSUEJK3E.js.map} +0 -0
  44. /package/dist/{install-commands-P2KTEXQ4.js.map → utils-H2BL5GNR.js.map} +0 -0
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-ZWE3DS7E.js";
3
+
4
+ // src/ocr/batch-provider.ts
5
+ import { spawn, execSync } from "child_process";
6
+ import { writeFileSync, readFileSync, unlinkSync, mkdirSync } from "fs";
7
+ import { join } from "path";
8
+ import { tmpdir } from "os";
9
+ var BATCH_OCR_PROMPT = `\uB2E4\uC74C \uBB38\uC11C \uD398\uC774\uC9C0 \uC774\uBBF8\uC9C0\uB4E4\uC744 OCR\uD558\uC5EC \uC21C\uC218 Markdown\uC73C\uB85C \uBCC0\uD658\uD558\uACE0, \uBA85\uBC31\uD55C OCR \uC624\uC778\uC2DD\uACFC \uD45C \uAD6C\uC870 \uAE68\uC9D0\uB9CC \uAD50\uC815\uD558\uC5EC \uCD5C\uC885 \uACB0\uACFC\uB97C \uCD9C\uB825\uD558\uC138\uC694.
10
+
11
+ [\uD398\uC774\uC9C0 \uAD6C\uBD84 \u2014 \uD544\uC218]
12
+ - \uAC01 \uD398\uC774\uC9C0 \uACB0\uACFC \uC0AC\uC774\uC5D0 \uBC18\uB4DC\uC2DC \uC774 \uAD6C\uBD84\uC790\uB97C \uC0BD\uC785: <!-- PAGE_BREAK -->
13
+
14
+ [\uAE30\uBCF8 \uCD94\uCD9C \uADDC\uCE59]
15
+ - \uBCF8\uBB38, \uD45C, \uC81C\uBAA9, \uB9AC\uC2A4\uD2B8, \uCEA1\uC158\uC744 \uC6D0\uBB38 \uAD6C\uC870 \uADF8\uB300\uB85C Markdown\uC73C\uB85C \uBCC0\uD658
16
+ - \uD5E4\uB529\uC740 \uC2DC\uAC01\uC801 \uD06C\uAE30\xB7\uAD75\uAE30\uC5D0 \uB530\uB77C # ~ ###### \uC0AC\uC6A9
17
+ - \uB9AC\uC2A4\uD2B8\uB294 -, 1. \uC0AC\uC6A9 (\uC6D0\uBB38 \uBC88\uD638 \uCCB4\uACC4\uAC00 \u2460, \uAC00., 1) \uB4F1\uC774\uBA74 \uADF8 \uD45C\uAE30 \uC720\uC9C0)
18
+ - \uC774\uBBF8\uC9C0\xB7\uB3C4\uD615\xB7\uB85C\uACE0\xB7\uD398\uC774\uC9C0\uBC88\uD638\xB7\uBA38\uB9AC\uAE00/\uBC14\uB2E5\uAE00\uC740 \uBB34\uC2DC
19
+ - \uC6D0\uBB38\uC758 \uC77D\uAE30 \uC21C\uC11C(\uC88C\u2192\uC6B0, \uC704\u2192\uC544\uB798, \uB2E4\uB2E8\uC774\uBA74 \uB2E8\uBCC4\uB85C)\uB97C \uC720\uC9C0
20
+
21
+ [\uD45C \uCD94\uCD9C \uADDC\uCE59 \u2014 \uAC00\uC7A5 \uC911\uC694]
22
+ - \uD45C\uB294 \uBC18\uB4DC\uC2DC Markdown \uD30C\uC774\uD504 \uD14C\uC774\uBE14 \uBB38\uBC95 \uC0AC\uC6A9: \uD5E4\uB354 \uD589 + |---| \uAD6C\uBD84\uC120 + \uB370\uC774\uD130 \uD589
23
+ - \uC2DC\uAC01\uC801\uC73C\uB85C \uAD75\uAC70\uB098 \uC74C\uC601(\uD68C\uC0C9/\uC0C9\uC0C1) \uBC30\uACBD\uC774 \uC788\uB294 \uD589\uC744 \uD5E4\uB354\uB85C \uC2DD\uBCC4. \uD5E4\uB354\uAC00 \uC5C6\uB294 \uD45C\uB77C\uB3C4 \uCCAB \uD589\uC744 \uD5E4\uB354\uB85C \uB450\uACE0 |---| \uAD6C\uBD84\uC120 \uCD94\uAC00
24
+ - \uBAA8\uB4E0 \uD589\uC758 \uD30C\uC774\uD504(|) \uAC1C\uC218\uB97C \uD5E4\uB354\uC640 \uB3D9\uC77C\uD558\uAC8C \uB9DE\uCD9C \uAC83 \u2014 \uBE48 \uC140\uC740 \uACF5\uBC31\uC73C\uB85C \uCC44\uC6B0\uACE0 \uC808\uB300 \uC140\uC744 \uC0DD\uB7B5\uD558\uC9C0 \uB9D0 \uAC83
25
+ - \uC140 \uC548\uC758 \uC904\uBC14\uAFC8\uC740 <br>\uB85C \uD45C\uAE30 (\uC2E4\uC81C \uAC1C\uD589 \uBB38\uC790 \uC0AC\uC6A9 \uAE08\uC9C0 \u2014 \uD45C\uAC00 \uAE68\uC9D0)
26
+ - \uBCD1\uD569 \uC140 \uCC98\uB9AC:
27
+ - \uAC00\uB85C \uBCD1\uD569(colspan): \uBCD1\uD569\uB41C \uC140 \uB0B4\uC6A9\uC744 \uCCAB \uCE78\uC5D0 \uC4F0\uACE0 \uB098\uBA38\uC9C0 \uCE78\uC740 \uBE48 \uCE78\uC73C\uB85C \uB458 \uAC83
28
+ - \uC138\uB85C \uBCD1\uD569(rowspan): \uBCD1\uD569\uB41C \uBAA8\uB4E0 \uD589\uC758 \uD574\uB2F9 \uCE78\uC5D0 \uB3D9\uC77C\uD55C \uB0B4\uC6A9\uC744 \uBC18\uBCF5 \uAE30\uC7AC\uD560 \uAC83
29
+ - 2\uB2E8 \uD5E4\uB354(\uD5E4\uB354\uAC00 \uB450 \uC904\uC778 \uACBD\uC6B0): \uC0C1\uC704/\uD558\uC704 \uD5E4\uB354\uB97C " / "\uB85C \uD569\uCCD0 \uB2E8\uC77C \uD5E4\uB354 \uD589\uC73C\uB85C \uB9CC\uB4E4 \uAC83 (\uC608: "2024 / 1\uBD84\uAE30")
30
+ - \uD45C \uC704\xB7\uC544\uB798\uC758 \uCEA1\uC158(\uC608: "<\uD45C 1> ...", "[\uD45C 2-1]")\uC740 \uD45C \uBC14\uB85C \uC704\uC5D0 \uBCC4\uB3C4 \uC904\uB85C \uBCF4\uC874
31
+ - \uD55C \uD398\uC774\uC9C0\uC5D0\uC11C \uD45C\uAC00 \uC911\uAC04\uC5D0 \uB04A\uC5B4\uC838 \uBCF4\uC774\uB354\uB77C\uB3C4, \uD5E4\uB354\uAC00 \uB3D9\uC77C\uD558\uBA74 \uD558\uB098\uC758 \uD45C\uB85C \uC774\uC5B4\uBD99\uC77C \uAC83
32
+ - \uC88C\uCE21 \uCCAB \uCEEC\uB7FC\uC774 \uC138\uB85C\uC4F0\uAE30 \uB77C\uBCA8(\uAD6C\uBD84/\uBD84\uB958/\uC5F0\uB3C4 \uB4F1)\uC774\uB77C\uB3C4 \uC77C\uBC18 \uC140\uB85C \uBCC0\uD658\uD558\uC5EC \uB204\uB77D \uAE08\uC9C0
33
+
34
+ [OCR \uC624\uC778\uC2DD\xB7\uAE68\uC9D0 \uAD50\uC815 \u2014 \uD5C8\uC6A9 \uBC94\uC704\uB9CC]
35
+ - \uC22B\uC790 \uCE78\uC5D0\uC11C 'O'\u2192'0', 'l/I'\u2192'1' \uB4F1 \uB9E5\uB77D\uC0C1 \uBA85\uBC31\uD55C \uC624\uC778\uC2DD\uB9CC \uAD50\uC815
36
+ - \uB2E8\uC5B4 \uC911\uAC04\uC5D0 \uC798\uBABB \uC0BD\uC785\uB41C \uACF5\uBC31 \uC81C\uAC70 (\uC608: "\uC8FC\uAC70 \uC885 \uD569 \uACC4\uD68D" \u2192 "\uC8FC\uAC70\uC885\uD569\uACC4\uD68D")
37
+ - \uC904\uBC14\uAFC8 \uC624\uB958\uB85C \uB04A\uAE34 \uBB38\uC7A5\uC740 \uC758\uBBF8 \uB2E8\uC704\uB85C \uBCD1\uD569 (\uB2E8, \uD45C \uC140 \uB0B4\uBD80\uB294 <br> \uC720\uC9C0)
38
+ - \uD45C\uC5D0\uC11C \uD589 \uC5B4\uAE0B\uB0A8\uC774 \uC758\uC2EC\uB418\uBA74(\uCEEC\uB7FC \uC218 \uBD88\uC77C\uCE58) \uBE48 \uC140\uB85C \uD328\uB529\uD558\uC5EC \uCEEC\uB7FC \uC815\uD569\uC131\uC744 \uC6B0\uC120 \uD655\uBCF4
39
+
40
+ [\uC808\uB300 \uAE08\uC9C0 \uC0AC\uD56D]
41
+ - \uBB38\uC7A5\xB7\uB2E8\uB77D\xB7\uD56D\uBAA9\xB7\uD45C \uD589/\uC5F4\uC744 \uCD94\uAC00\uD558\uAC70\uB098 \uC0AD\uC81C\uD558\uC9C0 \uB9D0 \uAC83
42
+ - \uC22B\uC790, \uD37C\uC13C\uD2B8, \uB0A0\uC9DC, \uB2E8\uC704, \uAE08\uC561\uC744 \uC808\uB300 \uBCC0\uACBD\uD558\uC9C0 \uB9D0 \uAC83
43
+ - \uACE0\uC720\uBA85\uC0AC, \uAE30\uAD00\uBA85, \uBC95\uB839\uBA85, \uC9C0\uBA85, \uC778\uBA85\uC744 \uBCC0\uACBD\uD558\uC9C0 \uB9D0 \uAC83
44
+ - \uD45C\uC758 \uD5E4\uB354 \uD14D\uC2A4\uD2B8, \uCEA1\uC158, \uD589/\uC5F4 \uAC1C\uC218\uB97C \uC784\uC758\uB85C \uBC14\uAFB8\uC9C0 \uB9D0 \uAC83
45
+ - \uCCB4\uD06C\uBC15\uC2A4(\u2611/\u2610/\u25A0/\u25A1), \uD2B9\uC218\uBB38\uC790(\u203B, \u2460\u2461\u2462, \u3260, \uAC00., \uB098.)\uB97C \uC784\uC758\uB85C \uBC14\uAFB8\uC9C0 \uB9D0 \uAC83
46
+ - \uC6D0\uBB38\uC5D0 \uC5C6\uB294 \uB0B4\uC6A9\uC744 \uC694\uC57D\xB7\uBCF4\uC644\xB7\uCD94\uB860\uD558\uC9C0 \uB9D0 \uAC83
47
+ - \`\`\`, \`\`\`markdown \uAC19\uC740 \uCF54\uB4DC\uD39C\uC2A4\uB85C \uAC10\uC2F8\uC9C0 \uB9D0 \uAC83
48
+ - \uC124\uBA85\xB7\uC8FC\uC11D\xB7\uBA54\uD0C0 \uBB38\uC7A5 \uCD94\uAC00 \uAE08\uC9C0
49
+
50
+ [\uBD88\uD655\uC2E4\uD560 \uB54C]
51
+ - \uAE00\uC790\uAC00 \uD750\uB9BF\uD558\uAC70\uB098 \uD310\uB3C5 \uBD88\uAC00\uD558\uBA74 \uBCF4\uC774\uB294 \uADF8\uB300\uB85C \uB450\uAC70\uB098, \uBD88\uAC00\uD53C\uD558\uBA74 \uD55C \uAE00\uC790\uB9CC ?\uB85C \uD45C\uC2DC
52
+ - \uD45C \uAD6C\uC870\uAC00 \uBAA8\uD638\uD558\uBA74 \uCEEC\uB7FC \uC218 \uC77C\uCE58 + \uBE48 \uC140 \uD328\uB529 \uD615\uD0DC\uB85C \uCD9C\uB825
53
+ - \uCD94\uCE21\uBCF4\uB2E4 \uC6D0\uBB38 \uBCF4\uC874\uC744 \uD56D\uC0C1 \uC6B0\uC120`;
54
+ var DEFAULT_BATCH_SIZES = {
55
+ gemini: 5,
56
+ claude: 5,
57
+ codex: 10
58
+ };
59
+ var _batchTempDir = null;
60
+ function getBatchTempDir() {
61
+ if (!_batchTempDir) {
62
+ _batchTempDir = join(process.cwd(), ".kordoc_ocr_tmp");
63
+ mkdirSync(_batchTempDir, { recursive: true });
64
+ if (process.platform === "win32") {
65
+ try {
66
+ execSync(`attrib +h "${_batchTempDir}"`, { stdio: "ignore" });
67
+ } catch {
68
+ }
69
+ }
70
+ }
71
+ return _batchTempDir;
72
+ }
73
+ function createBatchCliProvider(mode, batchSize) {
74
+ return {
75
+ __batch: true,
76
+ batchSize,
77
+ async processBatch(pages) {
78
+ const results = /* @__PURE__ */ new Map();
79
+ const tempDir = getBatchTempDir();
80
+ const tempFiles = [];
81
+ try {
82
+ for (const { image, pageNum } of pages) {
83
+ const path = join(tempDir, `batch-p${pageNum}.png`);
84
+ writeFileSync(path, image);
85
+ tempFiles.push(path);
86
+ }
87
+ let output;
88
+ if (mode === "codex") {
89
+ output = await callBatchCodexCli(tempFiles);
90
+ } else {
91
+ output = await callBatchCli(mode, tempFiles);
92
+ }
93
+ const cleaned = stripCodeFence(output.trim());
94
+ const parts = cleaned.split(/<!--\s*PAGE_BREAK\s*-->/).map((p) => p.trim()).filter((p) => p.length > 0);
95
+ for (let i = 0; i < pages.length; i++) {
96
+ const pageNum = pages[i].pageNum;
97
+ if (i < parts.length) {
98
+ results.set(pageNum, { markdown: parts[i] });
99
+ }
100
+ }
101
+ } finally {
102
+ for (const f of tempFiles) {
103
+ try {
104
+ unlinkSync(f);
105
+ } catch {
106
+ }
107
+ }
108
+ }
109
+ return results;
110
+ }
111
+ };
112
+ }
113
+ function spawnAsync(cmd, args, opts) {
114
+ return new Promise((resolve, reject) => {
115
+ const child = spawn(cmd, args, {
116
+ cwd: opts.cwd,
117
+ env: process.env,
118
+ stdio: ["pipe", "pipe", "pipe"],
119
+ shell: process.platform === "win32"
120
+ });
121
+ let stdout = "";
122
+ let stderr = "";
123
+ let killed = false;
124
+ child.stdout.setEncoding("utf-8");
125
+ child.stderr.setEncoding("utf-8");
126
+ child.stdout.on("data", (d) => {
127
+ stdout += d;
128
+ });
129
+ child.stderr.on("data", (d) => {
130
+ stderr += d;
131
+ });
132
+ const timer = setTimeout(() => {
133
+ killed = true;
134
+ if (process.platform === "win32") {
135
+ child.kill();
136
+ } else {
137
+ child.kill("SIGTERM");
138
+ }
139
+ }, opts.timeoutMs);
140
+ if (opts.stdin !== void 0) {
141
+ child.stdin.end(opts.stdin);
142
+ } else {
143
+ child.stdin.end();
144
+ }
145
+ child.on("close", (code) => {
146
+ clearTimeout(timer);
147
+ if (killed) {
148
+ reject(new Error(`\uD0C0\uC784\uC544\uC6C3 (${Math.round(opts.timeoutMs / 1e3)}\uCD08)`));
149
+ } else {
150
+ resolve({ stdout, stderr, exitCode: code ?? 1 });
151
+ }
152
+ });
153
+ child.on("error", (err) => {
154
+ clearTimeout(timer);
155
+ reject(err);
156
+ });
157
+ });
158
+ }
159
+ async function callBatchCli(mode, imagePaths) {
160
+ const fileRefs = imagePaths.map((p) => `@${p.replace(/\\/g, "/")}`).join("\n");
161
+ const prompt = `${BATCH_OCR_PROMPT}
162
+
163
+ ${fileRefs}`;
164
+ let args;
165
+ if (mode === "gemini") {
166
+ const model = process.env.KORDOC_GEMINI_MODEL ?? "gemini-2.5-flash";
167
+ args = ["--prompt", prompt, "--yolo", "--model", model];
168
+ } else {
169
+ args = ["--print", prompt];
170
+ const model = process.env.KORDOC_CLAUDE_MODEL;
171
+ if (model) args.push("--model", model);
172
+ }
173
+ const timeoutMs = 6e4 + imagePaths.length * 2e4;
174
+ const result = await spawnAsync(mode, args, {
175
+ timeoutMs,
176
+ ...mode === "claude" ? { cwd: tmpdir() } : {}
177
+ });
178
+ if (result.exitCode !== 0) {
179
+ const errMsg = result.stderr?.trim() || `exit code ${result.exitCode}`;
180
+ throw new Error(`${mode} \uBC30\uCE58 OCR \uC2E4\uD328: ${errMsg}`);
181
+ }
182
+ const output = result.stdout || "";
183
+ checkForLimitError(output, mode);
184
+ return output;
185
+ }
186
+ async function callBatchCodexCli(imagePaths) {
187
+ const outPath = join(tmpdir(), `kordoc-codex-batch-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
188
+ try {
189
+ const args = ["exec", BATCH_OCR_PROMPT];
190
+ for (const p of imagePaths) {
191
+ args.push("--image", p);
192
+ }
193
+ args.push("--output-last-message", outPath);
194
+ const model = process.env.KORDOC_CODEX_MODEL;
195
+ if (model) args.push("--model", model);
196
+ const timeoutMs = 6e4 + imagePaths.length * 2e4;
197
+ const result = await spawnAsync("codex", args, {
198
+ timeoutMs,
199
+ stdin: ""
200
+ });
201
+ if (result.exitCode !== 0) {
202
+ const errMsg = result.stderr?.trim() || `exit code ${result.exitCode}`;
203
+ throw new Error(`codex \uBC30\uCE58 OCR \uC2E4\uD328: ${errMsg}`);
204
+ }
205
+ let text;
206
+ try {
207
+ text = readFileSync(outPath, "utf-8");
208
+ } catch {
209
+ text = result.stdout || "";
210
+ }
211
+ checkForLimitError(text, "codex");
212
+ return text;
213
+ } finally {
214
+ try {
215
+ unlinkSync(outPath);
216
+ } catch {
217
+ }
218
+ }
219
+ }
220
+ function checkForLimitError(output, mode) {
221
+ const lower = output.toLowerCase();
222
+ if (lower.includes("usage limit") || lower.includes("rate limit")) {
223
+ throw new Error(`${mode} \uC0AC\uC6A9\uB7C9/\uC18D\uB3C4 \uC81C\uD55C: ${output.trim().slice(0, 200)}`);
224
+ }
225
+ }
226
+ function stripCodeFence(text) {
227
+ const match = text.match(/^```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/m);
228
+ return match ? match[1].trim() : text;
229
+ }
230
+ export {
231
+ DEFAULT_BATCH_SIZES,
232
+ createBatchCliProvider
233
+ };
234
+ //# sourceMappingURL=batch-provider-XRF6F26E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ocr/batch-provider.ts"],"sourcesContent":["/**\n * CLI 배치 OCR 프로바이더\n *\n * 여러 페이지 이미지를 단일 CLI 호출로 처리하여 API 호출 수를 대폭 감소.\n * gemini/claude: @file 멀티 참조, codex: --image 멀티 플래그\n *\n * 299페이지 기준:\n * - 기존: CLI 299회 호출 (~30분)\n * - 배치: CLI 3~6회 호출 (~3분)\n */\n\nimport { spawn, execSync } from \"child_process\"\nimport { writeFileSync, readFileSync, unlinkSync, mkdirSync } from \"fs\"\nimport { join } from \"path\"\nimport { tmpdir } from \"os\"\nimport type { StructuredOcrResult, BatchOcrProvider } from \"../types.js\"\n\n/** 배치 OCR 프롬프트 */\nconst BATCH_OCR_PROMPT = `다음 문서 페이지 이미지들을 OCR하여 순수 Markdown으로 변환하고, 명백한 OCR 오인식과 표 구조 깨짐만 교정하여 최종 결과를 출력하세요.\n\n[페이지 구분 — 필수]\n- 각 페이지 결과 사이에 반드시 이 구분자를 삽입: <!-- PAGE_BREAK -->\n\n[기본 추출 규칙]\n- 본문, 표, 제목, 리스트, 캡션을 원문 구조 그대로 Markdown으로 변환\n- 헤딩은 시각적 크기·굵기에 따라 # ~ ###### 사용\n- 리스트는 -, 1. 사용 (원문 번호 체계가 ①, 가., 1) 등이면 그 표기 유지)\n- 이미지·도형·로고·페이지번호·머리글/바닥글은 무시\n- 원문의 읽기 순서(좌→우, 위→아래, 다단이면 단별로)를 유지\n\n[표 추출 규칙 — 가장 중요]\n- 표는 반드시 Markdown 파이프 테이블 문법 사용: 헤더 행 + |---| 구분선 + 데이터 행\n- 시각적으로 굵거나 음영(회색/색상) 배경이 있는 행을 헤더로 식별. 헤더가 없는 표라도 첫 행을 헤더로 두고 |---| 구분선 추가\n- 모든 행의 파이프(|) 개수를 헤더와 동일하게 맞출 것 — 빈 셀은 공백으로 채우고 절대 셀을 생략하지 말 것\n- 셀 안의 줄바꿈은 <br>로 표기 (실제 개행 문자 사용 금지 — 표가 깨짐)\n- 병합 셀 처리:\n - 가로 병합(colspan): 병합된 셀 내용을 첫 칸에 쓰고 나머지 칸은 빈 칸으로 둘 것\n - 세로 병합(rowspan): 병합된 모든 행의 해당 칸에 동일한 내용을 반복 기재할 것\n- 2단 헤더(헤더가 두 줄인 경우): 상위/하위 헤더를 \" / \"로 합쳐 단일 헤더 행으로 만들 것 (예: \"2024 / 1분기\")\n- 표 위·아래의 캡션(예: \"<표 1> ...\", \"[표 2-1]\")은 표 바로 위에 별도 줄로 보존\n- 한 페이지에서 표가 중간에 끊어져 보이더라도, 헤더가 동일하면 하나의 표로 이어붙일 것\n- 좌측 첫 컬럼이 세로쓰기 라벨(구분/분류/연도 등)이라도 일반 셀로 변환하여 누락 금지\n\n[OCR 오인식·깨짐 교정 — 허용 범위만]\n- 숫자 칸에서 'O'→'0', 'l/I'→'1' 등 맥락상 명백한 오인식만 교정\n- 단어 중간에 잘못 삽입된 공백 제거 (예: \"주거 종 합 계획\" → \"주거종합계획\")\n- 줄바꿈 오류로 끊긴 문장은 의미 단위로 병합 (단, 표 셀 내부는 <br> 유지)\n- 표에서 행 어긋남이 의심되면(컬럼 수 불일치) 빈 셀로 패딩하여 컬럼 정합성을 우선 확보\n\n[절대 금지 사항]\n- 문장·단락·항목·표 행/열을 추가하거나 삭제하지 말 것\n- 숫자, 퍼센트, 날짜, 단위, 금액을 절대 변경하지 말 것\n- 고유명사, 기관명, 법령명, 지명, 인명을 변경하지 말 것\n- 표의 헤더 텍스트, 캡션, 행/열 개수를 임의로 바꾸지 말 것\n- 체크박스(☑/☐/■/□), 특수문자(※, ①②③, ㉠, 가., 나.)를 임의로 바꾸지 말 것\n- 원문에 없는 내용을 요약·보완·추론하지 말 것\n- \\`\\`\\`, \\`\\`\\`markdown 같은 코드펜스로 감싸지 말 것\n- 설명·주석·메타 문장 추가 금지\n\n[불확실할 때]\n- 글자가 흐릿하거나 판독 불가하면 보이는 그대로 두거나, 불가피하면 한 글자만 ?로 표시\n- 표 구조가 모호하면 컬럼 수 일치 + 빈 셀 패딩 형태로 출력\n- 추측보다 원문 보존을 항상 우선`\n\n/** 모드별 기본 배치 크기 (CLI 내부 타임아웃 + 실측 기반)\n *\n * gemini CLI: 10장 이상에서 AbortError 발생 (내부 타임아웃).\n * 5장 배치가 안정적으로 동작 확인 (35초/배치).\n * 299페이지 = 60배치 = 기존 299회 대비 80% 감소.\n */\nexport const DEFAULT_BATCH_SIZES: Record<string, number> = {\n gemini: 5,\n claude: 5,\n codex: 10,\n}\n\n/**\n * 임시 디렉토리 — gemini CLI는 cwd 하위 + gitignore 밖만 @참조 가능\n *\n * 숨김 처리:\n * - macOS/Linux: '.' 접두사로 기본 숨김 (ls -a 로만 표시)\n * - Windows: '.' 접두사 + attrib +h 로 숨김 속성 부여\n */\nlet _batchTempDir: string | null = null\nfunction getBatchTempDir(): string {\n if (!_batchTempDir) {\n _batchTempDir = join(process.cwd(), \".kordoc_ocr_tmp\")\n mkdirSync(_batchTempDir, { recursive: true })\n // Windows: dot-prefix만으로 숨김 처리 불충분 → attrib +h 추가\n if (process.platform === \"win32\") {\n try { execSync(`attrib +h \"${_batchTempDir}\"`, { stdio: \"ignore\" }) } catch { /* ignore */ }\n }\n }\n return _batchTempDir\n}\n\n/**\n * 배치 CLI 프로바이더 생성\n */\nexport function createBatchCliProvider(\n mode: \"gemini\" | \"claude\" | \"codex\",\n batchSize: number\n): BatchOcrProvider {\n return {\n __batch: true as const,\n batchSize,\n async processBatch(pages) {\n const results = new Map<number, StructuredOcrResult>()\n const tempDir = getBatchTempDir()\n const tempFiles: string[] = []\n\n try {\n // 1. Write all page images to temp files\n for (const { image, pageNum } of pages) {\n const path = join(tempDir, `batch-p${pageNum}.png`)\n writeFileSync(path, image)\n tempFiles.push(path)\n }\n\n // 2. Call CLI with all file references (비동기 — 병렬 배치 실행 가능)\n let output: string\n if (mode === \"codex\") {\n output = await callBatchCodexCli(tempFiles)\n } else {\n output = await callBatchCli(mode, tempFiles)\n }\n\n // 3. Parse response by PAGE_BREAK separator\n const cleaned = stripCodeFence(output.trim())\n const parts = cleaned.split(/<!--\\s*PAGE_BREAK\\s*-->/)\n .map(p => p.trim())\n .filter(p => p.length > 0)\n\n // 4. Map results to page numbers (best-effort if count mismatch)\n for (let i = 0; i < pages.length; i++) {\n const pageNum = pages[i].pageNum\n if (i < parts.length) {\n results.set(pageNum, { markdown: parts[i] })\n }\n // If fewer parts than pages, remaining pages get no result\n }\n } finally {\n // 5. Clean up temp files\n for (const f of tempFiles) {\n try { unlinkSync(f) } catch { /* ignore */ }\n }\n }\n\n return results\n },\n }\n}\n\n/**\n * 비동기 CLI 실행 헬퍼 — spawn + Promise 래핑.\n * spawnSync는 이벤트 루프를 차단하여 병렬 배치 실행 불가.\n */\nfunction spawnAsync(\n cmd: string,\n args: string[],\n opts: { timeoutMs: number; cwd?: string; stdin?: string }\n): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n return new Promise((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: opts.cwd,\n env: process.env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n })\n\n let stdout = \"\"\n let stderr = \"\"\n let killed = false\n\n child.stdout.setEncoding(\"utf-8\")\n child.stderr.setEncoding(\"utf-8\")\n child.stdout.on(\"data\", (d: string) => { stdout += d })\n child.stderr.on(\"data\", (d: string) => { stderr += d })\n\n const timer = setTimeout(() => {\n killed = true\n if (process.platform === \"win32\") {\n child.kill()\n } else {\n child.kill(\"SIGTERM\")\n }\n }, opts.timeoutMs)\n\n if (opts.stdin !== undefined) {\n child.stdin.end(opts.stdin)\n } else {\n child.stdin.end()\n }\n\n child.on(\"close\", (code) => {\n clearTimeout(timer)\n if (killed) {\n reject(new Error(`타임아웃 (${Math.round(opts.timeoutMs / 1000)}초)`))\n } else {\n resolve({ stdout, stderr, exitCode: code ?? 1 })\n }\n })\n child.on(\"error\", (err) => {\n clearTimeout(timer)\n reject(err)\n })\n })\n}\n\n/** gemini/claude 배치 호출 (비동기) */\nasync function callBatchCli(mode: \"gemini\" | \"claude\", imagePaths: string[]): Promise<string> {\n const fileRefs = imagePaths.map(p => `@${p.replace(/\\\\/g, \"/\")}`).join(\"\\n\")\n const prompt = `${BATCH_OCR_PROMPT}\\n\\n${fileRefs}`\n\n let args: string[]\n if (mode === \"gemini\") {\n const model = process.env.KORDOC_GEMINI_MODEL ?? \"gemini-2.5-flash\"\n args = [\"--prompt\", prompt, \"--yolo\", \"--model\", model]\n } else {\n args = [\"--print\", prompt]\n const model = process.env.KORDOC_CLAUDE_MODEL\n if (model) args.push(\"--model\", model)\n }\n\n const timeoutMs = 60_000 + imagePaths.length * 20_000\n const result = await spawnAsync(mode, args, {\n timeoutMs,\n ...(mode === \"claude\" ? { cwd: tmpdir() } : {}),\n })\n\n if (result.exitCode !== 0) {\n const errMsg = result.stderr?.trim() || `exit code ${result.exitCode}`\n throw new Error(`${mode} 배치 OCR 실패: ${errMsg}`)\n }\n\n const output = result.stdout || \"\"\n checkForLimitError(output, mode)\n return output\n}\n\n/** codex 배치 호출 (비동기) — --image를 여러 번 지정 */\nasync function callBatchCodexCli(imagePaths: string[]): Promise<string> {\n const outPath = join(tmpdir(), `kordoc-codex-batch-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`)\n try {\n const args = [\"exec\", BATCH_OCR_PROMPT]\n for (const p of imagePaths) {\n args.push(\"--image\", p)\n }\n args.push(\"--output-last-message\", outPath)\n const model = process.env.KORDOC_CODEX_MODEL\n if (model) args.push(\"--model\", model)\n\n const timeoutMs = 60_000 + imagePaths.length * 20_000\n const result = await spawnAsync(\"codex\", args, {\n timeoutMs,\n stdin: \"\",\n })\n\n if (result.exitCode !== 0) {\n const errMsg = result.stderr?.trim() || `exit code ${result.exitCode}`\n throw new Error(`codex 배치 OCR 실패: ${errMsg}`)\n }\n\n let text: string\n try {\n text = readFileSync(outPath, \"utf-8\")\n } catch {\n text = result.stdout || \"\"\n }\n checkForLimitError(text, \"codex\")\n return text\n } finally {\n try { unlinkSync(outPath) } catch { /* ignore */ }\n }\n}\n\n/**\n * 출력 텍스트에서 사용량·속도 제한 에러 감지.\n * 해당 메시지가 포함된 경우 throw하여 다음 엔진으로 fallback 트리거.\n */\nfunction checkForLimitError(output: string, mode: string): void {\n const lower = output.toLowerCase()\n if (lower.includes(\"usage limit\") || lower.includes(\"rate limit\")) {\n throw new Error(`${mode} 사용량/속도 제한: ${output.trim().slice(0, 200)}`)\n }\n}\n\n/** LLM 출력에서 코드 펜스 제거 (cli-provider.ts와 동일 로직) */\nfunction stripCodeFence(text: string): string {\n const match = text.match(/^```(?:markdown|md)?\\s*\\n([\\s\\S]*?)\\n```\\s*$/m)\n return match ? match[1].trim() : text\n}\n"],"mappings":"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","names":[]}
@@ -7,13 +7,13 @@ import {
7
7
  precheckZipSize,
8
8
  sanitizeHref,
9
9
  toArrayBuffer
10
- } from "./chunk-IJGNPAK2.js";
10
+ } from "./chunk-5CILZHRW.js";
11
11
  import {
12
12
  parsePageRange
13
13
  } from "./chunk-MOL7MDBG.js";
14
14
  import {
15
15
  createCliOcrProvider
16
- } from "./chunk-Y4WFKJ5P.js";
16
+ } from "./chunk-S7BHLD2V.js";
17
17
  import {
18
18
  markdownToBlocks
19
19
  } from "./chunk-YW5G6BCJ.js";
@@ -27,9 +27,9 @@ import {
27
27
  __toESM
28
28
  } from "./chunk-ZWE3DS7E.js";
29
29
 
30
- // ../../../node_modules/cfb/cfb.js
30
+ // node_modules/cfb/cfb.js
31
31
  var require_cfb = __commonJS({
32
- "../../../node_modules/cfb/cfb.js"(exports, module) {
32
+ "node_modules/cfb/cfb.js"(exports, module) {
33
33
  "use strict";
34
34
  var Base64_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
35
35
  function Base64_encode(input) {
@@ -5685,7 +5685,7 @@ async function parsePdfDocument(buffer, options) {
5685
5685
  if (ocrMode === "off") {
5686
5686
  throw Object.assign(new KordocError(`\uC774\uBBF8\uC9C0 \uAE30\uBC18 PDF (${pageCount}\uD398\uC774\uC9C0, ${totalChars}\uC790)`), { isImageBased: true });
5687
5687
  }
5688
- const { resolveOcrProvider } = await import("./resolve-XWYJYKKH.js");
5688
+ const { resolveOcrProvider } = await import("./resolve-ZSUEJK3E.js");
5689
5689
  const { ocrPages } = await import("./provider-T2D5XRTI.js");
5690
5690
  const tryProvider = async (provider, filter) => {
5691
5691
  try {
@@ -9823,6 +9823,8 @@ async function markdownToXlsx(markdown, options) {
9823
9823
 
9824
9824
  // src/pipeline/unified-ocr.ts
9825
9825
  import { performance } from "perf_hooks";
9826
+ import libre from "libreoffice-convert";
9827
+ var libreConvert = libre.convert;
9826
9828
  var OCR_PROMPT = [
9827
9829
  "\uC774 PDF \uD398\uC774\uC9C0 \uC774\uBBF8\uC9C0\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uC640 \uD45C\uB97C \uCD94\uCD9C\uD558\uC5EC Markdown\uC73C\uB85C \uBCC0\uD658\uD558\uACE0, OCR \uC624\uC778\uC2DD \uC624\uB958\uB97C \uC989\uC2DC \uAD50\uC815\uD558\uC5EC \uCD5C\uC885 \uACB0\uACFC\uBB3C\uC744 \uCD9C\uB825\uD558\uC138\uC694.",
9828
9830
  "",
@@ -10204,4 +10206,4 @@ export {
10204
10206
  cfb/cfb.js:
10205
10207
  (*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com *)
10206
10208
  */
10207
- //# sourceMappingURL=chunk-NKUNXGWI.js.map
10209
+ //# sourceMappingURL=chunk-25ZYYLVP.js.map