@autorag/librarian 2.0.0 → 2.1.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.
- package/README.md +45 -1
- package/dist/agent/search-documents.d.ts +1 -0
- package/dist/cli/config.d.ts +6 -0
- package/dist/cli/index.js +3423 -306
- package/dist/datasource/chunk-store.d.ts +67 -0
- package/dist/datasource/connector-skill.d.ts +107 -0
- package/dist/datasource/connector.d.ts +72 -0
- package/dist/datasource/http.d.ts +49 -0
- package/dist/datasource/index.d.ts +14 -0
- package/dist/datasource/skills/discord/connector.d.ts +30 -0
- package/dist/datasource/skills/discord/index.d.ts +2 -0
- package/dist/datasource/skills/discord/skill.d.ts +20 -0
- package/dist/datasource/skills/factory.d.ts +35 -0
- package/dist/datasource/skills/gdrive/connector.d.ts +30 -0
- package/dist/datasource/skills/gdrive/index.d.ts +3 -0
- package/dist/datasource/skills/gdrive/rclone-connector.d.ts +51 -0
- package/dist/datasource/skills/gdrive/skill.d.ts +24 -0
- package/dist/datasource/skills/github/connector.d.ts +28 -0
- package/dist/datasource/skills/github/index.d.ts +2 -0
- package/dist/datasource/skills/github/skill.d.ts +20 -0
- package/dist/datasource/skills/gmail/connector.d.ts +30 -0
- package/dist/datasource/skills/gmail/himalaya-connector.d.ts +40 -0
- package/dist/datasource/skills/gmail/index.d.ts +3 -0
- package/dist/datasource/skills/gmail/skill.d.ts +24 -0
- package/dist/datasource/skills/mail-export/connector.d.ts +21 -0
- package/dist/datasource/skills/mail-export/index.d.ts +2 -0
- package/dist/datasource/skills/mail-export/skill.d.ts +20 -0
- package/dist/datasource/skills/notion/connector.d.ts +26 -0
- package/dist/datasource/skills/notion/index.d.ts +2 -0
- package/dist/datasource/skills/notion/skill.d.ts +20 -0
- package/dist/datasource/skills/obsidian/connector.d.ts +22 -0
- package/dist/datasource/skills/obsidian/index.d.ts +2 -0
- package/dist/datasource/skills/obsidian/skill.d.ts +20 -0
- package/dist/datasource/skills/rss/connector.d.ts +28 -0
- package/dist/datasource/skills/rss/index.d.ts +2 -0
- package/dist/datasource/skills/rss/skill.d.ts +22 -0
- package/dist/datasource/skills/slack/connector.d.ts +28 -0
- package/dist/datasource/skills/slack/index.d.ts +2 -0
- package/dist/datasource/skills/slack/skill.d.ts +20 -0
- package/dist/datasource/skills/spotlight/connector.d.ts +59 -0
- package/dist/datasource/skills/spotlight/index.d.ts +2 -0
- package/dist/datasource/skills/spotlight/skill.d.ts +20 -0
- package/dist/index.js +3267 -152
- package/dist/parser/defaults.d.ts +2 -0
- package/dist/parser/hwp-markdown.d.ts +25 -0
- package/dist/parser/hwp.d.ts +7 -0
- package/dist/parser/index.d.ts +2 -1
- package/dist/parser/rhwp-adapter.d.ts +30 -0
- package/package.json +3 -2
- package/skills/autorag-setup/SKILL.md +52 -0
package/dist/index.js
CHANGED
|
@@ -2582,6 +2582,477 @@ var init_eml = __esm(() => {
|
|
|
2582
2582
|
};
|
|
2583
2583
|
});
|
|
2584
2584
|
|
|
2585
|
+
// src/parser/hwp-markdown.ts
|
|
2586
|
+
function renderHwpMarkdown(document) {
|
|
2587
|
+
const tables = new Map;
|
|
2588
|
+
for (const table of document.tables) {
|
|
2589
|
+
const key = `${table.sectionIndex}:${table.parentParagraphIndex}`;
|
|
2590
|
+
const group = tables.get(key) ?? [];
|
|
2591
|
+
group.push(table);
|
|
2592
|
+
tables.set(key, group);
|
|
2593
|
+
}
|
|
2594
|
+
for (const group of tables.values()) {
|
|
2595
|
+
group.sort((left, right) => left.controlIndex - right.controlIndex);
|
|
2596
|
+
}
|
|
2597
|
+
const blocks = [];
|
|
2598
|
+
for (const paragraph of document.paragraphs) {
|
|
2599
|
+
const text = cleanText(paragraph.text);
|
|
2600
|
+
if (hasRenderableText(text))
|
|
2601
|
+
blocks.push(text);
|
|
2602
|
+
for (const table of tables.get(`${paragraph.sectionIndex}:${paragraph.paragraphIndex}`) ?? []) {
|
|
2603
|
+
const rendered = renderTable(table);
|
|
2604
|
+
if (rendered.length > 0)
|
|
2605
|
+
blocks.push(rendered);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
return blocks.join(`
|
|
2609
|
+
|
|
2610
|
+
`);
|
|
2611
|
+
}
|
|
2612
|
+
function cleanText(text) {
|
|
2613
|
+
return text.replace(/[\u0000\uFFFC]/gu, "");
|
|
2614
|
+
}
|
|
2615
|
+
function hasRenderableText(text) {
|
|
2616
|
+
return text.trim().length > 0;
|
|
2617
|
+
}
|
|
2618
|
+
function renderTable(table) {
|
|
2619
|
+
const cells = table.cells.map((cell) => ({ ...cell, text: renderCell(cell) }));
|
|
2620
|
+
if (!cells.some((cell) => hasRenderableText(cell.text)))
|
|
2621
|
+
return "";
|
|
2622
|
+
if (cells.some((cell) => cell.rowSpan > 1 || cell.columnSpan > 1))
|
|
2623
|
+
return renderMergedTable(cells);
|
|
2624
|
+
const cellsByPosition = new Map(cells.map((cell) => [`${cell.row}:${cell.column}`, cell.text]));
|
|
2625
|
+
const header = Array.from({ length: table.columnCount }, (_, column) => `Column ${column + 1}`);
|
|
2626
|
+
const rows = Array.from({ length: table.rowCount }, (_, row) => Array.from({ length: table.columnCount }, (_2, column) => cellsByPosition.get(`${row}:${column}`) ?? ""));
|
|
2627
|
+
return [
|
|
2628
|
+
`| ${header.join(" | ")} |`,
|
|
2629
|
+
`| ${header.map(() => "---").join(" | ")} |`,
|
|
2630
|
+
...rows.map((row) => `| ${row.join(" | ")} |`)
|
|
2631
|
+
].join(`
|
|
2632
|
+
`);
|
|
2633
|
+
}
|
|
2634
|
+
function renderMergedTable(cells) {
|
|
2635
|
+
const rows = new Map;
|
|
2636
|
+
const coveredPositions = new Set;
|
|
2637
|
+
for (const cell of [...cells].sort((left, right) => left.row - right.row || left.column - right.column)) {
|
|
2638
|
+
const position = `${cell.row}:${cell.column}`;
|
|
2639
|
+
if (coveredPositions.has(position))
|
|
2640
|
+
continue;
|
|
2641
|
+
const row = rows.get(cell.row) ?? [];
|
|
2642
|
+
row.push(cell);
|
|
2643
|
+
rows.set(cell.row, row);
|
|
2644
|
+
for (let rowIndex = cell.row;rowIndex < cell.row + Math.max(1, cell.rowSpan); rowIndex += 1) {
|
|
2645
|
+
for (let columnIndex = cell.column;columnIndex < cell.column + Math.max(1, cell.columnSpan); columnIndex += 1) {
|
|
2646
|
+
if (rowIndex !== cell.row || columnIndex !== cell.column)
|
|
2647
|
+
coveredPositions.add(`${rowIndex}:${columnIndex}`);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
const renderedRows = [...rows.entries()].sort(([left], [right]) => left - right).map(([row, cellsInRow]) => {
|
|
2652
|
+
const text = cellsInRow.sort((left, right) => left.column - right.column).map((cell) => cell.text).filter(hasRenderableText).join(" | ");
|
|
2653
|
+
return hasRenderableText(text) ? `Row ${row + 1}: ${text}` : "";
|
|
2654
|
+
}).filter((row) => row.length > 0);
|
|
2655
|
+
return ["[Table]", ...renderedRows].join(`
|
|
2656
|
+
`);
|
|
2657
|
+
}
|
|
2658
|
+
function renderCell(cell) {
|
|
2659
|
+
return cell.paragraphs.map(cleanText).filter(hasRenderableText).join("<br>").replaceAll("\\", "\\\\").replace(/\r\n|\r|\n/gu, "<br>").replaceAll("|", "\\|");
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// src/parser/rhwp-adapter.ts
|
|
2663
|
+
import { readFile } from "node:fs/promises";
|
|
2664
|
+
import initRhwp, { HwpDocument } from "@rhwp/core";
|
|
2665
|
+
function createRhwpExtractor(runtime = defaultRhwpRuntime, overrides = {}) {
|
|
2666
|
+
const limits = resolveLimits(overrides);
|
|
2667
|
+
const initialize = runtime === defaultRhwpRuntime ? initializeDefaultRhwpRuntime : createRuntimeInitializer(runtime);
|
|
2668
|
+
return async (bytes) => {
|
|
2669
|
+
await initialize();
|
|
2670
|
+
const document = runtime.open(bytes);
|
|
2671
|
+
try {
|
|
2672
|
+
return extractDocument(document, limits);
|
|
2673
|
+
} finally {
|
|
2674
|
+
document.free();
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
function createRuntimeInitializer(runtime) {
|
|
2679
|
+
let initialization;
|
|
2680
|
+
return () => {
|
|
2681
|
+
if (initialization === undefined) {
|
|
2682
|
+
const attempt = runtime.initialize();
|
|
2683
|
+
initialization = attempt.catch((error) => {
|
|
2684
|
+
initialization = undefined;
|
|
2685
|
+
throw error;
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
return initialization;
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
function extractDocument(document, limits) {
|
|
2692
|
+
const paragraphs = [];
|
|
2693
|
+
const paragraphCounts = [];
|
|
2694
|
+
const counters = { characters: 0, tables: 0, cells: 0, cellParagraphs: 0 };
|
|
2695
|
+
const sectionCount = readCount(document.getSectionCount(), "section count");
|
|
2696
|
+
checkBudget("maxSections", sectionCount, limits);
|
|
2697
|
+
for (let sectionIndex = 0;sectionIndex < sectionCount; sectionIndex += 1) {
|
|
2698
|
+
const paragraphCount = readCount(document.getParagraphCount(sectionIndex), `paragraph count for section ${sectionIndex}`);
|
|
2699
|
+
paragraphCounts.push(paragraphCount);
|
|
2700
|
+
checkBudget("maxBodyParagraphs", paragraphs.length + paragraphCount, limits);
|
|
2701
|
+
for (let paragraphIndex = 0;paragraphIndex < paragraphCount; paragraphIndex += 1) {
|
|
2702
|
+
const length = readCount(document.getParagraphLength(sectionIndex, paragraphIndex), `paragraph length at ${sectionIndex}:${paragraphIndex}`);
|
|
2703
|
+
const text = readText(document.getTextRange(sectionIndex, paragraphIndex, 0, length), `paragraph text at ${sectionIndex}:${paragraphIndex}`);
|
|
2704
|
+
counters.characters += [...text].length;
|
|
2705
|
+
checkBudget("maxCharacters", counters.characters, limits);
|
|
2706
|
+
paragraphs.push({
|
|
2707
|
+
sectionIndex,
|
|
2708
|
+
paragraphIndex,
|
|
2709
|
+
text
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
return { paragraphs, tables: extractTables(document, limits, counters, paragraphCounts) };
|
|
2714
|
+
}
|
|
2715
|
+
function extractTables(document, limits, counters, paragraphCounts) {
|
|
2716
|
+
const tables = [];
|
|
2717
|
+
const seen = new Set;
|
|
2718
|
+
const pageCount = readCount(document.pageCount(), "page count");
|
|
2719
|
+
for (let pageIndex = 0;pageIndex < pageCount; pageIndex += 1) {
|
|
2720
|
+
const layout = readJsonRecord(document.getPageControlLayout(pageIndex), `page ${pageIndex} control layout`);
|
|
2721
|
+
const controls = readArray(layout.controls, `controls for page ${pageIndex}`);
|
|
2722
|
+
for (let layoutIndex = 0;layoutIndex < controls.length; layoutIndex += 1) {
|
|
2723
|
+
const control = readRecord(controls[layoutIndex], `control ${layoutIndex} on page ${pageIndex}`);
|
|
2724
|
+
if (typeof control.type !== "string") {
|
|
2725
|
+
throw new TypeError(`rhwp returned an invalid control type at page ${pageIndex}:${layoutIndex}`);
|
|
2726
|
+
}
|
|
2727
|
+
if (control.type !== "table")
|
|
2728
|
+
continue;
|
|
2729
|
+
const label = `table at page ${pageIndex}:${layoutIndex}`;
|
|
2730
|
+
const coordinates = readTableCoordinates(control, label, paragraphCounts, limits);
|
|
2731
|
+
if (coordinates === undefined)
|
|
2732
|
+
continue;
|
|
2733
|
+
const key = `${coordinates.sectionIndex}:${coordinates.parentParagraphIndex}:${coordinates.controlIndex}`;
|
|
2734
|
+
if (seen.has(key))
|
|
2735
|
+
continue;
|
|
2736
|
+
seen.add(key);
|
|
2737
|
+
counters.tables += 1;
|
|
2738
|
+
checkBudget("maxTables", counters.tables, limits);
|
|
2739
|
+
tables.push(extractTable(document, coordinates, limits, counters));
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
return tables;
|
|
2743
|
+
}
|
|
2744
|
+
function readTableCoordinates(control, label, paragraphCounts, limits) {
|
|
2745
|
+
const hasSectionIndex = Object.hasOwn(control, "secIdx");
|
|
2746
|
+
const hasParagraphIndex = Object.hasOwn(control, "paraIdx");
|
|
2747
|
+
const hasControlIndex = Object.hasOwn(control, "controlIdx");
|
|
2748
|
+
const coordinateCount = Number(hasSectionIndex) + Number(hasParagraphIndex) + Number(hasControlIndex);
|
|
2749
|
+
if (coordinateCount === 0) {
|
|
2750
|
+
validateDeferredNestedTable(control, label, limits);
|
|
2751
|
+
return;
|
|
2752
|
+
}
|
|
2753
|
+
if (coordinateCount !== 3) {
|
|
2754
|
+
throw new TypeError(`rhwp returned a partial coordinate set for ${label}`);
|
|
2755
|
+
}
|
|
2756
|
+
if (Object.hasOwn(control, "cellPath") || Object.hasOwn(control, "parentParaIdx")) {
|
|
2757
|
+
throw new TypeError(`rhwp returned conflicting top-level and nested context for ${label}`);
|
|
2758
|
+
}
|
|
2759
|
+
const coordinates = {
|
|
2760
|
+
sectionIndex: readCount(control.secIdx, `secIdx for ${label}`),
|
|
2761
|
+
parentParagraphIndex: readCount(control.paraIdx, `paraIdx for ${label}`),
|
|
2762
|
+
controlIndex: readCount(control.controlIdx, `controlIdx for ${label}`)
|
|
2763
|
+
};
|
|
2764
|
+
const paragraphCount = paragraphCounts[coordinates.sectionIndex];
|
|
2765
|
+
if (paragraphCount === undefined) {
|
|
2766
|
+
throw new TypeError(`rhwp returned a section coordinate outside the announced document range for ${label}`);
|
|
2767
|
+
}
|
|
2768
|
+
if (coordinates.parentParagraphIndex >= paragraphCount) {
|
|
2769
|
+
throw new TypeError(`rhwp returned a parent paragraph coordinate outside the announced section range for ${label}`);
|
|
2770
|
+
}
|
|
2771
|
+
return coordinates;
|
|
2772
|
+
}
|
|
2773
|
+
function validateDeferredNestedTable(control, label, limits) {
|
|
2774
|
+
if (!Object.hasOwn(control, "cellPath") && !Object.hasOwn(control, "parentParaIdx")) {
|
|
2775
|
+
validateDeferredNestedLayout(control, label, limits);
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
readCount(control.parentParaIdx, `parentParaIdx for deferred nested ${label}`);
|
|
2779
|
+
const cellPath = readArray(control.cellPath, `cellPath for deferred nested ${label}`);
|
|
2780
|
+
if (cellPath.length === 0) {
|
|
2781
|
+
throw new TypeError(`rhwp returned an empty cellPath for deferred nested ${label}`);
|
|
2782
|
+
}
|
|
2783
|
+
checkBudget("maxCells", cellPath.length, limits);
|
|
2784
|
+
for (let pathIndex = 0;pathIndex < cellPath.length; pathIndex += 1) {
|
|
2785
|
+
const entry = readRecord(cellPath[pathIndex], `cellPath entry ${pathIndex} for deferred nested ${label}`);
|
|
2786
|
+
readCount(entry.controlIndex, `controlIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
|
|
2787
|
+
readCount(entry.cellIndex, `cellIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
|
|
2788
|
+
readCount(entry.cellParaIndex, `cellParaIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
function validateDeferredNestedLayout(control, label, limits) {
|
|
2792
|
+
readFiniteNumber(control.x, `x for deferred nested ${label}`);
|
|
2793
|
+
readFiniteNumber(control.y, `y for deferred nested ${label}`);
|
|
2794
|
+
readPositiveFiniteNumber(control.w, `w for deferred nested ${label}`);
|
|
2795
|
+
readPositiveFiniteNumber(control.h, `h for deferred nested ${label}`);
|
|
2796
|
+
const rowCount = readPositiveCount(control.rowCount, `rowCount for deferred nested ${label}`);
|
|
2797
|
+
const columnCount = readPositiveCount(control.colCount, `colCount for deferred nested ${label}`);
|
|
2798
|
+
const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
|
|
2799
|
+
readCount(control.plane, `plane for deferred nested ${label}`);
|
|
2800
|
+
readSafeInteger(control.zOrder, `zOrder for deferred nested ${label}`);
|
|
2801
|
+
readCount(control.stableIndex, `stableIndex for deferred nested ${label}`);
|
|
2802
|
+
const cells = readArray(control.cells, `cells for deferred nested ${label}`);
|
|
2803
|
+
if (cells.length === 0) {
|
|
2804
|
+
throw new TypeError(`rhwp returned empty cells for deferred nested ${label}`);
|
|
2805
|
+
}
|
|
2806
|
+
checkBudget("maxCells", cells.length, limits);
|
|
2807
|
+
if (cells.length > logicalCellCount) {
|
|
2808
|
+
throw new TypeError(`rhwp returned too many cells for deferred nested ${label}`);
|
|
2809
|
+
}
|
|
2810
|
+
const anchors = new Set;
|
|
2811
|
+
const cellIndexes = new Set;
|
|
2812
|
+
const coveredCoordinates = new Set;
|
|
2813
|
+
for (let cellIndex = 0;cellIndex < cells.length; cellIndex += 1) {
|
|
2814
|
+
const cell = readRecord(cells[cellIndex], `cell ${cellIndex} for deferred nested ${label}`);
|
|
2815
|
+
readFiniteNumber(cell.x, `x for cell ${cellIndex} in deferred nested ${label}`);
|
|
2816
|
+
readFiniteNumber(cell.y, `y for cell ${cellIndex} in deferred nested ${label}`);
|
|
2817
|
+
readPositiveFiniteNumber(cell.w, `w for cell ${cellIndex} in deferred nested ${label}`);
|
|
2818
|
+
readPositiveFiniteNumber(cell.h, `h for cell ${cellIndex} in deferred nested ${label}`);
|
|
2819
|
+
const row = readCount(cell.row, `row for cell ${cellIndex} in deferred nested ${label}`);
|
|
2820
|
+
const column = readCount(cell.col, `col for cell ${cellIndex} in deferred nested ${label}`);
|
|
2821
|
+
const rowSpan = readPositiveCount(cell.rowSpan, `rowSpan for cell ${cellIndex} in deferred nested ${label}`);
|
|
2822
|
+
const columnSpan = readPositiveCount(cell.colSpan, `colSpan for cell ${cellIndex} in deferred nested ${label}`);
|
|
2823
|
+
const runtimeCellIndex = readCount(cell.cellIdx, `cellIdx for cell ${cellIndex} in deferred nested ${label}`);
|
|
2824
|
+
if (row >= rowCount || rowSpan > rowCount - row) {
|
|
2825
|
+
throw new TypeError(`rhwp returned an out-of-range row span for cell ${cellIndex} in deferred nested ${label}`);
|
|
2826
|
+
}
|
|
2827
|
+
if (column >= columnCount || columnSpan > columnCount - column) {
|
|
2828
|
+
throw new TypeError(`rhwp returned an out-of-range col span for cell ${cellIndex} in deferred nested ${label}`);
|
|
2829
|
+
}
|
|
2830
|
+
if (cellIndexes.has(runtimeCellIndex)) {
|
|
2831
|
+
throw new TypeError(`rhwp returned a duplicate cellIdx for deferred nested ${label}`);
|
|
2832
|
+
}
|
|
2833
|
+
cellIndexes.add(runtimeCellIndex);
|
|
2834
|
+
const anchor = `${row}:${column}`;
|
|
2835
|
+
if (anchors.has(anchor)) {
|
|
2836
|
+
throw new TypeError(`rhwp returned a duplicate cell anchor for deferred nested ${label}`);
|
|
2837
|
+
}
|
|
2838
|
+
anchors.add(anchor);
|
|
2839
|
+
for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
|
|
2840
|
+
for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
|
|
2841
|
+
const coordinate = `${coveredRow}:${coveredColumn}`;
|
|
2842
|
+
if (coveredCoordinates.has(coordinate)) {
|
|
2843
|
+
throw new TypeError(`rhwp returned overlapping cell spans for deferred nested ${label}`);
|
|
2844
|
+
}
|
|
2845
|
+
coveredCoordinates.add(coordinate);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
function extractTable(document, coordinates, limits, counters) {
|
|
2851
|
+
const { sectionIndex, parentParagraphIndex, controlIndex } = coordinates;
|
|
2852
|
+
const key = `${sectionIndex}:${parentParagraphIndex}:${controlIndex}`;
|
|
2853
|
+
const dimensions = readJsonRecord(document.getTableDimensions(sectionIndex, parentParagraphIndex, controlIndex), `table dimensions at ${key}`);
|
|
2854
|
+
const rowCount = readPositiveCount(dimensions.rowCount, `rowCount for table ${key}`);
|
|
2855
|
+
const columnCount = readPositiveCount(dimensions.colCount, `colCount for table ${key}`);
|
|
2856
|
+
const cellCount = readCount(dimensions.cellCount, `cellCount for table ${key}`);
|
|
2857
|
+
const logicalCellCount = chargeLogicalCells(rowCount, columnCount, counters, limits);
|
|
2858
|
+
if (cellCount === 0 || cellCount > logicalCellCount) {
|
|
2859
|
+
throw new TypeError(`rhwp returned an invalid cellCount for table ${key}; expected 1..${logicalCellCount}, received ${cellCount}`);
|
|
2860
|
+
}
|
|
2861
|
+
const cellGeometry = [];
|
|
2862
|
+
const anchors = new Set;
|
|
2863
|
+
const coveredCoordinates = new Set;
|
|
2864
|
+
for (let cellIndex = 0;cellIndex < cellCount; cellIndex += 1) {
|
|
2865
|
+
const cellInfo = readJsonRecord(document.getCellInfo(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell metadata at ${key}:${cellIndex}`);
|
|
2866
|
+
const row = readCount(cellInfo.row, `row for cell ${key}:${cellIndex}`);
|
|
2867
|
+
const column = readCount(cellInfo.col, `col for cell ${key}:${cellIndex}`);
|
|
2868
|
+
const rowSpan = readPositiveCount(cellInfo.rowSpan, `rowSpan for cell ${key}:${cellIndex}`);
|
|
2869
|
+
const columnSpan = readPositiveCount(cellInfo.colSpan, `colSpan for cell ${key}:${cellIndex}`);
|
|
2870
|
+
if (row >= rowCount) {
|
|
2871
|
+
throw new TypeError(`rhwp returned a row outside the table range for cell ${key}:${cellIndex}`);
|
|
2872
|
+
}
|
|
2873
|
+
if (column >= columnCount) {
|
|
2874
|
+
throw new TypeError(`rhwp returned a col outside the table range for cell ${key}:${cellIndex}`);
|
|
2875
|
+
}
|
|
2876
|
+
if (rowSpan > rowCount - row) {
|
|
2877
|
+
throw new TypeError(`rhwp returned a rowSpan outside the table range for cell ${key}:${cellIndex}`);
|
|
2878
|
+
}
|
|
2879
|
+
if (columnSpan > columnCount - column) {
|
|
2880
|
+
throw new TypeError(`rhwp returned a colSpan outside the table range for cell ${key}:${cellIndex}`);
|
|
2881
|
+
}
|
|
2882
|
+
const anchor = row * columnCount + column;
|
|
2883
|
+
if (anchors.has(anchor)) {
|
|
2884
|
+
throw new TypeError(`rhwp returned a duplicate cell anchor for table ${key} at ${row}:${column}`);
|
|
2885
|
+
}
|
|
2886
|
+
anchors.add(anchor);
|
|
2887
|
+
for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
|
|
2888
|
+
for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
|
|
2889
|
+
const coordinate = coveredRow * columnCount + coveredColumn;
|
|
2890
|
+
if (coveredCoordinates.has(coordinate)) {
|
|
2891
|
+
throw new TypeError(`rhwp returned overlapping cell spans for table ${key} at ${coveredRow}:${coveredColumn}`);
|
|
2892
|
+
}
|
|
2893
|
+
coveredCoordinates.add(coordinate);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
cellGeometry.push({ row, column, rowSpan, columnSpan });
|
|
2897
|
+
}
|
|
2898
|
+
if (coveredCoordinates.size !== logicalCellCount) {
|
|
2899
|
+
throw new TypeError(`rhwp returned cellCount ${cellCount} that covers ${coveredCoordinates.size} of ${logicalCellCount} logical grid coordinates for table ${key}`);
|
|
2900
|
+
}
|
|
2901
|
+
const cells = [];
|
|
2902
|
+
for (let cellIndex = 0;cellIndex < cellGeometry.length; cellIndex += 1) {
|
|
2903
|
+
const geometry = cellGeometry[cellIndex];
|
|
2904
|
+
if (geometry === undefined)
|
|
2905
|
+
throw new TypeError(`rhwp omitted cell geometry at ${key}:${cellIndex}`);
|
|
2906
|
+
const paragraphCount = readCount(document.getCellParagraphCount(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell paragraph count at ${key}:${cellIndex}`);
|
|
2907
|
+
counters.cellParagraphs += paragraphCount;
|
|
2908
|
+
checkBudget("maxCellParagraphs", counters.cellParagraphs, limits);
|
|
2909
|
+
const paragraphs = [];
|
|
2910
|
+
for (let cellParagraphIndex = 0;cellParagraphIndex < paragraphCount; cellParagraphIndex += 1) {
|
|
2911
|
+
const length = readCount(document.getCellParagraphLength(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex), `cell paragraph length at ${key}:${cellIndex}:${cellParagraphIndex}`);
|
|
2912
|
+
const text = readText(document.getTextInCell(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex, 0, length), `cell paragraph text at ${key}:${cellIndex}:${cellParagraphIndex}`);
|
|
2913
|
+
counters.characters += [...text].length;
|
|
2914
|
+
checkBudget("maxCharacters", counters.characters, limits);
|
|
2915
|
+
paragraphs.push(text);
|
|
2916
|
+
}
|
|
2917
|
+
cells.push({
|
|
2918
|
+
...geometry,
|
|
2919
|
+
paragraphs
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
return {
|
|
2923
|
+
sectionIndex,
|
|
2924
|
+
parentParagraphIndex,
|
|
2925
|
+
controlIndex,
|
|
2926
|
+
rowCount,
|
|
2927
|
+
columnCount,
|
|
2928
|
+
cells
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
function chargeLogicalCells(rowCount, columnCount, counters, limits) {
|
|
2932
|
+
const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
|
|
2933
|
+
if (logicalCellCount > limits.maxCells - counters.cells) {
|
|
2934
|
+
throw new HwpExtractionBudgetError("maxCells", counters.cells + logicalCellCount, limits.maxCells);
|
|
2935
|
+
}
|
|
2936
|
+
counters.cells += logicalCellCount;
|
|
2937
|
+
return logicalCellCount;
|
|
2938
|
+
}
|
|
2939
|
+
function readLogicalCellCount(rowCount, columnCount, limits) {
|
|
2940
|
+
if (rowCount > Math.floor(limits.maxCells / columnCount)) {
|
|
2941
|
+
throw new HwpExtractionBudgetError("maxCells", rowCount * columnCount, limits.maxCells);
|
|
2942
|
+
}
|
|
2943
|
+
return rowCount * columnCount;
|
|
2944
|
+
}
|
|
2945
|
+
function resolveLimits(overrides) {
|
|
2946
|
+
const limits = { ...DEFAULT_LIMITS };
|
|
2947
|
+
for (const name of Object.keys(DEFAULT_LIMITS)) {
|
|
2948
|
+
const value = overrides[name];
|
|
2949
|
+
if (value === undefined)
|
|
2950
|
+
continue;
|
|
2951
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
2952
|
+
throw new TypeError(`${name} must be a positive finite safe integer`);
|
|
2953
|
+
}
|
|
2954
|
+
limits[name] = value;
|
|
2955
|
+
}
|
|
2956
|
+
return limits;
|
|
2957
|
+
}
|
|
2958
|
+
function checkBudget(name, actual, limits) {
|
|
2959
|
+
if (actual > limits[name])
|
|
2960
|
+
throw new HwpExtractionBudgetError(name, actual, limits[name]);
|
|
2961
|
+
}
|
|
2962
|
+
function readCount(value, label) {
|
|
2963
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
2964
|
+
throw new TypeError(`rhwp returned an invalid ${label}; expected a non-negative safe integer`);
|
|
2965
|
+
}
|
|
2966
|
+
return value;
|
|
2967
|
+
}
|
|
2968
|
+
function readPositiveCount(value, label) {
|
|
2969
|
+
const count = readCount(value, label);
|
|
2970
|
+
if (count === 0) {
|
|
2971
|
+
throw new TypeError(`rhwp returned an invalid ${label}; expected a positive safe integer`);
|
|
2972
|
+
}
|
|
2973
|
+
return count;
|
|
2974
|
+
}
|
|
2975
|
+
function readSafeInteger(value, label) {
|
|
2976
|
+
if (!Number.isSafeInteger(value)) {
|
|
2977
|
+
throw new TypeError(`rhwp returned an invalid ${label}; expected a safe integer`);
|
|
2978
|
+
}
|
|
2979
|
+
return value;
|
|
2980
|
+
}
|
|
2981
|
+
function readFiniteNumber(value, label) {
|
|
2982
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2983
|
+
throw new TypeError(`rhwp returned an invalid ${label}; expected a finite number`);
|
|
2984
|
+
}
|
|
2985
|
+
return value;
|
|
2986
|
+
}
|
|
2987
|
+
function readPositiveFiniteNumber(value, label) {
|
|
2988
|
+
const number = readFiniteNumber(value, label);
|
|
2989
|
+
if (number <= 0) {
|
|
2990
|
+
throw new TypeError(`rhwp returned an invalid ${label}; expected a positive finite number`);
|
|
2991
|
+
}
|
|
2992
|
+
return number;
|
|
2993
|
+
}
|
|
2994
|
+
function readText(value, label) {
|
|
2995
|
+
if (typeof value !== "string")
|
|
2996
|
+
throw new TypeError(`rhwp returned invalid ${label}; expected a string`);
|
|
2997
|
+
return value;
|
|
2998
|
+
}
|
|
2999
|
+
function readJsonRecord(value, label) {
|
|
3000
|
+
const text = readText(value, label);
|
|
3001
|
+
let parsed;
|
|
3002
|
+
try {
|
|
3003
|
+
parsed = JSON.parse(text);
|
|
3004
|
+
} catch (cause) {
|
|
3005
|
+
throw new TypeError(`rhwp returned malformed JSON for ${label}`, { cause });
|
|
3006
|
+
}
|
|
3007
|
+
return readRecord(parsed, label);
|
|
3008
|
+
}
|
|
3009
|
+
function readRecord(value, label) {
|
|
3010
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3011
|
+
throw new TypeError(`rhwp returned invalid ${label}; expected an object`);
|
|
3012
|
+
}
|
|
3013
|
+
return value;
|
|
3014
|
+
}
|
|
3015
|
+
function readArray(value, label) {
|
|
3016
|
+
if (!Array.isArray(value))
|
|
3017
|
+
throw new TypeError(`rhwp returned invalid ${label}; expected an array`);
|
|
3018
|
+
return value;
|
|
3019
|
+
}
|
|
3020
|
+
var DEFAULT_LIMITS, HwpExtractionBudgetError, defaultRhwpRuntime, initializeDefaultRhwpRuntime;
|
|
3021
|
+
var init_rhwp_adapter = __esm(() => {
|
|
3022
|
+
DEFAULT_LIMITS = {
|
|
3023
|
+
maxSections: 256,
|
|
3024
|
+
maxBodyParagraphs: 200000,
|
|
3025
|
+
maxTables: 20000,
|
|
3026
|
+
maxCells: 500000,
|
|
3027
|
+
maxCellParagraphs: 1e6,
|
|
3028
|
+
maxCharacters: 20000000
|
|
3029
|
+
};
|
|
3030
|
+
HwpExtractionBudgetError = class HwpExtractionBudgetError extends Error {
|
|
3031
|
+
code = "HWP_EXTRACTION_BUDGET_EXCEEDED";
|
|
3032
|
+
limit;
|
|
3033
|
+
actual;
|
|
3034
|
+
maximum;
|
|
3035
|
+
constructor(limit, actual, maximum) {
|
|
3036
|
+
super(`HWP extraction exceeded ${limit} budget of ${maximum} (received ${actual})`);
|
|
3037
|
+
this.name = "HwpExtractionBudgetError";
|
|
3038
|
+
this.limit = limit;
|
|
3039
|
+
this.actual = actual;
|
|
3040
|
+
this.maximum = maximum;
|
|
3041
|
+
}
|
|
3042
|
+
};
|
|
3043
|
+
defaultRhwpRuntime = {
|
|
3044
|
+
async initialize() {
|
|
3045
|
+
const rhwpModuleUrl = import.meta.resolve("@rhwp/core");
|
|
3046
|
+
const wasmBytes = await readFile(new URL("./rhwp_bg.wasm", rhwpModuleUrl));
|
|
3047
|
+
await initRhwp({ module_or_path: wasmBytes });
|
|
3048
|
+
},
|
|
3049
|
+
open(bytes) {
|
|
3050
|
+
return new HwpDocument(bytes);
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
initializeDefaultRhwpRuntime = createRuntimeInitializer(defaultRhwpRuntime);
|
|
3054
|
+
});
|
|
3055
|
+
|
|
2585
3056
|
// src/parser/xml-text.ts
|
|
2586
3057
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
2587
3058
|
import { XMLParser } from "fast-xml-parser";
|
|
@@ -2687,15 +3158,25 @@ import { extname as extname2 } from "node:path";
|
|
|
2687
3158
|
var HwpParser;
|
|
2688
3159
|
var init_hwp = __esm(() => {
|
|
2689
3160
|
init_errors();
|
|
3161
|
+
init_rhwp_adapter();
|
|
2690
3162
|
init_text();
|
|
2691
3163
|
init_xml_text();
|
|
2692
3164
|
HwpParser = class HwpParser extends Parser {
|
|
2693
3165
|
name = "hwp";
|
|
2694
3166
|
extensions = [".hwpx", ".hwp"];
|
|
3167
|
+
extractor;
|
|
3168
|
+
constructor(options = {}) {
|
|
3169
|
+
super();
|
|
3170
|
+
this.extractor = options.extractor ?? createRhwpExtractor(undefined, options.limits);
|
|
3171
|
+
}
|
|
2695
3172
|
async parse(input) {
|
|
2696
3173
|
try {
|
|
2697
3174
|
if (extname2(input.virtualPath).toLowerCase() === ".hwp") {
|
|
2698
|
-
|
|
3175
|
+
const document = await this.extractor(input.bytes);
|
|
3176
|
+
return {
|
|
3177
|
+
markdown: normalizeMarkdown(renderHwpMarkdown(document)),
|
|
3178
|
+
metadata: { parser: this.name, format: "hwp5" }
|
|
3179
|
+
};
|
|
2699
3180
|
}
|
|
2700
3181
|
const chunks = await readZipXmlText(input.bytes, /^(?:Contents|content)\/.*\.xml$/i);
|
|
2701
3182
|
return {
|
|
@@ -2874,7 +3355,7 @@ var init_office = __esm(() => {
|
|
|
2874
3355
|
});
|
|
2875
3356
|
|
|
2876
3357
|
// src/parser/opendataloader-pdf.ts
|
|
2877
|
-
import { mkdtemp as mkdtemp2, readFile, rm, writeFile } from "node:fs/promises";
|
|
3358
|
+
import { mkdtemp as mkdtemp2, readFile as readFile2, rm, writeFile } from "node:fs/promises";
|
|
2878
3359
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
2879
3360
|
import { basename as basename4, join as join12, parse as parse2 } from "node:path";
|
|
2880
3361
|
import { convert } from "@opendataloader/pdf";
|
|
@@ -2917,7 +3398,7 @@ var init_opendataloader_pdf = __esm(() => {
|
|
|
2917
3398
|
} : {}
|
|
2918
3399
|
});
|
|
2919
3400
|
return {
|
|
2920
|
-
markdown: normalizeMarkdown(await
|
|
3401
|
+
markdown: normalizeMarkdown(await readFile2(markdownOutputPath(tempRoot, inputPath), "utf8")),
|
|
2921
3402
|
metadata: { parser: this.name }
|
|
2922
3403
|
};
|
|
2923
3404
|
} catch (cause) {
|
|
@@ -2982,7 +3463,7 @@ function createDefaultParserRegistry(options = {}) {
|
|
|
2982
3463
|
new DocxParser,
|
|
2983
3464
|
new PptxParser,
|
|
2984
3465
|
new XlsxParser,
|
|
2985
|
-
new HwpParser,
|
|
3466
|
+
new HwpParser(options.hwp),
|
|
2986
3467
|
new EmlParser
|
|
2987
3468
|
];
|
|
2988
3469
|
if (options.ocr?.enabled)
|
|
@@ -3002,7 +3483,7 @@ var init_defaults = __esm(() => {
|
|
|
3002
3483
|
// src/mirror/sync.ts
|
|
3003
3484
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
3004
3485
|
import { existsSync as existsSync10, mkdirSync as mkdirSync6, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3005
|
-
import { opendir, readFile as
|
|
3486
|
+
import { opendir, readFile as readFile3, stat } from "node:fs/promises";
|
|
3006
3487
|
import { dirname as dirname5, extname as extname5, resolve as resolve5 } from "node:path";
|
|
3007
3488
|
async function syncParsedMirrors(options) {
|
|
3008
3489
|
const registry = options.registry ?? createDefaultParserRegistry(options.parserOptions);
|
|
@@ -3055,7 +3536,7 @@ async function syncParsedMirrors(options) {
|
|
|
3055
3536
|
if (!unchanged) {
|
|
3056
3537
|
let parsed;
|
|
3057
3538
|
try {
|
|
3058
|
-
const bytes = await
|
|
3539
|
+
const bytes = await readFile3(entry.sourcePath);
|
|
3059
3540
|
parsed = await parser2.parse({ virtualPath: entry.virtualPath, sourcePath: entry.sourcePath, bytes });
|
|
3060
3541
|
} catch (error) {
|
|
3061
3542
|
if (!(error instanceof ParseError))
|
|
@@ -6335,7 +6816,8 @@ function recordStructuredResultsSession(sessionId, query, details, sessions, mem
|
|
|
6335
6816
|
summary: result.summary,
|
|
6336
6817
|
evidence: result.evidence.map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
|
|
6337
6818
|
confidence: confidenceFrom(result.confidence),
|
|
6338
|
-
feedbackId: `${sessionId}:${result.number}
|
|
6819
|
+
feedbackId: `${sessionId}:${result.number}`,
|
|
6820
|
+
source: registry.get(result.number)?.source
|
|
6339
6821
|
}));
|
|
6340
6822
|
const answer = details.answer;
|
|
6341
6823
|
const diagnostics = [];
|
|
@@ -8386,155 +8868,2740 @@ var init_agent = __esm(() => {
|
|
|
8386
8868
|
TIMESTAMP_TEXT_PATTERN = /^(\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?)(?:\s*;\s*.+)?$/;
|
|
8387
8869
|
});
|
|
8388
8870
|
|
|
8389
|
-
// src/
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
function
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8871
|
+
// src/datasource/connector.ts
|
|
8872
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
8873
|
+
function connectorFailureToDiagnosticCode(reason) {
|
|
8874
|
+
switch (reason) {
|
|
8875
|
+
case "auth":
|
|
8876
|
+
return "datasource-auth-error";
|
|
8877
|
+
case "permission":
|
|
8878
|
+
return "datasource-permission-denied";
|
|
8879
|
+
case "rate-limited":
|
|
8880
|
+
return "datasource-rate-limited";
|
|
8881
|
+
case "unavailable":
|
|
8882
|
+
case "not-configured":
|
|
8883
|
+
return "datasource-unavailable";
|
|
8884
|
+
case "empty":
|
|
8885
|
+
return "datasource-empty";
|
|
8886
|
+
case "api-error":
|
|
8887
|
+
case "invalid-data":
|
|
8888
|
+
return "datasource-index-failed";
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
function boundDiagnosticText(value) {
|
|
8892
|
+
const trimmed = value.trim();
|
|
8893
|
+
return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
|
|
8894
|
+
}
|
|
8895
|
+
function sanitizeIdSegment(value) {
|
|
8896
|
+
const normalized = value.replace(/[^\p{L}\p{N}._-]+/gu, "-");
|
|
8897
|
+
let start = 0;
|
|
8898
|
+
while (start < normalized.length && (normalized[start] === "-" || normalized[start] === "."))
|
|
8899
|
+
start += 1;
|
|
8900
|
+
let end = normalized.length;
|
|
8901
|
+
while (end > start && (normalized[end - 1] === "-" || normalized[end - 1] === "."))
|
|
8902
|
+
end -= 1;
|
|
8903
|
+
const cleaned = normalized.slice(start, end);
|
|
8904
|
+
if (cleaned === value && cleaned.length > 0 && cleaned.length <= 80)
|
|
8905
|
+
return cleaned;
|
|
8906
|
+
const hash2 = createHash7("sha256").update(value).digest("hex").slice(0, 8);
|
|
8907
|
+
const stem = cleaned.slice(0, 60);
|
|
8908
|
+
return stem.length > 0 ? `${stem}-${hash2}` : hash2;
|
|
8909
|
+
}
|
|
8910
|
+
var init_connector = () => {};
|
|
8911
|
+
|
|
8912
|
+
// src/datasource/chunk-store.ts
|
|
8913
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
8914
|
+
import { dirname as dirname10, join as join17 } from "node:path";
|
|
8915
|
+
function datasourceChunkStorePath(workspaceRoot, skillName, instanceId) {
|
|
8916
|
+
return join17(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId), "chunks.json");
|
|
8917
|
+
}
|
|
8918
|
+
|
|
8919
|
+
class DatasourceChunkStore {
|
|
8920
|
+
skillName;
|
|
8921
|
+
instanceId;
|
|
8922
|
+
storePath;
|
|
8923
|
+
maxChunkChars;
|
|
8924
|
+
chunkList = [];
|
|
8925
|
+
loaded = false;
|
|
8926
|
+
constructor(options) {
|
|
8927
|
+
this.skillName = options.skillName;
|
|
8928
|
+
this.instanceId = options.instanceId;
|
|
8929
|
+
this.maxChunkChars = options.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
|
|
8930
|
+
this.storePath = options.workspaceRoot === undefined ? undefined : datasourceChunkStorePath(options.workspaceRoot, options.skillName, options.instanceId);
|
|
8429
8931
|
}
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8932
|
+
replaceDocuments(documents) {
|
|
8933
|
+
const chunks = [];
|
|
8934
|
+
const seenIds = new Set;
|
|
8935
|
+
for (const document of documents) {
|
|
8936
|
+
const docSegment = sanitizeIdSegment(document.docId);
|
|
8937
|
+
const hierarchy = (document.hierarchy ?? []).map(sanitizeIdSegment).filter((segment) => segment.length > 0);
|
|
8938
|
+
const parts = splitContent(document.content, this.maxChunkChars);
|
|
8939
|
+
for (const [index, part] of parts.entries()) {
|
|
8940
|
+
let chunkId = parts.length === 1 ? docSegment : `${docSegment}-p${index + 1}`;
|
|
8941
|
+
while (seenIds.has(chunkId))
|
|
8942
|
+
chunkId = `${chunkId}x`;
|
|
8943
|
+
seenIds.add(chunkId);
|
|
8944
|
+
chunks.push({
|
|
8945
|
+
chunkId,
|
|
8946
|
+
docId: document.docId,
|
|
8947
|
+
hierarchy,
|
|
8948
|
+
...document.title !== undefined ? { title: document.title } : {},
|
|
8949
|
+
content: part,
|
|
8950
|
+
...document.publishedAt !== undefined ? { publishedAt: document.publishedAt } : {},
|
|
8951
|
+
metadata: { ...document.metadata ?? {} }
|
|
8952
|
+
});
|
|
8953
|
+
}
|
|
8954
|
+
}
|
|
8955
|
+
this.chunkList = chunks;
|
|
8956
|
+
this.loaded = true;
|
|
8957
|
+
this.persist();
|
|
8958
|
+
return chunks.length;
|
|
8433
8959
|
}
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8960
|
+
load() {
|
|
8961
|
+
if (this.storePath === undefined)
|
|
8962
|
+
return false;
|
|
8963
|
+
try {
|
|
8964
|
+
if (!existsSync13(this.storePath))
|
|
8965
|
+
return false;
|
|
8966
|
+
const parsed = JSON.parse(readFileSync9(this.storePath, "utf8"));
|
|
8967
|
+
if (parsed.version !== STORE_VERSION || !Array.isArray(parsed.chunks))
|
|
8968
|
+
return false;
|
|
8969
|
+
this.chunkList = parsed.chunks.filter((chunk) => typeof chunk?.chunkId === "string" && typeof chunk?.content === "string");
|
|
8970
|
+
this.loaded = true;
|
|
8971
|
+
return true;
|
|
8972
|
+
} catch {
|
|
8973
|
+
return false;
|
|
8974
|
+
}
|
|
8443
8975
|
}
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
}
|
|
8449
|
-
function validateField(field, min, max, label, errors) {
|
|
8450
|
-
const parts = field.split(",");
|
|
8451
|
-
if (parts.length === 0) {
|
|
8452
|
-
errors.push(`${label}: empty`);
|
|
8453
|
-
return;
|
|
8976
|
+
ensureLoaded() {
|
|
8977
|
+
if (!this.loaded) {
|
|
8978
|
+
this.load();
|
|
8979
|
+
this.loaded = true;
|
|
8980
|
+
}
|
|
8454
8981
|
}
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
errors.push(err);
|
|
8982
|
+
chunks() {
|
|
8983
|
+
this.ensureLoaded();
|
|
8984
|
+
return this.chunkList;
|
|
8459
8985
|
}
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
let base = part;
|
|
8466
|
-
let stepText;
|
|
8467
|
-
if (slashIndex >= 0) {
|
|
8468
|
-
base = part.slice(0, slashIndex);
|
|
8469
|
-
stepText = part.slice(slashIndex + 1);
|
|
8470
|
-
const step = parseNumber(stepText);
|
|
8471
|
-
if (step === null || step <= 0) {
|
|
8472
|
-
return `${label}: invalid step "${stepText}"`;
|
|
8986
|
+
hierarchies() {
|
|
8987
|
+
this.ensureLoaded();
|
|
8988
|
+
const seen = new Map;
|
|
8989
|
+
for (const chunk of this.chunkList) {
|
|
8990
|
+
seen.set(chunk.hierarchy.join("/"), chunk.hierarchy);
|
|
8473
8991
|
}
|
|
8992
|
+
return [...seen.values()];
|
|
8474
8993
|
}
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
}
|
|
8482
|
-
const
|
|
8483
|
-
const
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
return
|
|
8994
|
+
search(query, topK) {
|
|
8995
|
+
this.ensureLoaded();
|
|
8996
|
+
const queryTerms = [...new Set(tokenize2(query))];
|
|
8997
|
+
if (queryTerms.length === 0 || this.chunkList.length === 0 || topK <= 0)
|
|
8998
|
+
return [];
|
|
8999
|
+
const docCount = this.chunkList.length;
|
|
9000
|
+
const tokenized = this.chunkList.map((chunk) => tokenize2(`${chunk.title ?? ""} ${chunk.content}`));
|
|
9001
|
+
const avgLength = tokenized.reduce((sum, tokens) => sum + tokens.length, 0) / docCount || 1;
|
|
9002
|
+
const termFrequency = (term, tokens) => {
|
|
9003
|
+
let tf = 0;
|
|
9004
|
+
for (const token of tokens) {
|
|
9005
|
+
if (token === term)
|
|
9006
|
+
tf += 1;
|
|
9007
|
+
else if (term.length >= 2 && token.startsWith(term))
|
|
9008
|
+
tf += 0.75;
|
|
9009
|
+
}
|
|
9010
|
+
return tf;
|
|
9011
|
+
};
|
|
9012
|
+
const frequencies = queryTerms.map((term) => tokenized.map((tokens) => termFrequency(term, tokens)));
|
|
9013
|
+
const documentFrequency = frequencies.map((perDoc) => perDoc.reduce((count, tf) => count + (tf > 0 ? 1 : 0), 0));
|
|
9014
|
+
const k1 = 1.2;
|
|
9015
|
+
const b = 0.75;
|
|
9016
|
+
const scored = [];
|
|
9017
|
+
for (const [index, chunk] of this.chunkList.entries()) {
|
|
9018
|
+
const tokens = tokenized[index] ?? [];
|
|
9019
|
+
let score = 0;
|
|
9020
|
+
for (const [termIndex] of queryTerms.entries()) {
|
|
9021
|
+
const df = documentFrequency[termIndex] ?? 0;
|
|
9022
|
+
if (df === 0)
|
|
9023
|
+
continue;
|
|
9024
|
+
const tf = frequencies[termIndex]?.[index] ?? 0;
|
|
9025
|
+
if (tf === 0)
|
|
9026
|
+
continue;
|
|
9027
|
+
const idf = Math.log(1 + (docCount - df + 0.5) / (df + 0.5));
|
|
9028
|
+
score += idf * tf * (k1 + 1) / (tf + k1 * (1 - b + b * tokens.length / avgLength));
|
|
9029
|
+
}
|
|
9030
|
+
if (score > 0)
|
|
9031
|
+
scored.push({ chunk, score });
|
|
8492
9032
|
}
|
|
8493
|
-
|
|
9033
|
+
scored.sort((a, b2) => b2.score - a.score || a.chunk.chunkId.localeCompare(b2.chunk.chunkId));
|
|
9034
|
+
return scored.slice(0, topK);
|
|
8494
9035
|
}
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
9036
|
+
persist() {
|
|
9037
|
+
if (this.storePath === undefined)
|
|
9038
|
+
return;
|
|
9039
|
+
try {
|
|
9040
|
+
mkdirSync10(dirname10(this.storePath), { recursive: true });
|
|
9041
|
+
const payload = {
|
|
9042
|
+
version: STORE_VERSION,
|
|
9043
|
+
skill: this.skillName,
|
|
9044
|
+
instance: this.instanceId,
|
|
9045
|
+
updatedAt: Date.now(),
|
|
9046
|
+
chunks: this.chunkList
|
|
9047
|
+
};
|
|
9048
|
+
writeFileSync7(this.storePath, `${JSON.stringify(payload)}
|
|
9049
|
+
`, "utf8");
|
|
9050
|
+
} catch {}
|
|
8498
9051
|
}
|
|
8499
|
-
|
|
8500
|
-
|
|
9052
|
+
}
|
|
9053
|
+
function splitContent(content, maxChars) {
|
|
9054
|
+
const trimmed = content.trim();
|
|
9055
|
+
if (trimmed.length === 0)
|
|
9056
|
+
return [];
|
|
9057
|
+
if (trimmed.length <= maxChars)
|
|
9058
|
+
return [trimmed];
|
|
9059
|
+
const parts = [];
|
|
9060
|
+
const paragraphs = trimmed.split(/\n{2,}/u);
|
|
9061
|
+
let current = "";
|
|
9062
|
+
for (const paragraph of paragraphs) {
|
|
9063
|
+
if (current.length > 0 && current.length + paragraph.length + 2 > maxChars) {
|
|
9064
|
+
parts.push(current);
|
|
9065
|
+
current = "";
|
|
9066
|
+
}
|
|
9067
|
+
if (paragraph.length > maxChars) {
|
|
9068
|
+
if (current.length > 0) {
|
|
9069
|
+
parts.push(current);
|
|
9070
|
+
current = "";
|
|
9071
|
+
}
|
|
9072
|
+
for (let offset = 0;offset < paragraph.length; offset += maxChars) {
|
|
9073
|
+
parts.push(paragraph.slice(offset, offset + maxChars));
|
|
9074
|
+
}
|
|
9075
|
+
continue;
|
|
9076
|
+
}
|
|
9077
|
+
current = current.length === 0 ? paragraph : `${current}
|
|
9078
|
+
|
|
9079
|
+
${paragraph}`;
|
|
8501
9080
|
}
|
|
8502
|
-
|
|
9081
|
+
if (current.length > 0)
|
|
9082
|
+
parts.push(current);
|
|
9083
|
+
return parts.filter((part) => part.trim().length > 0);
|
|
8503
9084
|
}
|
|
8504
|
-
function
|
|
8505
|
-
|
|
8506
|
-
return null;
|
|
8507
|
-
return Number.parseInt(text, 10);
|
|
9085
|
+
function tokenize2(text) {
|
|
9086
|
+
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
|
|
8508
9087
|
}
|
|
9088
|
+
var STORE_VERSION = 1, DEFAULT_MAX_CHUNK_CHARS = 2000;
|
|
9089
|
+
var init_chunk_store = __esm(() => {
|
|
9090
|
+
init_connector();
|
|
9091
|
+
});
|
|
9092
|
+
|
|
8509
9093
|
// src/datasource/scope.ts
|
|
8510
|
-
var DATASOURCE_CHUNKS_SEGMENT = "chunks";
|
|
8511
9094
|
function buildDatasourceInstanceSource(skillName, instanceId) {
|
|
8512
9095
|
return normalizeVirtualPath(`/${skillName}/${instanceId}`);
|
|
8513
9096
|
}
|
|
8514
9097
|
function buildDatasourceChunkSource(skillName, instanceId, chunkId) {
|
|
8515
9098
|
return normalizeVirtualPath(`/${skillName}/${instanceId}/${DATASOURCE_CHUNKS_SEGMENT}/${chunkId}`);
|
|
8516
9099
|
}
|
|
8517
|
-
function datasourceSourcePath(skillName, instanceId, chunkId) {
|
|
8518
|
-
return chunkId === undefined ? buildDatasourceInstanceSource(skillName, instanceId) : buildDatasourceChunkSource(skillName, instanceId, chunkId);
|
|
9100
|
+
function datasourceSourcePath(skillName, instanceId, chunkId) {
|
|
9101
|
+
return chunkId === undefined ? buildDatasourceInstanceSource(skillName, instanceId) : buildDatasourceChunkSource(skillName, instanceId, chunkId);
|
|
9102
|
+
}
|
|
9103
|
+
function datasourceSourceHasFragment(source) {
|
|
9104
|
+
return typeof source === "string" && source.includes("#");
|
|
9105
|
+
}
|
|
9106
|
+
function isDatasourceSource(source) {
|
|
9107
|
+
if (typeof source !== "string" || source.length === 0)
|
|
9108
|
+
return false;
|
|
9109
|
+
if (datasourceSourceHasFragment(source))
|
|
9110
|
+
return false;
|
|
9111
|
+
const normalized = normalizeVirtualPath(source);
|
|
9112
|
+
return normalized !== "/";
|
|
9113
|
+
}
|
|
9114
|
+
function matchesDatasourceScope(source, scope) {
|
|
9115
|
+
if (datasourceSourceHasFragment(source))
|
|
9116
|
+
return false;
|
|
9117
|
+
return matchesVirtualPathScope(source, scope);
|
|
9118
|
+
}
|
|
9119
|
+
var DATASOURCE_CHUNKS_SEGMENT = "chunks";
|
|
9120
|
+
var init_scope = () => {};
|
|
9121
|
+
|
|
9122
|
+
// src/datasource/connector-skill.ts
|
|
9123
|
+
class ConnectorDatasourceSkill {
|
|
9124
|
+
definition;
|
|
9125
|
+
connector;
|
|
9126
|
+
instanceId;
|
|
9127
|
+
instances;
|
|
9128
|
+
pollingIntervalMs;
|
|
9129
|
+
tags;
|
|
9130
|
+
store;
|
|
9131
|
+
dedupeWindowMs;
|
|
9132
|
+
lastIndexedAt;
|
|
9133
|
+
lastPolledAt;
|
|
9134
|
+
lastError;
|
|
9135
|
+
seenDocs = new Map;
|
|
9136
|
+
constructor(definition, options) {
|
|
9137
|
+
this.definition = definition;
|
|
9138
|
+
this.connector = options.connector;
|
|
9139
|
+
this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID;
|
|
9140
|
+
this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
|
|
9141
|
+
this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS;
|
|
9142
|
+
this.tags = options.tags ?? definition.defaultTags;
|
|
9143
|
+
this.lastIndexedAt = options.lastIndexedAt;
|
|
9144
|
+
this.dedupeWindowMs = options.dedupeWindowMs ?? 0;
|
|
9145
|
+
this.store = new DatasourceChunkStore({
|
|
9146
|
+
skillName: definition.skillName,
|
|
9147
|
+
instanceId: this.instanceId,
|
|
9148
|
+
workspaceRoot: options.workspaceRoot,
|
|
9149
|
+
maxChunkChars: options.maxChunkChars
|
|
9150
|
+
});
|
|
9151
|
+
}
|
|
9152
|
+
describe() {
|
|
9153
|
+
return {
|
|
9154
|
+
name: this.definition.skillName,
|
|
9155
|
+
id: this.definition.skillName,
|
|
9156
|
+
type: this.definition.skillType,
|
|
9157
|
+
description: this.definition.description,
|
|
9158
|
+
capabilities: this.definition.capabilities,
|
|
9159
|
+
tags: this.tags,
|
|
9160
|
+
status: "active",
|
|
9161
|
+
datasourceId: this.definition.skillName,
|
|
9162
|
+
instanceId: this.instanceId,
|
|
9163
|
+
instances: this.instances
|
|
9164
|
+
};
|
|
9165
|
+
}
|
|
9166
|
+
polling() {
|
|
9167
|
+
return {
|
|
9168
|
+
mode: this.pollingIntervalMs > 0 ? "poll" : "none",
|
|
9169
|
+
...this.pollingIntervalMs > 0 ? { intervalMs: this.pollingIntervalMs } : {},
|
|
9170
|
+
lastIndexedAt: this.lastIndexedAt,
|
|
9171
|
+
...this.lastPolledAt !== undefined ? { lastPolledAt: this.lastPolledAt } : {},
|
|
9172
|
+
...this.lastError !== undefined ? { lastError: this.lastError } : {}
|
|
9173
|
+
};
|
|
9174
|
+
}
|
|
9175
|
+
async index() {
|
|
9176
|
+
this.lastPolledAt = Date.now();
|
|
9177
|
+
let fetched;
|
|
9178
|
+
try {
|
|
9179
|
+
fetched = await this.connector.fetch();
|
|
9180
|
+
} catch {
|
|
9181
|
+
return this.fail("datasource-unavailable", "connector failed unexpectedly");
|
|
9182
|
+
}
|
|
9183
|
+
if (!fetched.ok) {
|
|
9184
|
+
const code = connectorFailureToDiagnosticCode(fetched.reason);
|
|
9185
|
+
const message = boundDiagnosticText(fetched.message ?? fetched.reason);
|
|
9186
|
+
return this.fail(code, `${fetched.reason}: ${message}`);
|
|
9187
|
+
}
|
|
9188
|
+
const documents = this.applyDedupeWindow(fetched.documents);
|
|
9189
|
+
const chunkCount = this.store.replaceDocuments(documents);
|
|
9190
|
+
this.lastIndexedAt = Date.now();
|
|
9191
|
+
this.lastError = undefined;
|
|
9192
|
+
const diagnostics = (fetched.warnings ?? []).map((warning) => ({
|
|
9193
|
+
code: "datasource-index-failed",
|
|
9194
|
+
severity: "warning",
|
|
9195
|
+
message: boundDiagnosticText(warning),
|
|
9196
|
+
instanceId: this.instanceId,
|
|
9197
|
+
source: this.definition.skillName
|
|
9198
|
+
}));
|
|
9199
|
+
if (chunkCount === 0) {
|
|
9200
|
+
diagnostics.push({
|
|
9201
|
+
code: "datasource-empty",
|
|
9202
|
+
severity: "info",
|
|
9203
|
+
message: "datasource fetch returned no indexable documents",
|
|
9204
|
+
instanceId: this.instanceId,
|
|
9205
|
+
source: this.definition.skillName
|
|
9206
|
+
});
|
|
9207
|
+
}
|
|
9208
|
+
return {
|
|
9209
|
+
ok: true,
|
|
9210
|
+
instanceId: this.instanceId,
|
|
9211
|
+
skill: this.definition.skillName,
|
|
9212
|
+
chunkCount,
|
|
9213
|
+
indexedAt: this.lastIndexedAt,
|
|
9214
|
+
diagnostics
|
|
9215
|
+
};
|
|
9216
|
+
}
|
|
9217
|
+
retrievalMethods() {
|
|
9218
|
+
return [
|
|
9219
|
+
new ConnectorLexicalMethod({
|
|
9220
|
+
skillName: this.definition.skillName,
|
|
9221
|
+
skillType: this.definition.skillType,
|
|
9222
|
+
instanceId: this.instanceId,
|
|
9223
|
+
tags: this.tags,
|
|
9224
|
+
store: this.store
|
|
9225
|
+
})
|
|
9226
|
+
];
|
|
9227
|
+
}
|
|
9228
|
+
describeSources() {
|
|
9229
|
+
const out = this.instances.map((instanceId) => ({
|
|
9230
|
+
source: datasourceSourcePath(this.definition.skillName, instanceId),
|
|
9231
|
+
datasourceId: this.definition.skillName,
|
|
9232
|
+
skill: this.definition.skillName,
|
|
9233
|
+
instanceId,
|
|
9234
|
+
contentType: this.definition.contentType,
|
|
9235
|
+
metadata: {
|
|
9236
|
+
datasourceId: this.definition.skillName,
|
|
9237
|
+
instanceId,
|
|
9238
|
+
tags: this.tags
|
|
9239
|
+
}
|
|
9240
|
+
}));
|
|
9241
|
+
for (const hierarchy of this.store.hierarchies()) {
|
|
9242
|
+
if (hierarchy.length === 0)
|
|
9243
|
+
continue;
|
|
9244
|
+
out.push({
|
|
9245
|
+
source: `${datasourceSourcePath(this.definition.skillName, this.instanceId)}/${hierarchy.join("/")}`,
|
|
9246
|
+
datasourceId: this.definition.skillName,
|
|
9247
|
+
skill: this.definition.skillName,
|
|
9248
|
+
instanceId: this.instanceId,
|
|
9249
|
+
contentType: this.definition.contentType,
|
|
9250
|
+
metadata: {
|
|
9251
|
+
datasourceId: this.definition.skillName,
|
|
9252
|
+
instanceId: this.instanceId,
|
|
9253
|
+
hierarchy
|
|
9254
|
+
}
|
|
9255
|
+
});
|
|
9256
|
+
}
|
|
9257
|
+
return out;
|
|
9258
|
+
}
|
|
9259
|
+
skillManifest() {
|
|
9260
|
+
const skillName = this.definition.skillName;
|
|
9261
|
+
const instanceScopes = this.instances.map((instanceId) => `- \`${datasourceSourcePath(skillName, instanceId)}\``).join(`
|
|
9262
|
+
`);
|
|
9263
|
+
const cadence = this.pollingIntervalMs > 0 ? `roughly every ${Math.round(this.pollingIntervalMs / 60000)} minute(s) when auto-refresh runs` : "on manual refresh only";
|
|
9264
|
+
return {
|
|
9265
|
+
name: `datasource-${skillName}`,
|
|
9266
|
+
description: this.definition.manifestDescription,
|
|
9267
|
+
content: [
|
|
9268
|
+
`# ${this.definition.description} (${this.definition.skillType})`,
|
|
9269
|
+
"",
|
|
9270
|
+
"## When to use",
|
|
9271
|
+
this.definition.manifestDescription,
|
|
9272
|
+
"",
|
|
9273
|
+
"## Indexing",
|
|
9274
|
+
`Indexing is server-managed and refreshed ${cadence}. You do not trigger indexing; just search.`,
|
|
9275
|
+
"",
|
|
9276
|
+
"## How to search",
|
|
9277
|
+
"Call `search_datasource_documents` with a natural-language `query`. Optionally pass `topK` and a narrowing `scope`. Available authorized scopes:",
|
|
9278
|
+
instanceScopes.length > 0 ? instanceScopes : "- (no authorized instances)",
|
|
9279
|
+
"",
|
|
9280
|
+
"`scope` can only narrow within already-authorized scopes; it can never widen access.",
|
|
9281
|
+
"",
|
|
9282
|
+
"## Output rules",
|
|
9283
|
+
`Datasource source identifiers such as \`/${skillName}/<instance>/chunks/<id>\` are stable and traceable. Result metadata may carry real file paths or account identifiers; you may cite them in the visible answer when they help the user locate the underlying item. Privacy is the operator's responsibility: run AutoRAG with a local LLM if results must not leave this machine.`,
|
|
9284
|
+
...this.definition.manifestNotes !== undefined && this.definition.manifestNotes.length > 0 ? ["", ...this.definition.manifestNotes] : []
|
|
9285
|
+
].join(`
|
|
9286
|
+
`)
|
|
9287
|
+
};
|
|
9288
|
+
}
|
|
9289
|
+
applyDedupeWindow(documents) {
|
|
9290
|
+
if (this.dedupeWindowMs <= 0)
|
|
9291
|
+
return documents;
|
|
9292
|
+
const now = Date.now();
|
|
9293
|
+
for (const [docId, seenAt] of this.seenDocs) {
|
|
9294
|
+
if (now - seenAt > this.dedupeWindowMs)
|
|
9295
|
+
this.seenDocs.delete(docId);
|
|
9296
|
+
}
|
|
9297
|
+
const out = [];
|
|
9298
|
+
const inBatch = new Set;
|
|
9299
|
+
for (const document of documents) {
|
|
9300
|
+
if (inBatch.has(document.docId))
|
|
9301
|
+
continue;
|
|
9302
|
+
inBatch.add(document.docId);
|
|
9303
|
+
this.seenDocs.set(document.docId, now);
|
|
9304
|
+
out.push(document);
|
|
9305
|
+
}
|
|
9306
|
+
return out;
|
|
9307
|
+
}
|
|
9308
|
+
fail(code, message) {
|
|
9309
|
+
const sanitized = boundDiagnosticText(message);
|
|
9310
|
+
this.lastError = sanitized;
|
|
9311
|
+
const diagnostic = {
|
|
9312
|
+
code,
|
|
9313
|
+
severity: code === "datasource-unavailable" || code === "datasource-empty" ? "warning" : "error",
|
|
9314
|
+
message: sanitized,
|
|
9315
|
+
instanceId: this.instanceId,
|
|
9316
|
+
source: this.definition.skillName
|
|
9317
|
+
};
|
|
9318
|
+
return {
|
|
9319
|
+
ok: false,
|
|
9320
|
+
instanceId: this.instanceId,
|
|
9321
|
+
skill: this.definition.skillName,
|
|
9322
|
+
indexedAt: Date.now(),
|
|
9323
|
+
diagnostics: [diagnostic],
|
|
9324
|
+
error: sanitized,
|
|
9325
|
+
code,
|
|
9326
|
+
message: sanitized
|
|
9327
|
+
};
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
|
|
9331
|
+
class ConnectorLexicalMethod {
|
|
9332
|
+
options;
|
|
9333
|
+
constructor(options) {
|
|
9334
|
+
this.options = options;
|
|
9335
|
+
}
|
|
9336
|
+
describe() {
|
|
9337
|
+
const { skillName, skillType, tags } = this.options;
|
|
9338
|
+
return {
|
|
9339
|
+
name: `${skillName}-lexical`,
|
|
9340
|
+
type: "bm25",
|
|
9341
|
+
description: `Lexical retrieval over indexed ${skillType} chunks`,
|
|
9342
|
+
status: "active",
|
|
9343
|
+
capabilities: ["lexical", "scoped", "path-opaque-sources"],
|
|
9344
|
+
datasourceId: skillName,
|
|
9345
|
+
tags: [...tags]
|
|
9346
|
+
};
|
|
9347
|
+
}
|
|
9348
|
+
async retrieve(query, options) {
|
|
9349
|
+
const trimmed = query.trim();
|
|
9350
|
+
if (trimmed.length === 0)
|
|
9351
|
+
return [];
|
|
9352
|
+
const topK = options.topK ?? DEFAULT_TOP_K2;
|
|
9353
|
+
let hits;
|
|
9354
|
+
try {
|
|
9355
|
+
hits = this.options.store.search(trimmed, Math.max(topK * 3, topK));
|
|
9356
|
+
} catch {
|
|
9357
|
+
return [];
|
|
9358
|
+
}
|
|
9359
|
+
const { skillName, instanceId } = this.options;
|
|
9360
|
+
const mapped = [];
|
|
9361
|
+
for (const { chunk, score } of hits) {
|
|
9362
|
+
const source = datasourceSourcePath(skillName, instanceId, chunk.chunkId);
|
|
9363
|
+
if (!this.matchesScope(source, options.scope, options.allowedScopes))
|
|
9364
|
+
continue;
|
|
9365
|
+
mapped.push({
|
|
9366
|
+
id: `${skillName}:${instanceId}:${chunk.chunkId}`,
|
|
9367
|
+
content: chunk.content,
|
|
9368
|
+
source,
|
|
9369
|
+
score,
|
|
9370
|
+
metadata: {
|
|
9371
|
+
...chunk.metadata,
|
|
9372
|
+
method: `${skillName}-lexical`,
|
|
9373
|
+
datasourceId: skillName,
|
|
9374
|
+
instanceId,
|
|
9375
|
+
chunkId: chunk.chunkId,
|
|
9376
|
+
...chunk.title !== undefined ? { title: chunk.title } : {},
|
|
9377
|
+
...chunk.hierarchy.length > 0 ? { hierarchy: chunk.hierarchy.join("/") } : {},
|
|
9378
|
+
...chunk.publishedAt !== undefined ? { publishedAt: chunk.publishedAt } : {}
|
|
9379
|
+
}
|
|
9380
|
+
});
|
|
9381
|
+
if (mapped.length >= topK)
|
|
9382
|
+
break;
|
|
9383
|
+
}
|
|
9384
|
+
return mapped;
|
|
9385
|
+
}
|
|
9386
|
+
matchesScope(source, scope, allowedScopes) {
|
|
9387
|
+
if (!matchesDatasourceScope(source, scope))
|
|
9388
|
+
return false;
|
|
9389
|
+
if (allowedScopes === undefined || allowedScopes.length === 0)
|
|
9390
|
+
return true;
|
|
9391
|
+
return allowedScopes.some((entry) => matchesDatasourceScope(source, entry));
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
var DEFAULT_POLLING_INTERVAL_MS, DEFAULT_INSTANCE_ID = "default", DEFAULT_TOP_K2 = 20;
|
|
9395
|
+
var init_connector_skill = __esm(() => {
|
|
9396
|
+
init_chunk_store();
|
|
9397
|
+
init_connector();
|
|
9398
|
+
init_scope();
|
|
9399
|
+
DEFAULT_POLLING_INTERVAL_MS = 15 * 60 * 1000;
|
|
9400
|
+
});
|
|
9401
|
+
|
|
9402
|
+
// src/datasource/http.ts
|
|
9403
|
+
function combineSignals(timeoutMs, outer) {
|
|
9404
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
9405
|
+
return outer === undefined ? timeout : AbortSignal.any([timeout, outer]);
|
|
9406
|
+
}
|
|
9407
|
+
function statusToReason(status) {
|
|
9408
|
+
if (status === 401)
|
|
9409
|
+
return "auth";
|
|
9410
|
+
if (status === 403)
|
|
9411
|
+
return "permission";
|
|
9412
|
+
if (status === 429)
|
|
9413
|
+
return "rate-limited";
|
|
9414
|
+
return "api-error";
|
|
9415
|
+
}
|
|
9416
|
+
async function requestText(url, options) {
|
|
9417
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
9418
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
|
|
9419
|
+
let response;
|
|
9420
|
+
try {
|
|
9421
|
+
response = await fetchImpl(url, {
|
|
9422
|
+
method: options.method ?? "GET",
|
|
9423
|
+
headers: options.headers,
|
|
9424
|
+
body: options.body,
|
|
9425
|
+
signal: combineSignals(timeoutMs, options.signal)
|
|
9426
|
+
});
|
|
9427
|
+
} catch {
|
|
9428
|
+
return { ok: false, reason: "unavailable", message: "request failed or timed out" };
|
|
9429
|
+
}
|
|
9430
|
+
if (!response.ok) {
|
|
9431
|
+
return { ok: false, reason: statusToReason(response.status), message: `http-${response.status}` };
|
|
9432
|
+
}
|
|
9433
|
+
let text;
|
|
9434
|
+
try {
|
|
9435
|
+
text = await response.text();
|
|
9436
|
+
} catch {
|
|
9437
|
+
return { ok: false, reason: "invalid-data", message: "response body unreadable" };
|
|
9438
|
+
}
|
|
9439
|
+
return { ok: true, status: response.status, text };
|
|
9440
|
+
}
|
|
9441
|
+
function httpText(url, options = {}) {
|
|
9442
|
+
return requestText(url, options);
|
|
9443
|
+
}
|
|
9444
|
+
async function httpJson(url, options = {}) {
|
|
9445
|
+
const result = await requestText(url, options);
|
|
9446
|
+
if (!result.ok)
|
|
9447
|
+
return result;
|
|
9448
|
+
try {
|
|
9449
|
+
return { ok: true, status: result.status, json: JSON.parse(result.text) };
|
|
9450
|
+
} catch {
|
|
9451
|
+
return { ok: false, reason: "invalid-data", message: "response was not valid JSON" };
|
|
9452
|
+
}
|
|
9453
|
+
}
|
|
9454
|
+
function asRecord2(value) {
|
|
9455
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
9456
|
+
}
|
|
9457
|
+
function asArray(value) {
|
|
9458
|
+
return Array.isArray(value) ? value : [];
|
|
9459
|
+
}
|
|
9460
|
+
function asString(value) {
|
|
9461
|
+
return typeof value === "string" ? value : undefined;
|
|
9462
|
+
}
|
|
9463
|
+
function asNumber(value) {
|
|
9464
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
9465
|
+
}
|
|
9466
|
+
function parseEpochMs(value) {
|
|
9467
|
+
if (typeof value !== "string" || value.length === 0)
|
|
9468
|
+
return;
|
|
9469
|
+
const parsed = Date.parse(value);
|
|
9470
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
9471
|
+
}
|
|
9472
|
+
function resolveToken(token, tokenEnv) {
|
|
9473
|
+
if (token !== undefined && token.length > 0)
|
|
9474
|
+
return token;
|
|
9475
|
+
if (tokenEnv !== undefined && tokenEnv.length > 0) {
|
|
9476
|
+
const fromEnv = process.env[tokenEnv];
|
|
9477
|
+
if (fromEnv !== undefined && fromEnv.length > 0)
|
|
9478
|
+
return fromEnv;
|
|
9479
|
+
}
|
|
9480
|
+
return;
|
|
9481
|
+
}
|
|
9482
|
+
var DEFAULT_TIMEOUT_MS3 = 15000;
|
|
9483
|
+
|
|
9484
|
+
// src/datasource/skills/discord/connector.ts
|
|
9485
|
+
class DiscordConnector {
|
|
9486
|
+
options;
|
|
9487
|
+
constructor(options = {}) {
|
|
9488
|
+
this.options = options;
|
|
9489
|
+
}
|
|
9490
|
+
async fetch(signal) {
|
|
9491
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV);
|
|
9492
|
+
if (token === undefined)
|
|
9493
|
+
return { ok: false, reason: "not-configured", message: "auth token not configured" };
|
|
9494
|
+
const guildId = this.options.guildId;
|
|
9495
|
+
if (guildId === undefined || guildId.length === 0) {
|
|
9496
|
+
return { ok: false, reason: "not-configured", message: "guild id not configured" };
|
|
9497
|
+
}
|
|
9498
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL;
|
|
9499
|
+
const request = {
|
|
9500
|
+
headers: { Authorization: `Bot ${token}` },
|
|
9501
|
+
timeoutMs: this.options.timeoutMs,
|
|
9502
|
+
fetchImpl: this.options.fetchImpl,
|
|
9503
|
+
signal
|
|
9504
|
+
};
|
|
9505
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES;
|
|
9506
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS;
|
|
9507
|
+
const listResult = await httpJson(`${baseUrl}/guilds/${guildId}/channels`, request);
|
|
9508
|
+
if (!listResult.ok) {
|
|
9509
|
+
return { ok: false, reason: listResult.reason, message: `channel list failed: ${listResult.message}` };
|
|
9510
|
+
}
|
|
9511
|
+
const channels = [];
|
|
9512
|
+
for (const raw of asArray(listResult.json)) {
|
|
9513
|
+
const channel = asRecord2(raw);
|
|
9514
|
+
const id = asString(channel?.id);
|
|
9515
|
+
const name = asString(channel?.name);
|
|
9516
|
+
const type = asNumber(channel?.type);
|
|
9517
|
+
if (id === undefined || name === undefined || type === undefined)
|
|
9518
|
+
continue;
|
|
9519
|
+
if (!TEXT_CHANNEL_TYPES.has(type))
|
|
9520
|
+
continue;
|
|
9521
|
+
channels.push({ id, name });
|
|
9522
|
+
}
|
|
9523
|
+
const filter = this.options.channels;
|
|
9524
|
+
const selected = filter === undefined || filter.length === 0 ? channels : channels.filter((channel) => filter.includes(channel.id) || filter.includes(channel.name));
|
|
9525
|
+
const documents = [];
|
|
9526
|
+
const warnings = [];
|
|
9527
|
+
for (const channel of selected) {
|
|
9528
|
+
if (documents.length >= maxDocuments)
|
|
9529
|
+
break;
|
|
9530
|
+
let before;
|
|
9531
|
+
for (let page = 0;page < maxPages && documents.length < maxDocuments; page += 1) {
|
|
9532
|
+
const url = new URL(`${baseUrl}/channels/${channel.id}/messages`);
|
|
9533
|
+
url.searchParams.set("limit", "100");
|
|
9534
|
+
if (before !== undefined)
|
|
9535
|
+
url.searchParams.set("before", before);
|
|
9536
|
+
const result = await httpJson(url.toString(), request);
|
|
9537
|
+
if (!result.ok) {
|
|
9538
|
+
if (result.reason === "rate-limited") {
|
|
9539
|
+
return { ok: false, reason: "rate-limited", message: "discord: rate limited" };
|
|
9540
|
+
}
|
|
9541
|
+
if (result.reason === "permission") {
|
|
9542
|
+
warnings.push(`channel ${channel.name} skipped: missing permission`);
|
|
9543
|
+
} else {
|
|
9544
|
+
warnings.push(`channel ${channel.name} history failed: ${result.message}`);
|
|
9545
|
+
}
|
|
9546
|
+
break;
|
|
9547
|
+
}
|
|
9548
|
+
const messages = asArray(result.json);
|
|
9549
|
+
if (messages.length === 0)
|
|
9550
|
+
break;
|
|
9551
|
+
let lastId;
|
|
9552
|
+
for (const raw of messages) {
|
|
9553
|
+
const message = asRecord2(raw);
|
|
9554
|
+
const id = asString(message?.id);
|
|
9555
|
+
if (id !== undefined)
|
|
9556
|
+
lastId = id;
|
|
9557
|
+
if (documents.length >= maxDocuments)
|
|
9558
|
+
continue;
|
|
9559
|
+
const content = asString(message?.content);
|
|
9560
|
+
if (message === undefined || id === undefined || content === undefined || content.length === 0)
|
|
9561
|
+
continue;
|
|
9562
|
+
const author = asString(asRecord2(message.author)?.username) ?? "unknown";
|
|
9563
|
+
documents.push({
|
|
9564
|
+
docId: `${channel.id}-${id}`,
|
|
9565
|
+
hierarchy: ["channels", channel.name],
|
|
9566
|
+
title: `#${channel.name}`,
|
|
9567
|
+
content: `${author}: ${content}`,
|
|
9568
|
+
publishedAt: parseEpochMs(message.timestamp),
|
|
9569
|
+
metadata: { channelId: channel.id, messageId: id, author }
|
|
9570
|
+
});
|
|
9571
|
+
}
|
|
9572
|
+
if (lastId === undefined || messages.length < 100)
|
|
9573
|
+
break;
|
|
9574
|
+
before = lastId;
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
9577
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
9578
|
+
}
|
|
9579
|
+
}
|
|
9580
|
+
var DEFAULT_BASE_URL = "https://discord.com/api/v10", DEFAULT_TOKEN_ENV = "DISCORD_BOT_TOKEN", DEFAULT_MAX_PAGES = 10, DEFAULT_MAX_DOCUMENTS = 500, TEXT_CHANNEL_TYPES;
|
|
9581
|
+
var init_connector2 = __esm(() => {
|
|
9582
|
+
TEXT_CHANNEL_TYPES = new Set([0, 5]);
|
|
9583
|
+
});
|
|
9584
|
+
|
|
9585
|
+
// src/datasource/skills/discord/skill.ts
|
|
9586
|
+
var DISCORD_SKILL_DEFINITION, DiscordSkill;
|
|
9587
|
+
var init_skill = __esm(() => {
|
|
9588
|
+
init_connector_skill();
|
|
9589
|
+
init_connector2();
|
|
9590
|
+
DISCORD_SKILL_DEFINITION = {
|
|
9591
|
+
skillName: "discord",
|
|
9592
|
+
skillType: "discord-guild",
|
|
9593
|
+
description: "Discord guild datasource",
|
|
9594
|
+
capabilities: ["chat", "api", "polling"],
|
|
9595
|
+
defaultTags: ["discord", "chat", "pii"],
|
|
9596
|
+
contentType: "chat",
|
|
9597
|
+
manifestDescription: "Search indexed Discord messages across authorized guild channels and threads. Use for questions about Discord conversations or community discussions.",
|
|
9598
|
+
manifestNotes: ["Channel visibility and history windows are bounded by the server-configured bot permissions."]
|
|
9599
|
+
};
|
|
9600
|
+
DiscordSkill = class DiscordSkill extends ConnectorDatasourceSkill {
|
|
9601
|
+
constructor(options = {}) {
|
|
9602
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
9603
|
+
super(DISCORD_SKILL_DEFINITION, {
|
|
9604
|
+
...rest,
|
|
9605
|
+
connector: connector ?? new DiscordConnector(connectorOptions ?? {})
|
|
9606
|
+
});
|
|
9607
|
+
}
|
|
9608
|
+
};
|
|
9609
|
+
});
|
|
9610
|
+
|
|
9611
|
+
// src/datasource/skills/discord/index.ts
|
|
9612
|
+
var init_discord = __esm(() => {
|
|
9613
|
+
init_connector2();
|
|
9614
|
+
init_skill();
|
|
9615
|
+
});
|
|
9616
|
+
|
|
9617
|
+
// src/datasource/skills/gdrive/connector.ts
|
|
9618
|
+
class GDriveConnector {
|
|
9619
|
+
options;
|
|
9620
|
+
constructor(options = {}) {
|
|
9621
|
+
this.options = options;
|
|
9622
|
+
}
|
|
9623
|
+
async fetch(signal) {
|
|
9624
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV2);
|
|
9625
|
+
if (token === undefined)
|
|
9626
|
+
return { ok: false, reason: "not-configured", message: "auth token not configured" };
|
|
9627
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL2;
|
|
9628
|
+
const request = {
|
|
9629
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
9630
|
+
timeoutMs: this.options.timeoutMs,
|
|
9631
|
+
fetchImpl: this.options.fetchImpl,
|
|
9632
|
+
signal
|
|
9633
|
+
};
|
|
9634
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES2;
|
|
9635
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS2;
|
|
9636
|
+
const files = [];
|
|
9637
|
+
let pageToken;
|
|
9638
|
+
for (let page = 0;page < maxPages; page += 1) {
|
|
9639
|
+
const url = new URL(`${baseUrl}/files`);
|
|
9640
|
+
url.searchParams.set("pageSize", "100");
|
|
9641
|
+
url.searchParams.set("fields", "nextPageToken,files(id,name,mimeType,modifiedTime,parents)");
|
|
9642
|
+
const query = this.options.folderId !== undefined ? `trashed=false and '${this.options.folderId}' in parents` : "trashed=false";
|
|
9643
|
+
url.searchParams.set("q", query);
|
|
9644
|
+
if (this.options.includeSharedDrives === true) {
|
|
9645
|
+
url.searchParams.set("supportsAllDrives", "true");
|
|
9646
|
+
url.searchParams.set("includeItemsFromAllDrives", "true");
|
|
9647
|
+
url.searchParams.set("corpora", "allDrives");
|
|
9648
|
+
}
|
|
9649
|
+
if (pageToken !== undefined)
|
|
9650
|
+
url.searchParams.set("pageToken", pageToken);
|
|
9651
|
+
const result = await httpJson(url.toString(), request);
|
|
9652
|
+
if (!result.ok)
|
|
9653
|
+
return { ok: false, reason: result.reason, message: `file list failed: ${result.message}` };
|
|
9654
|
+
const envelope = asRecord2(result.json);
|
|
9655
|
+
if (envelope === undefined)
|
|
9656
|
+
return { ok: false, reason: "invalid-data", message: "file list malformed" };
|
|
9657
|
+
for (const raw of asArray(envelope.files)) {
|
|
9658
|
+
const file = asRecord2(raw);
|
|
9659
|
+
const id = asString(file?.id);
|
|
9660
|
+
const name = asString(file?.name);
|
|
9661
|
+
const mimeType = asString(file?.mimeType);
|
|
9662
|
+
if (id === undefined || name === undefined || mimeType === undefined)
|
|
9663
|
+
continue;
|
|
9664
|
+
files.push({ id, name, mimeType, modifiedTime: asString(file?.modifiedTime) });
|
|
9665
|
+
}
|
|
9666
|
+
pageToken = asString(envelope.nextPageToken);
|
|
9667
|
+
if (pageToken === undefined || pageToken.length === 0)
|
|
9668
|
+
break;
|
|
9669
|
+
}
|
|
9670
|
+
const documents = [];
|
|
9671
|
+
const warnings = [];
|
|
9672
|
+
for (const file of files) {
|
|
9673
|
+
if (documents.length >= maxDocuments)
|
|
9674
|
+
break;
|
|
9675
|
+
const exportMime = EXPORT_MIME[file.mimeType];
|
|
9676
|
+
let contentUrl;
|
|
9677
|
+
if (exportMime !== undefined) {
|
|
9678
|
+
contentUrl = `${baseUrl}/files/${file.id}/export?mimeType=${encodeURIComponent(exportMime)}`;
|
|
9679
|
+
} else if (DOWNLOAD_MIME.has(file.mimeType)) {
|
|
9680
|
+
contentUrl = `${baseUrl}/files/${file.id}?alt=media`;
|
|
9681
|
+
} else {
|
|
9682
|
+
continue;
|
|
9683
|
+
}
|
|
9684
|
+
const contentResult = await httpText(contentUrl, request);
|
|
9685
|
+
if (!contentResult.ok) {
|
|
9686
|
+
warnings.push(`file export failed: ${contentResult.message}`);
|
|
9687
|
+
continue;
|
|
9688
|
+
}
|
|
9689
|
+
const content = contentResult.text.trim().slice(0, MAX_CONTENT_CHARS);
|
|
9690
|
+
if (content.length === 0)
|
|
9691
|
+
continue;
|
|
9692
|
+
documents.push({
|
|
9693
|
+
docId: file.id,
|
|
9694
|
+
hierarchy: ["files"],
|
|
9695
|
+
title: file.name,
|
|
9696
|
+
content,
|
|
9697
|
+
publishedAt: parseEpochMs(file.modifiedTime),
|
|
9698
|
+
metadata: {
|
|
9699
|
+
mimeType: file.mimeType,
|
|
9700
|
+
...file.modifiedTime !== undefined ? { modifiedTime: file.modifiedTime } : {}
|
|
9701
|
+
}
|
|
9702
|
+
});
|
|
9703
|
+
}
|
|
9704
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
var DEFAULT_BASE_URL2 = "https://www.googleapis.com/drive/v3", DEFAULT_TOKEN_ENV2 = "GDRIVE_ACCESS_TOKEN", DEFAULT_MAX_PAGES2 = 10, DEFAULT_MAX_DOCUMENTS2 = 500, MAX_CONTENT_CHARS = 1e5, EXPORT_MIME, DOWNLOAD_MIME;
|
|
9708
|
+
var init_connector3 = __esm(() => {
|
|
9709
|
+
EXPORT_MIME = {
|
|
9710
|
+
"application/vnd.google-apps.document": "text/plain",
|
|
9711
|
+
"application/vnd.google-apps.spreadsheet": "text/csv"
|
|
9712
|
+
};
|
|
9713
|
+
DOWNLOAD_MIME = new Set(["text/plain", "text/markdown", "text/csv", "application/json"]);
|
|
9714
|
+
});
|
|
9715
|
+
|
|
9716
|
+
// src/datasource/skills/gdrive/rclone-connector.ts
|
|
9717
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
9718
|
+
|
|
9719
|
+
class RcloneConnector {
|
|
9720
|
+
options;
|
|
9721
|
+
runner;
|
|
9722
|
+
constructor(options = {}) {
|
|
9723
|
+
this.options = options;
|
|
9724
|
+
this.runner = options.runner ?? ((args, timeoutMs) => runBinary(options.binaryPath ?? DEFAULT_BINARY2, args, timeoutMs));
|
|
9725
|
+
}
|
|
9726
|
+
async fetch() {
|
|
9727
|
+
const remote = this.options.remote;
|
|
9728
|
+
if (remote === undefined || remote.length === 0) {
|
|
9729
|
+
return { ok: false, reason: "not-configured", message: "rclone remote not configured" };
|
|
9730
|
+
}
|
|
9731
|
+
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
|
|
9732
|
+
const exportFormats = this.options.exportFormats ?? DEFAULT_EXPORT_FORMATS;
|
|
9733
|
+
const extensions = this.options.extensions ?? DEFAULT_EXTENSIONS;
|
|
9734
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS3;
|
|
9735
|
+
const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE;
|
|
9736
|
+
let listResult;
|
|
9737
|
+
try {
|
|
9738
|
+
listResult = await this.runner(["lsjson", remote, "--recursive", "--files-only", "--drive-export-formats", exportFormats], timeoutMs);
|
|
9739
|
+
} catch {
|
|
9740
|
+
return { ok: false, reason: "unavailable", message: "rclone binary not found or failed to spawn" };
|
|
9741
|
+
}
|
|
9742
|
+
if (!listResult.ok) {
|
|
9743
|
+
return { ok: false, reason: classifyFailure(listResult.stderr), message: shortFailure(listResult) };
|
|
9744
|
+
}
|
|
9745
|
+
let entries;
|
|
9746
|
+
try {
|
|
9747
|
+
entries = asArray(JSON.parse(listResult.stdout));
|
|
9748
|
+
} catch {
|
|
9749
|
+
return { ok: false, reason: "invalid-data", message: "rclone listing was not valid JSON" };
|
|
9750
|
+
}
|
|
9751
|
+
const documents = [];
|
|
9752
|
+
let readFailures = 0;
|
|
9753
|
+
let skipped = 0;
|
|
9754
|
+
for (const raw of entries) {
|
|
9755
|
+
if (documents.length >= maxDocuments)
|
|
9756
|
+
break;
|
|
9757
|
+
const entry = asRecord2(raw);
|
|
9758
|
+
const path = asString(entry?.Path);
|
|
9759
|
+
const name = asString(entry?.Name);
|
|
9760
|
+
if (entry === undefined || path === undefined || name === undefined)
|
|
9761
|
+
continue;
|
|
9762
|
+
const extension = name.includes(".") ? name.slice(name.lastIndexOf(".")).toLowerCase() : "";
|
|
9763
|
+
if (!extensions.includes(extension)) {
|
|
9764
|
+
skipped += 1;
|
|
9765
|
+
continue;
|
|
9766
|
+
}
|
|
9767
|
+
const size = typeof entry.Size === "number" ? entry.Size : 0;
|
|
9768
|
+
if (size > maxBytes) {
|
|
9769
|
+
skipped += 1;
|
|
9770
|
+
continue;
|
|
9771
|
+
}
|
|
9772
|
+
const target = remote.endsWith(":") || remote.endsWith("/") ? `${remote}${path}` : `${remote}/${path}`;
|
|
9773
|
+
let content = "";
|
|
9774
|
+
try {
|
|
9775
|
+
const catResult = await this.runner(["cat", target, "--drive-export-formats", exportFormats, "--count", String(maxBytes)], timeoutMs);
|
|
9776
|
+
if (!catResult.ok) {
|
|
9777
|
+
readFailures += 1;
|
|
9778
|
+
continue;
|
|
9779
|
+
}
|
|
9780
|
+
content = catResult.stdout.trim().slice(0, MAX_CONTENT_CHARS2);
|
|
9781
|
+
} catch {
|
|
9782
|
+
readFailures += 1;
|
|
9783
|
+
continue;
|
|
9784
|
+
}
|
|
9785
|
+
if (content.length === 0)
|
|
9786
|
+
continue;
|
|
9787
|
+
const segments = path.split("/");
|
|
9788
|
+
documents.push({
|
|
9789
|
+
docId: path,
|
|
9790
|
+
hierarchy: ["files", ...segments.slice(0, -1)],
|
|
9791
|
+
title: name,
|
|
9792
|
+
content,
|
|
9793
|
+
publishedAt: parseEpochMs(asString(entry.ModTime)),
|
|
9794
|
+
metadata: {
|
|
9795
|
+
...asString(entry.MimeType) !== undefined ? { mimeType: asString(entry.MimeType) } : {},
|
|
9796
|
+
size
|
|
9797
|
+
}
|
|
9798
|
+
});
|
|
9799
|
+
}
|
|
9800
|
+
const warnings = [];
|
|
9801
|
+
if (readFailures > 0)
|
|
9802
|
+
warnings.push(`${readFailures} file(s) failed to read`);
|
|
9803
|
+
if (skipped > 0)
|
|
9804
|
+
warnings.push(`${skipped} file(s) skipped (non-text or oversized)`);
|
|
9805
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
9806
|
+
}
|
|
9807
|
+
}
|
|
9808
|
+
function classifyFailure(stderr) {
|
|
9809
|
+
const lower = stderr.toLowerCase();
|
|
9810
|
+
if (lower.includes("didn't find section") || lower.includes("config file") || lower.includes("no remotes")) {
|
|
9811
|
+
return "not-configured";
|
|
9812
|
+
}
|
|
9813
|
+
if (lower.includes("oauth") || lower.includes("token") || lower.includes("unauthorized") || lower.includes("401")) {
|
|
9814
|
+
return "auth";
|
|
9815
|
+
}
|
|
9816
|
+
if (lower.includes("permission") || lower.includes("403") || lower.includes("forbidden"))
|
|
9817
|
+
return "permission";
|
|
9818
|
+
return "api-error";
|
|
9819
|
+
}
|
|
9820
|
+
function shortFailure(result) {
|
|
9821
|
+
return `rclone exited with code ${result.code ?? "unknown"}`;
|
|
9822
|
+
}
|
|
9823
|
+
function runBinary(binary, args, timeoutMs) {
|
|
9824
|
+
return new Promise((resolvePromise) => {
|
|
9825
|
+
const child = spawn5(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
9826
|
+
let stdout = "";
|
|
9827
|
+
let stderr = "";
|
|
9828
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
|
|
9829
|
+
child.stdout.on("data", (chunk) => {
|
|
9830
|
+
stdout += chunk.toString("utf8");
|
|
9831
|
+
});
|
|
9832
|
+
child.stderr.on("data", (chunk) => {
|
|
9833
|
+
stderr += chunk.toString("utf8");
|
|
9834
|
+
});
|
|
9835
|
+
child.on("error", () => {
|
|
9836
|
+
clearTimeout(timer);
|
|
9837
|
+
resolvePromise({ ok: false, stdout, stderr, code: null });
|
|
9838
|
+
});
|
|
9839
|
+
child.on("close", (code) => {
|
|
9840
|
+
clearTimeout(timer);
|
|
9841
|
+
resolvePromise({ ok: code === 0, stdout, stderr, code });
|
|
9842
|
+
});
|
|
9843
|
+
});
|
|
9844
|
+
}
|
|
9845
|
+
var DEFAULT_BINARY2 = "rclone", DEFAULT_EXTENSIONS, DEFAULT_EXPORT_FORMATS = "txt,csv", DEFAULT_MAX_DOCUMENTS3 = 200, DEFAULT_MAX_BYTES_PER_FILE, DEFAULT_TIMEOUT_MS4 = 60000, MAX_CONTENT_CHARS2 = 1e5;
|
|
9846
|
+
var init_rclone_connector = __esm(() => {
|
|
9847
|
+
DEFAULT_EXTENSIONS = [".txt", ".md", ".csv", ".json", ".html", ".rst", ".org"];
|
|
9848
|
+
DEFAULT_MAX_BYTES_PER_FILE = 2 * 1024 * 1024;
|
|
9849
|
+
});
|
|
9850
|
+
|
|
9851
|
+
// src/datasource/skills/gdrive/skill.ts
|
|
9852
|
+
var GDRIVE_SKILL_DEFINITION, GDriveSkill;
|
|
9853
|
+
var init_skill2 = __esm(() => {
|
|
9854
|
+
init_connector_skill();
|
|
9855
|
+
init_connector3();
|
|
9856
|
+
GDRIVE_SKILL_DEFINITION = {
|
|
9857
|
+
skillName: "gdrive",
|
|
9858
|
+
skillType: "google-drive",
|
|
9859
|
+
description: "Google Drive datasource",
|
|
9860
|
+
capabilities: ["documents", "api", "polling"],
|
|
9861
|
+
defaultTags: ["gdrive", "documents", "pii"],
|
|
9862
|
+
contentType: "document",
|
|
9863
|
+
manifestDescription: "Search indexed Google Drive documents (Docs, Sheets, plain text). Use for questions about files stored in the authorized Drive account or folder.",
|
|
9864
|
+
manifestNotes: [
|
|
9865
|
+
"Shared-drive permissions are enforced by the server-configured OAuth token; unexported binary formats are not indexed."
|
|
9866
|
+
]
|
|
9867
|
+
};
|
|
9868
|
+
GDriveSkill = class GDriveSkill extends ConnectorDatasourceSkill {
|
|
9869
|
+
constructor(options = {}) {
|
|
9870
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
9871
|
+
super(GDRIVE_SKILL_DEFINITION, {
|
|
9872
|
+
...rest,
|
|
9873
|
+
connector: connector ?? new GDriveConnector(connectorOptions ?? {})
|
|
9874
|
+
});
|
|
9875
|
+
}
|
|
9876
|
+
};
|
|
9877
|
+
});
|
|
9878
|
+
|
|
9879
|
+
// src/datasource/skills/gdrive/index.ts
|
|
9880
|
+
var init_gdrive = __esm(() => {
|
|
9881
|
+
init_connector3();
|
|
9882
|
+
init_rclone_connector();
|
|
9883
|
+
init_skill2();
|
|
9884
|
+
});
|
|
9885
|
+
|
|
9886
|
+
// src/datasource/skills/github/connector.ts
|
|
9887
|
+
class GitHubConnector {
|
|
9888
|
+
options;
|
|
9889
|
+
constructor(options = {}) {
|
|
9890
|
+
this.options = options;
|
|
9891
|
+
}
|
|
9892
|
+
async fetch(signal) {
|
|
9893
|
+
const repos = this.options.repos ?? [];
|
|
9894
|
+
if (repos.length === 0)
|
|
9895
|
+
return { ok: false, reason: "not-configured", message: "no repositories configured" };
|
|
9896
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV3);
|
|
9897
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL3;
|
|
9898
|
+
const request = {
|
|
9899
|
+
headers: {
|
|
9900
|
+
Accept: "application/vnd.github+json",
|
|
9901
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
9902
|
+
"User-Agent": "autorag-datasource",
|
|
9903
|
+
...token !== undefined ? { Authorization: `Bearer ${token}` } : {}
|
|
9904
|
+
},
|
|
9905
|
+
timeoutMs: this.options.timeoutMs,
|
|
9906
|
+
fetchImpl: this.options.fetchImpl,
|
|
9907
|
+
signal
|
|
9908
|
+
};
|
|
9909
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES3;
|
|
9910
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS4;
|
|
9911
|
+
const documents = [];
|
|
9912
|
+
const warnings = [];
|
|
9913
|
+
for (const repo of repos) {
|
|
9914
|
+
if (documents.length >= maxDocuments)
|
|
9915
|
+
break;
|
|
9916
|
+
const [owner, name] = repo.split("/", 2);
|
|
9917
|
+
if (owner === undefined || name === undefined || owner.length === 0 || name.length === 0) {
|
|
9918
|
+
warnings.push(`repository entry ${repos.indexOf(repo) + 1} is not owner/repo`);
|
|
9919
|
+
continue;
|
|
9920
|
+
}
|
|
9921
|
+
for (let page = 1;page <= maxPages && documents.length < maxDocuments; page += 1) {
|
|
9922
|
+
const url = `${baseUrl}/repos/${owner}/${name}/issues?state=all&per_page=100&page=${page}`;
|
|
9923
|
+
const result = await httpJson(url, request);
|
|
9924
|
+
if (!result.ok) {
|
|
9925
|
+
if (result.reason === "auth")
|
|
9926
|
+
return { ok: false, reason: "auth", message: "github: unauthorized" };
|
|
9927
|
+
if (result.reason === "rate-limited" || result.reason === "permission") {
|
|
9928
|
+
return { ok: false, reason: "rate-limited", message: "github: rate limited or forbidden" };
|
|
9929
|
+
}
|
|
9930
|
+
warnings.push(`repo ${owner}-${name} fetch failed: ${result.message}`);
|
|
9931
|
+
break;
|
|
9932
|
+
}
|
|
9933
|
+
const items = asArray(result.json);
|
|
9934
|
+
if (items.length === 0)
|
|
9935
|
+
break;
|
|
9936
|
+
for (const raw of items) {
|
|
9937
|
+
if (documents.length >= maxDocuments)
|
|
9938
|
+
break;
|
|
9939
|
+
const item = asRecord2(raw);
|
|
9940
|
+
const number = asNumber(item?.number);
|
|
9941
|
+
const title = asString(item?.title);
|
|
9942
|
+
if (item === undefined || number === undefined || title === undefined)
|
|
9943
|
+
continue;
|
|
9944
|
+
const isPull = asRecord2(item.pull_request) !== undefined;
|
|
9945
|
+
const body = asString(item.body) ?? "";
|
|
9946
|
+
const labels = asArray(item.labels).map((label) => asString(asRecord2(label)?.name)).filter((label) => label !== undefined);
|
|
9947
|
+
documents.push({
|
|
9948
|
+
docId: `${owner}-${name}-${number}`,
|
|
9949
|
+
hierarchy: [owner, name, isPull ? "pulls" : "issues"],
|
|
9950
|
+
title: `#${number} ${title}`,
|
|
9951
|
+
content: body.length > 0 ? `${title}
|
|
9952
|
+
|
|
9953
|
+
${body}` : title,
|
|
9954
|
+
publishedAt: parseEpochMs(item.updated_at),
|
|
9955
|
+
metadata: {
|
|
9956
|
+
state: asString(item.state) ?? "unknown",
|
|
9957
|
+
number,
|
|
9958
|
+
kind: isPull ? "pull" : "issue",
|
|
9959
|
+
labels
|
|
9960
|
+
}
|
|
9961
|
+
});
|
|
9962
|
+
}
|
|
9963
|
+
if (items.length < 100)
|
|
9964
|
+
break;
|
|
9965
|
+
}
|
|
9966
|
+
}
|
|
9967
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9970
|
+
var DEFAULT_BASE_URL3 = "https://api.github.com", DEFAULT_TOKEN_ENV3 = "GITHUB_TOKEN", DEFAULT_MAX_PAGES3 = 10, DEFAULT_MAX_DOCUMENTS4 = 500;
|
|
9971
|
+
var init_connector4 = () => {};
|
|
9972
|
+
|
|
9973
|
+
// src/datasource/skills/github/skill.ts
|
|
9974
|
+
var GITHUB_SKILL_DEFINITION, GitHubSkill;
|
|
9975
|
+
var init_skill3 = __esm(() => {
|
|
9976
|
+
init_connector_skill();
|
|
9977
|
+
init_connector4();
|
|
9978
|
+
GITHUB_SKILL_DEFINITION = {
|
|
9979
|
+
skillName: "github",
|
|
9980
|
+
skillType: "github-issues",
|
|
9981
|
+
description: "GitHub Issues/PRs datasource",
|
|
9982
|
+
capabilities: ["issues", "api", "polling"],
|
|
9983
|
+
defaultTags: ["github", "issues"],
|
|
9984
|
+
contentType: "issue",
|
|
9985
|
+
manifestDescription: "Search indexed GitHub issues and pull requests for the configured repositories. Use for questions about bug reports, feature requests, and review discussions.",
|
|
9986
|
+
manifestNotes: ["Repository visibility is bounded by the server-configured token scopes."]
|
|
9987
|
+
};
|
|
9988
|
+
GitHubSkill = class GitHubSkill extends ConnectorDatasourceSkill {
|
|
9989
|
+
constructor(options = {}) {
|
|
9990
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
9991
|
+
super(GITHUB_SKILL_DEFINITION, {
|
|
9992
|
+
...rest,
|
|
9993
|
+
connector: connector ?? new GitHubConnector(connectorOptions ?? {})
|
|
9994
|
+
});
|
|
9995
|
+
}
|
|
9996
|
+
};
|
|
9997
|
+
});
|
|
9998
|
+
|
|
9999
|
+
// src/datasource/skills/github/index.ts
|
|
10000
|
+
var init_github = __esm(() => {
|
|
10001
|
+
init_connector4();
|
|
10002
|
+
init_skill3();
|
|
10003
|
+
});
|
|
10004
|
+
|
|
10005
|
+
// src/datasource/skills/gmail/himalaya-connector.ts
|
|
10006
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
10007
|
+
|
|
10008
|
+
class HimalayaConnector {
|
|
10009
|
+
options;
|
|
10010
|
+
runner;
|
|
10011
|
+
constructor(options = {}) {
|
|
10012
|
+
this.options = options;
|
|
10013
|
+
this.runner = options.runner ?? ((args, timeoutMs) => runBinary2(options.binaryPath ?? DEFAULT_BINARY3, args, timeoutMs));
|
|
10014
|
+
}
|
|
10015
|
+
async fetch() {
|
|
10016
|
+
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS5;
|
|
10017
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS5;
|
|
10018
|
+
const accountArgs = this.options.account !== undefined ? ["--account", this.options.account] : [];
|
|
10019
|
+
const folderArgs = this.options.folder !== undefined ? ["--folder", this.options.folder] : [];
|
|
10020
|
+
const listArgs = [
|
|
10021
|
+
"envelope",
|
|
10022
|
+
"list",
|
|
10023
|
+
...accountArgs,
|
|
10024
|
+
...folderArgs,
|
|
10025
|
+
"--page-size",
|
|
10026
|
+
String(this.options.pageSize ?? DEFAULT_PAGE_SIZE),
|
|
10027
|
+
"--output",
|
|
10028
|
+
"json"
|
|
10029
|
+
];
|
|
10030
|
+
let listResult;
|
|
10031
|
+
try {
|
|
10032
|
+
listResult = await this.runner(listArgs, timeoutMs);
|
|
10033
|
+
} catch {
|
|
10034
|
+
return { ok: false, reason: "unavailable", message: "himalaya binary not found or failed to spawn" };
|
|
10035
|
+
}
|
|
10036
|
+
if (!listResult.ok) {
|
|
10037
|
+
return { ok: false, reason: classifyFailure2(listResult.stderr), message: shortFailure2(listResult) };
|
|
10038
|
+
}
|
|
10039
|
+
let envelopes;
|
|
10040
|
+
try {
|
|
10041
|
+
envelopes = asArray(JSON.parse(listResult.stdout));
|
|
10042
|
+
} catch {
|
|
10043
|
+
return { ok: false, reason: "invalid-data", message: "envelope listing was not valid JSON" };
|
|
10044
|
+
}
|
|
10045
|
+
const account = this.options.account ?? "default";
|
|
10046
|
+
const folder = this.options.folder ?? "INBOX";
|
|
10047
|
+
const documents = [];
|
|
10048
|
+
let readFailures = 0;
|
|
10049
|
+
for (const raw of envelopes.slice(0, maxDocuments)) {
|
|
10050
|
+
const envelope = asRecord2(raw);
|
|
10051
|
+
const id = asString(envelope?.id);
|
|
10052
|
+
if (envelope === undefined || id === undefined)
|
|
10053
|
+
continue;
|
|
10054
|
+
const subject = asString(envelope.subject) ?? "(no subject)";
|
|
10055
|
+
const from = asRecord2(envelope.from);
|
|
10056
|
+
const fromText = [asString(from?.name), asString(from?.addr)].filter(Boolean).join(" ").trim();
|
|
10057
|
+
let body = "";
|
|
10058
|
+
try {
|
|
10059
|
+
const readResult = await this.runner(["message", "read", ...accountArgs, ...folderArgs, id], timeoutMs);
|
|
10060
|
+
if (readResult.ok)
|
|
10061
|
+
body = readResult.stdout.trim().slice(0, MAX_BODY_CHARS);
|
|
10062
|
+
else
|
|
10063
|
+
readFailures += 1;
|
|
10064
|
+
} catch {
|
|
10065
|
+
readFailures += 1;
|
|
10066
|
+
}
|
|
10067
|
+
const headerBlock = [
|
|
10068
|
+
`Subject: ${subject}`,
|
|
10069
|
+
...fromText.length > 0 ? [`From: ${fromText}`] : [],
|
|
10070
|
+
...asString(envelope.date) !== undefined ? [`Date: ${asString(envelope.date)}`] : []
|
|
10071
|
+
].join(`
|
|
10072
|
+
`);
|
|
10073
|
+
documents.push({
|
|
10074
|
+
docId: `${account}-${folder}-${id}`,
|
|
10075
|
+
hierarchy: ["accounts", account, folder],
|
|
10076
|
+
title: subject,
|
|
10077
|
+
content: body.length > 0 ? `${headerBlock}
|
|
10078
|
+
|
|
10079
|
+
${body}` : headerBlock,
|
|
10080
|
+
publishedAt: parseHimalayaDate(asString(envelope.date)),
|
|
10081
|
+
metadata: {
|
|
10082
|
+
account,
|
|
10083
|
+
folder,
|
|
10084
|
+
messageId: id,
|
|
10085
|
+
...asArray(envelope.flags).length > 0 ? {
|
|
10086
|
+
flags: asArray(envelope.flags).map((flag) => asString(flag)).filter(Boolean)
|
|
10087
|
+
} : {}
|
|
10088
|
+
}
|
|
10089
|
+
});
|
|
10090
|
+
}
|
|
10091
|
+
const warnings = readFailures > 0 ? [`${readFailures} message(s) failed to read`] : undefined;
|
|
10092
|
+
return { ok: true, documents, ...warnings !== undefined ? { warnings } : {} };
|
|
10093
|
+
}
|
|
10094
|
+
}
|
|
10095
|
+
function parseHimalayaDate(value) {
|
|
10096
|
+
if (value === undefined || value.length === 0)
|
|
10097
|
+
return;
|
|
10098
|
+
const parsed = Date.parse(value.replace(" ", "T"));
|
|
10099
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
10100
|
+
}
|
|
10101
|
+
function classifyFailure2(stderr) {
|
|
10102
|
+
const lower = stderr.toLowerCase();
|
|
10103
|
+
if (lower.includes("cannot find") && lower.includes("account"))
|
|
10104
|
+
return "not-configured";
|
|
10105
|
+
if (lower.includes("auth") || lower.includes("login") || lower.includes("credential"))
|
|
10106
|
+
return "auth";
|
|
10107
|
+
return "api-error";
|
|
10108
|
+
}
|
|
10109
|
+
function shortFailure2(result) {
|
|
10110
|
+
return `himalaya exited with code ${result.code ?? "unknown"}`;
|
|
10111
|
+
}
|
|
10112
|
+
function runBinary2(binary, args, timeoutMs) {
|
|
10113
|
+
return new Promise((resolvePromise) => {
|
|
10114
|
+
const child = spawn6(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
10115
|
+
let stdout = "";
|
|
10116
|
+
let stderr = "";
|
|
10117
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
|
|
10118
|
+
child.stdout.on("data", (chunk) => {
|
|
10119
|
+
stdout += chunk.toString("utf8");
|
|
10120
|
+
});
|
|
10121
|
+
child.stderr.on("data", (chunk) => {
|
|
10122
|
+
stderr += chunk.toString("utf8");
|
|
10123
|
+
});
|
|
10124
|
+
child.on("error", () => {
|
|
10125
|
+
clearTimeout(timer);
|
|
10126
|
+
resolvePromise({ ok: false, stdout, stderr, code: null });
|
|
10127
|
+
});
|
|
10128
|
+
child.on("close", (code) => {
|
|
10129
|
+
clearTimeout(timer);
|
|
10130
|
+
resolvePromise({ ok: code === 0, stdout, stderr, code });
|
|
10131
|
+
});
|
|
10132
|
+
});
|
|
10133
|
+
}
|
|
10134
|
+
var DEFAULT_BINARY3 = "himalaya", DEFAULT_PAGE_SIZE = 100, DEFAULT_MAX_DOCUMENTS5 = 50, DEFAULT_TIMEOUT_MS5 = 30000, MAX_BODY_CHARS = 50000;
|
|
10135
|
+
var init_himalaya_connector = () => {};
|
|
10136
|
+
|
|
10137
|
+
// src/datasource/skills/gmail/connector.ts
|
|
10138
|
+
class GmailConnector {
|
|
10139
|
+
options;
|
|
10140
|
+
constructor(options = {}) {
|
|
10141
|
+
this.options = options;
|
|
10142
|
+
}
|
|
10143
|
+
async fetch(signal) {
|
|
10144
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV4);
|
|
10145
|
+
if (token === undefined)
|
|
10146
|
+
return { ok: false, reason: "not-configured", message: "auth token not configured" };
|
|
10147
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL4;
|
|
10148
|
+
const request = {
|
|
10149
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
10150
|
+
timeoutMs: this.options.timeoutMs,
|
|
10151
|
+
fetchImpl: this.options.fetchImpl,
|
|
10152
|
+
signal
|
|
10153
|
+
};
|
|
10154
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES4;
|
|
10155
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS6;
|
|
10156
|
+
const messageIds = [];
|
|
10157
|
+
let pageToken;
|
|
10158
|
+
for (let page = 0;page < maxPages && messageIds.length < maxDocuments; page += 1) {
|
|
10159
|
+
const url = new URL(`${baseUrl}/users/me/messages`);
|
|
10160
|
+
url.searchParams.set("maxResults", "100");
|
|
10161
|
+
for (const labelId of this.options.labelIds ?? [])
|
|
10162
|
+
url.searchParams.append("labelIds", labelId);
|
|
10163
|
+
if (this.options.query !== undefined)
|
|
10164
|
+
url.searchParams.set("q", this.options.query);
|
|
10165
|
+
if (pageToken !== undefined)
|
|
10166
|
+
url.searchParams.set("pageToken", pageToken);
|
|
10167
|
+
const result = await httpJson(url.toString(), request);
|
|
10168
|
+
if (!result.ok)
|
|
10169
|
+
return { ok: false, reason: result.reason, message: `message list failed: ${result.message}` };
|
|
10170
|
+
const envelope = asRecord2(result.json);
|
|
10171
|
+
if (envelope === undefined)
|
|
10172
|
+
return { ok: false, reason: "invalid-data", message: "message list malformed" };
|
|
10173
|
+
for (const raw of asArray(envelope.messages)) {
|
|
10174
|
+
const id = asString(asRecord2(raw)?.id);
|
|
10175
|
+
if (id !== undefined)
|
|
10176
|
+
messageIds.push(id);
|
|
10177
|
+
}
|
|
10178
|
+
pageToken = asString(envelope.nextPageToken);
|
|
10179
|
+
if (pageToken === undefined || pageToken.length === 0)
|
|
10180
|
+
break;
|
|
10181
|
+
}
|
|
10182
|
+
const documents = [];
|
|
10183
|
+
let failedFetches = 0;
|
|
10184
|
+
for (const id of messageIds.slice(0, maxDocuments)) {
|
|
10185
|
+
const result = await httpJson(`${baseUrl}/users/me/messages/${id}?format=full`, request);
|
|
10186
|
+
if (!result.ok) {
|
|
10187
|
+
if (result.reason === "auth")
|
|
10188
|
+
return { ok: false, reason: "auth", message: "gmail: unauthorized" };
|
|
10189
|
+
if (result.reason === "rate-limited") {
|
|
10190
|
+
return { ok: false, reason: "rate-limited", message: "gmail: rate limited" };
|
|
10191
|
+
}
|
|
10192
|
+
failedFetches += 1;
|
|
10193
|
+
continue;
|
|
10194
|
+
}
|
|
10195
|
+
const message = asRecord2(result.json);
|
|
10196
|
+
if (message === undefined) {
|
|
10197
|
+
failedFetches += 1;
|
|
10198
|
+
continue;
|
|
10199
|
+
}
|
|
10200
|
+
const payload = asRecord2(message.payload);
|
|
10201
|
+
const headers = headerMap(payload);
|
|
10202
|
+
const subject = headers.get("subject") ?? "(no subject)";
|
|
10203
|
+
const body = (extractBody(payload) ?? asString(message.snippet) ?? "").slice(0, MAX_BODY_CHARS2);
|
|
10204
|
+
const labelIds = asArray(message.labelIds).map((label) => asString(label)).filter((label) => label !== undefined);
|
|
10205
|
+
const internalDate = asString(message.internalDate);
|
|
10206
|
+
const publishedAt = internalDate !== undefined && /^\d+$/u.test(internalDate) ? Number(internalDate) : undefined;
|
|
10207
|
+
const headerBlock = [
|
|
10208
|
+
`Subject: ${subject}`,
|
|
10209
|
+
...headers.has("from") ? [`From: ${headers.get("from")}`] : [],
|
|
10210
|
+
...headers.has("to") ? [`To: ${headers.get("to")}`] : [],
|
|
10211
|
+
...headers.has("date") ? [`Date: ${headers.get("date")}`] : []
|
|
10212
|
+
].join(`
|
|
10213
|
+
`);
|
|
10214
|
+
documents.push({
|
|
10215
|
+
docId: id,
|
|
10216
|
+
hierarchy: ["labels", this.options.labelIds?.[0] ?? "all"],
|
|
10217
|
+
title: subject,
|
|
10218
|
+
content: `${headerBlock}
|
|
10219
|
+
|
|
10220
|
+
${body}`.trim(),
|
|
10221
|
+
...publishedAt !== undefined ? { publishedAt } : {},
|
|
10222
|
+
metadata: {
|
|
10223
|
+
...asString(message.threadId) !== undefined ? { threadId: asString(message.threadId) } : {},
|
|
10224
|
+
labelIds
|
|
10225
|
+
}
|
|
10226
|
+
});
|
|
10227
|
+
}
|
|
10228
|
+
const warnings = failedFetches > 0 ? [`${failedFetches} message(s) failed to fetch`] : undefined;
|
|
10229
|
+
return { ok: true, documents, ...warnings !== undefined ? { warnings } : {} };
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10232
|
+
function headerMap(payload) {
|
|
10233
|
+
const map = new Map;
|
|
10234
|
+
for (const raw of asArray(payload?.headers)) {
|
|
10235
|
+
const header = asRecord2(raw);
|
|
10236
|
+
const name = asString(header?.name)?.toLowerCase();
|
|
10237
|
+
const value = asString(header?.value);
|
|
10238
|
+
if (name !== undefined && value !== undefined && !map.has(name))
|
|
10239
|
+
map.set(name, value);
|
|
10240
|
+
}
|
|
10241
|
+
return map;
|
|
10242
|
+
}
|
|
10243
|
+
function extractBody(payload) {
|
|
10244
|
+
if (payload === undefined)
|
|
10245
|
+
return;
|
|
10246
|
+
const mimeType = asString(payload.mimeType);
|
|
10247
|
+
const data = asString(asRecord2(payload.body)?.data);
|
|
10248
|
+
if (mimeType === "text/plain" && data !== undefined)
|
|
10249
|
+
return decodeBase64Url(data);
|
|
10250
|
+
const collected = [];
|
|
10251
|
+
for (const raw of asArray(payload.parts)) {
|
|
10252
|
+
const part = asRecord2(raw);
|
|
10253
|
+
const text = extractBody(part);
|
|
10254
|
+
if (text !== undefined && text.length > 0)
|
|
10255
|
+
collected.push(text);
|
|
10256
|
+
}
|
|
10257
|
+
if (collected.length > 0)
|
|
10258
|
+
return collected.join(`
|
|
10259
|
+
`);
|
|
10260
|
+
return data !== undefined ? decodeBase64Url(data) : undefined;
|
|
10261
|
+
}
|
|
10262
|
+
function decodeBase64Url(data) {
|
|
10263
|
+
try {
|
|
10264
|
+
return Buffer.from(data, "base64url").toString("utf8");
|
|
10265
|
+
} catch {
|
|
10266
|
+
return "";
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
var DEFAULT_BASE_URL4 = "https://gmail.googleapis.com/gmail/v1", DEFAULT_TOKEN_ENV4 = "GMAIL_ACCESS_TOKEN", DEFAULT_MAX_PAGES4 = 10, DEFAULT_MAX_DOCUMENTS6 = 500, MAX_BODY_CHARS2 = 50000;
|
|
10270
|
+
var init_connector5 = () => {};
|
|
10271
|
+
|
|
10272
|
+
// src/datasource/skills/gmail/skill.ts
|
|
10273
|
+
var GMAIL_SKILL_DEFINITION, GmailSkill;
|
|
10274
|
+
var init_skill4 = __esm(() => {
|
|
10275
|
+
init_connector_skill();
|
|
10276
|
+
init_connector5();
|
|
10277
|
+
GMAIL_SKILL_DEFINITION = {
|
|
10278
|
+
skillName: "gmail",
|
|
10279
|
+
skillType: "gmail-account",
|
|
10280
|
+
description: "Gmail datasource",
|
|
10281
|
+
capabilities: ["email", "api", "polling"],
|
|
10282
|
+
defaultTags: ["gmail", "email", "pii"],
|
|
10283
|
+
contentType: "email",
|
|
10284
|
+
manifestDescription: "Search indexed Gmail messages from the authorized account and labels. Use for questions about email conversations, decisions, or attachments discussed over email.",
|
|
10285
|
+
manifestNotes: ["Label and folder visibility is bounded by the server-configured OAuth token."]
|
|
10286
|
+
};
|
|
10287
|
+
GmailSkill = class GmailSkill extends ConnectorDatasourceSkill {
|
|
10288
|
+
constructor(options = {}) {
|
|
10289
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
10290
|
+
super(GMAIL_SKILL_DEFINITION, {
|
|
10291
|
+
...rest,
|
|
10292
|
+
connector: connector ?? new GmailConnector(connectorOptions ?? {})
|
|
10293
|
+
});
|
|
10294
|
+
}
|
|
10295
|
+
};
|
|
10296
|
+
});
|
|
10297
|
+
|
|
10298
|
+
// src/datasource/skills/gmail/index.ts
|
|
10299
|
+
var init_gmail = __esm(() => {
|
|
10300
|
+
init_connector5();
|
|
10301
|
+
init_himalaya_connector();
|
|
10302
|
+
init_skill4();
|
|
10303
|
+
});
|
|
10304
|
+
|
|
10305
|
+
// src/datasource/skills/mail-export/connector.ts
|
|
10306
|
+
import { readdir, readFile as readFile4, stat as stat2 } from "node:fs/promises";
|
|
10307
|
+
import { basename as basename6, dirname as dirname11, extname as extname7, join as join18 } from "node:path";
|
|
10308
|
+
import { simpleParser as simpleParser2 } from "mailparser";
|
|
10309
|
+
|
|
10310
|
+
class MailExportConnector {
|
|
10311
|
+
options;
|
|
10312
|
+
constructor(options = {}) {
|
|
10313
|
+
this.options = options;
|
|
10314
|
+
}
|
|
10315
|
+
async fetch() {
|
|
10316
|
+
const paths = this.options.paths ?? [];
|
|
10317
|
+
if (paths.length === 0)
|
|
10318
|
+
return { ok: false, reason: "not-configured", message: "no mail export paths configured" };
|
|
10319
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS7;
|
|
10320
|
+
const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE2;
|
|
10321
|
+
const files = [];
|
|
10322
|
+
let missingPaths = 0;
|
|
10323
|
+
for (const path of paths) {
|
|
10324
|
+
try {
|
|
10325
|
+
const info = await stat2(path);
|
|
10326
|
+
if (info.isDirectory()) {
|
|
10327
|
+
const entries = await readdir(path, { recursive: true, withFileTypes: true });
|
|
10328
|
+
for (const entry of entries) {
|
|
10329
|
+
if (!entry.isFile())
|
|
10330
|
+
continue;
|
|
10331
|
+
const extension = extname7(entry.name).toLowerCase();
|
|
10332
|
+
if (extension === ".eml" || extension === ".mbox") {
|
|
10333
|
+
files.push(join18(entry.parentPath, entry.name));
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10336
|
+
} else if (info.isFile()) {
|
|
10337
|
+
files.push(path);
|
|
10338
|
+
}
|
|
10339
|
+
} catch {
|
|
10340
|
+
missingPaths += 1;
|
|
10341
|
+
}
|
|
10342
|
+
}
|
|
10343
|
+
if (missingPaths === paths.length) {
|
|
10344
|
+
return { ok: false, reason: "unavailable", message: "no configured mail export paths were readable" };
|
|
10345
|
+
}
|
|
10346
|
+
const documents = [];
|
|
10347
|
+
const warnings = [];
|
|
10348
|
+
if (missingPaths > 0)
|
|
10349
|
+
warnings.push(`${missingPaths} configured path(s) were unreadable`);
|
|
10350
|
+
let parseFailures = 0;
|
|
10351
|
+
let skippedLarge = 0;
|
|
10352
|
+
for (const file of files.sort()) {
|
|
10353
|
+
if (documents.length >= maxDocuments)
|
|
10354
|
+
break;
|
|
10355
|
+
let bytes;
|
|
10356
|
+
try {
|
|
10357
|
+
const info = await stat2(file);
|
|
10358
|
+
if (info.size > maxBytes) {
|
|
10359
|
+
skippedLarge += 1;
|
|
10360
|
+
continue;
|
|
10361
|
+
}
|
|
10362
|
+
bytes = await readFile4(file);
|
|
10363
|
+
} catch {
|
|
10364
|
+
parseFailures += 1;
|
|
10365
|
+
continue;
|
|
10366
|
+
}
|
|
10367
|
+
const stem = basename6(file, extname7(file));
|
|
10368
|
+
const mailboxDir = basename6(dirname11(file));
|
|
10369
|
+
const hierarchy = ["mailboxes", mailboxDir, stem];
|
|
10370
|
+
const extension = extname7(file).toLowerCase();
|
|
10371
|
+
const rawMessages = extension === ".mbox" ? splitMbox(bytes.toString("utf8")) : [bytes.toString("utf8")];
|
|
10372
|
+
for (const [index, raw] of rawMessages.entries()) {
|
|
10373
|
+
if (documents.length >= maxDocuments)
|
|
10374
|
+
break;
|
|
10375
|
+
try {
|
|
10376
|
+
const parsed = await simpleParser2(raw);
|
|
10377
|
+
const subject = parsed.subject ?? "(no subject)";
|
|
10378
|
+
const from = Array.isArray(parsed.from) ? parsed.from[0]?.text : parsed.from?.text;
|
|
10379
|
+
const to = Array.isArray(parsed.to) ? parsed.to[0]?.text : parsed.to?.text;
|
|
10380
|
+
const body = (parsed.text ?? "").trim().slice(0, MAX_BODY_CHARS3);
|
|
10381
|
+
const headerBlock = [
|
|
10382
|
+
`Subject: ${subject}`,
|
|
10383
|
+
...from !== undefined ? [`From: ${from}`] : [],
|
|
10384
|
+
...to !== undefined ? [`To: ${to}`] : [],
|
|
10385
|
+
...parsed.date !== undefined ? [`Date: ${parsed.date.toISOString()}`] : []
|
|
10386
|
+
].join(`
|
|
10387
|
+
`);
|
|
10388
|
+
const content = `${headerBlock}
|
|
10389
|
+
|
|
10390
|
+
${body}`.trim();
|
|
10391
|
+
if (content.length === 0)
|
|
10392
|
+
continue;
|
|
10393
|
+
documents.push({
|
|
10394
|
+
docId: extension === ".mbox" ? `${stem}-${index + 1}` : stem,
|
|
10395
|
+
hierarchy,
|
|
10396
|
+
title: subject,
|
|
10397
|
+
content,
|
|
10398
|
+
...parsed.date !== undefined ? { publishedAt: parsed.date.getTime() } : {},
|
|
10399
|
+
metadata: {
|
|
10400
|
+
path: file,
|
|
10401
|
+
subject,
|
|
10402
|
+
...from !== undefined ? { from } : {},
|
|
10403
|
+
messageIndex: index
|
|
10404
|
+
}
|
|
10405
|
+
});
|
|
10406
|
+
} catch {
|
|
10407
|
+
parseFailures += 1;
|
|
10408
|
+
}
|
|
10409
|
+
}
|
|
10410
|
+
}
|
|
10411
|
+
if (skippedLarge > 0)
|
|
10412
|
+
warnings.push(`${skippedLarge} file(s) exceeded the size limit and were skipped`);
|
|
10413
|
+
if (parseFailures > 0)
|
|
10414
|
+
warnings.push(`${parseFailures} message(s) failed to parse`);
|
|
10415
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
10416
|
+
}
|
|
10417
|
+
}
|
|
10418
|
+
function splitMbox(text) {
|
|
10419
|
+
const lines = text.split(/\r?\n/u);
|
|
10420
|
+
const messages = [];
|
|
10421
|
+
let current = [];
|
|
10422
|
+
for (const line of lines) {
|
|
10423
|
+
if (MBOX_SEPARATOR.test(line) && line.startsWith("From ")) {
|
|
10424
|
+
if (current.length > 0)
|
|
10425
|
+
messages.push(current.join(`
|
|
10426
|
+
`));
|
|
10427
|
+
current = [];
|
|
10428
|
+
continue;
|
|
10429
|
+
}
|
|
10430
|
+
current.push(line);
|
|
10431
|
+
}
|
|
10432
|
+
if (current.length > 0)
|
|
10433
|
+
messages.push(current.join(`
|
|
10434
|
+
`));
|
|
10435
|
+
return messages.filter((message) => message.trim().length > 0);
|
|
10436
|
+
}
|
|
10437
|
+
var DEFAULT_MAX_DOCUMENTS7 = 500, DEFAULT_MAX_BYTES_PER_FILE2, MAX_BODY_CHARS3 = 50000, MBOX_SEPARATOR;
|
|
10438
|
+
var init_connector6 = __esm(() => {
|
|
10439
|
+
DEFAULT_MAX_BYTES_PER_FILE2 = 50 * 1024 * 1024;
|
|
10440
|
+
MBOX_SEPARATOR = /^From .*\d{4}$/mu;
|
|
10441
|
+
});
|
|
10442
|
+
|
|
10443
|
+
// src/datasource/skills/mail-export/skill.ts
|
|
10444
|
+
var MAIL_EXPORT_SKILL_DEFINITION, MailExportSkill;
|
|
10445
|
+
var init_skill5 = __esm(() => {
|
|
10446
|
+
init_connector_skill();
|
|
10447
|
+
init_connector6();
|
|
10448
|
+
MAIL_EXPORT_SKILL_DEFINITION = {
|
|
10449
|
+
skillName: "mail-export",
|
|
10450
|
+
skillType: "mail-export",
|
|
10451
|
+
description: "Local mail export datasource",
|
|
10452
|
+
capabilities: ["email", "filesystem"],
|
|
10453
|
+
defaultTags: ["mail-export", "email", "pii"],
|
|
10454
|
+
contentType: "email",
|
|
10455
|
+
manifestDescription: "Search indexed local mail exports (mbox/eml archives). Use for questions about archived email conversations.",
|
|
10456
|
+
manifestNotes: [
|
|
10457
|
+
"Only the server-configured export paths are indexed; archives are read locally and never uploaded."
|
|
10458
|
+
]
|
|
10459
|
+
};
|
|
10460
|
+
MailExportSkill = class MailExportSkill extends ConnectorDatasourceSkill {
|
|
10461
|
+
constructor(options = {}) {
|
|
10462
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
10463
|
+
super(MAIL_EXPORT_SKILL_DEFINITION, {
|
|
10464
|
+
...rest,
|
|
10465
|
+
connector: connector ?? new MailExportConnector(connectorOptions ?? {})
|
|
10466
|
+
});
|
|
10467
|
+
}
|
|
10468
|
+
};
|
|
10469
|
+
});
|
|
10470
|
+
|
|
10471
|
+
// src/datasource/skills/mail-export/index.ts
|
|
10472
|
+
var init_mail_export = __esm(() => {
|
|
10473
|
+
init_connector6();
|
|
10474
|
+
init_skill5();
|
|
10475
|
+
});
|
|
10476
|
+
|
|
10477
|
+
// src/datasource/skills/notion/connector.ts
|
|
10478
|
+
class NotionConnector {
|
|
10479
|
+
options;
|
|
10480
|
+
constructor(options = {}) {
|
|
10481
|
+
this.options = options;
|
|
10482
|
+
}
|
|
10483
|
+
async fetch(signal) {
|
|
10484
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV5);
|
|
10485
|
+
if (token === undefined)
|
|
10486
|
+
return { ok: false, reason: "not-configured", message: "auth token not configured" };
|
|
10487
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL5;
|
|
10488
|
+
const request = {
|
|
10489
|
+
headers: {
|
|
10490
|
+
Authorization: `Bearer ${token}`,
|
|
10491
|
+
"Notion-Version": NOTION_VERSION,
|
|
10492
|
+
"Content-Type": "application/json"
|
|
10493
|
+
},
|
|
10494
|
+
timeoutMs: this.options.timeoutMs,
|
|
10495
|
+
fetchImpl: this.options.fetchImpl,
|
|
10496
|
+
signal
|
|
10497
|
+
};
|
|
10498
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES5;
|
|
10499
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS8;
|
|
10500
|
+
const entries = [];
|
|
10501
|
+
let cursor;
|
|
10502
|
+
for (let page = 0;page < maxPages; page += 1) {
|
|
10503
|
+
const body = JSON.stringify({ page_size: 100, ...cursor !== undefined ? { start_cursor: cursor } : {} });
|
|
10504
|
+
const result = await httpJson(`${baseUrl}/search`, { ...request, method: "POST", body });
|
|
10505
|
+
if (!result.ok)
|
|
10506
|
+
return { ok: false, reason: result.reason, message: `search failed: ${result.message}` };
|
|
10507
|
+
const envelope = asRecord2(result.json);
|
|
10508
|
+
if (envelope === undefined)
|
|
10509
|
+
return { ok: false, reason: "invalid-data", message: "search response malformed" };
|
|
10510
|
+
for (const raw of asArray(envelope.results)) {
|
|
10511
|
+
const entry = asRecord2(raw);
|
|
10512
|
+
if (entry !== undefined)
|
|
10513
|
+
entries.push(entry);
|
|
10514
|
+
}
|
|
10515
|
+
cursor = envelope.has_more === true ? asString(envelope.next_cursor) : undefined;
|
|
10516
|
+
if (cursor === undefined)
|
|
10517
|
+
break;
|
|
10518
|
+
}
|
|
10519
|
+
const documents = [];
|
|
10520
|
+
const warnings = [];
|
|
10521
|
+
for (const entry of entries) {
|
|
10522
|
+
if (documents.length >= maxDocuments)
|
|
10523
|
+
break;
|
|
10524
|
+
const id = asString(entry.id);
|
|
10525
|
+
if (id === undefined)
|
|
10526
|
+
continue;
|
|
10527
|
+
const objectType = asString(entry.object) ?? "page";
|
|
10528
|
+
const title = extractTitle(entry);
|
|
10529
|
+
let blockText = "";
|
|
10530
|
+
if (objectType === "page") {
|
|
10531
|
+
const blocksResult = await httpJson(`${baseUrl}/blocks/${id}/children?page_size=100`, request);
|
|
10532
|
+
if (blocksResult.ok) {
|
|
10533
|
+
blockText = extractBlockText(blocksResult.json);
|
|
10534
|
+
} else {
|
|
10535
|
+
warnings.push(`page content fetch failed: ${blocksResult.message}`);
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
const content = [title, blockText].filter((part) => part.length > 0).join(`
|
|
10539
|
+
|
|
10540
|
+
`);
|
|
10541
|
+
if (content.length === 0)
|
|
10542
|
+
continue;
|
|
10543
|
+
documents.push({
|
|
10544
|
+
docId: id,
|
|
10545
|
+
hierarchy: hierarchyFor(entry, objectType),
|
|
10546
|
+
...title.length > 0 ? { title } : {},
|
|
10547
|
+
content,
|
|
10548
|
+
publishedAt: parseEpochMs(entry.last_edited_time),
|
|
10549
|
+
metadata: {
|
|
10550
|
+
objectType,
|
|
10551
|
+
...asString(entry.last_edited_time) !== undefined ? { lastEditedTime: asString(entry.last_edited_time) } : {}
|
|
10552
|
+
}
|
|
10553
|
+
});
|
|
10554
|
+
}
|
|
10555
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
10556
|
+
}
|
|
10557
|
+
}
|
|
10558
|
+
function hierarchyFor(entry, objectType) {
|
|
10559
|
+
if (objectType === "database")
|
|
10560
|
+
return ["databases", asString(entry.id) ?? "unknown"];
|
|
10561
|
+
const parent = asRecord2(entry.parent);
|
|
10562
|
+
const parentType = asString(parent?.type);
|
|
10563
|
+
if (parentType === "database_id") {
|
|
10564
|
+
return ["databases", asString(parent?.database_id) ?? "unknown"];
|
|
10565
|
+
}
|
|
10566
|
+
if (parentType === "page_id") {
|
|
10567
|
+
return ["pages", asString(parent?.page_id) ?? "unknown"];
|
|
10568
|
+
}
|
|
10569
|
+
return ["pages"];
|
|
10570
|
+
}
|
|
10571
|
+
function extractTitle(entry) {
|
|
10572
|
+
const directTitle = plainText(entry.title);
|
|
10573
|
+
if (directTitle.length > 0)
|
|
10574
|
+
return directTitle;
|
|
10575
|
+
const properties = asRecord2(entry.properties);
|
|
10576
|
+
if (properties === undefined)
|
|
10577
|
+
return "";
|
|
10578
|
+
for (const value of Object.values(properties)) {
|
|
10579
|
+
const property = asRecord2(value);
|
|
10580
|
+
if (property === undefined || property.type !== "title")
|
|
10581
|
+
continue;
|
|
10582
|
+
const text = plainText(property.title);
|
|
10583
|
+
if (text.length > 0)
|
|
10584
|
+
return text;
|
|
10585
|
+
}
|
|
10586
|
+
return "";
|
|
10587
|
+
}
|
|
10588
|
+
function extractBlockText(json) {
|
|
10589
|
+
const envelope = asRecord2(json);
|
|
10590
|
+
const lines = [];
|
|
10591
|
+
for (const raw of asArray(envelope?.results)) {
|
|
10592
|
+
const block = asRecord2(raw);
|
|
10593
|
+
const type = asString(block?.type);
|
|
10594
|
+
if (block === undefined || type === undefined)
|
|
10595
|
+
continue;
|
|
10596
|
+
if (!TEXT_BLOCK_TYPES.includes(type))
|
|
10597
|
+
continue;
|
|
10598
|
+
const payload = asRecord2(block[type]);
|
|
10599
|
+
const text = plainText(payload?.rich_text);
|
|
10600
|
+
if (text.length > 0)
|
|
10601
|
+
lines.push(text);
|
|
10602
|
+
}
|
|
10603
|
+
return lines.join(`
|
|
10604
|
+
`);
|
|
10605
|
+
}
|
|
10606
|
+
function plainText(richText) {
|
|
10607
|
+
return asArray(richText).map((raw) => asString(asRecord2(raw)?.plain_text) ?? "").filter((text) => text.length > 0).join("").trim();
|
|
10608
|
+
}
|
|
10609
|
+
var DEFAULT_BASE_URL5 = "https://api.notion.com/v1", DEFAULT_TOKEN_ENV5 = "NOTION_TOKEN", NOTION_VERSION = "2022-06-28", DEFAULT_MAX_PAGES5 = 10, DEFAULT_MAX_DOCUMENTS8 = 500, TEXT_BLOCK_TYPES;
|
|
10610
|
+
var init_connector7 = __esm(() => {
|
|
10611
|
+
TEXT_BLOCK_TYPES = [
|
|
10612
|
+
"paragraph",
|
|
10613
|
+
"heading_1",
|
|
10614
|
+
"heading_2",
|
|
10615
|
+
"heading_3",
|
|
10616
|
+
"bulleted_list_item",
|
|
10617
|
+
"numbered_list_item",
|
|
10618
|
+
"to_do",
|
|
10619
|
+
"quote",
|
|
10620
|
+
"callout",
|
|
10621
|
+
"code"
|
|
10622
|
+
];
|
|
10623
|
+
});
|
|
10624
|
+
|
|
10625
|
+
// src/datasource/skills/notion/skill.ts
|
|
10626
|
+
var NOTION_SKILL_DEFINITION, NotionSkill;
|
|
10627
|
+
var init_skill6 = __esm(() => {
|
|
10628
|
+
init_connector_skill();
|
|
10629
|
+
init_connector7();
|
|
10630
|
+
NOTION_SKILL_DEFINITION = {
|
|
10631
|
+
skillName: "notion",
|
|
10632
|
+
skillType: "notion-workspace",
|
|
10633
|
+
description: "Notion workspace datasource",
|
|
10634
|
+
capabilities: ["documents", "api", "polling"],
|
|
10635
|
+
defaultTags: ["notion", "documents"],
|
|
10636
|
+
contentType: "document",
|
|
10637
|
+
manifestDescription: "Search indexed Notion pages and databases shared with the integration. Use for questions about Notion docs, wikis, and database entries.",
|
|
10638
|
+
manifestNotes: ["Only pages shared with the server-configured integration token are indexed."]
|
|
10639
|
+
};
|
|
10640
|
+
NotionSkill = class NotionSkill extends ConnectorDatasourceSkill {
|
|
10641
|
+
constructor(options = {}) {
|
|
10642
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
10643
|
+
super(NOTION_SKILL_DEFINITION, {
|
|
10644
|
+
...rest,
|
|
10645
|
+
connector: connector ?? new NotionConnector(connectorOptions ?? {})
|
|
10646
|
+
});
|
|
10647
|
+
}
|
|
10648
|
+
};
|
|
10649
|
+
});
|
|
10650
|
+
|
|
10651
|
+
// src/datasource/skills/notion/index.ts
|
|
10652
|
+
var init_notion = __esm(() => {
|
|
10653
|
+
init_connector7();
|
|
10654
|
+
init_skill6();
|
|
10655
|
+
});
|
|
10656
|
+
|
|
10657
|
+
// src/datasource/skills/obsidian/connector.ts
|
|
10658
|
+
import { readdir as readdir2, readFile as readFile5, stat as stat3 } from "node:fs/promises";
|
|
10659
|
+
import { basename as basename7, extname as extname8, join as join19, relative as relative5, sep as sep4 } from "node:path";
|
|
10660
|
+
|
|
10661
|
+
class ObsidianConnector {
|
|
10662
|
+
options;
|
|
10663
|
+
constructor(options = {}) {
|
|
10664
|
+
this.options = options;
|
|
10665
|
+
}
|
|
10666
|
+
async fetch() {
|
|
10667
|
+
const vaultPath = this.options.vaultPath;
|
|
10668
|
+
if (vaultPath === undefined || vaultPath.length === 0) {
|
|
10669
|
+
return { ok: false, reason: "not-configured", message: "vault path not configured" };
|
|
10670
|
+
}
|
|
10671
|
+
try {
|
|
10672
|
+
const info = await stat3(vaultPath);
|
|
10673
|
+
if (!info.isDirectory())
|
|
10674
|
+
return { ok: false, reason: "unavailable", message: "vault path is not a directory" };
|
|
10675
|
+
} catch {
|
|
10676
|
+
return { ok: false, reason: "unavailable", message: "vault path is not readable" };
|
|
10677
|
+
}
|
|
10678
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS9;
|
|
10679
|
+
const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE3;
|
|
10680
|
+
const files = [];
|
|
10681
|
+
const walk = async (dir) => {
|
|
10682
|
+
let entries;
|
|
10683
|
+
try {
|
|
10684
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
10685
|
+
} catch {
|
|
10686
|
+
return;
|
|
10687
|
+
}
|
|
10688
|
+
for (const entry of entries) {
|
|
10689
|
+
if (entry.isDirectory()) {
|
|
10690
|
+
if (!SKIPPED_DIRS.has(entry.name))
|
|
10691
|
+
await walk(join19(dir, entry.name));
|
|
10692
|
+
} else if (entry.isFile() && extname8(entry.name).toLowerCase() === ".md") {
|
|
10693
|
+
files.push(join19(dir, entry.name));
|
|
10694
|
+
}
|
|
10695
|
+
}
|
|
10696
|
+
};
|
|
10697
|
+
await walk(vaultPath);
|
|
10698
|
+
const documents = [];
|
|
10699
|
+
let readFailures = 0;
|
|
10700
|
+
let skippedLarge = 0;
|
|
10701
|
+
for (const file of files.sort()) {
|
|
10702
|
+
if (documents.length >= maxDocuments)
|
|
10703
|
+
break;
|
|
10704
|
+
let text;
|
|
10705
|
+
try {
|
|
10706
|
+
const info = await stat3(file);
|
|
10707
|
+
if (info.size > maxBytes) {
|
|
10708
|
+
skippedLarge += 1;
|
|
10709
|
+
continue;
|
|
10710
|
+
}
|
|
10711
|
+
text = await readFile5(file, "utf8");
|
|
10712
|
+
const relativePath = relative5(vaultPath, file);
|
|
10713
|
+
const segments = relativePath.split(sep4);
|
|
10714
|
+
const directorySegments = segments.slice(0, -1);
|
|
10715
|
+
const stem = basename7(file, ".md");
|
|
10716
|
+
const { body, frontmatterTags } = stripFrontmatter(text);
|
|
10717
|
+
const inlineTags = matchAll(body, INLINE_TAG_PATTERN, 1);
|
|
10718
|
+
const links = [];
|
|
10719
|
+
const embeds = [];
|
|
10720
|
+
for (const match of body.matchAll(WIKI_LINK_PATTERN)) {
|
|
10721
|
+
const target = match[2]?.trim();
|
|
10722
|
+
if (target === undefined || target.length === 0)
|
|
10723
|
+
continue;
|
|
10724
|
+
(match[1] === "!" ? embeds : links).push(target);
|
|
10725
|
+
}
|
|
10726
|
+
const heading = /^#\s+(.+)$/mu.exec(body)?.[1]?.trim();
|
|
10727
|
+
const content = body.trim().slice(0, MAX_CONTENT_CHARS3);
|
|
10728
|
+
if (content.length === 0)
|
|
10729
|
+
continue;
|
|
10730
|
+
documents.push({
|
|
10731
|
+
docId: relativePath.split(sep4).join("__").replace(/\.md$/u, ""),
|
|
10732
|
+
hierarchy: ["folders", ...directorySegments],
|
|
10733
|
+
title: heading ?? stem,
|
|
10734
|
+
content,
|
|
10735
|
+
publishedAt: info.mtimeMs,
|
|
10736
|
+
metadata: {
|
|
10737
|
+
path: file,
|
|
10738
|
+
tags: [...new Set([...frontmatterTags, ...inlineTags])],
|
|
10739
|
+
links,
|
|
10740
|
+
embeds,
|
|
10741
|
+
folder: directorySegments.join("/")
|
|
10742
|
+
}
|
|
10743
|
+
});
|
|
10744
|
+
} catch {
|
|
10745
|
+
readFailures += 1;
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
const warnings = [];
|
|
10749
|
+
if (skippedLarge > 0)
|
|
10750
|
+
warnings.push(`${skippedLarge} note(s) exceeded the size limit and were skipped`);
|
|
10751
|
+
if (readFailures > 0)
|
|
10752
|
+
warnings.push(`${readFailures} note(s) failed to read`);
|
|
10753
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
10754
|
+
}
|
|
10755
|
+
}
|
|
10756
|
+
function stripFrontmatter(text) {
|
|
10757
|
+
if (!text.startsWith("---"))
|
|
10758
|
+
return { body: text, frontmatterTags: [] };
|
|
10759
|
+
const end = text.indexOf(`
|
|
10760
|
+
---`, 3);
|
|
10761
|
+
if (end === -1)
|
|
10762
|
+
return { body: text, frontmatterTags: [] };
|
|
10763
|
+
const frontmatter = text.slice(3, end);
|
|
10764
|
+
const body = text.slice(text.indexOf(`
|
|
10765
|
+
`, end + 1) + 1);
|
|
10766
|
+
return { body, frontmatterTags: parseFrontmatterTags(frontmatter) };
|
|
10767
|
+
}
|
|
10768
|
+
function parseFrontmatterTags(frontmatter) {
|
|
10769
|
+
const lines = frontmatter.split(`
|
|
10770
|
+
`);
|
|
10771
|
+
const tags = [];
|
|
10772
|
+
for (const [index, line] of lines.entries()) {
|
|
10773
|
+
const match = /^tags:\s*(.*)$/u.exec(line.trim());
|
|
10774
|
+
if (match === null)
|
|
10775
|
+
continue;
|
|
10776
|
+
const inline = match[1]?.trim() ?? "";
|
|
10777
|
+
if (inline.length > 0) {
|
|
10778
|
+
const cleaned = inline.replace(/^\[/u, "").replace(/\]$/u, "");
|
|
10779
|
+
for (const tag of cleaned.split(/[\s,]+/u)) {
|
|
10780
|
+
const trimmed = tag.trim().replace(/^["'#]+|["']+$/gu, "");
|
|
10781
|
+
if (trimmed.length > 0)
|
|
10782
|
+
tags.push(trimmed);
|
|
10783
|
+
}
|
|
10784
|
+
break;
|
|
10785
|
+
}
|
|
10786
|
+
for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
|
|
10787
|
+
const item = /^\s*-\s*(.+)$/u.exec(lines[cursor] ?? "");
|
|
10788
|
+
if (item === null)
|
|
10789
|
+
break;
|
|
10790
|
+
const trimmed = item[1]?.trim().replace(/^["'#]+|["']+$/gu, "") ?? "";
|
|
10791
|
+
if (trimmed.length > 0)
|
|
10792
|
+
tags.push(trimmed);
|
|
10793
|
+
}
|
|
10794
|
+
break;
|
|
10795
|
+
}
|
|
10796
|
+
return tags;
|
|
10797
|
+
}
|
|
10798
|
+
function matchAll(text, pattern, group) {
|
|
10799
|
+
const out = [];
|
|
10800
|
+
for (const match of text.matchAll(pattern)) {
|
|
10801
|
+
const value = match[group]?.trim();
|
|
10802
|
+
if (value !== undefined && value.length > 0)
|
|
10803
|
+
out.push(value);
|
|
10804
|
+
}
|
|
10805
|
+
return out;
|
|
10806
|
+
}
|
|
10807
|
+
var DEFAULT_MAX_DOCUMENTS9 = 1000, DEFAULT_MAX_BYTES_PER_FILE3, MAX_CONTENT_CHARS3 = 1e5, SKIPPED_DIRS, INLINE_TAG_PATTERN, WIKI_LINK_PATTERN;
|
|
10808
|
+
var init_connector8 = __esm(() => {
|
|
10809
|
+
DEFAULT_MAX_BYTES_PER_FILE3 = 2 * 1024 * 1024;
|
|
10810
|
+
SKIPPED_DIRS = new Set([".obsidian", ".trash", ".git", "node_modules"]);
|
|
10811
|
+
INLINE_TAG_PATTERN = /(?:^|\s)#([A-Za-z0-9_/-]+)/gu;
|
|
10812
|
+
WIKI_LINK_PATTERN = /(!?)\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/gu;
|
|
10813
|
+
});
|
|
10814
|
+
|
|
10815
|
+
// src/datasource/skills/obsidian/skill.ts
|
|
10816
|
+
var OBSIDIAN_SKILL_DEFINITION, ObsidianSkill;
|
|
10817
|
+
var init_skill7 = __esm(() => {
|
|
10818
|
+
init_connector_skill();
|
|
10819
|
+
init_connector8();
|
|
10820
|
+
OBSIDIAN_SKILL_DEFINITION = {
|
|
10821
|
+
skillName: "obsidian",
|
|
10822
|
+
skillType: "obsidian-vault",
|
|
10823
|
+
description: "Obsidian vault datasource",
|
|
10824
|
+
capabilities: ["notes", "filesystem"],
|
|
10825
|
+
defaultTags: ["obsidian", "notes"],
|
|
10826
|
+
contentType: "note",
|
|
10827
|
+
manifestDescription: "Search indexed Obsidian vault notes, including tags, wiki links, and embeds. Use for questions about personal notes and knowledge-base content.",
|
|
10828
|
+
manifestNotes: ["Only the server-configured vault is indexed; note metadata includes tags, links, and embeds."]
|
|
10829
|
+
};
|
|
10830
|
+
ObsidianSkill = class ObsidianSkill extends ConnectorDatasourceSkill {
|
|
10831
|
+
constructor(options = {}) {
|
|
10832
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
10833
|
+
super(OBSIDIAN_SKILL_DEFINITION, {
|
|
10834
|
+
...rest,
|
|
10835
|
+
connector: connector ?? new ObsidianConnector(connectorOptions ?? {})
|
|
10836
|
+
});
|
|
10837
|
+
}
|
|
10838
|
+
};
|
|
10839
|
+
});
|
|
10840
|
+
|
|
10841
|
+
// src/datasource/skills/obsidian/index.ts
|
|
10842
|
+
var init_obsidian = __esm(() => {
|
|
10843
|
+
init_connector8();
|
|
10844
|
+
init_skill7();
|
|
10845
|
+
});
|
|
10846
|
+
|
|
10847
|
+
// src/datasource/skills/rss/connector.ts
|
|
10848
|
+
import { XMLParser as XMLParser2 } from "fast-xml-parser";
|
|
10849
|
+
|
|
10850
|
+
class RssConnector {
|
|
10851
|
+
options;
|
|
10852
|
+
parser = new XMLParser2({ ignoreAttributes: false });
|
|
10853
|
+
constructor(options = {}) {
|
|
10854
|
+
this.options = options;
|
|
10855
|
+
}
|
|
10856
|
+
async fetch(signal) {
|
|
10857
|
+
const feeds = this.options.feeds ?? [];
|
|
10858
|
+
if (feeds.length === 0)
|
|
10859
|
+
return { ok: false, reason: "not-configured", message: "no feeds configured" };
|
|
10860
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS10;
|
|
10861
|
+
const maxItemsPerFeed = this.options.maxItemsPerFeed ?? DEFAULT_MAX_ITEMS_PER_FEED;
|
|
10862
|
+
const documents = [];
|
|
10863
|
+
const warnings = [];
|
|
10864
|
+
let failedFeeds = 0;
|
|
10865
|
+
for (const [index, feed] of feeds.entries()) {
|
|
10866
|
+
if (documents.length >= maxDocuments)
|
|
10867
|
+
break;
|
|
10868
|
+
const result = await httpText(feed.url, {
|
|
10869
|
+
headers: { Accept: ACCEPT_HEADER },
|
|
10870
|
+
timeoutMs: this.options.timeoutMs ?? DEFAULT_FEED_TIMEOUT_MS,
|
|
10871
|
+
fetchImpl: this.options.fetchImpl,
|
|
10872
|
+
signal
|
|
10873
|
+
});
|
|
10874
|
+
if (!result.ok) {
|
|
10875
|
+
failedFeeds += 1;
|
|
10876
|
+
warnings.push(`feed ${index + 1} failed: ${result.message}`);
|
|
10877
|
+
continue;
|
|
10878
|
+
}
|
|
10879
|
+
let parsed;
|
|
10880
|
+
try {
|
|
10881
|
+
parsed = this.parser.parse(result.text);
|
|
10882
|
+
} catch {
|
|
10883
|
+
failedFeeds += 1;
|
|
10884
|
+
warnings.push(`feed ${index + 1} failed: unparseable XML`);
|
|
10885
|
+
continue;
|
|
10886
|
+
}
|
|
10887
|
+
const normalized = normalizeFeed(parsed);
|
|
10888
|
+
if (normalized === undefined) {
|
|
10889
|
+
failedFeeds += 1;
|
|
10890
|
+
warnings.push(`feed ${index + 1} failed: unrecognized feed shape`);
|
|
10891
|
+
continue;
|
|
10892
|
+
}
|
|
10893
|
+
const feedTitle = normalized.title ?? `feed-${index + 1}`;
|
|
10894
|
+
const hierarchy = feed.category !== undefined ? ["feeds", feed.category, feedTitle] : ["feeds", feedTitle];
|
|
10895
|
+
for (const item of normalized.items.slice(0, maxItemsPerFeed)) {
|
|
10896
|
+
if (documents.length >= maxDocuments)
|
|
10897
|
+
break;
|
|
10898
|
+
const title = item.title ?? "(untitled)";
|
|
10899
|
+
const body = stripHtml(item.body ?? "");
|
|
10900
|
+
const content = body.length > 0 ? `${title}
|
|
10901
|
+
|
|
10902
|
+
${body}` : title;
|
|
10903
|
+
documents.push({
|
|
10904
|
+
docId: item.id,
|
|
10905
|
+
hierarchy,
|
|
10906
|
+
title,
|
|
10907
|
+
content: content.slice(0, MAX_CONTENT_CHARS4),
|
|
10908
|
+
...item.publishedAt !== undefined ? { publishedAt: item.publishedAt } : {},
|
|
10909
|
+
metadata: { feedIndex: index, feedTitle, categories: item.categories }
|
|
10910
|
+
});
|
|
10911
|
+
}
|
|
10912
|
+
}
|
|
10913
|
+
if (failedFeeds === feeds.length) {
|
|
10914
|
+
return { ok: false, reason: "unavailable", message: "all configured feeds failed" };
|
|
10915
|
+
}
|
|
10916
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
10917
|
+
}
|
|
10918
|
+
}
|
|
10919
|
+
function normalizeFeed(parsed) {
|
|
10920
|
+
const root = asRecord2(parsed);
|
|
10921
|
+
if (root === undefined)
|
|
10922
|
+
return;
|
|
10923
|
+
const channel = asRecord2(asRecord2(root.rss)?.channel);
|
|
10924
|
+
if (channel !== undefined) {
|
|
10925
|
+
const items = asArray(channel.item ?? []).concat(asRecord2(channel.item) !== undefined ? [channel.item] : []);
|
|
10926
|
+
return { title: textOf(channel.title), items: items.map(normalizeRssItem).filter(present) };
|
|
10927
|
+
}
|
|
10928
|
+
const atom = asRecord2(root.feed);
|
|
10929
|
+
if (atom !== undefined) {
|
|
10930
|
+
const entries = asArray(atom.entry ?? []).concat(asRecord2(atom.entry) !== undefined ? [atom.entry] : []);
|
|
10931
|
+
return { title: textOf(atom.title), items: entries.map(normalizeAtomEntry).filter(present) };
|
|
10932
|
+
}
|
|
10933
|
+
return;
|
|
10934
|
+
}
|
|
10935
|
+
function normalizeRssItem(raw) {
|
|
10936
|
+
const item = asRecord2(raw);
|
|
10937
|
+
if (item === undefined)
|
|
10938
|
+
return;
|
|
10939
|
+
const title = textOf(item.title);
|
|
10940
|
+
const guid = textOf(item.guid) ?? textOf(item.link) ?? title;
|
|
10941
|
+
if (guid === undefined)
|
|
10942
|
+
return;
|
|
10943
|
+
return {
|
|
10944
|
+
id: guid,
|
|
10945
|
+
title,
|
|
10946
|
+
body: textOf(item.description),
|
|
10947
|
+
publishedAt: parseEpochMs(textOf(item.pubDate)),
|
|
10948
|
+
categories: categoriesOf(item.category)
|
|
10949
|
+
};
|
|
10950
|
+
}
|
|
10951
|
+
function normalizeAtomEntry(raw) {
|
|
10952
|
+
const entry = asRecord2(raw);
|
|
10953
|
+
if (entry === undefined)
|
|
10954
|
+
return;
|
|
10955
|
+
const title = textOf(entry.title);
|
|
10956
|
+
const id = textOf(entry.id) ?? title;
|
|
10957
|
+
if (id === undefined)
|
|
10958
|
+
return;
|
|
10959
|
+
return {
|
|
10960
|
+
id,
|
|
10961
|
+
title,
|
|
10962
|
+
body: textOf(entry.summary) ?? textOf(entry.content),
|
|
10963
|
+
publishedAt: parseEpochMs(textOf(entry.published) ?? textOf(entry.updated)),
|
|
10964
|
+
categories: categoriesOf(entry.category)
|
|
10965
|
+
};
|
|
10966
|
+
}
|
|
10967
|
+
function textOf(value) {
|
|
10968
|
+
const direct = asString(value);
|
|
10969
|
+
if (direct !== undefined)
|
|
10970
|
+
return direct.trim();
|
|
10971
|
+
if (typeof value === "number")
|
|
10972
|
+
return String(value);
|
|
10973
|
+
const record = asRecord2(value);
|
|
10974
|
+
if (record !== undefined) {
|
|
10975
|
+
const text = asString(record["#text"]) ?? asString(record["@_term"]);
|
|
10976
|
+
if (text !== undefined)
|
|
10977
|
+
return text.trim();
|
|
10978
|
+
if (typeof record["#text"] === "number")
|
|
10979
|
+
return String(record["#text"]);
|
|
10980
|
+
}
|
|
10981
|
+
return;
|
|
10982
|
+
}
|
|
10983
|
+
function categoriesOf(value) {
|
|
10984
|
+
const values = Array.isArray(value) ? value : value !== undefined ? [value] : [];
|
|
10985
|
+
return values.map(textOf).filter((category) => category !== undefined && category.length > 0);
|
|
10986
|
+
}
|
|
10987
|
+
function stripHtml(text) {
|
|
10988
|
+
return text.replace(/<[^>]+>/gu, " ").replace(/\s+/gu, " ").trim();
|
|
10989
|
+
}
|
|
10990
|
+
function present(item) {
|
|
10991
|
+
return item !== undefined;
|
|
10992
|
+
}
|
|
10993
|
+
var DEFAULT_MAX_DOCUMENTS10 = 500, DEFAULT_MAX_ITEMS_PER_FEED = 100, DEFAULT_FEED_TIMEOUT_MS = 30000, MAX_CONTENT_CHARS4 = 20000, ACCEPT_HEADER = "application/rss+xml, application/atom+xml, application/xml, text/xml";
|
|
10994
|
+
var init_connector9 = () => {};
|
|
10995
|
+
|
|
10996
|
+
// src/datasource/skills/rss/skill.ts
|
|
10997
|
+
var RSS_SKILL_DEFINITION, DEFAULT_DEDUPE_WINDOW_MS, RssSkill;
|
|
10998
|
+
var init_skill8 = __esm(() => {
|
|
10999
|
+
init_connector_skill();
|
|
11000
|
+
init_connector9();
|
|
11001
|
+
RSS_SKILL_DEFINITION = {
|
|
11002
|
+
skillName: "rss",
|
|
11003
|
+
skillType: "rss-feeds",
|
|
11004
|
+
description: "RSS/news datasource",
|
|
11005
|
+
capabilities: ["news", "api", "polling"],
|
|
11006
|
+
defaultTags: ["rss", "news", "public"],
|
|
11007
|
+
contentType: "article",
|
|
11008
|
+
manifestDescription: "Search indexed RSS/news feed articles from the configured feeds. Use for questions about recent news items or blog posts the server subscribes to.",
|
|
11009
|
+
manifestNotes: ["Feeds are polled with a dedupe window so re-delivered items are indexed once per window."]
|
|
11010
|
+
};
|
|
11011
|
+
DEFAULT_DEDUPE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
11012
|
+
RssSkill = class RssSkill extends ConnectorDatasourceSkill {
|
|
11013
|
+
constructor(options = {}) {
|
|
11014
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
11015
|
+
super(RSS_SKILL_DEFINITION, {
|
|
11016
|
+
...rest,
|
|
11017
|
+
connector: connector ?? new RssConnector(connectorOptions ?? {}),
|
|
11018
|
+
dedupeWindowMs: options.dedupeWindowMs ?? DEFAULT_DEDUPE_WINDOW_MS
|
|
11019
|
+
});
|
|
11020
|
+
}
|
|
11021
|
+
};
|
|
11022
|
+
});
|
|
11023
|
+
|
|
11024
|
+
// src/datasource/skills/rss/index.ts
|
|
11025
|
+
var init_rss = __esm(() => {
|
|
11026
|
+
init_connector9();
|
|
11027
|
+
init_skill8();
|
|
11028
|
+
});
|
|
11029
|
+
|
|
11030
|
+
// src/datasource/skills/slack/connector.ts
|
|
11031
|
+
class SlackConnector {
|
|
11032
|
+
options;
|
|
11033
|
+
constructor(options = {}) {
|
|
11034
|
+
this.options = options;
|
|
11035
|
+
}
|
|
11036
|
+
async fetch(signal) {
|
|
11037
|
+
const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV6);
|
|
11038
|
+
if (token === undefined)
|
|
11039
|
+
return { ok: false, reason: "not-configured", message: "auth token not configured" };
|
|
11040
|
+
const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL6;
|
|
11041
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
11042
|
+
const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES6;
|
|
11043
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS11;
|
|
11044
|
+
const request = { headers, timeoutMs: this.options.timeoutMs, fetchImpl: this.options.fetchImpl, signal };
|
|
11045
|
+
const channels = [];
|
|
11046
|
+
let cursor;
|
|
11047
|
+
for (let page = 0;page < maxPages; page += 1) {
|
|
11048
|
+
const url = new URL(`${baseUrl}/conversations.list`);
|
|
11049
|
+
url.searchParams.set("limit", "200");
|
|
11050
|
+
url.searchParams.set("types", "public_channel,private_channel");
|
|
11051
|
+
if (cursor !== undefined)
|
|
11052
|
+
url.searchParams.set("cursor", cursor);
|
|
11053
|
+
const result = await httpJson(url.toString(), request);
|
|
11054
|
+
if (!result.ok)
|
|
11055
|
+
return { ok: false, reason: result.reason, message: `channel list failed: ${result.message}` };
|
|
11056
|
+
const envelope = asRecord2(result.json);
|
|
11057
|
+
if (envelope === undefined)
|
|
11058
|
+
return { ok: false, reason: "invalid-data", message: "channel list malformed" };
|
|
11059
|
+
if (envelope.ok !== true) {
|
|
11060
|
+
const error = asString(envelope.error) ?? "unknown-error";
|
|
11061
|
+
if (AUTH_ERRORS.has(error))
|
|
11062
|
+
return { ok: false, reason: "auth", message: `slack: ${error}` };
|
|
11063
|
+
if (error === "ratelimited")
|
|
11064
|
+
return { ok: false, reason: "rate-limited", message: "slack: ratelimited" };
|
|
11065
|
+
return { ok: false, reason: "api-error", message: `slack: ${error}` };
|
|
11066
|
+
}
|
|
11067
|
+
for (const raw of asArray(envelope.channels)) {
|
|
11068
|
+
const channel = asRecord2(raw);
|
|
11069
|
+
const id = asString(channel?.id);
|
|
11070
|
+
const name = asString(channel?.name);
|
|
11071
|
+
if (id !== undefined && name !== undefined)
|
|
11072
|
+
channels.push({ id, name });
|
|
11073
|
+
}
|
|
11074
|
+
cursor = asString(asRecord2(envelope.response_metadata)?.next_cursor);
|
|
11075
|
+
if (cursor === undefined || cursor.length === 0)
|
|
11076
|
+
break;
|
|
11077
|
+
}
|
|
11078
|
+
const filter = this.options.channels;
|
|
11079
|
+
const selected = filter === undefined || filter.length === 0 ? channels : channels.filter((channel) => filter.includes(channel.id) || filter.includes(channel.name));
|
|
11080
|
+
const documents = [];
|
|
11081
|
+
const warnings = [];
|
|
11082
|
+
for (const channel of selected) {
|
|
11083
|
+
if (documents.length >= maxDocuments)
|
|
11084
|
+
break;
|
|
11085
|
+
let historyCursor;
|
|
11086
|
+
for (let page = 0;page < maxPages && documents.length < maxDocuments; page += 1) {
|
|
11087
|
+
const url = new URL(`${baseUrl}/conversations.history`);
|
|
11088
|
+
url.searchParams.set("channel", channel.id);
|
|
11089
|
+
url.searchParams.set("limit", "200");
|
|
11090
|
+
if (historyCursor !== undefined)
|
|
11091
|
+
url.searchParams.set("cursor", historyCursor);
|
|
11092
|
+
const result = await httpJson(url.toString(), request);
|
|
11093
|
+
if (!result.ok) {
|
|
11094
|
+
warnings.push(`channel ${channel.name} history failed: ${result.message}`);
|
|
11095
|
+
break;
|
|
11096
|
+
}
|
|
11097
|
+
const envelope = asRecord2(result.json);
|
|
11098
|
+
if (envelope === undefined || envelope.ok !== true) {
|
|
11099
|
+
const error = asString(envelope?.error) ?? "malformed response";
|
|
11100
|
+
if (AUTH_ERRORS.has(error))
|
|
11101
|
+
return { ok: false, reason: "auth", message: `slack: ${error}` };
|
|
11102
|
+
if (error === "ratelimited")
|
|
11103
|
+
return { ok: false, reason: "rate-limited", message: "slack: ratelimited" };
|
|
11104
|
+
if (CHANNEL_SKIP_ERRORS.has(error))
|
|
11105
|
+
warnings.push(`channel ${channel.name} skipped: ${error}`);
|
|
11106
|
+
else
|
|
11107
|
+
warnings.push(`channel ${channel.name} history failed: ${error}`);
|
|
11108
|
+
break;
|
|
11109
|
+
}
|
|
11110
|
+
for (const raw of asArray(envelope.messages)) {
|
|
11111
|
+
if (documents.length >= maxDocuments)
|
|
11112
|
+
break;
|
|
11113
|
+
const message = asRecord2(raw);
|
|
11114
|
+
if (message === undefined)
|
|
11115
|
+
continue;
|
|
11116
|
+
const text = asString(message.text);
|
|
11117
|
+
const ts = asString(message.ts);
|
|
11118
|
+
const subtype = asString(message.subtype);
|
|
11119
|
+
if (text === undefined || text.length === 0 || ts === undefined)
|
|
11120
|
+
continue;
|
|
11121
|
+
if (subtype === "channel_join")
|
|
11122
|
+
continue;
|
|
11123
|
+
const user = asString(message.user) ?? asString(message.username) ?? "unknown";
|
|
11124
|
+
documents.push({
|
|
11125
|
+
docId: `${channel.id}-${ts.replace(/\./gu, "-")}`,
|
|
11126
|
+
hierarchy: ["channels", channel.name],
|
|
11127
|
+
title: `#${channel.name}`,
|
|
11128
|
+
content: `${user}: ${text}`,
|
|
11129
|
+
publishedAt: Math.round(Number.parseFloat(ts) * 1000),
|
|
11130
|
+
metadata: { channelId: channel.id, ts, user }
|
|
11131
|
+
});
|
|
11132
|
+
}
|
|
11133
|
+
historyCursor = asString(asRecord2(envelope.response_metadata)?.next_cursor);
|
|
11134
|
+
if (historyCursor === undefined || historyCursor.length === 0)
|
|
11135
|
+
break;
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
11139
|
+
}
|
|
11140
|
+
}
|
|
11141
|
+
var DEFAULT_BASE_URL6 = "https://slack.com/api", DEFAULT_TOKEN_ENV6 = "SLACK_BOT_TOKEN", DEFAULT_MAX_PAGES6 = 10, DEFAULT_MAX_DOCUMENTS11 = 500, AUTH_ERRORS, CHANNEL_SKIP_ERRORS;
|
|
11142
|
+
var init_connector10 = __esm(() => {
|
|
11143
|
+
AUTH_ERRORS = new Set(["invalid_auth", "not_authed", "token_revoked", "account_inactive"]);
|
|
11144
|
+
CHANNEL_SKIP_ERRORS = new Set(["missing_scope", "access_denied", "channel_not_found", "not_in_channel"]);
|
|
11145
|
+
});
|
|
11146
|
+
|
|
11147
|
+
// src/datasource/skills/slack/skill.ts
|
|
11148
|
+
var SLACK_SKILL_DEFINITION, SlackSkill;
|
|
11149
|
+
var init_skill9 = __esm(() => {
|
|
11150
|
+
init_connector_skill();
|
|
11151
|
+
init_connector10();
|
|
11152
|
+
SLACK_SKILL_DEFINITION = {
|
|
11153
|
+
skillName: "slack",
|
|
11154
|
+
skillType: "slack-workspace",
|
|
11155
|
+
description: "Slack workspace datasource",
|
|
11156
|
+
capabilities: ["chat", "api", "polling"],
|
|
11157
|
+
defaultTags: ["slack", "chat", "pii"],
|
|
11158
|
+
contentType: "chat",
|
|
11159
|
+
manifestDescription: "Search indexed Slack messages across authorized workspace channels. Use for questions about Slack conversations, decisions made in channels, or who said what.",
|
|
11160
|
+
manifestNotes: [
|
|
11161
|
+
"Message history windows and channel visibility are bounded by the server-configured bot token scopes."
|
|
11162
|
+
]
|
|
11163
|
+
};
|
|
11164
|
+
SlackSkill = class SlackSkill extends ConnectorDatasourceSkill {
|
|
11165
|
+
constructor(options = {}) {
|
|
11166
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
11167
|
+
super(SLACK_SKILL_DEFINITION, {
|
|
11168
|
+
...rest,
|
|
11169
|
+
connector: connector ?? new SlackConnector(connectorOptions ?? {})
|
|
11170
|
+
});
|
|
11171
|
+
}
|
|
11172
|
+
};
|
|
11173
|
+
});
|
|
11174
|
+
|
|
11175
|
+
// src/datasource/skills/slack/index.ts
|
|
11176
|
+
var init_slack = __esm(() => {
|
|
11177
|
+
init_connector10();
|
|
11178
|
+
init_skill9();
|
|
11179
|
+
});
|
|
11180
|
+
|
|
11181
|
+
// src/datasource/skills/spotlight/connector.ts
|
|
11182
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
11183
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
11184
|
+
import { readFile as fsReadFile, stat as stat4 } from "node:fs/promises";
|
|
11185
|
+
import { basename as basename8, extname as extname9 } from "node:path";
|
|
11186
|
+
|
|
11187
|
+
class SpotlightConnector {
|
|
11188
|
+
options;
|
|
11189
|
+
run;
|
|
11190
|
+
readFile;
|
|
11191
|
+
platform;
|
|
11192
|
+
constructor(options = {}) {
|
|
11193
|
+
this.options = options;
|
|
11194
|
+
this.run = options.run ?? spawnRunner;
|
|
11195
|
+
this.readFile = options.readFile ?? ((path) => fsReadFile(path, "utf8"));
|
|
11196
|
+
this.platform = options.platform ?? process.platform;
|
|
11197
|
+
}
|
|
11198
|
+
async fetch() {
|
|
11199
|
+
if (this.platform !== "darwin") {
|
|
11200
|
+
return { ok: false, reason: "unavailable", message: "spotlight datasource is supported on macOS only" };
|
|
11201
|
+
}
|
|
11202
|
+
const queries = (this.options.queries ?? []).map((query) => query.trim()).filter((query) => query.length > 0);
|
|
11203
|
+
if (queries.length === 0) {
|
|
11204
|
+
return { ok: false, reason: "not-configured", message: "no spotlight queries configured" };
|
|
11205
|
+
}
|
|
11206
|
+
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
|
|
11207
|
+
const status = await this.run("mdutil", ["-s", "/"], timeoutMs);
|
|
11208
|
+
if (status.ok && /indexing disabled/iu.test(status.stdout)) {
|
|
11209
|
+
return {
|
|
11210
|
+
ok: false,
|
|
11211
|
+
reason: "unavailable",
|
|
11212
|
+
message: "Spotlight indexing is disabled; enable it with `sudo mdutil -i on /`"
|
|
11213
|
+
};
|
|
11214
|
+
}
|
|
11215
|
+
const maxPerQuery = this.options.maxResultsPerQuery ?? DEFAULT_MAX_RESULTS_PER_QUERY;
|
|
11216
|
+
const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS12;
|
|
11217
|
+
const seen = new Set;
|
|
11218
|
+
const paths = [];
|
|
11219
|
+
let mdfindMissing = false;
|
|
11220
|
+
let failedQueries = 0;
|
|
11221
|
+
for (const query of queries) {
|
|
11222
|
+
const args = this.options.onlyIn !== undefined ? ["-onlyin", this.options.onlyIn, query] : [query];
|
|
11223
|
+
const result = await this.run("mdfind", args, timeoutMs);
|
|
11224
|
+
if (!result.ok) {
|
|
11225
|
+
if (result.error === "ENOENT") {
|
|
11226
|
+
mdfindMissing = true;
|
|
11227
|
+
break;
|
|
11228
|
+
}
|
|
11229
|
+
failedQueries += 1;
|
|
11230
|
+
continue;
|
|
11231
|
+
}
|
|
11232
|
+
let kept = 0;
|
|
11233
|
+
for (const line of result.stdout.split(`
|
|
11234
|
+
`)) {
|
|
11235
|
+
const path = line.trim();
|
|
11236
|
+
if (path.length === 0 || seen.has(path))
|
|
11237
|
+
continue;
|
|
11238
|
+
if (kept >= maxPerQuery)
|
|
11239
|
+
break;
|
|
11240
|
+
seen.add(path);
|
|
11241
|
+
paths.push(path);
|
|
11242
|
+
kept += 1;
|
|
11243
|
+
}
|
|
11244
|
+
}
|
|
11245
|
+
if (mdfindMissing) {
|
|
11246
|
+
return { ok: false, reason: "unavailable", message: "mdfind binary not found; Spotlight requires macOS" };
|
|
11247
|
+
}
|
|
11248
|
+
if (failedQueries === queries.length) {
|
|
11249
|
+
return { ok: false, reason: "unavailable", message: "every configured mdfind query failed" };
|
|
11250
|
+
}
|
|
11251
|
+
const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE4;
|
|
11252
|
+
const documents = [];
|
|
11253
|
+
let permissionDenied = 0;
|
|
11254
|
+
let readFailures = 0;
|
|
11255
|
+
for (const path of paths) {
|
|
11256
|
+
if (documents.length >= maxDocuments)
|
|
11257
|
+
break;
|
|
11258
|
+
try {
|
|
11259
|
+
const info = await stat4(path);
|
|
11260
|
+
if (!info.isFile() || info.size > maxBytes)
|
|
11261
|
+
continue;
|
|
11262
|
+
const text = await this.readFile(path);
|
|
11263
|
+
const content = text.trim().slice(0, MAX_CONTENT_CHARS5);
|
|
11264
|
+
if (content.length === 0)
|
|
11265
|
+
continue;
|
|
11266
|
+
documents.push({
|
|
11267
|
+
docId: createHash8("sha256").update(path).digest("hex").slice(0, 16),
|
|
11268
|
+
hierarchy: ["files"],
|
|
11269
|
+
title: basename8(path),
|
|
11270
|
+
content,
|
|
11271
|
+
publishedAt: info.mtimeMs,
|
|
11272
|
+
metadata: { path, extension: extname9(path).toLowerCase() }
|
|
11273
|
+
});
|
|
11274
|
+
} catch (error) {
|
|
11275
|
+
const code = error.code;
|
|
11276
|
+
if (code === "EPERM" || code === "EACCES")
|
|
11277
|
+
permissionDenied += 1;
|
|
11278
|
+
else
|
|
11279
|
+
readFailures += 1;
|
|
11280
|
+
}
|
|
11281
|
+
}
|
|
11282
|
+
if (documents.length === 0 && permissionDenied > 0) {
|
|
11283
|
+
return {
|
|
11284
|
+
ok: false,
|
|
11285
|
+
reason: "permission",
|
|
11286
|
+
message: `macOS denied access to all Spotlight hits; ${FDA_GUIDANCE}`
|
|
11287
|
+
};
|
|
11288
|
+
}
|
|
11289
|
+
const warnings = [];
|
|
11290
|
+
if (failedQueries > 0)
|
|
11291
|
+
warnings.push(`${failedQueries} mdfind quer(y/ies) failed`);
|
|
11292
|
+
if (permissionDenied > 0) {
|
|
11293
|
+
warnings.push(`${permissionDenied} Spotlight hit(s) were denied by macOS privacy protections; ${FDA_GUIDANCE}`);
|
|
11294
|
+
}
|
|
11295
|
+
if (readFailures > 0)
|
|
11296
|
+
warnings.push(`${readFailures} Spotlight hit(s) failed to read`);
|
|
11297
|
+
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
11298
|
+
}
|
|
8519
11299
|
}
|
|
8520
|
-
|
|
8521
|
-
|
|
11300
|
+
var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS12 = 300, DEFAULT_MAX_BYTES_PER_FILE4, DEFAULT_TIMEOUT_MS6 = 15000, MAX_CONTENT_CHARS5 = 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((resolve9) => {
|
|
11301
|
+
const child = spawn7(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
|
|
11302
|
+
let stdout = "";
|
|
11303
|
+
let stderr = "";
|
|
11304
|
+
let settled = false;
|
|
11305
|
+
const finish = (result) => {
|
|
11306
|
+
if (settled)
|
|
11307
|
+
return;
|
|
11308
|
+
settled = true;
|
|
11309
|
+
clearTimeout(timer);
|
|
11310
|
+
resolve9(result);
|
|
11311
|
+
};
|
|
11312
|
+
const timer = setTimeout(() => {
|
|
11313
|
+
child.kill("SIGKILL");
|
|
11314
|
+
finish({ ok: false, stdout, stderr: "process timed out", code: null });
|
|
11315
|
+
}, timeoutMs);
|
|
11316
|
+
child.stdout.setEncoding("utf8");
|
|
11317
|
+
child.stderr.setEncoding("utf8");
|
|
11318
|
+
child.stdout.on("data", (chunk) => {
|
|
11319
|
+
if (stdout.length < MAX_BUFFER_BYTES)
|
|
11320
|
+
stdout += chunk;
|
|
11321
|
+
});
|
|
11322
|
+
child.stderr.on("data", (chunk) => {
|
|
11323
|
+
if (stderr.length < MAX_BUFFER_BYTES)
|
|
11324
|
+
stderr += chunk;
|
|
11325
|
+
});
|
|
11326
|
+
child.on("error", (error) => {
|
|
11327
|
+
finish({ ok: false, stdout, stderr: error.message, code: null, error: error.code });
|
|
11328
|
+
});
|
|
11329
|
+
child.on("close", (code) => {
|
|
11330
|
+
finish({ ok: code === 0, stdout, stderr, code });
|
|
11331
|
+
});
|
|
11332
|
+
});
|
|
11333
|
+
var init_connector11 = __esm(() => {
|
|
11334
|
+
DEFAULT_MAX_BYTES_PER_FILE4 = 256 * 1024;
|
|
11335
|
+
MAX_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
11336
|
+
});
|
|
11337
|
+
|
|
11338
|
+
// src/datasource/skills/spotlight/skill.ts
|
|
11339
|
+
var SPOTLIGHT_SKILL_DEFINITION, SpotlightSkill;
|
|
11340
|
+
var init_skill10 = __esm(() => {
|
|
11341
|
+
init_connector_skill();
|
|
11342
|
+
init_connector11();
|
|
11343
|
+
SPOTLIGHT_SKILL_DEFINITION = {
|
|
11344
|
+
skillName: "spotlight",
|
|
11345
|
+
skillType: "spotlight-search",
|
|
11346
|
+
description: "macOS Spotlight datasource",
|
|
11347
|
+
capabilities: ["files", "macos-only", "local-search"],
|
|
11348
|
+
defaultTags: ["spotlight", "files"],
|
|
11349
|
+
contentType: "document",
|
|
11350
|
+
manifestDescription: "Search files indexed by macOS Spotlight via the built-in mdfind CLI. macOS only; no external installs. Use for questions about local documents on the Mac running AutoRAG.",
|
|
11351
|
+
manifestNotes: [
|
|
11352
|
+
"## Platform requirements",
|
|
11353
|
+
"- macOS only: this datasource is unavailable on any other platform.",
|
|
11354
|
+
"- Spotlight indexing must be enabled (`sudo mdutil -i on /`; check with `mdutil -s /`).",
|
|
11355
|
+
"- **Full Disk Access**: Spotlight can find protected files (Mail, Messages, Safari data, parts of ~/Library), but reading their content fails unless the terminal or app running AutoRAG has Full Disk Access. Grant it under System Settings -> Privacy & Security -> Full Disk Access, add the host app (e.g. Terminal, iTerm, or the AutoRAG binary), then restart the app. Permission failures surface as `datasource-permission-denied`.",
|
|
11356
|
+
"",
|
|
11357
|
+
"## Operator cheat sheet",
|
|
11358
|
+
"- `mdfind <query>` — Spotlight search; `mdfind -onlyin <dir> <query>` scopes to a directory; `mdfind -name <text>` matches file names.",
|
|
11359
|
+
"- `mdls <file>` — show Spotlight metadata attributes (kMDItemDisplayName, kMDItemContentType, …) for one file.",
|
|
11360
|
+
"- `mdutil -s /` — index status; `sudo mdutil -i on /` / `-i off /` — enable/disable indexing; `sudo mdutil -E /` — erase and rebuild the index.",
|
|
11361
|
+
"",
|
|
11362
|
+
"Queries are trusted server configuration; indexing re-runs the configured mdfind queries and hydrates text content. Result metadata carries the real absolute file path (`path`) so hits are traceable back to disk; if paths must not leave this machine, run AutoRAG with a local LLM."
|
|
11363
|
+
]
|
|
11364
|
+
};
|
|
11365
|
+
SpotlightSkill = class SpotlightSkill extends ConnectorDatasourceSkill {
|
|
11366
|
+
constructor(options = {}) {
|
|
11367
|
+
const { connector, connectorOptions, ...rest } = options;
|
|
11368
|
+
super(SPOTLIGHT_SKILL_DEFINITION, {
|
|
11369
|
+
...rest,
|
|
11370
|
+
connector: connector ?? new SpotlightConnector(connectorOptions ?? {})
|
|
11371
|
+
});
|
|
11372
|
+
}
|
|
11373
|
+
};
|
|
11374
|
+
});
|
|
11375
|
+
|
|
11376
|
+
// src/datasource/skills/spotlight/index.ts
|
|
11377
|
+
var init_spotlight = __esm(() => {
|
|
11378
|
+
init_connector11();
|
|
11379
|
+
init_skill10();
|
|
11380
|
+
});
|
|
11381
|
+
|
|
11382
|
+
// src/datasource/skills/factory.ts
|
|
11383
|
+
function common(config, workspaceRoot) {
|
|
11384
|
+
return {
|
|
11385
|
+
...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
|
|
11386
|
+
...config.pollingIntervalMs !== undefined ? { pollingIntervalMs: config.pollingIntervalMs } : {},
|
|
11387
|
+
...config.tags !== undefined ? { tags: config.tags } : {},
|
|
11388
|
+
...workspaceRoot !== undefined ? { workspaceRoot } : {}
|
|
11389
|
+
};
|
|
8522
11390
|
}
|
|
8523
|
-
function
|
|
8524
|
-
|
|
11391
|
+
function buildDatasourceSkills(config, workspaceRoot) {
|
|
11392
|
+
const skills = [];
|
|
11393
|
+
const unknown = [];
|
|
11394
|
+
for (const [name, raw] of Object.entries(config ?? {})) {
|
|
11395
|
+
if (raw === false)
|
|
11396
|
+
continue;
|
|
11397
|
+
const entry = raw === true ? {} : raw;
|
|
11398
|
+
if (entry.enabled === false)
|
|
11399
|
+
continue;
|
|
11400
|
+
const builder = BUILDERS[name];
|
|
11401
|
+
if (builder === undefined) {
|
|
11402
|
+
unknown.push(name);
|
|
11403
|
+
continue;
|
|
11404
|
+
}
|
|
11405
|
+
skills.push(builder(entry, workspaceRoot));
|
|
11406
|
+
}
|
|
11407
|
+
return { skills, unknown };
|
|
11408
|
+
}
|
|
11409
|
+
var BUILDERS, BUILTIN_DATASOURCE_SKILL_NAMES;
|
|
11410
|
+
var init_factory = __esm(() => {
|
|
11411
|
+
init_discord();
|
|
11412
|
+
init_gdrive();
|
|
11413
|
+
init_rclone_connector();
|
|
11414
|
+
init_github();
|
|
11415
|
+
init_himalaya_connector();
|
|
11416
|
+
init_gmail();
|
|
11417
|
+
init_mail_export();
|
|
11418
|
+
init_notion();
|
|
11419
|
+
init_obsidian();
|
|
11420
|
+
init_rss();
|
|
11421
|
+
init_slack();
|
|
11422
|
+
init_spotlight();
|
|
11423
|
+
BUILDERS = {
|
|
11424
|
+
slack: (config, workspaceRoot) => new SlackSkill({ ...common(config, workspaceRoot), connectorOptions: config.connector }),
|
|
11425
|
+
discord: (config, workspaceRoot) => new DiscordSkill({
|
|
11426
|
+
...common(config, workspaceRoot),
|
|
11427
|
+
connectorOptions: config.connector
|
|
11428
|
+
}),
|
|
11429
|
+
notion: (config, workspaceRoot) => new NotionSkill({
|
|
11430
|
+
...common(config, workspaceRoot),
|
|
11431
|
+
connectorOptions: config.connector
|
|
11432
|
+
}),
|
|
11433
|
+
github: (config, workspaceRoot) => new GitHubSkill({
|
|
11434
|
+
...common(config, workspaceRoot),
|
|
11435
|
+
connectorOptions: config.connector
|
|
11436
|
+
}),
|
|
11437
|
+
gdrive: (config, workspaceRoot) => {
|
|
11438
|
+
const connector = config.connector;
|
|
11439
|
+
if (connector?.backend === "rclone") {
|
|
11440
|
+
const { backend: _backend, ...rcloneOptions } = connector;
|
|
11441
|
+
return new GDriveSkill({
|
|
11442
|
+
...common(config, workspaceRoot),
|
|
11443
|
+
connector: new RcloneConnector(rcloneOptions)
|
|
11444
|
+
});
|
|
11445
|
+
}
|
|
11446
|
+
return new GDriveSkill({
|
|
11447
|
+
...common(config, workspaceRoot),
|
|
11448
|
+
connectorOptions: connector
|
|
11449
|
+
});
|
|
11450
|
+
},
|
|
11451
|
+
gmail: (config, workspaceRoot) => {
|
|
11452
|
+
const connector = config.connector;
|
|
11453
|
+
if (connector?.backend === "himalaya") {
|
|
11454
|
+
const { backend: _backend, ...himalayaOptions } = connector;
|
|
11455
|
+
return new GmailSkill({
|
|
11456
|
+
...common(config, workspaceRoot),
|
|
11457
|
+
connector: new HimalayaConnector(himalayaOptions)
|
|
11458
|
+
});
|
|
11459
|
+
}
|
|
11460
|
+
return new GmailSkill({ ...common(config, workspaceRoot), connectorOptions: connector });
|
|
11461
|
+
},
|
|
11462
|
+
"mail-export": (config, workspaceRoot) => new MailExportSkill({
|
|
11463
|
+
...common(config, workspaceRoot),
|
|
11464
|
+
connectorOptions: config.connector
|
|
11465
|
+
}),
|
|
11466
|
+
obsidian: (config, workspaceRoot) => new ObsidianSkill({
|
|
11467
|
+
...common(config, workspaceRoot),
|
|
11468
|
+
connectorOptions: config.connector
|
|
11469
|
+
}),
|
|
11470
|
+
rss: (config, workspaceRoot) => new RssSkill({ ...common(config, workspaceRoot), connectorOptions: config.connector }),
|
|
11471
|
+
spotlight: (config, workspaceRoot) => new SpotlightSkill({
|
|
11472
|
+
...common(config, workspaceRoot),
|
|
11473
|
+
connectorOptions: config.connector
|
|
11474
|
+
})
|
|
11475
|
+
};
|
|
11476
|
+
BUILTIN_DATASOURCE_SKILL_NAMES = Object.keys(BUILDERS);
|
|
11477
|
+
});
|
|
11478
|
+
|
|
11479
|
+
// src/index.ts
|
|
11480
|
+
init_agent();
|
|
11481
|
+
|
|
11482
|
+
// src/agent/index.ts
|
|
11483
|
+
init_agent();
|
|
11484
|
+
init_datasource_skill();
|
|
11485
|
+
init_emit_results_tool();
|
|
11486
|
+
init_jikji_find_tool();
|
|
11487
|
+
init_search_all_tool();
|
|
11488
|
+
init_search_datasource_tool();
|
|
11489
|
+
init_search_minsync_tool();
|
|
11490
|
+
init_system_prompt();
|
|
11491
|
+
|
|
11492
|
+
// src/index.ts
|
|
11493
|
+
init_system_prompt();
|
|
11494
|
+
|
|
11495
|
+
// src/datasource/index.ts
|
|
11496
|
+
init_access_context();
|
|
11497
|
+
init_chunk_store();
|
|
11498
|
+
init_connector();
|
|
11499
|
+
init_connector_skill();
|
|
11500
|
+
|
|
11501
|
+
// src/datasource/polling.ts
|
|
11502
|
+
function isDue(metadata, now, lastIndexedAt) {
|
|
11503
|
+
if (metadata.mode !== "poll")
|
|
8525
11504
|
return false;
|
|
8526
|
-
|
|
11505
|
+
const intervalMs = metadata.intervalMs;
|
|
11506
|
+
if (intervalMs === undefined || intervalMs <= 0)
|
|
8527
11507
|
return false;
|
|
8528
|
-
const
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
if (datasourceSourceHasFragment(source))
|
|
11508
|
+
const last = lastIndexedAt ?? metadata.lastIndexedAt;
|
|
11509
|
+
if (last === undefined)
|
|
11510
|
+
return true;
|
|
11511
|
+
if (!Number.isFinite(now) || !Number.isFinite(last))
|
|
8533
11512
|
return false;
|
|
8534
|
-
return
|
|
11513
|
+
return now - last >= intervalMs;
|
|
11514
|
+
}
|
|
11515
|
+
function parseCronExpr(expr) {
|
|
11516
|
+
if (typeof expr !== "string") {
|
|
11517
|
+
return fail("cron expression must be a string");
|
|
11518
|
+
}
|
|
11519
|
+
const trimmed = expr.trim();
|
|
11520
|
+
if (trimmed.length === 0) {
|
|
11521
|
+
return fail("cron expression is empty");
|
|
11522
|
+
}
|
|
11523
|
+
const fields = trimmed.split(/\s+/u);
|
|
11524
|
+
if (fields.length !== 5) {
|
|
11525
|
+
return fail(`expected 5 fields, got ${fields.length}`);
|
|
11526
|
+
}
|
|
11527
|
+
const [minute, hour, dom, month, dow] = fields;
|
|
11528
|
+
const errors = [];
|
|
11529
|
+
validateField(minute, 0, 59, "minute", errors);
|
|
11530
|
+
validateField(hour, 0, 23, "hour", errors);
|
|
11531
|
+
validateField(dom, 1, 31, "day-of-month", errors);
|
|
11532
|
+
validateField(month, 1, 12, "month", errors);
|
|
11533
|
+
validateField(dow, 0, 7, "day-of-week", errors);
|
|
11534
|
+
if (errors.length > 0) {
|
|
11535
|
+
return fail(errors.join("; "));
|
|
11536
|
+
}
|
|
11537
|
+
return { ok: true, expr: trimmed };
|
|
11538
|
+
}
|
|
11539
|
+
function fail(error) {
|
|
11540
|
+
return { ok: false, error };
|
|
11541
|
+
}
|
|
11542
|
+
function validateField(field, min, max, label, errors) {
|
|
11543
|
+
const parts = field.split(",");
|
|
11544
|
+
if (parts.length === 0) {
|
|
11545
|
+
errors.push(`${label}: empty`);
|
|
11546
|
+
return;
|
|
11547
|
+
}
|
|
11548
|
+
for (const part of parts) {
|
|
11549
|
+
const err = validatePart(part, min, max, label);
|
|
11550
|
+
if (err !== null)
|
|
11551
|
+
errors.push(err);
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11554
|
+
function validatePart(part, min, max, label) {
|
|
11555
|
+
if (part.length === 0)
|
|
11556
|
+
return `${label}: empty term`;
|
|
11557
|
+
const slashIndex = part.indexOf("/");
|
|
11558
|
+
let base = part;
|
|
11559
|
+
let stepText;
|
|
11560
|
+
if (slashIndex >= 0) {
|
|
11561
|
+
base = part.slice(0, slashIndex);
|
|
11562
|
+
stepText = part.slice(slashIndex + 1);
|
|
11563
|
+
const step = parseNumber(stepText);
|
|
11564
|
+
if (step === null || step <= 0) {
|
|
11565
|
+
return `${label}: invalid step "${stepText}"`;
|
|
11566
|
+
}
|
|
11567
|
+
}
|
|
11568
|
+
if (base === "*")
|
|
11569
|
+
return null;
|
|
11570
|
+
if (base.includes("-")) {
|
|
11571
|
+
const [loText, hiText] = base.split("-");
|
|
11572
|
+
if (hiText === undefined || loText === undefined) {
|
|
11573
|
+
return `${label}: malformed range "${base}"`;
|
|
11574
|
+
}
|
|
11575
|
+
const lo = parseNumber(loText);
|
|
11576
|
+
const hi = parseNumber(hiText);
|
|
11577
|
+
if (lo === null || hi === null) {
|
|
11578
|
+
return `${label}: non-numeric range "${base}"`;
|
|
11579
|
+
}
|
|
11580
|
+
if (lo < min || lo > max || hi < min || hi > max) {
|
|
11581
|
+
return `${label}: range "${base}" out of bounds (${min}-${max})`;
|
|
11582
|
+
}
|
|
11583
|
+
if (lo > hi) {
|
|
11584
|
+
return `${label}: range "${base}" has low > high`;
|
|
11585
|
+
}
|
|
11586
|
+
return null;
|
|
11587
|
+
}
|
|
11588
|
+
const v = parseNumber(base);
|
|
11589
|
+
if (v === null) {
|
|
11590
|
+
return `${label}: invalid value "${base}"`;
|
|
11591
|
+
}
|
|
11592
|
+
if (v < min || v > max) {
|
|
11593
|
+
return `${label}: value ${v} out of bounds (${min}-${max})`;
|
|
11594
|
+
}
|
|
11595
|
+
return null;
|
|
11596
|
+
}
|
|
11597
|
+
function parseNumber(text) {
|
|
11598
|
+
if (!/^\d+$/u.test(text))
|
|
11599
|
+
return null;
|
|
11600
|
+
return Number.parseInt(text, 10);
|
|
8535
11601
|
}
|
|
8536
|
-
|
|
8537
11602
|
// src/datasource/registry.ts
|
|
11603
|
+
init_scope();
|
|
11604
|
+
|
|
8538
11605
|
class DatasourceSkillRegistry {
|
|
8539
11606
|
skills = new Map;
|
|
8540
11607
|
register(skill) {
|
|
@@ -8597,16 +11664,25 @@ function safePolling(skill) {
|
|
|
8597
11664
|
return;
|
|
8598
11665
|
}
|
|
8599
11666
|
}
|
|
11667
|
+
|
|
11668
|
+
// src/datasource/index.ts
|
|
11669
|
+
init_scope();
|
|
11670
|
+
init_discord();
|
|
11671
|
+
init_factory();
|
|
11672
|
+
init_gdrive();
|
|
11673
|
+
init_github();
|
|
11674
|
+
init_gmail();
|
|
11675
|
+
|
|
8600
11676
|
// src/datasource/skills/katok/client.ts
|
|
8601
|
-
import { spawn as
|
|
11677
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
8602
11678
|
|
|
8603
11679
|
// src/datasource/skills/katok/paths.ts
|
|
8604
|
-
import { join as
|
|
11680
|
+
import { join as join20 } from "node:path";
|
|
8605
11681
|
var AUTORAG_DIRNAME = ".autorag";
|
|
8606
11682
|
var DATASOURCES_DIRNAME = "datasources";
|
|
8607
11683
|
var KATOK_DATASOURCE_DIRNAME = "katok";
|
|
8608
11684
|
function katokDatasourceRoot(workspaceRoot) {
|
|
8609
|
-
return
|
|
11685
|
+
return join20(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, KATOK_DATASOURCE_DIRNAME);
|
|
8610
11686
|
}
|
|
8611
11687
|
|
|
8612
11688
|
// src/datasource/skills/katok/types.ts
|
|
@@ -8720,7 +11796,7 @@ class KatokClient {
|
|
|
8720
11796
|
function spawnKatok(request) {
|
|
8721
11797
|
return new Promise((resolve9) => {
|
|
8722
11798
|
const { options, args, env, signal } = request;
|
|
8723
|
-
const child =
|
|
11799
|
+
const child = spawn8(commandFor2(options.binaryPath), args, {
|
|
8724
11800
|
env,
|
|
8725
11801
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8726
11802
|
});
|
|
@@ -8871,22 +11947,22 @@ function parseJsonObject(stdout) {
|
|
|
8871
11947
|
return;
|
|
8872
11948
|
return parsed;
|
|
8873
11949
|
}
|
|
8874
|
-
function
|
|
11950
|
+
function asRecord3(value) {
|
|
8875
11951
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
8876
11952
|
return;
|
|
8877
11953
|
return value;
|
|
8878
11954
|
}
|
|
8879
|
-
function
|
|
11955
|
+
function asString2(value) {
|
|
8880
11956
|
return typeof value === "string" ? value : undefined;
|
|
8881
11957
|
}
|
|
8882
|
-
function
|
|
11958
|
+
function asNumber2(value) {
|
|
8883
11959
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
8884
11960
|
}
|
|
8885
11961
|
function asBoolean(value) {
|
|
8886
11962
|
return typeof value === "boolean" ? value : undefined;
|
|
8887
11963
|
}
|
|
8888
11964
|
function normalizeDoctor(raw) {
|
|
8889
|
-
const version =
|
|
11965
|
+
const version = asString2(raw.version);
|
|
8890
11966
|
const ready = asBoolean(raw.ready);
|
|
8891
11967
|
if (ready === undefined)
|
|
8892
11968
|
return;
|
|
@@ -8897,12 +11973,12 @@ function normalizeSync(raw) {
|
|
|
8897
11973
|
const synced = asBoolean(raw.synced);
|
|
8898
11974
|
if (synced === undefined)
|
|
8899
11975
|
return;
|
|
8900
|
-
const messageCount =
|
|
11976
|
+
const messageCount = asNumber2(raw.messageCount);
|
|
8901
11977
|
const metadata = stripKnown(raw, new Set(["synced", "messageCount"]));
|
|
8902
11978
|
return { synced, ...messageCount !== undefined ? { messageCount } : {}, metadata };
|
|
8903
11979
|
}
|
|
8904
11980
|
function normalizeIndex(raw) {
|
|
8905
|
-
const chunkCount =
|
|
11981
|
+
const chunkCount = asNumber2(raw.chunkCount);
|
|
8906
11982
|
if (chunkCount === undefined)
|
|
8907
11983
|
return;
|
|
8908
11984
|
const metadata = stripKnown(raw, new Set(["chunkCount"]));
|
|
@@ -8914,12 +11990,12 @@ function normalizeHits(raw) {
|
|
|
8914
11990
|
return;
|
|
8915
11991
|
const normalized = [];
|
|
8916
11992
|
for (const entry of hits) {
|
|
8917
|
-
const record =
|
|
11993
|
+
const record = asRecord3(entry);
|
|
8918
11994
|
if (record === undefined)
|
|
8919
11995
|
return;
|
|
8920
|
-
const chunkId =
|
|
8921
|
-
const score =
|
|
8922
|
-
const content =
|
|
11996
|
+
const chunkId = asString2(record.chunkId);
|
|
11997
|
+
const score = asNumber2(record.score);
|
|
11998
|
+
const content = asString2(record.content);
|
|
8923
11999
|
if (chunkId === undefined || chunkId.length === 0 || score === undefined || content === undefined)
|
|
8924
12000
|
return;
|
|
8925
12001
|
const metadata = stripKnown(record, new Set(["chunkId", "score", "content"]));
|
|
@@ -8928,8 +12004,8 @@ function normalizeHits(raw) {
|
|
|
8928
12004
|
return normalized;
|
|
8929
12005
|
}
|
|
8930
12006
|
function normalizeChunk(raw) {
|
|
8931
|
-
const chunkId =
|
|
8932
|
-
const content =
|
|
12007
|
+
const chunkId = asString2(raw.chunkId);
|
|
12008
|
+
const content = asString2(raw.content);
|
|
8933
12009
|
if (chunkId === undefined || chunkId.length === 0 || content === undefined)
|
|
8934
12010
|
return;
|
|
8935
12011
|
const metadata = stripKnown(raw, new Set(["chunkId", "content"]));
|
|
@@ -8941,7 +12017,7 @@ function normalizeContext(raw) {
|
|
|
8941
12017
|
return;
|
|
8942
12018
|
const chunks = [];
|
|
8943
12019
|
for (const entry of chunksValue) {
|
|
8944
|
-
const record =
|
|
12020
|
+
const record = asRecord3(entry);
|
|
8945
12021
|
if (record === undefined)
|
|
8946
12022
|
return;
|
|
8947
12023
|
const chunk = normalizeChunk(record);
|
|
@@ -8979,9 +12055,10 @@ function ok(data, result) {
|
|
|
8979
12055
|
return { ok: true, data, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
|
|
8980
12056
|
}
|
|
8981
12057
|
// src/datasource/skills/katok/methods.ts
|
|
12058
|
+
init_scope();
|
|
8982
12059
|
var KAKAO_DATASOURCE_ID = "kakao";
|
|
8983
12060
|
var DEFAULT_KAKAO_TAGS = ["kakaotalk", "personal", "pii"];
|
|
8984
|
-
var
|
|
12061
|
+
var DEFAULT_TOP_K3 = 20;
|
|
8985
12062
|
|
|
8986
12063
|
class KatokBm25Method {
|
|
8987
12064
|
client;
|
|
@@ -9036,7 +12113,7 @@ async function retrieveKatok(client, mode, methodName, instanceId, query, option
|
|
|
9036
12113
|
const trimmed = query.trim();
|
|
9037
12114
|
if (trimmed.length === 0)
|
|
9038
12115
|
return [];
|
|
9039
|
-
const topK = options.topK ??
|
|
12116
|
+
const topK = options.topK ?? DEFAULT_TOP_K3;
|
|
9040
12117
|
const searchOptions = { topK, signal: options.signal };
|
|
9041
12118
|
let result;
|
|
9042
12119
|
try {
|
|
@@ -9086,10 +12163,11 @@ function toRetrievalResult(hit, source, methodName, instanceId, mode) {
|
|
|
9086
12163
|
};
|
|
9087
12164
|
}
|
|
9088
12165
|
// src/datasource/skills/katok/skill.ts
|
|
12166
|
+
init_scope();
|
|
9089
12167
|
var KAKAO_DATASOURCE_ID2 = "kakao";
|
|
9090
12168
|
var KAKAO_SKILL_TYPE = "kakaotalk";
|
|
9091
|
-
var
|
|
9092
|
-
var
|
|
12169
|
+
var DEFAULT_INSTANCE_ID2 = "default";
|
|
12170
|
+
var DEFAULT_POLLING_INTERVAL_MS2 = 15 * 60 * 1000;
|
|
9093
12171
|
var DEFAULT_KAKAO_TAGS2 = ["kakaotalk", "personal", "pii"];
|
|
9094
12172
|
|
|
9095
12173
|
class KatokSkill {
|
|
@@ -9101,9 +12179,9 @@ class KatokSkill {
|
|
|
9101
12179
|
lastIndexedAt;
|
|
9102
12180
|
constructor(options) {
|
|
9103
12181
|
this.client = options.client;
|
|
9104
|
-
this.instanceId = options.instanceId ??
|
|
12182
|
+
this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID2;
|
|
9105
12183
|
this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
|
|
9106
|
-
this.pollingIntervalMs = options.pollingIntervalMs ??
|
|
12184
|
+
this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS2;
|
|
9107
12185
|
this.tags = options.tags ?? DEFAULT_KAKAO_TAGS2;
|
|
9108
12186
|
this.lastIndexedAt = options.lastIndexedAt;
|
|
9109
12187
|
}
|
|
@@ -9240,6 +12318,14 @@ function sanitizeDiagnosticText2(value) {
|
|
|
9240
12318
|
}
|
|
9241
12319
|
return value;
|
|
9242
12320
|
}
|
|
12321
|
+
// src/datasource/index.ts
|
|
12322
|
+
init_mail_export();
|
|
12323
|
+
init_notion();
|
|
12324
|
+
init_obsidian();
|
|
12325
|
+
init_rss();
|
|
12326
|
+
init_slack();
|
|
12327
|
+
init_spotlight();
|
|
12328
|
+
|
|
9243
12329
|
// src/index.ts
|
|
9244
12330
|
init_jikji();
|
|
9245
12331
|
|
|
@@ -9274,6 +12360,7 @@ export {
|
|
|
9274
12360
|
syncMinSyncWorkspace,
|
|
9275
12361
|
selectReleaseAsset,
|
|
9276
12362
|
saveMirrorIndex,
|
|
12363
|
+
sanitizeIdSegment,
|
|
9277
12364
|
rewriteEmbedderConfig,
|
|
9278
12365
|
resolveReturnedPath,
|
|
9279
12366
|
renderMemoryContext,
|
|
@@ -9311,15 +12398,25 @@ export {
|
|
|
9311
12398
|
detectMirrorStaleness,
|
|
9312
12399
|
datasourceSourcePath,
|
|
9313
12400
|
datasourceSourceHasFragment,
|
|
12401
|
+
datasourceChunkStorePath,
|
|
9314
12402
|
createEmitResultsTool,
|
|
9315
12403
|
createDefaultParserRegistry,
|
|
9316
12404
|
createCheckMemoryTool,
|
|
12405
|
+
connectorFailureToDiagnosticCode,
|
|
9317
12406
|
cachedJikjiBinaryPath,
|
|
9318
12407
|
buildSystemPrompt,
|
|
9319
12408
|
buildMinSyncPathMap,
|
|
12409
|
+
buildDatasourceSkills,
|
|
9320
12410
|
buildDatasourceInstanceSource,
|
|
9321
12411
|
buildDatasourceChunkSource,
|
|
12412
|
+
boundDiagnosticText,
|
|
9322
12413
|
XlsxParser,
|
|
12414
|
+
SpotlightSkill,
|
|
12415
|
+
SpotlightConnector,
|
|
12416
|
+
SlackSkill,
|
|
12417
|
+
SlackConnector,
|
|
12418
|
+
RssSkill,
|
|
12419
|
+
RssConnector,
|
|
9323
12420
|
RetrievalMethodRegistry,
|
|
9324
12421
|
RetrievalMemory,
|
|
9325
12422
|
ResultMerger,
|
|
@@ -9330,9 +12427,15 @@ export {
|
|
|
9330
12427
|
ParseError,
|
|
9331
12428
|
ParallelRetriever,
|
|
9332
12429
|
OpendataloaderPdfParser,
|
|
12430
|
+
ObsidianSkill,
|
|
12431
|
+
ObsidianConnector,
|
|
12432
|
+
NotionSkill,
|
|
12433
|
+
NotionConnector,
|
|
9333
12434
|
MinSyncVectorMethod,
|
|
9334
12435
|
MinSyncReleaseError,
|
|
9335
12436
|
MinSyncClient,
|
|
12437
|
+
MailExportSkill,
|
|
12438
|
+
MailExportConnector,
|
|
9336
12439
|
MINSYNC_SUBDIR,
|
|
9337
12440
|
MINSYNC_FILES_SUBDIR,
|
|
9338
12441
|
MINSYNC_CONFIG_FILE,
|
|
@@ -9346,15 +12449,27 @@ export {
|
|
|
9346
12449
|
JIKJI_CRATE_NAME,
|
|
9347
12450
|
ImageOcrParser,
|
|
9348
12451
|
HwpParser,
|
|
12452
|
+
GmailSkill,
|
|
12453
|
+
GmailConnector,
|
|
12454
|
+
GitHubSkill,
|
|
12455
|
+
GitHubConnector,
|
|
12456
|
+
GDriveSkill,
|
|
12457
|
+
GDriveConnector,
|
|
9349
12458
|
EmlParser,
|
|
9350
12459
|
EMIT_AUTORAG_RESULTS_TOOL_NAME,
|
|
9351
12460
|
DocxParser,
|
|
12461
|
+
DiscordSkill,
|
|
12462
|
+
DiscordConnector,
|
|
9352
12463
|
DatasourceSkillRegistry,
|
|
9353
12464
|
DatasourceResultFilter,
|
|
12465
|
+
DatasourceChunkStore,
|
|
9354
12466
|
DatasourceAccessContext,
|
|
9355
12467
|
DEFAULT_MAX_SOURCE_BYTES,
|
|
9356
12468
|
DEFAULT_JIKJI_OPTIONS,
|
|
9357
12469
|
DATASOURCE_CHUNKS_SEGMENT,
|
|
12470
|
+
ConnectorLexicalMethod,
|
|
12471
|
+
ConnectorDatasourceSkill,
|
|
12472
|
+
BUILTIN_DATASOURCE_SKILL_NAMES,
|
|
9358
12473
|
BM25UnavailableError,
|
|
9359
12474
|
BM25Method,
|
|
9360
12475
|
AutoRAGAgent
|