@autorag/librarian 2.5.3 → 2.6.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.
Files changed (43) hide show
  1. package/NOTICE +10 -13
  2. package/README.md +46 -7
  3. package/dist/agent/agent.d.ts +16 -1
  4. package/dist/agent/refresh-progress.d.ts +30 -0
  5. package/dist/agent/search-documents.d.ts +1 -1
  6. package/dist/cli/commands/report.d.ts +3 -0
  7. package/dist/cli/commands/serve.d.ts +2 -1
  8. package/dist/cli/config.d.ts +1 -10
  9. package/dist/cli/index.d.ts +1 -1
  10. package/dist/cli/index.js +4927 -6925
  11. package/dist/core.d.ts +1 -1
  12. package/dist/core.js +1120 -1175
  13. package/dist/index.js +1123 -1179
  14. package/dist/language.d.ts +21 -0
  15. package/dist/memory/memory.d.ts +0 -1
  16. package/dist/mirror/paths.d.ts +2 -0
  17. package/dist/mirror/sync.d.ts +1 -1
  18. package/dist/p2p/simplex-server.d.ts +21 -5
  19. package/dist/p2p/simplex-transport.d.ts +21 -4
  20. package/dist/parser/defaults.d.ts +10 -4
  21. package/dist/parser/index.d.ts +3 -5
  22. package/dist/parser/kordoc.d.ts +53 -0
  23. package/dist/parser/ocr-engines.d.ts +12 -0
  24. package/dist/parser/ocr.d.ts +7 -1
  25. package/dist/parser/office.d.ts +1 -10
  26. package/dist/parser/types.d.ts +1 -1
  27. package/package.json +10 -10
  28. package/skills/autorag-lite-search/SKILL.md +57 -7
  29. package/skills/autorag-lite-setup/SKILL.md +2 -2
  30. package/skills/autorag-setup/SKILL.md +2 -2
  31. package/dist/cli/commands/ui.d.ts +0 -13
  32. package/dist/parser/hwp-markdown.d.ts +0 -25
  33. package/dist/parser/hwp.d.ts +0 -13
  34. package/dist/parser/opendataloader-pdf.d.ts +0 -31
  35. package/dist/parser/rhwp-adapter.d.ts +0 -30
  36. package/dist/ui/browse.d.ts +0 -11
  37. package/dist/ui/catalog.d.ts +0 -59
  38. package/dist/ui/config-store.d.ts +0 -43
  39. package/dist/ui/discover.d.ts +0 -17
  40. package/dist/ui/html.d.ts +0 -2
  41. package/dist/ui/probe.d.ts +0 -24
  42. package/dist/ui/prompt.d.ts +0 -21
  43. package/dist/ui/server.d.ts +0 -28
package/dist/index.js CHANGED
@@ -146,7 +146,10 @@ function parsedOutputPath(root, virtualPath) {
146
146
  function refreshReadinessPath(root) {
147
147
  return join2(root, ".autorag", REFRESH_READINESS_FILE);
148
148
  }
149
- var PARSED_MIRROR_SUBDIR, PARSED_FILES_SUBDIR = "files", PARSED_INDEX_FILE = "index.json", REFRESH_READINESS_FILE = "refresh-complete.json";
149
+ function refreshProgressPath(root) {
150
+ return join2(root, ".autorag", REFRESH_PROGRESS_FILE);
151
+ }
152
+ var PARSED_MIRROR_SUBDIR, PARSED_FILES_SUBDIR = "files", PARSED_INDEX_FILE = "index.json", REFRESH_READINESS_FILE = "refresh-complete.json", REFRESH_PROGRESS_FILE = "refresh-progress.json";
150
153
  var init_paths = __esm(() => {
151
154
  PARSED_MIRROR_SUBDIR = join2(".autorag", "parsed");
152
155
  });
@@ -1212,6 +1215,72 @@ var init_jikji = __esm(() => {
1212
1215
  init_types();
1213
1216
  });
1214
1217
 
1218
+ // src/language.ts
1219
+ function supportedList() {
1220
+ return AUTORAG_LANGUAGE_TAGS.join(", ");
1221
+ }
1222
+ function toTag(raw) {
1223
+ const tag = raw.trim().toLowerCase();
1224
+ if (!AUTORAG_LANGUAGE_TAGS.includes(tag)) {
1225
+ throw new LanguageError(`Unsupported language "${raw.trim()}". Supported: ${supportedList()}.`);
1226
+ }
1227
+ return tag;
1228
+ }
1229
+ function dedupe(tags) {
1230
+ return [...new Set(tags)];
1231
+ }
1232
+ function normalizeLanguages(raw) {
1233
+ if (raw === undefined || raw === null)
1234
+ return [...DEFAULT_LANGUAGES];
1235
+ if (typeof raw === "string")
1236
+ return parseLanguageList(raw);
1237
+ if (!Array.isArray(raw)) {
1238
+ throw new LanguageError(`languages must be a string or an array of language tags; received ${typeof raw}.`);
1239
+ }
1240
+ const entries = raw.map((entry, index) => {
1241
+ if (typeof entry !== "string") {
1242
+ throw new LanguageError(`languages[${index}] must be a string language tag; received ${typeof entry}.`);
1243
+ }
1244
+ return entry;
1245
+ });
1246
+ const tags = entries.filter((entry) => entry.trim() !== "").map(toTag);
1247
+ if (tags.length === 0) {
1248
+ throw new LanguageError(`languages must list at least one language. Supported: ${supportedList()}.`);
1249
+ }
1250
+ return dedupe(tags);
1251
+ }
1252
+ function parseLanguageList(value) {
1253
+ const tags = value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "").map(toTag);
1254
+ if (tags.length === 0) {
1255
+ throw new LanguageError(`languages must list at least one language. Supported: ${supportedList()}.`);
1256
+ }
1257
+ return dedupe(tags);
1258
+ }
1259
+ var AUTORAG_LANGUAGE_TAGS, DEFAULT_LANGUAGES, LanguageError;
1260
+ var init_language = __esm(() => {
1261
+ AUTORAG_LANGUAGE_TAGS = [
1262
+ "ko",
1263
+ "en",
1264
+ "ja",
1265
+ "zh-hans",
1266
+ "zh-hant",
1267
+ "fr",
1268
+ "de",
1269
+ "es",
1270
+ "ru",
1271
+ "it",
1272
+ "pt",
1273
+ "vi",
1274
+ "th",
1275
+ "ar",
1276
+ "hi"
1277
+ ];
1278
+ DEFAULT_LANGUAGES = ["ko", "en"];
1279
+ LanguageError = class LanguageError extends Error {
1280
+ name = "LanguageError";
1281
+ };
1282
+ });
1283
+
1215
1284
  // src/manifest/loader.ts
1216
1285
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3 } from "node:fs";
1217
1286
  import { extname as extname2, join as join6 } from "node:path";
@@ -4981,475 +5050,220 @@ var init_eml = __esm(() => {
4981
5050
  };
4982
5051
  });
4983
5052
 
