@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.
- package/dist/batch-provider-XRF6F26E.js +234 -0
- package/dist/batch-provider-XRF6F26E.js.map +1 -0
- package/dist/{chunk-NKUNXGWI.js → chunk-25ZYYLVP.js} +8 -6
- package/dist/chunk-25ZYYLVP.js.map +1 -0
- package/dist/{chunk-IJGNPAK2.js → chunk-5CILZHRW.js} +2 -2
- package/dist/chunk-S7BHLD2V.js +200 -0
- package/dist/{chunk-Y4WFKJ5P.js.map → chunk-S7BHLD2V.js.map} +1 -1
- package/dist/cli.js +6 -38
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +473 -2816
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +482 -2826
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -6
- package/dist/mcp.js.map +1 -1
- package/dist/{resolve-XWYJYKKH.js → resolve-ZSUEJK3E.js} +4 -4
- package/dist/{utils-RBXHHCLI.js → utils-H2BL5GNR.js} +2 -2
- package/dist/{watch-FRLS6FKE.js → watch-D6ODQLPJ.js} +4 -7
- package/dist/{watch-FRLS6FKE.js.map → watch-D6ODQLPJ.js.map} +1 -1
- package/package.json +3 -7
- package/dist/batch-provider-5BFJRKAZ.js +0 -190
- package/dist/batch-provider-5BFJRKAZ.js.map +0 -1
- package/dist/chunk-AEWWERJ5.js +0 -35
- package/dist/chunk-AEWWERJ5.js.map +0 -1
- package/dist/chunk-CPTOBJJD.js +0 -125
- package/dist/chunk-CPTOBJJD.js.map +0 -1
- package/dist/chunk-NKUNXGWI.js.map +0 -1
- package/dist/chunk-THBLCND6.js +0 -33
- package/dist/chunk-THBLCND6.js.map +0 -1
- package/dist/chunk-Y4WFKJ5P.js +0 -167
- package/dist/doctor-SJ7NYSXC.js +0 -126
- package/dist/doctor-SJ7NYSXC.js.map +0 -1
- package/dist/install-commands-P2KTEXQ4.js +0 -11
- package/dist/pm-7KGLH6MX.js +0 -9
- package/dist/pm-7KGLH6MX.js.map +0 -1
- package/dist/setup/doctor.cjs +0 -308
- package/dist/setup/doctor.js +0 -288
- package/dist/utils-RBXHHCLI.js.map +0 -1
- package/scripts/postinstall.cjs +0 -27
- /package/dist/{chunk-IJGNPAK2.js.map → chunk-5CILZHRW.js.map} +0 -0
- /package/dist/{resolve-XWYJYKKH.js.map → resolve-ZSUEJK3E.js.map} +0 -0
- /package/dist/{install-commands-P2KTEXQ4.js.map → utils-H2BL5GNR.js.map} +0 -0
package/dist/chunk-THBLCND6.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/setup/pm.ts
|
|
4
|
-
import { spawnSync } from "child_process";
|
|
5
|
-
var isWin = process.platform === "win32";
|
|
6
|
-
function which(cmd) {
|
|
7
|
-
const finder = isWin ? "where" : "which";
|
|
8
|
-
const r = spawnSync(finder, [cmd], { encoding: "utf8", shell: isWin });
|
|
9
|
-
return r.status === 0;
|
|
10
|
-
}
|
|
11
|
-
function detectPackageManager() {
|
|
12
|
-
if (process.platform === "darwin") {
|
|
13
|
-
return which("brew") ? "brew" : "unknown";
|
|
14
|
-
}
|
|
15
|
-
if (isWin) {
|
|
16
|
-
if (which("winget")) return "winget";
|
|
17
|
-
if (which("scoop")) return "scoop";
|
|
18
|
-
if (which("choco")) return "choco";
|
|
19
|
-
return "unknown";
|
|
20
|
-
}
|
|
21
|
-
if (which("apt-get") || which("apt")) return "apt";
|
|
22
|
-
if (which("dnf")) return "dnf";
|
|
23
|
-
if (which("yum")) return "yum";
|
|
24
|
-
if (which("pacman")) return "pacman";
|
|
25
|
-
if (which("zypper")) return "zypper";
|
|
26
|
-
if (which("apk")) return "apk";
|
|
27
|
-
return "unknown";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
detectPackageManager
|
|
32
|
-
};
|
|
33
|
-
//# sourceMappingURL=chunk-THBLCND6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/setup/pm.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\"\n\nexport type PackageManager =\n | \"brew\" | \"choco\" | \"scoop\" | \"winget\"\n | \"apt\" | \"dnf\" | \"yum\" | \"pacman\" | \"zypper\" | \"apk\"\n | \"unknown\"\n\nconst isWin = process.platform === \"win32\"\n\nfunction which(cmd: string): boolean {\n const finder = isWin ? \"where\" : \"which\"\n const r = spawnSync(finder, [cmd], { encoding: \"utf8\", shell: isWin })\n return r.status === 0\n}\n\nexport function detectPackageManager(): PackageManager {\n if (process.platform === \"darwin\") {\n return which(\"brew\") ? \"brew\" : \"unknown\"\n }\n if (isWin) {\n // winget이 Windows 10/11 기본 내장이므로 최우선\n if (which(\"winget\")) return \"winget\"\n if (which(\"scoop\")) return \"scoop\"\n if (which(\"choco\")) return \"choco\"\n return \"unknown\"\n }\n // Linux\n if (which(\"apt-get\") || which(\"apt\")) return \"apt\"\n if (which(\"dnf\")) return \"dnf\"\n if (which(\"yum\")) return \"yum\"\n if (which(\"pacman\")) return \"pacman\"\n if (which(\"zypper\")) return \"zypper\"\n if (which(\"apk\")) return \"apk\"\n return \"unknown\"\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAO1B,IAAM,QAAQ,QAAQ,aAAa;AAEnC,SAAS,MAAM,KAAsB;AACnC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,IAAI,UAAU,QAAQ,CAAC,GAAG,GAAG,EAAE,UAAU,QAAQ,OAAO,MAAM,CAAC;AACrE,SAAO,EAAE,WAAW;AACtB;AAEO,SAAS,uBAAuC;AACrD,MAAI,QAAQ,aAAa,UAAU;AACjC,WAAO,MAAM,MAAM,IAAI,SAAS;AAAA,EAClC;AACA,MAAI,OAAO;AAET,QAAI,MAAM,QAAQ,EAAG,QAAO;AAC5B,QAAI,MAAM,OAAO,EAAG,QAAO;AAC3B,QAAI,MAAM,OAAO,EAAG,QAAO;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,SAAS,KAAK,MAAM,KAAK,EAAG,QAAO;AAC7C,MAAI,MAAM,KAAK,EAAG,QAAO;AACzB,MAAI,MAAM,KAAK,EAAG,QAAO;AACzB,MAAI,MAAM,QAAQ,EAAG,QAAO;AAC5B,MAAI,MAAM,QAAQ,EAAG,QAAO;AAC5B,MAAI,MAAM,KAAK,EAAG,QAAO;AACzB,SAAO;AACT;","names":[]}
|
package/dist/chunk-Y4WFKJ5P.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/ocr/cli-provider.ts
|
|
4
|
-
import { spawnSync } from "child_process";
|
|
5
|
-
import { writeFileSync, readFileSync, unlinkSync, mkdirSync } from "fs";
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
import { tmpdir } from "os";
|
|
8
|
-
var OCR_PROMPT = `\uC774 PDF \uD398\uC774\uC9C0 \uC774\uBBF8\uC9C0\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uC640 \uD14C\uC774\uBE14\uC744 \uCD94\uCD9C\uD558\uC5EC \uC21C\uC218 Markdown\uC73C\uB85C \uBCC0\uD658\uD558\uC138\uC694.
|
|
9
|
-
\uADDC\uCE59:
|
|
10
|
-
- \uD14C\uC774\uBE14\uC740 Markdown \uD14C\uC774\uBE14 \uBB38\uBC95 \uC0AC\uC6A9 (| \uAD6C\uBD84, |---|---| \uD5E4\uB354 \uAD6C\uBD84\uC120 \uD3EC\uD568)
|
|
11
|
-
- \uBCD1\uD569\uB41C \uC140\uC740 \uD574\uB2F9 \uC704\uCE58\uC5D0 \uB0B4\uC6A9 \uAE30\uC7AC
|
|
12
|
-
- \uD5E4\uB529\uC740 \uAE00\uC790 \uD06C\uAE30\uC5D0 \uB530\uB77C ## ~ ###### \uC0AC\uC6A9
|
|
13
|
-
- \uB9AC\uC2A4\uD2B8\uB294 - \uB610\uB294 1. \uC0AC\uC6A9
|
|
14
|
-
- \uC774\uBBF8\uC9C0, \uB3C4\uD615 \uB4F1 \uBE44\uD14D\uC2A4\uD2B8 \uC694\uC18C\uB294 \uBB34\uC2DC
|
|
15
|
-
- \uC6D0\uBB38\uC758 \uC77D\uAE30 \uC21C\uC11C\uC640 \uAD6C\uC870\uB97C \uC720\uC9C0
|
|
16
|
-
- \`\`\`\uB85C \uAC10\uC2F8\uC9C0 \uB9D0\uACE0 \uC21C\uC218 Markdown\uB9CC \uCD9C\uB825`;
|
|
17
|
-
var _tempDir = null;
|
|
18
|
-
function getTempDir() {
|
|
19
|
-
if (!_tempDir) {
|
|
20
|
-
_tempDir = join(process.cwd(), ".kordoc_ocr_tmp");
|
|
21
|
-
mkdirSync(_tempDir, { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
return _tempDir;
|
|
24
|
-
}
|
|
25
|
-
function createCliOcrProvider(mode) {
|
|
26
|
-
return async (pageImage, pageNumber) => {
|
|
27
|
-
const tempPath = join(getTempDir(), `page-${pageNumber}.png`);
|
|
28
|
-
try {
|
|
29
|
-
writeFileSync(tempPath, pageImage);
|
|
30
|
-
let output;
|
|
31
|
-
if (mode === "ollama") {
|
|
32
|
-
output = await callOllamaApi(tempPath);
|
|
33
|
-
} else {
|
|
34
|
-
output = callCli(mode, tempPath);
|
|
35
|
-
}
|
|
36
|
-
return { markdown: stripCodeFence(output.trim()) };
|
|
37
|
-
} finally {
|
|
38
|
-
try {
|
|
39
|
-
unlinkSync(tempPath);
|
|
40
|
-
} catch {
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
function checkForLimitError(output, mode) {
|
|
46
|
-
const lower = output.toLowerCase();
|
|
47
|
-
if (lower.includes("usage limit") || lower.includes("rate limit")) {
|
|
48
|
-
throw new Error(`${mode} \uC0AC\uC6A9\uB7C9/\uC18D\uB3C4 \uC81C\uD55C: ${output.trim().slice(0, 200)}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function callCli(mode, imagePath) {
|
|
52
|
-
if (mode === "codex") {
|
|
53
|
-
return callCodexCli(imagePath);
|
|
54
|
-
}
|
|
55
|
-
const args = buildCliArgs(mode, imagePath);
|
|
56
|
-
const result = spawnSync(mode, args, {
|
|
57
|
-
encoding: "utf-8",
|
|
58
|
-
timeout: 6e5,
|
|
59
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
60
|
-
shell: process.platform === "win32",
|
|
61
|
-
// claude: /tmp에서 실행하여 프로젝트 CLAUDE.md의 규칙 간섭 방지
|
|
62
|
-
...mode === "claude" ? { cwd: tmpdir() } : {}
|
|
63
|
-
});
|
|
64
|
-
if (result.error) {
|
|
65
|
-
throw new Error(`${mode} CLI \uC2E4\uD589 \uC2E4\uD328: ${result.error.message}`);
|
|
66
|
-
}
|
|
67
|
-
if (result.status !== 0) {
|
|
68
|
-
const errMsg = result.stderr?.trim() || `exit code ${result.status}`;
|
|
69
|
-
throw new Error(`${mode} OCR \uC2E4\uD328: ${errMsg}`);
|
|
70
|
-
}
|
|
71
|
-
const output = result.stdout || "";
|
|
72
|
-
checkForLimitError(output, mode);
|
|
73
|
-
return output;
|
|
74
|
-
}
|
|
75
|
-
function callCodexCli(imagePath) {
|
|
76
|
-
const outPath = join(tmpdir(), `kordoc-codex-out-${Date.now()}.txt`);
|
|
77
|
-
try {
|
|
78
|
-
const args = ["exec", OCR_PROMPT, "--image", imagePath, "--output-last-message", outPath];
|
|
79
|
-
const model = process.env.KORDOC_CODEX_MODEL;
|
|
80
|
-
if (model) args.push("--model", model);
|
|
81
|
-
const result = spawnSync("codex", args, {
|
|
82
|
-
encoding: "utf-8",
|
|
83
|
-
timeout: 18e4,
|
|
84
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
85
|
-
input: "",
|
|
86
|
-
// stdin EOF 즉시 전달 (대화형 입력 차단)
|
|
87
|
-
shell: process.platform === "win32"
|
|
88
|
-
});
|
|
89
|
-
if (result.error) {
|
|
90
|
-
throw new Error(`codex CLI \uC2E4\uD589 \uC2E4\uD328: ${result.error.message}`);
|
|
91
|
-
}
|
|
92
|
-
if (result.status !== 0) {
|
|
93
|
-
const errMsg = result.stderr?.trim() || `exit code ${result.status}`;
|
|
94
|
-
throw new Error(`codex OCR \uC2E4\uD328: ${errMsg}`);
|
|
95
|
-
}
|
|
96
|
-
let text;
|
|
97
|
-
try {
|
|
98
|
-
text = readFileSync(outPath, "utf-8");
|
|
99
|
-
} catch {
|
|
100
|
-
text = result.stdout || "";
|
|
101
|
-
}
|
|
102
|
-
checkForLimitError(text, "codex");
|
|
103
|
-
return text;
|
|
104
|
-
} finally {
|
|
105
|
-
try {
|
|
106
|
-
unlinkSync(outPath);
|
|
107
|
-
} catch {
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function buildCliArgs(mode, imagePath) {
|
|
112
|
-
const normalizedPath = imagePath.replace(/\\/g, "/");
|
|
113
|
-
const promptWithImage = `${OCR_PROMPT}
|
|
114
|
-
|
|
115
|
-
\uC774\uBBF8\uC9C0: @${normalizedPath}`;
|
|
116
|
-
switch (mode) {
|
|
117
|
-
case "gemini": {
|
|
118
|
-
const args = ["--prompt", promptWithImage, "--yolo"];
|
|
119
|
-
const model = process.env.KORDOC_GEMINI_MODEL;
|
|
120
|
-
if (model) args.push("--model", model);
|
|
121
|
-
return args;
|
|
122
|
-
}
|
|
123
|
-
case "claude": {
|
|
124
|
-
const args = ["--print", promptWithImage];
|
|
125
|
-
const model = process.env.KORDOC_CLAUDE_MODEL;
|
|
126
|
-
if (model) args.push("--model", model);
|
|
127
|
-
return args;
|
|
128
|
-
}
|
|
129
|
-
default:
|
|
130
|
-
throw new Error(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 CLI: ${mode}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
async function callOllamaApi(imagePath) {
|
|
134
|
-
const { readFileSync: readFileSync2 } = await import("fs");
|
|
135
|
-
const imageBase64 = readFileSync2(imagePath).toString("base64");
|
|
136
|
-
const model = process.env.KORDOC_OLLAMA_MODEL || "qwen3-vl:8b";
|
|
137
|
-
const host = process.env.KORDOC_OLLAMA_HOST || "http://localhost:11434";
|
|
138
|
-
const timeoutMs = Number(process.env.KORDOC_OLLAMA_TIMEOUT) || 12e4;
|
|
139
|
-
const response = await fetch(`${host}/api/chat`, {
|
|
140
|
-
method: "POST",
|
|
141
|
-
headers: { "Content-Type": "application/json" },
|
|
142
|
-
body: JSON.stringify({
|
|
143
|
-
model,
|
|
144
|
-
messages: [{
|
|
145
|
-
role: "user",
|
|
146
|
-
content: OCR_PROMPT,
|
|
147
|
-
images: [imageBase64]
|
|
148
|
-
}],
|
|
149
|
-
stream: false
|
|
150
|
-
}),
|
|
151
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
152
|
-
});
|
|
153
|
-
if (!response.ok) {
|
|
154
|
-
throw new Error(`Ollama API \uC624\uB958: ${response.status} ${response.statusText}`);
|
|
155
|
-
}
|
|
156
|
-
const data = await response.json();
|
|
157
|
-
return data.message?.content || "";
|
|
158
|
-
}
|
|
159
|
-
function stripCodeFence(text) {
|
|
160
|
-
const match = text.match(/^```(?:markdown|md)?\s*([\s\S]*?)```\s*$/m);
|
|
161
|
-
return match ? match[1].trim() : text;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export {
|
|
165
|
-
createCliOcrProvider
|
|
166
|
-
};
|
|
167
|
-
//# sourceMappingURL=chunk-Y4WFKJ5P.js.map
|
package/dist/doctor-SJ7NYSXC.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
isWSL,
|
|
4
|
-
probeOcrToolchain
|
|
5
|
-
} from "./chunk-CPTOBJJD.js";
|
|
6
|
-
import {
|
|
7
|
-
detectPackageManager
|
|
8
|
-
} from "./chunk-THBLCND6.js";
|
|
9
|
-
import {
|
|
10
|
-
LIBREOFFICE_RECIPES,
|
|
11
|
-
POPPLER_RECIPES
|
|
12
|
-
} from "./chunk-AEWWERJ5.js";
|
|
13
|
-
import "./chunk-ZWE3DS7E.js";
|
|
14
|
-
|
|
15
|
-
// src/setup/doctor.ts
|
|
16
|
-
var hr = "\u2500".repeat(45);
|
|
17
|
-
function platform() {
|
|
18
|
-
const arch = process.arch;
|
|
19
|
-
if (isWSL()) return `linux/WSL (arch: ${arch})`;
|
|
20
|
-
return `${process.platform} (arch: ${arch})`;
|
|
21
|
-
}
|
|
22
|
-
function icon(found) {
|
|
23
|
-
return found ? "\u2705" : "\u274C";
|
|
24
|
-
}
|
|
25
|
-
function pad(s, n) {
|
|
26
|
-
return s.padEnd(n);
|
|
27
|
-
}
|
|
28
|
-
function formatTool(name, status) {
|
|
29
|
-
const ico = icon(status.found);
|
|
30
|
-
if (!status.found) return ` ${pad(name, 10)} ${ico} \uCC3E\uC744 \uC218 \uC5C6\uC74C`;
|
|
31
|
-
const ver = status.version ? ` (${status.version})` : "";
|
|
32
|
-
return ` ${pad(name, 10)} ${ico} ${status.path}${ver}`;
|
|
33
|
-
}
|
|
34
|
-
function runDoctor(opts = {}) {
|
|
35
|
-
const toolchain = probeOcrToolchain();
|
|
36
|
-
const pm = detectPackageManager();
|
|
37
|
-
const popplerOk = toolchain.pdftoppm.found && toolchain.pdfinfo.found;
|
|
38
|
-
const sofficeOk = toolchain.soffice.found;
|
|
39
|
-
const missing = [];
|
|
40
|
-
if (!popplerOk) missing.push("poppler");
|
|
41
|
-
if (!sofficeOk) missing.push("libreoffice");
|
|
42
|
-
const out = opts.postinstall ? process.stderr : process.stdout;
|
|
43
|
-
out.write(`${hr}
|
|
44
|
-
`);
|
|
45
|
-
out.write(`kordoc OCR \uC758\uC874\uC131 \uC810\uAC80
|
|
46
|
-
`);
|
|
47
|
-
out.write(`${hr}
|
|
48
|
-
`);
|
|
49
|
-
out.write(`\uD50C\uB7AB\uD3FC: ${platform()}
|
|
50
|
-
`);
|
|
51
|
-
out.write(`\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${pm === "unknown" ? "\uAC10\uC9C0 \uC2E4\uD328" : pm}
|
|
52
|
-
`);
|
|
53
|
-
out.write(`Node.js: ${process.version}
|
|
54
|
-
`);
|
|
55
|
-
out.write(`
|
|
56
|
-
`);
|
|
57
|
-
out.write(`[OCR \uD30C\uC774\uD504\uB77C\uC778 \uC758\uC874\uC131]
|
|
58
|
-
`);
|
|
59
|
-
out.write(`${formatTool("pdftoppm", toolchain.pdftoppm)}
|
|
60
|
-
`);
|
|
61
|
-
out.write(`${formatTool("pdfinfo", toolchain.pdfinfo)}
|
|
62
|
-
`);
|
|
63
|
-
out.write(`${formatTool("pdftotext", toolchain.pdftotext)}
|
|
64
|
-
`);
|
|
65
|
-
out.write(`${formatTool("soffice", toolchain.soffice)}
|
|
66
|
-
`);
|
|
67
|
-
if (missing.length > 0) {
|
|
68
|
-
out.write(`
|
|
69
|
-
\uAD8C\uC7A5 \uC124\uCE58 \uBA85\uB839:
|
|
70
|
-
`);
|
|
71
|
-
if (!popplerOk) {
|
|
72
|
-
const recipe = POPPLER_RECIPES[pm];
|
|
73
|
-
if (recipe) {
|
|
74
|
-
out.write(` # poppler (pdftoppm/pdfinfo)
|
|
75
|
-
`);
|
|
76
|
-
out.write(` ${recipe.cmd}
|
|
77
|
-
`);
|
|
78
|
-
if (recipe.notes) out.write(` # ${recipe.notes}
|
|
79
|
-
`);
|
|
80
|
-
} else {
|
|
81
|
-
out.write(` poppler: OS\uC5D0 \uB9DE\uB294 \uBC29\uBC95\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694 (README \uCC38\uACE0)
|
|
82
|
-
`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (!sofficeOk) {
|
|
86
|
-
const recipe = LIBREOFFICE_RECIPES[pm];
|
|
87
|
-
if (recipe) {
|
|
88
|
-
out.write(`
|
|
89
|
-
# LibreOffice (soffice)
|
|
90
|
-
`);
|
|
91
|
-
out.write(` ${recipe.cmd}
|
|
92
|
-
`);
|
|
93
|
-
if (recipe.notes) out.write(` # ${recipe.notes}
|
|
94
|
-
`);
|
|
95
|
-
} else {
|
|
96
|
-
out.write(` LibreOffice: OS\uC5D0 \uB9DE\uB294 \uBC29\uBC95\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694 (README \uCC38\uACE0)
|
|
97
|
-
`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (process.platform === "win32") {
|
|
101
|
-
out.write(`
|
|
102
|
-
\u26A0\uFE0F \uC124\uCE58 \uD6C4 \uC0C8 \uD130\uBBF8\uB110\uC744 \uC5F4\uAC70\uB098 PATH\uB97C \uAC31\uC2E0\uD574\uC57C \uD569\uB2C8\uB2E4.
|
|
103
|
-
`);
|
|
104
|
-
out.write(` PowerShell: $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
105
|
-
`);
|
|
106
|
-
}
|
|
107
|
-
out.write(`
|
|
108
|
-
\uD658\uACBD\uBCC0\uC218\uB85C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC9C0\uC815\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4:
|
|
109
|
-
`);
|
|
110
|
-
out.write(` KORDOC_PDFTOPPM_PATH, KORDOC_PDFINFO_PATH, KORDOC_SOFFICE_PATH
|
|
111
|
-
`);
|
|
112
|
-
out.write(`
|
|
113
|
-
\uC790\uC138\uD55C \uC810\uAC80: npx kordoc doctor
|
|
114
|
-
`);
|
|
115
|
-
}
|
|
116
|
-
out.write(`${hr}
|
|
117
|
-
`);
|
|
118
|
-
const summary = missing.length === 0 ? "\uBAA8\uB4E0 \uC758\uC874\uC131\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 \u2705" : `${missing.length}\uAC1C \uACB0\uC190 \u2014 OCR \uBAA8\uB4DC \uC0AC\uC6A9 \uC2DC \uCC28\uB2E8\uB429\uB2C8\uB2E4 (${missing.join(", ")})`;
|
|
119
|
-
out.write(`\uC694\uC57D: ${summary}
|
|
120
|
-
`);
|
|
121
|
-
return { ok: missing.length === 0, missing, toolchain, pm };
|
|
122
|
-
}
|
|
123
|
-
export {
|
|
124
|
-
runDoctor
|
|
125
|
-
};
|
|
126
|
-
//# sourceMappingURL=doctor-SJ7NYSXC.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/setup/doctor.ts"],"sourcesContent":["import { probeOcrToolchain, isWSL } from \"./detect.js\"\nimport { detectPackageManager } from \"./pm.js\"\nimport { POPPLER_RECIPES, LIBREOFFICE_RECIPES } from \"./install-commands.js\"\nimport type { OcrToolchainStatus } from \"./detect.js\"\n\nconst hr = \"─\".repeat(45)\n\nfunction platform(): string {\n const arch = process.arch\n if (isWSL()) return `linux/WSL (arch: ${arch})`\n return `${process.platform} (arch: ${arch})`\n}\n\nfunction icon(found: boolean): string {\n return found ? \"✅\" : \"❌\"\n}\n\nfunction pad(s: string, n: number): string {\n return s.padEnd(n)\n}\n\nfunction formatTool(name: string, status: { found: boolean; path?: string; version?: string }): string {\n const ico = icon(status.found)\n if (!status.found) return ` ${pad(name, 10)} ${ico} 찾을 수 없음`\n const ver = status.version ? ` (${status.version})` : \"\"\n return ` ${pad(name, 10)} ${ico} ${status.path}${ver}`\n}\n\nexport interface DoctorResult {\n ok: boolean\n missing: string[]\n toolchain: OcrToolchainStatus\n pm: ReturnType<typeof detectPackageManager>\n}\n\nexport function runDoctor(opts: { postinstall?: boolean } = {}): DoctorResult {\n const toolchain = probeOcrToolchain()\n const pm = detectPackageManager()\n\n const popplerOk = toolchain.pdftoppm.found && toolchain.pdfinfo.found\n const sofficeOk = toolchain.soffice.found\n\n const missing: string[] = []\n if (!popplerOk) missing.push(\"poppler\")\n if (!sofficeOk) missing.push(\"libreoffice\")\n\n const out = opts.postinstall ? process.stderr : process.stdout\n\n out.write(`${hr}\\n`)\n out.write(`kordoc OCR 의존성 점검\\n`)\n out.write(`${hr}\\n`)\n out.write(`플랫폼: ${platform()}\\n`)\n out.write(`패키지 매니저: ${pm === \"unknown\" ? \"감지 실패\" : pm}\\n`)\n out.write(`Node.js: ${process.version}\\n`)\n out.write(`\\n`)\n out.write(`[OCR 파이프라인 의존성]\\n`)\n out.write(`${formatTool(\"pdftoppm\", toolchain.pdftoppm)}\\n`)\n out.write(`${formatTool(\"pdfinfo\", toolchain.pdfinfo)}\\n`)\n out.write(`${formatTool(\"pdftotext\", toolchain.pdftotext)}\\n`)\n out.write(`${formatTool(\"soffice\", toolchain.soffice)}\\n`)\n\n if (missing.length > 0) {\n out.write(`\\n권장 설치 명령:\\n`)\n\n if (!popplerOk) {\n const recipe = POPPLER_RECIPES[pm]\n if (recipe) {\n out.write(` # poppler (pdftoppm/pdfinfo)\\n`)\n out.write(` ${recipe.cmd}\\n`)\n if (recipe.notes) out.write(` # ${recipe.notes}\\n`)\n } else {\n out.write(` poppler: OS에 맞는 방법으로 설치하세요 (README 참고)\\n`)\n }\n }\n\n if (!sofficeOk) {\n const recipe = LIBREOFFICE_RECIPES[pm]\n if (recipe) {\n out.write(`\\n # LibreOffice (soffice)\\n`)\n out.write(` ${recipe.cmd}\\n`)\n if (recipe.notes) out.write(` # ${recipe.notes}\\n`)\n } else {\n out.write(` LibreOffice: OS에 맞는 방법으로 설치하세요 (README 참고)\\n`)\n }\n }\n\n if (process.platform === \"win32\") {\n out.write(`\\n ⚠️ 설치 후 새 터미널을 열거나 PATH를 갱신해야 합니다.\\n`)\n out.write(` PowerShell: $env:Path = [System.Environment]::GetEnvironmentVariable(\"Path\",\"Machine\") + \";\" + [System.Environment]::GetEnvironmentVariable(\"Path\",\"User\")\\n`)\n }\n\n out.write(`\\n환경변수로 경로를 직접 지정할 수도 있습니다:\\n`)\n out.write(` KORDOC_PDFTOPPM_PATH, KORDOC_PDFINFO_PATH, KORDOC_SOFFICE_PATH\\n`)\n out.write(`\\n자세한 점검: npx kordoc doctor\\n`)\n }\n\n out.write(`${hr}\\n`)\n\n const summary = missing.length === 0\n ? \"모든 의존성이 설치되어 있습니다 ✅\"\n : `${missing.length}개 결손 — OCR 모드 사용 시 차단됩니다 (${missing.join(\", \")})`\n out.write(`요약: ${summary}\\n`)\n\n return { ok: missing.length === 0, missing, toolchain, pm }\n}\n"],"mappings":"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","names":[]}
|
package/dist/pm-7KGLH6MX.js
DELETED
package/dist/pm-7KGLH6MX.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/setup/doctor.cjs
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/setup/doctor.ts
|
|
21
|
-
var doctor_exports = {};
|
|
22
|
-
__export(doctor_exports, {
|
|
23
|
-
runDoctor: () => runDoctor
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(doctor_exports);
|
|
26
|
-
|
|
27
|
-
// src/setup/detect.ts
|
|
28
|
-
var import_node_child_process = require("child_process");
|
|
29
|
-
var import_node_fs = require("fs");
|
|
30
|
-
var import_node_path = require("path");
|
|
31
|
-
var isWin = process.platform === "win32";
|
|
32
|
-
function which(cmd) {
|
|
33
|
-
const finder = isWin ? "where" : "which";
|
|
34
|
-
const r = (0, import_node_child_process.spawnSync)(finder, [cmd], { encoding: "utf8", shell: isWin });
|
|
35
|
-
if (r.status !== 0) return void 0;
|
|
36
|
-
return r.stdout.split(/\r?\n/).find(Boolean)?.trim();
|
|
37
|
-
}
|
|
38
|
-
function resolveCmd(cmd, envKey) {
|
|
39
|
-
const override = process.env[envKey];
|
|
40
|
-
if (override) return override;
|
|
41
|
-
const found = which(cmd);
|
|
42
|
-
if (found) return found;
|
|
43
|
-
if (isWin) return findWinCandidate(cmd);
|
|
44
|
-
return void 0;
|
|
45
|
-
}
|
|
46
|
-
function findWinCandidate(cmd) {
|
|
47
|
-
const exe = cmd.endsWith(".exe") ? cmd : `${cmd}.exe`;
|
|
48
|
-
const userProfile = process.env["USERPROFILE"] ?? "C:\\Users\\Default";
|
|
49
|
-
const localAppData = process.env["LOCALAPPDATA"] ?? (0, import_node_path.join)(userProfile, "AppData", "Local");
|
|
50
|
-
const programFiles = process.env["ProgramFiles"] ?? "C:\\Program Files";
|
|
51
|
-
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
52
|
-
const isPopplerExe = ["pdftoppm.exe", "pdfinfo.exe", "pdftotext.exe"].includes(exe);
|
|
53
|
-
const isSoffice = exe === "soffice.exe";
|
|
54
|
-
if (isPopplerExe) return findPopplerWin(exe, { userProfile, localAppData, programFiles, programFilesX86 });
|
|
55
|
-
if (isSoffice) return findSofficeWin({ programFiles, programFilesX86, localAppData });
|
|
56
|
-
return void 0;
|
|
57
|
-
}
|
|
58
|
-
function findPopplerWin(exe, p) {
|
|
59
|
-
const scoopPath = (0, import_node_path.join)(p.userProfile, "scoop", "apps", "poppler", "current", "bin", exe);
|
|
60
|
-
if ((0, import_node_fs.existsSync)(scoopPath)) return scoopPath;
|
|
61
|
-
const wingetBase = (0, import_node_path.join)(p.localAppData, "Microsoft", "WinGet", "Packages");
|
|
62
|
-
const wingetResult = globFirst(wingetBase, ["oschwartz10612.Poppler_*"], ["poppler-*", "Library", "bin", exe]);
|
|
63
|
-
if (wingetResult) return wingetResult;
|
|
64
|
-
const chocoBase = (0, import_node_path.join)("C:", "ProgramData", "chocolatey", "lib", "poppler", "tools");
|
|
65
|
-
const chocoResult = globFirst(chocoBase, ["poppler-*"], ["bin", exe]);
|
|
66
|
-
if (chocoResult) return chocoResult;
|
|
67
|
-
for (const base of [
|
|
68
|
-
(0, import_node_path.join)("C:", "poppler", "bin", exe),
|
|
69
|
-
(0, import_node_path.join)("C:", "tools", "poppler", "bin", exe),
|
|
70
|
-
(0, import_node_path.join)(p.programFiles, "poppler", "bin", exe),
|
|
71
|
-
(0, import_node_path.join)(p.programFilesX86, "poppler", "bin", exe),
|
|
72
|
-
(0, import_node_path.join)(p.localAppData, "Programs", "poppler", "bin", exe)
|
|
73
|
-
]) {
|
|
74
|
-
if ((0, import_node_fs.existsSync)(base)) return base;
|
|
75
|
-
}
|
|
76
|
-
return void 0;
|
|
77
|
-
}
|
|
78
|
-
function findSofficeWin(p) {
|
|
79
|
-
for (const candidate of [
|
|
80
|
-
(0, import_node_path.join)(p.programFiles, "LibreOffice", "program", "soffice.exe"),
|
|
81
|
-
(0, import_node_path.join)(p.programFilesX86, "LibreOffice", "program", "soffice.exe"),
|
|
82
|
-
(0, import_node_path.join)(p.localAppData, "Programs", "LibreOffice", "program", "soffice.exe"),
|
|
83
|
-
// winget 설치는 Program Files와 동일
|
|
84
|
-
(0, import_node_path.join)(p.programFiles, "LibreOffice 24", "program", "soffice.exe"),
|
|
85
|
-
(0, import_node_path.join)(p.programFiles, "LibreOffice 25", "program", "soffice.exe")
|
|
86
|
-
]) {
|
|
87
|
-
if ((0, import_node_fs.existsSync)(candidate)) return candidate;
|
|
88
|
-
}
|
|
89
|
-
return void 0;
|
|
90
|
-
}
|
|
91
|
-
function globFirst(base, wildcardSegments, rest) {
|
|
92
|
-
if (!(0, import_node_fs.existsSync)(base)) return void 0;
|
|
93
|
-
let current = base;
|
|
94
|
-
for (const seg of wildcardSegments) {
|
|
95
|
-
if (!seg.includes("*")) {
|
|
96
|
-
current = (0, import_node_path.join)(current, seg);
|
|
97
|
-
if (!(0, import_node_fs.existsSync)(current)) return void 0;
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const { readdirSync } = require("fs");
|
|
102
|
-
const entries = readdirSync(current);
|
|
103
|
-
const pattern = seg.replace(/\*/g, ".*");
|
|
104
|
-
const re = new RegExp(`^${pattern}$`, "i");
|
|
105
|
-
const match = entries.find((e) => re.test(e));
|
|
106
|
-
if (!match) return void 0;
|
|
107
|
-
current = (0, import_node_path.join)(current, match);
|
|
108
|
-
} catch {
|
|
109
|
-
return void 0;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const final = (0, import_node_path.join)(current, ...rest);
|
|
113
|
-
return (0, import_node_fs.existsSync)(final) ? final : void 0;
|
|
114
|
-
}
|
|
115
|
-
function probe(cmd, envKey, versionArgs = ["--version"]) {
|
|
116
|
-
const resolvedPath = resolveCmd(cmd, envKey);
|
|
117
|
-
if (!resolvedPath) return { name: cmd, found: false };
|
|
118
|
-
const v = (0, import_node_child_process.spawnSync)(resolvedPath, versionArgs, { encoding: "utf8", shell: isWin });
|
|
119
|
-
const versionLine = (v.stdout || v.stderr).split(/\r?\n/)[0]?.trim();
|
|
120
|
-
return { name: cmd, found: true, path: resolvedPath, version: versionLine };
|
|
121
|
-
}
|
|
122
|
-
function probeOcrToolchain() {
|
|
123
|
-
return {
|
|
124
|
-
// poppler는 --version이 비표준이므로 -v 사용
|
|
125
|
-
pdftoppm: probe("pdftoppm", "KORDOC_PDFTOPPM_PATH", ["-v"]),
|
|
126
|
-
pdfinfo: probe("pdfinfo", "KORDOC_PDFINFO_PATH", ["-v"]),
|
|
127
|
-
pdftotext: probe("pdftotext", "KORDOC_PDFTOTEXT_PATH", ["-v"]),
|
|
128
|
-
soffice: probe("soffice", "KORDOC_SOFFICE_PATH", ["--version"])
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function isWSL() {
|
|
132
|
-
if (process.platform !== "linux") return false;
|
|
133
|
-
try {
|
|
134
|
-
const { readFileSync } = require("fs");
|
|
135
|
-
const version = readFileSync("/proc/version", "utf8");
|
|
136
|
-
return /microsoft|wsl/i.test(version);
|
|
137
|
-
} catch {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/setup/pm.ts
|
|
143
|
-
var import_node_child_process2 = require("child_process");
|
|
144
|
-
var isWin2 = process.platform === "win32";
|
|
145
|
-
function which2(cmd) {
|
|
146
|
-
const finder = isWin2 ? "where" : "which";
|
|
147
|
-
const r = (0, import_node_child_process2.spawnSync)(finder, [cmd], { encoding: "utf8", shell: isWin2 });
|
|
148
|
-
return r.status === 0;
|
|
149
|
-
}
|
|
150
|
-
function detectPackageManager() {
|
|
151
|
-
if (process.platform === "darwin") {
|
|
152
|
-
return which2("brew") ? "brew" : "unknown";
|
|
153
|
-
}
|
|
154
|
-
if (isWin2) {
|
|
155
|
-
if (which2("winget")) return "winget";
|
|
156
|
-
if (which2("scoop")) return "scoop";
|
|
157
|
-
if (which2("choco")) return "choco";
|
|
158
|
-
return "unknown";
|
|
159
|
-
}
|
|
160
|
-
if (which2("apt-get") || which2("apt")) return "apt";
|
|
161
|
-
if (which2("dnf")) return "dnf";
|
|
162
|
-
if (which2("yum")) return "yum";
|
|
163
|
-
if (which2("pacman")) return "pacman";
|
|
164
|
-
if (which2("zypper")) return "zypper";
|
|
165
|
-
if (which2("apk")) return "apk";
|
|
166
|
-
return "unknown";
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// src/setup/install-commands.ts
|
|
170
|
-
var POPPLER_RECIPES = {
|
|
171
|
-
brew: { pm: "brew", cmd: "brew install poppler", needsSudo: false },
|
|
172
|
-
winget: { pm: "winget", cmd: "winget install --id oschwartz10612.Poppler", needsSudo: false, notes: "\uC2E4\uC874 \uD655\uC778\uB428 (winget-pkgs 25.07.0-0)" },
|
|
173
|
-
scoop: { pm: "scoop", cmd: "scoop install poppler", needsSudo: false, notes: "main \uBC84\uD0B7 \uD3EC\uD568, PATH \uC790\uB3D9 \uB4F1\uB85D" },
|
|
174
|
-
choco: { pm: "choco", cmd: "choco install poppler -y", needsSudo: true, notes: "\uAD00\uB9AC\uC790 PowerShell \uD544\uC694" },
|
|
175
|
-
apt: { pm: "apt", cmd: "sudo apt-get install -y poppler-utils", needsSudo: true },
|
|
176
|
-
dnf: { pm: "dnf", cmd: "sudo dnf install -y poppler-utils", needsSudo: true },
|
|
177
|
-
yum: { pm: "yum", cmd: "sudo yum install -y poppler-utils", needsSudo: true },
|
|
178
|
-
pacman: { pm: "pacman", cmd: "sudo pacman -S --noconfirm poppler", needsSudo: true },
|
|
179
|
-
zypper: { pm: "zypper", cmd: "sudo zypper install -y poppler-tools", needsSudo: true },
|
|
180
|
-
apk: { pm: "apk", cmd: "apk add --no-cache poppler-utils", needsSudo: false, notes: "Alpine/Docker \uCE5C\uD654" },
|
|
181
|
-
unknown: null
|
|
182
|
-
};
|
|
183
|
-
var LIBREOFFICE_RECIPES = {
|
|
184
|
-
brew: { pm: "brew", cmd: "brew install --cask libreoffice", needsSudo: false },
|
|
185
|
-
winget: { pm: "winget", cmd: "winget install --id TheDocumentFoundation.LibreOffice", needsSudo: false, notes: "\uC2E4\uC874 \uD655\uC778\uB428" },
|
|
186
|
-
scoop: { pm: "scoop", cmd: "scoop bucket add extras && scoop install libreoffice", needsSudo: false },
|
|
187
|
-
choco: { pm: "choco", cmd: "choco install libreoffice-fresh -y", needsSudo: true, notes: "\uAD00\uB9AC\uC790 PowerShell \uD544\uC694" },
|
|
188
|
-
apt: { pm: "apt", cmd: "sudo apt-get install -y libreoffice", needsSudo: true },
|
|
189
|
-
dnf: { pm: "dnf", cmd: "sudo dnf install -y libreoffice", needsSudo: true },
|
|
190
|
-
yum: { pm: "yum", cmd: "sudo yum install -y libreoffice", needsSudo: true },
|
|
191
|
-
pacman: { pm: "pacman", cmd: "sudo pacman -S --noconfirm libreoffice-still", needsSudo: true },
|
|
192
|
-
zypper: { pm: "zypper", cmd: "sudo zypper install -y libreoffice", needsSudo: true },
|
|
193
|
-
apk: { pm: "apk", cmd: "apk add --no-cache libreoffice", needsSudo: false, notes: "Alpine \uBBF8\uC9C0\uC6D0 \uAC00\uB2A5 \u2014 node:20-slim(Debian) \uAD8C\uC7A5" },
|
|
194
|
-
unknown: null
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// src/setup/doctor.ts
|
|
198
|
-
var hr = "\u2500".repeat(45);
|
|
199
|
-
function platform() {
|
|
200
|
-
const arch = process.arch;
|
|
201
|
-
if (isWSL()) return `linux/WSL (arch: ${arch})`;
|
|
202
|
-
return `${process.platform} (arch: ${arch})`;
|
|
203
|
-
}
|
|
204
|
-
function icon(found) {
|
|
205
|
-
return found ? "\u2705" : "\u274C";
|
|
206
|
-
}
|
|
207
|
-
function pad(s, n) {
|
|
208
|
-
return s.padEnd(n);
|
|
209
|
-
}
|
|
210
|
-
function formatTool(name, status) {
|
|
211
|
-
const ico = icon(status.found);
|
|
212
|
-
if (!status.found) return ` ${pad(name, 10)} ${ico} \uCC3E\uC744 \uC218 \uC5C6\uC74C`;
|
|
213
|
-
const ver = status.version ? ` (${status.version})` : "";
|
|
214
|
-
return ` ${pad(name, 10)} ${ico} ${status.path}${ver}`;
|
|
215
|
-
}
|
|
216
|
-
function runDoctor(opts = {}) {
|
|
217
|
-
const toolchain = probeOcrToolchain();
|
|
218
|
-
const pm = detectPackageManager();
|
|
219
|
-
const popplerOk = toolchain.pdftoppm.found && toolchain.pdfinfo.found;
|
|
220
|
-
const sofficeOk = toolchain.soffice.found;
|
|
221
|
-
const missing = [];
|
|
222
|
-
if (!popplerOk) missing.push("poppler");
|
|
223
|
-
if (!sofficeOk) missing.push("libreoffice");
|
|
224
|
-
const out = opts.postinstall ? process.stderr : process.stdout;
|
|
225
|
-
out.write(`${hr}
|
|
226
|
-
`);
|
|
227
|
-
out.write(`kordoc OCR \uC758\uC874\uC131 \uC810\uAC80
|
|
228
|
-
`);
|
|
229
|
-
out.write(`${hr}
|
|
230
|
-
`);
|
|
231
|
-
out.write(`\uD50C\uB7AB\uD3FC: ${platform()}
|
|
232
|
-
`);
|
|
233
|
-
out.write(`\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${pm === "unknown" ? "\uAC10\uC9C0 \uC2E4\uD328" : pm}
|
|
234
|
-
`);
|
|
235
|
-
out.write(`Node.js: ${process.version}
|
|
236
|
-
`);
|
|
237
|
-
out.write(`
|
|
238
|
-
`);
|
|
239
|
-
out.write(`[OCR \uD30C\uC774\uD504\uB77C\uC778 \uC758\uC874\uC131]
|
|
240
|
-
`);
|
|
241
|
-
out.write(`${formatTool("pdftoppm", toolchain.pdftoppm)}
|
|
242
|
-
`);
|
|
243
|
-
out.write(`${formatTool("pdfinfo", toolchain.pdfinfo)}
|
|
244
|
-
`);
|
|
245
|
-
out.write(`${formatTool("pdftotext", toolchain.pdftotext)}
|
|
246
|
-
`);
|
|
247
|
-
out.write(`${formatTool("soffice", toolchain.soffice)}
|
|
248
|
-
`);
|
|
249
|
-
if (missing.length > 0) {
|
|
250
|
-
out.write(`
|
|
251
|
-
\uAD8C\uC7A5 \uC124\uCE58 \uBA85\uB839:
|
|
252
|
-
`);
|
|
253
|
-
if (!popplerOk) {
|
|
254
|
-
const recipe = POPPLER_RECIPES[pm];
|
|
255
|
-
if (recipe) {
|
|
256
|
-
out.write(` # poppler (pdftoppm/pdfinfo)
|
|
257
|
-
`);
|
|
258
|
-
out.write(` ${recipe.cmd}
|
|
259
|
-
`);
|
|
260
|
-
if (recipe.notes) out.write(` # ${recipe.notes}
|
|
261
|
-
`);
|
|
262
|
-
} else {
|
|
263
|
-
out.write(` poppler: OS\uC5D0 \uB9DE\uB294 \uBC29\uBC95\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694 (README \uCC38\uACE0)
|
|
264
|
-
`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
if (!sofficeOk) {
|
|
268
|
-
const recipe = LIBREOFFICE_RECIPES[pm];
|
|
269
|
-
if (recipe) {
|
|
270
|
-
out.write(`
|
|
271
|
-
# LibreOffice (soffice)
|
|
272
|
-
`);
|
|
273
|
-
out.write(` ${recipe.cmd}
|
|
274
|
-
`);
|
|
275
|
-
if (recipe.notes) out.write(` # ${recipe.notes}
|
|
276
|
-
`);
|
|
277
|
-
} else {
|
|
278
|
-
out.write(` LibreOffice: OS\uC5D0 \uB9DE\uB294 \uBC29\uBC95\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694 (README \uCC38\uACE0)
|
|
279
|
-
`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
if (process.platform === "win32") {
|
|
283
|
-
out.write(`
|
|
284
|
-
\u26A0\uFE0F \uC124\uCE58 \uD6C4 \uC0C8 \uD130\uBBF8\uB110\uC744 \uC5F4\uAC70\uB098 PATH\uB97C \uAC31\uC2E0\uD574\uC57C \uD569\uB2C8\uB2E4.
|
|
285
|
-
`);
|
|
286
|
-
out.write(` PowerShell: $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
287
|
-
`);
|
|
288
|
-
}
|
|
289
|
-
out.write(`
|
|
290
|
-
\uD658\uACBD\uBCC0\uC218\uB85C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC9C0\uC815\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4:
|
|
291
|
-
`);
|
|
292
|
-
out.write(` KORDOC_PDFTOPPM_PATH, KORDOC_PDFINFO_PATH, KORDOC_SOFFICE_PATH
|
|
293
|
-
`);
|
|
294
|
-
out.write(`
|
|
295
|
-
\uC790\uC138\uD55C \uC810\uAC80: npx kordoc doctor
|
|
296
|
-
`);
|
|
297
|
-
}
|
|
298
|
-
out.write(`${hr}
|
|
299
|
-
`);
|
|
300
|
-
const summary = missing.length === 0 ? "\uBAA8\uB4E0 \uC758\uC874\uC131\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 \u2705" : `${missing.length}\uAC1C \uACB0\uC190 \u2014 OCR \uBAA8\uB4DC \uC0AC\uC6A9 \uC2DC \uCC28\uB2E8\uB429\uB2C8\uB2E4 (${missing.join(", ")})`;
|
|
301
|
-
out.write(`\uC694\uC57D: ${summary}
|
|
302
|
-
`);
|
|
303
|
-
return { ok: missing.length === 0, missing, toolchain, pm };
|
|
304
|
-
}
|
|
305
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
306
|
-
0 && (module.exports = {
|
|
307
|
-
runDoctor
|
|
308
|
-
});
|