okrapdf 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,497 @@
1
+ import {
2
+ OkraRuntimeError
3
+ } from "./chunk-NIZM2ETT.js";
4
+
5
+ // src/local/index.ts
6
+ import { basename, extname } from "path";
7
+ import { existsSync as existsSync3 } from "fs";
8
+
9
+ // src/local/store.ts
10
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
11
+ import { homedir } from "os";
12
+ import { dirname, join } from "path";
13
+ import { randomUUID } from "crypto";
14
+ var DEFAULT_LOCAL_DATA_DIR = join(homedir(), ".openclaw", "okra-docs");
15
+ function resolveLocalDataDir(dataDir) {
16
+ return dataDir || process.env.OKRA_LOCAL_DATA_DIR || DEFAULT_LOCAL_DATA_DIR;
17
+ }
18
+ function ensureLocalStore(dataDir) {
19
+ const resolved = resolveLocalDataDir(dataDir);
20
+ mkdirSync(join(resolved, "docs"), { recursive: true });
21
+ return resolved;
22
+ }
23
+ function createLocalDocumentId() {
24
+ return `doc-local-${randomUUID()}`;
25
+ }
26
+ function getDocumentDir(documentId, dataDir) {
27
+ return join(resolveLocalDataDir(dataDir), "docs", documentId);
28
+ }
29
+ function getDocumentMetaPath(documentId, dataDir) {
30
+ return join(getDocumentDir(documentId, dataDir), "meta.json");
31
+ }
32
+ function getDocumentPagesDir(documentId, dataDir) {
33
+ return join(getDocumentDir(documentId, dataDir), "pages");
34
+ }
35
+ function getDocumentPagePath(documentId, pageNumber, dataDir) {
36
+ return join(getDocumentPagesDir(documentId, dataDir), `page-${String(pageNumber).padStart(4, "0")}.txt`);
37
+ }
38
+ function initializeDocumentDir(documentId, dataDir) {
39
+ const dir = getDocumentDir(documentId, dataDir);
40
+ rmSync(dir, { force: true, recursive: true });
41
+ mkdirSync(join(dir, "pages"), { recursive: true });
42
+ return dir;
43
+ }
44
+ function copySourceIntoStore(documentId, sourcePath, dataDir) {
45
+ const target = join(getDocumentDir(documentId, dataDir), "source.pdf");
46
+ mkdirSync(dirname(target), { recursive: true });
47
+ copyFileSync(sourcePath, target);
48
+ return target;
49
+ }
50
+ function writeDocumentRecord(record, dataDir) {
51
+ const metaPath = getDocumentMetaPath(record.documentId, dataDir);
52
+ mkdirSync(dirname(metaPath), { recursive: true });
53
+ writeFileSync(metaPath, JSON.stringify(record, null, 2), "utf8");
54
+ }
55
+ function readDocumentRecord(documentId, dataDir) {
56
+ const metaPath = getDocumentMetaPath(documentId, dataDir);
57
+ if (!existsSync(metaPath)) {
58
+ throw new OkraRuntimeError("DOCUMENT_NOT_FOUND", `Local document not found: ${documentId}`, 404);
59
+ }
60
+ return JSON.parse(readFileSync(metaPath, "utf8"));
61
+ }
62
+ function writeDocumentPageText(documentId, pageNumber, text, dataDir) {
63
+ const pagePath = getDocumentPagePath(documentId, pageNumber, dataDir);
64
+ mkdirSync(dirname(pagePath), { recursive: true });
65
+ writeFileSync(pagePath, text, "utf8");
66
+ return pagePath;
67
+ }
68
+ function readDocumentPageText(documentId, pageNumber, dataDir) {
69
+ const pagePath = getDocumentPagePath(documentId, pageNumber, dataDir);
70
+ if (!existsSync(pagePath)) {
71
+ throw new OkraRuntimeError(
72
+ "DOCUMENT_NOT_FOUND",
73
+ `Local page ${pageNumber} not found for document ${documentId}`,
74
+ 404
75
+ );
76
+ }
77
+ return readFileSync(pagePath, "utf8");
78
+ }
79
+
80
+ // src/local/extract.ts
81
+ import { existsSync as existsSync2, mkdtempSync, rmSync as rmSync2 } from "fs";
82
+ import { tmpdir } from "os";
83
+ import { join as join2 } from "path";
84
+ import { spawnSync } from "child_process";
85
+ function runCommand(command, args) {
86
+ const result = spawnSync(command, args, {
87
+ encoding: "utf8",
88
+ maxBuffer: 20 * 1024 * 1024
89
+ });
90
+ if (result.error) {
91
+ throw new OkraRuntimeError(
92
+ "INVALID_REQUEST",
93
+ `Failed to run ${command}: ${result.error.message}`,
94
+ 500
95
+ );
96
+ }
97
+ if (result.status !== 0) {
98
+ const stderr = (result.stderr || "").trim();
99
+ throw new OkraRuntimeError(
100
+ "INVALID_REQUEST",
101
+ stderr ? `${command} failed: ${stderr}` : `${command} exited with code ${result.status}`,
102
+ 500
103
+ );
104
+ }
105
+ return result.stdout ?? "";
106
+ }
107
+ function detectTool(name) {
108
+ const result = spawnSync("/bin/zsh", ["-lc", `command -v ${name}`], { encoding: "utf8" });
109
+ const path = result.status === 0 ? (result.stdout || "").trim() : "";
110
+ return {
111
+ available: path.length > 0,
112
+ path: path || null
113
+ };
114
+ }
115
+ function splitPages(raw, pageCount) {
116
+ const pages = raw.split("\f").map((value) => value.replace(/\r/g, "").trimEnd());
117
+ while (pages.length > 0 && pages[pages.length - 1]?.trim() === "") {
118
+ pages.pop();
119
+ }
120
+ if (pages.length < pageCount) {
121
+ return [...pages, ...new Array(pageCount - pages.length).fill("")];
122
+ }
123
+ return pages.slice(0, pageCount);
124
+ }
125
+ function normalizeWhitespace(text) {
126
+ return text.replace(/\r/g, "").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
127
+ }
128
+ function needsOcr(text) {
129
+ const normalized = normalizeWhitespace(text);
130
+ if (!normalized) return true;
131
+ const alphaNumeric = (normalized.match(/[A-Za-z0-9]/g) || []).length;
132
+ return normalized.length < 80 || alphaNumeric / normalized.length < 0.35;
133
+ }
134
+ function extractPageCount(sourcePath) {
135
+ const output = runCommand("pdfinfo", [sourcePath]);
136
+ const match = output.match(/^Pages:\s+(\d+)$/m);
137
+ if (!match) {
138
+ throw new OkraRuntimeError("INVALID_RESPONSE", `Unable to determine page count for ${sourcePath}`, 500);
139
+ }
140
+ return Number.parseInt(match[1], 10);
141
+ }
142
+ function ocrPage(sourcePath, pageNumber) {
143
+ const tempDir = mkdtempSync(join2(tmpdir(), "okra-local-ocr-"));
144
+ const prefix = join2(tempDir, `page-${pageNumber}`);
145
+ try {
146
+ runCommand("pdftoppm", ["-f", String(pageNumber), "-l", String(pageNumber), "-png", sourcePath, prefix]);
147
+ const imagePath = `${prefix}-${pageNumber}.png`;
148
+ if (!existsSync2(imagePath)) {
149
+ throw new OkraRuntimeError(
150
+ "INVALID_RESPONSE",
151
+ `pdftoppm did not produce an image for page ${pageNumber}`,
152
+ 500
153
+ );
154
+ }
155
+ return normalizeWhitespace(runCommand("tesseract", [imagePath, "stdout", "--psm", "6"]));
156
+ } finally {
157
+ rmSync2(tempDir, { recursive: true, force: true });
158
+ }
159
+ }
160
+ function localToolReport(dataDir) {
161
+ const tools = {
162
+ pdftotext: detectTool("pdftotext"),
163
+ pdfinfo: detectTool("pdfinfo"),
164
+ pdftoppm: detectTool("pdftoppm"),
165
+ tesseract: detectTool("tesseract")
166
+ };
167
+ return {
168
+ ok: tools.pdftotext.available && tools.pdfinfo.available,
169
+ dataDir,
170
+ tools
171
+ };
172
+ }
173
+ function extractLocalDocument(sourcePath, dataDir) {
174
+ const report = localToolReport(dataDir);
175
+ if (!report.tools.pdftotext.available || !report.tools.pdfinfo.available) {
176
+ throw new OkraRuntimeError(
177
+ "INVALID_REQUEST",
178
+ "Local PDF extraction requires pdftotext and pdfinfo to be installed",
179
+ 500,
180
+ report.tools
181
+ );
182
+ }
183
+ const pageCount = extractPageCount(sourcePath);
184
+ const raw = runCommand("pdftotext", ["-layout", "-enc", "UTF-8", sourcePath, "-"]);
185
+ const split = splitPages(raw, pageCount);
186
+ let charCount = 0;
187
+ let ocrUsed = false;
188
+ const pages = split.map((pageText, index) => {
189
+ const pageNumber = index + 1;
190
+ let text = normalizeWhitespace(pageText);
191
+ let ocrApplied = false;
192
+ if (needsOcr(text) && report.tools.pdftoppm.available && report.tools.tesseract.available) {
193
+ const ocrText = ocrPage(sourcePath, pageNumber);
194
+ if (ocrText.length > text.length) {
195
+ text = ocrText;
196
+ }
197
+ ocrApplied = ocrText.length > 0;
198
+ ocrUsed = ocrUsed || ocrApplied;
199
+ }
200
+ charCount += text.length;
201
+ return { pageNumber, text, ocrApplied };
202
+ });
203
+ return {
204
+ pageCount,
205
+ charCount,
206
+ ocrUsed,
207
+ pages,
208
+ tools: {
209
+ ...report.tools,
210
+ tesseract: report.tools.tesseract,
211
+ pdftoppm: report.tools.pdftoppm,
212
+ pdftotext: report.tools.pdftotext,
213
+ pdfinfo: report.tools.pdfinfo
214
+ }
215
+ };
216
+ }
217
+
218
+ // src/local/index.ts
219
+ var STOP_WORDS = /* @__PURE__ */ new Set([
220
+ "a",
221
+ "an",
222
+ "and",
223
+ "are",
224
+ "as",
225
+ "at",
226
+ "be",
227
+ "by",
228
+ "for",
229
+ "from",
230
+ "how",
231
+ "in",
232
+ "is",
233
+ "it",
234
+ "of",
235
+ "on",
236
+ "or",
237
+ "that",
238
+ "the",
239
+ "this",
240
+ "to",
241
+ "what",
242
+ "when",
243
+ "where",
244
+ "who",
245
+ "why",
246
+ "with"
247
+ ]);
248
+ function sanitizeExcerpt(text, maxLength = 280) {
249
+ const normalized = text.replace(/\s+/g, " ").trim();
250
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1).trimEnd()}\u2026` : normalized;
251
+ }
252
+ function sanitizeTablePreview(text, maxLength = 320) {
253
+ const normalized = text.replace(/\r/g, "").replace(/\n{3,}/g, "\n\n").trim();
254
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1).trimEnd()}\u2026` : normalized;
255
+ }
256
+ function tokenizeQuery(query) {
257
+ return query.toLowerCase().split(/[^a-z0-9]+/g).map((token) => token.trim()).filter((token) => token.length >= 2 && !STOP_WORDS.has(token));
258
+ }
259
+ function buildDocumentInfo(record) {
260
+ return {
261
+ documentId: record.documentId,
262
+ filename: record.filename,
263
+ status: record.status,
264
+ pageCount: record.pageCount,
265
+ charCount: record.charCount,
266
+ createdAt: record.createdAt,
267
+ updatedAt: record.updatedAt,
268
+ error: record.error,
269
+ extractor: record.extractor
270
+ };
271
+ }
272
+ function ensureReady(documentId, dataDir) {
273
+ const record = readDocumentRecord(documentId, dataDir);
274
+ if (record.status === "failed") {
275
+ throw new OkraRuntimeError(
276
+ "INVALID_REQUEST",
277
+ record.error || `Local document ${documentId} failed to process`,
278
+ 500
279
+ );
280
+ }
281
+ return record;
282
+ }
283
+ function snippetAround(text, tokens) {
284
+ const normalized = text.replace(/\s+/g, " ").trim();
285
+ if (!normalized) return "";
286
+ const lower = normalized.toLowerCase();
287
+ let index = -1;
288
+ for (const token of tokens) {
289
+ index = lower.indexOf(token);
290
+ if (index >= 0) break;
291
+ }
292
+ if (index < 0) {
293
+ return sanitizeExcerpt(normalized);
294
+ }
295
+ const start = Math.max(0, index - 110);
296
+ const end = Math.min(normalized.length, index + 170);
297
+ return sanitizeExcerpt(normalized.slice(start, end));
298
+ }
299
+ function summarizeText(record, dataDir) {
300
+ const citations = [];
301
+ const seenPages = /* @__PURE__ */ new Set();
302
+ const snippets = [];
303
+ for (const page of record.pages) {
304
+ const text = readDocumentPageText(record.documentId, page.pageNumber, dataDir).split(/\n{2,}/).map((chunk) => chunk.trim()).filter(Boolean);
305
+ for (const block of text) {
306
+ snippets.push(block);
307
+ if (!seenPages.has(page.pageNumber)) {
308
+ citations.push({
309
+ page: page.pageNumber,
310
+ snippet: sanitizeExcerpt(block, 200)
311
+ });
312
+ seenPages.add(page.pageNumber);
313
+ }
314
+ if (snippets.join(" ").length >= 420 || citations.length >= 3) {
315
+ break;
316
+ }
317
+ }
318
+ if (snippets.join(" ").length >= 420 || citations.length >= 3) break;
319
+ }
320
+ const summary = snippets.length > 0 ? sanitizeExcerpt(snippets.join(" "), 520) : `No extractable text was found in ${record.filename}.`;
321
+ return { summary, citations };
322
+ }
323
+ function ingestLocalDocument(sourcePath, dataDir) {
324
+ if (!existsSync3(sourcePath)) {
325
+ throw new OkraRuntimeError("DOCUMENT_NOT_FOUND", `File not found: ${sourcePath}`, 404);
326
+ }
327
+ if (extname(sourcePath).toLowerCase() !== ".pdf") {
328
+ throw new OkraRuntimeError("INVALID_REQUEST", `Local ingest only supports PDFs: ${sourcePath}`, 400);
329
+ }
330
+ const resolvedDataDir = ensureLocalStore(dataDir);
331
+ const documentId = createLocalDocumentId();
332
+ initializeDocumentDir(documentId, resolvedDataDir);
333
+ const storedPath = copySourceIntoStore(documentId, sourcePath, resolvedDataDir);
334
+ const now = (/* @__PURE__ */ new Date()).toISOString();
335
+ const extracted = extractLocalDocument(storedPath, resolvedDataDir);
336
+ const pages = extracted.pages.map((page) => {
337
+ const textPath = writeDocumentPageText(documentId, page.pageNumber, page.text, resolvedDataDir);
338
+ return {
339
+ pageNumber: page.pageNumber,
340
+ textPath,
341
+ charCount: page.text.length,
342
+ excerpt: sanitizeExcerpt(page.text, 160),
343
+ ocrApplied: page.ocrApplied
344
+ };
345
+ });
346
+ const record = {
347
+ documentId,
348
+ filename: basename(sourcePath),
349
+ storedPath,
350
+ sourcePath,
351
+ status: extracted.charCount > 0 ? "ready" : "failed",
352
+ pageCount: extracted.pageCount,
353
+ charCount: extracted.charCount,
354
+ createdAt: now,
355
+ updatedAt: now,
356
+ error: extracted.charCount > 0 ? null : "No extractable text found in this PDF.",
357
+ extractor: {
358
+ ...extracted.tools,
359
+ ocrUsed: extracted.ocrUsed
360
+ },
361
+ pages
362
+ };
363
+ writeDocumentRecord(record, resolvedDataDir);
364
+ return {
365
+ ...buildDocumentInfo(record),
366
+ storedPath: record.storedPath
367
+ };
368
+ }
369
+ function getLocalDocumentStatus(documentId, dataDir) {
370
+ const record = readDocumentRecord(documentId, dataDir);
371
+ return {
372
+ ...buildDocumentInfo(record),
373
+ pagesWithText: record.pages.filter((page) => page.charCount > 0).length
374
+ };
375
+ }
376
+ function summarizeLocalDocument(documentId, dataDir) {
377
+ const record = ensureReady(documentId, dataDir);
378
+ const { summary, citations } = summarizeText(record, dataDir);
379
+ return {
380
+ ...buildDocumentInfo(record),
381
+ summary,
382
+ citations
383
+ };
384
+ }
385
+ function searchLocalDocument(documentId, query, dataDir) {
386
+ const record = ensureReady(documentId, dataDir);
387
+ const tokens = tokenizeQuery(query);
388
+ const matches = record.pages.map((page) => {
389
+ const text = readDocumentPageText(record.documentId, page.pageNumber, dataDir);
390
+ const lower = text.toLowerCase();
391
+ let score = 0;
392
+ for (const token of tokens) {
393
+ const occurrences = lower.split(token).length - 1;
394
+ score += occurrences * Math.max(1, token.length);
395
+ }
396
+ return {
397
+ page: page.pageNumber,
398
+ snippet: snippetAround(text, tokens),
399
+ score
400
+ };
401
+ }).filter((match) => match.score > 0 || tokens.length === 0).sort((lhs, rhs) => rhs.score - lhs.score).slice(0, 5);
402
+ return {
403
+ ...buildDocumentInfo(record),
404
+ query,
405
+ matches
406
+ };
407
+ }
408
+ function readLocalPage(documentId, pageNumber, dataDir) {
409
+ const record = ensureReady(documentId, dataDir);
410
+ const page = record.pages.find((entry) => entry.pageNumber === pageNumber);
411
+ if (!page) {
412
+ throw new OkraRuntimeError(
413
+ "DOCUMENT_NOT_FOUND",
414
+ `Page ${pageNumber} not found for local document ${documentId}`,
415
+ 404
416
+ );
417
+ }
418
+ return {
419
+ ...buildDocumentInfo(record),
420
+ page: page.pageNumber,
421
+ text: readDocumentPageText(documentId, pageNumber, dataDir),
422
+ ocrApplied: page.ocrApplied
423
+ };
424
+ }
425
+ function looksLikeTableRow(line) {
426
+ return /\S(?:.*(?: {2,}|\t).*)\S/.test(line.trim());
427
+ }
428
+ function detectTableCandidates(text, page, query) {
429
+ const lines = text.split("\n");
430
+ const tables = [];
431
+ let current = [];
432
+ let previousNonEmpty = "";
433
+ for (const line of lines) {
434
+ const trimmed = line.trim();
435
+ if (looksLikeTableRow(line)) {
436
+ current.push(line.trimEnd());
437
+ continue;
438
+ }
439
+ if (current.length >= 2) {
440
+ tables.push({ rows: current, heading: previousNonEmpty || void 0 });
441
+ }
442
+ current = [];
443
+ if (trimmed) {
444
+ previousNonEmpty = trimmed;
445
+ }
446
+ }
447
+ if (current.length >= 2) {
448
+ tables.push({ rows: current, heading: previousNonEmpty || void 0 });
449
+ }
450
+ const loweredQuery = query?.toLowerCase().trim();
451
+ return tables.map((table) => {
452
+ const body = table.rows.join("\n");
453
+ const preview = sanitizeTablePreview(
454
+ table.heading ? `${table.heading}
455
+ ${body}` : body,
456
+ 320
457
+ );
458
+ const score = loweredQuery ? preview.toLowerCase().includes(loweredQuery) ? 10 : 0 : 1;
459
+ return {
460
+ page,
461
+ preview,
462
+ rowCount: table.rows.length,
463
+ score
464
+ };
465
+ }).filter((table) => table.score > 0);
466
+ }
467
+ function findLocalTables(documentId, query, dataDir) {
468
+ const record = ensureReady(documentId, dataDir);
469
+ const tables = record.pages.flatMap(
470
+ (page) => detectTableCandidates(
471
+ readDocumentPageText(record.documentId, page.pageNumber, dataDir),
472
+ page.pageNumber,
473
+ query
474
+ )
475
+ ).sort((lhs, rhs) => (rhs.score ?? 0) - (lhs.score ?? 0)).slice(0, 8).map(({ page, preview, rowCount }) => ({ page, preview, rowCount }));
476
+ return {
477
+ ...buildDocumentInfo(record),
478
+ query,
479
+ tables
480
+ };
481
+ }
482
+ function doctorLocalHarness(dataDir) {
483
+ const resolvedDataDir = resolveLocalDataDir(dataDir);
484
+ ensureLocalStore(resolvedDataDir);
485
+ return localToolReport(resolvedDataDir);
486
+ }
487
+
488
+ export {
489
+ ingestLocalDocument,
490
+ getLocalDocumentStatus,
491
+ summarizeLocalDocument,
492
+ searchLocalDocument,
493
+ readLocalPage,
494
+ findLocalTables,
495
+ doctorLocalHarness
496
+ };
497
+ //# sourceMappingURL=chunk-MSZQPLMQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/local/index.ts","../src/local/store.ts","../src/local/extract.ts"],"sourcesContent":["import { basename, extname } from 'path';\nimport { existsSync } from 'fs';\nimport { OkraRuntimeError } from '../errors.js';\nimport {\n copySourceIntoStore,\n createLocalDocumentId,\n ensureLocalStore,\n initializeDocumentDir,\n readDocumentPageText,\n readDocumentRecord,\n resolveLocalDataDir,\n writeDocumentPageText,\n writeDocumentRecord,\n} from './store.js';\nimport { extractLocalDocument, localToolReport } from './extract.js';\nimport type {\n LocalCitation,\n LocalDoctorReport,\n LocalDocumentRecord,\n LocalTableCandidate,\n} from './types.js';\n\nconst STOP_WORDS = new Set([\n 'a',\n 'an',\n 'and',\n 'are',\n 'as',\n 'at',\n 'be',\n 'by',\n 'for',\n 'from',\n 'how',\n 'in',\n 'is',\n 'it',\n 'of',\n 'on',\n 'or',\n 'that',\n 'the',\n 'this',\n 'to',\n 'what',\n 'when',\n 'where',\n 'who',\n 'why',\n 'with',\n]);\n\nfunction sanitizeExcerpt(text: string, maxLength = 280): string {\n const normalized = text.replace(/\\s+/g, ' ').trim();\n return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1).trimEnd()}…` : normalized;\n}\n\nfunction sanitizeTablePreview(text: string, maxLength = 320): string {\n const normalized = text.replace(/\\r/g, '').replace(/\\n{3,}/g, '\\n\\n').trim();\n return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1).trimEnd()}…` : normalized;\n}\n\nfunction tokenizeQuery(query: string): string[] {\n return query\n .toLowerCase()\n .split(/[^a-z0-9]+/g)\n .map((token) => token.trim())\n .filter((token) => token.length >= 2 && !STOP_WORDS.has(token));\n}\n\nfunction buildDocumentInfo(record: LocalDocumentRecord) {\n return {\n documentId: record.documentId,\n filename: record.filename,\n status: record.status,\n pageCount: record.pageCount,\n charCount: record.charCount,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n error: record.error,\n extractor: record.extractor,\n };\n}\n\nfunction ensureReady(documentId: string, dataDir?: string): LocalDocumentRecord {\n const record = readDocumentRecord(documentId, dataDir);\n if (record.status === 'failed') {\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n record.error || `Local document ${documentId} failed to process`,\n 500,\n );\n }\n return record;\n}\n\nfunction snippetAround(text: string, tokens: string[]): string {\n const normalized = text.replace(/\\s+/g, ' ').trim();\n if (!normalized) return '';\n const lower = normalized.toLowerCase();\n let index = -1;\n for (const token of tokens) {\n index = lower.indexOf(token);\n if (index >= 0) break;\n }\n if (index < 0) {\n return sanitizeExcerpt(normalized);\n }\n const start = Math.max(0, index - 110);\n const end = Math.min(normalized.length, index + 170);\n return sanitizeExcerpt(normalized.slice(start, end));\n}\n\nfunction summarizeText(record: LocalDocumentRecord, dataDir?: string): { summary: string; citations: LocalCitation[] } {\n const citations: LocalCitation[] = [];\n const seenPages = new Set<number>();\n const snippets: string[] = [];\n\n for (const page of record.pages) {\n const text = readDocumentPageText(record.documentId, page.pageNumber, dataDir)\n .split(/\\n{2,}/)\n .map((chunk) => chunk.trim())\n .filter(Boolean);\n\n for (const block of text) {\n snippets.push(block);\n if (!seenPages.has(page.pageNumber)) {\n citations.push({\n page: page.pageNumber,\n snippet: sanitizeExcerpt(block, 200),\n });\n seenPages.add(page.pageNumber);\n }\n if (snippets.join(' ').length >= 420 || citations.length >= 3) {\n break;\n }\n }\n if (snippets.join(' ').length >= 420 || citations.length >= 3) break;\n }\n\n const summary = snippets.length > 0\n ? sanitizeExcerpt(snippets.join(' '), 520)\n : `No extractable text was found in ${record.filename}.`;\n\n return { summary, citations };\n}\n\nexport function ingestLocalDocument(sourcePath: string, dataDir?: string) {\n if (!existsSync(sourcePath)) {\n throw new OkraRuntimeError('DOCUMENT_NOT_FOUND', `File not found: ${sourcePath}`, 404);\n }\n if (extname(sourcePath).toLowerCase() !== '.pdf') {\n throw new OkraRuntimeError('INVALID_REQUEST', `Local ingest only supports PDFs: ${sourcePath}`, 400);\n }\n\n const resolvedDataDir = ensureLocalStore(dataDir);\n const documentId = createLocalDocumentId();\n initializeDocumentDir(documentId, resolvedDataDir);\n const storedPath = copySourceIntoStore(documentId, sourcePath, resolvedDataDir);\n const now = new Date().toISOString();\n\n const extracted = extractLocalDocument(storedPath, resolvedDataDir);\n\n const pages = extracted.pages.map((page) => {\n const textPath = writeDocumentPageText(documentId, page.pageNumber, page.text, resolvedDataDir);\n return {\n pageNumber: page.pageNumber,\n textPath,\n charCount: page.text.length,\n excerpt: sanitizeExcerpt(page.text, 160),\n ocrApplied: page.ocrApplied,\n };\n });\n\n const record: LocalDocumentRecord = {\n documentId,\n filename: basename(sourcePath),\n storedPath,\n sourcePath,\n status: extracted.charCount > 0 ? 'ready' : 'failed',\n pageCount: extracted.pageCount,\n charCount: extracted.charCount,\n createdAt: now,\n updatedAt: now,\n error: extracted.charCount > 0 ? null : 'No extractable text found in this PDF.',\n extractor: {\n ...extracted.tools,\n ocrUsed: extracted.ocrUsed,\n },\n pages,\n };\n\n writeDocumentRecord(record, resolvedDataDir);\n return {\n ...buildDocumentInfo(record),\n storedPath: record.storedPath,\n };\n}\n\nexport function getLocalDocumentStatus(documentId: string, dataDir?: string) {\n const record = readDocumentRecord(documentId, dataDir);\n return {\n ...buildDocumentInfo(record),\n pagesWithText: record.pages.filter((page) => page.charCount > 0).length,\n };\n}\n\nexport function summarizeLocalDocument(documentId: string, dataDir?: string) {\n const record = ensureReady(documentId, dataDir);\n const { summary, citations } = summarizeText(record, dataDir);\n return {\n ...buildDocumentInfo(record),\n summary,\n citations,\n };\n}\n\nexport function searchLocalDocument(documentId: string, query: string, dataDir?: string) {\n const record = ensureReady(documentId, dataDir);\n const tokens = tokenizeQuery(query);\n const matches = record.pages\n .map((page) => {\n const text = readDocumentPageText(record.documentId, page.pageNumber, dataDir);\n const lower = text.toLowerCase();\n let score = 0;\n for (const token of tokens) {\n const occurrences = lower.split(token).length - 1;\n score += occurrences * Math.max(1, token.length);\n }\n return {\n page: page.pageNumber,\n snippet: snippetAround(text, tokens),\n score,\n };\n })\n .filter((match) => match.score > 0 || tokens.length === 0)\n .sort((lhs, rhs) => rhs.score - lhs.score)\n .slice(0, 5);\n\n return {\n ...buildDocumentInfo(record),\n query,\n matches,\n };\n}\n\nexport function readLocalPage(documentId: string, pageNumber: number, dataDir?: string) {\n const record = ensureReady(documentId, dataDir);\n const page = record.pages.find((entry) => entry.pageNumber === pageNumber);\n if (!page) {\n throw new OkraRuntimeError(\n 'DOCUMENT_NOT_FOUND',\n `Page ${pageNumber} not found for local document ${documentId}`,\n 404,\n );\n }\n\n return {\n ...buildDocumentInfo(record),\n page: page.pageNumber,\n text: readDocumentPageText(documentId, pageNumber, dataDir),\n ocrApplied: page.ocrApplied,\n };\n}\n\nfunction looksLikeTableRow(line: string): boolean {\n return /\\S(?:.*(?: {2,}|\\t).*)\\S/.test(line.trim());\n}\n\nfunction detectTableCandidates(text: string, page: number, query?: string): LocalTableCandidate[] {\n const lines = text.split('\\n');\n const tables: Array<{ rows: string[]; heading?: string }> = [];\n let current: string[] = [];\n let previousNonEmpty = '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (looksLikeTableRow(line)) {\n current.push(line.trimEnd());\n continue;\n }\n\n if (current.length >= 2) {\n tables.push({ rows: current, heading: previousNonEmpty || undefined });\n }\n current = [];\n if (trimmed) {\n previousNonEmpty = trimmed;\n }\n }\n\n if (current.length >= 2) {\n tables.push({ rows: current, heading: previousNonEmpty || undefined });\n }\n\n const loweredQuery = query?.toLowerCase().trim();\n\n return tables\n .map((table) => {\n const body = table.rows.join('\\n');\n const preview = sanitizeTablePreview(\n table.heading ? `${table.heading}\\n${body}` : body,\n 320,\n );\n const score = loweredQuery\n ? (preview.toLowerCase().includes(loweredQuery) ? 10 : 0)\n : 1;\n return {\n page,\n preview,\n rowCount: table.rows.length,\n score,\n };\n })\n .filter((table) => table.score > 0);\n}\n\nexport function findLocalTables(documentId: string, query?: string, dataDir?: string) {\n const record = ensureReady(documentId, dataDir);\n const tables = record.pages\n .flatMap((page) =>\n detectTableCandidates(\n readDocumentPageText(record.documentId, page.pageNumber, dataDir),\n page.pageNumber,\n query,\n ),\n )\n .sort((lhs, rhs) => (rhs.score ?? 0) - (lhs.score ?? 0))\n .slice(0, 8)\n .map(({ page, preview, rowCount }) => ({ page, preview, rowCount }));\n\n return {\n ...buildDocumentInfo(record),\n query,\n tables,\n };\n}\n\nexport function doctorLocalHarness(dataDir?: string): LocalDoctorReport {\n const resolvedDataDir = resolveLocalDataDir(dataDir);\n ensureLocalStore(resolvedDataDir);\n return localToolReport(resolvedDataDir);\n}\n","import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { dirname, join } from 'path';\nimport { randomUUID } from 'crypto';\nimport { OkraRuntimeError } from '../errors.js';\nimport type { LocalDocumentRecord } from './types.js';\n\nexport const DEFAULT_LOCAL_DATA_DIR = join(homedir(), '.openclaw', 'okra-docs');\n\nexport function resolveLocalDataDir(dataDir?: string): string {\n return dataDir || process.env.OKRA_LOCAL_DATA_DIR || DEFAULT_LOCAL_DATA_DIR;\n}\n\nexport function ensureLocalStore(dataDir?: string): string {\n const resolved = resolveLocalDataDir(dataDir);\n mkdirSync(join(resolved, 'docs'), { recursive: true });\n return resolved;\n}\n\nexport function createLocalDocumentId(): string {\n return `doc-local-${randomUUID()}`;\n}\n\nexport function getDocumentDir(documentId: string, dataDir?: string): string {\n return join(resolveLocalDataDir(dataDir), 'docs', documentId);\n}\n\nexport function getDocumentMetaPath(documentId: string, dataDir?: string): string {\n return join(getDocumentDir(documentId, dataDir), 'meta.json');\n}\n\nexport function getDocumentPagesDir(documentId: string, dataDir?: string): string {\n return join(getDocumentDir(documentId, dataDir), 'pages');\n}\n\nexport function getDocumentPagePath(\n documentId: string,\n pageNumber: number,\n dataDir?: string,\n): string {\n return join(getDocumentPagesDir(documentId, dataDir), `page-${String(pageNumber).padStart(4, '0')}.txt`);\n}\n\nexport function initializeDocumentDir(documentId: string, dataDir?: string): string {\n const dir = getDocumentDir(documentId, dataDir);\n rmSync(dir, { force: true, recursive: true });\n mkdirSync(join(dir, 'pages'), { recursive: true });\n return dir;\n}\n\nexport function copySourceIntoStore(\n documentId: string,\n sourcePath: string,\n dataDir?: string,\n): string {\n const target = join(getDocumentDir(documentId, dataDir), 'source.pdf');\n mkdirSync(dirname(target), { recursive: true });\n copyFileSync(sourcePath, target);\n return target;\n}\n\nexport function writeDocumentRecord(record: LocalDocumentRecord, dataDir?: string): void {\n const metaPath = getDocumentMetaPath(record.documentId, dataDir);\n mkdirSync(dirname(metaPath), { recursive: true });\n writeFileSync(metaPath, JSON.stringify(record, null, 2), 'utf8');\n}\n\nexport function readDocumentRecord(documentId: string, dataDir?: string): LocalDocumentRecord {\n const metaPath = getDocumentMetaPath(documentId, dataDir);\n if (!existsSync(metaPath)) {\n throw new OkraRuntimeError('DOCUMENT_NOT_FOUND', `Local document not found: ${documentId}`, 404);\n }\n return JSON.parse(readFileSync(metaPath, 'utf8')) as LocalDocumentRecord;\n}\n\nexport function writeDocumentPageText(\n documentId: string,\n pageNumber: number,\n text: string,\n dataDir?: string,\n): string {\n const pagePath = getDocumentPagePath(documentId, pageNumber, dataDir);\n mkdirSync(dirname(pagePath), { recursive: true });\n writeFileSync(pagePath, text, 'utf8');\n return pagePath;\n}\n\nexport function readDocumentPageText(\n documentId: string,\n pageNumber: number,\n dataDir?: string,\n): string {\n const pagePath = getDocumentPagePath(documentId, pageNumber, dataDir);\n if (!existsSync(pagePath)) {\n throw new OkraRuntimeError(\n 'DOCUMENT_NOT_FOUND',\n `Local page ${pageNumber} not found for document ${documentId}`,\n 404,\n );\n }\n return readFileSync(pagePath, 'utf8');\n}\n\n","import { existsSync, mkdtempSync, rmSync } from 'fs';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\nimport { spawnSync } from 'child_process';\nimport { OkraRuntimeError } from '../errors.js';\nimport type { LocalDoctorReport, LocalToolAvailability } from './types.js';\n\nconst TOOL_NAMES = ['pdftotext', 'pdfinfo', 'pdftoppm', 'tesseract'] as const;\n\ntype ToolName = (typeof TOOL_NAMES)[number];\n\ninterface ExtractedPage {\n pageNumber: number;\n text: string;\n ocrApplied: boolean;\n}\n\nexport interface ExtractedDocument {\n pageCount: number;\n charCount: number;\n ocrUsed: boolean;\n pages: ExtractedPage[];\n tools: LocalDoctorReport['tools'];\n}\n\nfunction runCommand(command: string, args: string[]): string {\n const result = spawnSync(command, args, {\n encoding: 'utf8',\n maxBuffer: 20 * 1024 * 1024,\n });\n\n if (result.error) {\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n `Failed to run ${command}: ${result.error.message}`,\n 500,\n );\n }\n\n if (result.status !== 0) {\n const stderr = (result.stderr || '').trim();\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n stderr ? `${command} failed: ${stderr}` : `${command} exited with code ${result.status}`,\n 500,\n );\n }\n\n return result.stdout ?? '';\n}\n\nfunction detectTool(name: ToolName): LocalToolAvailability {\n const result = spawnSync('/bin/zsh', ['-lc', `command -v ${name}`], { encoding: 'utf8' });\n const path = result.status === 0 ? (result.stdout || '').trim() : '';\n return {\n available: path.length > 0,\n path: path || null,\n };\n}\n\nfunction splitPages(raw: string, pageCount: number): string[] {\n const pages = raw\n .split('\\f')\n .map((value) => value.replace(/\\r/g, '').trimEnd());\n\n while (pages.length > 0 && pages[pages.length - 1]?.trim() === '') {\n pages.pop();\n }\n\n if (pages.length < pageCount) {\n return [...pages, ...new Array(pageCount - pages.length).fill('')];\n }\n\n return pages.slice(0, pageCount);\n}\n\nfunction normalizeWhitespace(text: string): string {\n return text.replace(/\\r/g, '').replace(/[ \\t]+\\n/g, '\\n').replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\nfunction needsOcr(text: string): boolean {\n const normalized = normalizeWhitespace(text);\n if (!normalized) return true;\n const alphaNumeric = (normalized.match(/[A-Za-z0-9]/g) || []).length;\n return normalized.length < 80 || alphaNumeric / normalized.length < 0.35;\n}\n\nfunction extractPageCount(sourcePath: string): number {\n const output = runCommand('pdfinfo', [sourcePath]);\n const match = output.match(/^Pages:\\s+(\\d+)$/m);\n if (!match) {\n throw new OkraRuntimeError('INVALID_RESPONSE', `Unable to determine page count for ${sourcePath}`, 500);\n }\n return Number.parseInt(match[1]!, 10);\n}\n\nfunction ocrPage(sourcePath: string, pageNumber: number): string {\n const tempDir = mkdtempSync(join(tmpdir(), 'okra-local-ocr-'));\n const prefix = join(tempDir, `page-${pageNumber}`);\n try {\n runCommand('pdftoppm', ['-f', String(pageNumber), '-l', String(pageNumber), '-png', sourcePath, prefix]);\n const imagePath = `${prefix}-${pageNumber}.png`;\n if (!existsSync(imagePath)) {\n throw new OkraRuntimeError(\n 'INVALID_RESPONSE',\n `pdftoppm did not produce an image for page ${pageNumber}`,\n 500,\n );\n }\n return normalizeWhitespace(runCommand('tesseract', [imagePath, 'stdout', '--psm', '6']));\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n\nexport function localToolReport(dataDir: string): LocalDoctorReport {\n const tools = {\n pdftotext: detectTool('pdftotext'),\n pdfinfo: detectTool('pdfinfo'),\n pdftoppm: detectTool('pdftoppm'),\n tesseract: detectTool('tesseract'),\n };\n\n return {\n ok: tools.pdftotext.available && tools.pdfinfo.available,\n dataDir,\n tools,\n };\n}\n\nexport function extractLocalDocument(sourcePath: string, dataDir: string): ExtractedDocument {\n const report = localToolReport(dataDir);\n if (!report.tools.pdftotext.available || !report.tools.pdfinfo.available) {\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n 'Local PDF extraction requires pdftotext and pdfinfo to be installed',\n 500,\n report.tools,\n );\n }\n\n const pageCount = extractPageCount(sourcePath);\n const raw = runCommand('pdftotext', ['-layout', '-enc', 'UTF-8', sourcePath, '-']);\n const split = splitPages(raw, pageCount);\n\n let charCount = 0;\n let ocrUsed = false;\n const pages = split.map((pageText, index) => {\n const pageNumber = index + 1;\n let text = normalizeWhitespace(pageText);\n let ocrApplied = false;\n\n if (needsOcr(text) && report.tools.pdftoppm.available && report.tools.tesseract.available) {\n const ocrText = ocrPage(sourcePath, pageNumber);\n if (ocrText.length > text.length) {\n text = ocrText;\n }\n ocrApplied = ocrText.length > 0;\n ocrUsed = ocrUsed || ocrApplied;\n }\n\n charCount += text.length;\n return { pageNumber, text, ocrApplied };\n });\n\n return {\n pageCount,\n charCount,\n ocrUsed,\n pages,\n tools: {\n ...report.tools,\n tesseract: report.tools.tesseract,\n pdftoppm: report.tools.pdftoppm,\n pdftotext: report.tools.pdftotext,\n pdfinfo: report.tools.pdfinfo,\n },\n };\n}\n\n"],"mappings":"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","names":["existsSync","existsSync","rmSync","join","join","existsSync","rmSync","existsSync"]}