@arabold/docs-mcp-server 1.25.2 → 1.26.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 +6 -1
- package/dist/android-icon-144x144.png +0 -0
- package/dist/android-icon-192x192.png +0 -0
- package/dist/android-icon-36x36.png +0 -0
- package/dist/android-icon-48x48.png +0 -0
- package/dist/android-icon-72x72.png +0 -0
- package/dist/android-icon-96x96.png +0 -0
- package/dist/apple-icon-114x114.png +0 -0
- package/dist/apple-icon-120x120.png +0 -0
- package/dist/apple-icon-144x144.png +0 -0
- package/dist/apple-icon-152x152.png +0 -0
- package/dist/apple-icon-180x180.png +0 -0
- package/dist/apple-icon-57x57.png +0 -0
- package/dist/apple-icon-60x60.png +0 -0
- package/dist/apple-icon-72x72.png +0 -0
- package/dist/apple-icon-76x76.png +0 -0
- package/dist/apple-icon-precomposed.png +0 -0
- package/dist/apple-icon.png +0 -0
- package/dist/assets/android-icon-144x144.png +0 -0
- package/dist/assets/android-icon-192x192.png +0 -0
- package/dist/assets/android-icon-36x36.png +0 -0
- package/dist/assets/android-icon-48x48.png +0 -0
- package/dist/assets/android-icon-72x72.png +0 -0
- package/dist/assets/android-icon-96x96.png +0 -0
- package/dist/assets/apple-icon-114x114.png +0 -0
- package/dist/assets/apple-icon-120x120.png +0 -0
- package/dist/assets/apple-icon-144x144.png +0 -0
- package/dist/assets/apple-icon-152x152.png +0 -0
- package/dist/assets/apple-icon-180x180.png +0 -0
- package/dist/assets/apple-icon-57x57.png +0 -0
- package/dist/assets/apple-icon-60x60.png +0 -0
- package/dist/assets/apple-icon-72x72.png +0 -0
- package/dist/assets/apple-icon-76x76.png +0 -0
- package/dist/assets/apple-icon-precomposed.png +0 -0
- package/dist/assets/apple-icon.png +0 -0
- package/dist/assets/favicon-16x16.png +0 -0
- package/dist/assets/favicon-32x32.png +0 -0
- package/dist/assets/favicon-96x96.png +0 -0
- package/dist/assets/favicon.ico +0 -0
- package/dist/assets/main.css +1 -1
- package/dist/assets/main.js +167 -81
- package/dist/assets/main.js.map +1 -1
- package/dist/assets/manifest.json +47 -0
- package/dist/assets/ms-icon-144x144.png +0 -0
- package/dist/assets/ms-icon-150x150.png +0 -0
- package/dist/assets/ms-icon-310x310.png +0 -0
- package/dist/assets/ms-icon-70x70.png +0 -0
- package/dist/favicon-16x16.png +0 -0
- package/dist/favicon-32x32.png +0 -0
- package/dist/favicon-96x96.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.js +930 -224
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +47 -0
- package/dist/ms-icon-144x144.png +0 -0
- package/dist/ms-icon-150x150.png +0 -0
- package/dist/ms-icon-310x310.png +0 -0
- package/dist/ms-icon-70x70.png +0 -0
- package/package.json +3 -7
- package/public/android-icon-144x144.png +0 -0
- package/public/android-icon-192x192.png +0 -0
- package/public/android-icon-36x36.png +0 -0
- package/public/android-icon-48x48.png +0 -0
- package/public/android-icon-72x72.png +0 -0
- package/public/android-icon-96x96.png +0 -0
- package/public/apple-icon-114x114.png +0 -0
- package/public/apple-icon-120x120.png +0 -0
- package/public/apple-icon-144x144.png +0 -0
- package/public/apple-icon-152x152.png +0 -0
- package/public/apple-icon-180x180.png +0 -0
- package/public/apple-icon-57x57.png +0 -0
- package/public/apple-icon-60x60.png +0 -0
- package/public/apple-icon-72x72.png +0 -0
- package/public/apple-icon-76x76.png +0 -0
- package/public/apple-icon-precomposed.png +0 -0
- package/public/apple-icon.png +0 -0
- package/public/assets/android-icon-144x144.png +0 -0
- package/public/assets/android-icon-192x192.png +0 -0
- package/public/assets/android-icon-36x36.png +0 -0
- package/public/assets/android-icon-48x48.png +0 -0
- package/public/assets/android-icon-72x72.png +0 -0
- package/public/assets/android-icon-96x96.png +0 -0
- package/public/assets/apple-icon-114x114.png +0 -0
- package/public/assets/apple-icon-120x120.png +0 -0
- package/public/assets/apple-icon-144x144.png +0 -0
- package/public/assets/apple-icon-152x152.png +0 -0
- package/public/assets/apple-icon-180x180.png +0 -0
- package/public/assets/apple-icon-57x57.png +0 -0
- package/public/assets/apple-icon-60x60.png +0 -0
- package/public/assets/apple-icon-72x72.png +0 -0
- package/public/assets/apple-icon-76x76.png +0 -0
- package/public/assets/apple-icon-precomposed.png +0 -0
- package/public/assets/apple-icon.png +0 -0
- package/public/assets/favicon-16x16.png +0 -0
- package/public/assets/favicon-32x32.png +0 -0
- package/public/assets/favicon-96x96.png +0 -0
- package/public/assets/favicon.ico +0 -0
- package/public/assets/main.css +1 -1
- package/public/assets/main.js +167 -81
- package/public/assets/main.js.map +1 -1
- package/public/assets/manifest.json +47 -0
- package/public/assets/ms-icon-144x144.png +0 -0
- package/public/assets/ms-icon-150x150.png +0 -0
- package/public/assets/ms-icon-310x310.png +0 -0
- package/public/assets/ms-icon-70x70.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon-96x96.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/manifest.json +47 -0
- package/public/ms-icon-144x144.png +0 -0
- package/public/ms-icon-150x150.png +0 -0
- package/public/ms-icon-310x310.png +0 -0
- package/public/ms-icon-70x70.png +0 -0
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { PostHog } from "posthog-node";
|
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import fs, { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import path from "node:path";
|
|
12
|
+
import { fileURLToPath, URL as URL$1 } from "node:url";
|
|
12
13
|
import envPaths from "env-paths";
|
|
13
14
|
import { Option, Command } from "commander";
|
|
14
15
|
import formBody from "@fastify/formbody";
|
|
@@ -22,11 +23,10 @@ import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
|
|
|
22
23
|
import { v4 } from "uuid";
|
|
23
24
|
import { VirtualConsole, JSDOM } from "jsdom";
|
|
24
25
|
import mime from "mime";
|
|
25
|
-
import { fileURLToPath, URL as URL$1 } from "node:url";
|
|
26
26
|
import psl from "psl";
|
|
27
|
+
import { HeaderGenerator } from "header-generator";
|
|
27
28
|
import fs$1 from "node:fs/promises";
|
|
28
29
|
import axios from "axios";
|
|
29
|
-
import { HeaderGenerator } from "header-generator";
|
|
30
30
|
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
|
|
31
31
|
import remarkGfm from "remark-gfm";
|
|
32
32
|
import remarkHtml from "remark-html";
|
|
@@ -539,6 +539,49 @@ class PostHogClient {
|
|
|
539
539
|
return this.enabled && !!this.client;
|
|
540
540
|
}
|
|
541
541
|
}
|
|
542
|
+
let projectRoot = null;
|
|
543
|
+
function getProjectRoot() {
|
|
544
|
+
if (projectRoot) {
|
|
545
|
+
return projectRoot;
|
|
546
|
+
}
|
|
547
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
548
|
+
let currentDir = path.dirname(currentFilePath);
|
|
549
|
+
while (true) {
|
|
550
|
+
const packageJsonPath = path.join(currentDir, "package.json");
|
|
551
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
552
|
+
projectRoot = currentDir;
|
|
553
|
+
return currentDir;
|
|
554
|
+
}
|
|
555
|
+
const parentDir = path.dirname(currentDir);
|
|
556
|
+
if (parentDir === currentDir) {
|
|
557
|
+
throw new Error("Could not find project root containing package.json.");
|
|
558
|
+
}
|
|
559
|
+
currentDir = parentDir;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function resolveStorePath(storePath) {
|
|
563
|
+
let dbDir;
|
|
564
|
+
if (storePath) {
|
|
565
|
+
dbDir = storePath;
|
|
566
|
+
} else {
|
|
567
|
+
const projectRoot2 = getProjectRoot();
|
|
568
|
+
const oldDbDir = path.join(projectRoot2, ".store");
|
|
569
|
+
const oldDbPath = path.join(oldDbDir, "documents.db");
|
|
570
|
+
const oldDbExists = fs.existsSync(oldDbPath);
|
|
571
|
+
if (oldDbExists) {
|
|
572
|
+
dbDir = oldDbDir;
|
|
573
|
+
} else {
|
|
574
|
+
const standardPaths = envPaths("docs-mcp-server", { suffix: "" });
|
|
575
|
+
dbDir = standardPaths.data;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
fs.mkdirSync(dbDir, { recursive: true });
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.warn(`⚠️ Failed to create database directory ${dbDir}: ${error}`);
|
|
582
|
+
}
|
|
583
|
+
return dbDir;
|
|
584
|
+
}
|
|
542
585
|
class TelemetryConfig {
|
|
543
586
|
static instance;
|
|
544
587
|
enabled = true;
|
|
@@ -560,7 +603,7 @@ class TelemetryConfig {
|
|
|
560
603
|
}
|
|
561
604
|
function generateInstallationId(storePath) {
|
|
562
605
|
try {
|
|
563
|
-
const dataDir = storePath
|
|
606
|
+
const dataDir = resolveStorePath(storePath);
|
|
564
607
|
const installationIdPath = path.join(dataDir, "installation.id");
|
|
565
608
|
if (fs.existsSync(installationIdPath)) {
|
|
566
609
|
const existingId = fs.readFileSync(installationIdPath, "utf8").trim();
|
|
@@ -709,14 +752,14 @@ function extractProtocol(urlOrPath) {
|
|
|
709
752
|
}
|
|
710
753
|
}
|
|
711
754
|
const name = "@arabold/docs-mcp-server";
|
|
712
|
-
const version = "1.25.
|
|
755
|
+
const version = "1.25.3";
|
|
713
756
|
const description = "MCP server for fetching and searching documentation";
|
|
714
757
|
const type = "module";
|
|
715
758
|
const bin = { "docs-mcp-server": "dist/index.js" };
|
|
716
759
|
const license = "MIT";
|
|
717
760
|
const repository = { "type": "git", "url": "git+https://github.com/arabold/docs-mcp-server.git" };
|
|
718
761
|
const files = ["dist", "public", "db", "README.md", "LICENSE", "package.json"];
|
|
719
|
-
const scripts = { "prepare": "husky || true", "build": "vite build --config vite.config.web.ts && vite build", "start": "node --enable-source-maps dist/index.js", "cli": "node --enable-source-maps dist/index.js", "server": "node --enable-source-maps dist/index.ts", "web": "node --enable-source-maps dist/index.ts web", "dev": "
|
|
762
|
+
const scripts = { "prepare": "husky || true", "build": "vite build --config vite.config.web.ts && vite build", "start": "node --enable-source-maps dist/index.js", "cli": "node --enable-source-maps dist/index.js", "server": "node --enable-source-maps dist/index.ts", "web": "node --enable-source-maps dist/index.ts web", "dev": "npm-run-all --parallel dev:server dev:web", "dev:server": "vite-node --watch src/index.ts", "dev:web": "vite build --config vite.config.web.ts --watch", "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage", "test:e2e": "vitest run --config test/vitest.config.ts", "test:e2e:watch": "vitest --config test/vitest.config.ts", "lint": "biome check .", "lint:fix": "biome check . --fix", "format": "biome format . --write", "postinstall": "echo 'Skipping Playwright browser install. See README.md for details.'" };
|
|
720
763
|
const dependencies = { "@fastify/formbody": "^8.0.2", "@fastify/static": "^8.2.0", "@joplin/turndown-plugin-gfm": "^1.0.62", "@kitajs/html": "^4.2.9", "@kitajs/ts-html-plugin": "^4.1.1", "@langchain/aws": "^0.1.13", "@langchain/google-genai": "^0.2.16", "@langchain/google-vertexai": "^0.2.16", "@langchain/openai": "^0.6.3", "@modelcontextprotocol/sdk": "^1.17.1", "@trpc/client": "^11.4.4", "@trpc/server": "^11.4.4", "alpinejs": "^3.14.9", "axios": "^1.11.0", "axios-retry": "^4.5.0", "better-sqlite3": "^12.2.0", "cheerio": "^1.1.2", "commander": "^14.0.0", "dompurify": "^3.2.6", "dotenv": "^17.2.1", "env-paths": "^3.0.0", "fastify": "^5.4.0", "flowbite": "^3.1.2", "fuse.js": "^7.1.0", "header-generator": "^2.1.69", "htmx.org": "^2.0.6", "iconv-lite": "^0.6.3", "jose": "^6.0.12", "jsdom": "^26.1.0", "langchain": "^0.3.30", "mime": "^4.0.7", "minimatch": "^10.0.1", "playwright": "^1.52.0", "posthog-node": "^5.7.0", "psl": "^1.15.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", "semver": "^7.7.2", "sqlite-vec": "^0.1.7-alpha.2", "tree-sitter": "^0.21.1", "tree-sitter-javascript": "^0.23.1", "tree-sitter-python": "^0.21.0", "tree-sitter-typescript": "^0.23.2", "turndown": "^7.2.0", "zod": "^4.0.14" };
|
|
721
764
|
const devDependencies = { "@biomejs/biome": "^2.1.3", "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^11.0.3", "@semantic-release/npm": "^12.0.2", "@tailwindcss/postcss": "^4.1.11", "@tailwindcss/vite": "^4.1.11", "@types/alpinejs": "^3.13.11", "@types/better-sqlite3": "^7.6.13", "@types/jsdom": "~21.1.7", "@types/lint-staged": "~13.3.0", "@types/node": "^24.1.0", "@types/node-fetch": "^2.6.13", "@types/psl": "^1.1.3", "@types/semver": "^7.7.0", "@types/turndown": "^5.0.5", "autoprefixer": "^10.4.21", "flowbite-typography": "^1.0.5", "husky": "^9.1.7", "lint-staged": "^16.1.2", "memfs": "^4.34.0", "npm-run-all": "^4.1.5", "postcss": "^8.5.6", "semantic-release": "^24.2.7", "tailwindcss": "^4.1.4", "typescript": "^5.9.2", "vite": "^6.3.5", "vite-node": "^3.1.2", "vite-plugin-dts": "^4.5.4", "vitest": "^3.2.4" };
|
|
722
765
|
const engines = { "node": ">=20.0.0" };
|
|
@@ -1364,6 +1407,17 @@ class RedirectError extends ScraperError {
|
|
|
1364
1407
|
this.statusCode = statusCode;
|
|
1365
1408
|
}
|
|
1366
1409
|
}
|
|
1410
|
+
class ChallengeError extends ScraperError {
|
|
1411
|
+
constructor(url, statusCode, challengeType) {
|
|
1412
|
+
super(
|
|
1413
|
+
`Challenge detected for ${url} (status: ${statusCode}, type: ${challengeType})`,
|
|
1414
|
+
false
|
|
1415
|
+
);
|
|
1416
|
+
this.url = url;
|
|
1417
|
+
this.statusCode = statusCode;
|
|
1418
|
+
this.challengeType = challengeType;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1367
1421
|
class MimeTypeUtils {
|
|
1368
1422
|
/**
|
|
1369
1423
|
* Parses a Content-Type header string into its MIME type and charset.
|
|
@@ -1582,26 +1636,6 @@ class MimeTypeUtils {
|
|
|
1582
1636
|
return mimeToLanguage[mimeType] || "";
|
|
1583
1637
|
}
|
|
1584
1638
|
}
|
|
1585
|
-
let projectRoot = null;
|
|
1586
|
-
function getProjectRoot() {
|
|
1587
|
-
if (projectRoot) {
|
|
1588
|
-
return projectRoot;
|
|
1589
|
-
}
|
|
1590
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
1591
|
-
let currentDir = path.dirname(currentFilePath);
|
|
1592
|
-
while (true) {
|
|
1593
|
-
const packageJsonPath = path.join(currentDir, "package.json");
|
|
1594
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
1595
|
-
projectRoot = currentDir;
|
|
1596
|
-
return projectRoot;
|
|
1597
|
-
}
|
|
1598
|
-
const parentDir = path.dirname(currentDir);
|
|
1599
|
-
if (parentDir === currentDir) {
|
|
1600
|
-
throw new Error("Could not find project root containing package.json.");
|
|
1601
|
-
}
|
|
1602
|
-
currentDir = parentDir;
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
1639
|
const fullTrim = (str) => {
|
|
1606
1640
|
return str.replace(/^[\s\r\n\t]+|[\s\r\n\t]+$/g, "");
|
|
1607
1641
|
};
|
|
@@ -1660,6 +1694,132 @@ function extractPrimaryDomain(hostname) {
|
|
|
1660
1694
|
const domain = psl.get(hostname.toLowerCase());
|
|
1661
1695
|
return domain || hostname;
|
|
1662
1696
|
}
|
|
1697
|
+
class FingerprintGenerator {
|
|
1698
|
+
headerGenerator;
|
|
1699
|
+
/**
|
|
1700
|
+
* Creates an instance of FingerprintGenerator.
|
|
1701
|
+
* @param options Optional configuration for the header generator.
|
|
1702
|
+
*/
|
|
1703
|
+
constructor(options) {
|
|
1704
|
+
const defaultOptions = {
|
|
1705
|
+
browsers: [{ name: "chrome", minVersion: 100 }, "firefox", "safari"],
|
|
1706
|
+
devices: ["desktop", "mobile"],
|
|
1707
|
+
operatingSystems: ["windows", "linux", "macos", "android", "ios"],
|
|
1708
|
+
locales: ["en-US", "en"],
|
|
1709
|
+
httpVersion: "2"
|
|
1710
|
+
};
|
|
1711
|
+
this.headerGenerator = new HeaderGenerator({
|
|
1712
|
+
...defaultOptions,
|
|
1713
|
+
...options
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Generates a set of realistic HTTP headers.
|
|
1718
|
+
* @returns A set of realistic HTTP headers.
|
|
1719
|
+
*/
|
|
1720
|
+
generateHeaders() {
|
|
1721
|
+
return this.headerGenerator.getHeaders();
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
class BrowserFetcher {
|
|
1725
|
+
browser = null;
|
|
1726
|
+
page = null;
|
|
1727
|
+
fingerprintGenerator;
|
|
1728
|
+
constructor() {
|
|
1729
|
+
this.fingerprintGenerator = new FingerprintGenerator();
|
|
1730
|
+
}
|
|
1731
|
+
canFetch(source) {
|
|
1732
|
+
return source.startsWith("http://") || source.startsWith("https://");
|
|
1733
|
+
}
|
|
1734
|
+
async fetch(source, options) {
|
|
1735
|
+
try {
|
|
1736
|
+
await this.ensureBrowserReady();
|
|
1737
|
+
if (!this.page) {
|
|
1738
|
+
throw new ScraperError("Failed to create browser page", false);
|
|
1739
|
+
}
|
|
1740
|
+
if (options?.headers) {
|
|
1741
|
+
await this.page.setExtraHTTPHeaders(options.headers);
|
|
1742
|
+
}
|
|
1743
|
+
const timeout = options?.timeout || 3e4;
|
|
1744
|
+
logger.debug(`🌐 Navigating to ${source} with browser...`);
|
|
1745
|
+
const response = await this.page.goto(source, {
|
|
1746
|
+
waitUntil: "networkidle",
|
|
1747
|
+
timeout
|
|
1748
|
+
});
|
|
1749
|
+
if (!response) {
|
|
1750
|
+
throw new ScraperError(`Failed to navigate to ${source}`, false);
|
|
1751
|
+
}
|
|
1752
|
+
if (options?.followRedirects === false && response.status() >= 300 && response.status() < 400) {
|
|
1753
|
+
const location = response.headers().location;
|
|
1754
|
+
if (location) {
|
|
1755
|
+
throw new ScraperError(`Redirect blocked: ${source} -> ${location}`, false);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
const finalUrl = this.page.url();
|
|
1759
|
+
const content = await this.page.content();
|
|
1760
|
+
const contentBuffer = Buffer.from(content, "utf-8");
|
|
1761
|
+
const contentType = response.headers()["content-type"] || "text/html";
|
|
1762
|
+
const { mimeType, charset } = MimeTypeUtils.parseContentType(contentType);
|
|
1763
|
+
return {
|
|
1764
|
+
content: contentBuffer,
|
|
1765
|
+
mimeType,
|
|
1766
|
+
charset,
|
|
1767
|
+
encoding: void 0,
|
|
1768
|
+
// Browser handles encoding automatically
|
|
1769
|
+
source: finalUrl
|
|
1770
|
+
};
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
if (options?.signal?.aborted) {
|
|
1773
|
+
throw new ScraperError("Browser fetch cancelled", false);
|
|
1774
|
+
}
|
|
1775
|
+
logger.error(`❌ Browser fetch failed for ${source}: ${error}`);
|
|
1776
|
+
throw new ScraperError(
|
|
1777
|
+
`Browser fetch failed for ${source}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1778
|
+
false,
|
|
1779
|
+
error instanceof Error ? error : void 0
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
async ensureBrowserReady() {
|
|
1784
|
+
if (!this.browser) {
|
|
1785
|
+
logger.debug("🚀 Launching browser...");
|
|
1786
|
+
this.browser = await chromium.launch({
|
|
1787
|
+
headless: true,
|
|
1788
|
+
args: [
|
|
1789
|
+
"--no-sandbox",
|
|
1790
|
+
"--disable-setuid-sandbox",
|
|
1791
|
+
"--disable-dev-shm-usage",
|
|
1792
|
+
"--disable-web-security",
|
|
1793
|
+
"--disable-features=site-per-process"
|
|
1794
|
+
]
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
if (!this.page) {
|
|
1798
|
+
this.page = await this.browser.newPage();
|
|
1799
|
+
const dynamicHeaders = this.fingerprintGenerator.generateHeaders();
|
|
1800
|
+
await this.page.setExtraHTTPHeaders(dynamicHeaders);
|
|
1801
|
+
await this.page.setViewportSize({ width: 1920, height: 1080 });
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Close the browser and clean up resources
|
|
1806
|
+
*/
|
|
1807
|
+
async close() {
|
|
1808
|
+
try {
|
|
1809
|
+
if (this.page) {
|
|
1810
|
+
await this.page.close();
|
|
1811
|
+
this.page = null;
|
|
1812
|
+
}
|
|
1813
|
+
if (this.browser) {
|
|
1814
|
+
await this.browser.close();
|
|
1815
|
+
this.browser = null;
|
|
1816
|
+
}
|
|
1817
|
+
logger.debug("🔒 Browser closed successfully");
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
logger.warn(`⚠️ Error closing browser: ${error}`);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1663
1823
|
class FileFetcher {
|
|
1664
1824
|
canFetch(source) {
|
|
1665
1825
|
return source.startsWith("file://");
|
|
@@ -1693,33 +1853,6 @@ class FileFetcher {
|
|
|
1693
1853
|
}
|
|
1694
1854
|
}
|
|
1695
1855
|
}
|
|
1696
|
-
class FingerprintGenerator {
|
|
1697
|
-
headerGenerator;
|
|
1698
|
-
/**
|
|
1699
|
-
* Creates an instance of FingerprintGenerator.
|
|
1700
|
-
* @param options Optional configuration for the header generator.
|
|
1701
|
-
*/
|
|
1702
|
-
constructor(options) {
|
|
1703
|
-
const defaultOptions = {
|
|
1704
|
-
browsers: [{ name: "chrome", minVersion: 100 }, "firefox", "safari"],
|
|
1705
|
-
devices: ["desktop", "mobile"],
|
|
1706
|
-
operatingSystems: ["windows", "linux", "macos", "android", "ios"],
|
|
1707
|
-
locales: ["en-US", "en"],
|
|
1708
|
-
httpVersion: "2"
|
|
1709
|
-
};
|
|
1710
|
-
this.headerGenerator = new HeaderGenerator({
|
|
1711
|
-
...defaultOptions,
|
|
1712
|
-
...options
|
|
1713
|
-
});
|
|
1714
|
-
}
|
|
1715
|
-
/**
|
|
1716
|
-
* Generates a set of realistic HTTP headers.
|
|
1717
|
-
* @returns A set of realistic HTTP headers.
|
|
1718
|
-
*/
|
|
1719
|
-
generateHeaders() {
|
|
1720
|
-
return this.headerGenerator.getHeaders();
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
1856
|
class HttpFetcher {
|
|
1724
1857
|
retryableStatusCodes = [
|
|
1725
1858
|
408,
|
|
@@ -1854,6 +1987,27 @@ class HttpFetcher {
|
|
|
1854
1987
|
throw new RedirectError(source, location, status);
|
|
1855
1988
|
}
|
|
1856
1989
|
}
|
|
1990
|
+
if (status === 403) {
|
|
1991
|
+
const cfMitigated = axiosError.response?.headers?.["cf-mitigated"];
|
|
1992
|
+
const server = axiosError.response?.headers?.server;
|
|
1993
|
+
let responseBody = "";
|
|
1994
|
+
if (axiosError.response?.data) {
|
|
1995
|
+
try {
|
|
1996
|
+
if (typeof axiosError.response.data === "string") {
|
|
1997
|
+
responseBody = axiosError.response.data;
|
|
1998
|
+
} else if (Buffer.isBuffer(axiosError.response.data)) {
|
|
1999
|
+
responseBody = axiosError.response.data.toString("utf-8");
|
|
2000
|
+
} else if (axiosError.response.data instanceof ArrayBuffer) {
|
|
2001
|
+
responseBody = Buffer.from(axiosError.response.data).toString("utf-8");
|
|
2002
|
+
}
|
|
2003
|
+
} catch {
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
const isCloudflareChallenge = cfMitigated === "challenge" || server === "cloudflare" || responseBody.includes("Enable JavaScript and cookies to continue") || responseBody.includes("Just a moment...") || responseBody.includes("cf_chl_opt");
|
|
2007
|
+
if (isCloudflareChallenge) {
|
|
2008
|
+
throw new ChallengeError(source, status, "cloudflare");
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1857
2011
|
if (attempt < maxRetries && (status === void 0 || this.retryableStatusCodes.includes(status))) {
|
|
1858
2012
|
const delay = baseDelay * 2 ** attempt;
|
|
1859
2013
|
logger.warn(
|
|
@@ -1875,6 +2029,52 @@ class HttpFetcher {
|
|
|
1875
2029
|
);
|
|
1876
2030
|
}
|
|
1877
2031
|
}
|
|
2032
|
+
class AutoDetectFetcher {
|
|
2033
|
+
httpFetcher = new HttpFetcher();
|
|
2034
|
+
browserFetcher = new BrowserFetcher();
|
|
2035
|
+
fileFetcher = new FileFetcher();
|
|
2036
|
+
/**
|
|
2037
|
+
* Check if this fetcher can handle the given source.
|
|
2038
|
+
* Returns true for any URL that any of the underlying fetchers can handle.
|
|
2039
|
+
*/
|
|
2040
|
+
canFetch(source) {
|
|
2041
|
+
return this.httpFetcher.canFetch(source) || this.browserFetcher.canFetch(source) || this.fileFetcher.canFetch(source);
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Fetch content from the source, automatically selecting the appropriate fetcher
|
|
2045
|
+
* and handling fallbacks when challenges are detected.
|
|
2046
|
+
*/
|
|
2047
|
+
async fetch(source, options) {
|
|
2048
|
+
if (this.fileFetcher.canFetch(source)) {
|
|
2049
|
+
logger.debug(`Using FileFetcher for: ${source}`);
|
|
2050
|
+
return this.fileFetcher.fetch(source, options);
|
|
2051
|
+
}
|
|
2052
|
+
if (this.httpFetcher.canFetch(source)) {
|
|
2053
|
+
try {
|
|
2054
|
+
logger.debug(`Using HttpFetcher for: ${source}`);
|
|
2055
|
+
return await this.httpFetcher.fetch(source, options);
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
if (error instanceof ChallengeError) {
|
|
2058
|
+
logger.info(
|
|
2059
|
+
`🔄 Challenge detected for ${source}, falling back to browser fetcher...`
|
|
2060
|
+
);
|
|
2061
|
+
return this.browserFetcher.fetch(source, options);
|
|
2062
|
+
}
|
|
2063
|
+
throw error;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
throw new Error(`No suitable fetcher found for URL: ${source}`);
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Close all underlying fetchers to prevent resource leaks.
|
|
2070
|
+
*/
|
|
2071
|
+
async close() {
|
|
2072
|
+
await Promise.allSettled([
|
|
2073
|
+
this.browserFetcher.close()
|
|
2074
|
+
// HttpFetcher and FileFetcher don't need explicit cleanup
|
|
2075
|
+
]);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
1878
2078
|
class SplitterError extends Error {
|
|
1879
2079
|
}
|
|
1880
2080
|
class MinimumChunkSizeError extends SplitterError {
|
|
@@ -5742,7 +5942,7 @@ class BaseScraperStrategy {
|
|
|
5742
5942
|
async cleanup() {
|
|
5743
5943
|
}
|
|
5744
5944
|
}
|
|
5745
|
-
class
|
|
5945
|
+
class GitHubRepoScraperStrategy extends BaseScraperStrategy {
|
|
5746
5946
|
httpFetcher = new HttpFetcher();
|
|
5747
5947
|
pipelines;
|
|
5748
5948
|
resolvedBranch;
|
|
@@ -5776,9 +5976,18 @@ class GitHubScraperStrategy extends BaseScraperStrategy {
|
|
|
5776
5976
|
throw new Error(`Invalid GitHub repository URL: ${url}`);
|
|
5777
5977
|
}
|
|
5778
5978
|
const [, owner, repo] = match;
|
|
5779
|
-
const
|
|
5780
|
-
|
|
5781
|
-
|
|
5979
|
+
const segments = parsedUrl.pathname.split("/").filter(Boolean);
|
|
5980
|
+
if (segments.length >= 4 && segments[2] === "blob") {
|
|
5981
|
+
const branch2 = segments[3];
|
|
5982
|
+
const filePath = segments.length > 4 ? segments.slice(4).join("/") : void 0;
|
|
5983
|
+
return { owner, repo, branch: branch2, filePath, isBlob: true };
|
|
5984
|
+
}
|
|
5985
|
+
if (segments.length < 4 || segments[2] !== "tree") {
|
|
5986
|
+
return { owner, repo };
|
|
5987
|
+
}
|
|
5988
|
+
const branch = segments[3];
|
|
5989
|
+
const subPath = segments.length > 4 ? segments.slice(4).join("/") : void 0;
|
|
5990
|
+
return { owner, repo, branch, subPath };
|
|
5782
5991
|
}
|
|
5783
5992
|
/**
|
|
5784
5993
|
* Fetches the repository tree structure from GitHub API.
|
|
@@ -5976,13 +6185,24 @@ class GitHubScraperStrategy extends BaseScraperStrategy {
|
|
|
5976
6185
|
async processItem(item, options, _progressCallback, signal) {
|
|
5977
6186
|
const repoInfo = this.parseGitHubUrl(options.url);
|
|
5978
6187
|
if (item.depth === 0) {
|
|
6188
|
+
if ("isBlob" in repoInfo && repoInfo.isBlob) {
|
|
6189
|
+
if (repoInfo.filePath) {
|
|
6190
|
+
logger.info(
|
|
6191
|
+
`📄 Processing single file: ${repoInfo.owner}/${repoInfo.repo}/${repoInfo.filePath}`
|
|
6192
|
+
);
|
|
6193
|
+
return { links: [`github-file://${repoInfo.filePath}`] };
|
|
6194
|
+
} else {
|
|
6195
|
+
logger.warn(
|
|
6196
|
+
`⚠️ Blob URL without file path: ${options.url}. No files to process.`
|
|
6197
|
+
);
|
|
6198
|
+
return { links: [] };
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
5979
6201
|
logger.info(
|
|
5980
6202
|
`🗂️ Discovering repository structure for ${repoInfo.owner}/${repoInfo.repo}`
|
|
5981
6203
|
);
|
|
5982
6204
|
const { tree, resolvedBranch } = await this.fetchRepositoryTree(repoInfo, signal);
|
|
5983
|
-
const fileItems = tree.tree.filter(
|
|
5984
|
-
(treeItem) => this.shouldProcessFile(treeItem, options)
|
|
5985
|
-
);
|
|
6205
|
+
const fileItems = tree.tree.filter((treeItem) => this.isWithinSubPath(treeItem.path, repoInfo.subPath)).filter((treeItem) => this.shouldProcessFile(treeItem, options));
|
|
5986
6206
|
logger.info(
|
|
5987
6207
|
`📁 Found ${fileItems.length} processable files in repository (branch: ${resolvedBranch})`
|
|
5988
6208
|
);
|
|
@@ -6016,12 +6236,15 @@ class GitHubScraperStrategy extends BaseScraperStrategy {
|
|
|
6016
6236
|
logger.warn(`⚠️ Processing error for ${filePath}: ${err.message}`);
|
|
6017
6237
|
}
|
|
6018
6238
|
const githubUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/blob/${this.resolvedBranch || repoInfo.branch || "main"}/${filePath}`;
|
|
6239
|
+
const processedTitle = processed.metadata.title;
|
|
6240
|
+
const hasValidTitle = typeof processedTitle === "string" && processedTitle.trim() !== "";
|
|
6241
|
+
const fallbackTitle = filePath.split("/").pop() || "Untitled";
|
|
6019
6242
|
return {
|
|
6020
6243
|
document: {
|
|
6021
6244
|
content: typeof processed.textContent === "string" ? processed.textContent : "",
|
|
6022
6245
|
metadata: {
|
|
6023
6246
|
url: githubUrl,
|
|
6024
|
-
title:
|
|
6247
|
+
title: hasValidTitle ? processedTitle : fallbackTitle,
|
|
6025
6248
|
library: options.library,
|
|
6026
6249
|
version: options.version
|
|
6027
6250
|
},
|
|
@@ -6034,6 +6257,26 @@ class GitHubScraperStrategy extends BaseScraperStrategy {
|
|
|
6034
6257
|
}
|
|
6035
6258
|
return { document: void 0, links: [] };
|
|
6036
6259
|
}
|
|
6260
|
+
/**
|
|
6261
|
+
* Normalize a path by removing leading and trailing slashes.
|
|
6262
|
+
*/
|
|
6263
|
+
normalizePath(path2) {
|
|
6264
|
+
return path2.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
6265
|
+
}
|
|
6266
|
+
isWithinSubPath(path2, subPath) {
|
|
6267
|
+
if (!subPath) {
|
|
6268
|
+
return true;
|
|
6269
|
+
}
|
|
6270
|
+
const trimmedSubPath = this.normalizePath(subPath);
|
|
6271
|
+
if (trimmedSubPath.length === 0) {
|
|
6272
|
+
return true;
|
|
6273
|
+
}
|
|
6274
|
+
const normalizedPath = this.normalizePath(path2);
|
|
6275
|
+
if (normalizedPath === trimmedSubPath) {
|
|
6276
|
+
return true;
|
|
6277
|
+
}
|
|
6278
|
+
return normalizedPath.startsWith(`${trimmedSubPath}/`);
|
|
6279
|
+
}
|
|
6037
6280
|
async scrape(options, progressCallback, signal) {
|
|
6038
6281
|
const url = new URL(options.url);
|
|
6039
6282
|
if (!url.hostname.includes("github.com")) {
|
|
@@ -6048,6 +6291,228 @@ class GitHubScraperStrategy extends BaseScraperStrategy {
|
|
|
6048
6291
|
await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));
|
|
6049
6292
|
}
|
|
6050
6293
|
}
|
|
6294
|
+
class GitHubWikiScraperStrategy extends BaseScraperStrategy {
|
|
6295
|
+
httpFetcher = new HttpFetcher();
|
|
6296
|
+
pipelines;
|
|
6297
|
+
constructor() {
|
|
6298
|
+
super();
|
|
6299
|
+
this.pipelines = PipelineFactory$1.createStandardPipelines();
|
|
6300
|
+
}
|
|
6301
|
+
canHandle(url) {
|
|
6302
|
+
try {
|
|
6303
|
+
const parsedUrl = new URL(url);
|
|
6304
|
+
const { hostname, pathname } = parsedUrl;
|
|
6305
|
+
return ["github.com", "www.github.com"].includes(hostname) && pathname.includes("/wiki") && pathname.match(/^\/([^/]+)\/([^/]+)\/wiki/) !== null;
|
|
6306
|
+
} catch {
|
|
6307
|
+
return false;
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
/**
|
|
6311
|
+
* Parses a GitHub wiki URL to extract repository information.
|
|
6312
|
+
*/
|
|
6313
|
+
parseGitHubWikiUrl(url) {
|
|
6314
|
+
const parsedUrl = new URL(url);
|
|
6315
|
+
const match = parsedUrl.pathname.match(/^\/([^/]+)\/([^/]+)\/wiki/);
|
|
6316
|
+
if (!match) {
|
|
6317
|
+
throw new Error(`Invalid GitHub wiki URL: ${url}`);
|
|
6318
|
+
}
|
|
6319
|
+
const [, owner, repo] = match;
|
|
6320
|
+
return { owner, repo };
|
|
6321
|
+
}
|
|
6322
|
+
/**
|
|
6323
|
+
* Override shouldProcessUrl to only process URLs within the wiki scope.
|
|
6324
|
+
*/
|
|
6325
|
+
shouldProcessUrl(url, options) {
|
|
6326
|
+
try {
|
|
6327
|
+
const parsedUrl = new URL(url);
|
|
6328
|
+
const wikiInfo = this.parseGitHubWikiUrl(options.url);
|
|
6329
|
+
const expectedWikiPath = `/${wikiInfo.owner}/${wikiInfo.repo}/wiki`;
|
|
6330
|
+
if (!parsedUrl.pathname.startsWith(expectedWikiPath)) {
|
|
6331
|
+
return false;
|
|
6332
|
+
}
|
|
6333
|
+
const wikiPagePath = parsedUrl.pathname.replace(expectedWikiPath, "").replace(/^\//, "");
|
|
6334
|
+
return shouldIncludeUrl(
|
|
6335
|
+
wikiPagePath || "Home",
|
|
6336
|
+
options.includePatterns,
|
|
6337
|
+
options.excludePatterns
|
|
6338
|
+
);
|
|
6339
|
+
} catch {
|
|
6340
|
+
return false;
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
async processItem(item, options, _progressCallback, signal) {
|
|
6344
|
+
const currentUrl = item.url;
|
|
6345
|
+
logger.info(
|
|
6346
|
+
`📖 Processing wiki page ${this.pageCount}/${options.maxPages}: ${currentUrl}`
|
|
6347
|
+
);
|
|
6348
|
+
try {
|
|
6349
|
+
const rawContent = await this.httpFetcher.fetch(currentUrl, { signal });
|
|
6350
|
+
let processed;
|
|
6351
|
+
for (const pipeline of this.pipelines) {
|
|
6352
|
+
if (pipeline.canProcess(rawContent)) {
|
|
6353
|
+
logger.debug(
|
|
6354
|
+
`Selected ${pipeline.constructor.name} for content type "${rawContent.mimeType}" (${currentUrl})`
|
|
6355
|
+
);
|
|
6356
|
+
const wikiOptions = { ...options, scrapeMode: ScrapeMode.Fetch };
|
|
6357
|
+
processed = await pipeline.process(rawContent, wikiOptions, this.httpFetcher);
|
|
6358
|
+
break;
|
|
6359
|
+
}
|
|
6360
|
+
}
|
|
6361
|
+
if (!processed) {
|
|
6362
|
+
logger.warn(
|
|
6363
|
+
`⚠️ Unsupported content type "${rawContent.mimeType}" for wiki page ${currentUrl}. Skipping processing.`
|
|
6364
|
+
);
|
|
6365
|
+
return { document: void 0, links: [] };
|
|
6366
|
+
}
|
|
6367
|
+
for (const err of processed.errors) {
|
|
6368
|
+
logger.warn(`⚠️ Processing error for ${currentUrl}: ${err.message}`);
|
|
6369
|
+
}
|
|
6370
|
+
const parsedUrl = new URL(currentUrl);
|
|
6371
|
+
const wikiInfo = this.parseGitHubWikiUrl(currentUrl);
|
|
6372
|
+
const wikiPagePath = parsedUrl.pathname.replace(`/${wikiInfo.owner}/${wikiInfo.repo}/wiki`, "").replace(/^\//, "");
|
|
6373
|
+
const pageTitle = wikiPagePath || "Home";
|
|
6374
|
+
const document2 = {
|
|
6375
|
+
content: typeof processed.textContent === "string" ? processed.textContent : "",
|
|
6376
|
+
metadata: {
|
|
6377
|
+
url: currentUrl,
|
|
6378
|
+
title: typeof processed.metadata.title === "string" && processed.metadata.title.trim() !== "" ? processed.metadata.title : pageTitle,
|
|
6379
|
+
library: options.library,
|
|
6380
|
+
version: options.version
|
|
6381
|
+
},
|
|
6382
|
+
contentType: rawContent.mimeType
|
|
6383
|
+
};
|
|
6384
|
+
const links = processed.links || [];
|
|
6385
|
+
const wikiLinks = links.filter((link) => {
|
|
6386
|
+
if (!link || link.trim() === "" || link === "invalid-url" || link === "not-a-url-at-all") {
|
|
6387
|
+
return false;
|
|
6388
|
+
}
|
|
6389
|
+
return true;
|
|
6390
|
+
}).map((link) => {
|
|
6391
|
+
try {
|
|
6392
|
+
return new URL(link, currentUrl).href;
|
|
6393
|
+
} catch {
|
|
6394
|
+
return null;
|
|
6395
|
+
}
|
|
6396
|
+
}).filter((link) => link !== null).filter((link) => {
|
|
6397
|
+
try {
|
|
6398
|
+
const linkUrl = new URL(link);
|
|
6399
|
+
return linkUrl.hostname === parsedUrl.hostname && linkUrl.pathname.startsWith(`/${wikiInfo.owner}/${wikiInfo.repo}/wiki`);
|
|
6400
|
+
} catch {
|
|
6401
|
+
return false;
|
|
6402
|
+
}
|
|
6403
|
+
});
|
|
6404
|
+
return { document: document2, links: wikiLinks };
|
|
6405
|
+
} catch (error) {
|
|
6406
|
+
logger.warn(`⚠️ Failed to process wiki page ${currentUrl}: ${error}`);
|
|
6407
|
+
return { document: void 0, links: [] };
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
async scrape(options, progressCallback, signal) {
|
|
6411
|
+
const url = new URL(options.url);
|
|
6412
|
+
if (!url.hostname.includes("github.com") || !url.pathname.includes("/wiki")) {
|
|
6413
|
+
throw new Error("URL must be a GitHub wiki URL");
|
|
6414
|
+
}
|
|
6415
|
+
let startUrl = options.url;
|
|
6416
|
+
if (url.pathname.endsWith("/wiki") || url.pathname.endsWith("/wiki/")) {
|
|
6417
|
+
startUrl = url.pathname.endsWith("/") ? `${options.url}Home` : `${options.url}/Home`;
|
|
6418
|
+
}
|
|
6419
|
+
const wikiOptions = { ...options, url: startUrl };
|
|
6420
|
+
return super.scrape(wikiOptions, progressCallback, signal);
|
|
6421
|
+
}
|
|
6422
|
+
/**
|
|
6423
|
+
* Cleanup resources used by this strategy.
|
|
6424
|
+
*/
|
|
6425
|
+
async cleanup() {
|
|
6426
|
+
await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
class GitHubScraperStrategy {
|
|
6430
|
+
repoStrategy = new GitHubRepoScraperStrategy();
|
|
6431
|
+
wikiStrategy = new GitHubWikiScraperStrategy();
|
|
6432
|
+
canHandle(url) {
|
|
6433
|
+
try {
|
|
6434
|
+
const parsedUrl = new URL(url);
|
|
6435
|
+
const { hostname, pathname } = parsedUrl;
|
|
6436
|
+
if (!["github.com", "www.github.com"].includes(hostname)) {
|
|
6437
|
+
return false;
|
|
6438
|
+
}
|
|
6439
|
+
const pathMatch = pathname.match(/^\/([^/]+)\/([^/]+)\/?$/);
|
|
6440
|
+
return pathMatch !== null;
|
|
6441
|
+
} catch {
|
|
6442
|
+
return false;
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
async scrape(options, progressCallback, signal) {
|
|
6446
|
+
const url = new URL(options.url);
|
|
6447
|
+
if (!url.hostname.includes("github.com")) {
|
|
6448
|
+
throw new Error("URL must be a GitHub URL");
|
|
6449
|
+
}
|
|
6450
|
+
const pathMatch = url.pathname.match(/^\/([^/]+)\/([^/]+)\/?$/);
|
|
6451
|
+
if (!pathMatch) {
|
|
6452
|
+
throw new Error("URL must be a base GitHub repository URL");
|
|
6453
|
+
}
|
|
6454
|
+
const [, owner, repo] = pathMatch;
|
|
6455
|
+
logger.info(`🚀 Starting comprehensive GitHub scraping for ${owner}/${repo}`);
|
|
6456
|
+
let totalPagesDiscovered = 0;
|
|
6457
|
+
let wikiPagesScraped = 0;
|
|
6458
|
+
let wikiCompleted = false;
|
|
6459
|
+
let repoCompleted = false;
|
|
6460
|
+
const mergedProgressCallback = async (progress) => {
|
|
6461
|
+
if (!wikiCompleted) {
|
|
6462
|
+
totalPagesDiscovered = progress.totalDiscovered;
|
|
6463
|
+
wikiPagesScraped = progress.pagesScraped;
|
|
6464
|
+
} else if (!repoCompleted) {
|
|
6465
|
+
progress = {
|
|
6466
|
+
...progress,
|
|
6467
|
+
pagesScraped: wikiPagesScraped + progress.pagesScraped,
|
|
6468
|
+
totalPages: wikiPagesScraped + progress.totalPages,
|
|
6469
|
+
totalDiscovered: totalPagesDiscovered + progress.totalDiscovered
|
|
6470
|
+
};
|
|
6471
|
+
}
|
|
6472
|
+
await progressCallback(progress);
|
|
6473
|
+
};
|
|
6474
|
+
try {
|
|
6475
|
+
const wikiUrl = `${options.url.replace(/\/$/, "")}/wiki`;
|
|
6476
|
+
const wikiOptions = { ...options, url: wikiUrl };
|
|
6477
|
+
logger.info(`📖 Attempting to scrape wiki for ${owner}/${repo}`);
|
|
6478
|
+
try {
|
|
6479
|
+
await this.wikiStrategy.scrape(wikiOptions, mergedProgressCallback, signal);
|
|
6480
|
+
wikiCompleted = true;
|
|
6481
|
+
logger.info(
|
|
6482
|
+
`✅ Completed wiki scraping for ${owner}/${repo} (${wikiPagesScraped} pages)`
|
|
6483
|
+
);
|
|
6484
|
+
} catch (error) {
|
|
6485
|
+
wikiCompleted = true;
|
|
6486
|
+
logger.info(`ℹ️ Wiki not available or accessible for ${owner}/${repo}: ${error}`);
|
|
6487
|
+
}
|
|
6488
|
+
const maxPages = options.maxPages || 1e3;
|
|
6489
|
+
const remainingPages = Math.max(0, maxPages - wikiPagesScraped);
|
|
6490
|
+
if (remainingPages > 0) {
|
|
6491
|
+
logger.info(
|
|
6492
|
+
`📂 Scraping repository code for ${owner}/${repo} (${remainingPages} pages remaining)`
|
|
6493
|
+
);
|
|
6494
|
+
const repoOptions = { ...options, maxPages: remainingPages };
|
|
6495
|
+
await this.repoStrategy.scrape(repoOptions, mergedProgressCallback, signal);
|
|
6496
|
+
repoCompleted = true;
|
|
6497
|
+
logger.info(`✅ Completed repository code scraping for ${owner}/${repo}`);
|
|
6498
|
+
} else {
|
|
6499
|
+
logger.info(
|
|
6500
|
+
`ℹ️ Skipping repository code scraping - page limit reached with wiki content`
|
|
6501
|
+
);
|
|
6502
|
+
}
|
|
6503
|
+
logger.info(`🎉 Comprehensive GitHub scraping completed for ${owner}/${repo}`);
|
|
6504
|
+
} catch (error) {
|
|
6505
|
+
logger.error(`❌ GitHub scraping failed for ${owner}/${repo}: ${error}`);
|
|
6506
|
+
throw error;
|
|
6507
|
+
}
|
|
6508
|
+
}
|
|
6509
|
+
/**
|
|
6510
|
+
* Cleanup resources used by both underlying strategies.
|
|
6511
|
+
*/
|
|
6512
|
+
async cleanup() {
|
|
6513
|
+
await Promise.allSettled([this.repoStrategy.cleanup(), this.wikiStrategy.cleanup()]);
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6051
6516
|
class LocalFileStrategy extends BaseScraperStrategy {
|
|
6052
6517
|
fileFetcher = new FileFetcher();
|
|
6053
6518
|
pipelines;
|
|
@@ -6112,7 +6577,7 @@ class LocalFileStrategy extends BaseScraperStrategy {
|
|
|
6112
6577
|
}
|
|
6113
6578
|
}
|
|
6114
6579
|
class WebScraperStrategy extends BaseScraperStrategy {
|
|
6115
|
-
|
|
6580
|
+
fetcher = new AutoDetectFetcher();
|
|
6116
6581
|
shouldFollowLinkFn;
|
|
6117
6582
|
pipelines;
|
|
6118
6583
|
constructor(options = {}) {
|
|
@@ -6146,14 +6611,14 @@ class WebScraperStrategy extends BaseScraperStrategy {
|
|
|
6146
6611
|
headers: options.headers
|
|
6147
6612
|
// Forward custom headers
|
|
6148
6613
|
};
|
|
6149
|
-
const rawContent = await this.
|
|
6614
|
+
const rawContent = await this.fetcher.fetch(url, fetchOptions);
|
|
6150
6615
|
let processed;
|
|
6151
6616
|
for (const pipeline of this.pipelines) {
|
|
6152
6617
|
if (pipeline.canProcess(rawContent)) {
|
|
6153
6618
|
logger.debug(
|
|
6154
6619
|
`Selected ${pipeline.constructor.name} for content type "${rawContent.mimeType}" (${url})`
|
|
6155
6620
|
);
|
|
6156
|
-
processed = await pipeline.process(rawContent, options, this.
|
|
6621
|
+
processed = await pipeline.process(rawContent, options, this.fetcher);
|
|
6157
6622
|
break;
|
|
6158
6623
|
}
|
|
6159
6624
|
}
|
|
@@ -6202,10 +6667,13 @@ class WebScraperStrategy extends BaseScraperStrategy {
|
|
|
6202
6667
|
}
|
|
6203
6668
|
}
|
|
6204
6669
|
/**
|
|
6205
|
-
* Cleanup resources used by this strategy, specifically the pipeline browser instances.
|
|
6670
|
+
* Cleanup resources used by this strategy, specifically the pipeline browser instances and fetcher.
|
|
6206
6671
|
*/
|
|
6207
6672
|
async cleanup() {
|
|
6208
|
-
await Promise.allSettled(
|
|
6673
|
+
await Promise.allSettled([
|
|
6674
|
+
...this.pipelines.map((pipeline) => pipeline.close()),
|
|
6675
|
+
this.fetcher.close()
|
|
6676
|
+
]);
|
|
6209
6677
|
}
|
|
6210
6678
|
}
|
|
6211
6679
|
class NpmScraperStrategy {
|
|
@@ -6268,6 +6736,7 @@ class ScraperRegistry {
|
|
|
6268
6736
|
this.strategies = [
|
|
6269
6737
|
new NpmScraperStrategy(),
|
|
6270
6738
|
new PyPiScraperStrategy(),
|
|
6739
|
+
new GitHubWikiScraperStrategy(),
|
|
6271
6740
|
new GitHubScraperStrategy(),
|
|
6272
6741
|
new WebScraperStrategy(),
|
|
6273
6742
|
new LocalFileStrategy()
|
|
@@ -7390,14 +7859,6 @@ function parseHeaders(headerOptions) {
|
|
|
7390
7859
|
}
|
|
7391
7860
|
return headers;
|
|
7392
7861
|
}
|
|
7393
|
-
const CLI_DEFAULTS = {
|
|
7394
|
-
PROTOCOL: DEFAULT_PROTOCOL,
|
|
7395
|
-
HTTP_PORT: DEFAULT_HTTP_PORT,
|
|
7396
|
-
WEB_PORT: DEFAULT_WEB_PORT,
|
|
7397
|
-
HOST: DEFAULT_HOST,
|
|
7398
|
-
MAX_CONCURRENCY: DEFAULT_MAX_CONCURRENCY,
|
|
7399
|
-
TELEMETRY: true
|
|
7400
|
-
};
|
|
7401
7862
|
function parseAuthConfig(options) {
|
|
7402
7863
|
if (!options.authEnabled) {
|
|
7403
7864
|
return void 0;
|
|
@@ -7614,17 +8075,17 @@ class LibraryNotFoundError extends ToolError {
|
|
|
7614
8075
|
}
|
|
7615
8076
|
class FetchUrlTool {
|
|
7616
8077
|
/**
|
|
7617
|
-
*
|
|
8078
|
+
* AutoDetectFetcher handles all URL types and fallback logic automatically.
|
|
7618
8079
|
*/
|
|
7619
|
-
|
|
8080
|
+
fetcher;
|
|
7620
8081
|
/**
|
|
7621
8082
|
* Collection of pipelines that will be tried in order for processing content.
|
|
7622
8083
|
* The first pipeline that can process the content type will be used.
|
|
7623
8084
|
* Currently includes HtmlPipeline, MarkdownPipeline, and TextPipeline (as fallback).
|
|
7624
8085
|
*/
|
|
7625
8086
|
pipelines;
|
|
7626
|
-
constructor(
|
|
7627
|
-
this.
|
|
8087
|
+
constructor(fetcher) {
|
|
8088
|
+
this.fetcher = fetcher;
|
|
7628
8089
|
const htmlPipeline = new HtmlPipeline();
|
|
7629
8090
|
const markdownPipeline = new MarkdownPipeline();
|
|
7630
8091
|
const textPipeline = new TextPipeline();
|
|
@@ -7638,24 +8099,21 @@ class FetchUrlTool {
|
|
|
7638
8099
|
*/
|
|
7639
8100
|
async execute(options) {
|
|
7640
8101
|
const { url, scrapeMode = ScrapeMode.Auto, headers } = options;
|
|
7641
|
-
|
|
7642
|
-
const fetcherIndex = canFetchResults.indexOf(true);
|
|
7643
|
-
if (fetcherIndex === -1) {
|
|
8102
|
+
if (!this.fetcher.canFetch(url)) {
|
|
7644
8103
|
throw new ToolError(
|
|
7645
8104
|
`Invalid URL: ${url}. Must be an HTTP/HTTPS URL or a file:// URL.`,
|
|
7646
8105
|
this.constructor.name
|
|
7647
8106
|
);
|
|
7648
8107
|
}
|
|
7649
|
-
const fetcher = this.fetchers[fetcherIndex];
|
|
7650
|
-
logger.debug(`Using fetcher "${fetcher.constructor.name}" for URL: ${url}`);
|
|
7651
8108
|
try {
|
|
7652
8109
|
logger.info(`📡 Fetching ${url}...`);
|
|
7653
|
-
const
|
|
8110
|
+
const fetchOptions = {
|
|
7654
8111
|
followRedirects: options.followRedirects ?? true,
|
|
7655
8112
|
maxRetries: 3,
|
|
7656
8113
|
headers
|
|
7657
8114
|
// propagate custom headers
|
|
7658
|
-
}
|
|
8115
|
+
};
|
|
8116
|
+
const rawContent = await this.fetcher.fetch(url, fetchOptions);
|
|
7659
8117
|
logger.info("🔄 Processing content...");
|
|
7660
8118
|
let processed;
|
|
7661
8119
|
for (const pipeline of this.pipelines) {
|
|
@@ -7677,7 +8135,7 @@ class FetchUrlTool {
|
|
|
7677
8135
|
headers
|
|
7678
8136
|
// propagate custom headers
|
|
7679
8137
|
},
|
|
7680
|
-
fetcher
|
|
8138
|
+
this.fetcher
|
|
7681
8139
|
);
|
|
7682
8140
|
break;
|
|
7683
8141
|
}
|
|
@@ -7717,7 +8175,10 @@ class FetchUrlTool {
|
|
|
7717
8175
|
this.constructor.name
|
|
7718
8176
|
);
|
|
7719
8177
|
} finally {
|
|
7720
|
-
await Promise.allSettled(
|
|
8178
|
+
await Promise.allSettled([
|
|
8179
|
+
...this.pipelines.map((pipeline) => pipeline.close()),
|
|
8180
|
+
this.fetcher.close()
|
|
8181
|
+
]);
|
|
7721
8182
|
}
|
|
7722
8183
|
}
|
|
7723
8184
|
}
|
|
@@ -8551,7 +9012,7 @@ async function initializeTools(docService, pipeline) {
|
|
|
8551
9012
|
cancelJob: new CancelJobTool(pipeline),
|
|
8552
9013
|
// clearCompletedJobs: new ClearCompletedJobsTool(pipeline),
|
|
8553
9014
|
remove: new RemoveTool(docService, pipeline),
|
|
8554
|
-
fetchUrl: new FetchUrlTool(new
|
|
9015
|
+
fetchUrl: new FetchUrlTool(new AutoDetectFetcher())
|
|
8555
9016
|
};
|
|
8556
9017
|
return tools;
|
|
8557
9018
|
}
|
|
@@ -8909,15 +9370,130 @@ const Layout = ({ title, version: version2, children }) => {
|
|
|
8909
9370
|
try {
|
|
8910
9371
|
const packageJson2 = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
8911
9372
|
versionString = packageJson2.version;
|
|
9373
|
+
logger.debug(`Resolved version from package.json: ${versionString}`);
|
|
8912
9374
|
} catch (error) {
|
|
8913
9375
|
logger.error(`Error reading package.json: ${error}`);
|
|
8914
9376
|
}
|
|
8915
9377
|
}
|
|
9378
|
+
const versionInitializer = `versionUpdate({ currentVersion: ${versionString ? `'${versionString}'` : "null"} })`;
|
|
8916
9379
|
return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
|
|
8917
9380
|
/* @__PURE__ */ jsxs("head", { children: [
|
|
8918
9381
|
/* @__PURE__ */ jsx("meta", { charset: "UTF-8" }),
|
|
8919
9382
|
/* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }),
|
|
8920
9383
|
/* @__PURE__ */ jsx("title", { safe: true, children: title }),
|
|
9384
|
+
/* @__PURE__ */ jsx(
|
|
9385
|
+
"link",
|
|
9386
|
+
{
|
|
9387
|
+
rel: "apple-touch-icon",
|
|
9388
|
+
sizes: "57x57",
|
|
9389
|
+
href: "/apple-icon-57x57.png"
|
|
9390
|
+
}
|
|
9391
|
+
),
|
|
9392
|
+
/* @__PURE__ */ jsx(
|
|
9393
|
+
"link",
|
|
9394
|
+
{
|
|
9395
|
+
rel: "apple-touch-icon",
|
|
9396
|
+
sizes: "60x60",
|
|
9397
|
+
href: "/apple-icon-60x60.png"
|
|
9398
|
+
}
|
|
9399
|
+
),
|
|
9400
|
+
/* @__PURE__ */ jsx(
|
|
9401
|
+
"link",
|
|
9402
|
+
{
|
|
9403
|
+
rel: "apple-touch-icon",
|
|
9404
|
+
sizes: "72x72",
|
|
9405
|
+
href: "/apple-icon-72x72.png"
|
|
9406
|
+
}
|
|
9407
|
+
),
|
|
9408
|
+
/* @__PURE__ */ jsx(
|
|
9409
|
+
"link",
|
|
9410
|
+
{
|
|
9411
|
+
rel: "apple-touch-icon",
|
|
9412
|
+
sizes: "76x76",
|
|
9413
|
+
href: "/apple-icon-76x76.png"
|
|
9414
|
+
}
|
|
9415
|
+
),
|
|
9416
|
+
/* @__PURE__ */ jsx(
|
|
9417
|
+
"link",
|
|
9418
|
+
{
|
|
9419
|
+
rel: "apple-touch-icon",
|
|
9420
|
+
sizes: "114x114",
|
|
9421
|
+
href: "/apple-icon-114x114.png"
|
|
9422
|
+
}
|
|
9423
|
+
),
|
|
9424
|
+
/* @__PURE__ */ jsx(
|
|
9425
|
+
"link",
|
|
9426
|
+
{
|
|
9427
|
+
rel: "apple-touch-icon",
|
|
9428
|
+
sizes: "120x120",
|
|
9429
|
+
href: "/apple-icon-120x120.png"
|
|
9430
|
+
}
|
|
9431
|
+
),
|
|
9432
|
+
/* @__PURE__ */ jsx(
|
|
9433
|
+
"link",
|
|
9434
|
+
{
|
|
9435
|
+
rel: "apple-touch-icon",
|
|
9436
|
+
sizes: "144x144",
|
|
9437
|
+
href: "/apple-icon-144x144.png"
|
|
9438
|
+
}
|
|
9439
|
+
),
|
|
9440
|
+
/* @__PURE__ */ jsx(
|
|
9441
|
+
"link",
|
|
9442
|
+
{
|
|
9443
|
+
rel: "apple-touch-icon",
|
|
9444
|
+
sizes: "152x152",
|
|
9445
|
+
href: "/apple-icon-152x152.png"
|
|
9446
|
+
}
|
|
9447
|
+
),
|
|
9448
|
+
/* @__PURE__ */ jsx(
|
|
9449
|
+
"link",
|
|
9450
|
+
{
|
|
9451
|
+
rel: "apple-touch-icon",
|
|
9452
|
+
sizes: "180x180",
|
|
9453
|
+
href: "/apple-icon-180x180.png"
|
|
9454
|
+
}
|
|
9455
|
+
),
|
|
9456
|
+
/* @__PURE__ */ jsx(
|
|
9457
|
+
"link",
|
|
9458
|
+
{
|
|
9459
|
+
rel: "icon",
|
|
9460
|
+
type: "image/png",
|
|
9461
|
+
sizes: "192x192",
|
|
9462
|
+
href: "/android-icon-192x192.png"
|
|
9463
|
+
}
|
|
9464
|
+
),
|
|
9465
|
+
/* @__PURE__ */ jsx(
|
|
9466
|
+
"link",
|
|
9467
|
+
{
|
|
9468
|
+
rel: "icon",
|
|
9469
|
+
type: "image/png",
|
|
9470
|
+
sizes: "32x32",
|
|
9471
|
+
href: "/favicon-32x32.png"
|
|
9472
|
+
}
|
|
9473
|
+
),
|
|
9474
|
+
/* @__PURE__ */ jsx(
|
|
9475
|
+
"link",
|
|
9476
|
+
{
|
|
9477
|
+
rel: "icon",
|
|
9478
|
+
type: "image/png",
|
|
9479
|
+
sizes: "96x96",
|
|
9480
|
+
href: "/favicon-96x96.png"
|
|
9481
|
+
}
|
|
9482
|
+
),
|
|
9483
|
+
/* @__PURE__ */ jsx(
|
|
9484
|
+
"link",
|
|
9485
|
+
{
|
|
9486
|
+
rel: "icon",
|
|
9487
|
+
type: "image/png",
|
|
9488
|
+
sizes: "16x16",
|
|
9489
|
+
href: "/favicon-16x16.png"
|
|
9490
|
+
}
|
|
9491
|
+
),
|
|
9492
|
+
/* @__PURE__ */ jsx("link", { rel: "shortcut icon", href: "/favicon.ico" }),
|
|
9493
|
+
/* @__PURE__ */ jsx("link", { rel: "manifest", href: "/manifest.json" }),
|
|
9494
|
+
/* @__PURE__ */ jsx("meta", { name: "msapplication-TileColor", content: "#ffffff" }),
|
|
9495
|
+
/* @__PURE__ */ jsx("meta", { name: "msapplication-TileImage", content: "/ms-icon-144x144.png" }),
|
|
9496
|
+
/* @__PURE__ */ jsx("meta", { name: "theme-color", content: "#ffffff" }),
|
|
8921
9497
|
/* @__PURE__ */ jsx("link", { rel: "stylesheet", href: "/assets/main.css" }),
|
|
8922
9498
|
/* @__PURE__ */ jsx("style", { children: `
|
|
8923
9499
|
.htmx-indicator {
|
|
@@ -8941,27 +9517,143 @@ const Layout = ({ title, version: version2, children }) => {
|
|
|
8941
9517
|
form .htmx-indicator .spinner { display: flex; }
|
|
8942
9518
|
form .htmx-indicator .search-text { display: none; }
|
|
8943
9519
|
form .spinner { display: none; }
|
|
8944
|
-
|
|
9520
|
+
` })
|
|
8945
9521
|
] }),
|
|
8946
9522
|
/* @__PURE__ */ jsxs("body", { class: "bg-gray-50 dark:bg-gray-900", children: [
|
|
8947
|
-
/* @__PURE__ */
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
9523
|
+
/* @__PURE__ */ jsx(
|
|
9524
|
+
"header",
|
|
9525
|
+
{
|
|
9526
|
+
class: "bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700",
|
|
9527
|
+
"x-data": versionInitializer,
|
|
9528
|
+
"x-init": "queueCheck()",
|
|
9529
|
+
children: /* @__PURE__ */ jsxs("div", { class: "container max-w-2xl mx-auto px-4 py-4", children: [
|
|
9530
|
+
/* @__PURE__ */ jsxs("div", { class: "hidden sm:flex items-center justify-between", children: [
|
|
9531
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center gap-3", children: [
|
|
9532
|
+
/* @__PURE__ */ jsxs(
|
|
9533
|
+
"a",
|
|
9534
|
+
{
|
|
9535
|
+
href: "https://grounded.tools",
|
|
9536
|
+
target: "_blank",
|
|
9537
|
+
rel: "noopener noreferrer",
|
|
9538
|
+
class: "text-xl font-medium text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand",
|
|
9539
|
+
children: [
|
|
9540
|
+
/* @__PURE__ */ jsx("span", { class: "text-primary-600 dark:text-primary-300", children: "grounded" }),
|
|
9541
|
+
/* @__PURE__ */ jsx("span", { class: "text-accent-500", children: "." }),
|
|
9542
|
+
/* @__PURE__ */ jsx("span", { class: "text-gray-900 dark:text-gray-100", children: "tools" })
|
|
9543
|
+
]
|
|
9544
|
+
}
|
|
9545
|
+
),
|
|
9546
|
+
/* @__PURE__ */ jsx("span", { class: "text-gray-400 dark:text-gray-400", children: "|" }),
|
|
9547
|
+
/* @__PURE__ */ jsx(
|
|
9548
|
+
"a",
|
|
9549
|
+
{
|
|
9550
|
+
href: "/",
|
|
9551
|
+
class: "text-lg font-semibold text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand",
|
|
9552
|
+
children: "Docs MCP Server"
|
|
9553
|
+
}
|
|
9554
|
+
),
|
|
9555
|
+
versionString ? /* @__PURE__ */ jsxs(
|
|
9556
|
+
"span",
|
|
9557
|
+
{
|
|
9558
|
+
safe: true,
|
|
9559
|
+
class: "text-sm font-normal text-gray-500 dark:text-slate-400",
|
|
9560
|
+
title: `Version ${versionString}`,
|
|
9561
|
+
children: [
|
|
9562
|
+
"v",
|
|
9563
|
+
versionString
|
|
9564
|
+
]
|
|
9565
|
+
}
|
|
9566
|
+
) : null
|
|
9567
|
+
] }),
|
|
9568
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
|
|
9569
|
+
"span",
|
|
9570
|
+
{
|
|
9571
|
+
"x-show": "hasUpdate",
|
|
9572
|
+
"x-cloak": true,
|
|
9573
|
+
class: "inline-flex items-center gap-2 rounded-full bg-amber-100 dark:bg-amber-500/20 px-3 py-1.5 text-sm font-medium text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30",
|
|
9574
|
+
role: "status",
|
|
9575
|
+
"aria-live": "polite",
|
|
9576
|
+
children: [
|
|
9577
|
+
/* @__PURE__ */ jsx("span", { class: "flex h-4 w-4 items-center justify-center rounded-full bg-amber-500 text-amber-800 dark:text-amber-900 text-xs font-bold", children: "!" }),
|
|
9578
|
+
/* @__PURE__ */ jsx(
|
|
9579
|
+
"a",
|
|
9580
|
+
{
|
|
9581
|
+
"x-bind:href": "latestReleaseUrl",
|
|
9582
|
+
target: "_blank",
|
|
9583
|
+
rel: "noopener noreferrer",
|
|
9584
|
+
class: "hover:text-amber-800 dark:hover:text-amber-200 transition-colors",
|
|
9585
|
+
children: /* @__PURE__ */ jsx("span", { class: "mr-1", children: "Update available" })
|
|
9586
|
+
}
|
|
9587
|
+
)
|
|
9588
|
+
]
|
|
9589
|
+
}
|
|
9590
|
+
) })
|
|
9591
|
+
] }),
|
|
9592
|
+
/* @__PURE__ */ jsxs("div", { class: "sm:hidden space-y-2", children: [
|
|
9593
|
+
/* @__PURE__ */ jsx("div", { class: "flex justify-center", children: /* @__PURE__ */ jsxs(
|
|
9594
|
+
"a",
|
|
9595
|
+
{
|
|
9596
|
+
href: "https://grounded.tools",
|
|
9597
|
+
target: "_blank",
|
|
9598
|
+
rel: "noopener noreferrer",
|
|
9599
|
+
class: "text-xl font-medium text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand",
|
|
9600
|
+
children: [
|
|
9601
|
+
/* @__PURE__ */ jsx("span", { class: "text-primary-600 dark:text-primary-300", children: "grounded" }),
|
|
9602
|
+
/* @__PURE__ */ jsx("span", { class: "text-accent-500", children: "." }),
|
|
9603
|
+
/* @__PURE__ */ jsx("span", { class: "text-gray-900 dark:text-gray-100", children: "tools" })
|
|
9604
|
+
]
|
|
9605
|
+
}
|
|
9606
|
+
) }),
|
|
9607
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center justify-center gap-2", children: [
|
|
9608
|
+
/* @__PURE__ */ jsx(
|
|
9609
|
+
"a",
|
|
9610
|
+
{
|
|
9611
|
+
href: "/",
|
|
9612
|
+
class: "text-lg font-semibold text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand",
|
|
9613
|
+
children: "Docs MCP Server"
|
|
9614
|
+
}
|
|
9615
|
+
),
|
|
9616
|
+
versionString ? /* @__PURE__ */ jsxs(
|
|
9617
|
+
"span",
|
|
9618
|
+
{
|
|
9619
|
+
safe: true,
|
|
9620
|
+
class: "text-sm font-normal text-gray-500 dark:text-slate-400",
|
|
9621
|
+
title: `Version ${versionString}`,
|
|
9622
|
+
children: [
|
|
9623
|
+
"v",
|
|
9624
|
+
versionString
|
|
9625
|
+
]
|
|
9626
|
+
}
|
|
9627
|
+
) : null
|
|
9628
|
+
] }),
|
|
9629
|
+
/* @__PURE__ */ jsx("div", { class: "flex justify-center", children: /* @__PURE__ */ jsxs(
|
|
9630
|
+
"span",
|
|
9631
|
+
{
|
|
9632
|
+
"x-show": "hasUpdate",
|
|
9633
|
+
"x-cloak": true,
|
|
9634
|
+
class: "inline-flex items-center gap-2 rounded-full bg-amber-100 dark:bg-amber-500/20 px-3 py-1.5 text-sm font-medium text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30",
|
|
9635
|
+
role: "status",
|
|
9636
|
+
"aria-live": "polite",
|
|
9637
|
+
children: [
|
|
9638
|
+
/* @__PURE__ */ jsx("span", { class: "flex h-4 w-4 items-center justify-center rounded-full bg-amber-500 text-amber-800 dark:text-amber-900 text-xs font-bold", children: "!" }),
|
|
9639
|
+
/* @__PURE__ */ jsx(
|
|
9640
|
+
"a",
|
|
9641
|
+
{
|
|
9642
|
+
"x-bind:href": "latestReleaseUrl",
|
|
9643
|
+
target: "_blank",
|
|
9644
|
+
rel: "noopener noreferrer",
|
|
9645
|
+
class: "hover:text-amber-800 dark:hover:text-amber-200 transition-colors",
|
|
9646
|
+
children: /* @__PURE__ */ jsx("span", { class: "mr-1", children: "Update available" })
|
|
9647
|
+
}
|
|
9648
|
+
)
|
|
9649
|
+
]
|
|
9650
|
+
}
|
|
9651
|
+
) })
|
|
9652
|
+
] })
|
|
9653
|
+
] })
|
|
9654
|
+
}
|
|
9655
|
+
),
|
|
9656
|
+
/* @__PURE__ */ jsx("div", { class: "container max-w-2xl mx-auto px-4 py-6", children: /* @__PURE__ */ jsx("main", { children }) }),
|
|
8965
9657
|
/* @__PURE__ */ jsx("script", { type: "module", src: "/assets/main.js" })
|
|
8966
9658
|
] })
|
|
8967
9659
|
] });
|
|
@@ -9054,7 +9746,7 @@ const VersionBadge = ({ version: version2 }) => {
|
|
|
9054
9746
|
if (!version2) {
|
|
9055
9747
|
return null;
|
|
9056
9748
|
}
|
|
9057
|
-
return /* @__PURE__ */ jsx("span", { class: "bg-
|
|
9749
|
+
return /* @__PURE__ */ jsx("span", { class: "bg-primary-100 text-primary-800 text-xs font-medium me-2 px-1.5 py-0.5 rounded dark:bg-primary-900 dark:text-primary-300", children: /* @__PURE__ */ jsx("span", { safe: true, children: version2 }) });
|
|
9058
9750
|
};
|
|
9059
9751
|
function getStatusClasses(status) {
|
|
9060
9752
|
const baseClasses = "px-1.5 py-0.5 text-xs font-medium rounded";
|
|
@@ -9463,7 +10155,7 @@ const ScrapeFormContent = ({
|
|
|
9463
10155
|
"x-model": "url",
|
|
9464
10156
|
"x-on:input": "checkUrlPath",
|
|
9465
10157
|
"x-on:paste": "$nextTick(() => checkUrlPath())",
|
|
9466
|
-
class: "mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10158
|
+
class: "mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9467
10159
|
}
|
|
9468
10160
|
),
|
|
9469
10161
|
/* @__PURE__ */ jsx(
|
|
@@ -9504,7 +10196,7 @@ const ScrapeFormContent = ({
|
|
|
9504
10196
|
name: "library",
|
|
9505
10197
|
id: "library",
|
|
9506
10198
|
required: true,
|
|
9507
|
-
class: "mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10199
|
+
class: "mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9508
10200
|
}
|
|
9509
10201
|
)
|
|
9510
10202
|
] }),
|
|
@@ -9526,7 +10218,7 @@ const ScrapeFormContent = ({
|
|
|
9526
10218
|
type: "text",
|
|
9527
10219
|
name: "version",
|
|
9528
10220
|
id: "version",
|
|
9529
|
-
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10221
|
+
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9530
10222
|
}
|
|
9531
10223
|
)
|
|
9532
10224
|
] }),
|
|
@@ -9553,7 +10245,7 @@ const ScrapeFormContent = ({
|
|
|
9553
10245
|
id: "maxPages",
|
|
9554
10246
|
min: "1",
|
|
9555
10247
|
placeholder: "1000",
|
|
9556
|
-
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10248
|
+
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9557
10249
|
}
|
|
9558
10250
|
)
|
|
9559
10251
|
] }),
|
|
@@ -9577,7 +10269,7 @@ const ScrapeFormContent = ({
|
|
|
9577
10269
|
id: "maxDepth",
|
|
9578
10270
|
min: "0",
|
|
9579
10271
|
placeholder: "3",
|
|
9580
|
-
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10272
|
+
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9581
10273
|
}
|
|
9582
10274
|
)
|
|
9583
10275
|
] }),
|
|
@@ -9610,7 +10302,7 @@ const ScrapeFormContent = ({
|
|
|
9610
10302
|
{
|
|
9611
10303
|
name: "scope",
|
|
9612
10304
|
id: "scope",
|
|
9613
|
-
class: "mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-
|
|
10305
|
+
class: "mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white",
|
|
9614
10306
|
children: [
|
|
9615
10307
|
/* @__PURE__ */ jsx("option", { value: "subpages", selected: true, children: "Subpages (Default)" }),
|
|
9616
10308
|
/* @__PURE__ */ jsx("option", { value: "hostname", children: "Hostname" }),
|
|
@@ -9638,7 +10330,7 @@ const ScrapeFormContent = ({
|
|
|
9638
10330
|
id: "includePatterns",
|
|
9639
10331
|
rows: "2",
|
|
9640
10332
|
placeholder: "e.g. docs/* or /api\\/v1.*/",
|
|
9641
|
-
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10333
|
+
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
9642
10334
|
}
|
|
9643
10335
|
)
|
|
9644
10336
|
] }),
|
|
@@ -9661,7 +10353,7 @@ const ScrapeFormContent = ({
|
|
|
9661
10353
|
id: "excludePatterns",
|
|
9662
10354
|
rows: "5",
|
|
9663
10355
|
safe: true,
|
|
9664
|
-
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-
|
|
10356
|
+
class: "mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono text-xs",
|
|
9665
10357
|
children: defaultExcludePatternsText
|
|
9666
10358
|
}
|
|
9667
10359
|
),
|
|
@@ -9693,7 +10385,7 @@ const ScrapeFormContent = ({
|
|
|
9693
10385
|
{
|
|
9694
10386
|
name: "scrapeMode",
|
|
9695
10387
|
id: "scrapeMode",
|
|
9696
|
-
class: "mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-
|
|
10388
|
+
class: "mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white",
|
|
9697
10389
|
children: [
|
|
9698
10390
|
/* @__PURE__ */ jsx("option", { value: ScrapeMode.Auto, selected: true, children: "Auto (Default)" }),
|
|
9699
10391
|
/* @__PURE__ */ jsx("option", { value: ScrapeMode.Fetch, children: "Fetch" }),
|
|
@@ -9752,7 +10444,7 @@ const ScrapeFormContent = ({
|
|
|
9752
10444
|
"button",
|
|
9753
10445
|
{
|
|
9754
10446
|
type: "button",
|
|
9755
|
-
class: "mt-1 px-2 py-0.5 bg-
|
|
10447
|
+
class: "mt-1 px-2 py-0.5 bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-200 rounded text-xs",
|
|
9756
10448
|
"x-on:click": "headers.push({ name: '', value: '' })",
|
|
9757
10449
|
children: "+ Add Header"
|
|
9758
10450
|
}
|
|
@@ -9767,7 +10459,7 @@ const ScrapeFormContent = ({
|
|
|
9767
10459
|
name: "followRedirects",
|
|
9768
10460
|
type: "checkbox",
|
|
9769
10461
|
checked: true,
|
|
9770
|
-
class: "h-4 w-4 text-
|
|
10462
|
+
class: "h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700"
|
|
9771
10463
|
}
|
|
9772
10464
|
),
|
|
9773
10465
|
/* @__PURE__ */ jsx(
|
|
@@ -9787,7 +10479,7 @@ const ScrapeFormContent = ({
|
|
|
9787
10479
|
name: "ignoreErrors",
|
|
9788
10480
|
type: "checkbox",
|
|
9789
10481
|
checked: true,
|
|
9790
|
-
class: "h-4 w-4 text-
|
|
10482
|
+
class: "h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700"
|
|
9791
10483
|
}
|
|
9792
10484
|
),
|
|
9793
10485
|
/* @__PURE__ */ jsx(
|
|
@@ -9805,7 +10497,7 @@ const ScrapeFormContent = ({
|
|
|
9805
10497
|
"button",
|
|
9806
10498
|
{
|
|
9807
10499
|
type: "submit",
|
|
9808
|
-
class: "w-full flex justify-center py-1.5 px-3 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-
|
|
10500
|
+
class: "w-full flex justify-center py-1.5 px-3 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500",
|
|
9809
10501
|
children: "Queue Job"
|
|
9810
10502
|
}
|
|
9811
10503
|
) })
|
|
@@ -10022,6 +10714,7 @@ const VersionDetailsRow = ({
|
|
|
10022
10714
|
"span",
|
|
10023
10715
|
{
|
|
10024
10716
|
"x-show": `$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && !$store.confirmingAction.isDeleting`,
|
|
10717
|
+
class: "mx-1",
|
|
10025
10718
|
children: [
|
|
10026
10719
|
"Confirm?",
|
|
10027
10720
|
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Confirm delete" })
|
|
@@ -10046,34 +10739,48 @@ const VersionDetailsRow = ({
|
|
|
10046
10739
|
)
|
|
10047
10740
|
);
|
|
10048
10741
|
};
|
|
10049
|
-
const LibraryDetailCard = ({ library }) =>
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
progress: v.progress,
|
|
10059
|
-
counts: {
|
|
10060
|
-
documents: v.documentCount,
|
|
10061
|
-
uniqueUrls: v.uniqueUrlCount
|
|
10062
|
-
},
|
|
10063
|
-
indexedAt: v.indexedAt,
|
|
10064
|
-
sourceUrl: v.sourceUrl ?? void 0
|
|
10065
|
-
};
|
|
10066
|
-
return /* @__PURE__ */ jsx(
|
|
10067
|
-
VersionDetailsRow,
|
|
10742
|
+
const LibraryDetailCard = ({ library }) => {
|
|
10743
|
+
const versions = library.versions?.reverse() || [];
|
|
10744
|
+
const latestVersion = versions[0];
|
|
10745
|
+
return (
|
|
10746
|
+
// Use Flowbite Card structure with updated padding and border, and white background
|
|
10747
|
+
/* @__PURE__ */ jsxs("div", { class: "block p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600 mb-4", children: [
|
|
10748
|
+
/* @__PURE__ */ jsx("h3", { class: "text-lg font-medium text-gray-900 dark:text-white mb-1", children: /* @__PURE__ */ jsx("span", { safe: true, children: library.name }) }),
|
|
10749
|
+
latestVersion?.sourceUrl ? /* @__PURE__ */ jsx("div", { class: "text-sm text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx(
|
|
10750
|
+
"a",
|
|
10068
10751
|
{
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10752
|
+
href: latestVersion.sourceUrl,
|
|
10753
|
+
target: "_blank",
|
|
10754
|
+
class: "hover:underline",
|
|
10755
|
+
safe: true,
|
|
10756
|
+
children: latestVersion.sourceUrl
|
|
10072
10757
|
}
|
|
10073
|
-
)
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10758
|
+
) }) : null,
|
|
10759
|
+
/* @__PURE__ */ jsx("div", { class: "mt-2", children: versions.length > 0 ? versions.map((v) => {
|
|
10760
|
+
const adapted = {
|
|
10761
|
+
id: -1,
|
|
10762
|
+
ref: { library: library.name, version: v.version },
|
|
10763
|
+
status: v.status,
|
|
10764
|
+
progress: v.progress,
|
|
10765
|
+
counts: {
|
|
10766
|
+
documents: v.documentCount,
|
|
10767
|
+
uniqueUrls: v.uniqueUrlCount
|
|
10768
|
+
},
|
|
10769
|
+
indexedAt: v.indexedAt,
|
|
10770
|
+
sourceUrl: v.sourceUrl ?? void 0
|
|
10771
|
+
};
|
|
10772
|
+
return /* @__PURE__ */ jsx(
|
|
10773
|
+
VersionDetailsRow,
|
|
10774
|
+
{
|
|
10775
|
+
libraryName: library.name,
|
|
10776
|
+
version: adapted,
|
|
10777
|
+
showDelete: false
|
|
10778
|
+
}
|
|
10779
|
+
);
|
|
10780
|
+
}) : /* @__PURE__ */ jsx("p", { class: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No versions indexed." }) })
|
|
10781
|
+
] })
|
|
10782
|
+
);
|
|
10783
|
+
};
|
|
10077
10784
|
const LibrarySearchCard = ({ library }) => {
|
|
10078
10785
|
return /* @__PURE__ */ jsxs("div", { class: "block p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600 mb-4", children: [
|
|
10079
10786
|
/* @__PURE__ */ jsxs("h2", { class: "text-xl font-semibold mb-2 text-gray-900 dark:text-white", safe: true, children: [
|
|
@@ -10236,37 +10943,51 @@ function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool) {
|
|
|
10236
10943
|
}
|
|
10237
10944
|
);
|
|
10238
10945
|
}
|
|
10239
|
-
const LibraryItem = ({ library }) =>
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
}
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10946
|
+
const LibraryItem = ({ library }) => {
|
|
10947
|
+
const versions = library.versions?.reverse() || [];
|
|
10948
|
+
const latestVersion = versions[0];
|
|
10949
|
+
return (
|
|
10950
|
+
// Use Flowbite Card structure with updated padding and border, and white background
|
|
10951
|
+
/* @__PURE__ */ jsxs("div", { class: "block px-4 py-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600", children: [
|
|
10952
|
+
/* @__PURE__ */ jsx("h3", { class: "text-lg font-medium text-gray-900 dark:text-white", children: /* @__PURE__ */ jsx(
|
|
10953
|
+
"a",
|
|
10954
|
+
{
|
|
10955
|
+
href: `/libraries/${encodeURIComponent(library.name)}`,
|
|
10956
|
+
class: "hover:underline",
|
|
10957
|
+
children: /* @__PURE__ */ jsx("span", { safe: true, children: library.name })
|
|
10958
|
+
}
|
|
10959
|
+
) }),
|
|
10960
|
+
latestVersion?.sourceUrl ? /* @__PURE__ */ jsx("div", { class: "text-sm text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx(
|
|
10961
|
+
"a",
|
|
10962
|
+
{
|
|
10963
|
+
href: latestVersion.sourceUrl,
|
|
10964
|
+
target: "_blank",
|
|
10965
|
+
class: "hover:underline",
|
|
10966
|
+
safe: true,
|
|
10967
|
+
children: latestVersion.sourceUrl
|
|
10968
|
+
}
|
|
10969
|
+
) }) : null,
|
|
10970
|
+
/* @__PURE__ */ jsx("div", { class: "mt-2", children: versions.length > 0 ? versions.map((v) => {
|
|
10971
|
+
const adapted = {
|
|
10972
|
+
id: -1,
|
|
10973
|
+
ref: { library: library.name, version: v.version },
|
|
10974
|
+
status: v.status,
|
|
10975
|
+
progress: v.progress,
|
|
10976
|
+
counts: {
|
|
10977
|
+
documents: v.documentCount,
|
|
10978
|
+
uniqueUrls: v.uniqueUrlCount
|
|
10979
|
+
},
|
|
10980
|
+
indexedAt: v.indexedAt,
|
|
10981
|
+
sourceUrl: v.sourceUrl ?? void 0
|
|
10982
|
+
};
|
|
10983
|
+
return /* @__PURE__ */ jsx(VersionDetailsRow, { libraryName: library.name, version: adapted });
|
|
10984
|
+
}) : (
|
|
10985
|
+
// Display message if no versions are indexed
|
|
10986
|
+
/* @__PURE__ */ jsx("p", { class: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No versions indexed." })
|
|
10987
|
+
) })
|
|
10988
|
+
] })
|
|
10989
|
+
);
|
|
10990
|
+
};
|
|
10270
10991
|
const LibraryList = ({ libraries }) => {
|
|
10271
10992
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { class: "space-y-2", children: libraries.map((library) => /* @__PURE__ */ jsx(LibraryItem, { library })) }) });
|
|
10272
10993
|
};
|
|
@@ -12540,34 +13261,10 @@ class DocumentManagementService {
|
|
|
12540
13261
|
normalizeVersion(version2) {
|
|
12541
13262
|
return (version2 ?? "").toLowerCase();
|
|
12542
13263
|
}
|
|
12543
|
-
constructor(embeddingConfig, pipelineConfig
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
dbDir = storePath;
|
|
12548
|
-
dbPath = path.join(dbDir, "documents.db");
|
|
12549
|
-
logger.debug(`Using database directory from storePath parameter: ${dbDir}`);
|
|
12550
|
-
} else {
|
|
12551
|
-
const projectRoot2 = getProjectRoot();
|
|
12552
|
-
const oldDbDir = path.join(projectRoot2, ".store");
|
|
12553
|
-
const oldDbPath = path.join(oldDbDir, "documents.db");
|
|
12554
|
-
const oldDbExists = fs.existsSync(oldDbPath);
|
|
12555
|
-
if (oldDbExists) {
|
|
12556
|
-
dbPath = oldDbPath;
|
|
12557
|
-
dbDir = oldDbDir;
|
|
12558
|
-
logger.debug(`Using legacy database path: ${dbPath}`);
|
|
12559
|
-
} else {
|
|
12560
|
-
const standardPaths = envPaths("docs-mcp-server", { suffix: "" });
|
|
12561
|
-
dbDir = standardPaths.data;
|
|
12562
|
-
dbPath = path.join(dbDir, "documents.db");
|
|
12563
|
-
logger.debug(`Using standard database directory: ${dbDir}`);
|
|
12564
|
-
}
|
|
12565
|
-
}
|
|
12566
|
-
try {
|
|
12567
|
-
fs.mkdirSync(dbDir, { recursive: true });
|
|
12568
|
-
} catch (error) {
|
|
12569
|
-
logger.error(`⚠️ Failed to create database directory ${dbDir}: ${error}`);
|
|
12570
|
-
}
|
|
13264
|
+
constructor(storePath, embeddingConfig, pipelineConfig) {
|
|
13265
|
+
const dbDir = storePath;
|
|
13266
|
+
const dbPath = path.join(dbDir, "documents.db");
|
|
13267
|
+
logger.debug(`Using database directory: ${dbDir}`);
|
|
12571
13268
|
this.store = new DocumentStore(dbPath, embeddingConfig);
|
|
12572
13269
|
this.documentRetriever = new DocumentRetrieverService(this.store);
|
|
12573
13270
|
this.pipelines = PipelineFactory$1.createStandardPipelines(pipelineConfig);
|
|
@@ -12922,16 +13619,19 @@ async function createDocumentManagement(options = {}) {
|
|
|
12922
13619
|
await client.initialize();
|
|
12923
13620
|
return client;
|
|
12924
13621
|
}
|
|
13622
|
+
if (!options.storePath) {
|
|
13623
|
+
throw new Error("storePath is required when not using a remote server");
|
|
13624
|
+
}
|
|
12925
13625
|
const service = new DocumentManagementService(
|
|
13626
|
+
options.storePath,
|
|
12926
13627
|
options.embeddingConfig,
|
|
12927
|
-
void 0
|
|
12928
|
-
options.storePath
|
|
13628
|
+
void 0
|
|
12929
13629
|
);
|
|
12930
13630
|
await service.initialize();
|
|
12931
13631
|
return service;
|
|
12932
13632
|
}
|
|
12933
|
-
async function createLocalDocumentManagement(
|
|
12934
|
-
const service = new DocumentManagementService(embeddingConfig, void 0
|
|
13633
|
+
async function createLocalDocumentManagement(storePath, embeddingConfig) {
|
|
13634
|
+
const service = new DocumentManagementService(storePath, embeddingConfig, void 0);
|
|
12935
13635
|
await service.initialize();
|
|
12936
13636
|
return service;
|
|
12937
13637
|
}
|
|
@@ -12939,7 +13639,7 @@ function createDefaultAction(program) {
|
|
|
12939
13639
|
return program.addOption(
|
|
12940
13640
|
new Option("--protocol <protocol>", "Protocol for MCP server").env("DOCS_MCP_PROTOCOL").default("auto").choices(["auto", "stdio", "http"])
|
|
12941
13641
|
).addOption(
|
|
12942
|
-
new Option("--port <number>", "Port for the server").env("DOCS_MCP_PORT").env("PORT").default(
|
|
13642
|
+
new Option("--port <number>", "Port for the server").env("DOCS_MCP_PORT").env("PORT").default(DEFAULT_HTTP_PORT.toString()).argParser((v) => {
|
|
12943
13643
|
const n = Number(v);
|
|
12944
13644
|
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
12945
13645
|
throw new Error("Port must be an integer between 1 and 65535");
|
|
@@ -12947,7 +13647,7 @@ function createDefaultAction(program) {
|
|
|
12947
13647
|
return String(n);
|
|
12948
13648
|
})
|
|
12949
13649
|
).addOption(
|
|
12950
|
-
new Option("--host <host>", "Host to bind the server to").env("DOCS_MCP_HOST").env("HOST").default(
|
|
13650
|
+
new Option("--host <host>", "Host to bind the server to").env("DOCS_MCP_HOST").env("HOST").default(DEFAULT_HOST).argParser(validateHost)
|
|
12951
13651
|
).addOption(
|
|
12952
13652
|
new Option(
|
|
12953
13653
|
"--embedding-model <model>",
|
|
@@ -13004,12 +13704,12 @@ function createDefaultAction(program) {
|
|
|
13004
13704
|
validateAuthConfig(authConfig);
|
|
13005
13705
|
warnHttpUsage(authConfig, port);
|
|
13006
13706
|
}
|
|
13007
|
-
const globalOptions = program.
|
|
13707
|
+
const globalOptions = program.opts();
|
|
13008
13708
|
ensurePlaywrightBrowsersInstalled();
|
|
13009
13709
|
const embeddingConfig = resolveEmbeddingContext(options.embeddingModel);
|
|
13010
13710
|
const docService = await createLocalDocumentManagement(
|
|
13011
|
-
|
|
13012
|
-
|
|
13711
|
+
globalOptions.storePath,
|
|
13712
|
+
embeddingConfig
|
|
13013
13713
|
);
|
|
13014
13714
|
const pipelineOptions = {
|
|
13015
13715
|
recoverJobs: options.resume || false,
|
|
@@ -13070,7 +13770,7 @@ async function fetchUrlAction(url, options) {
|
|
|
13070
13770
|
hasHeaders: options.header.length > 0
|
|
13071
13771
|
});
|
|
13072
13772
|
const headers = parseHeaders(options.header);
|
|
13073
|
-
const fetchUrlTool = new FetchUrlTool(new
|
|
13773
|
+
const fetchUrlTool = new FetchUrlTool(new AutoDetectFetcher());
|
|
13074
13774
|
const content = await fetchUrlTool.execute({
|
|
13075
13775
|
url,
|
|
13076
13776
|
followRedirects: options.followRedirects,
|
|
@@ -13160,9 +13860,9 @@ function createListCommand(program) {
|
|
|
13160
13860
|
}
|
|
13161
13861
|
function createMcpCommand(program) {
|
|
13162
13862
|
return program.command("mcp").description("Start MCP server only").addOption(
|
|
13163
|
-
new Option("--protocol <protocol>", "Protocol for MCP server").env("DOCS_MCP_PROTOCOL").default(
|
|
13863
|
+
new Option("--protocol <protocol>", "Protocol for MCP server").env("DOCS_MCP_PROTOCOL").default(DEFAULT_PROTOCOL).choices(["auto", "stdio", "http"])
|
|
13164
13864
|
).addOption(
|
|
13165
|
-
new Option("--port <number>", "Port for the MCP server").env("DOCS_MCP_PORT").env("PORT").default(
|
|
13865
|
+
new Option("--port <number>", "Port for the MCP server").env("DOCS_MCP_PORT").env("PORT").default(DEFAULT_HTTP_PORT.toString()).argParser((v) => {
|
|
13166
13866
|
const n = Number(v);
|
|
13167
13867
|
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
13168
13868
|
throw new Error("Port must be an integer between 1 and 65535");
|
|
@@ -13170,7 +13870,7 @@ function createMcpCommand(program) {
|
|
|
13170
13870
|
return String(n);
|
|
13171
13871
|
})
|
|
13172
13872
|
).addOption(
|
|
13173
|
-
new Option("--host <host>", "Host to bind the MCP server to").env("DOCS_MCP_HOST").env("HOST").default(
|
|
13873
|
+
new Option("--host <host>", "Host to bind the MCP server to").env("DOCS_MCP_HOST").env("HOST").default(DEFAULT_HOST).argParser(validateHost)
|
|
13174
13874
|
).addOption(
|
|
13175
13875
|
new Option(
|
|
13176
13876
|
"--embedding-model <model>",
|
|
@@ -13530,7 +14230,7 @@ function createSearchCommand(program) {
|
|
|
13530
14230
|
}
|
|
13531
14231
|
function createWebCommand(program) {
|
|
13532
14232
|
return program.command("web").description("Start web interface only").addOption(
|
|
13533
|
-
new Option("--port <number>", "Port for the web interface").env("DOCS_MCP_WEB_PORT").env("DOCS_MCP_PORT").env("PORT").default(
|
|
14233
|
+
new Option("--port <number>", "Port for the web interface").env("DOCS_MCP_WEB_PORT").env("DOCS_MCP_PORT").env("PORT").default(DEFAULT_WEB_PORT.toString()).argParser((v) => {
|
|
13534
14234
|
const n = Number(v);
|
|
13535
14235
|
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
13536
14236
|
throw new Error("Port must be an integer between 1 and 65535");
|
|
@@ -13538,7 +14238,7 @@ function createWebCommand(program) {
|
|
|
13538
14238
|
return String(n);
|
|
13539
14239
|
})
|
|
13540
14240
|
).addOption(
|
|
13541
|
-
new Option("--host <host>", "Host to bind the web interface to").env("DOCS_MCP_HOST").env("HOST").default(
|
|
14241
|
+
new Option("--host <host>", "Host to bind the web interface to").env("DOCS_MCP_HOST").env("HOST").default(DEFAULT_HOST).argParser(validateHost)
|
|
13542
14242
|
).addOption(
|
|
13543
14243
|
new Option(
|
|
13544
14244
|
"--embedding-model <model>",
|
|
@@ -13620,7 +14320,7 @@ function createWorkerCommand(program) {
|
|
|
13620
14320
|
return String(n);
|
|
13621
14321
|
})
|
|
13622
14322
|
).addOption(
|
|
13623
|
-
new Option("--host <host>", "Host to bind the worker API to").env("DOCS_MCP_HOST").env("HOST").default(
|
|
14323
|
+
new Option("--host <host>", "Host to bind the worker API to").env("DOCS_MCP_HOST").env("HOST").default(DEFAULT_HOST).argParser(validateHost)
|
|
13624
14324
|
).addOption(
|
|
13625
14325
|
new Option(
|
|
13626
14326
|
"--embedding-model <model>",
|
|
@@ -13640,11 +14340,15 @@ function createWorkerCommand(program) {
|
|
|
13640
14340
|
logger.info(`🚀 Starting external pipeline worker on port ${port}`);
|
|
13641
14341
|
ensurePlaywrightBrowsersInstalled();
|
|
13642
14342
|
const embeddingConfig = resolveEmbeddingContext(cmdOptions.embeddingModel);
|
|
13643
|
-
const
|
|
14343
|
+
const globalOptions = program.parent?.opts() || {};
|
|
14344
|
+
const docService = await createLocalDocumentManagement(
|
|
14345
|
+
globalOptions.storePath,
|
|
14346
|
+
embeddingConfig
|
|
14347
|
+
);
|
|
13644
14348
|
const pipelineOptions = {
|
|
13645
14349
|
recoverJobs: cmdOptions.resume,
|
|
13646
14350
|
// Use the resume option
|
|
13647
|
-
concurrency:
|
|
14351
|
+
concurrency: DEFAULT_MAX_CONCURRENCY
|
|
13648
14352
|
};
|
|
13649
14353
|
const pipeline = await createPipelineWithCallbacks(docService, pipelineOptions);
|
|
13650
14354
|
const config = createAppServerConfig({
|
|
@@ -13692,10 +14396,12 @@ function createCliProgram() {
|
|
|
13692
14396
|
).enablePositionalOptions().allowExcessArguments(false).showHelpAfterError(true);
|
|
13693
14397
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
13694
14398
|
const globalOptions = thisCommand.opts();
|
|
14399
|
+
const resolvedStorePath = resolveStorePath(globalOptions.storePath);
|
|
14400
|
+
globalOptions.storePath = resolvedStorePath;
|
|
13695
14401
|
setupLogging(globalOptions);
|
|
13696
14402
|
initTelemetry({
|
|
13697
14403
|
enabled: globalOptions.telemetry ?? true,
|
|
13698
|
-
storePath:
|
|
14404
|
+
storePath: resolvedStorePath
|
|
13699
14405
|
});
|
|
13700
14406
|
if (shouldEnableTelemetry()) {
|
|
13701
14407
|
if (analytics.isEnabled()) {
|