@waifucave/discord-waifus 1.5.154
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/LICENSE +21 -0
- package/README.md +230 -0
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/assets/ocr/eng.traineddata +0 -0
- package/bin/waifus.mjs +36 -0
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/errors.js +23 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/server.d.ts +19 -0
- package/dist/api/server.js +1290 -0
- package/dist/api/server.js.map +1 -0
- package/dist/backend/logger.d.ts +20 -0
- package/dist/backend/logger.js +65 -0
- package/dist/backend/logger.js.map +1 -0
- package/dist/backend/migrations.d.ts +4 -0
- package/dist/backend/migrations.js +521 -0
- package/dist/backend/migrations.js.map +1 -0
- package/dist/backend/redaction.d.ts +1 -0
- package/dist/backend/redaction.js +22 -0
- package/dist/backend/redaction.js.map +1 -0
- package/dist/backend/runtime.d.ts +30 -0
- package/dist/backend/runtime.js +49 -0
- package/dist/backend/runtime.js.map +1 -0
- package/dist/backend/server.d.ts +18 -0
- package/dist/backend/server.js +581 -0
- package/dist/backend/server.js.map +1 -0
- package/dist/cli/commands.d.ts +35 -0
- package/dist/cli/commands.js +510 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +12 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/parser.d.ts +10 -0
- package/dist/cli/parser.js +64 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/config/appConfig.d.ts +3 -0
- package/dist/config/appConfig.js +27 -0
- package/dist/config/appConfig.js.map +1 -0
- package/dist/config/layout.d.ts +13 -0
- package/dist/config/layout.js +173 -0
- package/dist/config/layout.js.map +1 -0
- package/dist/config/paths.d.ts +5 -0
- package/dist/config/paths.js +20 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/prebuiltWaifus.d.ts +3 -0
- package/dist/config/prebuiltWaifus.js +106 -0
- package/dist/config/prebuiltWaifus.js.map +1 -0
- package/dist/discord/client.d.ts +394 -0
- package/dist/discord/client.js +1196 -0
- package/dist/discord/client.js.map +1 -0
- package/dist/discord/contextBuilder.d.ts +15 -0
- package/dist/discord/contextBuilder.js +18 -0
- package/dist/discord/contextBuilder.js.map +1 -0
- package/dist/discord/intents.d.ts +5 -0
- package/dist/discord/intents.js +21 -0
- package/dist/discord/intents.js.map +1 -0
- package/dist/discord/memberCache.d.ts +12 -0
- package/dist/discord/memberCache.js +55 -0
- package/dist/discord/memberCache.js.map +1 -0
- package/dist/discord/normalization.d.ts +34 -0
- package/dist/discord/normalization.js +330 -0
- package/dist/discord/normalization.js.map +1 -0
- package/dist/discord/rateLimit.d.ts +9 -0
- package/dist/discord/rateLimit.js +11 -0
- package/dist/discord/rateLimit.js.map +1 -0
- package/dist/orchestration/context.d.ts +85 -0
- package/dist/orchestration/context.js +200 -0
- package/dist/orchestration/context.js.map +1 -0
- package/dist/orchestration/decisions.d.ts +52 -0
- package/dist/orchestration/decisions.js +61 -0
- package/dist/orchestration/decisions.js.map +1 -0
- package/dist/orchestration/messageSplit.d.ts +6 -0
- package/dist/orchestration/messageSplit.js +118 -0
- package/dist/orchestration/messageSplit.js.map +1 -0
- package/dist/orchestration/ocr.d.ts +43 -0
- package/dist/orchestration/ocr.js +504 -0
- package/dist/orchestration/ocr.js.map +1 -0
- package/dist/orchestration/ocrPackages.d.ts +24 -0
- package/dist/orchestration/ocrPackages.js +85 -0
- package/dist/orchestration/ocrPackages.js.map +1 -0
- package/dist/orchestration/promptBlocks.d.ts +28 -0
- package/dist/orchestration/promptBlocks.js +232 -0
- package/dist/orchestration/promptBlocks.js.map +1 -0
- package/dist/orchestration/replyQuote.d.ts +7 -0
- package/dist/orchestration/replyQuote.js +198 -0
- package/dist/orchestration/replyQuote.js.map +1 -0
- package/dist/orchestration/reviewer.d.ts +5 -0
- package/dist/orchestration/reviewer.js +5 -0
- package/dist/orchestration/reviewer.js.map +1 -0
- package/dist/orchestration/runtime.d.ts +152 -0
- package/dist/orchestration/runtime.js +3535 -0
- package/dist/orchestration/runtime.js.map +1 -0
- package/dist/orchestration/session.d.ts +30 -0
- package/dist/orchestration/session.js +41 -0
- package/dist/orchestration/session.js.map +1 -0
- package/dist/orchestration/stageManager.d.ts +42 -0
- package/dist/orchestration/stageManager.js +50 -0
- package/dist/orchestration/stageManager.js.map +1 -0
- package/dist/providers/catalog.d.ts +6 -0
- package/dist/providers/catalog.js +212 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/pipelines.d.ts +13 -0
- package/dist/providers/pipelines.js +2388 -0
- package/dist/providers/pipelines.js.map +1 -0
- package/dist/providers/types.d.ts +91 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/shared/queryLog.d.ts +39 -0
- package/dist/shared/queryLog.js +80 -0
- package/dist/shared/queryLog.js.map +1 -0
- package/dist/shared/schemas/common.d.ts +21 -0
- package/dist/shared/schemas/common.js +27 -0
- package/dist/shared/schemas/common.js.map +1 -0
- package/dist/shared/schemas/config.d.ts +57 -0
- package/dist/shared/schemas/config.js +49 -0
- package/dist/shared/schemas/config.js.map +1 -0
- package/dist/shared/schemas/domain.d.ts +825 -0
- package/dist/shared/schemas/domain.js +444 -0
- package/dist/shared/schemas/domain.js.map +1 -0
- package/dist/storage/atomic.d.ts +6 -0
- package/dist/storage/atomic.js +60 -0
- package/dist/storage/atomic.js.map +1 -0
- package/dist/storage/errors.d.ts +12 -0
- package/dist/storage/errors.js +23 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/locks.d.ts +9 -0
- package/dist/storage/locks.js +42 -0
- package/dist/storage/locks.js.map +1 -0
- package/dist/storage/storageService.d.ts +25 -0
- package/dist/storage/storageService.js +66 -0
- package/dist/storage/storageService.js.map +1 -0
- package/dist-frontend/assets/index-B2bB3_2r.js +39 -0
- package/dist-frontend/assets/index-B2bB3_2r.js.map +1 -0
- package/dist-frontend/assets/index-Dj1L8Px-.css +1 -0
- package/dist-frontend/index.html +14 -0
- package/docs/api.md +162 -0
- package/docs/old-orchestrator-prompt.md +123 -0
- package/package.json +81 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { access, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { createWorker, OEM, PSM } from "tesseract.js";
|
|
7
|
+
import { appDataPath } from "../config/paths.js";
|
|
8
|
+
import { BUNDLED_OCR_LANGUAGE, bundledOcrLangPath } from "./ocrPackages.js";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
// WASM recognition is much slower than the old native binary, and cold worker
|
|
11
|
+
// init can take several seconds. Worker init is awaited before the recognize
|
|
12
|
+
// timer starts, so this floor only bounds the recognize pass itself — and it is
|
|
13
|
+
// kept separate from `config.timeoutMs` (which also governs image downloads and
|
|
14
|
+
// the Apple Vision / system-tesseract engines).
|
|
15
|
+
const BUNDLED_OCR_RECOGNIZE_TIMEOUT_MS = 15_000;
|
|
16
|
+
const CACHE_SCHEMA_VERSION = 1;
|
|
17
|
+
const TEMP_FILE_TTL_MS = 60 * 60 * 1000;
|
|
18
|
+
const CLEANUP_INTERVAL_MS = 10 * 60 * 1000;
|
|
19
|
+
const APPLE_VISION_HELPER_SOURCE = String.raw `import AppKit
|
|
20
|
+
import Foundation
|
|
21
|
+
import Vision
|
|
22
|
+
|
|
23
|
+
let args = CommandLine.arguments
|
|
24
|
+
guard args.count >= 3 else {
|
|
25
|
+
FileHandle.standardError.write(Data("usage: ocr-helper <image-path> <max-chars>\n".utf8))
|
|
26
|
+
exit(2)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let imagePath = args[1]
|
|
30
|
+
let maxChars = Int(args[2]) ?? 1500
|
|
31
|
+
guard let image = NSImage(contentsOfFile: imagePath),
|
|
32
|
+
let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
|
|
33
|
+
exit(3)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let request = VNRecognizeTextRequest()
|
|
37
|
+
request.recognitionLevel = .fast
|
|
38
|
+
request.usesLanguageCorrection = false
|
|
39
|
+
if #available(macOS 11.0, *) {
|
|
40
|
+
request.recognitionLanguages = ["en-US"]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
|
|
44
|
+
try handler.perform([request])
|
|
45
|
+
let text = (request.results ?? [])
|
|
46
|
+
.compactMap { $0.topCandidates(1).first?.string }
|
|
47
|
+
.joined(separator: "\n")
|
|
48
|
+
|
|
49
|
+
print(String(text.prefix(maxChars)))
|
|
50
|
+
`;
|
|
51
|
+
export class ImageOcrService {
|
|
52
|
+
options;
|
|
53
|
+
helperPathPromise;
|
|
54
|
+
tesseractWorkerPromise;
|
|
55
|
+
lastCleanupAt = 0;
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
}
|
|
59
|
+
/** Terminate the reused tesseract.js worker (if any). Call on shutdown. */
|
|
60
|
+
async dispose() {
|
|
61
|
+
await this.disposeTesseractWorker();
|
|
62
|
+
}
|
|
63
|
+
async cleanupExpired() {
|
|
64
|
+
await Promise.all([
|
|
65
|
+
removeExpiredCacheFiles(this.cacheResultsDir()),
|
|
66
|
+
removeOldFiles(this.tmpOcrDir(), TEMP_FILE_TTL_MS)
|
|
67
|
+
]);
|
|
68
|
+
this.lastCleanupAt = Date.now();
|
|
69
|
+
}
|
|
70
|
+
async enrichMessages(messages, options = {}) {
|
|
71
|
+
if (!this.options.config.enabled || this.options.config.maxImagesPerModelCall <= 0) {
|
|
72
|
+
return messages;
|
|
73
|
+
}
|
|
74
|
+
await this.cleanupIfDue();
|
|
75
|
+
let remaining = this.options.config.maxImagesPerModelCall;
|
|
76
|
+
let changed = false;
|
|
77
|
+
const nextMessages = [];
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
if (!message.images?.length || remaining <= 0) {
|
|
80
|
+
nextMessages.push(message);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const nextImages = [];
|
|
84
|
+
for (const image of message.images) {
|
|
85
|
+
if (remaining <= 0) {
|
|
86
|
+
nextImages.push(image);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
remaining -= 1;
|
|
90
|
+
const text = await this.extractImageText(image, options);
|
|
91
|
+
if (text) {
|
|
92
|
+
changed = true;
|
|
93
|
+
nextImages.push({ ...image, ocrText: clipOcrText(text, this.options.config.maxTextCharsPerImage) });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
nextImages.push(image);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
nextMessages.push({ ...message, images: nextImages });
|
|
100
|
+
}
|
|
101
|
+
return changed ? nextMessages : messages;
|
|
102
|
+
}
|
|
103
|
+
async extractImageText(image, options) {
|
|
104
|
+
const key = cacheKeyForImage(image);
|
|
105
|
+
const cached = await this.readCache(key);
|
|
106
|
+
if (cached !== undefined) {
|
|
107
|
+
return cached || undefined;
|
|
108
|
+
}
|
|
109
|
+
const tempPath = await this.downloadImage(image, key, options.signal).catch((error) => {
|
|
110
|
+
this.options.logger.warn("OCR image download failed", { message: errorMessage(error) });
|
|
111
|
+
return undefined;
|
|
112
|
+
});
|
|
113
|
+
if (!tempPath)
|
|
114
|
+
return undefined;
|
|
115
|
+
try {
|
|
116
|
+
const result = await this.runConfiguredEngine(tempPath);
|
|
117
|
+
await this.writeCache(key, result.text, result.engine);
|
|
118
|
+
return result.text || undefined;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.options.logger.warn("OCR extraction failed", { message: errorMessage(error) });
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
await rm(tempPath, { force: true }).catch(() => undefined);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async runConfiguredEngine(imagePath) {
|
|
129
|
+
const engine = this.options.config.engine;
|
|
130
|
+
if (engine === "apple-vision") {
|
|
131
|
+
return {
|
|
132
|
+
engine,
|
|
133
|
+
text: await this.runAppleVision(imagePath)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (engine === "bundled-tesseract") {
|
|
137
|
+
return {
|
|
138
|
+
engine,
|
|
139
|
+
text: await this.runBundledTesseract(imagePath)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (engine === "system-tesseract") {
|
|
143
|
+
return {
|
|
144
|
+
engine,
|
|
145
|
+
text: await this.runSystemTesseract(imagePath)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const errors = [];
|
|
149
|
+
if (process.platform === "darwin") {
|
|
150
|
+
try {
|
|
151
|
+
return {
|
|
152
|
+
engine: "apple-vision",
|
|
153
|
+
text: await this.runAppleVision(imagePath)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
errors.push(`apple-vision: ${errorMessage(error)}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
return {
|
|
162
|
+
engine: "bundled-tesseract",
|
|
163
|
+
text: await this.runBundledTesseract(imagePath)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
errors.push(`bundled-tesseract: ${errorMessage(error)}`);
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
return {
|
|
171
|
+
engine: "system-tesseract",
|
|
172
|
+
text: await this.runSystemTesseract(imagePath)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
errors.push(`system-tesseract: ${errorMessage(error)}`);
|
|
177
|
+
}
|
|
178
|
+
throw new Error(errors.join("; ") || "No OCR engine is available.");
|
|
179
|
+
}
|
|
180
|
+
async runAppleVision(imagePath) {
|
|
181
|
+
const helperPath = await this.ensureAppleVisionHelper();
|
|
182
|
+
const { stdout } = await execFileAsync(helperPath, [imagePath, String(this.options.config.maxTextCharsPerImage)], {
|
|
183
|
+
timeout: this.options.config.timeoutMs,
|
|
184
|
+
maxBuffer: 512 * 1024
|
|
185
|
+
});
|
|
186
|
+
return sanitizeOcrText(stdout, this.options.config.maxTextCharsPerImage);
|
|
187
|
+
}
|
|
188
|
+
async runBundledTesseract(imagePath) {
|
|
189
|
+
const worker = await this.ensureTesseractWorker();
|
|
190
|
+
const timeoutMs = Math.max(this.options.config.timeoutMs, BUNDLED_OCR_RECOGNIZE_TIMEOUT_MS);
|
|
191
|
+
const recognition = worker.recognize(imagePath);
|
|
192
|
+
// On timeout we discard this promise and terminate the worker; keep a no-op
|
|
193
|
+
// handler so a late rejection does not surface as an unhandledRejection.
|
|
194
|
+
void recognition.catch(() => undefined);
|
|
195
|
+
try {
|
|
196
|
+
const { data } = await withTimeout(recognition, timeoutMs, "Bundled OCR recognition timed out.");
|
|
197
|
+
return sanitizeOcrText(data.text ?? "", this.options.config.maxTextCharsPerImage);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
// A worker stays busy on a job until it resolves; a timed-out or errored
|
|
201
|
+
// job would block every subsequent image. Drop the worker so the next call
|
|
202
|
+
// starts from a clean one.
|
|
203
|
+
await this.disposeTesseractWorker();
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async ensureTesseractWorker() {
|
|
208
|
+
this.tesseractWorkerPromise ??= this.createTesseractWorker();
|
|
209
|
+
try {
|
|
210
|
+
return await this.tesseractWorkerPromise;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.tesseractWorkerPromise = undefined;
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async createTesseractWorker() {
|
|
218
|
+
const worker = await createWorker(BUNDLED_OCR_LANGUAGE, OEM.LSTM_ONLY, {
|
|
219
|
+
langPath: bundledOcrLangPath(),
|
|
220
|
+
gzip: false,
|
|
221
|
+
cacheMethod: "none"
|
|
222
|
+
});
|
|
223
|
+
// PSM 11 (sparse text) matches the old native `--psm 11` and suits the
|
|
224
|
+
// scattered text typical of screenshots.
|
|
225
|
+
await worker.setParameters({ tessedit_pageseg_mode: PSM.SPARSE_TEXT });
|
|
226
|
+
return worker;
|
|
227
|
+
}
|
|
228
|
+
async disposeTesseractWorker() {
|
|
229
|
+
const pending = this.tesseractWorkerPromise;
|
|
230
|
+
this.tesseractWorkerPromise = undefined;
|
|
231
|
+
if (!pending)
|
|
232
|
+
return;
|
|
233
|
+
await pending.then((worker) => worker.terminate()).catch(() => undefined);
|
|
234
|
+
}
|
|
235
|
+
async runSystemTesseract(imagePath) {
|
|
236
|
+
await ensureCommandAvailable("tesseract");
|
|
237
|
+
const { stdout } = await execFileAsync("tesseract", [imagePath, "stdout", "-l", "eng", "--psm", "11"], {
|
|
238
|
+
timeout: this.options.config.timeoutMs,
|
|
239
|
+
maxBuffer: 512 * 1024
|
|
240
|
+
});
|
|
241
|
+
return sanitizeOcrText(stdout, this.options.config.maxTextCharsPerImage);
|
|
242
|
+
}
|
|
243
|
+
async ensureAppleVisionHelper() {
|
|
244
|
+
if (process.platform !== "darwin") {
|
|
245
|
+
throw new Error("Apple Vision OCR is only available on macOS.");
|
|
246
|
+
}
|
|
247
|
+
if (this.helperPathPromise) {
|
|
248
|
+
const helperPath = await this.helperPathPromise;
|
|
249
|
+
try {
|
|
250
|
+
await access(helperPath);
|
|
251
|
+
return helperPath;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
this.helperPathPromise = undefined;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
this.helperPathPromise ??= this.buildAppleVisionHelper();
|
|
258
|
+
return this.helperPathPromise;
|
|
259
|
+
}
|
|
260
|
+
async buildAppleVisionHelper() {
|
|
261
|
+
const binDir = this.cacheBinDir();
|
|
262
|
+
const moduleCacheDir = this.swiftModuleCacheDir();
|
|
263
|
+
await Promise.all([
|
|
264
|
+
mkdir(binDir, { recursive: true }),
|
|
265
|
+
mkdir(moduleCacheDir, { recursive: true })
|
|
266
|
+
]);
|
|
267
|
+
const helperPath = path.join(binDir, "apple-vision-ocr");
|
|
268
|
+
const sourcePath = path.join(binDir, "apple-vision-ocr.swift");
|
|
269
|
+
await writeFile(sourcePath, APPLE_VISION_HELPER_SOURCE, { mode: 0o600 });
|
|
270
|
+
await execFileAsync("xcrun", [
|
|
271
|
+
"swiftc",
|
|
272
|
+
"-module-cache-path",
|
|
273
|
+
moduleCacheDir,
|
|
274
|
+
"-o",
|
|
275
|
+
helperPath,
|
|
276
|
+
sourcePath
|
|
277
|
+
], {
|
|
278
|
+
timeout: Math.max(this.options.config.timeoutMs * 4, 10_000),
|
|
279
|
+
maxBuffer: 512 * 1024,
|
|
280
|
+
env: {
|
|
281
|
+
...process.env,
|
|
282
|
+
CLANG_MODULE_CACHE_PATH: moduleCacheDir,
|
|
283
|
+
SWIFT_MODULE_CACHE_PATH: moduleCacheDir,
|
|
284
|
+
TMPDIR: appDataPath(this.options.dataRoot, "tmp")
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
return helperPath;
|
|
288
|
+
}
|
|
289
|
+
async downloadImage(image, key, signal) {
|
|
290
|
+
await mkdir(this.tmpOcrDir(), { recursive: true });
|
|
291
|
+
const contentLength = Number(image.contentLengthBytes ?? 0);
|
|
292
|
+
if (contentLength > this.options.config.maxImageBytes) {
|
|
293
|
+
throw new Error("Image exceeds OCR size limit.");
|
|
294
|
+
}
|
|
295
|
+
const controller = new AbortController();
|
|
296
|
+
const timer = setTimeout(() => controller.abort(new Error("OCR image download timed out.")), this.options.config.timeoutMs);
|
|
297
|
+
const onAbort = () => controller.abort(signal?.reason);
|
|
298
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
299
|
+
try {
|
|
300
|
+
const response = await fetch(image.url, { signal: controller.signal });
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
throw new Error(`Image download returned HTTP ${response.status}.`);
|
|
303
|
+
}
|
|
304
|
+
const buffer = await readLimitedResponse(response, this.options.config.maxImageBytes);
|
|
305
|
+
const tempPath = path.join(this.tmpOcrDir(), `${key}-${randomUUID()}${extensionForImage(image, response)}`);
|
|
306
|
+
await writeFile(tempPath, buffer, { mode: 0o600 });
|
|
307
|
+
return tempPath;
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
clearTimeout(timer);
|
|
311
|
+
signal?.removeEventListener("abort", onAbort);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async readCache(key) {
|
|
315
|
+
try {
|
|
316
|
+
const raw = await readFile(this.cachePath(key), "utf8");
|
|
317
|
+
const parsed = JSON.parse(raw);
|
|
318
|
+
if (parsed.schemaVersion !== CACHE_SCHEMA_VERSION ||
|
|
319
|
+
typeof parsed.text !== "string" ||
|
|
320
|
+
typeof parsed.expiresAt !== "string") {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
if (new Date(parsed.expiresAt).getTime() <= Date.now()) {
|
|
324
|
+
await rm(this.cachePath(key), { force: true });
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
return parsed.text;
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
if (error.code !== "ENOENT") {
|
|
331
|
+
this.options.logger.warn("OCR cache read failed", { message: errorMessage(error) });
|
|
332
|
+
}
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async writeCache(key, text, engine) {
|
|
337
|
+
await mkdir(this.cacheResultsDir(), { recursive: true });
|
|
338
|
+
const now = new Date();
|
|
339
|
+
const entry = {
|
|
340
|
+
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
341
|
+
createdAt: now.toISOString(),
|
|
342
|
+
expiresAt: new Date(now.getTime() + this.options.config.cacheTtlHours * 60 * 60 * 1000).toISOString(),
|
|
343
|
+
engine,
|
|
344
|
+
text
|
|
345
|
+
};
|
|
346
|
+
await writeFile(this.cachePath(key), JSON.stringify(entry, null, 2) + "\n", { mode: 0o600 });
|
|
347
|
+
await this.cleanupIfDue();
|
|
348
|
+
}
|
|
349
|
+
async cleanupIfDue() {
|
|
350
|
+
if (Date.now() - this.lastCleanupAt < CLEANUP_INTERVAL_MS)
|
|
351
|
+
return;
|
|
352
|
+
await this.cleanupExpired().catch((error) => {
|
|
353
|
+
this.options.logger.warn("OCR cleanup failed", { message: errorMessage(error) });
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
cacheBinDir() {
|
|
357
|
+
return appDataPath(this.options.dataRoot, "cache", "ocr", "bin");
|
|
358
|
+
}
|
|
359
|
+
cacheResultsDir() {
|
|
360
|
+
return appDataPath(this.options.dataRoot, "cache", "ocr", "results");
|
|
361
|
+
}
|
|
362
|
+
cachePath(key) {
|
|
363
|
+
return path.join(this.cacheResultsDir(), `${key}.json`);
|
|
364
|
+
}
|
|
365
|
+
tmpOcrDir() {
|
|
366
|
+
return appDataPath(this.options.dataRoot, "tmp", "ocr");
|
|
367
|
+
}
|
|
368
|
+
swiftModuleCacheDir() {
|
|
369
|
+
return appDataPath(this.options.dataRoot, "tmp", "swift-module-cache");
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
export async function clearOcrArtifacts(dataRoot) {
|
|
373
|
+
await Promise.all([
|
|
374
|
+
rm(appDataPath(dataRoot, "cache", "ocr"), { recursive: true, force: true }),
|
|
375
|
+
rm(appDataPath(dataRoot, "tmp", "ocr"), { recursive: true, force: true })
|
|
376
|
+
]);
|
|
377
|
+
await Promise.all([
|
|
378
|
+
mkdir(appDataPath(dataRoot, "cache", "ocr", "bin"), { recursive: true }),
|
|
379
|
+
mkdir(appDataPath(dataRoot, "cache", "ocr", "results"), { recursive: true }),
|
|
380
|
+
mkdir(appDataPath(dataRoot, "tmp", "ocr"), { recursive: true })
|
|
381
|
+
]);
|
|
382
|
+
return { cleared: true };
|
|
383
|
+
}
|
|
384
|
+
function cacheKeyForImage(image) {
|
|
385
|
+
return createHash("sha256").update(image.url).digest("hex");
|
|
386
|
+
}
|
|
387
|
+
function clipOcrText(text, maxChars) {
|
|
388
|
+
return text.length > maxChars ? text.slice(0, maxChars) : text;
|
|
389
|
+
}
|
|
390
|
+
function sanitizeOcrText(text, maxChars) {
|
|
391
|
+
return text
|
|
392
|
+
.replace(/\r\n?/g, "\n")
|
|
393
|
+
.replace(/[\t\f\v ]+/g, " ")
|
|
394
|
+
.split("\n")
|
|
395
|
+
.map((line) => line.trim())
|
|
396
|
+
.filter(Boolean)
|
|
397
|
+
.join("\n")
|
|
398
|
+
.slice(0, maxChars)
|
|
399
|
+
.trim();
|
|
400
|
+
}
|
|
401
|
+
async function readLimitedResponse(response, maxBytes) {
|
|
402
|
+
if (!response.body) {
|
|
403
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
404
|
+
if (buffer.byteLength > maxBytes)
|
|
405
|
+
throw new Error("Image exceeds OCR size limit.");
|
|
406
|
+
return buffer;
|
|
407
|
+
}
|
|
408
|
+
const reader = response.body.getReader();
|
|
409
|
+
const chunks = [];
|
|
410
|
+
let total = 0;
|
|
411
|
+
while (true) {
|
|
412
|
+
const { done, value } = await reader.read();
|
|
413
|
+
if (done)
|
|
414
|
+
break;
|
|
415
|
+
total += value.byteLength;
|
|
416
|
+
if (total > maxBytes) {
|
|
417
|
+
await reader.cancel();
|
|
418
|
+
throw new Error("Image exceeds OCR size limit.");
|
|
419
|
+
}
|
|
420
|
+
chunks.push(value);
|
|
421
|
+
}
|
|
422
|
+
return Buffer.concat(chunks);
|
|
423
|
+
}
|
|
424
|
+
function extensionForImage(image, response) {
|
|
425
|
+
const contentType = (image.contentType ?? response.headers.get("content-type") ?? "").toLowerCase();
|
|
426
|
+
if (contentType.includes("png"))
|
|
427
|
+
return ".png";
|
|
428
|
+
if (contentType.includes("jpeg") || contentType.includes("jpg"))
|
|
429
|
+
return ".jpg";
|
|
430
|
+
if (contentType.includes("webp"))
|
|
431
|
+
return ".webp";
|
|
432
|
+
if (contentType.includes("gif"))
|
|
433
|
+
return ".gif";
|
|
434
|
+
const match = new URL(image.url).pathname.match(/\.(png|jpe?g|webp|gif)$/iu);
|
|
435
|
+
return match ? match[0].toLowerCase() : ".png";
|
|
436
|
+
}
|
|
437
|
+
async function ensureCommandAvailable(command) {
|
|
438
|
+
try {
|
|
439
|
+
await execFileAsync(command, ["--version"], { timeout: 1_000, maxBuffer: 64 * 1024 });
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
throw new Error(`${command} is not available.`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function removeExpiredCacheFiles(dir) {
|
|
446
|
+
const entries = await safeReaddir(dir);
|
|
447
|
+
await Promise.all(entries.map(async (entry) => {
|
|
448
|
+
const filePath = path.join(dir, entry);
|
|
449
|
+
if (!entry.endsWith(".json"))
|
|
450
|
+
return;
|
|
451
|
+
try {
|
|
452
|
+
const raw = await readFile(filePath, "utf8");
|
|
453
|
+
const parsed = JSON.parse(raw);
|
|
454
|
+
if (typeof parsed.expiresAt !== "string" || new Date(parsed.expiresAt).getTime() <= Date.now()) {
|
|
455
|
+
await rm(filePath, { force: true });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
await rm(filePath, { force: true });
|
|
460
|
+
}
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
async function removeOldFiles(dir, maxAgeMs) {
|
|
464
|
+
const entries = await safeReaddir(dir);
|
|
465
|
+
await Promise.all(entries.map(async (entry) => {
|
|
466
|
+
const filePath = path.join(dir, entry);
|
|
467
|
+
try {
|
|
468
|
+
const info = await stat(filePath);
|
|
469
|
+
if (info.isFile() && Date.now() - info.mtimeMs > maxAgeMs) {
|
|
470
|
+
await rm(filePath, { force: true });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
await rm(filePath, { force: true }).catch(() => undefined);
|
|
475
|
+
}
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
async function safeReaddir(dir) {
|
|
479
|
+
try {
|
|
480
|
+
return await readdir(dir);
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
if (error.code === "ENOENT")
|
|
484
|
+
return [];
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function withTimeout(promise, timeoutMs, message) {
|
|
489
|
+
let timer;
|
|
490
|
+
const timeout = new Promise((_, reject) => {
|
|
491
|
+
timer = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
492
|
+
});
|
|
493
|
+
try {
|
|
494
|
+
return await Promise.race([promise, timeout]);
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
if (timer)
|
|
498
|
+
clearTimeout(timer);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function errorMessage(error) {
|
|
502
|
+
return error instanceof Error ? error.message : String(error);
|
|
503
|
+
}
|
|
504
|
+
//# sourceMappingURL=ocr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocr.js","sourceRoot":"","sources":["../../src/orchestration/ocr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const BUNDLED_OCR_LANGUAGE = "eng";
|
|
2
|
+
export type BundledOcrDiagnostics = {
|
|
3
|
+
supported: boolean;
|
|
4
|
+
engine: "tesseract.js";
|
|
5
|
+
available: boolean;
|
|
6
|
+
langPath?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
coreVersion?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Directory that holds the bundled `eng.traineddata`. The path is identical
|
|
13
|
+
* relative to this module in both a source checkout (`src/orchestration/...`)
|
|
14
|
+
* and the published package (`dist/orchestration/...`): two levels up to the
|
|
15
|
+
* package root, then `assets/ocr`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function bundledOcrLangPath(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Probe whether bundled (WASM) OCR can run fully offline on this machine: the
|
|
20
|
+
* model ships in the package, the tesseract.js runtime resolves, and a worker
|
|
21
|
+
* can initialize (loading the WASM core + the bundled model) without a recognize
|
|
22
|
+
* pass. Bundled OCR is cross-platform now, so `supported` is always true.
|
|
23
|
+
*/
|
|
24
|
+
export declare function diagnoseBundledOcr(): Promise<BundledOcrDiagnostics>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createWorker, OEM } from "tesseract.js";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
export const BUNDLED_OCR_LANGUAGE = "eng";
|
|
8
|
+
const BUNDLED_OCR_MODEL_FILE = `${BUNDLED_OCR_LANGUAGE}.traineddata`;
|
|
9
|
+
/**
|
|
10
|
+
* Directory that holds the bundled `eng.traineddata`. The path is identical
|
|
11
|
+
* relative to this module in both a source checkout (`src/orchestration/...`)
|
|
12
|
+
* and the published package (`dist/orchestration/...`): two levels up to the
|
|
13
|
+
* package root, then `assets/ocr`.
|
|
14
|
+
*/
|
|
15
|
+
export function bundledOcrLangPath() {
|
|
16
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
return path.resolve(here, "..", "..", "assets", "ocr");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Probe whether bundled (WASM) OCR can run fully offline on this machine: the
|
|
21
|
+
* model ships in the package, the tesseract.js runtime resolves, and a worker
|
|
22
|
+
* can initialize (loading the WASM core + the bundled model) without a recognize
|
|
23
|
+
* pass. Bundled OCR is cross-platform now, so `supported` is always true.
|
|
24
|
+
*/
|
|
25
|
+
export async function diagnoseBundledOcr() {
|
|
26
|
+
const langPath = bundledOcrLangPath();
|
|
27
|
+
try {
|
|
28
|
+
await access(path.join(langPath, BUNDLED_OCR_MODEL_FILE));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {
|
|
32
|
+
supported: true,
|
|
33
|
+
engine: "tesseract.js",
|
|
34
|
+
available: false,
|
|
35
|
+
langPath,
|
|
36
|
+
error: `Bundled OCR model not found at ${path.join(langPath, BUNDLED_OCR_MODEL_FILE)}.`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
let version;
|
|
40
|
+
let coreVersion;
|
|
41
|
+
try {
|
|
42
|
+
version = require("tesseract.js/package.json").version;
|
|
43
|
+
coreVersion = require("tesseract.js-core/package.json").version;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return {
|
|
47
|
+
supported: true,
|
|
48
|
+
engine: "tesseract.js",
|
|
49
|
+
available: false,
|
|
50
|
+
langPath,
|
|
51
|
+
error: `tesseract.js runtime is not resolvable: ${errorMessage(error)}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const worker = await createWorker(BUNDLED_OCR_LANGUAGE, OEM.LSTM_ONLY, {
|
|
56
|
+
langPath,
|
|
57
|
+
gzip: false,
|
|
58
|
+
cacheMethod: "none"
|
|
59
|
+
});
|
|
60
|
+
await worker.terminate();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
supported: true,
|
|
65
|
+
engine: "tesseract.js",
|
|
66
|
+
available: false,
|
|
67
|
+
langPath,
|
|
68
|
+
version,
|
|
69
|
+
coreVersion,
|
|
70
|
+
error: errorMessage(error)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
supported: true,
|
|
75
|
+
engine: "tesseract.js",
|
|
76
|
+
available: true,
|
|
77
|
+
langPath,
|
|
78
|
+
version,
|
|
79
|
+
coreVersion
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function errorMessage(error) {
|
|
83
|
+
return error instanceof Error ? error.message : String(error);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ocrPackages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocrPackages.js","sourceRoot":"","sources":["../../src/orchestration/ocrPackages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAC1C,MAAM,sBAAsB,GAAG,GAAG,oBAAoB,cAAc,CAAC;AAYrE;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,KAAK;YAChB,QAAQ;YACR,KAAK,EAAE,kCAAkC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,sBAAsB,CAAC,GAAG;SACxF,CAAC;IACJ,CAAC;IAED,IAAI,OAA2B,CAAC;IAChC,IAAI,WAA+B,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,GAAI,OAAO,CAAC,2BAA2B,CAA0B,CAAC,OAAO,CAAC;QACjF,WAAW,GAAI,OAAO,CAAC,gCAAgC,CAA0B,CAAC,OAAO,CAAC;IAC5F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,KAAK;YAChB,QAAQ;YACR,KAAK,EAAE,2CAA2C,YAAY,CAAC,KAAK,CAAC,EAAE;SACxE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,oBAAoB,EAAE,GAAG,CAAC,SAAS,EAAE;YACrE,QAAQ;YACR,IAAI,EAAE,KAAK;YACX,WAAW,EAAE,MAAM;SACpB,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,KAAK;YAChB,QAAQ;YACR,OAAO;YACP,WAAW;YACX,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;SAC3B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,cAAc;QACtB,SAAS,EAAE,IAAI;QACf,QAAQ;QACR,OAAO;QACP,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|