4984
- // src/parser/hwp-markdown.ts
4985
- function renderHwpMarkdown(document) {
4986
- const tables = new Map;
4987
- for (const table of document.tables) {
4988
- const key = `${table.sectionIndex}:${table.parentParagraphIndex}`;
4989
- const group = tables.get(key) ?? [];
4990
- group.push(table);
4991
- tables.set(key, group);
4992
- }
4993
- for (const group of tables.values()) {
4994
- group.sort((left, right) => left.controlIndex - right.controlIndex);
4995
- }
4996
- const blocks = [];
4997
- for (const paragraph of document.paragraphs) {
4998
- const text = cleanText(paragraph.text);
4999
- if (hasRenderableText(text))
5000
- blocks.push(text);
5001
- for (const table of tables.get(`${paragraph.sectionIndex}:${paragraph.paragraphIndex}`) ?? []) {
5002
- const rendered = renderTable(table);
5003
- if (rendered.length > 0)
5004
- blocks.push(rendered);
5005
- }
5006
- }
5007
- return blocks.join(`
5008
-
5009
- `);
5010
- }
5011
- function cleanText(text) {
5012
- return text.replace(/[\u0000\uFFFC]/gu, "");
5013
- }
5014
- function hasRenderableText(text) {
5015
- return text.trim().length > 0;
5016
- }
5017
- function renderTable(table) {
5018
- const cells = table.cells.map((cell) => ({ ...cell, text: renderCell(cell) }));
5019
- if (!cells.some((cell) => hasRenderableText(cell.text)))
5020
- return "";
5021
- if (cells.some((cell) => cell.rowSpan > 1 || cell.columnSpan > 1))
5022
- return renderMergedTable(cells);
5023
- const cellsByPosition = new Map(cells.map((cell) => [`${cell.row}:${cell.column}`, cell.text]));
5024
- const header = Array.from({ length: table.columnCount }, (_, column) => `Column ${column + 1}`);
5025
- const rows = Array.from({ length: table.rowCount }, (_, row) => Array.from({ length: table.columnCount }, (_, column) => cellsByPosition.get(`${row}:${column}`) ?? ""));
5026
- return [
5027
- `| ${header.join(" | ")} |`,
5028
- `| ${header.map(() => "---").join(" | ")} |`,
5029
- ...rows.map((row) => `| ${row.join(" | ")} |`)
5030
- ].join(`
5031
- `);
5053
+ // src/parser/kordoc.ts
5054
+ import { parse as kordocParse } from "kordoc";
5055
+ function toKordocInput(bytes) {
5056
+ return Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
5032
5057
  }
5033
- function renderMergedTable(cells) {
5034
- const rows = new Map;
5035
- const coveredPositions = new Set;
5036
- for (const cell of [...cells].sort((left, right) => left.row - right.row || left.column - right.column)) {
5037
- const position = `${cell.row}:${cell.column}`;
5038
- if (coveredPositions.has(position))
5039
- continue;
5040
- const row = rows.get(cell.row) ?? [];
5041
- row.push(cell);
5042
- rows.set(cell.row, row);
5043
- for (let rowIndex = cell.row;rowIndex < cell.row + Math.max(1, cell.rowSpan); rowIndex += 1) {
5044
- for (let columnIndex = cell.column;columnIndex < cell.column + Math.max(1, cell.columnSpan); columnIndex += 1) {
5045
- if (rowIndex !== cell.row || columnIndex !== cell.column)
5046
- coveredPositions.add(`${rowIndex}:${columnIndex}`);
5047
- }
5048
- }
5058
+ var KORDOC_EXTENSIONS, defaultParse = async (bytes, options) => {
5059
+ const result = await kordocParse(toKordocInput(bytes), {
5060
+ plain: true,
5061
+ ...options.ocr === undefined ? {} : { ocr: options.ocr }
5062
+ });
5063
+ if (result.success) {
5064
+ return {
5065
+ success: true,
5066
+ fileType: result.fileType,
5067
+ markdown: result.markdown,
5068
+ warnings: result.warnings
5069
+ };
5049
5070
  }
5050
- const renderedRows = [...rows.entries()].sort(([left], [right]) => left - right).map(([row, cellsInRow]) => {
5051
- const text = cellsInRow.sort((left, right) => left.column - right.column).map((cell) => cell.text).filter(hasRenderableText).join(" | ");
5052
- return hasRenderableText(text) ? `Row ${row + 1}: ${text}` : "";
5053
- }).filter((row) => row.length > 0);
5054
- return ["[Table]", ...renderedRows].join(`
5055
- `);
5056
- }
5057
- function renderCell(cell) {
5058
- return cell.paragraphs.map(cleanText).filter(hasRenderableText).join("<br>").replaceAll("\\", "\\\\").replace(/\r\n|\r|\n/gu, "<br>").replaceAll("|", "\\|");
5059
- }
5060
-
5061
- // src/parser/rhwp-adapter.ts
5062
- import { readFile as readFile3 } from "node:fs/promises";
5063
- import initRhwp, { HwpDocument } from "@rhwp/core";
5064
- function createRhwpExtractor(runtime = defaultRhwpRuntime, overrides = {}) {
5065
- const limits = resolveLimits(overrides);
5066
- const initialize = runtime === defaultRhwpRuntime ? initializeDefaultRhwpRuntime : createRuntimeInitializer(runtime);
5067
- return async (bytes) => {
5068
- await initialize();
5069
- const document = runtime.open(bytes);
5070
- try {
5071
- return extractDocument(document, limits);
5072
- } finally {
5073
- document.free();
5071
+ return { success: false, fileType: result.fileType, error: result.error, code: result.code };
5072
+ }, KordocParser;
5073
+ var init_kordoc = __esm(() => {
5074
+ init_errors();
5075
+ KORDOC_EXTENSIONS = [".hwp", ".hwpx", ".hml", ".hwpml", ".pdf", ".docx", ".xlsx", ".xls"];
5076
+ KordocParser = class KordocParser extends Parser {
5077
+ name = "kordoc";
5078
+ extensions = KORDOC_EXTENSIONS;
5079
+ #parse;
5080
+ #ocr;
5081
+ constructor(options = {}) {
5082
+ super();
5083
+ this.#parse = options.parse ?? defaultParse;
5084
+ this.#ocr = options.ocr;
5074
5085
  }
5075
- };
5076
- }
5077
- function createRuntimeInitializer(runtime) {
5078
- let initialization;
5079
- return () => {
5080
- if (initialization === undefined) {
5081
- const attempt = runtime.initialize();
5082
- initialization = attempt.catch((error) => {
5083
- initialization = undefined;
5084
- throw error;
5085
- });
5086
+ async parse(input) {
5087
+ let result;
5088
+ try {
5089
+ result = await this.#parse(input.bytes, { ocr: this.#ocr });
5090
+ } catch (cause) {
5091
+ throw new ParseError(this.name, input.virtualPath, cause);
5092
+ }
5093
+ if (!result.success) {
5094
+ throw new ParseError(this.name, input.virtualPath, new Error(`${result.code ?? "PARSE_ERROR"}: ${result.error}`));
5095
+ }
5096
+ const diagnostics = (result.warnings ?? []).map((warning) => ({
5097
+ code: "parser-warning",
5098
+ severity: "warning",
5099
+ message: warning.code ? `${warning.code}: ${warning.message}` : warning.message
5100
+ }));
5101
+ return {
5102
+ markdown: result.markdown,
5103
+ metadata: { parser: this.name, format: result.fileType ?? "unknown" },
5104
+ ...diagnostics.length > 0 ? { diagnostics } : {}
5105
+ };
5086
5106
  }
5087
- return initialization;
5088
5107
  };
5108
+ });
5109
+
5110
+ // src/parser/ocr.ts
5111
+ import { createWorker } from "tesseract.js";
5112
+ function engineWithNoopCleanup(engine) {
5113
+ return (input) => ({ result: engine(input), cleanup: Promise.resolve() });
5089
5114
  }
5090
- function extractDocument(document, limits) {
5091
- const paragraphs = [];
5092
- const paragraphCounts = [];
5093
- const counters = { characters: 0, tables: 0, cells: 0, cellParagraphs: 0 };
5094
- const sectionCount = readCount(document.getSectionCount(), "section count");
5095
- checkBudget("maxSections", sectionCount, limits);
5096
- for (let sectionIndex = 0;sectionIndex < sectionCount; sectionIndex += 1) {
5097
- const paragraphCount = readCount(document.getParagraphCount(sectionIndex), `paragraph count for section ${sectionIndex}`);
5098
- paragraphCounts.push(paragraphCount);
5099
- checkBudget("maxBodyParagraphs", paragraphs.length + paragraphCount, limits);
5100
- for (let paragraphIndex = 0;paragraphIndex < paragraphCount; paragraphIndex += 1) {
5101
- const length = readCount(document.getParagraphLength(sectionIndex, paragraphIndex), `paragraph length at ${sectionIndex}:${paragraphIndex}`);
5102
- const text = readText(document.getTextRange(sectionIndex, paragraphIndex, 0, length), `paragraph text at ${sectionIndex}:${paragraphIndex}`);
5103
- counters.characters += [...text].length;
5104
- checkBudget("maxCharacters", counters.characters, limits);
5105
- paragraphs.push({
5106
- sectionIndex,
5107
- paragraphIndex,
5108
- text
5109
- });
5110
- }
5111
- }
5112
- return { paragraphs, tables: extractTables(document, limits, counters, paragraphCounts) };
5113
- }
5114
- function extractTables(document, limits, counters, paragraphCounts) {
5115
- const tables = [];
5116
- const seen = new Set;
5117
- const pageCount = readCount(document.pageCount(), "page count");
5118
- for (let pageIndex = 0;pageIndex < pageCount; pageIndex += 1) {
5119
- const layout = readJsonRecord(document.getPageControlLayout(pageIndex), `page ${pageIndex} control layout`);
5120
- const controls = readArray(layout.controls, `controls for page ${pageIndex}`);
5121
- for (let layoutIndex = 0;layoutIndex < controls.length; layoutIndex += 1) {
5122
- const control = readRecord(controls[layoutIndex], `control ${layoutIndex} on page ${pageIndex}`);
5123
- if (typeof control.type !== "string") {
5124
- throw new TypeError(`rhwp returned an invalid control type at page ${pageIndex}:${layoutIndex}`);
5125
- }
5126
- if (control.type !== "table")
5127
- continue;
5128
- const label = `table at page ${pageIndex}:${layoutIndex}`;
5129
- const coordinates = readTableCoordinates(control, label, paragraphCounts, limits);
5130
- if (coordinates === undefined)
5131
- continue;
5132
- const key = `${coordinates.sectionIndex}:${coordinates.parentParagraphIndex}:${coordinates.controlIndex}`;
5133
- if (seen.has(key))
5134
- continue;
5135
- seen.add(key);
5136
- counters.tables += 1;
5137
- checkBudget("maxTables", counters.tables, limits);
5138
- tables.push(extractTable(document, coordinates, limits, counters));
5139
- }
5140
- }
5141
- return tables;
5115
+ function tesseractOcr(input) {
5116
+ return createTesseractOcrOperation(input);
5142
5117
  }
5143
- function readTableCoordinates(control, label, paragraphCounts, limits) {
5144
- const hasSectionIndex = Object.hasOwn(control, "secIdx");
5145
- const hasParagraphIndex = Object.hasOwn(control, "paraIdx");
5146
- const hasControlIndex = Object.hasOwn(control, "controlIdx");
5147
- const coordinateCount = Number(hasSectionIndex) + Number(hasParagraphIndex) + Number(hasControlIndex);
5148
- if (coordinateCount === 0) {
5149
- validateDeferredNestedTable(control, label, limits);
5118
+ function createTesseractOcrOperation(input) {
5119
+ let cleanupResolve = () => {
5150
5120
  return;
5151
- }
5152
- if (coordinateCount !== 3) {
5153
- throw new TypeError(`rhwp returned a partial coordinate set for ${label}`);
5154
- }
5155
- if (Object.hasOwn(control, "cellPath") || Object.hasOwn(control, "parentParaIdx")) {
5156
- throw new TypeError(`rhwp returned conflicting top-level and nested context for ${label}`);
5157
- }
5158
- const coordinates = {
5159
- sectionIndex: readCount(control.secIdx, `secIdx for ${label}`),
5160
- parentParagraphIndex: readCount(control.paraIdx, `paraIdx for ${label}`),
5161
- controlIndex: readCount(control.controlIdx, `controlIdx for ${label}`)
5162
5121
  };
5163
- const paragraphCount = paragraphCounts[coordinates.sectionIndex];
5164
- if (paragraphCount === undefined) {
5165
- throw new TypeError(`rhwp returned a section coordinate outside the announced document range for ${label}`);
5166
- }
5167
- if (coordinates.parentParagraphIndex >= paragraphCount) {
5168
- throw new TypeError(`rhwp returned a parent paragraph coordinate outside the announced section range for ${label}`);
5169
- }
5170
- return coordinates;
5171
- }
5172
- function validateDeferredNestedTable(control, label, limits) {
5173
- if (!Object.hasOwn(control, "cellPath") && !Object.hasOwn(control, "parentParaIdx")) {
5174
- validateDeferredNestedLayout(control, label, limits);
5122
+ let cleanupReject = () => {
5175
5123
  return;
5176
- }
5177
- readCount(control.parentParaIdx, `parentParaIdx for deferred nested ${label}`);
5178
- const cellPath = readArray(control.cellPath, `cellPath for deferred nested ${label}`);
5179
- if (cellPath.length === 0) {
5180
- throw new TypeError(`rhwp returned an empty cellPath for deferred nested ${label}`);
5181
- }
5182
- checkBudget("maxCells", cellPath.length, limits);
5183
- for (let pathIndex = 0;pathIndex < cellPath.length; pathIndex += 1) {
5184
- const entry = readRecord(cellPath[pathIndex], `cellPath entry ${pathIndex} for deferred nested ${label}`);
5185
- readCount(entry.controlIndex, `controlIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
5186
- readCount(entry.cellIndex, `cellIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
5187
- readCount(entry.cellParaIndex, `cellParaIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
5188
- }
5189
- }
5190
- function validateDeferredNestedLayout(control, label, limits) {
5191
- readFiniteNumber(control.x, `x for deferred nested ${label}`);
5192
- readFiniteNumber(control.y, `y for deferred nested ${label}`);
5193
- readPositiveFiniteNumber(control.w, `w for deferred nested ${label}`);
5194
- readPositiveFiniteNumber(control.h, `h for deferred nested ${label}`);
5195
- const rowCount = readPositiveCount(control.rowCount, `rowCount for deferred nested ${label}`);
5196
- const columnCount = readPositiveCount(control.colCount, `colCount for deferred nested ${label}`);
5197
- const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
5198
- readCount(control.plane, `plane for deferred nested ${label}`);
5199
- readSafeInteger(control.zOrder, `zOrder for deferred nested ${label}`);
5200
- readCount(control.stableIndex, `stableIndex for deferred nested ${label}`);
5201
- const cells = readArray(control.cells, `cells for deferred nested ${label}`);
5202
- if (cells.length === 0) {
5203
- throw new TypeError(`rhwp returned empty cells for deferred nested ${label}`);
5204
- }
5205
- checkBudget("maxCells", cells.length, limits);
5206
- if (cells.length > logicalCellCount) {
5207
- throw new TypeError(`rhwp returned too many cells for deferred nested ${label}`);
5208
- }
5209
- const anchors = new Set;
5210
- const cellIndexes = new Set;
5211
- const coveredCoordinates = new Set;
5212
- for (let cellIndex = 0;cellIndex < cells.length; cellIndex += 1) {
5213
- const cell = readRecord(cells[cellIndex], `cell ${cellIndex} for deferred nested ${label}`);
5214
- readFiniteNumber(cell.x, `x for cell ${cellIndex} in deferred nested ${label}`);
5215
- readFiniteNumber(cell.y, `y for cell ${cellIndex} in deferred nested ${label}`);
5216
- readPositiveFiniteNumber(cell.w, `w for cell ${cellIndex} in deferred nested ${label}`);
5217
- readPositiveFiniteNumber(cell.h, `h for cell ${cellIndex} in deferred nested ${label}`);
5218
- const row = readCount(cell.row, `row for cell ${cellIndex} in deferred nested ${label}`);
5219
- const column = readCount(cell.col, `col for cell ${cellIndex} in deferred nested ${label}`);
5220
- const rowSpan = readPositiveCount(cell.rowSpan, `rowSpan for cell ${cellIndex} in deferred nested ${label}`);
5221
- const columnSpan = readPositiveCount(cell.colSpan, `colSpan for cell ${cellIndex} in deferred nested ${label}`);
5222
- const runtimeCellIndex = readCount(cell.cellIdx, `cellIdx for cell ${cellIndex} in deferred nested ${label}`);
5223
- if (row >= rowCount || rowSpan > rowCount - row) {
5224
- throw new TypeError(`rhwp returned an out-of-range row span for cell ${cellIndex} in deferred nested ${label}`);
5225
- }
5226
- if (column >= columnCount || columnSpan > columnCount - column) {
5227
- throw new TypeError(`rhwp returned an out-of-range col span for cell ${cellIndex} in deferred nested ${label}`);
5228
- }
5229
- if (cellIndexes.has(runtimeCellIndex)) {
5230
- throw new TypeError(`rhwp returned a duplicate cellIdx for deferred nested ${label}`);
5231
- }
5232
- cellIndexes.add(runtimeCellIndex);
5233
- const anchor = `${row}:${column}`;
5234
- if (anchors.has(anchor)) {
5235
- throw new TypeError(`rhwp returned a duplicate cell anchor for deferred nested ${label}`);
5236
- }
5237
- anchors.add(anchor);
5238
- for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
5239
- for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
5240
- const coordinate = `${coveredRow}:${coveredColumn}`;
5241
- if (coveredCoordinates.has(coordinate)) {
5242
- throw new TypeError(`rhwp returned overlapping cell spans for deferred nested ${label}`);
5243
- }
5244
- coveredCoordinates.add(coordinate);
5245
- }
5246
- }
5247
- }
5248
- }
5249
- function extractTable(document, coordinates, limits, counters) {
5250
- const { sectionIndex, parentParagraphIndex, controlIndex } = coordinates;
5251
- const key = `${sectionIndex}:${parentParagraphIndex}:${controlIndex}`;
5252
- const dimensions = readJsonRecord(document.getTableDimensions(sectionIndex, parentParagraphIndex, controlIndex), `table dimensions at ${key}`);
5253
- const rowCount = readPositiveCount(dimensions.rowCount, `rowCount for table ${key}`);
5254
- const columnCount = readPositiveCount(dimensions.colCount, `colCount for table ${key}`);
5255
- const cellCount = readCount(dimensions.cellCount, `cellCount for table ${key}`);
5256
- const logicalCellCount = chargeLogicalCells(rowCount, columnCount, counters, limits);
5257
- if (cellCount === 0 || cellCount > logicalCellCount) {
5258
- throw new TypeError(`rhwp returned an invalid cellCount for table ${key}; expected 1..${logicalCellCount}, received ${cellCount}`);
5259
- }
5260
- const cellGeometry = [];
5261
- const anchors = new Set;
5262
- const coveredCoordinates = new Set;
5263
- for (let cellIndex = 0;cellIndex < cellCount; cellIndex += 1) {
5264
- const cellInfo = readJsonRecord(document.getCellInfo(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell metadata at ${key}:${cellIndex}`);
5265
- const row = readCount(cellInfo.row, `row for cell ${key}:${cellIndex}`);
5266
- const column = readCount(cellInfo.col, `col for cell ${key}:${cellIndex}`);
5267
- const rowSpan = readPositiveCount(cellInfo.rowSpan, `rowSpan for cell ${key}:${cellIndex}`);
5268
- const columnSpan = readPositiveCount(cellInfo.colSpan, `colSpan for cell ${key}:${cellIndex}`);
5269
- if (row >= rowCount) {
5270
- throw new TypeError(`rhwp returned a row outside the table range for cell ${key}:${cellIndex}`);
5271
- }
5272
- if (column >= columnCount) {
5273
- throw new TypeError(`rhwp returned a col outside the table range for cell ${key}:${cellIndex}`);
5274
- }
5275
- if (rowSpan > rowCount - row) {
5276
- throw new TypeError(`rhwp returned a rowSpan outside the table range for cell ${key}:${cellIndex}`);
5277
- }
5278
- if (columnSpan > columnCount - column) {
5279
- throw new TypeError(`rhwp returned a colSpan outside the table range for cell ${key}:${cellIndex}`);
5280
- }
5281
- const anchor = row * columnCount + column;
5282
- if (anchors.has(anchor)) {
5283
- throw new TypeError(`rhwp returned a duplicate cell anchor for table ${key} at ${row}:${column}`);
5284
- }
5285
- anchors.add(anchor);
5286
- for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
5287
- for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
5288
- const coordinate = coveredRow * columnCount + coveredColumn;
5289
- if (coveredCoordinates.has(coordinate)) {
5290
- throw new TypeError(`rhwp returned overlapping cell spans for table ${key} at ${coveredRow}:${coveredColumn}`);
5291
- }
5292
- coveredCoordinates.add(coordinate);
5293
- }
5294
- }
5295
- cellGeometry.push({ row, column, rowSpan, columnSpan });
5296
- }
5297
- if (coveredCoordinates.size !== logicalCellCount) {
5298
- throw new TypeError(`rhwp returned cellCount ${cellCount} that covers ${coveredCoordinates.size} of ${logicalCellCount} logical grid coordinates for table ${key}`);
5299
- }
5300
- const cells = [];
5301
- for (let cellIndex = 0;cellIndex < cellGeometry.length; cellIndex += 1) {
5302
- const geometry = cellGeometry[cellIndex];
5303
- if (geometry === undefined)
5304
- throw new TypeError(`rhwp omitted cell geometry at ${key}:${cellIndex}`);
5305
- const paragraphCount = readCount(document.getCellParagraphCount(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell paragraph count at ${key}:${cellIndex}`);
5306
- counters.cellParagraphs += paragraphCount;
5307
- checkBudget("maxCellParagraphs", counters.cellParagraphs, limits);
5308
- const paragraphs = [];
5309
- for (let cellParagraphIndex = 0;cellParagraphIndex < paragraphCount; cellParagraphIndex += 1) {
5310
- const length = readCount(document.getCellParagraphLength(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex), `cell paragraph length at ${key}:${cellIndex}:${cellParagraphIndex}`);
5311
- const text = readText(document.getTextInCell(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex, 0, length), `cell paragraph text at ${key}:${cellIndex}:${cellParagraphIndex}`);
5312
- counters.characters += [...text].length;
5313
- checkBudget("maxCharacters", counters.characters, limits);
5314
- paragraphs.push(text);
5315
- }
5316
- cells.push({
5317
- ...geometry,
5318
- paragraphs
5319
- });
5320
- }
5321
- return {
5322
- sectionIndex,
5323
- parentParagraphIndex,
5324
- controlIndex,
5325
- rowCount,
5326
- columnCount,
5327
- cells
5328
5124
  };
5125
+ const cleanup = new Promise((resolve, reject) => {
5126
+ cleanupResolve = resolve;
5127
+ cleanupReject = reject;
5128
+ });
5129
+ const result = runTesseractOcr(input, cleanupResolve, cleanupReject);
5130
+ return { result, cleanup };
5329
5131
  }
5330
- function chargeLogicalCells(rowCount, columnCount, counters, limits) {
5331
- const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
5332
- if (logicalCellCount > limits.maxCells - counters.cells) {
5333
- throw new HwpExtractionBudgetError("maxCells", counters.cells + logicalCellCount, limits.maxCells);
5334
- }
5335
- counters.cells += logicalCellCount;
5336
- return logicalCellCount;
5337
- }
5338
- function readLogicalCellCount(rowCount, columnCount, limits) {
5339
- if (rowCount > Math.floor(limits.maxCells / columnCount)) {
5340
- throw new HwpExtractionBudgetError("maxCells", rowCount * columnCount, limits.maxCells);
5341
- }
5342
- return rowCount * columnCount;
5343
- }
5344
- function resolveLimits(overrides) {
5345
- const limits = { ...DEFAULT_LIMITS };
5346
- for (const name of Object.keys(DEFAULT_LIMITS)) {
5347
- const value = overrides[name];
5348
- if (value === undefined)
5349
- continue;
5350
- if (!Number.isSafeInteger(value) || value <= 0) {
5351
- throw new TypeError(`${name} must be a positive finite safe integer`);
5132
+ async function runTesseractOcr(input, cleanupResolve, cleanupReject) {
5133
+ let worker;
5134
+ let termination;
5135
+ const terminate = async () => {
5136
+ if (worker === undefined)
5137
+ return;
5138
+ termination ??= worker.terminate().then(() => {
5139
+ return;
5140
+ });
5141
+ await termination;
5142
+ };
5143
+ const abort = () => {
5144
+ if (worker !== undefined) {
5145
+ terminate().then(cleanupResolve, cleanupReject);
5352
5146
  }
5353
- limits[name] = value;
5354
- }
5355
- return limits;
5356
- }
5357
- function checkBudget(name, actual, limits) {
5358
- if (actual > limits[name])
5359
- throw new HwpExtractionBudgetError(name, actual, limits[name]);
5360
- }
5361
- function readCount(value, label) {
5362
- if (!Number.isSafeInteger(value) || value < 0) {
5363
- throw new TypeError(`rhwp returned an invalid ${label}; expected a non-negative safe integer`);
5364
- }
5365
- return value;
5366
- }
5367
- function readPositiveCount(value, label) {
5368
- const count = readCount(value, label);
5369
- if (count === 0) {
5370
- throw new TypeError(`rhwp returned an invalid ${label}; expected a positive safe integer`);
5371
- }
5372
- return count;
5373
- }
5374
- function readSafeInteger(value, label) {
5375
- if (!Number.isSafeInteger(value)) {
5376
- throw new TypeError(`rhwp returned an invalid ${label}; expected a safe integer`);
5377
- }
5378
- return value;
5379
- }
5380
- function readFiniteNumber(value, label) {
5381
- if (typeof value !== "number" || !Number.isFinite(value)) {
5382
- throw new TypeError(`rhwp returned an invalid ${label}; expected a finite number`);
5383
- }
5384
- return value;
5385
- }
5386
- function readPositiveFiniteNumber(value, label) {
5387
- const number = readFiniteNumber(value, label);
5388
- if (number <= 0) {
5389
- throw new TypeError(`rhwp returned an invalid ${label}; expected a positive finite number`);
5390
- }
5391
- return number;
5392
- }
5393
- function readText(value, label) {
5394
- if (typeof value !== "string")
5395
- throw new TypeError(`rhwp returned invalid ${label}; expected a string`);
5396
- return value;
5397
- }
5398
- function readJsonRecord(value, label) {
5399
- const text = readText(value, label);
5400
- let parsed;
5147
+ };
5148
+ input.signal.addEventListener("abort", abort, { once: true });
5401
5149
  try {
5402
- parsed = JSON.parse(text);
5403
- } catch (cause) {
5404
- throw new TypeError(`rhwp returned malformed JSON for ${label}`, { cause });
5405
- }
5406
- return readRecord(parsed, label);
5407
- }
5408
- function readRecord(value, label) {
5409
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
5410
- throw new TypeError(`rhwp returned invalid ${label}; expected an object`);
5150
+ worker = await createWorker(input.languages.join("+"));
5151
+ if (input.signal.aborted)
5152
+ throw new Error("OCR aborted before worker was ready");
5153
+ const result = await worker.recognize(Buffer.from(input.bytes));
5154
+ return result.data.text;
5155
+ } finally {
5156
+ input.signal.removeEventListener("abort", abort);
5157
+ await terminate().then(cleanupResolve, cleanupReject);
5411
5158
  }
5412
- return value;
5413
5159
  }
5414
- function readArray(value, label) {
5415
- if (!Array.isArray(value))
5416
- throw new TypeError(`rhwp returned invalid ${label}; expected an array`);
5417
- return value;
5160
+ function withTimeout(operation, timeoutMs, onTimeout) {
5161
+ return new Promise((resolve, reject) => {
5162
+ const timeout = setTimeout(() => {
5163
+ onTimeout();
5164
+ operation.cleanup.finally(() => reject(new Error(`OCR timed out after ${timeoutMs}ms`)));
5165
+ }, timeoutMs);
5166
+ operation.result.then((value) => {
5167
+ clearTimeout(timeout);
5168
+ resolve(value);
5169
+ }, (error) => {
5170
+ clearTimeout(timeout);
5171
+ operation.cleanup.then(() => reject(error), (cleanupError) => reject(cleanupError));
5172
+ });
5173
+ });
5418
5174
  }
5419
- var DEFAULT_LIMITS, HwpExtractionBudgetError, defaultRhwpRuntime, initializeDefaultRhwpRuntime;
5420
- var init_rhwp_adapter = __esm(() => {
5421
- DEFAULT_LIMITS = {
5422
- maxSections: 256,
5423
- maxBodyParagraphs: 200000,
5424
- maxTables: 20000,
5425
- maxCells: 500000,
5426
- maxCellParagraphs: 1e6,
5427
- maxCharacters: 20000000
5428
- };
5429
- HwpExtractionBudgetError = class HwpExtractionBudgetError extends Error {
5430
- code = "HWP_EXTRACTION_BUDGET_EXCEEDED";
5431
- limit;
5432
- actual;
5433
- maximum;
5434
- constructor(limit, actual, maximum) {
5435
- super(`HWP extraction exceeded ${limit} budget of ${maximum} (received ${actual})`);
5436
- this.name = "HwpExtractionBudgetError";
5437
- this.limit = limit;
5438
- this.actual = actual;
5439
- this.maximum = maximum;
5175
+ var DEFAULT_OCR_TIMEOUT_MS = 30000, DEFAULT_OCR_LANGUAGES, ImageOcrParser;
5176
+ var init_ocr = __esm(() => {
5177
+ init_errors();
5178
+ init_text();
5179
+ DEFAULT_OCR_LANGUAGES = ["eng"];
5180
+ ImageOcrParser = class ImageOcrParser extends Parser {
5181
+ name = "image-ocr";
5182
+ extensions = [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".webp"];
5183
+ languages;
5184
+ timeoutMs;
5185
+ maxBytes;
5186
+ engine;
5187
+ constructor(options) {
5188
+ super();
5189
+ this.languages = options.languages ?? DEFAULT_OCR_LANGUAGES;
5190
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS;
5191
+ this.maxBytes = options.maxBytes;
5192
+ this.engine = options.engine ? engineWithNoopCleanup(options.engine) : tesseractOcr;
5440
5193
  }
5441
- };
5442
- defaultRhwpRuntime = {
5443
- async initialize() {
5444
- const rhwpModuleUrl = import.meta.resolve("@rhwp/core");
5445
- const wasmBytes = await readFile3(new URL("./rhwp_bg.wasm", rhwpModuleUrl));
5446
- await initRhwp({ module_or_path: wasmBytes });
5447
- },
5448
- open(bytes) {
5449
- return new HwpDocument(bytes);
5194
+ async parse(input) {
5195
+ const controller = new AbortController;
5196
+ try {
5197
+ if (this.maxBytes !== undefined && input.bytes.byteLength > this.maxBytes) {
5198
+ throw new Error(`OCR input exceeds maxBytes budget of ${this.maxBytes}`);
5199
+ }
5200
+ const operation = this.engine({
5201
+ bytes: input.bytes,
5202
+ languages: this.languages,
5203
+ timeoutMs: this.timeoutMs,
5204
+ signal: controller.signal
5205
+ });
5206
+ const markdown = await withTimeout(operation, this.timeoutMs, () => controller.abort());
5207
+ return {
5208
+ markdown: normalizeMarkdown(markdown),
5209
+ metadata: { parser: this.name, languages: [...this.languages] }
5210
+ };
5211
+ } catch (cause) {
5212
+ throw new ParseError(this.name, input.virtualPath, cause);
5213
+ } finally {
5214
+ controller.abort();
5215
+ }
5450
5216
  }
5451
5217
  };
5452
- initializeDefaultRhwpRuntime = createRuntimeInitializer(defaultRhwpRuntime);
5218
+ });
5219
+
5220
+ // src/parser/ocr-engines.ts
5221
+ function tesseractLanguageListFor(tags) {
5222
+ const languages = [...new Set(tags)].map((tag) => TESSERACT_LANGUAGES[tag]);
5223
+ if (languages.length === 0) {
5224
+ throw new Error("OCR provider requires at least one language.");
5225
+ }
5226
+ return languages;
5227
+ }
5228
+ function tesseractLanguageFor(tags) {
5229
+ return tesseractLanguageListFor(tags).join("+");
5230
+ }
5231
+ function createTesseractOcrProvider(options) {
5232
+ const language = tesseractLanguageFor(options.languages);
5233
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TESSERACT_TIMEOUT_MS;
5234
+ const engine = options.engine ?? ((resolvedLanguage, pageImage, pageNumber, mimeType) => defaultTesseractEngine(resolvedLanguage, pageImage, pageNumber, mimeType, timeoutMs));
5235
+ return (pageImage, pageNumber, mimeType) => engine(language, pageImage, pageNumber, mimeType);
5236
+ }
5237
+ async function defaultTesseractEngine(language, pageImage, _pageNumber, _mimeType, timeoutMs) {
5238
+ const controller = new AbortController;
5239
+ const operation = createTesseractOcrOperation({
5240
+ bytes: pageImage,
5241
+ languages: [language],
5242
+ timeoutMs,
5243
+ signal: controller.signal
5244
+ });
5245
+ return withTimeout(operation, timeoutMs, () => controller.abort());
5246
+ }
5247
+ var TESSERACT_LANGUAGES, DEFAULT_TESSERACT_TIMEOUT_MS = 30000;
5248
+ var init_ocr_engines = __esm(() => {
5249
+ init_ocr();
5250
+ TESSERACT_LANGUAGES = {
5251
+ ko: "kor",
5252
+ en: "eng",
5253
+ ja: "jpn",
5254
+ "zh-hans": "chi_sim",
5255
+ "zh-hant": "chi_tra",
5256
+ fr: "fra",
5257
+ de: "deu",
5258
+ es: "spa",
5259
+ ru: "rus",
5260
+ it: "ita",
5261
+ pt: "por",
5262
+ vi: "vie",
5263
+ th: "tha",
5264
+ ar: "ara",
5265
+ hi: "hin"
5266
+ };
5453
5267
  });
5454
5268
 
5455
5269
  // src/parser/xml-text.ts
@@ -5552,38 +5366,22 @@ var init_xml_text = __esm(() => {
5552
5366
  });
5553
5367
  });
5554
5368
 
5555
- // src/parser/hwp.ts
5556
- import { extname as extname3 } from "node:path";
5557
- var HwpParser;
5558
- var init_hwp = __esm(() => {
5369
+ // src/parser/office.ts
5370
+ var PptxParser;
5371
+ var init_office = __esm(() => {
5559
5372
  init_errors();
5560
- init_rhwp_adapter();
5561
5373
  init_text();
5562
5374
  init_xml_text();
5563
- HwpParser = class HwpParser extends Parser {
5564
- name = "hwp";
5565
- extensions = [".hwpx", ".hwp"];
5566
- extractor;
5567
- constructor(options = {}) {
5568
- super();
5569
- this.extractor = options.extractor ?? createRhwpExtractor(undefined, options.limits);
5570
- }
5375
+ PptxParser = class PptxParser extends Parser {
5376
+ name = "pptx";
5377
+ extensions = [".pptx"];
5571
5378
  async parse(input) {
5572
5379
  try {
5573
- if (extname3(input.virtualPath).toLowerCase() === ".hwp") {
5574
- const document = await this.extractor(input.bytes);
5575
- return {
5576
- markdown: normalizeMarkdown(renderHwpMarkdown(document)),
5577
- metadata: { parser: this.name, format: "hwp5" }
5578
- };
5579
- }
5580
- const chunks = await readZipXmlText(input.bytes, /^(?:Contents|content)\/.*\.xml$/i);
5581
- return {
5582
- markdown: normalizeMarkdown(chunks.join(`
5380
+ const chunks = await readZipXmlText(input.bytes, /^ppt\/(?:slides\/slide\d+|notesSlides\/notesSlide\d+)\.xml$/);
5381
+ const markdown = normalizeMarkdown(chunks.filter((chunk) => chunk.trim().length > 0).join(`
5583
5382
 
5584
- `)),
5585
- metadata: { parser: this.name, format: "hwpx" }
5586
- };
5383
+ `));
5384
+ return { markdown, metadata: { parser: this.name } };
5587
5385
  } catch (cause) {
5588
5386
  throw new ParseError(this.name, input.virtualPath, cause);
5589
5387
  }
@@ -5591,340 +5389,21 @@ var init_hwp = __esm(() => {
5591
5389
  };
5592
5390
  });
5593
5391
 
5594
- // src/parser/ocr.ts
5595
- import { createWorker } from "tesseract.js";
5596
- function engineWithNoopCleanup(engine) {
5597
- return (input) => ({ result: engine(input), cleanup: Promise.resolve() });
5598
- }
5599
- function tesseractOcr(input) {
5600
- let cleanupResolve = () => {
5601
- return;
5602
- };
5603
- let cleanupReject = () => {
5604
- return;
5605
- };
5606
- const cleanup = new Promise((resolve, reject) => {
5607
- cleanupResolve = resolve;
5608
- cleanupReject = reject;
5609
- });
5610
- const result = runTesseractOcr(input, cleanupResolve, cleanupReject);
5611
- return { result, cleanup };
5612
- }
5613
- async function runTesseractOcr(input, cleanupResolve, cleanupReject) {
5614
- let worker;
5615
- let termination;
5616
- const terminate = async () => {
5617
- if (worker === undefined)
5618
- return;
5619
- termination ??= worker.terminate().then(() => {
5620
- return;
5621
- });
5622
- await termination;
5623
- };
5624
- const abort = () => {
5625
- if (worker !== undefined) {
5626
- terminate().then(cleanupResolve, cleanupReject);
5627
- }
5628
- };
5629
- input.signal.addEventListener("abort", abort, { once: true });
5630
- try {
5631
- worker = await createWorker(input.languages.join("+"));
5632
- if (input.signal.aborted)
5633
- throw new Error("OCR aborted before worker was ready");
5634
- const result = await worker.recognize(Buffer.from(input.bytes));
5635
- return result.data.text;
5636
- } finally {
5637
- input.signal.removeEventListener("abort", abort);
5638
- await terminate().then(cleanupResolve, cleanupReject);
5639
- }
5640
- }
5641
- function withTimeout(operation, timeoutMs, onTimeout) {
5642
- return new Promise((resolve, reject) => {
5643
- const timeout = setTimeout(() => {
5644
- onTimeout();
5645
- operation.cleanup.finally(() => reject(new Error(`OCR timed out after ${timeoutMs}ms`)));
5646
- }, timeoutMs);
5647
- operation.result.then((value) => {
5648
- clearTimeout(timeout);
5649
- resolve(value);
5650
- }, (error) => {
5651
- clearTimeout(timeout);
5652
- operation.cleanup.then(() => reject(error), (cleanupError) => reject(cleanupError));
5653
- });
5654
- });
5655
- }
5656
- var DEFAULT_OCR_TIMEOUT_MS = 30000, DEFAULT_OCR_LANGUAGES, ImageOcrParser;
5657
- var init_ocr = __esm(() => {
5658
- init_errors();
5659
- init_text();
5660
- DEFAULT_OCR_LANGUAGES = ["eng"];
5661
- ImageOcrParser = class ImageOcrParser extends Parser {
5662
- name = "image-ocr";
5663
- extensions = [".jpg", ".jpeg", ".png", ".bmp", ".tiff"];
5664
- languages;
5665
- timeoutMs;
5666
- maxBytes;
5667
- engine;
5668
- constructor(options) {
5669
- super();
5670
- this.languages = options.languages ?? DEFAULT_OCR_LANGUAGES;
5671
- this.timeoutMs = options.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS;
5672
- this.maxBytes = options.maxBytes;
5673
- this.engine = options.engine ? engineWithNoopCleanup(options.engine) : tesseractOcr;
5674
- }
5675
- async parse(input) {
5676
- const controller = new AbortController;
5677
- try {
5678
- if (this.maxBytes !== undefined && input.bytes.byteLength > this.maxBytes) {
5679
- throw new Error(`OCR input exceeds maxBytes budget of ${this.maxBytes}`);
5680
- }
5681
- const operation = this.engine({
5682
- bytes: input.bytes,
5683
- languages: this.languages,
5684
- timeoutMs: this.timeoutMs,
5685
- signal: controller.signal
5686
- });
5687
- const markdown = await withTimeout(operation, this.timeoutMs, () => controller.abort());
5688
- return {
5689
- markdown: normalizeMarkdown(markdown),
5690
- metadata: { parser: this.name, languages: [...this.languages] }
5691
- };
5692
- } catch (cause) {
5693
- throw new ParseError(this.name, input.virtualPath, cause);
5694
- } finally {
5695
- controller.abort();
5696
- }
5697
- }
5698
- };
5699
- });
5700
-
5701
- // src/parser/office.ts
5702
- import { extname as extname4 } from "node:path";
5703
- import * as XLSX from "xlsx";
5704
- function formatLegacyXls(bytes) {
5705
- const oleMagic = [208, 207, 17, 224, 161, 177, 26, 225];
5706
- if (bytes.length < oleMagic.length || !oleMagic.every((byte, index) => bytes[index] === byte)) {
5707
- throw new Error("legacy XLS input is not an OLE compound document");
5708
- }
5709
- const workbook = XLSX.read(Buffer.from(bytes), { type: "buffer", cellText: true, cellDates: true });
5710
- const markdown = workbook.SheetNames.map((sheetName) => {
5711
- const worksheet = workbook.Sheets[sheetName];
5712
- if (!worksheet)
5713
- return `## ${sheetName}`;
5714
- const rows = XLSX.utils.sheet_to_json(worksheet, {
5715
- header: 1,
5716
- raw: false,
5717
- blankrows: false,
5718
- defval: ""
5719
- });
5720
- const lines = rows.map((row) => formatLegacyXlsRow(row)).filter((row) => row !== undefined);
5721
- return [`## ${sheetName}`, ...lines].join(`
5722
- `);
5723
- }).join(`
5724
-
5725
- `);
5726
- return { markdown: normalizeMarkdown(markdown), metadata: { parser: "xlsx", format: "xls" } };
5727
- }
5728
- function formatLegacyXlsRow(row) {
5729
- let lastContentIndex = -1;
5730
- for (let index = row.length - 1;index >= 0; index -= 1) {
5731
- if (String(row[index] ?? "").length > 0) {
5732
- lastContentIndex = index;
5733
- break;
5734
- }
5735
- }
5736
- if (lastContentIndex < 0)
5737
- return;
5738
- return row.slice(0, lastContentIndex + 1).map((cell) => escapeMarkdownTableCell(String(cell ?? ""))).join(" | ");
5739
- }
5740
- function escapeMarkdownTableCell(value) {
5741
- return value.replaceAll("\\", "\\\\").replaceAll("|", "\\|").replace(/\r\n|\r|\n/g, "<br>");
5742
- }
5743
- function formatMarkdown(parserName, chunks) {
5744
- const markdown = normalizeMarkdown(chunks.filter((chunk) => chunk.trim().length > 0).join(`
5745
-
5746
- `));
5747
- return { markdown, metadata: { parser: parserName } };
5748
- }
5749
- var DocxParser, PptxParser, XlsxParser;
5750
- var init_office = __esm(() => {
5751
- init_errors();
5752
- init_text();
5753
- init_xml_text();
5754
- DocxParser = class DocxParser extends Parser {
5755
- name = "docx";
5756
- extensions = [".docx"];
5757
- async parse(input) {
5758
- try {
5759
- const chunks = await readZipXmlText(input.bytes, /^word\/(?:document|header\d+|footer\d+)\.xml$/);
5760
- return formatMarkdown(this.name, chunks);
5761
- } catch (cause) {
5762
- throw new ParseError(this.name, input.virtualPath, cause);
5763
- }
5764
- }
5765
- };
5766
- PptxParser = class PptxParser extends Parser {
5767
- name = "pptx";
5768
- extensions = [".pptx"];
5769
- async parse(input) {
5770
- try {
5771
- const chunks = await readZipXmlText(input.bytes, /^ppt\/(?:slides\/slide\d+|notesSlides\/notesSlide\d+)\.xml$/);
5772
- return formatMarkdown(this.name, chunks);
5773
- } catch (cause) {
5774
- throw new ParseError(this.name, input.virtualPath, cause);
5775
- }
5776
- }
5777
- };
5778
- XlsxParser = class XlsxParser extends Parser {
5779
- name = "xlsx";
5780
- extensions = [".xlsx", ".xls"];
5781
- async parse(input) {
5782
- try {
5783
- if (extname4(input.virtualPath).toLowerCase() === ".xls") {
5784
- return formatLegacyXls(input.bytes);
5785
- }
5786
- const sharedStrings = await readZipXmlText(input.bytes, /^xl\/sharedStrings\.xml$/);
5787
- const sheetText = await readZipXmlText(input.bytes, /^xl\/worksheets\/sheet\d+\.xml$/);
5788
- return formatMarkdown(this.name, [...sharedStrings, ...sheetText]);
5789
- } catch (cause) {
5790
- throw new ParseError(this.name, input.virtualPath, cause);
5791
- }
5792
- }
5793
- };
5794
- });
5795
-
5796
- // src/parser/opendataloader-pdf.ts
5797
- import { mkdtemp as mkdtemp2, readFile as readFile4, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
5798
- import { tmpdir as tmpdir2 } from "node:os";
5799
- import { basename as basename8, join as join16, parse as parse2 } from "node:path";
5800
- import { convert } from "@opendataloader/pdf";
5801
- async function readMarkdown(outputDir, inputPath) {
5802
- return readFile4(markdownOutputPath(outputDir, inputPath), "utf8");
5803
- }
5804
- function countPdfPages(bytes) {
5805
- const text = Buffer.from(bytes).toString("latin1");
5806
- return Math.max(1, [...text.matchAll(/\/Type\s*\/Page\b/gu)].length);
5807
- }
5808
- async function withTimeout2(operation, timeoutMs) {
5809
- let timer;
5810
- try {
5811
- return await Promise.race([
5812
- operation,
5813
- new Promise((_, reject) => {
5814
- timer = setTimeout(() => reject(new Error(`PDF hybrid extraction timed out after ${timeoutMs}ms`)), timeoutMs);
5815
- })
5816
- ]);
5817
- } finally {
5818
- if (timer !== undefined)
5819
- clearTimeout(timer);
5820
- }
5821
- }
5822
- function markdownOutputPath(outputDir, inputPath) {
5823
- return join16(outputDir, `${parse2(inputPath).name}.md`);
5824
- }
5825
- var OpendataloaderPdfParser;
5826
- var init_opendataloader_pdf = __esm(() => {
5827
- init_errors();
5828
- init_text();
5829
- OpendataloaderPdfParser = class OpendataloaderPdfParser extends Parser {
5830
- name = "opendataloader-pdf";
5831
- extensions = [".pdf"];
5832
- options;
5833
- converter;
5834
- constructor(options = {}) {
5835
- super();
5836
- this.options = options;
5837
- this.converter = options.converter ?? convert;
5838
- }
5839
- async parse(input) {
5840
- const tempRoot = await mkdtemp2(join16(tmpdir2(), "autorag-pdf-"));
5841
- try {
5842
- const inputPath = input.sourcePath ?? join16(tempRoot, basename8(input.virtualPath));
5843
- if (!input.sourcePath) {
5844
- await writeFile3(inputPath, input.bytes);
5845
- }
5846
- if (this.options.ocr?.enabled && this.options.ocr.maxBytes !== undefined && input.bytes.byteLength > this.options.ocr.maxBytes) {
5847
- throw new Error(`PDF OCR input exceeds maxBytes budget of ${this.options.ocr.maxBytes}`);
5848
- }
5849
- const localOptions = {
5850
- outputDir: tempRoot,
5851
- format: "markdown",
5852
- quiet: true,
5853
- imageOutput: "off",
5854
- ...this.options.ocr?.enabled ? {
5855
- hybrid: this.options.ocr.hybrid ?? "docling-fast",
5856
- hybridMode: this.options.ocr.hybridMode ?? "auto",
5857
- hybridTimeout: String(this.options.ocr.timeoutMs ?? 30000)
5858
- } : {}
5859
- };
5860
- await this.converter(inputPath, localOptions);
5861
- const localMarkdown = normalizeMarkdown(await readMarkdown(tempRoot, inputPath));
5862
- const pages = countPdfPages(input.bytes);
5863
- const thin = this.options.thinExtract;
5864
- const shouldRetry = this.options.ocr?.enabled !== true && thin?.enabled !== false && pages >= (thin?.minPages ?? 3) && (localMarkdown.length < (thin?.minChars ?? 800) || localMarkdown.length / pages < (thin?.minCharsPerPage ?? 40));
5865
- if (shouldRetry) {
5866
- try {
5867
- await withTimeout2(this.converter(inputPath, {
5868
- ...localOptions,
5869
- hybrid: thin?.hybrid ?? "docling-fast",
5870
- hybridMode: thin?.hybridMode ?? "auto",
5871
- hybridTimeout: String(thin?.timeoutMs ?? 30000)
5872
- }), thin?.timeoutMs ?? 30000);
5873
- return {
5874
- markdown: normalizeMarkdown(await readMarkdown(tempRoot, inputPath)),
5875
- metadata: { parser: this.name, pages, pdfExtract: "hybrid-retry" }
5876
- };
5877
- } catch {
5878
- return {
5879
- markdown: localMarkdown,
5880
- metadata: {
5881
- parser: this.name,
5882
- pages,
5883
- pdfExtract: "thin-local-fallback"
5884
- },
5885
- diagnostics: [
5886
- {
5887
- code: "pdf-extract-thin",
5888
- severity: "warning",
5889
- message: `Local PDF extraction yielded only ${localMarkdown.length} characters across ${pages} pages.`
5890
- },
5891
- {
5892
- code: "pdf-hybrid-unavailable",
5893
- severity: "warning",
5894
- message: "Hybrid PDF extraction was unavailable; local markdown was retained."
5895
- }
5896
- ]
5897
- };
5898
- }
5899
- }
5900
- return {
5901
- markdown: localMarkdown,
5902
- metadata: { parser: this.name, pages }
5903
- };
5904
- } catch (cause) {
5905
- throw new ParseError(this.name, input.virtualPath, cause);
5906
- } finally {
5907
- await rm3(tempRoot, { recursive: true, force: true });
5908
- }
5909
- }
5910
- };
5911
- });
5912
-
5913
- // src/parser/plain-text.ts
5914
- var PlainTextParser;
5915
- var init_plain_text = __esm(() => {
5916
- init_text();
5917
- PlainTextParser = class PlainTextParser extends Parser {
5918
- name = "plain-text";
5919
- extensions = [".txt", ".text", ".md", ".markdown"];
5920
- async parse(input) {
5921
- return { markdown: decodeText(input.bytes), metadata: { parser: this.name } };
5922
- }
5392
+ // src/parser/plain-text.ts
5393
+ var PlainTextParser;
5394
+ var init_plain_text = __esm(() => {
5395
+ init_text();
5396
+ PlainTextParser = class PlainTextParser extends Parser {
5397
+ name = "plain-text";
5398
+ extensions = [".txt", ".text", ".md", ".markdown"];
5399
+ async parse(input) {
5400
+ return { markdown: decodeText(input.bytes), metadata: { parser: this.name } };
5401
+ }
5923
5402
  };
5924
5403
  });
5925
5404
 
5926
5405
  // src/parser/registry.ts
5927
- import { extname as extname5 } from "node:path";
5406
+ import { extname as extname3 } from "node:path";
5928
5407
 
5929
5408
  class ParserRegistry {
5930
5409
  parsersByExtension = new Map;
@@ -5943,7 +5422,7 @@ class ParserRegistry {
5943
5422
  }
5944
5423
  }
5945
5424
  getForVirtualPath(virtualPath) {
5946
- return this.parsersByExtension.get(normalizeExtension(extname5(virtualPath)));
5425
+ return this.parsersByExtension.get(normalizeExtension(extname3(virtualPath)));
5947
5426
  }
5948
5427
  list() {
5949
5428
  return Array.from(new Set(this.parsersByExtension.values()));
@@ -5956,26 +5435,38 @@ function normalizeExtension(extension) {
5956
5435
  var init_registry = () => {};
5957
5436
 
5958
5437
  // src/parser/defaults.ts
5438
+ function resolveParserOptions(parserOptions, languages) {
5439
+ return { ...parserOptions, languages: parserOptions?.languages ?? languages };
5440
+ }
5959
5441
  function createDefaultParserRegistry(options = {}) {
5442
+ const languages = options.languages ?? DEFAULT_LANGUAGES;
5443
+ const ocrEnabled = options.ocr?.enabled === true;
5444
+ const kordocOptions = {
5445
+ ...options.kordoc,
5446
+ ...options.kordoc?.ocr === undefined && ocrEnabled ? { ocr: createTesseractOcrProvider({ languages, timeoutMs: options.ocr?.timeoutMs }) } : {}
5447
+ };
5960
5448
  const parsers = [
5961
5449
  new PlainTextParser,
5962
- new OpendataloaderPdfParser({ ocr: options.ocr, thinExtract: options.thinExtract }),
5963
- new DocxParser,
5450
+ new KordocParser(kordocOptions),
5964
5451
  new PptxParser,
5965
- new XlsxParser,
5966
- new HwpParser(options.hwp),
5967
5452
  new EmlParser
5968
5453
  ];
5969
- if (options.ocr?.enabled)
5970
- parsers.push(new ImageOcrParser(options.ocr));
5454
+ if (ocrEnabled) {
5455
+ parsers.push(new ImageOcrParser({
5456
+ ...options.ocr,
5457
+ enabled: true,
5458
+ languages: options.ocr?.languages ?? tesseractLanguageListFor(languages)
5459
+ }));
5460
+ }
5971
5461
  return new ParserRegistry(parsers);
5972
5462
  }
5973
5463
  var init_defaults = __esm(() => {
5464
+ init_language();
5974
5465
  init_eml();
5975
- init_hwp();
5466
+ init_kordoc();
5976
5467
  init_ocr();
5468
+ init_ocr_engines();
5977
5469
  init_office();
5978
- init_opendataloader_pdf();
5979
5470
  init_plain_text();
5980
5471
  init_registry();
5981
5472
  });
@@ -5983,17 +5474,8 @@ var init_defaults = __esm(() => {
5983
5474
  // src/mirror/sync.ts
5984
5475
  import { randomUUID as randomUUID5 } from "node:crypto";
5985
5476
  import { existsSync as existsSync12, mkdirSync as mkdirSync6, renameSync as renameSync5, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
5986
- import { opendir, readFile as readFile5, stat as stat4 } from "node:fs/promises";
5987
- import { dirname as dirname7, extname as extname6, resolve as resolve9 } from "node:path";
5988
- function parserFailureCode(parser, error) {
5989
- return parser.name === "opendataloader-pdf" && /UnsupportedClassVersionError/.test(error.message) ? "pdf-java-version" : "parser-failed";
5990
- }
5991
- function parserFailureMessage(parser, error) {
5992
- if (parserFailureCode(parser, error) === "pdf-java-version") {
5993
- return "PDF parsing requires Java 11 or newer; the Java runtime selected from PATH is too old.";
5994
- }
5995
- return "The registered parser failed on this file; it was skipped during indexing.";
5996
- }
5477
+ import { opendir, readFile as readFile3, stat as stat4 } from "node:fs/promises";
5478
+ import { dirname as dirname7, extname as extname4, resolve as resolve9 } from "node:path";
5997
5479
  async function syncParsedMirrors(options) {
5998
5480
  const registry = options.registry ?? createDefaultParserRegistry(options.parserOptions);
5999
5481
  const maxSourceBytes = options.maxSourceBytes ?? DEFAULT_MAX_SOURCE_BYTES;
@@ -6075,7 +5557,7 @@ async function syncParsedMirrors(options) {
6075
5557
  if (!unchanged) {
6076
5558
  let parsed;
6077
5559
  try {
6078
- const bytes = await readFile5(entry.sourcePath);
5560
+ const bytes = await readFile3(entry.sourcePath);
6079
5561
  parsed = await parser.parse({ virtualPath: entry.virtualPath, sourcePath: entry.sourcePath, bytes });
6080
5562
  } catch (error) {
6081
5563
  if (!(error instanceof ParseError))
@@ -6085,9 +5567,9 @@ async function syncParsedMirrors(options) {
6085
5567
  skipped += 1;
6086
5568
  recordSkip(entry, "parser-failed");
6087
5569
  diagnostics.push({
6088
- code: parserFailureCode(parser, error),
5570
+ code: "parser-failed",
6089
5571
  severity: "warning",
6090
- message: parserFailureMessage(parser, error),
5572
+ message: PARSER_FAILURE_MESSAGE,
6091
5573
  source: entry.virtualPath
6092
5574
  });
6093
5575
  sinceCheckpoint += 1;
@@ -6214,7 +5696,7 @@ async function collectFiles(sourceRoot, directory, entries, supportedExtensions)
6214
5696
  }
6215
5697
  if (!entry.isFile())
6216
5698
  continue;
6217
- const extension = normalizeExtension2(extname6(entry.name));
5699
+ const extension = normalizeExtension2(extname4(entry.name));
6218
5700
  if (!supportedExtensions.has(extension))
6219
5701
  continue;
6220
5702
  let fileStat;
@@ -6248,7 +5730,7 @@ function removeFile(path) {
6248
5730
  if (existsSync12(path))
6249
5731
  rmSync3(path, { force: true });
6250
5732
  }
6251
- var DEFAULT_MAX_SOURCE_BYTES, MIRROR_CHECKPOINT_EVERY = 25, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES;
5733
+ var DEFAULT_MAX_SOURCE_BYTES, MIRROR_CHECKPOINT_EVERY = 25, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.";
6252
5734
  var init_sync = __esm(() => {
6253
5735
  init_source_paths();
6254
5736
  init_defaults();
@@ -6534,6 +6016,18 @@ var init_injection_classifier = __esm(() => {
6534
6016
  });
6535
6017
  });
6536
6018
 
6019
+ // src/parser/index.ts
6020
+ var init_parser = __esm(() => {
6021
+ init_defaults();
6022
+ init_eml();
6023
+ init_errors();
6024
+ init_kordoc();
6025
+ init_ocr();
6026
+ init_office();
6027
+ init_plain_text();
6028
+ init_registry();
6029
+ });
6030
+
6537
6031
  // src/retrieval/skip.ts
6538
6032
  function retrievalSurfaceFor(descriptor) {
6539
6033
  const datasourceId = descriptor.datasourceId;
@@ -7664,11 +7158,11 @@ var init_jikji_find_tool = __esm(() => {
7664
7158
  // src/agent/local-model.ts
7665
7159
  import { readFileSync as readFileSync9 } from "node:fs";
7666
7160
  import { homedir as homedir2 } from "node:os";
7667
- import { join as join17 } from "node:path";
7668
- import { parse as parse3 } from "smol-toml";
7161
+ import { join as join16 } from "node:path";
7162
+ import { parse as parse2 } from "smol-toml";
7669
7163
  function loadLocalAutoRAGModel(options = {}) {
7670
- const configPath = options.configPath ?? join17(homedir2(), ".codex", "config.toml");
7671
- const config = parse3(readFileSync9(configPath, "utf8"));
7164
+ const configPath = options.configPath ?? join16(homedir2(), ".codex", "config.toml");
7165
+ const config = parse2(readFileSync9(configPath, "utf8"));
7672
7166
  const provider = config.model_provider;
7673
7167
  if (typeof provider !== "string" || provider.length === 0) {
7674
7168
  throw new Error(`AutoRAG requires model_provider in ${configPath}`);
@@ -7719,18 +7213,18 @@ import {
7719
7213
  watch,
7720
7214
  writeFileSync as writeFileSync7
7721
7215
  } from "node:fs";
7722
- import { dirname as dirname10, join as join18 } from "node:path";
7216
+ import { dirname as dirname10, join as join17 } from "node:path";
7723
7217
  function requestsDir(workspacePath) {
7724
- return join18(workspacePath, P2P_DIR, "requests");
7218
+ return join17(workspacePath, P2P_DIR, "requests");
7725
7219
  }
7726
7220
  function decisionsDir(workspacePath) {
7727
- return join18(workspacePath, P2P_DIR, "decisions");
7221
+ return join17(workspacePath, P2P_DIR, "decisions");
7728
7222
  }
7729
7223
  function requestPath(workspacePath, id) {
7730
- return join18(requestsDir(workspacePath), `${id}.json`);
7224
+ return join17(requestsDir(workspacePath), `${id}.json`);
7731
7225
  }
7732
7226
  function decisionPath(workspacePath, id) {
7733
- return join18(decisionsDir(workspacePath), `${id}.json`);
7227
+ return join17(decisionsDir(workspacePath), `${id}.json`);
7734
7228
  }
7735
7229
  function isRecord10(value) {
7736
7230
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -7862,7 +7356,7 @@ var init_approval_store = __esm(() => {
7862
7356
  this.name = "ApprovalAbortedError";
7863
7357
  }
7864
7358
  };
7865
- P2P_DIR = join18(".autorag", "p2p");
7359
+ P2P_DIR = join17(".autorag", "p2p");
7866
7360
  });
7867
7361
 
7868
7362
  // src/p2p/pii-gate.ts
@@ -8141,7 +7635,7 @@ var init_egress_gate = __esm(() => {
8141
7635
 
8142
7636
  // src/p2p/policy.ts
8143
7637
  import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
8144
- import { basename as basename9, dirname as dirname11, join as join19, resolve as resolve10 } from "node:path";
7638
+ import { basename as basename8, dirname as dirname11, join as join18, resolve as resolve10 } from "node:path";
8145
7639
  import { parse as parseToml } from "smol-toml";
8146
7640
  function isRecord11(value) {
8147
7641
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -8405,8 +7899,8 @@ function loadSeenSources(path, workspacePath) {
8405
7899
  }
8406
7900
  function resolveGlobalConfigPath(homePath) {
8407
7901
  if (homePath === undefined)
8408
- return join19(resolveAutoRAGHome(), "config.json");
8409
- return basename9(homePath) === ".autorag" ? join19(homePath, "config.json") : join19(homePath, ".autorag", "config.json");
7902
+ return join18(resolveAutoRAGHome(), "config.json");
7903
+ return basename8(homePath) === ".autorag" ? join18(homePath, "config.json") : join18(homePath, ".autorag", "config.json");
8410
7904
  }
8411
7905
 
8412
7906
  class PolicyStore {
@@ -8427,7 +7921,7 @@ class PolicyStore {
8427
7921
  this.workspacePath = resolve10(normalizedOptions.workspacePath);
8428
7922
  this.sourceRoots = normalizedOptions.sourceRoots;
8429
7923
  const globalConfigPath = normalizedOptions.globalConfigPath ?? resolveGlobalConfigPath(normalizedOptions.homePath);
8430
- const workspacePolicyPath = normalizedOptions.workspacePolicyPath ?? join19(this.workspacePath, ".autorag", "p2p", "policy.toml");
7924
+ const workspacePolicyPath = normalizedOptions.workspacePolicyPath ?? join18(this.workspacePath, ".autorag", "p2p", "policy.toml");
8431
7925
  const global = readGlobalPolicy(readJsonFile(globalConfigPath), this.workspacePath, globalConfigPath);
8432
7926
  const workspacePolicy = readTomlFile(workspacePolicyPath);
8433
7927
  const workspaceEntries = readPolicyEntries(workspacePolicy, this.workspacePath, workspacePolicyPath);
@@ -8444,7 +7938,7 @@ class PolicyStore {
8444
7938
  throw new PolicyError("newFilesPublic must be a boolean");
8445
7939
  }
8446
7940
  this.newFilesPublic = normalizedOptions.newFilesPublic ?? workspaceNewFilesPublic ?? configuredNewFilesPublic ?? false;
8447
- this.seenSourcesPath = normalizedOptions.seenSourcesPath ?? join19(this.workspacePath, ".autorag", "p2p", "seen-sources.json");
7941
+ this.seenSourcesPath = normalizedOptions.seenSourcesPath ?? join18(this.workspacePath, ".autorag", "p2p", "seen-sources.json");
8448
7942
  const seenState = loadSeenSources(this.seenSourcesPath, this.workspacePath);
8449
7943
  this.seenSources = seenState.promoted;
8450
7944
  this.pendingSources = seenState.pending;
@@ -8555,73 +8049,423 @@ class PolicyStore {
8555
8049
  if (isNewAllowSource && !this.newFilesPublic && !this.seenSources.has(canonical.normalize("NFC"))) {
8556
8050
  return privateResolution();
8557
8051
  }
8558
- return resolveEntry(selected, peerFingerprint);
8052
+ return resolveEntry(selected, peerFingerprint);
8053
+ }
8054
+ }
8055
+ function privateResolution() {
8056
+ return { tier: "private", allowed: false, shareBytes: false, redact: true };
8057
+ }
8058
+ function resolveEntry(entry, peerFingerprint) {
8059
+ switch (entry.tier) {
8060
+ case "private":
8061
+ case "never":
8062
+ return { tier: entry.tier, allowed: false, shareBytes: false, redact: true };
8063
+ case "always":
8064
+ return { tier: "always", allowed: true, shareBytes: true, redact: false };
8065
+ case "peers":
8066
+ return {
8067
+ tier: "peers",
8068
+ allowed: peerFingerprint !== undefined && (entry.peers ?? []).includes(peerFingerprint),
8069
+ shareBytes: false,
8070
+ redact: true
8071
+ };
8072
+ }
8073
+ }
8074
+ var PolicyError, DEFAULT_QUOTAS, POLICY_TIERS, UNSAFE_POLICY_CHARACTERS, WINDOWS_ABSOLUTE_PATH, POSIX_FILESYSTEM_ROOTS, DEFAULT_POLICY_QUOTAS;
8075
+ var init_policy = __esm(() => {
8076
+ init_home();
8077
+ init_source_paths();
8078
+ PolicyError = class PolicyError extends Error {
8079
+ cause;
8080
+ constructor(message, options) {
8081
+ super(message);
8082
+ this.name = "PolicyError";
8083
+ this.cause = options?.cause;
8084
+ }
8085
+ };
8086
+ DEFAULT_QUOTAS = {
8087
+ queriesPerHour: 10,
8088
+ burst: 3,
8089
+ maxBodyBytes: 65536,
8090
+ maxFileBytes: 26214400
8091
+ };
8092
+ POLICY_TIERS = new Set(["private", "never", "always", "peers"]);
8093
+ UNSAFE_POLICY_CHARACTERS = /[\p{Cc}\p{Cf}]/u;
8094
+ WINDOWS_ABSOLUTE_PATH = /^[a-z]:[\\/]/iu;
8095
+ POSIX_FILESYSTEM_ROOTS = new Set([
8096
+ "applications",
8097
+ "bin",
8098
+ "dev",
8099
+ "etc",
8100
+ "home",
8101
+ "library",
8102
+ "media",
8103
+ "mnt",
8104
+ "opt",
8105
+ "private",
8106
+ "proc",
8107
+ "root",
8108
+ "run",
8109
+ "users",
8110
+ "sbin",
8111
+ "srv",
8112
+ "system",
8113
+ "tmp",
8114
+ "usr",
8115
+ "var",
8116
+ "volumes"
8117
+ ]);
8118
+ DEFAULT_POLICY_QUOTAS = Object.freeze({ ...DEFAULT_QUOTAS });
8119
+ });
8120
+
8121
+ // src/p2p/simplex-transport.ts
8122
+ import { spawn as spawn7 } from "node:child_process";
8123
+ function isRecord12(value) {
8124
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8125
+ }
8126
+ function responseType(payload) {
8127
+ if (!isRecord12(payload))
8128
+ return;
8129
+ return typeof payload.type === "string" ? payload.type : undefined;
8130
+ }
8131
+ function parseSimplexProfile(value) {
8132
+ if (!isRecord12(value))
8133
+ return;
8134
+ if (typeof value.displayName !== "string" || value.displayName.trim().length === 0)
8135
+ return;
8136
+ const fullName = typeof value.fullName === "string" ? value.fullName : undefined;
8137
+ const shortDescr = typeof value.shortDescr === "string" ? value.shortDescr : undefined;
8138
+ const description = typeof value.description === "string" ? value.description : undefined;
8139
+ const image = typeof value.image === "string" ? value.image : undefined;
8140
+ return {
8141
+ displayName: value.displayName,
8142
+ ...fullName !== undefined ? { fullName } : {},
8143
+ ...shortDescr !== undefined ? { shortDescr } : {},
8144
+ ...description !== undefined ? { description } : {},
8145
+ ...image !== undefined ? { image } : {}
8146
+ };
8147
+ }
8148
+ function contactOf(payload) {
8149
+ if (!isRecord12(payload))
8150
+ return;
8151
+ const contact = payload.contact;
8152
+ if (!isRecord12(contact))
8153
+ return;
8154
+ if (typeof contact.contactId !== "number" || typeof contact.localDisplayName !== "string")
8155
+ return;
8156
+ const profile = parseSimplexProfile(contact.profile);
8157
+ return {
8158
+ contactId: contact.contactId,
8159
+ localDisplayName: contact.localDisplayName,
8160
+ ...profile !== undefined ? { profile } : {}
8161
+ };
8162
+ }
8163
+ function userIdOf(payload) {
8164
+ if (!isRecord12(payload))
8165
+ return;
8166
+ const user = payload.user;
8167
+ if (!isRecord12(user) || typeof user.userId !== "number")
8168
+ return;
8169
+ return user.userId;
8170
+ }
8171
+ function extractTextFromChatItem(item) {
8172
+ if (!isRecord12(item))
8173
+ return;
8174
+ const chatItem = item.chatItem;
8175
+ if (!isRecord12(chatItem))
8176
+ return;
8177
+ const meta = chatItem.meta;
8178
+ const content = chatItem.content;
8179
+ if (!isRecord12(meta) || !isRecord12(content))
8180
+ return;
8181
+ if (typeof meta.itemId !== "number")
8182
+ return;
8183
+ const msgContent = content.msgContent;
8184
+ if (!isRecord12(msgContent))
8185
+ return;
8186
+ if (msgContent.type !== "text" || typeof msgContent.text !== "string")
8187
+ return;
8188
+ return { chatItemId: meta.itemId, text: msgContent.text };
8189
+ }
8190
+ async function waitForPort(port, proc, timeoutMs) {
8191
+ const deadline = Date.now() + timeoutMs;
8192
+ for (;; ) {
8193
+ if (proc.exitCode !== null) {
8194
+ throw new SimplexError(`simplex-chat exited with code ${proc.exitCode} before the WebSocket server started`);
8195
+ }
8196
+ try {
8197
+ const ws = new WebSocket(`ws://127.0.0.1:${port}`);
8198
+ await new Promise((resolve, reject) => {
8199
+ ws.addEventListener("open", () => {
8200
+ ws.close();
8201
+ resolve();
8202
+ });
8203
+ ws.addEventListener("error", () => reject(new Error("connect failed")));
8204
+ });
8205
+ return;
8206
+ } catch {
8207
+ if (Date.now() >= deadline) {
8208
+ throw new SimplexError(`simplex-chat WebSocket server did not start within ${timeoutMs}ms`);
8209
+ }
8210
+ await new Promise((resolve) => setTimeout(resolve, 150));
8211
+ }
8212
+ }
8213
+ }
8214
+
8215
+ class SimplexClient {
8216
+ dbPrefix;
8217
+ displayName;
8218
+ proc;
8219
+ ws;
8220
+ nextCorrId = 1;
8221
+ pending = new Map;
8222
+ messageHandlers = [];
8223
+ closed = false;
8224
+ userId;
8225
+ constructor(dbPrefix, displayName, proc, ws) {
8226
+ this.dbPrefix = dbPrefix;
8227
+ this.displayName = displayName;
8228
+ this.proc = proc;
8229
+ this.ws = ws;
8230
+ ws.addEventListener("message", (event) => {
8231
+ const data = typeof event.data === "string" ? event.data : Buffer.from(event.data).toString();
8232
+ this.dispatch(data);
8233
+ });
8234
+ ws.addEventListener("close", () => this.failAllPending(new SimplexError("simplex-chat WebSocket closed")));
8235
+ ws.addEventListener("error", () => {});
8236
+ }
8237
+ dispatch(raw) {
8238
+ let parsed;
8239
+ try {
8240
+ parsed = JSON.parse(raw);
8241
+ } catch {
8242
+ return;
8243
+ }
8244
+ if (!isRecord12(parsed) || !isRecord12(parsed.resp))
8245
+ return;
8246
+ const resp = parsed.resp;
8247
+ if (typeof parsed.corrId === "string") {
8248
+ const entry = this.pending.get(parsed.corrId);
8249
+ if (entry !== undefined) {
8250
+ this.pending.delete(parsed.corrId);
8251
+ const type = responseType(resp);
8252
+ if (type === "chatCmdError" || type === "chatError") {
8253
+ entry.reject(new SimplexError(`simplex-chat command error: ${JSON.stringify(resp).slice(0, 300)}`));
8254
+ } else {
8255
+ entry.resolve(resp);
8256
+ }
8257
+ }
8258
+ return;
8259
+ }
8260
+ this.handleEvent(resp);
8261
+ }
8262
+ handleEvent(resp) {
8263
+ if (responseType(resp) !== "newChatItems" || !isRecord12(resp))
8264
+ return;
8265
+ const chatItems = resp.chatItems;
8266
+ if (!Array.isArray(chatItems))
8267
+ return;
8268
+ for (const item of chatItems) {
8269
+ if (!isRecord12(item))
8270
+ continue;
8271
+ const chatInfo = item.chatInfo;
8272
+ if (!isRecord12(chatInfo) || chatInfo.type !== "direct")
8273
+ continue;
8274
+ const contact = contactOf(chatInfo);
8275
+ const extracted = extractTextFromChatItem(item);
8276
+ if (contact === undefined || extracted === undefined)
8277
+ continue;
8278
+ const chatItem = item.chatItem;
8279
+ if (!isRecord12(chatItem))
8280
+ continue;
8281
+ const chatDir = chatItem.chatDir;
8282
+ if (!isRecord12(chatDir) || chatDir.type !== "directRcv")
8283
+ continue;
8284
+ const message = {
8285
+ contactId: contact.contactId,
8286
+ contactName: contact.localDisplayName,
8287
+ text: extracted.text,
8288
+ chatItemId: extracted.chatItemId
8289
+ };
8290
+ for (const handler of this.messageHandlers) {
8291
+ try {
8292
+ handler(message);
8293
+ } catch {}
8294
+ }
8295
+ }
8296
+ }
8297
+ cmd(command) {
8298
+ if (this.closed)
8299
+ return Promise.reject(new SimplexError("transport is closed"));
8300
+ const corrId = String(this.nextCorrId++);
8301
+ return new Promise((resolve, reject) => {
8302
+ this.pending.set(corrId, { resolve, reject });
8303
+ const request = { corrId, cmd: command };
8304
+ try {
8305
+ this.ws.send(JSON.stringify(request));
8306
+ } catch (error) {
8307
+ this.pending.delete(corrId);
8308
+ reject(new SimplexError(`WebSocket send failed: ${error instanceof Error ? error.message : String(error)}`));
8309
+ }
8310
+ });
8311
+ }
8312
+ failAllPending(error) {
8313
+ for (const entry of this.pending.values())
8314
+ entry.reject(error);
8315
+ this.pending.clear();
8316
+ }
8317
+ async requireUserId() {
8318
+ if (this.userId !== undefined)
8319
+ return this.userId;
8320
+ const active = await this.cmd("/user");
8321
+ if (responseType(active) === "activeUser") {
8322
+ const id = userIdOf(active);
8323
+ if (id !== undefined) {
8324
+ this.userId = id;
8325
+ return id;
8326
+ }
8327
+ }
8328
+ const created = await this.cmd(`/_create user ${JSON.stringify({
8329
+ profile: { displayName: this.displayName, fullName: "" },
8330
+ pastTimestamp: false,
8331
+ userChatRelay: false,
8332
+ clientService: false
8333
+ })}`);
8334
+ if (responseType(created) !== "activeUser") {
8335
+ throw new SimplexError(`failed to create SimpleX user profile: ${JSON.stringify(created).slice(0, 300)}`);
8336
+ }
8337
+ const id = userIdOf(created);
8338
+ if (id === undefined)
8339
+ throw new SimplexError("simplex-chat did not return a user id");
8340
+ this.userId = id;
8341
+ return id;
8342
+ }
8343
+ async getUserId() {
8344
+ return this.requireUserId();
8345
+ }
8346
+ async getOrCreateAddress() {
8347
+ const userId = await this.requireUserId();
8348
+ try {
8349
+ const existing = await this.cmd(`/_show_address ${userId}`);
8350
+ if (responseType(existing) === "userContactLink" && isRecord12(existing)) {
8351
+ const link = existing.contactLink;
8352
+ if (isRecord12(link) && isRecord12(link.connLinkContact)) {
8353
+ const full = link.connLinkContact.connFullLink;
8354
+ if (typeof full === "string")
8355
+ return full;
8356
+ }
8357
+ }
8358
+ } catch {}
8359
+ const created = await this.cmd(`/_address ${userId}`);
8360
+ if (responseType(created) !== "userContactLinkCreated" || !isRecord12(created)) {
8361
+ throw new SimplexError(`failed to create SimpleX address: ${JSON.stringify(created).slice(0, 300)}`);
8362
+ }
8363
+ const link = created.connLinkContact;
8364
+ if (!isRecord12(link) || typeof link.connFullLink !== "string") {
8365
+ throw new SimplexError("simplex-chat did not return a contact address");
8366
+ }
8367
+ await this.cmd(`/_address_settings ${userId} ${JSON.stringify({
8368
+ businessAddress: false,
8369
+ autoAccept: { acceptIncognito: false }
8370
+ })}`);
8371
+ return link.connFullLink;
8372
+ }
8373
+ async createInvitation() {
8374
+ const userId = await this.requireUserId();
8375
+ const resp = await this.cmd(`/_connect ${userId}`);
8376
+ if (responseType(resp) !== "invitation" || !isRecord12(resp)) {
8377
+ throw new SimplexError(`failed to create invitation: ${JSON.stringify(resp).slice(0, 300)}`);
8378
+ }
8379
+ const link = resp.connLinkInvitation;
8380
+ if (!isRecord12(link) || typeof link.connFullLink !== "string") {
8381
+ throw new SimplexError("simplex-chat did not return an invitation link");
8382
+ }
8383
+ return link.connFullLink;
8384
+ }
8385
+ async connect(link) {
8386
+ const userId = await this.requireUserId();
8387
+ const resp = await this.cmd(`/_connect ${userId} ${link}`);
8388
+ const type = responseType(resp);
8389
+ if (type === "sentInvitation" || type === "sentConfirmation" || type === "contactAlreadyExists")
8390
+ return;
8391
+ throw new SimplexError(`failed to connect via link: ${JSON.stringify(resp).slice(0, 300)}`);
8392
+ }
8393
+ async listContacts() {
8394
+ const userId = await this.requireUserId();
8395
+ const resp = await this.cmd(`/_contacts ${userId}`);
8396
+ if (responseType(resp) !== "contactsList" || !isRecord12(resp) || !Array.isArray(resp.contacts)) {
8397
+ throw new SimplexError(`failed to list contacts: ${JSON.stringify(resp).slice(0, 300)}`);
8398
+ }
8399
+ const contacts = [];
8400
+ for (const entry of resp.contacts) {
8401
+ const contact = contactOf({ contact: entry });
8402
+ if (contact !== undefined)
8403
+ contacts.push(contact);
8404
+ }
8405
+ return contacts;
8559
8406
  }
8560
- }
8561
- function privateResolution() {
8562
- return { tier: "private", allowed: false, shareBytes: false, redact: true };
8563
- }
8564
- function resolveEntry(entry, peerFingerprint) {
8565
- switch (entry.tier) {
8566
- case "private":
8567
- case "never":
8568
- return { tier: entry.tier, allowed: false, shareBytes: false, redact: true };
8569
- case "always":
8570
- return { tier: "always", allowed: true, shareBytes: true, redact: false };
8571
- case "peers":
8572
- return {
8573
- tier: "peers",
8574
- allowed: peerFingerprint !== undefined && (entry.peers ?? []).includes(peerFingerprint),
8575
- shareBytes: false,
8576
- redact: true
8577
- };
8407
+ async sendMessage(contactId, text) {
8408
+ const resp = await this.cmd(`/_send @${contactId} json ${JSON.stringify([{ msgContent: { type: "text", text }, mentions: {} }])}`);
8409
+ if (responseType(resp) !== "newChatItems") {
8410
+ throw new SimplexError(`failed to send message: ${JSON.stringify(resp).slice(0, 300)}`);
8411
+ }
8412
+ }
8413
+ onMessage(handler) {
8414
+ this.messageHandlers.push(handler);
8415
+ return () => {
8416
+ const index = this.messageHandlers.indexOf(handler);
8417
+ if (index !== -1)
8418
+ this.messageHandlers.splice(index, 1);
8419
+ };
8420
+ }
8421
+ async close() {
8422
+ if (this.closed)
8423
+ return;
8424
+ this.closed = true;
8425
+ this.failAllPending(new SimplexError("transport closed"));
8426
+ try {
8427
+ this.ws.close();
8428
+ } catch {}
8429
+ this.proc.kill("SIGTERM");
8430
+ await new Promise((resolve) => {
8431
+ const timer = setTimeout(() => {
8432
+ this.proc.kill("SIGKILL");
8433
+ resolve();
8434
+ }, 3000);
8435
+ this.proc.once("exit", () => {
8436
+ clearTimeout(timer);
8437
+ resolve();
8438
+ });
8439
+ });
8578
8440
  }
8579
8441
  }
8580
- var PolicyError, DEFAULT_QUOTAS, POLICY_TIERS, UNSAFE_POLICY_CHARACTERS, WINDOWS_ABSOLUTE_PATH, POSIX_FILESYSTEM_ROOTS, DEFAULT_POLICY_QUOTAS;
8581
- var init_policy = __esm(() => {
8582
- init_home();
8583
- init_source_paths();
8584
- PolicyError = class PolicyError extends Error {
8585
- cause;
8586
- constructor(message, options) {
8442
+ async function startSimplexChat(options) {
8443
+ const port = options.port ?? 5225;
8444
+ const binary = options.binary ?? "simplex-chat";
8445
+ const args = ["-p", String(port), "-d", options.dbPrefix];
8446
+ if (options.displayName !== undefined)
8447
+ args.push("--create-bot-display-name", options.displayName);
8448
+ const proc = spawn7(binary, args, {
8449
+ stdio: ["ignore", "pipe", "pipe"]
8450
+ });
8451
+ await waitForPort(port, proc, options.connectTimeoutMs ?? 15000);
8452
+ const ws = await new Promise((resolve, reject) => {
8453
+ const sock = new WebSocket(`ws://127.0.0.1:${port}`);
8454
+ sock.addEventListener("open", () => resolve(sock));
8455
+ sock.addEventListener("error", () => reject(new SimplexError(`WebSocket connect failed on port ${port}`)));
8456
+ });
8457
+ const client = new SimplexClient(options.dbPrefix, options.displayName, proc, ws);
8458
+ await client.getUserId();
8459
+ return client;
8460
+ }
8461
+ var SimplexError;
8462
+ var init_simplex_transport = __esm(() => {
8463
+ SimplexError = class SimplexError extends Error {
8464
+ constructor(message) {
8587
8465
  super(message);
8588
- this.name = "PolicyError";
8589
- this.cause = options?.cause;
8466
+ this.name = "SimplexError";
8590
8467
  }
8591
8468
  };
8592
- DEFAULT_QUOTAS = {
8593
- queriesPerHour: 10,
8594
- burst: 3,
8595
- maxBodyBytes: 65536,
8596
- maxFileBytes: 26214400
8597
- };
8598
- POLICY_TIERS = new Set(["private", "never", "always", "peers"]);
8599
- UNSAFE_POLICY_CHARACTERS = /[\p{Cc}\p{Cf}]/u;
8600
- WINDOWS_ABSOLUTE_PATH = /^[a-z]:[\\/]/iu;
8601
- POSIX_FILESYSTEM_ROOTS = new Set([
8602
- "applications",
8603
- "bin",
8604
- "dev",
8605
- "etc",
8606
- "home",
8607
- "library",
8608
- "media",
8609
- "mnt",
8610
- "opt",
8611
- "private",
8612
- "proc",
8613
- "root",
8614
- "run",
8615
- "users",
8616
- "sbin",
8617
- "srv",
8618
- "system",
8619
- "tmp",
8620
- "usr",
8621
- "var",
8622
- "volumes"
8623
- ]);
8624
- DEFAULT_POLICY_QUOTAS = Object.freeze({ ...DEFAULT_QUOTAS });
8625
8469
  });
8626
8470
 
8627
8471
  // src/p2p/wire.ts
@@ -8683,24 +8527,34 @@ var init_wire = __esm(() => {
8683
8527
 
8684
8528
  // src/p2p/simplex-server.ts
8685
8529
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
8686
- import { join as join20 } from "node:path";
8530
+ import { join as join19 } from "node:path";
8687
8531
  import { Type as Type9 } from "typebox";
8688
8532
  import { Value } from "typebox/value";
8689
8533
  function targetTokens(value) {
8690
8534
  return new Set((value.toLocaleLowerCase().match(TARGET_TOKEN_PATTERN) ?? []).filter((token) => token.length > 1));
8691
8535
  }
8536
+ function contactText(alias, peer) {
8537
+ return [
8538
+ alias,
8539
+ peer.description,
8540
+ peer.profile?.displayName,
8541
+ peer.profile?.fullName,
8542
+ peer.profile?.shortDescr,
8543
+ peer.profile?.description
8544
+ ].filter((value) => value !== undefined).join(" ");
8545
+ }
8692
8546
  function rankSimplexPeerTargets(query, registry) {
8693
8547
  const queryTerms = targetTokens(query);
8694
8548
  if (queryTerms.size === 0)
8695
8549
  return [];
8696
8550
  return Object.entries(registry).map(([alias, peer]) => {
8697
- const peerTerms = targetTokens([alias, peer.displayName, peer.description, peer.role, peer.org, ...peer.accessHint ?? []].filter((value) => value !== undefined).join(" "));
8551
+ const peerTerms = targetTokens(contactText(alias, peer));
8698
8552
  const matchedTerms = [...queryTerms].filter((term) => peerTerms.has(term)).sort();
8699
8553
  return { alias, score: matchedTerms.length, matchedTerms };
8700
8554
  }).filter((match) => match.score > 0).sort((left, right) => right.score - left.score || left.alias.localeCompare(right.alias));
8701
8555
  }
8702
8556
  function loadSimplexPeerRegistry(workspacePath) {
8703
- const path = join20(workspacePath, PEERS_DIR, PEERS_FILENAME);
8557
+ const path = join19(workspacePath, PEERS_DIR, PEERS_FILENAME);
8704
8558
  if (!existsSync15(path))
8705
8559
  return {};
8706
8560
  try {
@@ -8711,15 +8565,13 @@ function loadSimplexPeerRegistry(workspacePath) {
8711
8565
  for (const [alias, record] of Object.entries(parsed)) {
8712
8566
  if (typeof record === "object" && record !== null && typeof record.contactId === "number" && typeof record.addedAt === "string") {
8713
8567
  const raw = record;
8714
- const accessHint = Array.isArray(raw.accessHint) ? raw.accessHint.filter((value) => typeof value === "string") : undefined;
8568
+ const profile = parseSimplexProfile(raw.profile);
8715
8569
  registry[alias] = {
8716
8570
  contactId: raw.contactId,
8717
8571
  addedAt: raw.addedAt,
8718
- ...typeof raw.displayName === "string" ? { displayName: raw.displayName } : {},
8719
- ...typeof raw.description === "string" ? { description: raw.description } : {},
8720
- ...typeof raw.role === "string" ? { role: raw.role } : {},
8721
- ...typeof raw.org === "string" ? { org: raw.org } : {},
8722
- ...accessHint !== undefined ? { accessHint } : {}
8572
+ ...profile !== undefined ? { profile } : {},
8573
+ ...typeof raw.profileSyncedAt === "string" ? { profileSyncedAt: raw.profileSyncedAt } : {},
8574
+ ...typeof raw.description === "string" ? { description: raw.description } : {}
8723
8575
  };
8724
8576
  }
8725
8577
  }
@@ -8729,9 +8581,29 @@ function loadSimplexPeerRegistry(workspacePath) {
8729
8581
  }
8730
8582
  }
8731
8583
  function saveSimplexPeerRegistry(workspacePath, registry) {
8732
- const dir = join20(workspacePath, PEERS_DIR);
8584
+ const dir = join19(workspacePath, PEERS_DIR);
8733
8585
  mkdirSync10(dir, { recursive: true });
8734
- writeFileSync9(join20(dir, PEERS_FILENAME), JSON.stringify(registry, null, 2), { mode: 384 });
8586
+ writeFileSync9(join19(dir, PEERS_FILENAME), JSON.stringify(registry, null, 2), { mode: 384 });
8587
+ }
8588
+ function syncSimplexPeers(workspacePath, contacts, syncedAt = new Date().toISOString()) {
8589
+ const registry = loadSimplexPeerRegistry(workspacePath);
8590
+ const updated = [];
8591
+ const untrusted = [];
8592
+ for (const contact of contacts) {
8593
+ const entry = Object.entries(registry).find(([, peer]) => peer.contactId === contact.contactId);
8594
+ if (entry === undefined) {
8595
+ untrusted.push(contact);
8596
+ continue;
8597
+ }
8598
+ if (contact.profile === undefined)
8599
+ continue;
8600
+ const [alias, peer] = entry;
8601
+ registry[alias] = { ...peer, profile: contact.profile, profileSyncedAt: syncedAt };
8602
+ updated.push(alias);
8603
+ }
8604
+ if (updated.length > 0)
8605
+ saveSimplexPeerRegistry(workspacePath, registry);
8606
+ return { updated, untrusted };
8735
8607
  }
8736
8608
  function consumeToken(buckets, contactId, quotas) {
8737
8609
  const now = Date.now();
@@ -8961,6 +8833,7 @@ var init_simplex_server = __esm(() => {
8961
8833
  init_injection_classifier();
8962
8834
  init_injection_gate();
8963
8835
  init_policy();
8836
+ init_simplex_transport();
8964
8837
  init_wire();
8965
8838
  PRIVATE_POLICY = {
8966
8839
  tier: "private",
@@ -8975,7 +8848,7 @@ var init_simplex_server = __esm(() => {
8975
8848
  payload: Type9.Unknown({ description: "PeerQueryRequest or PeerQueryResponse" })
8976
8849
  });
8977
8850
  TARGET_TOKEN_PATTERN = /[\p{L}\p{N}]+/gu;
8978
- PEERS_DIR = join20(".autorag", "p2p");
8851
+ PEERS_DIR = join19(".autorag", "p2p");
8979
8852
  QueueFullError = class QueueFullError extends Error {
8980
8853
  constructor(message = "The peer query queue is full.") {
8981
8854
  super(message);
@@ -8999,7 +8872,7 @@ function createRecommendPeerTargetsTool(workspacePath) {
8999
8872
  return {
9000
8873
  alias: match.alias,
9001
8874
  matchedTerms: match.matchedTerms,
9002
- ...peer?.displayName !== undefined ? { displayName: peer.displayName } : {},
8875
+ ...peer?.profile?.displayName !== undefined ? { displayName: peer.profile.displayName } : {},
9003
8876
  ...peer?.description !== undefined ? { description: peer.description } : {}
9004
8877
  };
9005
8878
  });
@@ -9023,6 +8896,80 @@ var init_peer_target_tool = __esm(() => {
9023
8896
  });
9024
8897
  });
9025
8898
 
8899
+ // src/agent/refresh-progress.ts
8900
+ import { randomUUID as randomUUID6 } from "node:crypto";
8901
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync13, renameSync as renameSync7, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "node:fs";
8902
+ import { dirname as dirname12 } from "node:path";
8903
+ function readRefreshProgress(root) {
8904
+ try {
8905
+ const parsed = JSON.parse(readFileSync13(refreshProgressPath(root), "utf8"));
8906
+ return isPersistedRefreshProgress(parsed) ? parsed : undefined;
8907
+ } catch (error) {
8908
+ if (error instanceof SyntaxError || typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
8909
+ return;
8910
+ }
8911
+ throw error;
8912
+ }
8913
+ }
8914
+ function writeRefreshProgress(root, progress) {
8915
+ const path = refreshProgressPath(root);
8916
+ mkdirSync11(dirname12(path), { recursive: true });
8917
+ const temporaryPath = `${path}.${randomUUID6()}.tmp`;
8918
+ try {
8919
+ writeFileSync10(temporaryPath, `${JSON.stringify(progress, null, 2)}
8920
+ `);
8921
+ renameSync7(temporaryPath, path);
8922
+ } finally {
8923
+ rmSync4(temporaryPath, { force: true });
8924
+ }
8925
+ }
8926
+ function updateRefreshProgress(progress, update) {
8927
+ return {
8928
+ ...progress,
8929
+ ...update,
8930
+ updatedAt: new Date().toISOString()
8931
+ };
8932
+ }
8933
+ function removeRefreshProgress(root) {
8934
+ rmSync4(refreshProgressPath(root), { force: true });
8935
+ }
8936
+ function isRefreshOwnerAlive(progress) {
8937
+ if (progress.state !== "running")
8938
+ return false;
8939
+ try {
8940
+ process.kill(progress.pid, 0);
8941
+ return true;
8942
+ } catch (error) {
8943
+ if (error instanceof Error)
8944
+ return false;
8945
+ throw error;
8946
+ }
8947
+ }
8948
+ function isPersistedRefreshProgress(value) {
8949
+ if (!isRecord13(value))
8950
+ return false;
8951
+ const record = value;
8952
+ return record.version === 1 && typeof record.runId === "string" && typeof record.pid === "number" && Number.isInteger(record.pid) && (record.state === "running" || record.state === "success" || record.state === "failed") && isRefreshProgressPhase(record.phase) && typeof record.startedAt === "string" && typeof record.updatedAt === "string" && (record.finishedAt === undefined || typeof record.finishedAt === "string") && (record.error === undefined || typeof record.error === "string") && isSourceFiles(record.sourceFiles) && isParsedCounts(record.parsedCounts) && isMinsync(record.minsync);
8953
+ }
8954
+ function isRefreshProgressPhase(value) {
8955
+ return value === "parsed" || value === "minsync" || value === "datasources" || value === "jikji" || value === "finalizing";
8956
+ }
8957
+ function isSourceFiles(value) {
8958
+ return value === undefined || isRecord13(value) && typeof value.total === "number" && Number.isInteger(value.total);
8959
+ }
8960
+ function isParsedCounts(value) {
8961
+ return value === undefined || isRecord13(value) && typeof value.scanned === "number" && typeof value.written === "number" && typeof value.deleted === "number" && typeof value.skipped === "number";
8962
+ }
8963
+ function isMinsync(value) {
8964
+ return value === undefined || isRecord13(value) && (value.synced === undefined || typeof value.synced === "number");
8965
+ }
8966
+ function isRecord13(value) {
8967
+ return typeof value === "object" && value !== null;
8968
+ }
8969
+ var init_refresh_progress = __esm(() => {
8970
+ init_paths();
8971
+ });
8972
+
9026
8973
  // src/agent/search-documents.ts
9027
8974
  function toRetrievalTraceResults(results, limit = 5, excerptChars = 300) {
9028
8975
  return results.slice(0, limit).map((result) => ({
@@ -9622,19 +9569,6 @@ var init_watch_refresh = __esm(() => {
9622
9569
  EXCLUDED_SEGMENTS = new Set([".autorag", ".git", "node_modules"]);
9623
9570
  });
9624
9571
 
9625
- // src/parser/index.ts
9626
- var init_parser = __esm(() => {
9627
- init_defaults();
9628
- init_eml();
9629
- init_errors();
9630
- init_hwp();
9631
- init_ocr();
9632
- init_office();
9633
- init_opendataloader_pdf();
9634
- init_plain_text();
9635
- init_registry();
9636
- });
9637
-
9638
9572
  // src/web/entities.ts
9639
9573
  function decodeHtmlEntities(value) {
9640
9574
  return value.replace(ENTITY_PATTERN, (match, body) => {
@@ -9823,7 +9757,7 @@ var init_page_loader = __esm(() => {
9823
9757
  });
9824
9758
 
9825
9759
  // src/web/fetch/url-target.ts
9826
- import { extname as extname7 } from "node:path";
9760
+ import { extname as extname5 } from "node:path";
9827
9761
  function repairCollapsedScheme(value) {
9828
9762
  const m = value.match(/^(https?):\/(?!\/)/i);
9829
9763
  return m ? `${m[1]}://${value.slice(m[0].length)}` : value;
@@ -9842,7 +9776,7 @@ function getFilenameExtensionHint(filename) {
9842
9776
  const lower = filename.toLowerCase();
9843
9777
  if (lower.endsWith(".tar.gz"))
9844
9778
  return ".tar.gz";
9845
- return extname7(filename).toLowerCase();
9779
+ return extname5(filename).toLowerCase();
9846
9780
  }
9847
9781
  function getExtensionHint(url, contentDisposition) {
9848
9782
  if (contentDisposition) {
@@ -9910,9 +9844,9 @@ var init_url_target = __esm(() => {
9910
9844
  });
9911
9845
 
9912
9846
  // src/web/fetch/convert.ts
9913
- import { mkdtemp as mkdtemp3, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
9914
- import { tmpdir as tmpdir3 } from "node:os";
9915
- import { join as join21 } from "node:path";
9847
+ import { mkdtemp as mkdtemp2, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
9848
+ import { tmpdir as tmpdir2 } from "node:os";
9849
+ import { join as join20 } from "node:path";
9916
9850
  function combineSignals2(signal, timeoutMs) {
9917
9851
  if (!signal)
9918
9852
  return AbortSignal.timeout(timeoutMs);
@@ -9988,10 +9922,10 @@ async function convertBinaryPayload(buffer, extension, _finalUrl, signal) {
9988
9922
  if (!parser) {
9989
9923
  return { content: "", ok: false, error: `No parser registered for ${ext}` };
9990
9924
  }
9991
- const tempDir = await mkdtemp3(join21(tmpdir3(), "autorag-fetch-"));
9992
- const tempPath = join21(tempDir, `payload${ext}`);
9925
+ const tempDir = await mkdtemp2(join20(tmpdir2(), "autorag-fetch-"));
9926
+ const tempPath = join20(tempDir, `payload${ext}`);
9993
9927
  try {
9994
- await writeFile4(tempPath, buffer);
9928
+ await writeFile3(tempPath, buffer);
9995
9929
  const input = {
9996
9930
  virtualPath: `payload${ext}`,
9997
9931
  sourcePath: tempPath,
@@ -10008,7 +9942,7 @@ async function convertBinaryPayload(buffer, extension, _finalUrl, signal) {
10008
9942
  error: err instanceof Error ? err.message : "Conversion failed"
10009
9943
  };
10010
9944
  } finally {
10011
- await rm4(tempDir, { recursive: true, force: true });
9945
+ await rm3(tempDir, { recursive: true, force: true });
10012
9946
  }
10013
9947
  }
10014
9948
  var parserRegistry;
@@ -10019,7 +9953,7 @@ var init_convert = __esm(() => {
10019
9953
  });
10020
9954
 
10021
9955
  // src/web/fetch/feeds.ts
10022
- import { extname as extname8 } from "node:path";
9956
+ import { extname as extname6 } from "node:path";
10023
9957
  import { XMLParser as XMLParser2 } from "fast-xml-parser";
10024
9958
  function getHtmlAttribute(tag, attribute) {
10025
9959
  const pattern = new RegExp(`\\b${attribute}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'=<>]+))`, "i");
@@ -10082,7 +10016,7 @@ function extractDocumentLinks(html, baseUrl) {
10082
10016
  const href = getHtmlAttribute(tag, "href");
10083
10017
  if (!href)
10084
10018
  continue;
10085
- const ext = extname8(href).toLowerCase();
10019
+ const ext = extname6(href).toLowerCase();
10086
10020
  if (!CONVERTIBLE_EXTENSIONS2.has(ext))
10087
10021
  continue;
10088
10022
  const resolved = href.startsWith("http") ? href : new URL(href, baseUrl).href;
@@ -10241,7 +10175,7 @@ function alignmentMarker(cell) {
10241
10175
  return "--:";
10242
10176
  return "---";
10243
10177
  }
10244
- function renderTable2(service, table) {
10178
+ function renderTable(service, table) {
10245
10179
  const rows = descendantElements(table, "TR");
10246
10180
  const rendered = [];
10247
10181
  for (let rowIndex = 0;rowIndex < rows.length; rowIndex++) {
@@ -10298,7 +10232,7 @@ ${options.fence}
10298
10232
  service.addRule("table", {
10299
10233
  filter: isHeadingTable,
10300
10234
  replacement(_content, node) {
10301
- return renderTable2(service, node);
10235
+ return renderTable(service, node);
10302
10236
  }
10303
10237
  });
10304
10238
  }, gfm = (service) => {
@@ -15250,9 +15184,9 @@ var init_web_search_tool = __esm(() => {
15250
15184
  });
15251
15185
 
15252
15186
  // src/agent/agent.ts
15253
- import { randomUUID as randomUUID6 } from "node:crypto";
15254
- import { existsSync as existsSync17, watch as fsWatch, mkdirSync as mkdirSync11, realpathSync as realpathSync4, statSync, writeFileSync as writeFileSync10 } from "node:fs";
15255
- import { dirname as dirname12, join as join22, resolve as resolve11 } from "node:path";
15187
+ import { randomUUID as randomUUID7 } from "node:crypto";
15188
+ import { existsSync as existsSync17, watch as fsWatch, mkdirSync as mkdirSync12, realpathSync as realpathSync4, statSync, writeFileSync as writeFileSync11 } from "node:fs";
15189
+ import { dirname as dirname13, join as join21, resolve as resolve11 } from "node:path";
15256
15190
  import { Agent } from "@earendil-works/pi-agent-core";
15257
15191
  import { clampThinkingLevel, streamSimple } from "@earendil-works/pi-ai/compat";
15258
15192
 
@@ -15294,6 +15228,7 @@ class AutoRAGAgent {
15294
15228
  };
15295
15229
  searchPaths;
15296
15230
  configuredSearchPaths;
15231
+ languages;
15297
15232
  retrievalScopeBindings;
15298
15233
  datasourceVirtualScopePrefixes;
15299
15234
  workspaceProjectRoot;
@@ -15341,10 +15276,11 @@ class AutoRAGAgent {
15341
15276
  this.startupDiagnostics = options.startupDiagnostics ?? [];
15342
15277
  this.datasourceAgentSkills = this.buildAuthorizedDatasourceSkills();
15343
15278
  this.configuredSearchPaths = options.searchPaths.map((searchPath) => resolve11(searchPath));
15279
+ this.languages = options.languages ?? DEFAULT_LANGUAGES;
15344
15280
  this.searchPaths = options.searchPaths.map(pinSearchRoot);
15345
15281
  this.workspaceProjectRoot = options.workspacePath ?? process.cwd();
15346
15282
  this.retrievalScopeBindings = buildRetrievalScopeBindings(this.workspaceProjectRoot, this.searchPaths, this.configuredSearchPaths);
15347
- this.parserOptions = options.parserOptions;
15283
+ this.parserOptions = resolveParserOptions(options.parserOptions, this.languages);
15348
15284
  this.dupeyOptions = options.dupey ?? {};
15349
15285
  this.excludeExactDuplicates = options.excludeExactDuplicates ?? true;
15350
15286
  if (options.minSync !== false) {
@@ -15368,10 +15304,10 @@ class AutoRAGAgent {
15368
15304
  });
15369
15305
  this.jikjiReady = this.searchPaths.every((searchPath) => this.jikjiClient?.isPrepared(searchPath) === true);
15370
15306
  }
15371
- const memPath = memoryPath ?? join22(resolveAutoRAGHome(), "memory.json");
15307
+ const memPath = memoryPath ?? join21(resolveAutoRAGHome(), "memory.json");
15372
15308
  this.memory = new RetrievalMemory({ storagePath: memPath });
15373
15309
  this.memory.load();
15374
- this.runLogger = new AutoRAGRunLogger(join22(dirname12(memPath), "logs", "runs.jsonl"));
15310
+ this.runLogger = new AutoRAGRunLogger(join21(dirname13(memPath), "logs", "runs.jsonl"));
15375
15311
  const checkMemoryTool = createCheckMemoryTool(this.memory);
15376
15312
  const singleDatasourceTools = createSingleDatasourceSearchTools(this, this.singleDatasourceToolSpecs());
15377
15313
  this.searchToolNames = new Set([...SEARCH_TOOLS, ...singleDatasourceTools.map((tool) => tool.name)]);
@@ -15645,7 +15581,7 @@ ${summary}
15645
15581
  if (this.activeRun) {
15646
15582
  throw new Error("AutoRAG agent is busy; await the in-flight searchDocuments() call before starting another");
15647
15583
  }
15648
- const sessionId = randomUUID6();
15584
+ const sessionId = randomUUID7();
15649
15585
  const trimmedQuery = query.trim();
15650
15586
  if (trimmedQuery.length === 0) {
15651
15587
  this.lastQuery = trimmedQuery;
@@ -16148,16 +16084,54 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16148
16084
  const allMethods = methods === undefined;
16149
16085
  const wants = (m) => allMethods || methods.includes(m);
16150
16086
  const needsParsed = allMethods || wants("parsed") || wants("minsync");
16087
+ const runId = randomUUID7();
16088
+ const startedAt = new Date().toISOString();
16089
+ let progress = {
16090
+ version: 1,
16091
+ runId,
16092
+ pid: process.pid,
16093
+ state: "running",
16094
+ phase: "parsed",
16095
+ startedAt,
16096
+ updatedAt: startedAt
16097
+ };
16098
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16151
16099
  this.refreshState = {
16152
16100
  ...this.refreshState,
16153
16101
  inFlight: true,
16154
- lastStartedAt: new Date().toISOString()
16102
+ lastStartedAt: startedAt
16155
16103
  };
16156
16104
  try {
16157
16105
  const summary = needsParsed ? await this.syncParsedMirrors(force) : await this.scanMirrorStaleness();
16106
+ progress = updateRefreshProgress(progress, {
16107
+ phase: wants("minsync") ? "minsync" : wants("datasources") ? "datasources" : wants("jikji") ? "jikji" : "finalizing",
16108
+ sourceFiles: { total: summary.scanned },
16109
+ parsedCounts: {
16110
+ scanned: summary.scanned,
16111
+ written: summary.written,
16112
+ deleted: summary.deleted,
16113
+ skipped: summary.skipped
16114
+ }
16115
+ });
16116
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16158
16117
  const minsync = wants("minsync") ? await this.syncMinSync(force) : undefined;
16118
+ if (minsync !== undefined) {
16119
+ progress = updateRefreshProgress(progress, {
16120
+ phase: wants("datasources") ? "datasources" : wants("jikji") ? "jikji" : "finalizing",
16121
+ minsync: { synced: minsync.synced }
16122
+ });
16123
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16124
+ }
16159
16125
  const datasources = wants("datasources") ? await this.indexDatasources() : [];
16126
+ if (wants("datasources")) {
16127
+ progress = updateRefreshProgress(progress, {
16128
+ phase: wants("jikji") || allMethods ? "jikji" : "finalizing"
16129
+ });
16130
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16131
+ }
16160
16132
  const jikji = allMethods || wants("jikji") || wants("minsync") ? await this.executeJikjiPrepare() : undefined;
16133
+ progress = updateRefreshProgress(progress, { phase: "finalizing" });
16134
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16161
16135
  this.retrievalScopeBindings = buildRetrievalScopeBindings(this.workspaceProjectRoot, this.searchPaths, this.configuredSearchPaths);
16162
16136
  const jikjiDiagnostics = (jikji ?? []).map((result) => jikjiPrepareDiagnostic(result)).filter((diag) => diag !== undefined);
16163
16137
  this.refreshState = {
@@ -16176,10 +16150,15 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16176
16150
  lastError: undefined
16177
16151
  };
16178
16152
  if (needsParsed) {
16179
- mkdirSync11(dirname12(refreshReadinessPath(this.workspaceProjectRoot)), { recursive: true });
16180
- writeFileSync10(refreshReadinessPath(this.workspaceProjectRoot), `{"version":1,"completed":true,"parsed":true}
16153
+ mkdirSync12(dirname13(refreshReadinessPath(this.workspaceProjectRoot)), { recursive: true });
16154
+ writeFileSync11(refreshReadinessPath(this.workspaceProjectRoot), `{"version":1,"completed":true,"parsed":true}
16181
16155
  `);
16182
16156
  }
16157
+ progress = updateRefreshProgress(progress, {
16158
+ state: "success",
16159
+ finishedAt: new Date().toISOString()
16160
+ });
16161
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16183
16162
  const minsyncDiagnostics = minSyncRefreshDiagnostics(minsync);
16184
16163
  const publicMinsync = minsync ? {
16185
16164
  ok: minsync.ok,
@@ -16205,6 +16184,12 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16205
16184
  lastOutcome: "failed",
16206
16185
  lastError: error instanceof Error ? `Refresh failed: ${error.name}` : "Refresh failed."
16207
16186
  };
16187
+ progress = updateRefreshProgress(progress, {
16188
+ state: "failed",
16189
+ error: this.refreshState.lastError,
16190
+ finishedAt: new Date().toISOString()
16191
+ });
16192
+ writeRefreshProgress(this.workspaceProjectRoot, progress);
16208
16193
  throw error;
16209
16194
  } finally {
16210
16195
  this.refreshState = {
@@ -16215,6 +16200,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16215
16200
  }
16216
16201
  }
16217
16202
  async getRefreshStatus() {
16203
+ const persistedProgress = readRefreshProgress(this.workspaceProjectRoot);
16218
16204
  const staleDiagnostics = await detectMirrorStaleness({
16219
16205
  root: this.workspaceProjectRoot,
16220
16206
  searchPaths: this.searchPaths,
@@ -16255,18 +16241,37 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16255
16241
  source: "watch"
16256
16242
  });
16257
16243
  }
16258
- const state = this.refreshState.inFlight ? "indexing" : this.refreshState.lastOutcome === "never" ? "idle" : this.refreshState.lastOutcome;
16259
- const parsedMirrorReady = this.refreshState.lastOutcome === "success" || existsSync17(refreshReadinessPath(this.workspaceProjectRoot));
16244
+ const persistedRunning = persistedProgress?.state === "running";
16245
+ const ownerAlive = persistedProgress !== undefined && isRefreshOwnerAlive(persistedProgress);
16246
+ const interrupted = persistedRunning && !ownerAlive && !this.refreshState.inFlight;
16247
+ if (interrupted) {
16248
+ diagnostics.push({
16249
+ code: "refresh-interrupted",
16250
+ severity: "error",
16251
+ message: "A previous refresh stopped before it completed.",
16252
+ source: "refresh"
16253
+ });
16254
+ }
16255
+ const state = this.refreshState.inFlight || persistedRunning && ownerAlive ? "indexing" : interrupted ? "failed" : persistedProgress?.state === "success" || persistedProgress?.state === "failed" ? persistedProgress.state : this.refreshState.lastOutcome === "never" ? "idle" : this.refreshState.lastOutcome;
16256
+ const parsedMirrorReady = this.refreshState.lastOutcome === "success" || persistedProgress?.state === "success" || existsSync17(refreshReadinessPath(this.workspaceProjectRoot));
16257
+ const effectiveProgress = this.refreshState.inFlight || persistedRunning ? persistedProgress : undefined;
16260
16258
  return {
16261
16259
  state,
16262
16260
  inFlight: this.refreshState.inFlight,
16263
- lastStartedAt: this.refreshState.lastStartedAt,
16264
- lastFinishedAt: this.refreshState.lastFinishedAt,
16261
+ lastStartedAt: this.refreshState.lastStartedAt ?? persistedProgress?.startedAt,
16262
+ lastFinishedAt: this.refreshState.lastFinishedAt ?? persistedProgress?.finishedAt,
16265
16263
  counts: this.refreshState.counts,
16264
+ progress: effectiveProgress === undefined ? undefined : {
16265
+ phase: effectiveProgress.phase,
16266
+ sourceFiles: effectiveProgress.sourceFiles,
16267
+ parsedCounts: effectiveProgress.parsedCounts,
16268
+ minsync: effectiveProgress.minsync,
16269
+ ownerAlive
16270
+ },
16266
16271
  stale: !parsedMirrorReady || staleDiagnostics.length > 0,
16267
16272
  diagnostics,
16268
16273
  components: this.refreshComponentStatus(),
16269
- lastError: this.refreshState.lastError
16274
+ lastError: this.refreshState.lastError ?? (persistedProgress?.state === "failed" ? persistedProgress.error : undefined)
16270
16275
  };
16271
16276
  }
16272
16277
  refreshComponentStatus() {
@@ -16338,7 +16343,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16338
16343
  written: 0,
16339
16344
  deleted: 0,
16340
16345
  skipped: 0,
16341
- indexPath: join22(this.workspaceProjectRoot, PARSED_MIRROR_SUBDIR),
16346
+ indexPath: join21(this.workspaceProjectRoot, PARSED_MIRROR_SUBDIR),
16342
16347
  diagnostics
16343
16348
  };
16344
16349
  }
@@ -16698,7 +16703,7 @@ function buildMissingFinalEmitAnswer(query, reason, trace) {
16698
16703
  const lines = [
16699
16704
  `The search run for "${query}" ended without finalized results: the agent ended its run without calling emit_autorag_results, so no curated answer is available within the configured search range.`,
16700
16705
  reason === undefined ? "The agent did not record why it stopped." : `The agent's last note before stopping: "${reason.length > 500 ? `${reason.slice(0, 500)}…` : reason}"`,
16701
- "Next steps: broaden the configured searchPaths, connect additional datasources (for example via `autorag ui` or the datasources configuration), or retry with a narrower or different query.",
16706
+ "Next steps: broaden the configured searchPaths, connect additional datasources via the datasources configuration, or retry with a narrower or different query.",
16702
16707
  trace.length > 0 ? "Retrieval candidates gathered before the run ended are attached under `retrievalTrace` for inspection." : "No retrieval candidates were gathered before the run ended."
16703
16708
  ];
16704
16709
  return lines.join(`
@@ -16802,6 +16807,7 @@ var init_agent = __esm(() => {
16802
16807
  init_dupey();
16803
16808
  init_diagnostics();
16804
16809
  init_jikji();
16810
+ init_language();
16805
16811
  init_loader();
16806
16812
  init_check_memory_tool();
16807
16813
  init_memory();
@@ -16810,6 +16816,7 @@ var init_agent = __esm(() => {
16810
16816
  init_sync();
16811
16817
  init_run_log();
16812
16818
  init_injection_classifier();
16819
+ init_parser();
16813
16820
  init_engine();
16814
16821
  init_merger();
16815
16822
  init_scope();
@@ -16822,6 +16829,7 @@ var init_agent = __esm(() => {
16822
16829
  init_jikji_find_tool();
16823
16830
  init_local_model();
16824
16831
  init_peer_target_tool();
16832
+ init_refresh_progress();
16825
16833
  init_search_all_tool();
16826
16834
  init_search_documents();
16827
16835
  init_search_minsync_tool();
@@ -17002,7 +17010,7 @@ ${this.description}`
17002
17010
  }
17003
17011
 
17004
17012
  // src/datasource/skills/clawgallery/client.ts
17005
- import { spawn as spawn7 } from "node:child_process";
17013
+ import { spawn as spawn8 } from "node:child_process";
17006
17014
  import { existsSync as existsSync18 } from "node:fs";
17007
17015
 
17008
17016
  class ClawGalleryClient {
@@ -17107,7 +17115,7 @@ class ClawGalleryClient {
17107
17115
  function spawnCli(binary, args, env, options, signal) {
17108
17116
  return new Promise((resolve) => {
17109
17117
  const portable = portableSpawnCommand(binary, args);
17110
- const child = spawn7(portable.command, [...portable.args], { env, stdio: ["ignore", "pipe", "pipe"] });
17118
+ const child = spawn8(portable.command, [...portable.args], { env, stdio: ["ignore", "pipe", "pipe"] });
17111
17119
  let stdout = "";
17112
17120
  let stderr = "";
17113
17121
  let settled = false;
@@ -17626,10 +17634,10 @@ function resolveToken(token, tokenEnv) {
17626
17634
  var DEFAULT_TIMEOUT_MS6 = 15000;
17627
17635
 
17628
17636
  // src/datasource/skills/cloud-drive/rclone-connector.ts
17629
- import { spawn as spawn8 } from "node:child_process";
17630
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, renameSync as renameSync7, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
17631
- import { readFile as readFile6 } from "node:fs/promises";
17632
- import { dirname as dirname13, join as join23, normalize as normalize3 } from "node:path";
17637
+ import { spawn as spawn9 } from "node:child_process";
17638
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync8, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
17639
+ import { readFile as readFile4 } from "node:fs/promises";
17640
+ import { dirname as dirname14, join as join22, normalize as normalize3 } from "node:path";
17633
17641
 
17634
17642
  class RcloneConnector {
17635
17643
  options;
@@ -17716,18 +17724,18 @@ class RcloneConnector {
17716
17724
  return;
17717
17725
  const destination = mirrorFilePath(paths.mirrorRoot, entry.path);
17718
17726
  const temp = `${destination}.autorag-tmp`;
17719
- mkdirSync12(dirname13(temp), { recursive: true });
17727
+ mkdirSync13(dirname14(temp), { recursive: true });
17720
17728
  let copied;
17721
17729
  try {
17722
17730
  copied = await this.runner(copyArgs(remote, entry.path, temp, exportFormats, this.options.concurrency, this.options.bandwidthLimit), timeoutMs);
17723
17731
  } catch {
17724
17732
  copyFailure = { reason: "unavailable", message: "rclone copy failed unexpectedly" };
17725
- rmSync4(temp, { force: true });
17733
+ rmSync5(temp, { force: true });
17726
17734
  return;
17727
17735
  }
17728
17736
  if (!copied.ok) {
17729
17737
  copyFailure = { reason: classifyFailure(copied.stderr), message: shortFailure(copied) };
17730
- rmSync4(temp, { force: true });
17738
+ rmSync5(temp, { force: true });
17731
17739
  return;
17732
17740
  }
17733
17741
  staged.push({ temp, destination });
@@ -17737,13 +17745,13 @@ class RcloneConnector {
17737
17745
  await Promise.all(Array.from({ length: concurrency }, () => copyOne()));
17738
17746
  if (copyFailure !== undefined) {
17739
17747
  for (const file of staged)
17740
- rmSync4(file.temp, { force: true });
17748
+ rmSync5(file.temp, { force: true });
17741
17749
  return { ok: false, reason: copyFailure.reason, message: copyFailure.message };
17742
17750
  }
17743
17751
  for (const file of staged)
17744
- renameSync7(file.temp, file.destination);
17752
+ renameSync8(file.temp, file.destination);
17745
17753
  for (const entry of deletedEntries)
17746
- rmSync4(mirrorFilePath(paths.mirrorRoot, entry.path), { force: true });
17754
+ rmSync5(mirrorFilePath(paths.mirrorRoot, entry.path), { force: true });
17747
17755
  saveManifest(paths.manifestPath, { version: 1, remoteName: remoteName(remote), entries: inventory });
17748
17756
  const mirrorWarnings = await loadMirroredDocuments(paths.mirrorRoot, changedEntries, skillName, instanceId, remote, this.options.parserOptions);
17749
17757
  const warnings = [
@@ -17821,12 +17829,12 @@ function sameEntry(previous, current) {
17821
17829
  return previous?.path === current.path && previous.size === current.size && previous.modTime === current.modTime && previous.remoteId === current.remoteId && JSON.stringify(previous.hashes) === JSON.stringify(current.hashes);
17822
17830
  }
17823
17831
  function mirrorPaths(workspaceRoot, skillName, instanceId) {
17824
- const base = join23(workspaceRoot, ".autorag", "datasources", skillName, instanceId);
17825
- return { mirrorRoot: join23(base, "mirror"), manifestPath: join23(base, "manifest.json") };
17832
+ const base = join22(workspaceRoot, ".autorag", "datasources", skillName, instanceId);
17833
+ return { mirrorRoot: join22(base, "mirror"), manifestPath: join22(base, "manifest.json") };
17826
17834
  }
17827
17835
  function mirrorFilePath(root, path) {
17828
17836
  const safe = normalize3(path).replace(/^(\.\.(?:[/\\]|$))+|^[/\\]+/gu, "");
17829
- return join23(root, safe);
17837
+ return join22(root, safe);
17830
17838
  }
17831
17839
  function remoteTarget(remote, path) {
17832
17840
  return remote.endsWith(":") || remote.endsWith("/") ? `${remote}${path}` : `${remote}/${path}`;
@@ -17845,18 +17853,18 @@ function copyArgs(remote, path, destination, exportFormats, concurrency, bandwid
17845
17853
  }
17846
17854
  function loadManifest2(path) {
17847
17855
  try {
17848
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
17856
+ const parsed = JSON.parse(readFileSync14(path, "utf8"));
17849
17857
  return parsed.version === 1 && Array.isArray(parsed.entries) ? parsed : undefined;
17850
17858
  } catch {
17851
17859
  return;
17852
17860
  }
17853
17861
  }
17854
17862
  function saveManifest(path, manifest) {
17855
- mkdirSync12(dirname13(path), { recursive: true });
17863
+ mkdirSync13(dirname14(path), { recursive: true });
17856
17864
  const temp = `${path}.autorag-tmp`;
17857
- writeFileSync11(temp, `${JSON.stringify(manifest)}
17865
+ writeFileSync12(temp, `${JSON.stringify(manifest)}
17858
17866
  `, "utf8");
17859
- renameSync7(temp, path);
17867
+ renameSync8(temp, path);
17860
17868
  }
17861
17869
  async function loadMirroredDocuments(root, entries, skillName, instanceId, remote, parserOptions) {
17862
17870
  const documents = [];
@@ -17869,7 +17877,7 @@ async function loadMirroredDocuments(root, entries, skillName, instanceId, remot
17869
17877
  const parser = registry.getForVirtualPath(entry.path);
17870
17878
  if (parser === undefined)
17871
17879
  continue;
17872
- const bytes = await readFile6(sourcePath);
17880
+ const bytes = await readFile4(sourcePath);
17873
17881
  const parsed = await parser.parse({ virtualPath: entry.path, sourcePath, bytes });
17874
17882
  for (const diagnostic of parsed.diagnostics ?? []) {
17875
17883
  warnings.push(`${diagnostic.code}: ${diagnostic.message}`);
@@ -17930,7 +17938,7 @@ function shortFailure(result) {
17930
17938
  function runBinary(binary, args, timeoutMs, env, cwd) {
17931
17939
  return new Promise((resolvePromise) => {
17932
17940
  const portable = portableSpawnCommand(binary, args);
17933
- const child = spawn8(portable.command, [...portable.args], {
17941
+ const child = spawn9(portable.command, [...portable.args], {
17934
17942
  ...env === undefined ? {} : { env: { ...process.env, ...env } },
17935
17943
  ...cwd === undefined ? {} : { cwd },
17936
17944
  stdio: ["ignore", "pipe", "pipe"]
@@ -18004,10 +18012,10 @@ function sanitizeIdSegment(value) {
18004
18012
  var init_connector = () => {};
18005
18013
 
18006
18014
  // src/datasource/chunk-store.ts
18007
- import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "node:fs";
18008
- import { dirname as dirname14, join as join24 } from "node:path";
18015
+ import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
18016
+ import { dirname as dirname15, join as join23 } from "node:path";
18009
18017
  function datasourceChunkStorePath(workspaceRoot, skillName, instanceId) {
18010
- return join24(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId), "chunks.json");
18018
+ return join23(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId), "chunks.json");
18011
18019
  }
18012
18020
 
18013
18021
  class DatasourceChunkStore {
@@ -18046,7 +18054,7 @@ class DatasourceChunkStore {
18046
18054
  try {
18047
18055
  if (!existsSync19(this.storePath))
18048
18056
  return false;
18049
- const parsed = JSON.parse(readFileSync14(this.storePath, "utf8"));
18057
+ const parsed = JSON.parse(readFileSync15(this.storePath, "utf8"));
18050
18058
  if (parsed.version !== STORE_VERSION || !Array.isArray(parsed.chunks))
18051
18059
  return false;
18052
18060
  this.chunkList = parsed.chunks.filter((chunk) => typeof chunk?.chunkId === "string" && typeof chunk?.content === "string");
@@ -18120,7 +18128,7 @@ class DatasourceChunkStore {
18120
18128
  if (this.storePath === undefined)
18121
18129
  return;
18122
18130
  try {
18123
- mkdirSync13(dirname14(this.storePath), { recursive: true });
18131
+ mkdirSync14(dirname15(this.storePath), { recursive: true });
18124
18132
  const payload = {
18125
18133
  version: STORE_VERSION,
18126
18134
  skill: this.skillName,
@@ -18128,7 +18136,7 @@ class DatasourceChunkStore {
18128
18136
  updatedAt: Date.now(),
18129
18137
  chunks: this.chunkList
18130
18138
  };
18131
- writeFileSync12(this.storePath, `${JSON.stringify(payload)}
18139
+ writeFileSync13(this.storePath, `${JSON.stringify(payload)}
18132
18140
  `, "utf8");
18133
18141
  } catch {}
18134
18142
  }
@@ -18543,13 +18551,13 @@ var init_cloud_drive = __esm(() => {
18543
18551
  });
18544
18552
 
18545
18553
  // src/process/terminate-tree.ts
18546
- import { spawn as spawn9 } from "node:child_process";
18554
+ import { spawn as spawn10 } from "node:child_process";
18547
18555
  function terminateProcessTree(child, signal = "SIGKILL") {
18548
18556
  const pid = child.pid;
18549
18557
  if (pid === undefined)
18550
18558
  return false;
18551
18559
  if (process.platform === "win32") {
18552
- const killer = spawn9("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", windowsHide: true });
18560
+ const killer = spawn10("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", windowsHide: true });
18553
18561
  killer.on("error", () => killDirectChild(child, signal));
18554
18562
  killer.on("close", (code) => {
18555
18563
  if (code !== 0)
@@ -18592,9 +18600,9 @@ var init_types4 = __esm(() => {
18592
18600
  });
18593
18601
 
18594
18602
  // src/datasource/skills/discrawl/client.ts
18595
- import { spawn as spawn10 } from "node:child_process";
18596
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
18597
- import { dirname as dirname15 } from "node:path";
18603
+ import { spawn as spawn11 } from "node:child_process";
18604
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
18605
+ import { dirname as dirname16 } from "node:path";
18598
18606
 
18599
18607
  class DiscrawlClient {
18600
18608
  options;
@@ -18610,7 +18618,7 @@ class DiscrawlClient {
18610
18618
  const path = `${workspace}/.autorag/datasources/discrawl/config.toml`;
18611
18619
  let existing = "";
18612
18620
  try {
18613
- existing = readFileSync15(path, "utf8");
18621
+ existing = readFileSync16(path, "utf8");
18614
18622
  } catch {}
18615
18623
  const managed = existing.startsWith(`# AutoRAG managed discrawl embeddings v1
18616
18624
  `);
@@ -18618,9 +18626,9 @@ class DiscrawlClient {
18618
18626
  return { configured: false, rebuildRequired: false };
18619
18627
  const previous = managed ? parseManagedEmbedding(existing) : undefined;
18620
18628
  const config = managedDiscrawlConfig({ ...runtime, baseUrl: nativeGatewayBaseUrl(runtime.baseUrl) });
18621
- mkdirSync14(dirname15(path), { recursive: true });
18629
+ mkdirSync15(dirname16(path), { recursive: true });
18622
18630
  if (existing !== config)
18623
- writeFileSync13(path, config, "utf8");
18631
+ writeFileSync14(path, config, "utf8");
18624
18632
  return {
18625
18633
  configured: true,
18626
18634
  rebuildRequired: previous !== undefined && (previous.provider !== runtime.provider || previous.model !== runtime.model || previous.dimensions !== runtime.dimensions)
@@ -18732,7 +18740,7 @@ function spawnDiscrawl(request) {
18732
18740
  return new Promise((resolveResult) => {
18733
18741
  const { options, args, env, signal } = request;
18734
18742
  const portable = portableSpawnCommand(options.binaryPath ?? DEFAULT_DISCRAWL_BINARY, args);
18735
- const child = spawn10(portable.command, [...portable.args], {
18743
+ const child = spawn11(portable.command, [...portable.args], {
18736
18744
  env,
18737
18745
  ...request.cwd === undefined ? {} : { cwd: request.cwd },
18738
18746
  detached: process.platform !== "win32",
@@ -19642,8 +19650,8 @@ var init_github = __esm(() => {
19642
19650
 
19643
19651
  // src/datasource/skills/github-gist/connector.ts
19644
19652
  import { execFile as execFile3 } from "node:child_process";
19645
- import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
19646
- import { dirname as dirname16 } from "node:path";
19653
+ import { existsSync as existsSync20, mkdirSync as mkdirSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
19654
+ import { dirname as dirname17 } from "node:path";
19647
19655
  async function defaultGhTokenRunner() {
19648
19656
  return new Promise((resolve) => {
19649
19657
  execFile3("gh", ["auth", "token"], { timeout: GH_TOKEN_TIMEOUT_MS }, (error, stdout) => {
@@ -19765,7 +19773,7 @@ class GitHubGistConnector {
19765
19773
  if (statePath === undefined || !existsSync20(statePath))
19766
19774
  return { version: STATE_VERSION, gists: {} };
19767
19775
  try {
19768
- const parsed = JSON.parse(readFileSync16(statePath, "utf8"));
19776
+ const parsed = JSON.parse(readFileSync17(statePath, "utf8"));
19769
19777
  if (parsed.version !== STATE_VERSION || typeof parsed.gists !== "object" || parsed.gists === null) {
19770
19778
  return { version: STATE_VERSION, gists: {} };
19771
19779
  }
@@ -19779,8 +19787,8 @@ class GitHubGistConnector {
19779
19787
  if (statePath === undefined)
19780
19788
  return;
19781
19789
  try {
19782
- mkdirSync15(dirname16(statePath), { recursive: true });
19783
- writeFileSync14(statePath, `${JSON.stringify(state)}
19790
+ mkdirSync16(dirname17(statePath), { recursive: true });
19791
+ writeFileSync15(statePath, `${JSON.stringify(state)}
19784
19792
  `, "utf8");
19785
19793
  } catch {}
19786
19794
  }
@@ -19825,8 +19833,8 @@ var init_connector3 = () => {};
19825
19833
 
19826
19834
  // src/datasource/skills/github-gist/semantic.ts
19827
19835
  import { createHash as createHash7 } from "node:crypto";
19828
- import { existsSync as existsSync21, mkdirSync as mkdirSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
19829
- import { dirname as dirname17 } from "node:path";
19836
+ import { existsSync as existsSync21, mkdirSync as mkdirSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
19837
+ import { dirname as dirname18 } from "node:path";
19830
19838
  function contentHash(chunk) {
19831
19839
  return createHash7("sha256").update(`${chunk.title ?? ""}
19832
19840
  ${chunk.content}`).digest("hex");
@@ -19919,7 +19927,7 @@ ${chunk.content}`));
19919
19927
  if (this.statePath === undefined || !existsSync21(this.statePath))
19920
19928
  return;
19921
19929
  try {
19922
- const parsed = JSON.parse(readFileSync17(this.statePath, "utf8"));
19930
+ const parsed = JSON.parse(readFileSync18(this.statePath, "utf8"));
19923
19931
  if (parsed.version !== VECTORS_VERSION || typeof parsed.entries !== "object" || parsed.entries === null)
19924
19932
  return;
19925
19933
  if (typeof parsed.identity?.dimension !== "number")
@@ -19934,13 +19942,13 @@ ${chunk.content}`));
19934
19942
  if (this.identity === undefined)
19935
19943
  return;
19936
19944
  try {
19937
- mkdirSync16(dirname17(this.statePath), { recursive: true });
19945
+ mkdirSync17(dirname18(this.statePath), { recursive: true });
19938
19946
  const payload = {
19939
19947
  version: VECTORS_VERSION,
19940
19948
  identity: this.identity,
19941
19949
  entries: this.entries
19942
19950
  };
19943
- writeFileSync15(this.statePath, `${JSON.stringify(payload)}
19951
+ writeFileSync16(this.statePath, `${JSON.stringify(payload)}
19944
19952
  `, "utf8");
19945
19953
  } catch {}
19946
19954
  }
@@ -20074,9 +20082,9 @@ var init_semantic = __esm(() => {
20074
20082
  });
20075
20083
 
20076
20084
  // src/datasource/skills/github-gist/skill.ts
20077
- import { join as join25 } from "node:path";
20085
+ import { join as join24 } from "node:path";
20078
20086
  function gistDatasourceDir(workspaceRoot, skillName, instanceId) {
20079
- return join25(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId));
20087
+ return join24(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId));
20080
20088
  }
20081
20089
  var GITHUB_GIST_SKILL_DEFINITION, DEFAULT_INSTANCE_ID3 = "default", GitHubGistSkill;
20082
20090
  var init_skill5 = __esm(() => {
@@ -20104,7 +20112,7 @@ var init_skill5 = __esm(() => {
20104
20112
  constructor(options = {}) {
20105
20113
  const { connector, connectorOptions, semantic, ...rest } = options;
20106
20114
  const instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID3;
20107
- const statePath = connectorOptions?.statePath ?? (options.workspaceRoot === undefined ? undefined : join25(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "state.json"));
20115
+ const statePath = connectorOptions?.statePath ?? (options.workspaceRoot === undefined ? undefined : join24(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "state.json"));
20108
20116
  super(GITHUB_GIST_SKILL_DEFINITION, {
20109
20117
  ...rest,
20110
20118
  connector: connector ?? new GitHubGistConnector({
@@ -20114,7 +20122,7 @@ var init_skill5 = __esm(() => {
20114
20122
  });
20115
20123
  if (semantic?.enabled !== false) {
20116
20124
  this.semanticIndex = new GistSemanticIndex({
20117
- statePath: semantic?.statePath ?? (options.workspaceRoot === undefined ? undefined : join25(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "vectors.json"))
20125
+ statePath: semantic?.statePath ?? (options.workspaceRoot === undefined ? undefined : join24(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "vectors.json"))
20118
20126
  });
20119
20127
  this.semanticEmbedder = semantic?.embedder ?? createGatewayGistEmbedder(semantic?.embedderOptions ?? {});
20120
20128
  this.semanticMethod = new GitHubGistSemanticMethod({
@@ -20164,7 +20172,7 @@ var init_github_gist = __esm(() => {
20164
20172
  });
20165
20173
 
20166
20174
  // src/datasource/crawler-client.ts
20167
- import { spawn as spawn11 } from "node:child_process";
20175
+ import { spawn as spawn12 } from "node:child_process";
20168
20176
 
20169
20177
  class CrawlerCliClient {
20170
20178
  profile;
@@ -20201,7 +20209,7 @@ class CrawlerCliClient {
20201
20209
  function spawnCrawler(command, args, env, signal, options, cwd) {
20202
20210
  return new Promise((resolve) => {
20203
20211
  const portable = portableSpawnCommand(command, args);
20204
- const child = spawn11(portable.command, [...portable.args], {
20212
+ const child = spawn12(portable.command, [...portable.args], {
20205
20213
  env,
20206
20214
  ...cwd === undefined ? {} : { cwd },
20207
20215
  stdio: ["ignore", "pipe", "pipe"]
@@ -20414,7 +20422,7 @@ function parseMessages(stdout) {
20414
20422
  return;
20415
20423
  const hits = [];
20416
20424
  for (const row of rows) {
20417
- if (!isRecord12(row))
20425
+ if (!isRecord14(row))
20418
20426
  return;
20419
20427
  const id = scalarString(row.message_id);
20420
20428
  const content = scalarString(row.content);
@@ -20447,9 +20455,9 @@ function parseDocs(stdout) {
20447
20455
  return;
20448
20456
  const hits = [];
20449
20457
  for (const row of rows) {
20450
- if (!isRecord12(row))
20458
+ if (!isRecord14(row))
20451
20459
  return;
20452
- const meta = isRecord12(row.result_meta) ? row.result_meta : {};
20460
+ const meta = isRecord14(row.result_meta) ? row.result_meta : {};
20453
20461
  const token = firstString(meta, ["token", "docs_token"]) ?? firstString(row, ["token", "docs_token"]) ?? tokenFromUrl(firstString(meta, ["url"]));
20454
20462
  const content = stripHighlight(firstString(row, ["summary_highlighted", "title_highlighted", "summary", "title"]) ?? "");
20455
20463
  if (token === undefined || !isSafeId(token) || content.length === 0)
@@ -20480,7 +20488,7 @@ function envelopeData(stdout) {
20480
20488
  } catch {
20481
20489
  return;
20482
20490
  }
20483
- if (!isRecord12(parsed) || parsed.ok !== true || !isRecord12(parsed.data))
20491
+ if (!isRecord14(parsed) || parsed.ok !== true || !isRecord14(parsed.data))
20484
20492
  return;
20485
20493
  return parsed.data;
20486
20494
  }
@@ -20513,7 +20521,7 @@ function stripHighlight(value) {
20513
20521
  function isSafeId(value) {
20514
20522
  return value.length > 0 && value !== "." && value !== ".." && !value.includes("#") && !value.includes("/");
20515
20523
  }
20516
- function isRecord12(value) {
20524
+ function isRecord14(value) {
20517
20525
  return typeof value === "object" && value !== null && !Array.isArray(value);
20518
20526
  }
20519
20527
  function isRateLimited(failure) {
@@ -20828,9 +20836,9 @@ var init_types5 = __esm(() => {
20828
20836
  });
20829
20837
 
20830
20838
  // src/datasource/skills/lazykatok/client.ts
20831
- import { spawn as spawn12 } from "node:child_process";
20839
+ import { spawn as spawn13 } from "node:child_process";
20832
20840
  import { existsSync as existsSync22 } from "node:fs";
20833
- import { delimiter as delimiter4, join as join26 } from "node:path";
20841
+ import { delimiter as delimiter4, join as join25 } from "node:path";
20834
20842
 
20835
20843
  class LazykatokClient {
20836
20844
  options;
@@ -20924,7 +20932,7 @@ function spawnLazykatok(request) {
20924
20932
  return new Promise((resolve) => {
20925
20933
  const { options, args, env, signal } = request;
20926
20934
  const portable = portableSpawnCommand(commandFor2(options, env), args);
20927
- const child = spawn12(portable.command, [...portable.args], {
20935
+ const child = spawn13(portable.command, [...portable.args], {
20928
20936
  env,
20929
20937
  ...request.cwd === undefined ? {} : { cwd: request.cwd },
20930
20938
  detached: process.platform !== "win32",
@@ -21014,7 +21022,7 @@ function lookupInPath2(executable, env) {
21014
21022
  for (const dir of pathEnv.split(delimiter4)) {
21015
21023
  if (dir.length === 0)
21016
21024
  continue;
21017
- const candidate = join26(dir, executable);
21025
+ const candidate = join25(dir, executable);
21018
21026
  if (existsSync22(candidate))
21019
21027
  return candidate;
21020
21028
  }
@@ -21502,8 +21510,8 @@ var init_lazykatok = __esm(() => {
21502
21510
  });
21503
21511
 
21504
21512
  // src/datasource/skills/mail-export/connector.ts
21505
- import { readdir as readdir2, readFile as readFile7, stat as stat5 } from "node:fs/promises";
21506
- import { basename as basename10, dirname as dirname18, extname as extname9, join as join27 } from "node:path";
21513
+ import { readdir as readdir2, readFile as readFile5, stat as stat5 } from "node:fs/promises";
21514
+ import { basename as basename9, dirname as dirname19, extname as extname7, join as join26 } from "node:path";
21507
21515
  import { simpleParser as simpleParser2 } from "mailparser";
21508
21516
 
21509
21517
  class MailExportConnector {
@@ -21527,9 +21535,9 @@ class MailExportConnector {
21527
21535
  for (const entry of entries) {
21528
21536
  if (!entry.isFile())
21529
21537
  continue;
21530
- const extension = extname9(entry.name).toLowerCase();
21538
+ const extension = extname7(entry.name).toLowerCase();
21531
21539
  if (extension === ".eml" || extension === ".mbox") {
21532
- files.push(join27(entry.parentPath, entry.name));
21540
+ files.push(join26(entry.parentPath, entry.name));
21533
21541
  }
21534
21542
  }
21535
21543
  } else if (info.isFile()) {
@@ -21558,15 +21566,15 @@ class MailExportConnector {
21558
21566
  skippedLarge += 1;
21559
21567
  continue;
21560
21568
  }
21561
- bytes = await readFile7(file);
21569
+ bytes = await readFile5(file);
21562
21570
  } catch {
21563
21571
  parseFailures += 1;
21564
21572
  continue;
21565
21573
  }
21566
- const stem = basename10(file, extname9(file));
21567
- const mailboxDir = basename10(dirname18(file));
21574
+ const stem = basename9(file, extname7(file));
21575
+ const mailboxDir = basename9(dirname19(file));
21568
21576
  const hierarchy = ["mailboxes", mailboxDir, stem];
21569
- const extension = extname9(file).toLowerCase();
21577
+ const extension = extname7(file).toLowerCase();
21570
21578
  const rawMessages = extension === ".mbox" ? splitMbox(bytes.toString("utf8")) : [bytes.toString("utf8")];
21571
21579
  for (const [index, raw] of rawMessages.entries()) {
21572
21580
  if (documents.length >= maxDocuments)
@@ -21674,21 +21682,21 @@ var init_mail_export = __esm(() => {
21674
21682
  });
21675
21683
 
21676
21684
  // src/datasource/skills/mailcrawl/config.ts
21677
- import { chmodSync, mkdirSync as mkdirSync17 } from "node:fs";
21685
+ import { chmodSync, mkdirSync as mkdirSync18 } from "node:fs";
21678
21686
  function validateMailcrawlInstanceId(instanceId) {
21679
21687
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(instanceId)) {
21680
21688
  throw new Error("mailcrawl instanceId must be a safe single path segment");
21681
21689
  }
21682
21690
  }
21683
21691
  function ensurePrivateMailcrawlDataDir(path) {
21684
- mkdirSync17(path, { recursive: true, mode: 448 });
21692
+ mkdirSync18(path, { recursive: true, mode: 448 });
21685
21693
  if (process.platform !== "win32")
21686
21694
  chmodSync(path, 448);
21687
21695
  }
21688
21696
  var init_config = () => {};
21689
21697
 
21690
21698
  // src/datasource/skills/mailcrawl/client.ts
21691
- import { spawn as spawn13 } from "node:child_process";
21699
+ import { spawn as spawn14 } from "node:child_process";
21692
21700
 
21693
21701
  class MailcrawlClient {
21694
21702
  options;
@@ -21808,7 +21816,7 @@ class MailcrawlClient {
21808
21816
  function spawnMailcrawl(binary, args, env, options, signal, timeoutMs) {
21809
21817
  return new Promise((resolveResult) => {
21810
21818
  const portable = portableSpawnCommand(binary, args);
21811
- const child = spawn13(portable.command, portable.args, {
21819
+ const child = spawn14(portable.command, portable.args, {
21812
21820
  env,
21813
21821
  detached: process.platform !== "win32",
21814
21822
  stdio: ["ignore", "pipe", "pipe"]
@@ -22504,7 +22512,7 @@ function parseCount(stdout) {
22504
22512
  return;
22505
22513
  if (Array.isArray(parsed))
22506
22514
  return parsed.length;
22507
- if (!isRecord13(parsed))
22515
+ if (!isRecord15(parsed))
22508
22516
  return;
22509
22517
  for (const key of ["pages", "page_count", "pageCount", "synced", "count"]) {
22510
22518
  const value = parsed[key];
@@ -22519,12 +22527,12 @@ function parseHits(stdout) {
22519
22527
  return parseTabSeparatedHits(trimmed);
22520
22528
  }
22521
22529
  const parsed = parseJson4(stdout);
22522
- const rows = Array.isArray(parsed) ? parsed : isRecord13(parsed) && Array.isArray(parsed.results) ? parsed.results : undefined;
22530
+ const rows = Array.isArray(parsed) ? parsed : isRecord15(parsed) && Array.isArray(parsed.results) ? parsed.results : undefined;
22523
22531
  if (rows === undefined)
22524
22532
  return;
22525
22533
  const hits = [];
22526
22534
  for (const [index, row] of rows.entries()) {
22527
- if (!isRecord13(row))
22535
+ if (!isRecord15(row))
22528
22536
  return;
22529
22537
  const id = stringValue2(row, ["page_id", "id"]);
22530
22538
  const content = stringValue2(row, ["snippet", "text", "content"]);
@@ -22588,7 +22596,7 @@ function parseJson4(stdout) {
22588
22596
  return;
22589
22597
  }
22590
22598
  }
22591
- function isRecord13(value) {
22599
+ function isRecord15(value) {
22592
22600
  return typeof value === "object" && value !== null && !Array.isArray(value);
22593
22601
  }
22594
22602
  function stringValue2(record, keys) {
@@ -22638,15 +22646,15 @@ var init_notion = __esm(() => {
22638
22646
  });
22639
22647
 
22640
22648
  // src/datasource/skills/obsidian/paths.ts
22641
- import { join as join28 } from "node:path";
22649
+ import { join as join27 } from "node:path";
22642
22650
  function obsidianDatasourceRoot(workspaceRoot, instanceId) {
22643
- return join28(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, OBSIDIAN_DATASOURCE_DIRNAME, instanceId);
22651
+ return join27(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, OBSIDIAN_DATASOURCE_DIRNAME, instanceId);
22644
22652
  }
22645
22653
  function obsidianQmdConfigDir(workspaceRoot, instanceId) {
22646
- return join28(obsidianDatasourceRoot(workspaceRoot, instanceId), "config");
22654
+ return join27(obsidianDatasourceRoot(workspaceRoot, instanceId), "config");
22647
22655
  }
22648
22656
  function obsidianQmdCacheDir(workspaceRoot, instanceId) {
22649
- return join28(obsidianDatasourceRoot(workspaceRoot, instanceId), "cache");
22657
+ return join27(obsidianDatasourceRoot(workspaceRoot, instanceId), "cache");
22650
22658
  }
22651
22659
  function stripEdgeDashes(value) {
22652
22660
  let start = 0;
@@ -22668,8 +22676,8 @@ var init_paths5 = () => {};
22668
22676
  var DEFAULT_QMD_BINARY = "qmd", DEFAULT_QMD_TIMEOUT_MS = 60000, DEFAULT_QMD_MAX_BUFFER_BYTES = 1048576;
22669
22677
 
22670
22678
  // src/datasource/skills/obsidian/client.ts
22671
- import { spawn as spawn14 } from "node:child_process";
22672
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "node:fs";
22679
+ import { spawn as spawn15 } from "node:child_process";
22680
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync17 } from "node:fs";
22673
22681
  import { isAbsolute as isAbsolute6, resolve as resolve12 } from "node:path";
22674
22682
 
22675
22683
  class QmdClient {
@@ -22689,10 +22697,10 @@ class QmdClient {
22689
22697
  const cacheDir = obsidianQmdCacheDir(workspaceRoot, instanceId);
22690
22698
  try {
22691
22699
  if (this.options.configPath === undefined) {
22692
- mkdirSync18(configDir, { recursive: true });
22693
- writeFileSync16(resolve12(configDir, "index.yml"), renderIndexYaml(collectionName, vaultPath), "utf8");
22700
+ mkdirSync19(configDir, { recursive: true });
22701
+ writeFileSync17(resolve12(configDir, "index.yml"), renderIndexYaml(collectionName, vaultPath), "utf8");
22694
22702
  }
22695
- mkdirSync18(cacheDir, { recursive: true });
22703
+ mkdirSync19(cacheDir, { recursive: true });
22696
22704
  } catch (error) {
22697
22705
  return failure3("spawn-error", error instanceof Error ? error.message : "failed to write qmd config");
22698
22706
  }
@@ -22793,7 +22801,7 @@ function isAllowedEnvKey2(key) {
22793
22801
  function spawnQmd(request) {
22794
22802
  return new Promise((resolveResult) => {
22795
22803
  const portable = portableSpawnCommand(request.binaryPath ?? DEFAULT_QMD_BINARY, request.args);
22796
- const child = spawn14(portable.command, [...portable.args], {
22804
+ const child = spawn15(portable.command, [...portable.args], {
22797
22805
  env: request.env,
22798
22806
  ...request.cwd === undefined ? {} : { cwd: request.cwd },
22799
22807
  detached: process.platform !== "win32",
@@ -22915,7 +22923,7 @@ ${stderr}`;
22915
22923
  }
22916
22924
  try {
22917
22925
  const parsed = JSON.parse(stdout);
22918
- if (isRecord14(parsed)) {
22926
+ if (isRecord16(parsed)) {
22919
22927
  return {
22920
22928
  indexed: numberField(parsed, "indexed") ?? numberField(parsed, "new") ?? 0,
22921
22929
  updated: numberField(parsed, "updated") ?? 0,
@@ -22944,7 +22952,7 @@ function parseSearchHits(stdout) {
22944
22952
  function extractHitRows(parsed) {
22945
22953
  if (Array.isArray(parsed))
22946
22954
  return parsed;
22947
- if (!isRecord14(parsed))
22955
+ if (!isRecord16(parsed))
22948
22956
  return;
22949
22957
  for (const key of ["results", "hits", "documents", "items"]) {
22950
22958
  const value = parsed[key];
@@ -22956,7 +22964,7 @@ function extractHitRows(parsed) {
22956
22964
  return;
22957
22965
  }
22958
22966
  function normalizeHit3(raw) {
22959
- if (!isRecord14(raw))
22967
+ if (!isRecord16(raw))
22960
22968
  return;
22961
22969
  const score = numberField(raw, "score") ?? numberField(raw, "relevance") ?? 0;
22962
22970
  const docid = stringField(raw, "docid") ?? stringField(raw, "id");
@@ -23002,7 +23010,7 @@ function numberField(record, key) {
23002
23010
  const value = record[key];
23003
23011
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
23004
23012
  }
23005
- function isRecord14(value) {
23013
+ function isRecord16(value) {
23006
23014
  return typeof value === "object" && value !== null && !Array.isArray(value);
23007
23015
  }
23008
23016
  var SAFE_INHERITED_ENV_KEYS3, SAFE_QMD_ENV_PREFIX = "QMD_";
@@ -23529,7 +23537,7 @@ function parseCount2(stdout) {
23529
23537
  return;
23530
23538
  if (Array.isArray(parsed))
23531
23539
  return parsed.length;
23532
- if (!isRecord15(parsed))
23540
+ if (!isRecord17(parsed))
23533
23541
  return;
23534
23542
  for (const key of ["messages", "message_count", "messageCount", "synced", "count"]) {
23535
23543
  const value = parsed[key];
@@ -23542,12 +23550,12 @@ function parseHits2(stdout) {
23542
23550
  const parsed = parseJson5(stdout);
23543
23551
  if (parsed === null)
23544
23552
  return [];
23545
- const rows = Array.isArray(parsed) ? parsed : isRecord15(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23553
+ const rows = Array.isArray(parsed) ? parsed : isRecord17(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23546
23554
  if (rows === undefined)
23547
23555
  return;
23548
23556
  const hits = [];
23549
23557
  for (const [index, row] of rows.entries()) {
23550
- if (!isRecord15(row))
23558
+ if (!isRecord17(row))
23551
23559
  return;
23552
23560
  const workspaceId = stringValue3(row, ["workspace_id"]);
23553
23561
  const workspaceName = stringValue3(row, ["workspace_name"]);
@@ -23602,7 +23610,7 @@ function parseJson5(stdout) {
23602
23610
  return;
23603
23611
  }
23604
23612
  }
23605
- function isRecord15(value) {
23613
+ function isRecord17(value) {
23606
23614
  return typeof value === "object" && value !== null && !Array.isArray(value);
23607
23615
  }
23608
23616
  function stringValue3(record, keys) {
@@ -23665,10 +23673,10 @@ var init_slack = __esm(() => {
23665
23673
  });
23666
23674
 
23667
23675
  // src/datasource/skills/spotlight/connector.ts
23668
- import { spawn as spawn15 } from "node:child_process";
23676
+ import { spawn as spawn16 } from "node:child_process";
23669
23677
  import { createHash as createHash8 } from "node:crypto";
23670
23678
  import { readFile as fsReadFile, stat as stat6 } from "node:fs/promises";
23671
- import { basename as basename11, extname as extname10 } from "node:path";
23679
+ import { basename as basename10, extname as extname8 } from "node:path";
23672
23680
 
23673
23681
  class SpotlightConnector {
23674
23682
  options;
@@ -23752,10 +23760,10 @@ class SpotlightConnector {
23752
23760
  documents.push({
23753
23761
  docId: createHash8("sha256").update(path).digest("hex").slice(0, 16),
23754
23762
  hierarchy: ["files"],
23755
- title: basename11(path),
23763
+ title: basename10(path),
23756
23764
  content,
23757
23765
  publishedAt: info.mtimeMs,
23758
- metadata: { path, extension: extname10(path).toLowerCase() }
23766
+ metadata: { path, extension: extname8(path).toLowerCase() }
23759
23767
  });
23760
23768
  } catch (error) {
23761
23769
  const code = error.code;
@@ -23784,7 +23792,7 @@ class SpotlightConnector {
23784
23792
  }
23785
23793
  }
23786
23794
  var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS5 = 300, DEFAULT_MAX_BYTES_PER_FILE3, DEFAULT_TIMEOUT_MS9 = 15000, MAX_CONTENT_CHARS3 = 1e5, MAX_BUFFER_BYTES, FDA_GUIDANCE = "grant Full Disk Access to the terminal or app running AutoRAG (System Settings -> Privacy & Security -> Full Disk Access), then restart it", spawnRunner = (command, args, timeoutMs) => new Promise((resolve) => {
23787
- const child = spawn15(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
23795
+ const child = spawn16(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
23788
23796
  let stdout = "";
23789
23797
  let stderr = "";
23790
23798
  let settled = false;
@@ -23880,7 +23888,7 @@ function parseCount3(stdout) {
23880
23888
  return;
23881
23889
  if (Array.isArray(parsed))
23882
23890
  return parsed.length;
23883
- if (!isRecord16(parsed))
23891
+ if (!isRecord18(parsed))
23884
23892
  return;
23885
23893
  for (const key of ["messages", "message_count", "messageCount", "imported", "count"]) {
23886
23894
  const value = parsed[key];
@@ -23893,12 +23901,12 @@ function parseHits3(stdout) {
23893
23901
  const parsed = parseJson6(stdout);
23894
23902
  if (parsed === null)
23895
23903
  return [];
23896
- const rows = Array.isArray(parsed) ? parsed : isRecord16(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23904
+ const rows = Array.isArray(parsed) ? parsed : isRecord18(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23897
23905
  if (rows === undefined)
23898
23906
  return;
23899
23907
  const hits = [];
23900
23908
  for (const [index, row] of rows.entries()) {
23901
- if (!isRecord16(row))
23909
+ if (!isRecord18(row))
23902
23910
  return;
23903
23911
  const id = stringValue4(row, ["message_id", "event_id", "id"]);
23904
23912
  const content = stringValue4(row, ["snippet", "text", "content"]);
@@ -23937,7 +23945,7 @@ function parseJson6(stdout) {
23937
23945
  return;
23938
23946
  }
23939
23947
  }
23940
- function isRecord16(value) {
23948
+ function isRecord18(value) {
23941
23949
  return typeof value === "object" && value !== null && !Array.isArray(value);
23942
23950
  }
23943
23951
  function stringValue4(record, keys) {
@@ -24000,7 +24008,7 @@ function parseCount4(stdout) {
24000
24008
  return;
24001
24009
  if (Array.isArray(parsed))
24002
24010
  return parsed.length;
24003
- if (!isRecord17(parsed))
24011
+ if (!isRecord19(parsed))
24004
24012
  return;
24005
24013
  for (const key of ["messages", "message_count", "messageCount", "imported", "count"]) {
24006
24014
  const value = parsed[key];
@@ -24013,12 +24021,12 @@ function parseHits4(stdout) {
24013
24021
  const parsed = parseJson7(stdout);
24014
24022
  if (parsed === null)
24015
24023
  return [];
24016
- const rows = Array.isArray(parsed) ? parsed : isRecord17(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
24024
+ const rows = Array.isArray(parsed) ? parsed : isRecord19(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
24017
24025
  if (rows === undefined)
24018
24026
  return;
24019
24027
  const hits = [];
24020
24028
  for (const [index, row] of rows.entries()) {
24021
- if (!isRecord17(row))
24029
+ if (!isRecord19(row))
24022
24030
  return;
24023
24031
  const id = stringValue5(row, ["message_id", "event_id", "id"]);
24024
24032
  const content = stringValue5(row, ["snippet", "text", "content"]);
@@ -24055,7 +24063,7 @@ function parseJson7(stdout) {
24055
24063
  return;
24056
24064
  }
24057
24065
  }
24058
- function isRecord17(value) {
24066
+ function isRecord19(value) {
24059
24067
  return typeof value === "object" && value !== null && !Array.isArray(value);
24060
24068
  }
24061
24069
  function stringValue5(record, keys) {
@@ -24343,9 +24351,9 @@ var init_factory = __esm(() => {
24343
24351
  });
24344
24352
 
24345
24353
  // src/cli/config.ts
24346
- import { randomUUID as randomUUID7 } from "node:crypto";
24347
- import { existsSync as existsSync23, mkdirSync as mkdirSync19, readFileSync as readFileSync18, renameSync as renameSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync17 } from "node:fs";
24348
- import { basename as basename12, dirname as dirname19, isAbsolute as isAbsolute7, join as join29, resolve as resolve13 } from "node:path";
24354
+ import { randomUUID as randomUUID8 } from "node:crypto";
24355
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync19, renameSync as renameSync9, unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "node:fs";
24356
+ import { basename as basename11, dirname as dirname20, isAbsolute as isAbsolute7, join as join28, resolve as resolve13 } from "node:path";
24349
24357
  import { findEnvKeys, getEnvApiKey, getModel, getProviders } from "@earendil-works/pi-ai/compat";
24350
24358
  function resolveConfigPath(input) {
24351
24359
  const flags = input.flags;
@@ -24360,9 +24368,9 @@ function resolveConfigPath(input) {
24360
24368
  return { configPath: envConfig, explicit: true };
24361
24369
  }
24362
24370
  return {
24363
- configPath: join29(resolveAutoRAGHome(env), DEFAULT_CONFIG_FILENAME),
24371
+ configPath: join28(resolveAutoRAGHome(env), DEFAULT_CONFIG_FILENAME),
24364
24372
  explicit: false,
24365
- legacyPath: join29(cwd, LEGACY_CONFIG_FILENAME)
24373
+ legacyPath: join28(cwd, LEGACY_CONFIG_FILENAME)
24366
24374
  };
24367
24375
  }
24368
24376
  function readConfigFile(configPath, explicit) {
@@ -24380,7 +24388,7 @@ function readConfigFile(configPath, explicit) {
24380
24388
  }
24381
24389
  let text;
24382
24390
  try {
24383
- text = readFileSync18(configPath, "utf8");
24391
+ text = readFileSync19(configPath, "utf8");
24384
24392
  } catch (err) {
24385
24393
  throw new ConfigError(`Failed to read config file: ${err.message}`);
24386
24394
  }
@@ -24418,11 +24426,11 @@ function acquireConfigWriteLock(configPath) {
24418
24426
  });
24419
24427
  }
24420
24428
  function replaceFileAtomically(path, contents, assertCommitAllowed) {
24421
- const temporaryPath = join29(dirname19(path), `.${basename12(path)}.${process.pid}.${randomUUID7()}.tmp`);
24429
+ const temporaryPath = join28(dirname20(path), `.${basename11(path)}.${process.pid}.${randomUUID8()}.tmp`);
24422
24430
  try {
24423
- writeFileSync17(temporaryPath, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
24431
+ writeFileSync18(temporaryPath, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
24424
24432
  assertCommitAllowed?.();
24425
- renameSync8(temporaryPath, path);
24433
+ renameSync9(temporaryPath, path);
24426
24434
  } finally {
24427
24435
  removeFileIfPresent2(temporaryPath);
24428
24436
  }
@@ -24431,12 +24439,12 @@ function migrateLegacyConfig(configPath, legacyPath) {
24431
24439
  if (existsSync23(configPath) || !existsSync23(legacyPath))
24432
24440
  return;
24433
24441
  const legacy = readConfigFile(legacyPath, true);
24434
- const legacyBytes = readFileSync18(legacyPath);
24435
- const migrated = normalizeLegacyConfigPaths(legacy ?? {}, dirname19(legacyPath));
24442
+ const legacyBytes = readFileSync19(legacyPath);
24443
+ const migrated = normalizeLegacyConfigPaths(legacy ?? {}, dirname20(legacyPath));
24436
24444
  const migratedPathsUnchanged = legacy?.workspacePath === migrated.workspacePath && legacy?.memoryPath === migrated.memoryPath && JSON.stringify(legacy?.searchPaths) === JSON.stringify(migrated.searchPaths);
24437
24445
  const migratedBytes = migratedPathsUnchanged ? legacyBytes : `${JSON.stringify(migrated, null, 2)}
24438
24446
  `;
24439
- mkdirSync19(dirname19(configPath), { recursive: true });
24447
+ mkdirSync20(dirname20(configPath), { recursive: true });
24440
24448
  const lock = acquireConfigWriteLock(configPath);
24441
24449
  try {
24442
24450
  const winner = readConfigFile(configPath, false);
@@ -24466,7 +24474,7 @@ function resolveConfigFileReadOnly(configPath, explicit, legacyPath) {
24466
24474
  const legacy = readConfigFile(legacyPath, false);
24467
24475
  if (legacy === undefined)
24468
24476
  return {};
24469
- return normalizeLegacyConfigPaths(legacy, dirname19(legacyPath));
24477
+ return normalizeLegacyConfigPaths(legacy, dirname20(legacyPath));
24470
24478
  }
24471
24479
  function resolveSearchPaths(searchPaths, origin) {
24472
24480
  return searchPaths.map((searchPath) => resolvePersistedPath(searchPath, origin));
@@ -24501,6 +24509,15 @@ function envString(env, key) {
24501
24509
  return value;
24502
24510
  return;
24503
24511
  }
24512
+ function normalizeConfigLanguages(raw) {
24513
+ try {
24514
+ return normalizeLanguages(raw);
24515
+ } catch (error) {
24516
+ if (error instanceof LanguageError)
24517
+ throw new ConfigError(error.message);
24518
+ throw error;
24519
+ }
24520
+ }
24504
24521
  function modelReference(value, path) {
24505
24522
  if (value === undefined)
24506
24523
  return;
@@ -24666,80 +24683,6 @@ function normalizeEmbedder(raw, path) {
24666
24683
  }
24667
24684
  return out;
24668
24685
  }
24669
- function normalizeUiConfig(raw) {
24670
- if (raw === undefined || raw === null)
24671
- return {};
24672
- if (typeof raw !== "object" || Array.isArray(raw))
24673
- throw new ConfigError("Config field 'ui' must be an object");
24674
- const record = raw;
24675
- const allowed = new Set(["host", "port", "allowRemote", "publicOrigin", "corsOrigins", "tokenEnv"]);
24676
- for (const key of Object.keys(record)) {
24677
- if (!allowed.has(key))
24678
- throw new ConfigError(`ui.${key} is not a recognized field`);
24679
- }
24680
- const out = {};
24681
- if (record.host !== undefined) {
24682
- if (typeof record.host !== "string" || record.host.trim().length === 0) {
24683
- throw new ConfigError("ui.host must be a non-empty string");
24684
- }
24685
- out.host = record.host.trim();
24686
- }
24687
- if (record.port !== undefined) {
24688
- if (typeof record.port !== "number" || !Number.isInteger(record.port) || record.port < 0 || record.port > 65535) {
24689
- throw new ConfigError("ui.port must be an integer between 0 and 65535");
24690
- }
24691
- out.port = record.port;
24692
- }
24693
- if (record.allowRemote !== undefined) {
24694
- if (typeof record.allowRemote !== "boolean")
24695
- throw new ConfigError("ui.allowRemote must be a boolean");
24696
- out.allowRemote = record.allowRemote;
24697
- }
24698
- if (record.publicOrigin !== undefined) {
24699
- if (typeof record.publicOrigin !== "string" || record.publicOrigin.trim().length === 0) {
24700
- throw new ConfigError("ui.publicOrigin must be a non-empty origin");
24701
- }
24702
- let origin;
24703
- try {
24704
- origin = new URL(record.publicOrigin);
24705
- } catch {
24706
- throw new ConfigError("ui.publicOrigin must be a valid http(s) origin");
24707
- }
24708
- if (!["http:", "https:"].includes(origin.protocol) || origin.pathname !== "/" || origin.search || origin.hash) {
24709
- throw new ConfigError("ui.publicOrigin must be a valid http(s) origin");
24710
- }
24711
- out.publicOrigin = origin.origin;
24712
- }
24713
- if (record.corsOrigins !== undefined) {
24714
- if (!Array.isArray(record.corsOrigins))
24715
- throw new ConfigError("ui.corsOrigins must be an array of origins");
24716
- const origins = [];
24717
- for (const value of record.corsOrigins) {
24718
- if (typeof value !== "string" || value.trim().length === 0) {
24719
- throw new ConfigError("ui.corsOrigins must contain non-empty origins");
24720
- }
24721
- let origin;
24722
- try {
24723
- origin = new URL(value);
24724
- } catch {
24725
- throw new ConfigError("ui.corsOrigins must contain valid http(s) origins");
24726
- }
24727
- if (!["http:", "https:"].includes(origin.protocol) || origin.pathname !== "/" || origin.search || origin.hash) {
24728
- throw new ConfigError("ui.corsOrigins must contain valid http(s) origins");
24729
- }
24730
- if (!origins.includes(origin.origin))
24731
- origins.push(origin.origin);
24732
- }
24733
- out.corsOrigins = origins;
24734
- }
24735
- if (record.tokenEnv !== undefined) {
24736
- if (typeof record.tokenEnv !== "string" || !UI_TOKEN_ENV_PATTERN.test(record.tokenEnv)) {
24737
- throw new ConfigError("ui.tokenEnv must match /^[A-Za-z_][A-Za-z0-9_]*$/");
24738
- }
24739
- out.tokenEnv = record.tokenEnv;
24740
- }
24741
- return out;
24742
- }
24743
24686
  function normalizeP2pConfig(raw) {
24744
24687
  const out = {};
24745
24688
  if (raw === undefined || raw === null) {
@@ -24911,10 +24854,10 @@ function resolveConfig(input) {
24911
24854
  const file = readOnly ? resolveConfigFileReadOnly(configPath, explicit, legacyPath) : !explicit && legacyPath ? migrateLegacyConfig(configPath, legacyPath) ?? readConfigFile(configPath, explicit) ?? {} : readConfigFile(configPath, explicit) ?? {};
24912
24855
  const defaultSearchPaths = ["."];
24913
24856
  const defaultWorkspacePath = cwd;
24914
- const defaultMemoryPath = join29(resolveAutoRAGHome(env), "memory.json");
24857
+ const defaultMemoryPath = join28(resolveAutoRAGHome(env), "memory.json");
24915
24858
  const flagSearchPaths = parseCsv(flagString(flags, "search-paths"));
24916
24859
  const envSearchPaths = parseCsv(envString(env, "AUTORAG_SEARCH_PATHS"));
24917
- const configOrigin = dirname19(resolve13(configPath));
24860
+ const configOrigin = dirname20(resolve13(configPath));
24918
24861
  const fileWorkspacePath = typeof file.workspacePath === "string" ? resolvePersistedPath(file.workspacePath, configOrigin) : undefined;
24919
24862
  const fileSearchPaths = file.searchPaths ? resolveSearchPaths(file.searchPaths, fileWorkspacePath ?? configOrigin) : undefined;
24920
24863
  const searchPaths = flagSearchPaths ?? envSearchPaths ?? fileSearchPaths ?? defaultSearchPaths;
@@ -24925,6 +24868,9 @@ function resolveConfig(input) {
24925
24868
  const envMemoryPath = envString(env, "AUTORAG_MEMORY_PATH");
24926
24869
  const fileMemoryPath = typeof file.memoryPath === "string" ? resolvePersistedPath(file.memoryPath, workspacePath) : undefined;
24927
24870
  const memoryPath = flagMemoryPath ?? envMemoryPath ?? fileMemoryPath ?? defaultMemoryPath;
24871
+ const flagLanguages = typeof flags.languages === "string" ? flags.languages : undefined;
24872
+ const envLanguages = env.AUTORAG_LANGUAGES;
24873
+ const languages = flagLanguages !== undefined ? normalizeConfigLanguages(flagLanguages) : envLanguages !== undefined ? normalizeConfigLanguages(envLanguages) : normalizeConfigLanguages(file.languages);
24928
24874
  const fileModel = modelReference(file.model, "model");
24929
24875
  const flagModelProvider = pickString(flags, env, "model-provider", "AUTORAG_MODEL_PROVIDER", undefined);
24930
24876
  const flagModelId = pickString(flags, env, "model-id", "AUTORAG_MODEL_ID", undefined);
@@ -24932,7 +24878,8 @@ function resolveConfig(input) {
24932
24878
  const config = {
24933
24879
  searchPaths,
24934
24880
  workspacePath,
24935
- memoryPath
24881
+ memoryPath,
24882
+ languages
24936
24883
  };
24937
24884
  if (model)
24938
24885
  config.model = model;
@@ -24965,8 +24912,6 @@ function resolveConfig(input) {
24965
24912
  }
24966
24913
  config.datasourceAccess = file.datasourceAccess;
24967
24914
  }
24968
- if (file.ui !== undefined)
24969
- config.ui = normalizeUiConfig(file.ui);
24970
24915
  config.p2p = normalizeP2pConfig(file.p2p);
24971
24916
  return config;
24972
24917
  }
@@ -25028,7 +24973,8 @@ function buildWebSearchAgentOption(raw) {
25028
24973
  }
25029
24974
  function buildAgentOptions(config) {
25030
24975
  const opts = {
25031
- searchPaths: config.searchPaths
24976
+ searchPaths: config.searchPaths,
24977
+ languages: config.languages
25032
24978
  };
25033
24979
  if (config.workspacePath)
25034
24980
  opts.workspacePath = config.workspacePath;
@@ -25271,7 +25217,7 @@ function writeConfigObject(path, config) {
25271
25217
  if (config === null || typeof config !== "object" || Array.isArray(config)) {
25272
25218
  throw new ConfigError("Config file must be a JSON object");
25273
25219
  }
25274
- mkdirSync19(dirname19(path), { recursive: true });
25220
+ mkdirSync20(dirname20(path), { recursive: true });
25275
25221
  const contents = `${JSON.stringify(config, null, 2)}
25276
25222
  `;
25277
25223
  const lock = acquireConfigWriteLock(path);
@@ -25284,11 +25230,12 @@ function writeConfigObject(path, config) {
25284
25230
  function writeDefaultConfig(path, partial, opts = {}) {
25285
25231
  const cwd = resolve13(opts.cwd ?? process.cwd());
25286
25232
  const workspacePath = resolvePersistedPath(partial.workspacePath ?? ".", cwd);
25287
- const memoryPath = partial.memoryPath === undefined ? join29(resolveAutoRAGHome(opts.env), "memory.json") : resolvePersistedPath(partial.memoryPath, workspacePath);
25233
+ const memoryPath = partial.memoryPath === undefined ? join28(resolveAutoRAGHome(opts.env), "memory.json") : resolvePersistedPath(partial.memoryPath, workspacePath);
25288
25234
  const full = {
25289
25235
  searchPaths: resolveSearchPaths(partial.searchPaths ?? ["."], cwd),
25290
25236
  workspacePath,
25291
- memoryPath
25237
+ memoryPath,
25238
+ languages: normalizeConfigLanguages(partial.languages)
25292
25239
  };
25293
25240
  if (partial.model)
25294
25241
  full.model = partial.model;
@@ -25301,13 +25248,11 @@ function writeDefaultConfig(path, partial, opts = {}) {
25301
25248
  full.excludeExactDuplicates = partial.excludeExactDuplicates ?? true;
25302
25249
  if (partial.parserOptions)
25303
25250
  full.parserOptions = partial.parserOptions;
25304
- if (partial.ui !== undefined)
25305
- full.ui = normalizeUiConfig(partial.ui);
25306
25251
  if (partial.p2p !== undefined)
25307
25252
  full.p2p = normalizeP2pConfig(partial.p2p);
25308
25253
  else
25309
25254
  full.p2p = { enabled: false };
25310
- mkdirSync19(dirname19(path), { recursive: true });
25255
+ mkdirSync20(dirname20(path), { recursive: true });
25311
25256
  const contents = `${JSON.stringify(full, null, 2)}
25312
25257
  `;
25313
25258
  const lock = acquireConfigWriteLock(path);
@@ -25319,7 +25264,7 @@ function writeDefaultConfig(path, partial, opts = {}) {
25319
25264
  replaceFileAtomically(path, contents, lock.assertOwned);
25320
25265
  else {
25321
25266
  lock.assertOwned();
25322
- writeFileSync17(path, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
25267
+ writeFileSync18(path, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
25323
25268
  }
25324
25269
  } catch (error) {
25325
25270
  if (!opts.force && isEexistError(error)) {
@@ -25330,12 +25275,13 @@ function writeDefaultConfig(path, partial, opts = {}) {
25330
25275
  lock.release();
25331
25276
  }
25332
25277
  }
25333
- var DEFAULT_CONFIG_FILENAME = "config.json", LEGACY_CONFIG_FILENAME = "autorag.config.json", CONFIG_LOCK_RETRY_MS = 10, CONFIG_LOCK_TIMEOUT_MS = 1e4, CONFIG_LOCK_STALE_MS = 30000, ConfigError, AGENT_MODEL_APIS, AGENT_MODEL_INPUTS, API_KEY_ENV_PATTERN2, POSITIVE_INT_FIELDS, EMBEDDER_ALLOWLIST, MINSYNC_ALLOWLIST, MINSYNC_LEGACY_IGNORED, UI_TOKEN_ENV_PATTERN, P2P_ALLOWLIST, P2P_QUOTA_ALLOWLIST;
25278
+ var DEFAULT_CONFIG_FILENAME = "config.json", LEGACY_CONFIG_FILENAME = "autorag.config.json", CONFIG_LOCK_RETRY_MS = 10, CONFIG_LOCK_TIMEOUT_MS = 1e4, CONFIG_LOCK_STALE_MS = 30000, ConfigError, AGENT_MODEL_APIS, AGENT_MODEL_INPUTS, API_KEY_ENV_PATTERN2, POSITIVE_INT_FIELDS, EMBEDDER_ALLOWLIST, MINSYNC_ALLOWLIST, MINSYNC_LEGACY_IGNORED, P2P_ALLOWLIST, P2P_QUOTA_ALLOWLIST;
25334
25279
  var init_config2 = __esm(() => {
25335
25280
  init_local_model();
25336
25281
  init_home();
25337
25282
  init_factory();
25338
25283
  init_file_lock();
25284
+ init_language();
25339
25285
  init_types3();
25340
25286
  init_home();
25341
25287
  ConfigError = class ConfigError extends Error {
@@ -25376,7 +25322,6 @@ var init_config2 = __esm(() => {
25376
25322
  "embedder"
25377
25323
  ]);
25378
25324
  MINSYNC_LEGACY_IGNORED = new Set(["binaryPath"]);
25379
- UI_TOKEN_ENV_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
25380
25325
  P2P_ALLOWLIST = new Set([
25381
25326
  "enabled",
25382
25327
  "port",
@@ -25395,7 +25340,7 @@ var init_config2 = __esm(() => {
25395
25340
  });
25396
25341
 
25397
25342
  // src/core.ts
25398
- import { randomUUID as randomUUID8 } from "node:crypto";
25343
+ import { randomUUID as randomUUID9 } from "node:crypto";
25399
25344
 
25400
25345
  class AutoRAGLite {
25401
25346
  config;
@@ -25448,7 +25393,7 @@ class AutoRAGLite {
25448
25393
  return recordStructuredResultsSession(sessionId, query, details, this.sessions, this.memory);
25449
25394
  }
25450
25395
  recordReport(query, details) {
25451
- return this.recordStructuredResultsSession(randomUUID8(), query, details);
25396
+ return this.recordStructuredResultsSession(randomUUID9(), query, details);
25452
25397
  }
25453
25398
  getResultRegistry(sessionId) {
25454
25399
  return this.sessions.get(sessionId)?.registry ?? new Map;
@@ -25741,18 +25686,18 @@ export {
25741
25686
  DiscrawlHybridMethod,
25742
25687
  DiscrawlSemanticMethod,
25743
25688
  DiscrawlSkill,
25744
- DocxParser,
25745
25689
  DupeyCliError,
25746
25690
  EMIT_AUTORAG_RESULTS_TOOL_NAME,
25747
25691
  ENGLISH_ONLY_EMBEDDING_MODELS,
25748
25692
  EmlParser,
25749
25693
  GitHubConnector,
25750
25694
  GitHubSkill,
25751
- HwpParser,
25752
25695
  ImageOcrParser,
25753
25696
  JIKJI_CRATE_NAME,
25754
25697
  JIKJI_INSTALL_TIMEOUT_MS,
25755
25698
  JikjiClient,
25699
+ KORDOC_EXTENSIONS,
25700
+ KordocParser,
25756
25701
  LarkClient,
25757
25702
  LarkSkill,
25758
25703
  LazykatokBm25Method,
@@ -25783,7 +25728,6 @@ export {
25783
25728
  ObsidianBm25Method,
25784
25729
  ObsidianSemanticMethod,
25785
25730
  ObsidianSkill,
25786
- OpendataloaderPdfParser,
25787
25731
  ParallelRetriever,
25788
25732
  ParseError,
25789
25733
  Parser,
@@ -25806,7 +25750,6 @@ export {
25806
25750
  TelecrawlSkill,
25807
25751
  WacrawlClient,
25808
25752
  WacrawlSkill,
25809
- XlsxParser,
25810
25753
  boundDiagnosticText2 as boundDiagnosticText,
25811
25754
  buildAgentOptions,
25812
25755
  buildDatasourceChunkSource,
@@ -25866,6 +25809,7 @@ export {
25866
25809
  renderMemoryContext,
25867
25810
  resolveConfig,
25868
25811
  resolveConfigReadOnly,
25812
+ resolveParserOptions,
25869
25813
  resolveReturnedPath,
25870
25814
  retrievalSurfaceFor,
25871
25815
  rewriteEmbedderConfig,