@arabold/docs-mcp-server 1.29.0 → 1.31.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 +2 -2
- package/dist/assets/main.css +1 -1
- package/dist/assets/main.js +1091 -480
- package/dist/assets/main.js.map +1 -1
- package/dist/index.js +1772 -1183
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/public/assets/main.css +1 -1
- package/public/assets/main.js +1091 -480
- package/public/assets/main.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -19,14 +19,13 @@ import Fastify from "fastify";
|
|
|
19
19
|
import { WebSocketServer } from "ws";
|
|
20
20
|
import { ProxyOAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js";
|
|
21
21
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
22
|
-
import { execSync } from "node:child_process";
|
|
23
|
-
import { chromium } from "playwright";
|
|
24
22
|
import { createWSClient, createTRPCClient, splitLink, httpBatchLink, wsLink, createTRPCProxyClient } from "@trpc/client";
|
|
25
23
|
import superjson from "superjson";
|
|
26
24
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
27
25
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
28
26
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
29
27
|
import { z } from "zod/v3";
|
|
28
|
+
import { chromium } from "playwright";
|
|
30
29
|
import mime from "mime";
|
|
31
30
|
import { HeaderGenerator } from "header-generator";
|
|
32
31
|
import fs$1 from "node:fs/promises";
|
|
@@ -62,6 +61,7 @@ import { escapeHtml } from "@kitajs/html";
|
|
|
62
61
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
63
62
|
import { v4 } from "uuid";
|
|
64
63
|
import { minimatch } from "minimatch";
|
|
64
|
+
import { execSync } from "node:child_process";
|
|
65
65
|
class StoreError extends Error {
|
|
66
66
|
constructor(message, cause) {
|
|
67
67
|
super(cause ? `${message} caused by ${cause}` : message);
|
|
@@ -268,15 +268,19 @@ function createEmbeddingModel(providerAndModel) {
|
|
|
268
268
|
if (!process.env.OPENAI_API_KEY) {
|
|
269
269
|
throw new MissingCredentialsError("openai", ["OPENAI_API_KEY"]);
|
|
270
270
|
}
|
|
271
|
+
const timeoutMs = 3e4;
|
|
271
272
|
const config = {
|
|
272
273
|
...baseConfig,
|
|
273
274
|
modelName: model,
|
|
274
|
-
batchSize: 512
|
|
275
|
+
batchSize: 512,
|
|
275
276
|
// OpenAI supports large batches
|
|
277
|
+
timeout: timeoutMs
|
|
276
278
|
};
|
|
277
279
|
const baseURL = process.env.OPENAI_API_BASE;
|
|
278
280
|
if (baseURL) {
|
|
279
|
-
config.configuration = { baseURL };
|
|
281
|
+
config.configuration = { baseURL, timeout: timeoutMs };
|
|
282
|
+
} else {
|
|
283
|
+
config.configuration = { timeout: timeoutMs };
|
|
280
284
|
}
|
|
281
285
|
return new OpenAIEmbeddings(config);
|
|
282
286
|
}
|
|
@@ -1011,7 +1015,7 @@ class ProxyAuthManager {
|
|
|
1011
1015
|
logger.debug(`Token validation capabilities: ${capabilities.join(", ")}`);
|
|
1012
1016
|
if (capabilities.length === 0) {
|
|
1013
1017
|
logger.warn(
|
|
1014
|
-
"⚠️
|
|
1018
|
+
"⚠️ No token validation mechanisms available - authentication may fail"
|
|
1015
1019
|
);
|
|
1016
1020
|
}
|
|
1017
1021
|
this.proxyProvider = new ProxyOAuthServerProvider({
|
|
@@ -1349,657 +1353,142 @@ class ProxyAuthManager {
|
|
|
1349
1353
|
}
|
|
1350
1354
|
}
|
|
1351
1355
|
}
|
|
1352
|
-
class
|
|
1353
|
-
|
|
1356
|
+
class RemoteEventProxy {
|
|
1357
|
+
constructor(remoteWorkerUrl, localEventBus) {
|
|
1358
|
+
this.remoteWorkerUrl = remoteWorkerUrl;
|
|
1359
|
+
this.localEventBus = localEventBus;
|
|
1360
|
+
}
|
|
1361
|
+
trpcClient = null;
|
|
1362
|
+
wsClient = null;
|
|
1363
|
+
subscription = null;
|
|
1364
|
+
isConnected = false;
|
|
1354
1365
|
/**
|
|
1355
|
-
*
|
|
1356
|
-
* Creates the instance if it doesn't exist.
|
|
1366
|
+
* Start subscribing to remote events and forwarding them locally.
|
|
1357
1367
|
*/
|
|
1358
|
-
|
|
1359
|
-
if (
|
|
1360
|
-
|
|
1368
|
+
async connect() {
|
|
1369
|
+
if (this.isConnected) {
|
|
1370
|
+
logger.warn("Remote event proxy already connected");
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
logger.debug(`Connecting to remote worker at ${this.remoteWorkerUrl}`);
|
|
1374
|
+
try {
|
|
1375
|
+
const url = new URL(this.remoteWorkerUrl);
|
|
1376
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
1377
|
+
const wsUrl = baseUrl.replace(/^http/, "ws");
|
|
1378
|
+
this.wsClient = createWSClient({
|
|
1379
|
+
url: wsUrl
|
|
1380
|
+
});
|
|
1381
|
+
this.trpcClient = createTRPCClient({
|
|
1382
|
+
links: [
|
|
1383
|
+
splitLink({
|
|
1384
|
+
condition: (op) => op.type === "subscription",
|
|
1385
|
+
true: wsLink({ client: this.wsClient, transformer: superjson }),
|
|
1386
|
+
false: httpBatchLink({ url: this.remoteWorkerUrl, transformer: superjson })
|
|
1387
|
+
})
|
|
1388
|
+
]
|
|
1389
|
+
});
|
|
1390
|
+
this.subscription = this.trpcClient.events.subscribe.subscribe(
|
|
1391
|
+
{},
|
|
1392
|
+
// Subscribe to all event types
|
|
1393
|
+
{
|
|
1394
|
+
onData: (data) => {
|
|
1395
|
+
logger.debug(`Received remote event: ${data.type}`);
|
|
1396
|
+
this.localEventBus.emit(data.type, data.payload);
|
|
1397
|
+
},
|
|
1398
|
+
onError: (error) => {
|
|
1399
|
+
logger.error(`❌ Remote event subscription error: ${error}`);
|
|
1400
|
+
this.isConnected = false;
|
|
1401
|
+
this.scheduleReconnect();
|
|
1402
|
+
},
|
|
1403
|
+
onStarted: () => {
|
|
1404
|
+
logger.debug("Remote event subscription started");
|
|
1405
|
+
this.isConnected = true;
|
|
1406
|
+
},
|
|
1407
|
+
onComplete: () => {
|
|
1408
|
+
logger.debug("Remote event subscription completed");
|
|
1409
|
+
this.isConnected = false;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
logger.error(`❌ Failed to connect to remote worker: ${error}`);
|
|
1415
|
+
this.scheduleReconnect();
|
|
1361
1416
|
}
|
|
1362
|
-
return EmbeddingConfig.instance;
|
|
1363
1417
|
}
|
|
1364
1418
|
/**
|
|
1365
|
-
*
|
|
1419
|
+
* Disconnect from the remote worker and stop forwarding events.
|
|
1366
1420
|
*/
|
|
1367
|
-
|
|
1368
|
-
|
|
1421
|
+
disconnect() {
|
|
1422
|
+
if (this.subscription) {
|
|
1423
|
+
this.subscription.unsubscribe();
|
|
1424
|
+
this.subscription = null;
|
|
1425
|
+
}
|
|
1426
|
+
if (this.wsClient) {
|
|
1427
|
+
this.wsClient.close();
|
|
1428
|
+
this.wsClient = null;
|
|
1429
|
+
}
|
|
1430
|
+
this.isConnected = false;
|
|
1431
|
+
logger.info("🚫 Disconnected from remote worker");
|
|
1369
1432
|
}
|
|
1370
1433
|
/**
|
|
1371
|
-
*
|
|
1372
|
-
* This avoids expensive API calls for dimension detection in telemetry.
|
|
1373
|
-
*
|
|
1374
|
-
* Note: The "openai" provider also supports OpenAI-compatible APIs like:
|
|
1375
|
-
* - Ollama (local models)
|
|
1376
|
-
* - LMStudio (local models)
|
|
1377
|
-
* - Any service implementing OpenAI's embedding API
|
|
1434
|
+
* Check if the proxy is currently connected to the remote worker.
|
|
1378
1435
|
*/
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
"text-embedding-3-small": 1536,
|
|
1382
|
-
"text-embedding-3-large": 3072,
|
|
1383
|
-
"text-embedding-ada-002": 1536,
|
|
1384
|
-
// Google Vertex AI models
|
|
1385
|
-
"text-embedding-004": 768,
|
|
1386
|
-
"textembedding-gecko@003": 768,
|
|
1387
|
-
"textembedding-gecko@002": 768,
|
|
1388
|
-
"textembedding-gecko@001": 768,
|
|
1389
|
-
// Google Gemini models (with MRL support)
|
|
1390
|
-
"text-embedding-preview-0409": 768,
|
|
1391
|
-
"embedding-001": 768,
|
|
1392
|
-
// AWS Bedrock models
|
|
1393
|
-
// Amazon Titan models
|
|
1394
|
-
"amazon.titan-embed-text-v1": 1536,
|
|
1395
|
-
"amazon.titan-embed-text-v2:0": 1024,
|
|
1396
|
-
"amazon.titan-embed-image-v1": 1024,
|
|
1397
|
-
// Image embedding model
|
|
1398
|
-
// Cohere models
|
|
1399
|
-
"cohere.embed-english-v3": 1024,
|
|
1400
|
-
"cohere.embed-multilingual-v3": 1024,
|
|
1401
|
-
// SageMaker models (hosted on AWS SageMaker)
|
|
1402
|
-
"intfloat/multilingual-e5-large": 1024,
|
|
1403
|
-
// Additional AWS models that might be supported
|
|
1404
|
-
// Note: Some of these might be placeholders - verify dimensions before use
|
|
1405
|
-
// "amazon.nova-embed-multilingual-v1:0": 4096, // Commented out as noted in source
|
|
1406
|
-
// MTEB Leaderboard models (source: https://huggingface.co/spaces/mteb/leaderboard)
|
|
1407
|
-
// Top performing models from Massive Text Embedding Benchmark
|
|
1408
|
-
"sentence-transformers/all-MiniLM-L6-v2": 384,
|
|
1409
|
-
"gemini-embedding-001": 3072,
|
|
1410
|
-
"Qwen/Qwen3-Embedding-8B": 4096,
|
|
1411
|
-
"Qwen/Qwen3-Embedding-4B": 2560,
|
|
1412
|
-
"Qwen/Qwen3-Embedding-0.6B": 1024,
|
|
1413
|
-
"Linq-AI-Research/Linq-Embed-Mistral": 4096,
|
|
1414
|
-
"Alibaba-NLP/gte-Qwen2-7B-instruct": 3584,
|
|
1415
|
-
"intfloat/multilingual-e5-large-instruct": 1024,
|
|
1416
|
-
"Salesforce/SFR-Embedding-Mistral": 4096,
|
|
1417
|
-
"text-multilingual-embedding-002": 768,
|
|
1418
|
-
"GritLM/GritLM-7B": 4096,
|
|
1419
|
-
"GritLM/GritLM-8x7B": 4096,
|
|
1420
|
-
"intfloat/e5-mistral-7b-instruct": 4096,
|
|
1421
|
-
"Cohere/Cohere-embed-multilingual-v3.0": 1024,
|
|
1422
|
-
"Alibaba-NLP/gte-Qwen2-1.5B-instruct": 8960,
|
|
1423
|
-
"Lajavaness/bilingual-embedding-large": 1024,
|
|
1424
|
-
"Salesforce/SFR-Embedding-2_R": 4096,
|
|
1425
|
-
"NovaSearch/stella_en_1.5B_v5": 8960,
|
|
1426
|
-
"NovaSearch/jasper_en_vision_language_v1": 8960,
|
|
1427
|
-
"nvidia/NV-Embed-v2": 4096,
|
|
1428
|
-
"OrdalieTech/Solon-embeddings-large-0.1": 1024,
|
|
1429
|
-
"BAAI/bge-m3": 1024,
|
|
1430
|
-
"HIT-TMG/KaLM-embedding-multilingual-mini-v1": 896,
|
|
1431
|
-
"jinaai/jina-embeddings-v3": 1024,
|
|
1432
|
-
"Alibaba-NLP/gte-multilingual-base": 768,
|
|
1433
|
-
"Lajavaness/bilingual-embedding-base": 768,
|
|
1434
|
-
"HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1": 896,
|
|
1435
|
-
"nvidia/NV-Embed-v1": 4096,
|
|
1436
|
-
"Cohere/Cohere-embed-multilingual-light-v3.0": 384,
|
|
1437
|
-
"manu/bge-m3-custom-fr": 1024,
|
|
1438
|
-
"Lajavaness/bilingual-embedding-small": 384,
|
|
1439
|
-
"Snowflake/snowflake-arctic-embed-l-v2.0": 1024,
|
|
1440
|
-
"intfloat/multilingual-e5-base": 768,
|
|
1441
|
-
"voyage-3-lite": 512,
|
|
1442
|
-
"voyage-3": 1024,
|
|
1443
|
-
"intfloat/multilingual-e5-small": 384,
|
|
1444
|
-
"Alibaba-NLP/gte-Qwen1.5-7B-instruct": 4096,
|
|
1445
|
-
"Snowflake/snowflake-arctic-embed-m-v2.0": 768,
|
|
1446
|
-
"deepvk/USER-bge-m3": 1024,
|
|
1447
|
-
"Cohere/Cohere-embed-english-v3.0": 1024,
|
|
1448
|
-
"Omartificial-Intelligence-Space/Arabic-labse-Matryoshka": 768,
|
|
1449
|
-
"ibm-granite/granite-embedding-278m-multilingual": 768,
|
|
1450
|
-
"NovaSearch/stella_en_400M_v5": 4096,
|
|
1451
|
-
"omarelshehy/arabic-english-sts-matryoshka": 1024,
|
|
1452
|
-
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2": 768,
|
|
1453
|
-
"Omartificial-Intelligence-Space/Arabic-all-nli-triplet-Matryoshka": 768,
|
|
1454
|
-
"Haon-Chen/speed-embedding-7b-instruct": 4096,
|
|
1455
|
-
"sentence-transformers/LaBSE": 768,
|
|
1456
|
-
"WhereIsAI/UAE-Large-V1": 1024,
|
|
1457
|
-
"ibm-granite/granite-embedding-107m-multilingual": 384,
|
|
1458
|
-
"mixedbread-ai/mxbai-embed-large-v1": 1024,
|
|
1459
|
-
"intfloat/e5-large-v2": 1024,
|
|
1460
|
-
"avsolatorio/GIST-large-Embedding-v0": 1024,
|
|
1461
|
-
"sdadas/mmlw-e5-large": 1024,
|
|
1462
|
-
"nomic-ai/nomic-embed-text-v1": 768,
|
|
1463
|
-
"nomic-ai/nomic-embed-text-v1-ablated": 768,
|
|
1464
|
-
"intfloat/e5-base-v2": 768,
|
|
1465
|
-
"BAAI/bge-large-en-v1.5": 1024,
|
|
1466
|
-
"intfloat/e5-large": 1024,
|
|
1467
|
-
"Omartificial-Intelligence-Space/Arabic-MiniLM-L12-v2-all-nli-triplet": 384,
|
|
1468
|
-
"Cohere/Cohere-embed-english-light-v3.0": 384,
|
|
1469
|
-
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2": 768,
|
|
1470
|
-
"Gameselo/STS-multilingual-mpnet-base-v2": 768,
|
|
1471
|
-
"thenlper/gte-large": 1024,
|
|
1472
|
-
"avsolatorio/GIST-Embedding-v0": 768,
|
|
1473
|
-
"nomic-ai/nomic-embed-text-v1-unsupervised": 768,
|
|
1474
|
-
"infgrad/stella-base-en-v2": 768,
|
|
1475
|
-
"avsolatorio/NoInstruct-small-Embedding-v0": 384,
|
|
1476
|
-
"dwzhu/e5-base-4k": 768,
|
|
1477
|
-
"sdadas/mmlw-e5-base": 768,
|
|
1478
|
-
"voyage-multilingual-2": 1024,
|
|
1479
|
-
"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-supervised": 4096,
|
|
1480
|
-
"BAAI/bge-base-en-v1.5": 768,
|
|
1481
|
-
"avsolatorio/GIST-small-Embedding-v0": 384,
|
|
1482
|
-
"sdadas/mmlw-roberta-large": 1024,
|
|
1483
|
-
"nomic-ai/nomic-embed-text-v1.5": 768,
|
|
1484
|
-
"minishlab/potion-multilingual-128M": 256,
|
|
1485
|
-
"shibing624/text2vec-base-multilingual": 384,
|
|
1486
|
-
"thenlper/gte-base": 768,
|
|
1487
|
-
"intfloat/e5-small-v2": 384,
|
|
1488
|
-
"intfloat/e5-base": 768,
|
|
1489
|
-
"sentence-transformers/static-similarity-mrl-multilingual-v1": 1024,
|
|
1490
|
-
"manu/sentence_croissant_alpha_v0.3": 2048,
|
|
1491
|
-
"BAAI/bge-small-en-v1.5": 512,
|
|
1492
|
-
"thenlper/gte-small": 384,
|
|
1493
|
-
"sdadas/mmlw-e5-small": 384,
|
|
1494
|
-
"manu/sentence_croissant_alpha_v0.4": 2048,
|
|
1495
|
-
"manu/sentence_croissant_alpha_v0.2": 2048,
|
|
1496
|
-
"abhinand/MedEmbed-small-v0.1": 384,
|
|
1497
|
-
"ibm-granite/granite-embedding-125m-english": 768,
|
|
1498
|
-
"intfloat/e5-small": 384,
|
|
1499
|
-
"voyage-large-2-instruct": 1024,
|
|
1500
|
-
"sdadas/mmlw-roberta-base": 768,
|
|
1501
|
-
"Snowflake/snowflake-arctic-embed-l": 1024,
|
|
1502
|
-
"Mihaiii/Ivysaur": 384,
|
|
1503
|
-
"Snowflake/snowflake-arctic-embed-m-long": 768,
|
|
1504
|
-
"bigscience/sgpt-bloom-7b1-msmarco": 4096,
|
|
1505
|
-
"avsolatorio/GIST-all-MiniLM-L6-v2": 384,
|
|
1506
|
-
"sergeyzh/LaBSE-ru-turbo": 768,
|
|
1507
|
-
"sentence-transformers/all-mpnet-base-v2": 768,
|
|
1508
|
-
"Snowflake/snowflake-arctic-embed-m": 768,
|
|
1509
|
-
"Snowflake/snowflake-arctic-embed-s": 384,
|
|
1510
|
-
"sentence-transformers/all-MiniLM-L12-v2": 384,
|
|
1511
|
-
"Mihaiii/gte-micro-v4": 384,
|
|
1512
|
-
"Snowflake/snowflake-arctic-embed-m-v1.5": 768,
|
|
1513
|
-
"cointegrated/LaBSE-en-ru": 768,
|
|
1514
|
-
"Mihaiii/Bulbasaur": 384,
|
|
1515
|
-
"ibm-granite/granite-embedding-30m-english": 384,
|
|
1516
|
-
"deepfile/embedder-100p": 768,
|
|
1517
|
-
"Jaume/gemma-2b-embeddings": 2048,
|
|
1518
|
-
"OrlikB/KartonBERT-USE-base-v1": 768,
|
|
1519
|
-
"izhx/udever-bloom-7b1": 4096,
|
|
1520
|
-
"izhx/udever-bloom-1b1": 1024,
|
|
1521
|
-
"brahmairesearch/slx-v0.1": 384,
|
|
1522
|
-
"Mihaiii/Wartortle": 384,
|
|
1523
|
-
"izhx/udever-bloom-3b": 2048,
|
|
1524
|
-
"deepvk/USER-base": 768,
|
|
1525
|
-
"ai-forever/ru-en-RoSBERTa": 1024,
|
|
1526
|
-
"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-unsup-simcse": 4096,
|
|
1527
|
-
"Mihaiii/Venusaur": 384,
|
|
1528
|
-
"Snowflake/snowflake-arctic-embed-xs": 384,
|
|
1529
|
-
"jinaai/jina-embedding-b-en-v1": 768,
|
|
1530
|
-
"Mihaiii/gte-micro": 384,
|
|
1531
|
-
"aari1995/German_Semantic_STS_V2": 1024,
|
|
1532
|
-
"Mihaiii/Squirtle": 384,
|
|
1533
|
-
"OrlikB/st-polish-kartonberta-base-alpha-v1": 768,
|
|
1534
|
-
"sergeyzh/rubert-tiny-turbo": 312,
|
|
1535
|
-
"minishlab/potion-base-8M": 256,
|
|
1536
|
-
"minishlab/M2V_base_glove_subword": 256,
|
|
1537
|
-
"jinaai/jina-embedding-s-en-v1": 512,
|
|
1538
|
-
"minishlab/potion-base-4M": 128,
|
|
1539
|
-
"minishlab/M2V_base_output": 256,
|
|
1540
|
-
"DeepPavlov/rubert-base-cased-sentence": 768,
|
|
1541
|
-
"jinaai/jina-embeddings-v2-small-en": 512,
|
|
1542
|
-
"cointegrated/rubert-tiny2": 312,
|
|
1543
|
-
"minishlab/M2V_base_glove": 256,
|
|
1544
|
-
"cointegrated/rubert-tiny": 312,
|
|
1545
|
-
"silma-ai/silma-embeddding-matryoshka-v0.1": 768,
|
|
1546
|
-
"DeepPavlov/rubert-base-cased": 768,
|
|
1547
|
-
"Omartificial-Intelligence-Space/Arabic-mpnet-base-all-nli-triplet": 768,
|
|
1548
|
-
"izhx/udever-bloom-560m": 1024,
|
|
1549
|
-
"minishlab/potion-base-2M": 64,
|
|
1550
|
-
"DeepPavlov/distilrubert-small-cased-conversational": 768,
|
|
1551
|
-
"consciousAI/cai-lunaris-text-embeddings": 1024,
|
|
1552
|
-
"deepvk/deberta-v1-base": 768,
|
|
1553
|
-
"Omartificial-Intelligence-Space/Arabert-all-nli-triplet-Matryoshka": 768,
|
|
1554
|
-
"Omartificial-Intelligence-Space/Marbert-all-nli-triplet-Matryoshka": 768,
|
|
1555
|
-
"ai-forever/sbert_large_mt_nlu_ru": 1024,
|
|
1556
|
-
"ai-forever/sbert_large_nlu_ru": 1024,
|
|
1557
|
-
"malenia1/ternary-weight-embedding": 1024,
|
|
1558
|
-
"jinaai/jina-embeddings-v2-base-en": 768,
|
|
1559
|
-
"VPLabs/SearchMap_Preview": 4096,
|
|
1560
|
-
"Hum-Works/lodestone-base-4096-v1": 768,
|
|
1561
|
-
"jinaai/jina-embeddings-v4": 2048
|
|
1562
|
-
};
|
|
1563
|
-
/**
|
|
1564
|
-
* Lowercase lookup map for case-insensitive model dimension queries.
|
|
1565
|
-
* Built lazily from knownModelDimensions to ensure consistency.
|
|
1566
|
-
*/
|
|
1567
|
-
modelLookup;
|
|
1568
|
-
constructor() {
|
|
1569
|
-
this.modelLookup = /* @__PURE__ */ new Map();
|
|
1570
|
-
for (const [model, dimensions] of Object.entries(this.knownModelDimensions)) {
|
|
1571
|
-
this.modelLookup.set(model.toLowerCase(), dimensions);
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
/**
|
|
1575
|
-
* Parse embedding model configuration from a provided model specification.
|
|
1576
|
-
* This is a synchronous operation that extracts provider, model, and known dimensions.
|
|
1577
|
-
*
|
|
1578
|
-
* Supports various providers:
|
|
1579
|
-
* - openai: OpenAI models and OpenAI-compatible APIs (Ollama, LMStudio, etc.)
|
|
1580
|
-
* - vertex: Google Cloud Vertex AI
|
|
1581
|
-
* - gemini: Google Generative AI
|
|
1582
|
-
* - aws: AWS Bedrock models
|
|
1583
|
-
* - microsoft: Azure OpenAI
|
|
1584
|
-
* - sagemaker: AWS SageMaker hosted models
|
|
1585
|
-
*
|
|
1586
|
-
* @param modelSpec Model specification (e.g., "openai:text-embedding-3-small"), defaults to "text-embedding-3-small"
|
|
1587
|
-
* @returns Parsed embedding model configuration
|
|
1588
|
-
*/
|
|
1589
|
-
parse(modelSpec) {
|
|
1590
|
-
const spec = modelSpec || "text-embedding-3-small";
|
|
1591
|
-
const colonIndex = spec.indexOf(":");
|
|
1592
|
-
let provider;
|
|
1593
|
-
let model;
|
|
1594
|
-
if (colonIndex === -1) {
|
|
1595
|
-
provider = "openai";
|
|
1596
|
-
model = spec;
|
|
1597
|
-
} else {
|
|
1598
|
-
provider = spec.substring(0, colonIndex);
|
|
1599
|
-
model = spec.substring(colonIndex + 1);
|
|
1600
|
-
}
|
|
1601
|
-
const dimensions = this.modelLookup?.get(model.toLowerCase()) || null;
|
|
1602
|
-
return {
|
|
1603
|
-
provider,
|
|
1604
|
-
model,
|
|
1605
|
-
dimensions,
|
|
1606
|
-
modelSpec: spec
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
/**
|
|
1610
|
-
* Get the known dimensions for a specific model.
|
|
1611
|
-
* Returns null if the model dimensions are not known.
|
|
1612
|
-
* Uses case-insensitive lookup.
|
|
1613
|
-
*
|
|
1614
|
-
* @param model The model name (e.g., "text-embedding-3-small")
|
|
1615
|
-
* @returns Known dimensions or null
|
|
1616
|
-
*/
|
|
1617
|
-
getKnownDimensions(model) {
|
|
1618
|
-
return this.modelLookup?.get(model.toLowerCase()) || null;
|
|
1619
|
-
}
|
|
1620
|
-
/**
|
|
1621
|
-
* Add or update known dimensions for a model.
|
|
1622
|
-
* This can be used to cache discovered dimensions.
|
|
1623
|
-
* Stores both original case and lowercase for consistent lookup.
|
|
1624
|
-
*
|
|
1625
|
-
* @param model The model name
|
|
1626
|
-
* @param dimensions The dimensions to cache
|
|
1627
|
-
*/
|
|
1628
|
-
setKnownDimensions(model, dimensions) {
|
|
1629
|
-
this.knownModelDimensions[model] = dimensions;
|
|
1630
|
-
if (this.modelLookup) {
|
|
1631
|
-
this.modelLookup.set(model.toLowerCase(), dimensions);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
/**
|
|
1635
|
-
* Static method to parse embedding model configuration using the singleton instance.
|
|
1636
|
-
* This maintains backward compatibility while using the class-based approach.
|
|
1637
|
-
*/
|
|
1638
|
-
static parseEmbeddingConfig(modelSpec) {
|
|
1639
|
-
return EmbeddingConfig.getInstance().parse(modelSpec);
|
|
1640
|
-
}
|
|
1641
|
-
/**
|
|
1642
|
-
* Static method to get known model dimensions using the singleton instance.
|
|
1643
|
-
* This maintains backward compatibility while using the class-based approach.
|
|
1644
|
-
*/
|
|
1645
|
-
static getKnownModelDimensions(model) {
|
|
1646
|
-
return EmbeddingConfig.getInstance().getKnownDimensions(model);
|
|
1436
|
+
isActive() {
|
|
1437
|
+
return this.isConnected;
|
|
1647
1438
|
}
|
|
1648
1439
|
/**
|
|
1649
|
-
*
|
|
1650
|
-
* This maintains backward compatibility while using the class-based approach.
|
|
1440
|
+
* Schedule a reconnection attempt after a delay.
|
|
1651
1441
|
*/
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
rootCommand = rootCommand.parent;
|
|
1660
|
-
}
|
|
1661
|
-
return rootCommand?.opts() || {};
|
|
1662
|
-
}
|
|
1663
|
-
function getEventBus(command) {
|
|
1664
|
-
const eventBus = command?._eventBus;
|
|
1665
|
-
if (!eventBus) {
|
|
1666
|
-
throw new Error("EventBusService not initialized");
|
|
1667
|
-
}
|
|
1668
|
-
return eventBus;
|
|
1669
|
-
}
|
|
1670
|
-
function ensurePlaywrightBrowsersInstalled() {
|
|
1671
|
-
if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === "1") {
|
|
1672
|
-
logger.debug(
|
|
1673
|
-
"PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD is set, skipping Playwright browser install."
|
|
1674
|
-
);
|
|
1675
|
-
return;
|
|
1676
|
-
}
|
|
1677
|
-
const chromiumEnvPath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH;
|
|
1678
|
-
if (chromiumEnvPath && existsSync(chromiumEnvPath)) {
|
|
1679
|
-
logger.debug(
|
|
1680
|
-
`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set to '${chromiumEnvPath}', skipping Playwright browser install.`
|
|
1681
|
-
);
|
|
1682
|
-
return;
|
|
1683
|
-
}
|
|
1684
|
-
try {
|
|
1685
|
-
const chromiumPath = chromium.executablePath();
|
|
1686
|
-
if (!chromiumPath || !existsSync(chromiumPath)) {
|
|
1687
|
-
throw new Error("Playwright Chromium browser not found");
|
|
1688
|
-
}
|
|
1689
|
-
} catch (error) {
|
|
1690
|
-
logger.debug(String(error));
|
|
1691
|
-
try {
|
|
1692
|
-
console.log(
|
|
1693
|
-
"🌐 Installing Playwright Chromium browser... (this may take a moment)"
|
|
1694
|
-
);
|
|
1695
|
-
execSync("npm exec -y playwright install --no-shell --with-deps chromium", {
|
|
1696
|
-
stdio: "ignore",
|
|
1697
|
-
// Suppress output
|
|
1698
|
-
cwd: getProjectRoot()
|
|
1699
|
-
});
|
|
1700
|
-
} catch (_installErr) {
|
|
1701
|
-
console.error(
|
|
1702
|
-
"❌ Failed to install Playwright browsers automatically. Please run:\n npx playwright install --no-shell --with-deps chromium\nand try again."
|
|
1703
|
-
);
|
|
1704
|
-
process.exit(1);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
function resolveProtocol(protocol) {
|
|
1709
|
-
if (protocol === "auto") {
|
|
1710
|
-
if (!process.stdin.isTTY && !process.stdout.isTTY) {
|
|
1711
|
-
return "stdio";
|
|
1712
|
-
}
|
|
1713
|
-
return "http";
|
|
1714
|
-
}
|
|
1715
|
-
if (protocol === "stdio" || protocol === "http") {
|
|
1716
|
-
return protocol;
|
|
1717
|
-
}
|
|
1718
|
-
throw new Error(`Invalid protocol: ${protocol}. Must be 'auto', 'stdio', or 'http'`);
|
|
1719
|
-
}
|
|
1720
|
-
const formatOutput = (data) => JSON.stringify(data, null, 2);
|
|
1721
|
-
function setupLogging(options, protocol) {
|
|
1722
|
-
if (options.silent) {
|
|
1723
|
-
setLogLevel(LogLevel.ERROR);
|
|
1724
|
-
} else if (options.verbose) {
|
|
1725
|
-
setLogLevel(LogLevel.DEBUG);
|
|
1442
|
+
scheduleReconnect() {
|
|
1443
|
+
logger.info("🔄 Scheduling reconnect to remote worker in 5 seconds...");
|
|
1444
|
+
setTimeout(() => {
|
|
1445
|
+
if (!this.isConnected) {
|
|
1446
|
+
this.connect();
|
|
1447
|
+
}
|
|
1448
|
+
}, 5e3);
|
|
1726
1449
|
}
|
|
1727
1450
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1451
|
+
class ToolError extends Error {
|
|
1452
|
+
constructor(message, toolName) {
|
|
1453
|
+
super(message);
|
|
1454
|
+
this.toolName = toolName;
|
|
1455
|
+
this.name = this.constructor.name;
|
|
1732
1456
|
}
|
|
1733
|
-
return port;
|
|
1734
1457
|
}
|
|
1735
|
-
|
|
1736
|
-
const trimmed = hostString.trim();
|
|
1737
|
-
if (!trimmed) {
|
|
1738
|
-
throw new Error("❌ Host cannot be empty");
|
|
1739
|
-
}
|
|
1740
|
-
if (trimmed.includes(" ") || trimmed.includes(" ") || trimmed.includes("\n")) {
|
|
1741
|
-
throw new Error("❌ Host cannot contain whitespace");
|
|
1742
|
-
}
|
|
1743
|
-
return trimmed;
|
|
1458
|
+
class ValidationError extends ToolError {
|
|
1744
1459
|
}
|
|
1745
|
-
|
|
1460
|
+
const DEFAULT_MAX_PAGES = 1e3;
|
|
1461
|
+
const DEFAULT_MAX_DEPTH$1 = 3;
|
|
1462
|
+
const DEFAULT_MAX_CONCURRENCY = 3;
|
|
1463
|
+
const DEFAULT_PROTOCOL = "auto";
|
|
1464
|
+
const DEFAULT_HTTP_PORT = 6280;
|
|
1465
|
+
const DEFAULT_WEB_PORT = 6281;
|
|
1466
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
1467
|
+
const DEFAULT_PAGE_TIMEOUT = 5e3;
|
|
1468
|
+
const FETCHER_MAX_RETRIES = 6;
|
|
1469
|
+
const FETCHER_BASE_DELAY = 1e3;
|
|
1470
|
+
const FETCHER_MAX_CACHE_ITEMS = 200;
|
|
1471
|
+
const FETCHER_MAX_CACHE_ITEM_SIZE_BYTES = 500 * 1024;
|
|
1472
|
+
const SPLITTER_MIN_CHUNK_SIZE = 500;
|
|
1473
|
+
const SPLITTER_PREFERRED_CHUNK_SIZE = 1500;
|
|
1474
|
+
const SPLITTER_MAX_CHUNK_SIZE = 5e3;
|
|
1475
|
+
const EMBEDDING_BATCH_SIZE = 100;
|
|
1476
|
+
const EMBEDDING_BATCH_CHARS = 5e4;
|
|
1477
|
+
const MIGRATION_MAX_RETRIES = 5;
|
|
1478
|
+
const MIGRATION_RETRY_DELAY_MS = 300;
|
|
1479
|
+
const SEARCH_OVERFETCH_FACTOR = 2;
|
|
1480
|
+
const SEARCH_WEIGHT_VEC = 1;
|
|
1481
|
+
const SEARCH_WEIGHT_FTS = 1;
|
|
1482
|
+
const VECTOR_SEARCH_MULTIPLIER = 10;
|
|
1483
|
+
function createResponse(text) {
|
|
1746
1484
|
return {
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
readOnly: options.readOnly ?? false,
|
|
1755
|
-
auth: options.auth,
|
|
1756
|
-
startupContext: options.startupContext
|
|
1757
|
-
};
|
|
1758
|
-
}
|
|
1759
|
-
function parseHeaders(headerOptions) {
|
|
1760
|
-
const headers = {};
|
|
1761
|
-
if (Array.isArray(headerOptions)) {
|
|
1762
|
-
for (const entry of headerOptions) {
|
|
1763
|
-
const idx = entry.indexOf(":");
|
|
1764
|
-
if (idx > 0) {
|
|
1765
|
-
const name = entry.slice(0, idx).trim();
|
|
1766
|
-
const value = entry.slice(idx + 1).trim();
|
|
1767
|
-
if (name) headers[name] = value;
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
return headers;
|
|
1772
|
-
}
|
|
1773
|
-
function parseAuthConfig(options) {
|
|
1774
|
-
if (!options.authEnabled) {
|
|
1775
|
-
return void 0;
|
|
1776
|
-
}
|
|
1777
|
-
return {
|
|
1778
|
-
enabled: true,
|
|
1779
|
-
issuerUrl: options.authIssuerUrl,
|
|
1780
|
-
audience: options.authAudience,
|
|
1781
|
-
scopes: ["openid", "profile"]
|
|
1782
|
-
// Default scopes for OAuth2/OIDC
|
|
1783
|
-
};
|
|
1784
|
-
}
|
|
1785
|
-
function validateAuthConfig(authConfig) {
|
|
1786
|
-
if (!authConfig.enabled) {
|
|
1787
|
-
return;
|
|
1788
|
-
}
|
|
1789
|
-
const errors = [];
|
|
1790
|
-
if (!authConfig.issuerUrl) {
|
|
1791
|
-
errors.push("--auth-issuer-url is required when auth is enabled");
|
|
1792
|
-
} else {
|
|
1793
|
-
try {
|
|
1794
|
-
const url = new URL(authConfig.issuerUrl);
|
|
1795
|
-
if (url.protocol !== "https:") {
|
|
1796
|
-
errors.push("Issuer URL must use HTTPS protocol");
|
|
1797
|
-
}
|
|
1798
|
-
} catch {
|
|
1799
|
-
errors.push("Issuer URL must be a valid URL");
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
if (!authConfig.audience) {
|
|
1803
|
-
errors.push("--auth-audience is required when auth is enabled");
|
|
1804
|
-
} else {
|
|
1805
|
-
try {
|
|
1806
|
-
const url = new URL(authConfig.audience);
|
|
1807
|
-
if (url.protocol === "http:" && url.hostname !== "localhost") {
|
|
1808
|
-
logger.warn(
|
|
1809
|
-
"⚠️ Audience uses HTTP protocol - consider using HTTPS for production"
|
|
1810
|
-
);
|
|
1811
|
-
}
|
|
1812
|
-
if (url.hash) {
|
|
1813
|
-
errors.push("Audience must not contain URL fragments");
|
|
1814
|
-
}
|
|
1815
|
-
} catch {
|
|
1816
|
-
if (authConfig.audience.startsWith("urn:")) {
|
|
1817
|
-
const urnParts = authConfig.audience.split(":");
|
|
1818
|
-
if (urnParts.length < 3 || !urnParts[1] || !urnParts[2]) {
|
|
1819
|
-
errors.push("URN audience must follow format: urn:namespace:specific-string");
|
|
1820
|
-
}
|
|
1821
|
-
} else {
|
|
1822
|
-
errors.push(
|
|
1823
|
-
"Audience must be a valid absolute URL or URN (e.g., https://api.example.com or urn:company:service)"
|
|
1824
|
-
);
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
if (errors.length > 0) {
|
|
1829
|
-
throw new Error(`Auth configuration validation failed:
|
|
1830
|
-
${errors.join("\n")}`);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
function warnHttpUsage(authConfig, port) {
|
|
1834
|
-
if (!authConfig?.enabled) {
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
const isLocalhost = process.env.NODE_ENV !== "production" || port === 6280 || // default dev port
|
|
1838
|
-
process.env.HOSTNAME?.includes("localhost");
|
|
1839
|
-
if (!isLocalhost) {
|
|
1840
|
-
logger.warn(
|
|
1841
|
-
"⚠️ Authentication is enabled but running over HTTP in production. Consider using HTTPS for security."
|
|
1842
|
-
);
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
function resolveEmbeddingContext(embeddingModel) {
|
|
1846
|
-
try {
|
|
1847
|
-
let modelSpec = embeddingModel;
|
|
1848
|
-
if (!modelSpec && process.env.OPENAI_API_KEY) {
|
|
1849
|
-
modelSpec = "text-embedding-3-small";
|
|
1850
|
-
logger.debug(
|
|
1851
|
-
"Using default OpenAI embedding model due to OPENAI_API_KEY presence."
|
|
1852
|
-
);
|
|
1853
|
-
}
|
|
1854
|
-
if (!modelSpec) {
|
|
1855
|
-
logger.debug(
|
|
1856
|
-
"No embedding model specified and OPENAI_API_KEY not found. Embeddings are disabled."
|
|
1857
|
-
);
|
|
1858
|
-
return null;
|
|
1859
|
-
}
|
|
1860
|
-
logger.debug(`Resolving embedding configuration for model: ${modelSpec}`);
|
|
1861
|
-
return EmbeddingConfig.parseEmbeddingConfig(modelSpec);
|
|
1862
|
-
} catch (error) {
|
|
1863
|
-
logger.debug(`Failed to resolve embedding configuration: ${error}`);
|
|
1864
|
-
return null;
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
class RemoteEventProxy {
|
|
1868
|
-
constructor(remoteWorkerUrl, localEventBus) {
|
|
1869
|
-
this.remoteWorkerUrl = remoteWorkerUrl;
|
|
1870
|
-
this.localEventBus = localEventBus;
|
|
1871
|
-
}
|
|
1872
|
-
trpcClient = null;
|
|
1873
|
-
wsClient = null;
|
|
1874
|
-
subscription = null;
|
|
1875
|
-
isConnected = false;
|
|
1876
|
-
/**
|
|
1877
|
-
* Start subscribing to remote events and forwarding them locally.
|
|
1878
|
-
*/
|
|
1879
|
-
async connect() {
|
|
1880
|
-
if (this.isConnected) {
|
|
1881
|
-
logger.warn("Remote event proxy already connected");
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
logger.info(`📡 Connecting to remote worker at ${this.remoteWorkerUrl}`);
|
|
1885
|
-
try {
|
|
1886
|
-
const url = new URL(this.remoteWorkerUrl);
|
|
1887
|
-
const baseUrl = `${url.protocol}//${url.host}`;
|
|
1888
|
-
const wsUrl = baseUrl.replace(/^http/, "ws");
|
|
1889
|
-
this.wsClient = createWSClient({
|
|
1890
|
-
url: wsUrl
|
|
1891
|
-
});
|
|
1892
|
-
this.trpcClient = createTRPCClient({
|
|
1893
|
-
links: [
|
|
1894
|
-
splitLink({
|
|
1895
|
-
condition: (op) => op.type === "subscription",
|
|
1896
|
-
true: wsLink({ client: this.wsClient, transformer: superjson }),
|
|
1897
|
-
false: httpBatchLink({ url: this.remoteWorkerUrl, transformer: superjson })
|
|
1898
|
-
})
|
|
1899
|
-
]
|
|
1900
|
-
});
|
|
1901
|
-
this.subscription = this.trpcClient.events.subscribe.subscribe(
|
|
1902
|
-
{},
|
|
1903
|
-
// Subscribe to all event types
|
|
1904
|
-
{
|
|
1905
|
-
onData: (data) => {
|
|
1906
|
-
logger.debug(`📥 Received remote event: ${data.type}`);
|
|
1907
|
-
this.localEventBus.emit(data.type, data.payload);
|
|
1908
|
-
},
|
|
1909
|
-
onError: (error) => {
|
|
1910
|
-
logger.error(`❌ Remote event subscription error: ${error}`);
|
|
1911
|
-
this.isConnected = false;
|
|
1912
|
-
this.scheduleReconnect();
|
|
1913
|
-
},
|
|
1914
|
-
onStarted: () => {
|
|
1915
|
-
logger.info("✅ Remote event subscription started");
|
|
1916
|
-
this.isConnected = true;
|
|
1917
|
-
},
|
|
1918
|
-
onComplete: () => {
|
|
1919
|
-
logger.info("✅ Remote event subscription completed");
|
|
1920
|
-
this.isConnected = false;
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
);
|
|
1924
|
-
} catch (error) {
|
|
1925
|
-
logger.error(`❌ Failed to connect to remote worker: ${error}`);
|
|
1926
|
-
this.scheduleReconnect();
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
/**
|
|
1930
|
-
* Disconnect from the remote worker and stop forwarding events.
|
|
1931
|
-
*/
|
|
1932
|
-
disconnect() {
|
|
1933
|
-
if (this.subscription) {
|
|
1934
|
-
this.subscription.unsubscribe();
|
|
1935
|
-
this.subscription = null;
|
|
1936
|
-
}
|
|
1937
|
-
if (this.wsClient) {
|
|
1938
|
-
this.wsClient.close();
|
|
1939
|
-
this.wsClient = null;
|
|
1940
|
-
}
|
|
1941
|
-
this.isConnected = false;
|
|
1942
|
-
logger.info("🚫 Disconnected from remote worker");
|
|
1943
|
-
}
|
|
1944
|
-
/**
|
|
1945
|
-
* Check if the proxy is currently connected to the remote worker.
|
|
1946
|
-
*/
|
|
1947
|
-
isActive() {
|
|
1948
|
-
return this.isConnected;
|
|
1949
|
-
}
|
|
1950
|
-
/**
|
|
1951
|
-
* Schedule a reconnection attempt after a delay.
|
|
1952
|
-
*/
|
|
1953
|
-
scheduleReconnect() {
|
|
1954
|
-
logger.info("🔄 Scheduling reconnect to remote worker in 5 seconds...");
|
|
1955
|
-
setTimeout(() => {
|
|
1956
|
-
if (!this.isConnected) {
|
|
1957
|
-
this.connect();
|
|
1958
|
-
}
|
|
1959
|
-
}, 5e3);
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
class ToolError extends Error {
|
|
1963
|
-
constructor(message, toolName) {
|
|
1964
|
-
super(message);
|
|
1965
|
-
this.toolName = toolName;
|
|
1966
|
-
this.name = this.constructor.name;
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
class ValidationError extends ToolError {
|
|
1970
|
-
}
|
|
1971
|
-
const DEFAULT_MAX_PAGES = 1e3;
|
|
1972
|
-
const DEFAULT_MAX_DEPTH$1 = 3;
|
|
1973
|
-
const DEFAULT_MAX_CONCURRENCY = 3;
|
|
1974
|
-
const DEFAULT_PROTOCOL = "auto";
|
|
1975
|
-
const DEFAULT_HTTP_PORT = 6280;
|
|
1976
|
-
const DEFAULT_WEB_PORT = 6281;
|
|
1977
|
-
const DEFAULT_HOST = "127.0.0.1";
|
|
1978
|
-
const DEFAULT_PAGE_TIMEOUT = 5e3;
|
|
1979
|
-
const FETCHER_MAX_RETRIES = 6;
|
|
1980
|
-
const FETCHER_BASE_DELAY = 1e3;
|
|
1981
|
-
const FETCHER_MAX_CACHE_ITEMS = 200;
|
|
1982
|
-
const FETCHER_MAX_CACHE_ITEM_SIZE_BYTES = 500 * 1024;
|
|
1983
|
-
const SPLITTER_MIN_CHUNK_SIZE = 500;
|
|
1984
|
-
const SPLITTER_PREFERRED_CHUNK_SIZE = 1500;
|
|
1985
|
-
const SPLITTER_MAX_CHUNK_SIZE = 5e3;
|
|
1986
|
-
const EMBEDDING_BATCH_SIZE = 100;
|
|
1987
|
-
const EMBEDDING_BATCH_CHARS = 5e4;
|
|
1988
|
-
const MIGRATION_MAX_RETRIES = 5;
|
|
1989
|
-
const MIGRATION_RETRY_DELAY_MS = 300;
|
|
1990
|
-
const SEARCH_OVERFETCH_FACTOR = 2;
|
|
1991
|
-
const SEARCH_WEIGHT_VEC = 1;
|
|
1992
|
-
const SEARCH_WEIGHT_FTS = 1;
|
|
1993
|
-
const VECTOR_SEARCH_MULTIPLIER = 10;
|
|
1994
|
-
function createResponse(text) {
|
|
1995
|
-
return {
|
|
1996
|
-
content: [
|
|
1997
|
-
{
|
|
1998
|
-
type: "text",
|
|
1999
|
-
text
|
|
2000
|
-
}
|
|
2001
|
-
],
|
|
2002
|
-
isError: false
|
|
1485
|
+
content: [
|
|
1486
|
+
{
|
|
1487
|
+
type: "text",
|
|
1488
|
+
text
|
|
1489
|
+
}
|
|
1490
|
+
],
|
|
1491
|
+
isError: false
|
|
2003
1492
|
};
|
|
2004
1493
|
}
|
|
2005
1494
|
function createError(errorOrText) {
|
|
@@ -2090,7 +1579,7 @@ function createMcpServerInstance(tools, readOnly = false) {
|
|
|
2090
1579
|
"Re-scrape a previously indexed library version, updating only changed pages.",
|
|
2091
1580
|
{
|
|
2092
1581
|
library: z.string().trim().describe("Library name."),
|
|
2093
|
-
version: z.string().trim().optional().describe("Library version (optional, refreshes
|
|
1582
|
+
version: z.string().trim().optional().describe("Library version (optional, refreshes latest if omitted).")
|
|
2094
1583
|
},
|
|
2095
1584
|
{
|
|
2096
1585
|
title: "Refresh Library Version",
|
|
@@ -2344,7 +1833,7 @@ ${formattedJob}`);
|
|
|
2344
1833
|
"Remove indexed documentation for a library version. Use only if explicitly instructed.",
|
|
2345
1834
|
{
|
|
2346
1835
|
library: z.string().trim().describe("Library name."),
|
|
2347
|
-
version: z.string().trim().optional().describe("Library version (optional, removes
|
|
1836
|
+
version: z.string().trim().optional().describe("Library version (optional, removes latest if omitted).")
|
|
2348
1837
|
},
|
|
2349
1838
|
{
|
|
2350
1839
|
title: "Remove Library Documentation",
|
|
@@ -2902,7 +2391,7 @@ class BrowserFetcher {
|
|
|
2902
2391
|
}
|
|
2903
2392
|
logger.debug("Browser closed successfully");
|
|
2904
2393
|
} catch (error) {
|
|
2905
|
-
logger.warn(`⚠️
|
|
2394
|
+
logger.warn(`⚠️ Error closing browser: ${error}`);
|
|
2906
2395
|
}
|
|
2907
2396
|
}
|
|
2908
2397
|
}
|
|
@@ -7510,6 +6999,9 @@ Please verify the server URL includes the correct port (default 8080) and ends w
|
|
|
7510
6999
|
async storeScraperOptions(versionId, options) {
|
|
7511
7000
|
await this.client.storeScraperOptions.mutate({ versionId, options });
|
|
7512
7001
|
}
|
|
7002
|
+
getActiveEmbeddingConfig() {
|
|
7003
|
+
return null;
|
|
7004
|
+
}
|
|
7513
7005
|
}
|
|
7514
7006
|
class JsonPipeline extends BasePipeline {
|
|
7515
7007
|
middleware;
|
|
@@ -7688,6 +7180,24 @@ let PipelineFactory$1 = class PipelineFactory {
|
|
|
7688
7180
|
];
|
|
7689
7181
|
}
|
|
7690
7182
|
};
|
|
7183
|
+
function compareVersionsDescending(a, b) {
|
|
7184
|
+
const aIsUnversioned = a === "" || a === null || a === void 0;
|
|
7185
|
+
const bIsUnversioned = b === "" || b === null || b === void 0;
|
|
7186
|
+
if (aIsUnversioned && bIsUnversioned) return 0;
|
|
7187
|
+
if (aIsUnversioned) return -1;
|
|
7188
|
+
if (bIsUnversioned) return 1;
|
|
7189
|
+
const aSemver = semver__default.valid(a) ?? semver__default.valid(semver__default.coerce(a));
|
|
7190
|
+
const bSemver = semver__default.valid(b) ?? semver__default.valid(semver__default.coerce(b));
|
|
7191
|
+
if (aSemver && bSemver) {
|
|
7192
|
+
return semver__default.rcompare(aSemver, bSemver);
|
|
7193
|
+
}
|
|
7194
|
+
const aLower = a.toLowerCase();
|
|
7195
|
+
const bLower = b.toLowerCase();
|
|
7196
|
+
return bLower.localeCompare(aLower);
|
|
7197
|
+
}
|
|
7198
|
+
function sortVersionsDescending(versions) {
|
|
7199
|
+
return [...versions].sort(compareVersionsDescending);
|
|
7200
|
+
}
|
|
7691
7201
|
class HierarchicalAssemblyStrategy {
|
|
7692
7202
|
/**
|
|
7693
7203
|
* Determines if this strategy can handle the given content type.
|
|
@@ -8308,7 +7818,7 @@ async function applyMigrations(db) {
|
|
|
8308
7818
|
db.pragma("temp_store = MEMORY");
|
|
8309
7819
|
logger.debug("Applied performance optimizations for migration");
|
|
8310
7820
|
} catch (_error) {
|
|
8311
|
-
logger.warn("⚠️
|
|
7821
|
+
logger.warn("⚠️ Could not apply all performance optimizations for migration");
|
|
8312
7822
|
}
|
|
8313
7823
|
const overallTransaction = db.transaction(() => {
|
|
8314
7824
|
logger.debug("Checking database migrations...");
|
|
@@ -8361,7 +7871,7 @@ async function applyMigrations(db) {
|
|
|
8361
7871
|
db.exec("VACUUM");
|
|
8362
7872
|
logger.debug("Database vacuum completed successfully");
|
|
8363
7873
|
} catch (error) {
|
|
8364
|
-
logger.warn(`⚠️
|
|
7874
|
+
logger.warn(`⚠️ Could not vacuum database after migrations: ${error}`);
|
|
8365
7875
|
}
|
|
8366
7876
|
} else {
|
|
8367
7877
|
logger.debug("Skipping VACUUM - no migrations were applied");
|
|
@@ -8387,17 +7897,321 @@ async function applyMigrations(db) {
|
|
|
8387
7897
|
}
|
|
8388
7898
|
}
|
|
8389
7899
|
}
|
|
8390
|
-
try {
|
|
8391
|
-
db.pragma("journal_mode = WAL");
|
|
8392
|
-
db.pragma("wal_autocheckpoint = 1000");
|
|
8393
|
-
db.pragma("busy_timeout = 30000");
|
|
8394
|
-
db.pragma("foreign_keys = ON");
|
|
8395
|
-
db.pragma("synchronous = NORMAL");
|
|
8396
|
-
logger.debug(
|
|
8397
|
-
"Applied production database configuration (WAL mode, autocheckpoint, foreign keys, busy timeout)"
|
|
8398
|
-
);
|
|
8399
|
-
} catch (_error) {
|
|
8400
|
-
logger.warn("⚠️
|
|
7900
|
+
try {
|
|
7901
|
+
db.pragma("journal_mode = WAL");
|
|
7902
|
+
db.pragma("wal_autocheckpoint = 1000");
|
|
7903
|
+
db.pragma("busy_timeout = 30000");
|
|
7904
|
+
db.pragma("foreign_keys = ON");
|
|
7905
|
+
db.pragma("synchronous = NORMAL");
|
|
7906
|
+
logger.debug(
|
|
7907
|
+
"Applied production database configuration (WAL mode, autocheckpoint, foreign keys, busy timeout)"
|
|
7908
|
+
);
|
|
7909
|
+
} catch (_error) {
|
|
7910
|
+
logger.warn("⚠️ Could not apply all production database settings");
|
|
7911
|
+
}
|
|
7912
|
+
}
|
|
7913
|
+
class EmbeddingConfig {
|
|
7914
|
+
static instance = null;
|
|
7915
|
+
/**
|
|
7916
|
+
* Get the singleton instance of EmbeddingConfig.
|
|
7917
|
+
* Creates the instance if it doesn't exist.
|
|
7918
|
+
*/
|
|
7919
|
+
static getInstance() {
|
|
7920
|
+
if (EmbeddingConfig.instance === null) {
|
|
7921
|
+
EmbeddingConfig.instance = new EmbeddingConfig();
|
|
7922
|
+
}
|
|
7923
|
+
return EmbeddingConfig.instance;
|
|
7924
|
+
}
|
|
7925
|
+
/**
|
|
7926
|
+
* Reset the singleton instance (useful for testing).
|
|
7927
|
+
*/
|
|
7928
|
+
static resetInstance() {
|
|
7929
|
+
EmbeddingConfig.instance = null;
|
|
7930
|
+
}
|
|
7931
|
+
/**
|
|
7932
|
+
* Known dimensions for common embedding models.
|
|
7933
|
+
* This avoids expensive API calls for dimension detection in telemetry.
|
|
7934
|
+
*
|
|
7935
|
+
* Note: The "openai" provider also supports OpenAI-compatible APIs like:
|
|
7936
|
+
* - Ollama (local models)
|
|
7937
|
+
* - LMStudio (local models)
|
|
7938
|
+
* - Any service implementing OpenAI's embedding API
|
|
7939
|
+
*/
|
|
7940
|
+
knownModelDimensions = {
|
|
7941
|
+
// OpenAI models (also works with Ollama, LMStudio, and other OpenAI-compatible APIs)
|
|
7942
|
+
"text-embedding-3-small": 1536,
|
|
7943
|
+
"text-embedding-3-large": 3072,
|
|
7944
|
+
"text-embedding-ada-002": 1536,
|
|
7945
|
+
// Google Vertex AI models
|
|
7946
|
+
"text-embedding-004": 768,
|
|
7947
|
+
"textembedding-gecko@003": 768,
|
|
7948
|
+
"textembedding-gecko@002": 768,
|
|
7949
|
+
"textembedding-gecko@001": 768,
|
|
7950
|
+
// Google Gemini models (with MRL support)
|
|
7951
|
+
"text-embedding-preview-0409": 768,
|
|
7952
|
+
"embedding-001": 768,
|
|
7953
|
+
// AWS Bedrock models
|
|
7954
|
+
// Amazon Titan models
|
|
7955
|
+
"amazon.titan-embed-text-v1": 1536,
|
|
7956
|
+
"amazon.titan-embed-text-v2:0": 1024,
|
|
7957
|
+
"amazon.titan-embed-image-v1": 1024,
|
|
7958
|
+
// Image embedding model
|
|
7959
|
+
// Cohere models
|
|
7960
|
+
"cohere.embed-english-v3": 1024,
|
|
7961
|
+
"cohere.embed-multilingual-v3": 1024,
|
|
7962
|
+
// SageMaker models (hosted on AWS SageMaker)
|
|
7963
|
+
"intfloat/multilingual-e5-large": 1024,
|
|
7964
|
+
// Additional AWS models that might be supported
|
|
7965
|
+
// Note: Some of these might be placeholders - verify dimensions before use
|
|
7966
|
+
// "amazon.nova-embed-multilingual-v1:0": 4096, // Commented out as noted in source
|
|
7967
|
+
// MTEB Leaderboard models (source: https://huggingface.co/spaces/mteb/leaderboard)
|
|
7968
|
+
// Top performing models from Massive Text Embedding Benchmark
|
|
7969
|
+
"sentence-transformers/all-MiniLM-L6-v2": 384,
|
|
7970
|
+
"gemini-embedding-001": 3072,
|
|
7971
|
+
"Qwen/Qwen3-Embedding-8B": 4096,
|
|
7972
|
+
"Qwen/Qwen3-Embedding-4B": 2560,
|
|
7973
|
+
"Qwen/Qwen3-Embedding-0.6B": 1024,
|
|
7974
|
+
"Linq-AI-Research/Linq-Embed-Mistral": 4096,
|
|
7975
|
+
"Alibaba-NLP/gte-Qwen2-7B-instruct": 3584,
|
|
7976
|
+
"intfloat/multilingual-e5-large-instruct": 1024,
|
|
7977
|
+
"Salesforce/SFR-Embedding-Mistral": 4096,
|
|
7978
|
+
"text-multilingual-embedding-002": 768,
|
|
7979
|
+
"GritLM/GritLM-7B": 4096,
|
|
7980
|
+
"GritLM/GritLM-8x7B": 4096,
|
|
7981
|
+
"intfloat/e5-mistral-7b-instruct": 4096,
|
|
7982
|
+
"Cohere/Cohere-embed-multilingual-v3.0": 1024,
|
|
7983
|
+
"Alibaba-NLP/gte-Qwen2-1.5B-instruct": 8960,
|
|
7984
|
+
"Lajavaness/bilingual-embedding-large": 1024,
|
|
7985
|
+
"Salesforce/SFR-Embedding-2_R": 4096,
|
|
7986
|
+
"NovaSearch/stella_en_1.5B_v5": 8960,
|
|
7987
|
+
"NovaSearch/jasper_en_vision_language_v1": 8960,
|
|
7988
|
+
"nvidia/NV-Embed-v2": 4096,
|
|
7989
|
+
"OrdalieTech/Solon-embeddings-large-0.1": 1024,
|
|
7990
|
+
"BAAI/bge-m3": 1024,
|
|
7991
|
+
"HIT-TMG/KaLM-embedding-multilingual-mini-v1": 896,
|
|
7992
|
+
"jinaai/jina-embeddings-v3": 1024,
|
|
7993
|
+
"Alibaba-NLP/gte-multilingual-base": 768,
|
|
7994
|
+
"Lajavaness/bilingual-embedding-base": 768,
|
|
7995
|
+
"HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1": 896,
|
|
7996
|
+
"nvidia/NV-Embed-v1": 4096,
|
|
7997
|
+
"Cohere/Cohere-embed-multilingual-light-v3.0": 384,
|
|
7998
|
+
"manu/bge-m3-custom-fr": 1024,
|
|
7999
|
+
"Lajavaness/bilingual-embedding-small": 384,
|
|
8000
|
+
"Snowflake/snowflake-arctic-embed-l-v2.0": 1024,
|
|
8001
|
+
"intfloat/multilingual-e5-base": 768,
|
|
8002
|
+
"voyage-3-lite": 512,
|
|
8003
|
+
"voyage-3": 1024,
|
|
8004
|
+
"intfloat/multilingual-e5-small": 384,
|
|
8005
|
+
"Alibaba-NLP/gte-Qwen1.5-7B-instruct": 4096,
|
|
8006
|
+
"Snowflake/snowflake-arctic-embed-m-v2.0": 768,
|
|
8007
|
+
"deepvk/USER-bge-m3": 1024,
|
|
8008
|
+
"Cohere/Cohere-embed-english-v3.0": 1024,
|
|
8009
|
+
"Omartificial-Intelligence-Space/Arabic-labse-Matryoshka": 768,
|
|
8010
|
+
"ibm-granite/granite-embedding-278m-multilingual": 768,
|
|
8011
|
+
"NovaSearch/stella_en_400M_v5": 4096,
|
|
8012
|
+
"omarelshehy/arabic-english-sts-matryoshka": 1024,
|
|
8013
|
+
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2": 768,
|
|
8014
|
+
"Omartificial-Intelligence-Space/Arabic-all-nli-triplet-Matryoshka": 768,
|
|
8015
|
+
"Haon-Chen/speed-embedding-7b-instruct": 4096,
|
|
8016
|
+
"sentence-transformers/LaBSE": 768,
|
|
8017
|
+
"WhereIsAI/UAE-Large-V1": 1024,
|
|
8018
|
+
"ibm-granite/granite-embedding-107m-multilingual": 384,
|
|
8019
|
+
"mixedbread-ai/mxbai-embed-large-v1": 1024,
|
|
8020
|
+
"intfloat/e5-large-v2": 1024,
|
|
8021
|
+
"avsolatorio/GIST-large-Embedding-v0": 1024,
|
|
8022
|
+
"sdadas/mmlw-e5-large": 1024,
|
|
8023
|
+
"nomic-ai/nomic-embed-text-v1": 768,
|
|
8024
|
+
"nomic-ai/nomic-embed-text-v1-ablated": 768,
|
|
8025
|
+
"intfloat/e5-base-v2": 768,
|
|
8026
|
+
"BAAI/bge-large-en-v1.5": 1024,
|
|
8027
|
+
"intfloat/e5-large": 1024,
|
|
8028
|
+
"Omartificial-Intelligence-Space/Arabic-MiniLM-L12-v2-all-nli-triplet": 384,
|
|
8029
|
+
"Cohere/Cohere-embed-english-light-v3.0": 384,
|
|
8030
|
+
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2": 768,
|
|
8031
|
+
"Gameselo/STS-multilingual-mpnet-base-v2": 768,
|
|
8032
|
+
"thenlper/gte-large": 1024,
|
|
8033
|
+
"avsolatorio/GIST-Embedding-v0": 768,
|
|
8034
|
+
"nomic-ai/nomic-embed-text-v1-unsupervised": 768,
|
|
8035
|
+
"infgrad/stella-base-en-v2": 768,
|
|
8036
|
+
"avsolatorio/NoInstruct-small-Embedding-v0": 384,
|
|
8037
|
+
"dwzhu/e5-base-4k": 768,
|
|
8038
|
+
"sdadas/mmlw-e5-base": 768,
|
|
8039
|
+
"voyage-multilingual-2": 1024,
|
|
8040
|
+
"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-supervised": 4096,
|
|
8041
|
+
"BAAI/bge-base-en-v1.5": 768,
|
|
8042
|
+
"avsolatorio/GIST-small-Embedding-v0": 384,
|
|
8043
|
+
"sdadas/mmlw-roberta-large": 1024,
|
|
8044
|
+
"nomic-ai/nomic-embed-text-v1.5": 768,
|
|
8045
|
+
"minishlab/potion-multilingual-128M": 256,
|
|
8046
|
+
"shibing624/text2vec-base-multilingual": 384,
|
|
8047
|
+
"thenlper/gte-base": 768,
|
|
8048
|
+
"intfloat/e5-small-v2": 384,
|
|
8049
|
+
"intfloat/e5-base": 768,
|
|
8050
|
+
"sentence-transformers/static-similarity-mrl-multilingual-v1": 1024,
|
|
8051
|
+
"manu/sentence_croissant_alpha_v0.3": 2048,
|
|
8052
|
+
"BAAI/bge-small-en-v1.5": 512,
|
|
8053
|
+
"thenlper/gte-small": 384,
|
|
8054
|
+
"sdadas/mmlw-e5-small": 384,
|
|
8055
|
+
"manu/sentence_croissant_alpha_v0.4": 2048,
|
|
8056
|
+
"manu/sentence_croissant_alpha_v0.2": 2048,
|
|
8057
|
+
"abhinand/MedEmbed-small-v0.1": 384,
|
|
8058
|
+
"ibm-granite/granite-embedding-125m-english": 768,
|
|
8059
|
+
"intfloat/e5-small": 384,
|
|
8060
|
+
"voyage-large-2-instruct": 1024,
|
|
8061
|
+
"sdadas/mmlw-roberta-base": 768,
|
|
8062
|
+
"Snowflake/snowflake-arctic-embed-l": 1024,
|
|
8063
|
+
"Mihaiii/Ivysaur": 384,
|
|
8064
|
+
"Snowflake/snowflake-arctic-embed-m-long": 768,
|
|
8065
|
+
"bigscience/sgpt-bloom-7b1-msmarco": 4096,
|
|
8066
|
+
"avsolatorio/GIST-all-MiniLM-L6-v2": 384,
|
|
8067
|
+
"sergeyzh/LaBSE-ru-turbo": 768,
|
|
8068
|
+
"sentence-transformers/all-mpnet-base-v2": 768,
|
|
8069
|
+
"Snowflake/snowflake-arctic-embed-m": 768,
|
|
8070
|
+
"Snowflake/snowflake-arctic-embed-s": 384,
|
|
8071
|
+
"sentence-transformers/all-MiniLM-L12-v2": 384,
|
|
8072
|
+
"Mihaiii/gte-micro-v4": 384,
|
|
8073
|
+
"Snowflake/snowflake-arctic-embed-m-v1.5": 768,
|
|
8074
|
+
"cointegrated/LaBSE-en-ru": 768,
|
|
8075
|
+
"Mihaiii/Bulbasaur": 384,
|
|
8076
|
+
"ibm-granite/granite-embedding-30m-english": 384,
|
|
8077
|
+
"deepfile/embedder-100p": 768,
|
|
8078
|
+
"Jaume/gemma-2b-embeddings": 2048,
|
|
8079
|
+
"OrlikB/KartonBERT-USE-base-v1": 768,
|
|
8080
|
+
"izhx/udever-bloom-7b1": 4096,
|
|
8081
|
+
"izhx/udever-bloom-1b1": 1024,
|
|
8082
|
+
"brahmairesearch/slx-v0.1": 384,
|
|
8083
|
+
"Mihaiii/Wartortle": 384,
|
|
8084
|
+
"izhx/udever-bloom-3b": 2048,
|
|
8085
|
+
"deepvk/USER-base": 768,
|
|
8086
|
+
"ai-forever/ru-en-RoSBERTa": 1024,
|
|
8087
|
+
"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-unsup-simcse": 4096,
|
|
8088
|
+
"Mihaiii/Venusaur": 384,
|
|
8089
|
+
"Snowflake/snowflake-arctic-embed-xs": 384,
|
|
8090
|
+
"jinaai/jina-embedding-b-en-v1": 768,
|
|
8091
|
+
"Mihaiii/gte-micro": 384,
|
|
8092
|
+
"aari1995/German_Semantic_STS_V2": 1024,
|
|
8093
|
+
"Mihaiii/Squirtle": 384,
|
|
8094
|
+
"OrlikB/st-polish-kartonberta-base-alpha-v1": 768,
|
|
8095
|
+
"sergeyzh/rubert-tiny-turbo": 312,
|
|
8096
|
+
"minishlab/potion-base-8M": 256,
|
|
8097
|
+
"minishlab/M2V_base_glove_subword": 256,
|
|
8098
|
+
"jinaai/jina-embedding-s-en-v1": 512,
|
|
8099
|
+
"minishlab/potion-base-4M": 128,
|
|
8100
|
+
"minishlab/M2V_base_output": 256,
|
|
8101
|
+
"DeepPavlov/rubert-base-cased-sentence": 768,
|
|
8102
|
+
"jinaai/jina-embeddings-v2-small-en": 512,
|
|
8103
|
+
"cointegrated/rubert-tiny2": 312,
|
|
8104
|
+
"minishlab/M2V_base_glove": 256,
|
|
8105
|
+
"cointegrated/rubert-tiny": 312,
|
|
8106
|
+
"silma-ai/silma-embeddding-matryoshka-v0.1": 768,
|
|
8107
|
+
"DeepPavlov/rubert-base-cased": 768,
|
|
8108
|
+
"Omartificial-Intelligence-Space/Arabic-mpnet-base-all-nli-triplet": 768,
|
|
8109
|
+
"izhx/udever-bloom-560m": 1024,
|
|
8110
|
+
"minishlab/potion-base-2M": 64,
|
|
8111
|
+
"DeepPavlov/distilrubert-small-cased-conversational": 768,
|
|
8112
|
+
"consciousAI/cai-lunaris-text-embeddings": 1024,
|
|
8113
|
+
"deepvk/deberta-v1-base": 768,
|
|
8114
|
+
"Omartificial-Intelligence-Space/Arabert-all-nli-triplet-Matryoshka": 768,
|
|
8115
|
+
"Omartificial-Intelligence-Space/Marbert-all-nli-triplet-Matryoshka": 768,
|
|
8116
|
+
"ai-forever/sbert_large_mt_nlu_ru": 1024,
|
|
8117
|
+
"ai-forever/sbert_large_nlu_ru": 1024,
|
|
8118
|
+
"malenia1/ternary-weight-embedding": 1024,
|
|
8119
|
+
"jinaai/jina-embeddings-v2-base-en": 768,
|
|
8120
|
+
"VPLabs/SearchMap_Preview": 4096,
|
|
8121
|
+
"Hum-Works/lodestone-base-4096-v1": 768,
|
|
8122
|
+
"jinaai/jina-embeddings-v4": 2048
|
|
8123
|
+
};
|
|
8124
|
+
/**
|
|
8125
|
+
* Lowercase lookup map for case-insensitive model dimension queries.
|
|
8126
|
+
* Built lazily from knownModelDimensions to ensure consistency.
|
|
8127
|
+
*/
|
|
8128
|
+
modelLookup;
|
|
8129
|
+
constructor() {
|
|
8130
|
+
this.modelLookup = /* @__PURE__ */ new Map();
|
|
8131
|
+
for (const [model, dimensions] of Object.entries(this.knownModelDimensions)) {
|
|
8132
|
+
this.modelLookup.set(model.toLowerCase(), dimensions);
|
|
8133
|
+
}
|
|
8134
|
+
}
|
|
8135
|
+
/**
|
|
8136
|
+
* Parse embedding model configuration from a provided model specification.
|
|
8137
|
+
* This is a synchronous operation that extracts provider, model, and known dimensions.
|
|
8138
|
+
*
|
|
8139
|
+
* Supports various providers:
|
|
8140
|
+
* - openai: OpenAI models and OpenAI-compatible APIs (Ollama, LMStudio, etc.)
|
|
8141
|
+
* - vertex: Google Cloud Vertex AI
|
|
8142
|
+
* - gemini: Google Generative AI
|
|
8143
|
+
* - aws: AWS Bedrock models
|
|
8144
|
+
* - microsoft: Azure OpenAI
|
|
8145
|
+
* - sagemaker: AWS SageMaker hosted models
|
|
8146
|
+
*
|
|
8147
|
+
* @param modelSpec Model specification (e.g., "openai:text-embedding-3-small"), defaults to "text-embedding-3-small"
|
|
8148
|
+
* @returns Parsed embedding model configuration
|
|
8149
|
+
*/
|
|
8150
|
+
parse(modelSpec) {
|
|
8151
|
+
const spec = modelSpec || "text-embedding-3-small";
|
|
8152
|
+
const colonIndex = spec.indexOf(":");
|
|
8153
|
+
let provider;
|
|
8154
|
+
let model;
|
|
8155
|
+
if (colonIndex === -1) {
|
|
8156
|
+
provider = "openai";
|
|
8157
|
+
model = spec;
|
|
8158
|
+
} else {
|
|
8159
|
+
provider = spec.substring(0, colonIndex);
|
|
8160
|
+
model = spec.substring(colonIndex + 1);
|
|
8161
|
+
}
|
|
8162
|
+
const dimensions = this.modelLookup?.get(model.toLowerCase()) || null;
|
|
8163
|
+
return {
|
|
8164
|
+
provider,
|
|
8165
|
+
model,
|
|
8166
|
+
dimensions,
|
|
8167
|
+
modelSpec: spec
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
/**
|
|
8171
|
+
* Get the known dimensions for a specific model.
|
|
8172
|
+
* Returns null if the model dimensions are not known.
|
|
8173
|
+
* Uses case-insensitive lookup.
|
|
8174
|
+
*
|
|
8175
|
+
* @param model The model name (e.g., "text-embedding-3-small")
|
|
8176
|
+
* @returns Known dimensions or null
|
|
8177
|
+
*/
|
|
8178
|
+
getKnownDimensions(model) {
|
|
8179
|
+
return this.modelLookup?.get(model.toLowerCase()) || null;
|
|
8180
|
+
}
|
|
8181
|
+
/**
|
|
8182
|
+
* Add or update known dimensions for a model.
|
|
8183
|
+
* This can be used to cache discovered dimensions.
|
|
8184
|
+
* Stores both original case and lowercase for consistent lookup.
|
|
8185
|
+
*
|
|
8186
|
+
* @param model The model name
|
|
8187
|
+
* @param dimensions The dimensions to cache
|
|
8188
|
+
*/
|
|
8189
|
+
setKnownDimensions(model, dimensions) {
|
|
8190
|
+
this.knownModelDimensions[model] = dimensions;
|
|
8191
|
+
if (this.modelLookup) {
|
|
8192
|
+
this.modelLookup.set(model.toLowerCase(), dimensions);
|
|
8193
|
+
}
|
|
8194
|
+
}
|
|
8195
|
+
/**
|
|
8196
|
+
* Static method to parse embedding model configuration using the singleton instance.
|
|
8197
|
+
* This maintains backward compatibility while using the class-based approach.
|
|
8198
|
+
*/
|
|
8199
|
+
static parseEmbeddingConfig(modelSpec) {
|
|
8200
|
+
return EmbeddingConfig.getInstance().parse(modelSpec);
|
|
8201
|
+
}
|
|
8202
|
+
/**
|
|
8203
|
+
* Static method to get known model dimensions using the singleton instance.
|
|
8204
|
+
* This maintains backward compatibility while using the class-based approach.
|
|
8205
|
+
*/
|
|
8206
|
+
static getKnownModelDimensions(model) {
|
|
8207
|
+
return EmbeddingConfig.getInstance().getKnownDimensions(model);
|
|
8208
|
+
}
|
|
8209
|
+
/**
|
|
8210
|
+
* Static method to set known model dimensions using the singleton instance.
|
|
8211
|
+
* This maintains backward compatibility while using the class-based approach.
|
|
8212
|
+
*/
|
|
8213
|
+
static setKnownModelDimensions(model, dimensions) {
|
|
8214
|
+
EmbeddingConfig.getInstance().setKnownDimensions(model, dimensions);
|
|
8401
8215
|
}
|
|
8402
8216
|
}
|
|
8403
8217
|
class DocumentStore {
|
|
@@ -8407,6 +8221,16 @@ class DocumentStore {
|
|
|
8407
8221
|
modelDimension;
|
|
8408
8222
|
embeddingConfig;
|
|
8409
8223
|
isVectorSearchEnabled = false;
|
|
8224
|
+
/**
|
|
8225
|
+
* Returns the active embedding configuration if vector search is enabled,
|
|
8226
|
+
* or null if embeddings are disabled (no config provided or credentials unavailable).
|
|
8227
|
+
*/
|
|
8228
|
+
getActiveEmbeddingConfig() {
|
|
8229
|
+
if (!this.isVectorSearchEnabled || !this.embeddingConfig) {
|
|
8230
|
+
return null;
|
|
8231
|
+
}
|
|
8232
|
+
return this.embeddingConfig;
|
|
8233
|
+
}
|
|
8410
8234
|
statements;
|
|
8411
8235
|
/**
|
|
8412
8236
|
* Calculates Reciprocal Rank Fusion score for a result with configurable weights
|
|
@@ -8686,7 +8510,7 @@ class DocumentStore {
|
|
|
8686
8510
|
const config = this.embeddingConfig;
|
|
8687
8511
|
if (!areCredentialsAvailable(config.provider)) {
|
|
8688
8512
|
logger.warn(
|
|
8689
|
-
`⚠️
|
|
8513
|
+
`⚠️ No credentials found for ${config.provider} embedding provider. Vector search is disabled.
|
|
8690
8514
|
Only full-text search will be available. To enable vector search, please configure the required
|
|
8691
8515
|
environment variables for ${config.provider} or choose a different provider.
|
|
8692
8516
|
See README.md for configuration options or run with --help for more details.`
|
|
@@ -8698,8 +8522,26 @@ class DocumentStore {
|
|
|
8698
8522
|
if (config.dimensions !== null) {
|
|
8699
8523
|
this.modelDimension = config.dimensions;
|
|
8700
8524
|
} else {
|
|
8701
|
-
const
|
|
8702
|
-
|
|
8525
|
+
const EMBEDDING_INIT_TIMEOUT_MS = 3e4;
|
|
8526
|
+
const testPromise = this.embeddings.embedQuery("test");
|
|
8527
|
+
let timeoutId;
|
|
8528
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
8529
|
+
timeoutId = setTimeout(() => {
|
|
8530
|
+
reject(
|
|
8531
|
+
new Error(
|
|
8532
|
+
`Embedding service connection timed out after ${EMBEDDING_INIT_TIMEOUT_MS / 1e3} seconds`
|
|
8533
|
+
)
|
|
8534
|
+
);
|
|
8535
|
+
}, EMBEDDING_INIT_TIMEOUT_MS);
|
|
8536
|
+
});
|
|
8537
|
+
try {
|
|
8538
|
+
const testVector = await Promise.race([testPromise, timeoutPromise]);
|
|
8539
|
+
this.modelDimension = testVector.length;
|
|
8540
|
+
} finally {
|
|
8541
|
+
if (timeoutId !== void 0) {
|
|
8542
|
+
clearTimeout(timeoutId);
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8703
8545
|
EmbeddingConfig.setKnownModelDimensions(config.model, this.modelDimension);
|
|
8704
8546
|
}
|
|
8705
8547
|
if (this.modelDimension > this.dbDimension) {
|
|
@@ -8713,18 +8555,26 @@ class DocumentStore {
|
|
|
8713
8555
|
if (error instanceof Error) {
|
|
8714
8556
|
if (error.message.includes("does not exist") || error.message.includes("MODEL_NOT_FOUND")) {
|
|
8715
8557
|
throw new ModelConfigurationError(
|
|
8716
|
-
|
|
8558
|
+
`Invalid embedding model: ${config.model}
|
|
8717
8559
|
The model "${config.model}" is not available or you don't have access to it.
|
|
8718
8560
|
See README.md for supported models or run with --help for more details.`
|
|
8719
8561
|
);
|
|
8720
8562
|
}
|
|
8721
8563
|
if (error.message.includes("API key") || error.message.includes("401") || error.message.includes("authentication")) {
|
|
8722
8564
|
throw new ModelConfigurationError(
|
|
8723
|
-
|
|
8565
|
+
`Authentication failed for ${config.provider} embedding provider
|
|
8724
8566
|
Please check your API key configuration.
|
|
8725
8567
|
See README.md for configuration options or run with --help for more details.`
|
|
8726
8568
|
);
|
|
8727
8569
|
}
|
|
8570
|
+
if (error.message.includes("timed out") || error.message.includes("ECONNREFUSED") || error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT") || error.message.includes("ECONNRESET") || error.message.includes("network") || error.message.includes("fetch failed")) {
|
|
8571
|
+
throw new ModelConfigurationError(
|
|
8572
|
+
`Failed to connect to ${config.provider} embedding service
|
|
8573
|
+
${error.message}
|
|
8574
|
+
Please check that the embedding service is running and accessible.
|
|
8575
|
+
If using a local model (e.g., Ollama), ensure the service is started.`
|
|
8576
|
+
);
|
|
8577
|
+
}
|
|
8728
8578
|
}
|
|
8729
8579
|
throw error;
|
|
8730
8580
|
}
|
|
@@ -8988,7 +8838,7 @@ class DocumentStore {
|
|
|
8988
8838
|
try {
|
|
8989
8839
|
parsed = JSON.parse(row.scraper_options);
|
|
8990
8840
|
} catch (e) {
|
|
8991
|
-
logger.warn(`⚠️
|
|
8841
|
+
logger.warn(`⚠️ Invalid scraper_options JSON for version ${versionId}: ${e}`);
|
|
8992
8842
|
parsed = {};
|
|
8993
8843
|
}
|
|
8994
8844
|
}
|
|
@@ -9055,22 +8905,7 @@ class DocumentStore {
|
|
|
9055
8905
|
});
|
|
9056
8906
|
}
|
|
9057
8907
|
for (const versions of libraryMap.values()) {
|
|
9058
|
-
versions.sort((a, b) =>
|
|
9059
|
-
if (a.version === "" && b.version !== "") {
|
|
9060
|
-
return -1;
|
|
9061
|
-
}
|
|
9062
|
-
if (a.version !== "" && b.version === "") {
|
|
9063
|
-
return 1;
|
|
9064
|
-
}
|
|
9065
|
-
if (a.version === "" && b.version === "") {
|
|
9066
|
-
return 0;
|
|
9067
|
-
}
|
|
9068
|
-
try {
|
|
9069
|
-
return semver__default.compare(a.version, b.version);
|
|
9070
|
-
} catch (_error) {
|
|
9071
|
-
return a.version.localeCompare(b.version);
|
|
9072
|
-
}
|
|
9073
|
-
});
|
|
8908
|
+
versions.sort((a, b) => compareVersionsDescending(a.version, b.version));
|
|
9074
8909
|
}
|
|
9075
8910
|
return libraryMap;
|
|
9076
8911
|
} catch (error) {
|
|
@@ -9707,13 +9542,6 @@ class DocumentManagementService {
|
|
|
9707
9542
|
documentRetriever;
|
|
9708
9543
|
pipelines;
|
|
9709
9544
|
eventBus;
|
|
9710
|
-
/**
|
|
9711
|
-
* Normalizes a version string, converting null or undefined to an empty string
|
|
9712
|
-
* and converting to lowercase.
|
|
9713
|
-
*/
|
|
9714
|
-
normalizeVersion(version) {
|
|
9715
|
-
return (version ?? "").toLowerCase();
|
|
9716
|
-
}
|
|
9717
9545
|
constructor(storePath, eventBus, embeddingConfig, pipelineConfig) {
|
|
9718
9546
|
this.eventBus = eventBus;
|
|
9719
9547
|
const dbPath = storePath === ":memory:" ? ":memory:" : path.join(storePath, "documents.db");
|
|
@@ -9722,6 +9550,20 @@ class DocumentManagementService {
|
|
|
9722
9550
|
this.documentRetriever = new DocumentRetrieverService(this.store);
|
|
9723
9551
|
this.pipelines = PipelineFactory$1.createStandardPipelines(pipelineConfig);
|
|
9724
9552
|
}
|
|
9553
|
+
/**
|
|
9554
|
+
* Returns the active embedding configuration if vector search is enabled,
|
|
9555
|
+
* or null if embeddings are disabled.
|
|
9556
|
+
*/
|
|
9557
|
+
getActiveEmbeddingConfig() {
|
|
9558
|
+
return this.store.getActiveEmbeddingConfig();
|
|
9559
|
+
}
|
|
9560
|
+
/**
|
|
9561
|
+
* Normalizes a version string, converting null or undefined to an empty string
|
|
9562
|
+
* and converting to lowercase.
|
|
9563
|
+
*/
|
|
9564
|
+
normalizeVersion(version) {
|
|
9565
|
+
return (version ?? "").toLowerCase();
|
|
9566
|
+
}
|
|
9725
9567
|
/**
|
|
9726
9568
|
* Initializes the underlying document store.
|
|
9727
9569
|
*/
|
|
@@ -9841,10 +9683,12 @@ class DocumentManagementService {
|
|
|
9841
9683
|
}
|
|
9842
9684
|
/**
|
|
9843
9685
|
* Returns a list of all available semantic versions for a library.
|
|
9686
|
+
* Sorted in descending order (latest first).
|
|
9844
9687
|
*/
|
|
9845
9688
|
async listVersions(library) {
|
|
9846
9689
|
const versions = await this.store.queryUniqueVersions(library);
|
|
9847
|
-
|
|
9690
|
+
const validVersions = versions.filter((v) => semver__default.valid(v));
|
|
9691
|
+
return sortVersionsDescending(validVersions);
|
|
9848
9692
|
}
|
|
9849
9693
|
/**
|
|
9850
9694
|
* Checks if documents exist for a given library and optional version.
|
|
@@ -9922,7 +9766,7 @@ class DocumentManagementService {
|
|
|
9922
9766
|
async removeAllDocuments(library, version) {
|
|
9923
9767
|
const normalizedVersion = this.normalizeVersion(version);
|
|
9924
9768
|
logger.info(
|
|
9925
|
-
`🗑️ Removing all documents from ${library}@${normalizedVersion || "
|
|
9769
|
+
`🗑️ Removing all documents from ${library}@${normalizedVersion || "latest"} store`
|
|
9926
9770
|
);
|
|
9927
9771
|
const count = await this.store.deletePages(library, normalizedVersion);
|
|
9928
9772
|
logger.info(`🗑️ Deleted ${count} documents`);
|
|
@@ -9953,17 +9797,15 @@ class DocumentManagementService {
|
|
|
9953
9797
|
*/
|
|
9954
9798
|
async removeVersion(library, version) {
|
|
9955
9799
|
const normalizedVersion = this.normalizeVersion(version);
|
|
9956
|
-
logger.debug(`Removing version: ${library}@${normalizedVersion || "
|
|
9800
|
+
logger.debug(`Removing version: ${library}@${normalizedVersion || "latest"}`);
|
|
9957
9801
|
const result = await this.store.removeVersion(library, normalizedVersion, true);
|
|
9958
9802
|
logger.info(`🗑️ Removed ${result.documentsDeleted} documents`);
|
|
9959
9803
|
if (result.versionDeleted && result.libraryDeleted) {
|
|
9960
9804
|
logger.info(`🗑️ Completely removed library ${library} (was last version)`);
|
|
9961
9805
|
} else if (result.versionDeleted) {
|
|
9962
|
-
logger.info(`🗑️ Removed version ${library}@${normalizedVersion || "
|
|
9806
|
+
logger.info(`🗑️ Removed version ${library}@${normalizedVersion || "latest"}`);
|
|
9963
9807
|
} else {
|
|
9964
|
-
logger.warn(
|
|
9965
|
-
`⚠️ Version ${library}@${normalizedVersion || "[no version]"} not found`
|
|
9966
|
-
);
|
|
9808
|
+
logger.warn(`⚠️ Version ${library}@${normalizedVersion || "latest"} not found`);
|
|
9967
9809
|
const libraryRecord = await this.store.getLibrary(library);
|
|
9968
9810
|
if (libraryRecord) {
|
|
9969
9811
|
const versions = await this.store.queryUniqueVersions(library);
|
|
@@ -10650,7 +10492,7 @@ function registerEventsRoute(server, eventBus) {
|
|
|
10650
10492
|
// Disable buffering in nginx
|
|
10651
10493
|
});
|
|
10652
10494
|
reply.raw.write("data: connected\n\n");
|
|
10653
|
-
logger.
|
|
10495
|
+
logger.debug("SSE client connected");
|
|
10654
10496
|
const allEventTypes = [
|
|
10655
10497
|
EventType.JOB_STATUS_CHANGE,
|
|
10656
10498
|
EventType.JOB_PROGRESS,
|
|
@@ -10688,17 +10530,40 @@ function registerEventsRoute(server, eventBus) {
|
|
|
10688
10530
|
}
|
|
10689
10531
|
}, 3e4);
|
|
10690
10532
|
request.raw.on("close", () => {
|
|
10691
|
-
logger.
|
|
10533
|
+
logger.debug("SSE client disconnected");
|
|
10692
10534
|
cleanup();
|
|
10693
10535
|
clearInterval(heartbeatInterval);
|
|
10694
10536
|
});
|
|
10695
10537
|
request.raw.on("error", (error) => {
|
|
10696
|
-
logger.
|
|
10538
|
+
logger.debug(`SSE connection error: ${error}`);
|
|
10697
10539
|
cleanup();
|
|
10698
10540
|
clearInterval(heartbeatInterval);
|
|
10699
10541
|
});
|
|
10700
10542
|
});
|
|
10701
10543
|
}
|
|
10544
|
+
const PrimaryButton = ({
|
|
10545
|
+
children,
|
|
10546
|
+
type = "button",
|
|
10547
|
+
class: className = "",
|
|
10548
|
+
disabled = false,
|
|
10549
|
+
...rest
|
|
10550
|
+
}) => {
|
|
10551
|
+
const baseClasses = "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 transition-colors duration-150";
|
|
10552
|
+
const disabledClasses = disabled ? "opacity-50 cursor-not-allowed" : "";
|
|
10553
|
+
const combinedClasses = `${baseClasses} ${disabledClasses} ${className}`.trim();
|
|
10554
|
+
return /* @__PURE__ */ jsx("button", { type, class: combinedClasses, disabled, ...rest, children });
|
|
10555
|
+
};
|
|
10556
|
+
const AddJobButton = () => {
|
|
10557
|
+
return /* @__PURE__ */ jsx(
|
|
10558
|
+
PrimaryButton,
|
|
10559
|
+
{
|
|
10560
|
+
"hx-get": "/web/jobs/new",
|
|
10561
|
+
"hx-target": "#addJobForm",
|
|
10562
|
+
"hx-swap": "innerHTML",
|
|
10563
|
+
children: "Add New Documentation"
|
|
10564
|
+
}
|
|
10565
|
+
);
|
|
10566
|
+
};
|
|
10702
10567
|
const Toast = () => {
|
|
10703
10568
|
return /* @__PURE__ */ jsx(
|
|
10704
10569
|
"div",
|
|
@@ -10827,7 +10692,7 @@ const Layout = ({
|
|
|
10827
10692
|
children,
|
|
10828
10693
|
eventClientConfig
|
|
10829
10694
|
}) => {
|
|
10830
|
-
const versionString = version || "1.
|
|
10695
|
+
const versionString = version || "1.31.0";
|
|
10831
10696
|
const versionInitializer = `versionUpdate({ currentVersion: ${`'${versionString}'`} })`;
|
|
10832
10697
|
return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
|
|
10833
10698
|
/* @__PURE__ */ jsxs("head", { children: [
|
|
@@ -10972,7 +10837,7 @@ const Layout = ({
|
|
|
10972
10837
|
form .spinner { display: none; }
|
|
10973
10838
|
` })
|
|
10974
10839
|
] }),
|
|
10975
|
-
/* @__PURE__ */ jsxs("body", { class: "bg-gray-50 dark:bg-gray-900", children: [
|
|
10840
|
+
/* @__PURE__ */ jsxs("body", { class: "bg-gray-50 dark:bg-gray-900", "hx-ext": "morph", children: [
|
|
10976
10841
|
/* @__PURE__ */ jsx(Toast, {}),
|
|
10977
10842
|
/* @__PURE__ */ jsx(
|
|
10978
10843
|
"header",
|
|
@@ -11127,19 +10992,35 @@ function registerIndexRoute(server, config) {
|
|
|
11127
10992
|
trpcUrl
|
|
11128
10993
|
},
|
|
11129
10994
|
children: [
|
|
10995
|
+
/* @__PURE__ */ jsx(
|
|
10996
|
+
"div",
|
|
10997
|
+
{
|
|
10998
|
+
id: "analytics-stats",
|
|
10999
|
+
"hx-get": "/web/stats",
|
|
11000
|
+
"hx-trigger": "load, library-change from:body",
|
|
11001
|
+
"hx-swap": "morph:innerHTML",
|
|
11002
|
+
children: /* @__PURE__ */ jsxs("div", { class: "grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4 animate-pulse", children: [
|
|
11003
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20" }),
|
|
11004
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20" }),
|
|
11005
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20" })
|
|
11006
|
+
] })
|
|
11007
|
+
}
|
|
11008
|
+
),
|
|
11130
11009
|
/* @__PURE__ */ jsxs("section", { class: "mb-4 p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600", children: [
|
|
11131
11010
|
/* @__PURE__ */ jsxs("div", { class: "flex items-center justify-between mb-2", children: [
|
|
11132
11011
|
/* @__PURE__ */ jsx("h2", { class: "text-xl font-semibold text-gray-900 dark:text-white", children: "Job Queue" }),
|
|
11133
11012
|
/* @__PURE__ */ jsx(
|
|
11134
11013
|
"button",
|
|
11135
11014
|
{
|
|
11015
|
+
id: "clear-completed-btn",
|
|
11136
11016
|
type: "button",
|
|
11137
|
-
class: "text-xs px-3 py-1.5 text-gray-
|
|
11017
|
+
class: "text-xs px-3 py-1.5 text-gray-400 bg-gray-50 border border-gray-200 rounded-lg cursor-not-allowed focus:ring-4 focus:outline-none transition-colors duration-150 dark:bg-gray-700 dark:text-gray-500 dark:border-gray-600",
|
|
11138
11018
|
title: "Clear all completed, cancelled, and failed jobs",
|
|
11139
11019
|
"hx-post": "/web/jobs/clear-completed",
|
|
11140
11020
|
"hx-trigger": "click",
|
|
11141
11021
|
"hx-on": "htmx:afterRequest: document.dispatchEvent(new Event('job-list-refresh'))",
|
|
11142
11022
|
"hx-swap": "none",
|
|
11023
|
+
disabled: true,
|
|
11143
11024
|
children: "Clear Completed Jobs"
|
|
11144
11025
|
}
|
|
11145
11026
|
)
|
|
@@ -11149,7 +11030,8 @@ function registerIndexRoute(server, config) {
|
|
|
11149
11030
|
{
|
|
11150
11031
|
id: "job-queue",
|
|
11151
11032
|
"hx-get": "/web/jobs",
|
|
11152
|
-
"hx-trigger": "load, job-status-change from:body, job-progress from:body, job-list-change from:body",
|
|
11033
|
+
"hx-trigger": "load, job-status-change from:body, job-progress from:body, job-list-change from:body, job-list-refresh from:body",
|
|
11034
|
+
"hx-swap": "morph:innerHTML",
|
|
11153
11035
|
children: /* @__PURE__ */ jsxs("div", { class: "animate-pulse", children: [
|
|
11154
11036
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4" }),
|
|
11155
11037
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5" }),
|
|
@@ -11158,11 +11040,7 @@ function registerIndexRoute(server, config) {
|
|
|
11158
11040
|
}
|
|
11159
11041
|
)
|
|
11160
11042
|
] }),
|
|
11161
|
-
/* @__PURE__ */ jsx("section", { class: "mb-8", children: /* @__PURE__ */ jsx("div", { id: "addJobForm",
|
|
11162
|
-
/* @__PURE__ */ jsx("div", { class: "h-6 bg-gray-200 rounded-full dark:bg-gray-700 w-1/3 mb-4" }),
|
|
11163
|
-
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5" }),
|
|
11164
|
-
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5" })
|
|
11165
|
-
] }) }) }),
|
|
11043
|
+
/* @__PURE__ */ jsx("section", { class: "mb-8", children: /* @__PURE__ */ jsx("div", { id: "addJobForm", children: /* @__PURE__ */ jsx(AddJobButton, {}) }) }),
|
|
11166
11044
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
11167
11045
|
/* @__PURE__ */ jsx("h2", { class: "text-xl font-semibold mb-2 text-gray-900 dark:text-white", children: "Indexed Documentation" }),
|
|
11168
11046
|
/* @__PURE__ */ jsx(
|
|
@@ -11171,6 +11049,7 @@ function registerIndexRoute(server, config) {
|
|
|
11171
11049
|
id: "indexed-docs",
|
|
11172
11050
|
"hx-get": "/web/libraries",
|
|
11173
11051
|
"hx-trigger": "load, library-change from:body",
|
|
11052
|
+
"hx-swap": "morph:innerHTML",
|
|
11174
11053
|
children: /* @__PURE__ */ jsxs("div", { class: "animate-pulse", children: [
|
|
11175
11054
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4" }),
|
|
11176
11055
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5" }),
|
|
@@ -11292,10 +11171,12 @@ const ProgressBar = ({ progress, showText = true }) => {
|
|
|
11292
11171
|
) })
|
|
11293
11172
|
] });
|
|
11294
11173
|
};
|
|
11295
|
-
const LoadingSpinner = (
|
|
11174
|
+
const LoadingSpinner = ({
|
|
11175
|
+
class: className = "text-white"
|
|
11176
|
+
}) => /* @__PURE__ */ jsxs(
|
|
11296
11177
|
"svg",
|
|
11297
11178
|
{
|
|
11298
|
-
class:
|
|
11179
|
+
class: `animate-spin h-4 w-4 ${className}`,
|
|
11299
11180
|
xmlns: "http://www.w3.org/2000/svg",
|
|
11300
11181
|
fill: "none",
|
|
11301
11182
|
viewBox: "0 0 24 24",
|
|
@@ -11325,14 +11206,15 @@ const LoadingSpinner = () => /* @__PURE__ */ jsxs(
|
|
|
11325
11206
|
const JobItem = ({ job }) => {
|
|
11326
11207
|
job.dbStatus || job.status;
|
|
11327
11208
|
const isActiveJob = job.dbStatus ? isActiveStatus(job.dbStatus) : job.status === PipelineJobStatus.QUEUED || job.status === PipelineJobStatus.RUNNING;
|
|
11209
|
+
const defaultStateClasses = "border border-gray-300 bg-white text-red-600 hover:bg-red-50 focus:ring-4 focus:outline-none focus:ring-red-100 dark:border-gray-600 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700 dark:focus:ring-red-900";
|
|
11210
|
+
const confirmingStateClasses = "bg-red-600 text-white border-red-600 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-700 dark:border-red-700 dark:focus:ring-red-800";
|
|
11328
11211
|
return /* @__PURE__ */ jsx(
|
|
11329
11212
|
"div",
|
|
11330
11213
|
{
|
|
11331
11214
|
id: `job-item-${job.id}`,
|
|
11332
11215
|
class: "block p-3 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600",
|
|
11333
11216
|
"data-job-id": job.id,
|
|
11334
|
-
"x-data": "{ jobId: $el.dataset.jobId }",
|
|
11335
|
-
"x-bind:hx-preserve": "$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === jobId",
|
|
11217
|
+
"x-data": "{ jobId: $el.dataset.jobId, confirming: $el.dataset.confirming === 'true', isStopping: false }",
|
|
11336
11218
|
children: /* @__PURE__ */ jsxs("div", { class: "flex items-start justify-between", children: [
|
|
11337
11219
|
/* @__PURE__ */ jsxs("div", { class: "flex-1", children: [
|
|
11338
11220
|
/* @__PURE__ */ jsxs("p", { class: "text-sm font-medium text-gray-900 dark:text-white", children: [
|
|
@@ -11364,12 +11246,13 @@ const JobItem = ({ job }) => {
|
|
|
11364
11246
|
"button",
|
|
11365
11247
|
{
|
|
11366
11248
|
type: "button",
|
|
11367
|
-
class: "font-medium rounded-lg text-xs p-1 text-center inline-flex items-center transition-colors duration-150 ease-in-out
|
|
11249
|
+
class: "font-medium rounded-lg text-xs p-1 text-center inline-flex items-center transition-colors duration-150 ease-in-out",
|
|
11368
11250
|
title: "Stop this job",
|
|
11369
|
-
"x-
|
|
11370
|
-
"x-
|
|
11251
|
+
"x-bind:class": `confirming ? '${confirmingStateClasses}' : '${defaultStateClasses}'`,
|
|
11252
|
+
"x-on:click": "\n if (confirming) {\n isStopping = true;\n window.confirmationManager.clear($root.id);\n fetch('/web/jobs/' + jobId + '/cancel', {\n method: 'POST',\n headers: { 'Accept': 'application/json' },\n })\n .then(r => r.json())\n .then(() => {\n confirming = false;\n isStopping = false;\n document.dispatchEvent(new CustomEvent('job-list-refresh'));\n })\n .catch(() => { isStopping = false; });\n } else {\n confirming = true;\n isStopping = false;\n window.confirmationManager.start($root.id);\n }\n ",
|
|
11253
|
+
"x-bind:disabled": "isStopping",
|
|
11371
11254
|
children: [
|
|
11372
|
-
/* @__PURE__ */ jsxs("span", { "x-show": "
|
|
11255
|
+
/* @__PURE__ */ jsxs("span", { "x-show": "!confirming && !isStopping", children: [
|
|
11373
11256
|
/* @__PURE__ */ jsx(
|
|
11374
11257
|
"svg",
|
|
11375
11258
|
{
|
|
@@ -11382,15 +11265,8 @@ const JobItem = ({ job }) => {
|
|
|
11382
11265
|
),
|
|
11383
11266
|
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Stop job" })
|
|
11384
11267
|
] }),
|
|
11385
|
-
/* @__PURE__ */ jsx(
|
|
11386
|
-
|
|
11387
|
-
{
|
|
11388
|
-
"x-show": "$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === jobId && !$store.confirmingAction.isStopping",
|
|
11389
|
-
class: "px-2",
|
|
11390
|
-
children: "Cancel?"
|
|
11391
|
-
}
|
|
11392
|
-
),
|
|
11393
|
-
/* @__PURE__ */ jsxs("span", { "x-show": "$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === jobId && $store.confirmingAction.isStopping", children: [
|
|
11268
|
+
/* @__PURE__ */ jsx("span", { "x-show": "confirming && !isStopping", class: "px-2", children: "Cancel?" }),
|
|
11269
|
+
/* @__PURE__ */ jsxs("span", { "x-show": "isStopping", children: [
|
|
11394
11270
|
/* @__PURE__ */ jsx(LoadingSpinner, {}),
|
|
11395
11271
|
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Stopping..." })
|
|
11396
11272
|
] })
|
|
@@ -11407,13 +11283,45 @@ const JobItem = ({ job }) => {
|
|
|
11407
11283
|
}
|
|
11408
11284
|
);
|
|
11409
11285
|
};
|
|
11410
|
-
const JobList = ({ jobs }) =>
|
|
11286
|
+
const JobList = ({ jobs }) => {
|
|
11287
|
+
const hasJobs = jobs.length > 0;
|
|
11288
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11289
|
+
/* @__PURE__ */ jsx("div", { id: "job-list", class: "space-y-2 animate-[fadeSlideIn_0.2s_ease-out]", children: hasJobs ? jobs.map((job) => /* @__PURE__ */ jsx(JobItem, { job })) : /* @__PURE__ */ jsx("p", { class: "text-center text-gray-500 dark:text-gray-400", children: "No pending jobs." }) }),
|
|
11290
|
+
/* @__PURE__ */ jsx(
|
|
11291
|
+
"button",
|
|
11292
|
+
{
|
|
11293
|
+
id: "clear-completed-btn",
|
|
11294
|
+
"hx-swap-oob": "true",
|
|
11295
|
+
type: "button",
|
|
11296
|
+
class: `text-xs px-3 py-1.5 rounded-lg focus:ring-4 focus:outline-none transition-colors duration-150 ${hasJobs ? "text-gray-700 bg-gray-100 border border-gray-300 hover:bg-gray-200 focus:ring-gray-100 dark:bg-gray-600 dark:text-gray-300 dark:border-gray-500 dark:hover:bg-gray-700 dark:focus:ring-gray-700" : "text-gray-400 bg-gray-50 border border-gray-200 cursor-not-allowed dark:bg-gray-700 dark:text-gray-500 dark:border-gray-600"}`,
|
|
11297
|
+
title: "Clear all completed, cancelled, and failed jobs",
|
|
11298
|
+
"hx-post": "/web/jobs/clear-completed",
|
|
11299
|
+
"hx-trigger": "click",
|
|
11300
|
+
"hx-on": "htmx:afterRequest: document.dispatchEvent(new Event('job-list-refresh'))",
|
|
11301
|
+
"hx-swap": "none",
|
|
11302
|
+
disabled: !hasJobs,
|
|
11303
|
+
children: "Clear Completed Jobs"
|
|
11304
|
+
}
|
|
11305
|
+
)
|
|
11306
|
+
] });
|
|
11307
|
+
};
|
|
11411
11308
|
function registerJobListRoutes(server, listJobsTool) {
|
|
11412
11309
|
server.get("/web/jobs", async () => {
|
|
11413
11310
|
const result = await listJobsTool.execute({});
|
|
11414
11311
|
return /* @__PURE__ */ jsx(JobList, { jobs: result.jobs });
|
|
11415
11312
|
});
|
|
11416
11313
|
}
|
|
11314
|
+
const AddVersionButton = ({ libraryName }) => {
|
|
11315
|
+
return /* @__PURE__ */ jsx(
|
|
11316
|
+
PrimaryButton,
|
|
11317
|
+
{
|
|
11318
|
+
"hx-get": `/web/libraries/${encodeURIComponent(libraryName)}/add-version-form`,
|
|
11319
|
+
"hx-target": "#add-version-form-container",
|
|
11320
|
+
"hx-swap": "innerHTML",
|
|
11321
|
+
children: "Add New Version"
|
|
11322
|
+
}
|
|
11323
|
+
);
|
|
11324
|
+
};
|
|
11417
11325
|
const Alert = ({ type, title, message }) => {
|
|
11418
11326
|
let iconSvg;
|
|
11419
11327
|
let colorClasses;
|
|
@@ -11558,20 +11466,76 @@ const Tooltip = ({ text, position = "top" }) => {
|
|
|
11558
11466
|
);
|
|
11559
11467
|
};
|
|
11560
11468
|
const ScrapeFormContent = ({
|
|
11561
|
-
defaultExcludePatterns
|
|
11469
|
+
defaultExcludePatterns,
|
|
11470
|
+
initialValues,
|
|
11471
|
+
mode = "new"
|
|
11562
11472
|
}) => {
|
|
11563
|
-
const
|
|
11564
|
-
|
|
11565
|
-
|
|
11473
|
+
const isAddVersionMode = mode === "add-version";
|
|
11474
|
+
const urlValue = initialValues?.url || "";
|
|
11475
|
+
const libraryValue = initialValues?.library || "";
|
|
11476
|
+
const maxPagesValue = initialValues?.maxPages?.toString() || "";
|
|
11477
|
+
const maxDepthValue = initialValues?.maxDepth?.toString() || "";
|
|
11478
|
+
const scopeValue = initialValues?.scope || "subpages";
|
|
11479
|
+
const includePatternsValue = initialValues?.includePatterns || "";
|
|
11480
|
+
const scrapeModeValue = initialValues?.scrapeMode || ScrapeMode.Auto;
|
|
11481
|
+
const followRedirectsValue = initialValues?.followRedirects ?? true;
|
|
11482
|
+
const ignoreErrorsValue = initialValues?.ignoreErrors ?? true;
|
|
11483
|
+
const excludePatternsText = initialValues?.excludePatterns !== void 0 ? initialValues.excludePatterns : defaultExcludePatterns?.join("\n") || "";
|
|
11484
|
+
const headersJson = JSON.stringify(initialValues?.headers || []);
|
|
11485
|
+
const closeButtonAttrs = isAddVersionMode ? {
|
|
11486
|
+
"hx-get": `/web/libraries/${encodeURIComponent(libraryValue)}/add-version-button`,
|
|
11487
|
+
"hx-target": "#add-version-form-container",
|
|
11488
|
+
"hx-swap": "innerHTML"
|
|
11489
|
+
} : {
|
|
11490
|
+
"hx-get": "/web/jobs/new-button",
|
|
11491
|
+
"hx-target": "#addJobForm",
|
|
11492
|
+
"hx-swap": "innerHTML"
|
|
11493
|
+
};
|
|
11494
|
+
const formTarget = isAddVersionMode ? "#add-version-form-container" : "#addJobForm";
|
|
11495
|
+
const title = isAddVersionMode ? "Add New Version" : "Add New Documentation";
|
|
11496
|
+
return /* @__PURE__ */ jsxs("div", { class: "mt-4 p-4 bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-300 dark:border-gray-600 relative animate-[fadeSlideIn_0.2s_ease-out]", children: [
|
|
11497
|
+
/* @__PURE__ */ jsx(
|
|
11498
|
+
"button",
|
|
11499
|
+
{
|
|
11500
|
+
type: "button",
|
|
11501
|
+
...closeButtonAttrs,
|
|
11502
|
+
class: "absolute top-3 right-3 p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-150",
|
|
11503
|
+
title: "Close",
|
|
11504
|
+
children: /* @__PURE__ */ jsx(
|
|
11505
|
+
"svg",
|
|
11506
|
+
{
|
|
11507
|
+
class: "w-5 h-5",
|
|
11508
|
+
fill: "none",
|
|
11509
|
+
stroke: "currentColor",
|
|
11510
|
+
viewBox: "0 0 24 24",
|
|
11511
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
11512
|
+
children: /* @__PURE__ */ jsx(
|
|
11513
|
+
"path",
|
|
11514
|
+
{
|
|
11515
|
+
"stroke-linecap": "round",
|
|
11516
|
+
"stroke-linejoin": "round",
|
|
11517
|
+
"stroke-width": "2",
|
|
11518
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
11519
|
+
}
|
|
11520
|
+
)
|
|
11521
|
+
}
|
|
11522
|
+
)
|
|
11523
|
+
}
|
|
11524
|
+
),
|
|
11525
|
+
/* @__PURE__ */ jsx("h3", { class: "text-xl font-semibold text-gray-900 dark:text-white mb-2 pr-8", children: title }),
|
|
11566
11526
|
/* @__PURE__ */ jsxs(
|
|
11567
11527
|
"form",
|
|
11568
11528
|
{
|
|
11569
11529
|
"hx-post": "/web/jobs/scrape",
|
|
11570
|
-
"hx-target":
|
|
11530
|
+
"hx-target": formTarget,
|
|
11571
11531
|
"hx-swap": "innerHTML",
|
|
11572
11532
|
class: "space-y-2",
|
|
11573
|
-
"
|
|
11533
|
+
"data-initial-url": urlValue,
|
|
11534
|
+
"data-initial-headers": headersJson,
|
|
11535
|
+
"x-data": "{\n url: '',\n hasPath: false,\n headers: [],\n checkUrlPath() {\n try {\n const url = new URL(this.url);\n this.hasPath = url.pathname !== '/' && url.pathname !== '';\n } catch (e) {\n this.hasPath = false;\n }\n }\n }",
|
|
11536
|
+
"x-init": "\n url = $el.dataset.initialUrl || '';\n headers = JSON.parse($el.dataset.initialHeaders || '[]');\n checkUrlPath();\n ",
|
|
11574
11537
|
children: [
|
|
11538
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: "formMode", value: mode }),
|
|
11575
11539
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
11576
11540
|
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11577
11541
|
/* @__PURE__ */ jsx(
|
|
@@ -11612,6 +11576,7 @@ const ScrapeFormContent = ({
|
|
|
11612
11576
|
"x-model": "url",
|
|
11613
11577
|
"x-on:input": "checkUrlPath",
|
|
11614
11578
|
"x-on:paste": "$nextTick(() => checkUrlPath())",
|
|
11579
|
+
placeholder: "https://docs.example.com/library/",
|
|
11615
11580
|
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"
|
|
11616
11581
|
}
|
|
11617
11582
|
),
|
|
@@ -11646,13 +11611,18 @@ const ScrapeFormContent = ({
|
|
|
11646
11611
|
),
|
|
11647
11612
|
/* @__PURE__ */ jsx(Tooltip, { text: "The name of the library you're documenting. This will be used when searching." })
|
|
11648
11613
|
] }),
|
|
11649
|
-
/* @__PURE__ */
|
|
11614
|
+
isAddVersionMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11615
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: "library", value: libraryValue }),
|
|
11616
|
+
/* @__PURE__ */ jsx("div", { class: "mt-0.5 px-2 py-1 text-sm text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-md border border-gray-300 dark:border-gray-600", children: /* @__PURE__ */ jsx("span", { safe: true, children: libraryValue }) })
|
|
11617
|
+
] }) : /* @__PURE__ */ jsx(
|
|
11650
11618
|
"input",
|
|
11651
11619
|
{
|
|
11652
11620
|
type: "text",
|
|
11653
11621
|
name: "library",
|
|
11654
11622
|
id: "library",
|
|
11655
11623
|
required: true,
|
|
11624
|
+
value: libraryValue,
|
|
11625
|
+
placeholder: "e.g. react, vue, express",
|
|
11656
11626
|
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"
|
|
11657
11627
|
}
|
|
11658
11628
|
)
|
|
@@ -11667,7 +11637,7 @@ const ScrapeFormContent = ({
|
|
|
11667
11637
|
children: "Version (optional)"
|
|
11668
11638
|
}
|
|
11669
11639
|
),
|
|
11670
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "Specify the version of the library documentation you're indexing. This allows for version-specific searches." })
|
|
11640
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "Specify the version of the library documentation you're indexing (e.g. 2.0.0). Leave empty or enter 'latest' to index without a specific version. This allows for version-specific searches." })
|
|
11671
11641
|
] }),
|
|
11672
11642
|
/* @__PURE__ */ jsx(
|
|
11673
11643
|
"input",
|
|
@@ -11675,287 +11645,351 @@ const ScrapeFormContent = ({
|
|
|
11675
11645
|
type: "text",
|
|
11676
11646
|
name: "version",
|
|
11677
11647
|
id: "version",
|
|
11648
|
+
placeholder: "e.g. 2.0.0 or leave empty for latest",
|
|
11678
11649
|
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"
|
|
11679
11650
|
}
|
|
11680
11651
|
)
|
|
11681
11652
|
] }),
|
|
11682
|
-
/* @__PURE__ */ jsxs(
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
for: "maxPages",
|
|
11691
|
-
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11692
|
-
children: "Max Pages"
|
|
11693
|
-
}
|
|
11694
|
-
),
|
|
11695
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "The maximum number of pages to scrape. Default is 1000. Setting this too high may result in longer processing times." })
|
|
11696
|
-
] }),
|
|
11697
|
-
/* @__PURE__ */ jsx(
|
|
11698
|
-
"input",
|
|
11699
|
-
{
|
|
11700
|
-
type: "number",
|
|
11701
|
-
name: "maxPages",
|
|
11702
|
-
id: "maxPages",
|
|
11703
|
-
min: "1",
|
|
11704
|
-
placeholder: "1000",
|
|
11705
|
-
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"
|
|
11706
|
-
}
|
|
11707
|
-
)
|
|
11708
|
-
] }),
|
|
11709
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11710
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11711
|
-
/* @__PURE__ */ jsx(
|
|
11712
|
-
"label",
|
|
11713
|
-
{
|
|
11714
|
-
for: "maxDepth",
|
|
11715
|
-
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11716
|
-
children: "Max Depth"
|
|
11717
|
-
}
|
|
11718
|
-
),
|
|
11719
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "How many links deep the scraper should follow. Default is 3. Higher values capture more content but increase processing time." })
|
|
11720
|
-
] }),
|
|
11721
|
-
/* @__PURE__ */ jsx(
|
|
11722
|
-
"input",
|
|
11723
|
-
{
|
|
11724
|
-
type: "number",
|
|
11725
|
-
name: "maxDepth",
|
|
11726
|
-
id: "maxDepth",
|
|
11727
|
-
min: "0",
|
|
11728
|
-
placeholder: "3",
|
|
11729
|
-
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"
|
|
11730
|
-
}
|
|
11731
|
-
)
|
|
11732
|
-
] }),
|
|
11733
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11734
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11735
|
-
/* @__PURE__ */ jsx(
|
|
11736
|
-
"label",
|
|
11737
|
-
{
|
|
11738
|
-
for: "scope",
|
|
11739
|
-
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11740
|
-
children: "Scope"
|
|
11741
|
-
}
|
|
11742
|
-
),
|
|
11743
|
-
/* @__PURE__ */ jsx(
|
|
11744
|
-
Tooltip,
|
|
11745
|
-
{
|
|
11746
|
-
text: /* @__PURE__ */ jsxs("div", { children: [
|
|
11747
|
-
"Controls which pages are scraped:",
|
|
11748
|
-
/* @__PURE__ */ jsxs("ul", { class: "list-disc pl-5", children: [
|
|
11749
|
-
/* @__PURE__ */ jsx("li", { children: "'Subpages' only scrapes under the given URL path," }),
|
|
11750
|
-
/* @__PURE__ */ jsx("li", { children: "'Hostname' scrapes all content on the same host (e.g., all of docs.example.com)," }),
|
|
11751
|
-
/* @__PURE__ */ jsx("li", { children: "'Domain' scrapes all content on the domain and its subdomains (e.g., all of example.com)." })
|
|
11752
|
-
] })
|
|
11753
|
-
] })
|
|
11754
|
-
}
|
|
11755
|
-
)
|
|
11756
|
-
] }),
|
|
11653
|
+
/* @__PURE__ */ jsxs(
|
|
11654
|
+
"div",
|
|
11655
|
+
{
|
|
11656
|
+
class: "bg-gray-50 dark:bg-gray-900 p-2 rounded-md",
|
|
11657
|
+
"data-should-open": isAddVersionMode && (maxPagesValue || maxDepthValue || scopeValue !== "subpages" || includePatternsValue || excludePatternsText || scrapeModeValue !== ScrapeMode.Auto) ? "true" : "false",
|
|
11658
|
+
"x-data": "{ open: false }",
|
|
11659
|
+
"x-init": "open = $el.dataset.shouldOpen === 'true'",
|
|
11660
|
+
children: [
|
|
11757
11661
|
/* @__PURE__ */ jsxs(
|
|
11758
|
-
"
|
|
11662
|
+
"button",
|
|
11759
11663
|
{
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11664
|
+
type: "button",
|
|
11665
|
+
class: "w-full flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors",
|
|
11666
|
+
"x-on:click": "open = !open",
|
|
11763
11667
|
children: [
|
|
11764
|
-
/* @__PURE__ */ jsx(
|
|
11765
|
-
|
|
11766
|
-
|
|
11668
|
+
/* @__PURE__ */ jsx(
|
|
11669
|
+
"svg",
|
|
11670
|
+
{
|
|
11671
|
+
class: "w-4 h-4 transform transition-transform duration-200",
|
|
11672
|
+
"x-bind:class": "{ 'rotate-90': open }",
|
|
11673
|
+
fill: "none",
|
|
11674
|
+
stroke: "currentColor",
|
|
11675
|
+
viewBox: "0 0 24 24",
|
|
11676
|
+
children: /* @__PURE__ */ jsx(
|
|
11677
|
+
"path",
|
|
11678
|
+
{
|
|
11679
|
+
"stroke-linecap": "round",
|
|
11680
|
+
"stroke-linejoin": "round",
|
|
11681
|
+
"stroke-width": "2",
|
|
11682
|
+
d: "M9 5l7 7-7 7"
|
|
11683
|
+
}
|
|
11684
|
+
)
|
|
11685
|
+
}
|
|
11686
|
+
),
|
|
11687
|
+
/* @__PURE__ */ jsx("span", { children: "Advanced Options" })
|
|
11767
11688
|
]
|
|
11768
11689
|
}
|
|
11769
|
-
)
|
|
11770
|
-
] }),
|
|
11771
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11772
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11773
|
-
/* @__PURE__ */ jsx(
|
|
11774
|
-
"label",
|
|
11775
|
-
{
|
|
11776
|
-
for: "includePatterns",
|
|
11777
|
-
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11778
|
-
children: "Include Patterns"
|
|
11779
|
-
}
|
|
11780
|
-
),
|
|
11781
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "Glob or regex patterns for URLs to include. One per line or comma-separated. Regex patterns must be wrapped in slashes, e.g. /pattern/." })
|
|
11782
|
-
] }),
|
|
11783
|
-
/* @__PURE__ */ jsx(
|
|
11784
|
-
"textarea",
|
|
11785
|
-
{
|
|
11786
|
-
name: "includePatterns",
|
|
11787
|
-
id: "includePatterns",
|
|
11788
|
-
rows: "2",
|
|
11789
|
-
placeholder: "e.g. docs/* or /api\\/v1.*/",
|
|
11790
|
-
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"
|
|
11791
|
-
}
|
|
11792
|
-
)
|
|
11793
|
-
] }),
|
|
11794
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11795
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11796
|
-
/* @__PURE__ */ jsx(
|
|
11797
|
-
"label",
|
|
11798
|
-
{
|
|
11799
|
-
for: "excludePatterns",
|
|
11800
|
-
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11801
|
-
children: "Exclude Patterns"
|
|
11802
|
-
}
|
|
11803
|
-
),
|
|
11804
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "Glob or regex patterns for URLs to exclude. One per line or comma-separated. Exclude takes precedence over include. Regex patterns must be wrapped in slashes, e.g. /pattern/. Edit or clear this field to customize exclusions." })
|
|
11805
|
-
] }),
|
|
11806
|
-
/* @__PURE__ */ jsx(
|
|
11807
|
-
"textarea",
|
|
11808
|
-
{
|
|
11809
|
-
name: "excludePatterns",
|
|
11810
|
-
id: "excludePatterns",
|
|
11811
|
-
rows: "5",
|
|
11812
|
-
safe: true,
|
|
11813
|
-
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",
|
|
11814
|
-
children: defaultExcludePatternsText
|
|
11815
|
-
}
|
|
11816
11690
|
),
|
|
11817
|
-
/* @__PURE__ */
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
Tooltip,
|
|
11831
|
-
{
|
|
11832
|
-
text: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("ul", { class: "list-disc pl-5", children: [
|
|
11833
|
-
/* @__PURE__ */ jsx("li", { children: "'Auto' automatically selects the best method," }),
|
|
11834
|
-
/* @__PURE__ */ jsx("li", { children: "'Fetch' uses simple HTTP requests (faster but may miss dynamic content)," }),
|
|
11835
|
-
/* @__PURE__ */ jsx("li", { children: "'Playwright' uses a headless browser (slower but better for JS-heavy sites)." })
|
|
11836
|
-
] }) })
|
|
11837
|
-
}
|
|
11838
|
-
)
|
|
11839
|
-
] }),
|
|
11840
|
-
/* @__PURE__ */ jsxs(
|
|
11841
|
-
"select",
|
|
11842
|
-
{
|
|
11843
|
-
name: "scrapeMode",
|
|
11844
|
-
id: "scrapeMode",
|
|
11845
|
-
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",
|
|
11846
|
-
children: [
|
|
11847
|
-
/* @__PURE__ */ jsx("option", { value: ScrapeMode.Auto, selected: true, children: "Auto (Default)" }),
|
|
11848
|
-
/* @__PURE__ */ jsx("option", { value: ScrapeMode.Fetch, children: "Fetch" }),
|
|
11849
|
-
/* @__PURE__ */ jsx("option", { value: ScrapeMode.Playwright, children: "Playwright" })
|
|
11850
|
-
]
|
|
11851
|
-
}
|
|
11852
|
-
)
|
|
11853
|
-
] }),
|
|
11854
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11855
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center mb-1", children: [
|
|
11856
|
-
/* @__PURE__ */ jsx("label", { class: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: "Custom HTTP Headers" }),
|
|
11857
|
-
/* @__PURE__ */ jsx(Tooltip, { text: "Add custom HTTP headers (e.g., for authentication). These will be sent with every HTTP request." })
|
|
11858
|
-
] }),
|
|
11859
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
11860
|
-
/* @__PURE__ */ jsx("template", { "x-for": "(header, idx) in headers", children: /* @__PURE__ */ jsxs("div", { class: "flex space-x-2 mb-1", children: [
|
|
11691
|
+
/* @__PURE__ */ jsxs("div", { "x-show": "open", "x-cloak": true, "x-collapse": true, class: "mt-2 space-y-2", children: [
|
|
11692
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11693
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11694
|
+
/* @__PURE__ */ jsx(
|
|
11695
|
+
"label",
|
|
11696
|
+
{
|
|
11697
|
+
for: "maxPages",
|
|
11698
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11699
|
+
children: "Max Pages"
|
|
11700
|
+
}
|
|
11701
|
+
),
|
|
11702
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "The maximum number of pages to scrape. Default is 1000. Setting this too high may result in longer processing times." })
|
|
11703
|
+
] }),
|
|
11861
11704
|
/* @__PURE__ */ jsx(
|
|
11862
11705
|
"input",
|
|
11863
11706
|
{
|
|
11864
|
-
type: "
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11707
|
+
type: "number",
|
|
11708
|
+
name: "maxPages",
|
|
11709
|
+
id: "maxPages",
|
|
11710
|
+
min: "1",
|
|
11711
|
+
placeholder: "1000",
|
|
11712
|
+
value: maxPagesValue,
|
|
11713
|
+
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"
|
|
11869
11714
|
}
|
|
11870
|
-
)
|
|
11871
|
-
|
|
11715
|
+
)
|
|
11716
|
+
] }),
|
|
11717
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11718
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11719
|
+
/* @__PURE__ */ jsx(
|
|
11720
|
+
"label",
|
|
11721
|
+
{
|
|
11722
|
+
for: "maxDepth",
|
|
11723
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11724
|
+
children: "Max Depth"
|
|
11725
|
+
}
|
|
11726
|
+
),
|
|
11727
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "How many links deep the scraper should follow. Default is 3. Higher values capture more content but increase processing time." })
|
|
11728
|
+
] }),
|
|
11872
11729
|
/* @__PURE__ */ jsx(
|
|
11873
11730
|
"input",
|
|
11874
11731
|
{
|
|
11875
|
-
type: "
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11732
|
+
type: "number",
|
|
11733
|
+
name: "maxDepth",
|
|
11734
|
+
id: "maxDepth",
|
|
11735
|
+
min: "0",
|
|
11736
|
+
placeholder: "3",
|
|
11737
|
+
value: maxDepthValue,
|
|
11738
|
+
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"
|
|
11880
11739
|
}
|
|
11881
|
-
)
|
|
11740
|
+
)
|
|
11741
|
+
] }),
|
|
11742
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11743
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11744
|
+
/* @__PURE__ */ jsx(
|
|
11745
|
+
"label",
|
|
11746
|
+
{
|
|
11747
|
+
for: "scope",
|
|
11748
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11749
|
+
children: "Scope"
|
|
11750
|
+
}
|
|
11751
|
+
),
|
|
11752
|
+
/* @__PURE__ */ jsx(
|
|
11753
|
+
Tooltip,
|
|
11754
|
+
{
|
|
11755
|
+
text: /* @__PURE__ */ jsxs("div", { children: [
|
|
11756
|
+
"Controls which pages are scraped:",
|
|
11757
|
+
/* @__PURE__ */ jsxs("ul", { class: "list-disc pl-5", children: [
|
|
11758
|
+
/* @__PURE__ */ jsx("li", { children: "'Subpages' only scrapes under the given URL path," }),
|
|
11759
|
+
/* @__PURE__ */ jsx("li", { children: "'Hostname' scrapes all content on the same host (e.g., all of docs.example.com)," }),
|
|
11760
|
+
/* @__PURE__ */ jsx("li", { children: "'Domain' scrapes all content on the domain and its subdomains (e.g., all of example.com)." })
|
|
11761
|
+
] })
|
|
11762
|
+
] })
|
|
11763
|
+
}
|
|
11764
|
+
)
|
|
11765
|
+
] }),
|
|
11766
|
+
/* @__PURE__ */ jsxs(
|
|
11767
|
+
"select",
|
|
11768
|
+
{
|
|
11769
|
+
name: "scope",
|
|
11770
|
+
id: "scope",
|
|
11771
|
+
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",
|
|
11772
|
+
children: [
|
|
11773
|
+
/* @__PURE__ */ jsx("option", { value: "subpages", selected: scopeValue === "subpages", children: "Subpages (Default)" }),
|
|
11774
|
+
/* @__PURE__ */ jsx("option", { value: "hostname", selected: scopeValue === "hostname", children: "Hostname" }),
|
|
11775
|
+
/* @__PURE__ */ jsx("option", { value: "domain", selected: scopeValue === "domain", children: "Domain" })
|
|
11776
|
+
]
|
|
11777
|
+
}
|
|
11778
|
+
)
|
|
11779
|
+
] }),
|
|
11780
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11781
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11782
|
+
/* @__PURE__ */ jsx(
|
|
11783
|
+
"label",
|
|
11784
|
+
{
|
|
11785
|
+
for: "includePatterns",
|
|
11786
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11787
|
+
children: "Include Patterns"
|
|
11788
|
+
}
|
|
11789
|
+
),
|
|
11790
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "Glob or regex patterns for URLs to include. One per line or comma-separated. Regex patterns must be wrapped in slashes, e.g. /pattern/." })
|
|
11791
|
+
] }),
|
|
11792
|
+
/* @__PURE__ */ jsx(
|
|
11793
|
+
"textarea",
|
|
11794
|
+
{
|
|
11795
|
+
name: "includePatterns",
|
|
11796
|
+
id: "includePatterns",
|
|
11797
|
+
rows: "2",
|
|
11798
|
+
placeholder: "e.g. docs/* or /api\\/v1.*/",
|
|
11799
|
+
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",
|
|
11800
|
+
safe: true,
|
|
11801
|
+
children: includePatternsValue
|
|
11802
|
+
}
|
|
11803
|
+
)
|
|
11804
|
+
] }),
|
|
11805
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11806
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11807
|
+
/* @__PURE__ */ jsx(
|
|
11808
|
+
"label",
|
|
11809
|
+
{
|
|
11810
|
+
for: "excludePatterns",
|
|
11811
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11812
|
+
children: "Exclude Patterns"
|
|
11813
|
+
}
|
|
11814
|
+
),
|
|
11815
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "Glob or regex patterns for URLs to exclude. One per line or comma-separated. Exclude takes precedence over include. Regex patterns must be wrapped in slashes, e.g. /pattern/. Edit or clear this field to customize exclusions." })
|
|
11816
|
+
] }),
|
|
11882
11817
|
/* @__PURE__ */ jsx(
|
|
11883
|
-
"
|
|
11818
|
+
"textarea",
|
|
11884
11819
|
{
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11820
|
+
name: "excludePatterns",
|
|
11821
|
+
id: "excludePatterns",
|
|
11822
|
+
rows: "5",
|
|
11823
|
+
safe: true,
|
|
11824
|
+
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",
|
|
11825
|
+
children: excludePatternsText
|
|
11889
11826
|
}
|
|
11890
11827
|
),
|
|
11828
|
+
/* @__PURE__ */ jsx("p", { class: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: isAddVersionMode ? "Patterns from previous version. Edit as needed." : "Default patterns are pre-filled. Edit to customize or clear to exclude nothing." })
|
|
11829
|
+
] }),
|
|
11830
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11831
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11832
|
+
/* @__PURE__ */ jsx(
|
|
11833
|
+
"label",
|
|
11834
|
+
{
|
|
11835
|
+
for: "scrapeMode",
|
|
11836
|
+
class: "block text-sm font-medium text-gray-700 dark:text-gray-300",
|
|
11837
|
+
children: "Scrape Mode"
|
|
11838
|
+
}
|
|
11839
|
+
),
|
|
11840
|
+
/* @__PURE__ */ jsx(
|
|
11841
|
+
Tooltip,
|
|
11842
|
+
{
|
|
11843
|
+
text: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("ul", { class: "list-disc pl-5", children: [
|
|
11844
|
+
/* @__PURE__ */ jsx("li", { children: "'Auto' automatically selects the best method," }),
|
|
11845
|
+
/* @__PURE__ */ jsx("li", { children: "'Fetch' uses simple HTTP requests (faster but may miss dynamic content)," }),
|
|
11846
|
+
/* @__PURE__ */ jsx("li", { children: "'Playwright' uses a headless browser (slower but better for JS-heavy sites)." })
|
|
11847
|
+
] }) })
|
|
11848
|
+
}
|
|
11849
|
+
)
|
|
11850
|
+
] }),
|
|
11851
|
+
/* @__PURE__ */ jsxs(
|
|
11852
|
+
"select",
|
|
11853
|
+
{
|
|
11854
|
+
name: "scrapeMode",
|
|
11855
|
+
id: "scrapeMode",
|
|
11856
|
+
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",
|
|
11857
|
+
children: [
|
|
11858
|
+
/* @__PURE__ */ jsx(
|
|
11859
|
+
"option",
|
|
11860
|
+
{
|
|
11861
|
+
value: ScrapeMode.Auto,
|
|
11862
|
+
selected: scrapeModeValue === ScrapeMode.Auto,
|
|
11863
|
+
children: "Auto (Default)"
|
|
11864
|
+
}
|
|
11865
|
+
),
|
|
11866
|
+
/* @__PURE__ */ jsx(
|
|
11867
|
+
"option",
|
|
11868
|
+
{
|
|
11869
|
+
value: ScrapeMode.Fetch,
|
|
11870
|
+
selected: scrapeModeValue === ScrapeMode.Fetch,
|
|
11871
|
+
children: "Fetch"
|
|
11872
|
+
}
|
|
11873
|
+
),
|
|
11874
|
+
/* @__PURE__ */ jsx(
|
|
11875
|
+
"option",
|
|
11876
|
+
{
|
|
11877
|
+
value: ScrapeMode.Playwright,
|
|
11878
|
+
selected: scrapeModeValue === ScrapeMode.Playwright,
|
|
11879
|
+
children: "Playwright"
|
|
11880
|
+
}
|
|
11881
|
+
)
|
|
11882
|
+
]
|
|
11883
|
+
}
|
|
11884
|
+
)
|
|
11885
|
+
] }),
|
|
11886
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11887
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center mb-1", children: [
|
|
11888
|
+
/* @__PURE__ */ jsx("label", { class: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: "Custom HTTP Headers" }),
|
|
11889
|
+
/* @__PURE__ */ jsx(Tooltip, { text: "Add custom HTTP headers (e.g., for authentication). These will be sent with every HTTP request." })
|
|
11890
|
+
] }),
|
|
11891
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
11892
|
+
/* @__PURE__ */ jsx("template", { "x-for": "(header, idx) in headers", children: /* @__PURE__ */ jsxs("div", { class: "flex space-x-2 mb-1", children: [
|
|
11893
|
+
/* @__PURE__ */ jsx(
|
|
11894
|
+
"input",
|
|
11895
|
+
{
|
|
11896
|
+
type: "text",
|
|
11897
|
+
class: "w-1/3 px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-xs",
|
|
11898
|
+
placeholder: "Header Name",
|
|
11899
|
+
"x-model": "header.name",
|
|
11900
|
+
required: true
|
|
11901
|
+
}
|
|
11902
|
+
),
|
|
11903
|
+
/* @__PURE__ */ jsx("span", { class: "text-gray-500", children: ":" }),
|
|
11904
|
+
/* @__PURE__ */ jsx(
|
|
11905
|
+
"input",
|
|
11906
|
+
{
|
|
11907
|
+
type: "text",
|
|
11908
|
+
class: "w-1/2 px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-xs",
|
|
11909
|
+
placeholder: "Header Value",
|
|
11910
|
+
"x-model": "header.value",
|
|
11911
|
+
required: true
|
|
11912
|
+
}
|
|
11913
|
+
),
|
|
11914
|
+
/* @__PURE__ */ jsx(
|
|
11915
|
+
"button",
|
|
11916
|
+
{
|
|
11917
|
+
type: "button",
|
|
11918
|
+
class: "text-red-500 hover:text-red-700 text-xs",
|
|
11919
|
+
"x-on:click": "headers.splice(idx, 1)",
|
|
11920
|
+
children: "Remove"
|
|
11921
|
+
}
|
|
11922
|
+
),
|
|
11923
|
+
/* @__PURE__ */ jsx(
|
|
11924
|
+
"input",
|
|
11925
|
+
{
|
|
11926
|
+
type: "hidden",
|
|
11927
|
+
name: "header[]",
|
|
11928
|
+
"x-bind:value": "header.name && header.value ? header.name + ':' + header.value : ''"
|
|
11929
|
+
}
|
|
11930
|
+
)
|
|
11931
|
+
] }) }),
|
|
11932
|
+
/* @__PURE__ */ jsx(
|
|
11933
|
+
"button",
|
|
11934
|
+
{
|
|
11935
|
+
type: "button",
|
|
11936
|
+
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",
|
|
11937
|
+
"x-on:click": "headers.push({ name: '', value: '' })",
|
|
11938
|
+
children: "+ Add Header"
|
|
11939
|
+
}
|
|
11940
|
+
)
|
|
11941
|
+
] })
|
|
11942
|
+
] }),
|
|
11943
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11891
11944
|
/* @__PURE__ */ jsx(
|
|
11892
11945
|
"input",
|
|
11893
11946
|
{
|
|
11894
|
-
|
|
11895
|
-
name: "
|
|
11896
|
-
|
|
11947
|
+
id: "followRedirects",
|
|
11948
|
+
name: "followRedirects",
|
|
11949
|
+
type: "checkbox",
|
|
11950
|
+
checked: followRedirectsValue,
|
|
11951
|
+
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"
|
|
11952
|
+
}
|
|
11953
|
+
),
|
|
11954
|
+
/* @__PURE__ */ jsx(
|
|
11955
|
+
"label",
|
|
11956
|
+
{
|
|
11957
|
+
for: "followRedirects",
|
|
11958
|
+
class: "ml-1 block text-sm text-gray-900 dark:text-gray-300",
|
|
11959
|
+
children: "Follow Redirects"
|
|
11897
11960
|
}
|
|
11898
11961
|
)
|
|
11899
|
-
] })
|
|
11900
|
-
/* @__PURE__ */
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11962
|
+
] }),
|
|
11963
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11964
|
+
/* @__PURE__ */ jsx(
|
|
11965
|
+
"input",
|
|
11966
|
+
{
|
|
11967
|
+
id: "ignoreErrors",
|
|
11968
|
+
name: "ignoreErrors",
|
|
11969
|
+
type: "checkbox",
|
|
11970
|
+
checked: ignoreErrorsValue,
|
|
11971
|
+
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"
|
|
11972
|
+
}
|
|
11973
|
+
),
|
|
11974
|
+
/* @__PURE__ */ jsx(
|
|
11975
|
+
"label",
|
|
11976
|
+
{
|
|
11977
|
+
for: "ignoreErrors",
|
|
11978
|
+
class: "ml-1 block text-sm text-gray-900 dark:text-gray-300",
|
|
11979
|
+
children: "Ignore Errors During Scraping"
|
|
11980
|
+
}
|
|
11981
|
+
)
|
|
11982
|
+
] })
|
|
11909
11983
|
] })
|
|
11910
|
-
]
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
"input",
|
|
11914
|
-
{
|
|
11915
|
-
id: "followRedirects",
|
|
11916
|
-
name: "followRedirects",
|
|
11917
|
-
type: "checkbox",
|
|
11918
|
-
checked: true,
|
|
11919
|
-
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"
|
|
11920
|
-
}
|
|
11921
|
-
),
|
|
11922
|
-
/* @__PURE__ */ jsx(
|
|
11923
|
-
"label",
|
|
11924
|
-
{
|
|
11925
|
-
for: "followRedirects",
|
|
11926
|
-
class: "ml-1 block text-sm text-gray-900 dark:text-gray-300",
|
|
11927
|
-
children: "Follow Redirects"
|
|
11928
|
-
}
|
|
11929
|
-
)
|
|
11930
|
-
] }),
|
|
11931
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center", children: [
|
|
11932
|
-
/* @__PURE__ */ jsx(
|
|
11933
|
-
"input",
|
|
11934
|
-
{
|
|
11935
|
-
id: "ignoreErrors",
|
|
11936
|
-
name: "ignoreErrors",
|
|
11937
|
-
type: "checkbox",
|
|
11938
|
-
checked: true,
|
|
11939
|
-
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"
|
|
11940
|
-
}
|
|
11941
|
-
),
|
|
11942
|
-
/* @__PURE__ */ jsx(
|
|
11943
|
-
"label",
|
|
11944
|
-
{
|
|
11945
|
-
for: "ignoreErrors",
|
|
11946
|
-
class: "ml-1 block text-sm text-gray-900 dark:text-gray-300",
|
|
11947
|
-
children: "Ignore Errors During Scraping"
|
|
11948
|
-
}
|
|
11949
|
-
)
|
|
11950
|
-
] })
|
|
11951
|
-
] })
|
|
11952
|
-
] }),
|
|
11984
|
+
]
|
|
11985
|
+
}
|
|
11986
|
+
),
|
|
11953
11987
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
11954
11988
|
"button",
|
|
11955
11989
|
{
|
|
11956
11990
|
type: "submit",
|
|
11957
11991
|
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",
|
|
11958
|
-
children: "
|
|
11992
|
+
children: "Start Indexing"
|
|
11959
11993
|
}
|
|
11960
11994
|
) })
|
|
11961
11995
|
]
|
|
@@ -11964,7 +11998,7 @@ const ScrapeFormContent = ({
|
|
|
11964
11998
|
/* @__PURE__ */ jsx("div", { id: "job-response", class: "mt-2 text-sm" })
|
|
11965
11999
|
] });
|
|
11966
12000
|
};
|
|
11967
|
-
const ScrapeForm = ({ defaultExcludePatterns }) => /* @__PURE__ */ jsx("div", { id: "scrape-form-container", children: /* @__PURE__ */ jsx(ScrapeFormContent, { defaultExcludePatterns }) });
|
|
12001
|
+
const ScrapeForm = ({ defaultExcludePatterns }) => /* @__PURE__ */ jsx("div", { id: "scrape-form-container", class: "animate-[fadeSlideIn_0.2s_ease-out]", children: /* @__PURE__ */ jsx(ScrapeFormContent, { defaultExcludePatterns }) });
|
|
11968
12002
|
const DEFAULT_FILE_EXCLUSIONS = [
|
|
11969
12003
|
// CHANGELOG files (case variations)
|
|
11970
12004
|
"**/CHANGELOG.md",
|
|
@@ -12068,6 +12102,9 @@ function registerNewJobRoutes(server, scrapeTool) {
|
|
|
12068
12102
|
server.get("/web/jobs/new", async () => {
|
|
12069
12103
|
return /* @__PURE__ */ jsx(ScrapeForm, { defaultExcludePatterns: DEFAULT_EXCLUSION_PATTERNS });
|
|
12070
12104
|
});
|
|
12105
|
+
server.get("/web/jobs/new-button", async () => {
|
|
12106
|
+
return /* @__PURE__ */ jsx(AddJobButton, {});
|
|
12107
|
+
});
|
|
12071
12108
|
server.post(
|
|
12072
12109
|
"/web/jobs/scrape",
|
|
12073
12110
|
async (request, reply) => {
|
|
@@ -12102,11 +12139,11 @@ function registerNewJobRoutes(server, scrapeTool) {
|
|
|
12102
12139
|
}
|
|
12103
12140
|
);
|
|
12104
12141
|
}
|
|
12142
|
+
const normalizedVersion = !body.version || body.version.trim() === "" || body.version.trim().toLowerCase() === "latest" ? null : body.version.trim();
|
|
12105
12143
|
const scrapeOptions = {
|
|
12106
12144
|
url: body.url,
|
|
12107
12145
|
library: body.library,
|
|
12108
|
-
version:
|
|
12109
|
-
// Handle empty string as null
|
|
12146
|
+
version: normalizedVersion,
|
|
12110
12147
|
waitForCompletion: false,
|
|
12111
12148
|
// Don't wait in UI
|
|
12112
12149
|
options: {
|
|
@@ -12125,25 +12162,20 @@ function registerNewJobRoutes(server, scrapeTool) {
|
|
|
12125
12162
|
};
|
|
12126
12163
|
const result = await scrapeTool.execute(scrapeOptions);
|
|
12127
12164
|
if ("jobId" in result) {
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
message:
|
|
12134
|
-
|
|
12135
|
-
" ",
|
|
12136
|
-
/* @__PURE__ */ jsx("span", { safe: true, children: result.jobId })
|
|
12137
|
-
] })
|
|
12138
|
-
}
|
|
12139
|
-
),
|
|
12140
|
-
/* @__PURE__ */ jsx("div", { id: "scrape-form-container", "hx-swap-oob": "innerHTML", children: /* @__PURE__ */ jsx(
|
|
12141
|
-
ScrapeFormContent,
|
|
12142
|
-
{
|
|
12143
|
-
defaultExcludePatterns: DEFAULT_EXCLUSION_PATTERNS
|
|
12165
|
+
const versionDisplay = normalizedVersion || "latest";
|
|
12166
|
+
reply.header(
|
|
12167
|
+
"HX-Trigger",
|
|
12168
|
+
JSON.stringify({
|
|
12169
|
+
toast: {
|
|
12170
|
+
message: `Indexing started for ${body.library}@${versionDisplay}`,
|
|
12171
|
+
type: "success"
|
|
12144
12172
|
}
|
|
12145
|
-
|
|
12146
|
-
|
|
12173
|
+
})
|
|
12174
|
+
);
|
|
12175
|
+
if (body.formMode === "add-version") {
|
|
12176
|
+
return /* @__PURE__ */ jsx(AddVersionButton, { libraryName: body.library });
|
|
12177
|
+
}
|
|
12178
|
+
return /* @__PURE__ */ jsx(AddJobButton, {});
|
|
12147
12179
|
}
|
|
12148
12180
|
return /* @__PURE__ */ jsx(Alert, { type: "warning", message: "Job finished unexpectedly quickly." });
|
|
12149
12181
|
} catch (error) {
|
|
@@ -12169,15 +12201,16 @@ function registerNewJobRoutes(server, scrapeTool) {
|
|
|
12169
12201
|
const VersionDetailsRow = ({
|
|
12170
12202
|
version,
|
|
12171
12203
|
libraryName,
|
|
12172
|
-
showDelete = true
|
|
12173
|
-
|
|
12204
|
+
showDelete = true,
|
|
12205
|
+
showRefresh = false
|
|
12174
12206
|
}) => {
|
|
12175
12207
|
const indexedDate = version.indexedAt ? new Date(version.indexedAt).toLocaleDateString() : "N/A";
|
|
12176
|
-
const versionLabel = version.ref.version || "
|
|
12208
|
+
const versionLabel = version.ref.version || "Latest";
|
|
12177
12209
|
const versionParam = version.ref.version || "";
|
|
12178
12210
|
const sanitizedLibraryName = libraryName.replace(/[^a-zA-Z0-9-_]/g, "-");
|
|
12179
12211
|
const sanitizedVersionParam = versionParam.replace(/[^a-zA-Z0-9-_]/g, "-");
|
|
12180
12212
|
const rowId = `row-${sanitizedLibraryName}-${sanitizedVersionParam}`;
|
|
12213
|
+
const initialIsRefreshing = isActiveStatus(version.status);
|
|
12181
12214
|
const defaultStateClasses = "text-red-700 border border-red-700 hover:bg-red-700 hover:text-white focus:ring-4 focus:outline-none focus:ring-red-300 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:focus:ring-red-800 dark:hover:bg-red-500";
|
|
12182
12215
|
const confirmingStateClasses = "bg-red-600 text-white border-red-600 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-700 dark:border-red-700 dark:focus:ring-red-800";
|
|
12183
12216
|
return (
|
|
@@ -12189,15 +12222,16 @@ const VersionDetailsRow = ({
|
|
|
12189
12222
|
class: "flex justify-between items-center py-1 border-b border-gray-200 dark:border-gray-600 last:border-b-0",
|
|
12190
12223
|
"data-library-name": libraryName,
|
|
12191
12224
|
"data-version-param": versionParam,
|
|
12192
|
-
"
|
|
12193
|
-
"x-
|
|
12225
|
+
"data-is-refreshing": initialIsRefreshing ? "true" : "false",
|
|
12226
|
+
"x-data": "{ \n library: $el.dataset.libraryName, \n version: $el.dataset.versionParam, \n confirming: $el.dataset.confirming === 'true', \n isDeleting: false,\n isRefreshing: $el.dataset.isRefreshing === 'true',\n setRefreshing(val) {\n this.isRefreshing = !!val;\n this.$el.dataset.isRefreshing = val ? 'true' : 'false';\n },\n init() {\n const rowId = this.$el.id;\n const myLibrary = this.library;\n const myVersion = this.version;\n \n document.body.addEventListener('job-status-change', (e) => {\n const job = e.detail;\n const jobVersion = job.version || '';\n if (job.library === myLibrary && jobVersion === myVersion) {\n const newValue = ['queued', 'running'].includes(job.status);\n const el = document.getElementById(rowId);\n if (el) {\n el.dispatchEvent(new CustomEvent('set-refreshing', { detail: newValue, bubbles: true }));\n }\n }\n });\n }\n }",
|
|
12227
|
+
"x-on:set-refreshing": "setRefreshing($event.detail)",
|
|
12194
12228
|
children: [
|
|
12195
12229
|
/* @__PURE__ */ jsx(
|
|
12196
12230
|
"span",
|
|
12197
12231
|
{
|
|
12198
12232
|
class: "text-sm text-gray-900 dark:text-white w-1/4 truncate",
|
|
12199
12233
|
title: versionLabel,
|
|
12200
|
-
children: version.ref.version ? /* @__PURE__ */ jsx(VersionBadge, { version: version.ref.version }) : /* @__PURE__ */ jsx("span", { children: "
|
|
12234
|
+
children: version.ref.version ? /* @__PURE__ */ jsx(VersionBadge, { version: version.ref.version }) : /* @__PURE__ */ jsx("span", { class: "text-gray-600 dark:text-gray-400", children: "Latest" })
|
|
12201
12235
|
}
|
|
12202
12236
|
),
|
|
12203
12237
|
/* @__PURE__ */ jsxs("div", { class: "flex space-x-2 text-sm text-gray-600 dark:text-gray-400 w-3/4 justify-end items-center", children: [
|
|
@@ -12207,7 +12241,7 @@ const VersionDetailsRow = ({
|
|
|
12207
12241
|
/* @__PURE__ */ jsx("span", { class: "font-semibold", safe: true, children: version.counts.uniqueUrls.toLocaleString() })
|
|
12208
12242
|
] }),
|
|
12209
12243
|
/* @__PURE__ */ jsxs("span", { title: "Number of indexed snippets", children: [
|
|
12210
|
-
"
|
|
12244
|
+
"Chunks:",
|
|
12211
12245
|
" ",
|
|
12212
12246
|
/* @__PURE__ */ jsx("span", { class: "font-semibold", safe: true, children: version.counts.documents.toLocaleString() })
|
|
12213
12247
|
] }),
|
|
@@ -12217,73 +12251,117 @@ const VersionDetailsRow = ({
|
|
|
12217
12251
|
/* @__PURE__ */ jsx("span", { class: "font-semibold", safe: true, children: indexedDate })
|
|
12218
12252
|
] })
|
|
12219
12253
|
] }),
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
}
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12254
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center ml-2 space-x-1", children: [
|
|
12255
|
+
showRefresh && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12256
|
+
/* @__PURE__ */ jsx("template", { "x-if": "!isRefreshing", children: /* @__PURE__ */ jsxs(
|
|
12257
|
+
"button",
|
|
12258
|
+
{
|
|
12259
|
+
type: "button",
|
|
12260
|
+
class: "font-medium rounded-lg text-sm p-1 w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out text-gray-500 border border-gray-300 hover:bg-gray-100 hover:text-gray-700 focus:ring-4 focus:outline-none focus:ring-gray-200 dark:border-gray-600 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:hover:bg-gray-600",
|
|
12261
|
+
title: "Refresh this version (re-scrape changed pages)",
|
|
12262
|
+
"x-on:click": "\n isRefreshing = true;\n $root.dataset.isRefreshing = 'true';\n $el.dispatchEvent(new CustomEvent('trigger-refresh', { bubbles: true }));\n ",
|
|
12263
|
+
"hx-post": `/web/libraries/${encodeURIComponent(libraryName)}/versions/${encodeURIComponent(versionParam)}/refresh`,
|
|
12264
|
+
"hx-swap": "none",
|
|
12265
|
+
"hx-trigger": "trigger-refresh",
|
|
12266
|
+
children: [
|
|
12267
|
+
/* @__PURE__ */ jsx(
|
|
12268
|
+
"svg",
|
|
12269
|
+
{
|
|
12270
|
+
class: "w-4 h-4",
|
|
12271
|
+
"aria-hidden": "true",
|
|
12272
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
12273
|
+
fill: "none",
|
|
12274
|
+
viewBox: "0 0 24 24",
|
|
12275
|
+
children: /* @__PURE__ */ jsx(
|
|
12276
|
+
"path",
|
|
12277
|
+
{
|
|
12278
|
+
stroke: "currentColor",
|
|
12279
|
+
"stroke-linecap": "round",
|
|
12280
|
+
"stroke-linejoin": "round",
|
|
12281
|
+
"stroke-width": "2",
|
|
12282
|
+
d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
12283
|
+
}
|
|
12284
|
+
)
|
|
12285
|
+
}
|
|
12286
|
+
),
|
|
12287
|
+
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Refresh version" })
|
|
12288
|
+
]
|
|
12289
|
+
}
|
|
12290
|
+
) }),
|
|
12291
|
+
/* @__PURE__ */ jsx("template", { "x-if": "isRefreshing", children: /* @__PURE__ */ jsxs(
|
|
12292
|
+
"button",
|
|
12293
|
+
{
|
|
12294
|
+
type: "button",
|
|
12295
|
+
class: "font-medium rounded-lg text-sm p-1 w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out text-gray-500 border border-gray-300 dark:border-gray-600 dark:text-gray-400",
|
|
12296
|
+
title: "Refresh in progress...",
|
|
12297
|
+
disabled: true,
|
|
12298
|
+
children: [
|
|
12299
|
+
/* @__PURE__ */ jsx(LoadingSpinner, { class: "text-gray-500 dark:text-gray-400" }),
|
|
12300
|
+
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Refreshing..." })
|
|
12301
|
+
]
|
|
12302
|
+
}
|
|
12303
|
+
) })
|
|
12304
|
+
] }),
|
|
12305
|
+
showDelete && /* @__PURE__ */ jsxs(
|
|
12306
|
+
"button",
|
|
12307
|
+
{
|
|
12308
|
+
type: "button",
|
|
12309
|
+
class: "font-medium rounded-lg text-sm p-1 min-w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out",
|
|
12310
|
+
title: "Remove this version",
|
|
12311
|
+
"x-bind:class": `confirming ? '${confirmingStateClasses}' : '${defaultStateClasses}'`,
|
|
12312
|
+
"x-bind:disabled": "isDeleting",
|
|
12313
|
+
"x-on:click": "\n if (confirming) {\n isDeleting = true;\n window.confirmationManager.clear($root.id);\n $el.dispatchEvent(new CustomEvent('confirmed-delete', { bubbles: true }));\n } else {\n confirming = true;\n isDeleting = false;\n window.confirmationManager.start($root.id);\n }\n ",
|
|
12314
|
+
"hx-delete": `/web/libraries/${encodeURIComponent(libraryName)}/versions/${encodeURIComponent(versionParam)}`,
|
|
12315
|
+
"hx-target": `#${rowId}`,
|
|
12316
|
+
"hx-swap": "outerHTML",
|
|
12317
|
+
"hx-trigger": "confirmed-delete",
|
|
12318
|
+
children: [
|
|
12319
|
+
/* @__PURE__ */ jsxs("span", { "x-show": "!confirming && !isDeleting", children: [
|
|
12320
|
+
/* @__PURE__ */ jsx(
|
|
12321
|
+
"svg",
|
|
12322
|
+
{
|
|
12323
|
+
class: "w-4 h-4",
|
|
12324
|
+
"aria-hidden": "true",
|
|
12325
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
12326
|
+
fill: "none",
|
|
12327
|
+
viewBox: "0 0 18 20",
|
|
12328
|
+
children: /* @__PURE__ */ jsx(
|
|
12329
|
+
"path",
|
|
12330
|
+
{
|
|
12331
|
+
stroke: "currentColor",
|
|
12332
|
+
"stroke-linecap": "round",
|
|
12333
|
+
"stroke-linejoin": "round",
|
|
12334
|
+
"stroke-width": "2",
|
|
12335
|
+
d: "M1 5h16M7 8v8m4-8v8M7 1h4a1 1 0 0 1 1 1v3H6V2a1 1 0 0 1-1-1ZM3 5h12v13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5Z"
|
|
12336
|
+
}
|
|
12337
|
+
)
|
|
12338
|
+
}
|
|
12339
|
+
),
|
|
12340
|
+
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Remove version" })
|
|
12341
|
+
] }),
|
|
12342
|
+
/* @__PURE__ */ jsxs("span", { "x-show": "confirming && !isDeleting", class: "mx-1", children: [
|
|
12343
|
+
"Confirm?",
|
|
12344
|
+
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Confirm delete" })
|
|
12345
|
+
] }),
|
|
12346
|
+
/* @__PURE__ */ jsxs("span", { "x-show": "isDeleting", children: [
|
|
12347
|
+
/* @__PURE__ */ jsx(LoadingSpinner, {}),
|
|
12348
|
+
/* @__PURE__ */ jsx("span", { class: "sr-only", children: "Loading..." })
|
|
12349
|
+
] })
|
|
12350
|
+
]
|
|
12351
|
+
}
|
|
12352
|
+
)
|
|
12353
|
+
] })
|
|
12275
12354
|
]
|
|
12276
12355
|
}
|
|
12277
12356
|
)
|
|
12278
12357
|
);
|
|
12279
12358
|
};
|
|
12280
12359
|
const LibraryDetailCard = ({ library }) => {
|
|
12281
|
-
const versions = library.versions
|
|
12360
|
+
const versions = library.versions || [];
|
|
12282
12361
|
const latestVersion = versions[0];
|
|
12283
|
-
return (
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
/* @__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 }) }),
|
|
12362
|
+
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: [
|
|
12363
|
+
/* @__PURE__ */ jsx("div", { class: "flex justify-between items-start mb-1", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
12364
|
+
/* @__PURE__ */ jsx("h3", { class: "text-lg font-medium text-gray-900 dark:text-white", children: /* @__PURE__ */ jsx("span", { safe: true, children: library.name }) }),
|
|
12287
12365
|
latestVersion?.sourceUrl ? /* @__PURE__ */ jsx("div", { class: "text-sm text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx(
|
|
12288
12366
|
"a",
|
|
12289
12367
|
{
|
|
@@ -12293,31 +12371,43 @@ const LibraryDetailCard = ({ library }) => {
|
|
|
12293
12371
|
safe: true,
|
|
12294
12372
|
children: latestVersion.sourceUrl
|
|
12295
12373
|
}
|
|
12296
|
-
) }) : null
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12374
|
+
) }) : null
|
|
12375
|
+
] }) }),
|
|
12376
|
+
/* @__PURE__ */ jsx(
|
|
12377
|
+
"div",
|
|
12378
|
+
{
|
|
12379
|
+
class: "mt-2",
|
|
12380
|
+
id: "version-list",
|
|
12381
|
+
"hx-get": `/web/libraries/${encodeURIComponent(library.name)}/versions-list`,
|
|
12382
|
+
"hx-trigger": "library-change from:body",
|
|
12383
|
+
"hx-swap": "morph:innerHTML",
|
|
12384
|
+
children: versions.length > 0 ? versions.map((v) => {
|
|
12385
|
+
const adapted = {
|
|
12386
|
+
id: -1,
|
|
12387
|
+
ref: { library: library.name, version: v.version },
|
|
12388
|
+
status: v.status,
|
|
12389
|
+
progress: v.progress,
|
|
12390
|
+
counts: {
|
|
12391
|
+
documents: v.documentCount,
|
|
12392
|
+
uniqueUrls: v.uniqueUrlCount
|
|
12393
|
+
},
|
|
12394
|
+
indexedAt: v.indexedAt,
|
|
12395
|
+
sourceUrl: v.sourceUrl ?? void 0
|
|
12396
|
+
};
|
|
12397
|
+
return /* @__PURE__ */ jsx(
|
|
12398
|
+
VersionDetailsRow,
|
|
12399
|
+
{
|
|
12400
|
+
libraryName: library.name,
|
|
12401
|
+
version: adapted,
|
|
12402
|
+
showDelete: true,
|
|
12403
|
+
showRefresh: true
|
|
12404
|
+
}
|
|
12405
|
+
);
|
|
12406
|
+
}) : /* @__PURE__ */ jsx("p", { class: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No versions indexed." })
|
|
12407
|
+
}
|
|
12408
|
+
),
|
|
12409
|
+
/* @__PURE__ */ jsx("div", { id: "add-version-form-container", class: "mt-4", children: /* @__PURE__ */ jsx(AddVersionButton, { libraryName: library.name }) })
|
|
12410
|
+
] });
|
|
12321
12411
|
};
|
|
12322
12412
|
const LibrarySearchCard = ({ library }) => {
|
|
12323
12413
|
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: [
|
|
@@ -12343,7 +12433,7 @@ const LibrarySearchCard = ({ library }) => {
|
|
|
12343
12433
|
children: [
|
|
12344
12434
|
/* @__PURE__ */ jsx("option", { value: "", children: "Latest" }),
|
|
12345
12435
|
" ",
|
|
12346
|
-
library.versions.map((version) => /* @__PURE__ */ jsx("option", { value: version.version || "
|
|
12436
|
+
library.versions.map((version) => /* @__PURE__ */ jsx("option", { value: version.version || "latest", safe: true, children: version.version || "Latest" }))
|
|
12347
12437
|
]
|
|
12348
12438
|
}
|
|
12349
12439
|
),
|
|
@@ -12417,7 +12507,7 @@ const SearchResultSkeletonItem = () => /* @__PURE__ */ jsxs("div", { class: "blo
|
|
|
12417
12507
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 dark:bg-gray-700 rounded w-full mb-2" }),
|
|
12418
12508
|
/* @__PURE__ */ jsx("div", { class: "h-[0.8em] bg-gray-200 dark:bg-gray-700 rounded w-5/6" })
|
|
12419
12509
|
] });
|
|
12420
|
-
function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool) {
|
|
12510
|
+
function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool, scrapeTool, docService) {
|
|
12421
12511
|
server.get(
|
|
12422
12512
|
"/libraries/:libraryName",
|
|
12423
12513
|
async (request, reply) => {
|
|
@@ -12425,7 +12515,7 @@ function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool) {
|
|
|
12425
12515
|
try {
|
|
12426
12516
|
const result = await listLibrariesTool.execute();
|
|
12427
12517
|
const libraryInfo = result.libraries.find(
|
|
12428
|
-
(lib) => lib.name === libraryName
|
|
12518
|
+
(lib) => lib.name.toLowerCase() === libraryName.toLowerCase()
|
|
12429
12519
|
);
|
|
12430
12520
|
if (!libraryInfo) {
|
|
12431
12521
|
reply.status(404).send("Library not found");
|
|
@@ -12462,7 +12552,7 @@ function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool) {
|
|
|
12462
12552
|
reply.status(400).send("Search query is required.");
|
|
12463
12553
|
return;
|
|
12464
12554
|
}
|
|
12465
|
-
const versionParam = version === "
|
|
12555
|
+
const versionParam = version === "latest" ? void 0 : version;
|
|
12466
12556
|
try {
|
|
12467
12557
|
const searchResult = await searchTool.execute({
|
|
12468
12558
|
library: libraryName,
|
|
@@ -12481,56 +12571,217 @@ function registerLibraryDetailRoutes(server, listLibrariesTool, searchTool) {
|
|
|
12481
12571
|
}
|
|
12482
12572
|
}
|
|
12483
12573
|
);
|
|
12574
|
+
server.get(
|
|
12575
|
+
"/web/libraries/:libraryName/versions-list",
|
|
12576
|
+
async (request, reply) => {
|
|
12577
|
+
const { libraryName } = request.params;
|
|
12578
|
+
try {
|
|
12579
|
+
const result = await listLibrariesTool.execute();
|
|
12580
|
+
const libraryInfo = result.libraries.find(
|
|
12581
|
+
(lib) => lib.name.toLowerCase() === libraryName.toLowerCase()
|
|
12582
|
+
);
|
|
12583
|
+
if (!libraryInfo) {
|
|
12584
|
+
reply.status(404).send("Library not found");
|
|
12585
|
+
return;
|
|
12586
|
+
}
|
|
12587
|
+
const versions = libraryInfo.versions || [];
|
|
12588
|
+
reply.type("text/html; charset=utf-8");
|
|
12589
|
+
if (versions.length === 0) {
|
|
12590
|
+
return /* @__PURE__ */ jsx("p", { class: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No versions indexed." });
|
|
12591
|
+
}
|
|
12592
|
+
return /* @__PURE__ */ jsx(Fragment, { children: versions.map((v) => {
|
|
12593
|
+
const adapted = {
|
|
12594
|
+
id: -1,
|
|
12595
|
+
ref: { library: libraryInfo.name, version: v.version },
|
|
12596
|
+
status: v.status,
|
|
12597
|
+
progress: v.progress,
|
|
12598
|
+
counts: {
|
|
12599
|
+
documents: v.documentCount,
|
|
12600
|
+
uniqueUrls: v.uniqueUrlCount
|
|
12601
|
+
},
|
|
12602
|
+
indexedAt: v.indexedAt,
|
|
12603
|
+
sourceUrl: v.sourceUrl ?? void 0
|
|
12604
|
+
};
|
|
12605
|
+
return /* @__PURE__ */ jsx(
|
|
12606
|
+
VersionDetailsRow,
|
|
12607
|
+
{
|
|
12608
|
+
libraryName: libraryInfo.name,
|
|
12609
|
+
version: adapted,
|
|
12610
|
+
showDelete: true,
|
|
12611
|
+
showRefresh: true
|
|
12612
|
+
}
|
|
12613
|
+
);
|
|
12614
|
+
}) });
|
|
12615
|
+
} catch (error) {
|
|
12616
|
+
logger.error(`Failed to fetch versions for ${libraryName}: ${error}`);
|
|
12617
|
+
reply.status(500).send("Internal Server Error");
|
|
12618
|
+
}
|
|
12619
|
+
}
|
|
12620
|
+
);
|
|
12621
|
+
server.get(
|
|
12622
|
+
"/web/libraries/:libraryName/add-version-button",
|
|
12623
|
+
async (request, reply) => {
|
|
12624
|
+
const { libraryName } = request.params;
|
|
12625
|
+
reply.type("text/html; charset=utf-8");
|
|
12626
|
+
return /* @__PURE__ */ jsx(AddVersionButton, { libraryName });
|
|
12627
|
+
}
|
|
12628
|
+
);
|
|
12629
|
+
server.get(
|
|
12630
|
+
"/web/libraries/:libraryName/add-version-form",
|
|
12631
|
+
async (request, reply) => {
|
|
12632
|
+
const { libraryName } = request.params;
|
|
12633
|
+
try {
|
|
12634
|
+
const result = await listLibrariesTool.execute();
|
|
12635
|
+
const libraryInfo = result.libraries.find(
|
|
12636
|
+
(lib) => lib.name.toLowerCase() === libraryName.toLowerCase()
|
|
12637
|
+
);
|
|
12638
|
+
if (!libraryInfo) {
|
|
12639
|
+
reply.status(404).send("Library not found");
|
|
12640
|
+
return;
|
|
12641
|
+
}
|
|
12642
|
+
const versions = libraryInfo.versions || [];
|
|
12643
|
+
const latestVersion = versions[0];
|
|
12644
|
+
let initialValues = {
|
|
12645
|
+
library: libraryName
|
|
12646
|
+
};
|
|
12647
|
+
if (latestVersion) {
|
|
12648
|
+
const summaries = await docService.listLibraries();
|
|
12649
|
+
const libSummary = summaries.find(
|
|
12650
|
+
(s) => s.library.toLowerCase() === libraryName.toLowerCase()
|
|
12651
|
+
);
|
|
12652
|
+
if (libSummary) {
|
|
12653
|
+
const versionSummary = libSummary.versions.find(
|
|
12654
|
+
(v) => v.ref.version === (latestVersion.version || "") || !latestVersion.version && v.ref.version === ""
|
|
12655
|
+
);
|
|
12656
|
+
if (versionSummary) {
|
|
12657
|
+
const scraperConfig = await docService.getScraperOptions(
|
|
12658
|
+
versionSummary.id
|
|
12659
|
+
);
|
|
12660
|
+
if (scraperConfig) {
|
|
12661
|
+
const opts = scraperConfig.options;
|
|
12662
|
+
initialValues = {
|
|
12663
|
+
library: libraryName,
|
|
12664
|
+
url: scraperConfig.sourceUrl,
|
|
12665
|
+
maxPages: opts.maxPages,
|
|
12666
|
+
maxDepth: opts.maxDepth,
|
|
12667
|
+
scope: opts.scope,
|
|
12668
|
+
includePatterns: opts.includePatterns?.join("\n"),
|
|
12669
|
+
excludePatterns: opts.excludePatterns?.join("\n"),
|
|
12670
|
+
scrapeMode: opts.scrapeMode,
|
|
12671
|
+
headers: opts.headers ? Object.entries(opts.headers).map(([name, value]) => ({
|
|
12672
|
+
name,
|
|
12673
|
+
value
|
|
12674
|
+
})) : void 0,
|
|
12675
|
+
followRedirects: opts.followRedirects,
|
|
12676
|
+
ignoreErrors: opts.ignoreErrors
|
|
12677
|
+
};
|
|
12678
|
+
}
|
|
12679
|
+
}
|
|
12680
|
+
}
|
|
12681
|
+
}
|
|
12682
|
+
reply.type("text/html; charset=utf-8");
|
|
12683
|
+
return /* @__PURE__ */ jsx(ScrapeFormContent, { initialValues, mode: "add-version" });
|
|
12684
|
+
} catch (error) {
|
|
12685
|
+
logger.error(
|
|
12686
|
+
`Failed to load add-version form for ${libraryName}: ${error}`
|
|
12687
|
+
);
|
|
12688
|
+
reply.type("text/html; charset=utf-8");
|
|
12689
|
+
return /* @__PURE__ */ jsx(Alert, { type: "error", message: "Failed to load the add version form." });
|
|
12690
|
+
}
|
|
12691
|
+
}
|
|
12692
|
+
);
|
|
12484
12693
|
}
|
|
12485
12694
|
const LibraryItem = ({ library }) => {
|
|
12486
|
-
const versions = library.versions
|
|
12695
|
+
const versions = library.versions || [];
|
|
12487
12696
|
const latestVersion = versions[0];
|
|
12488
12697
|
return (
|
|
12489
12698
|
// Use Flowbite Card structure with updated padding and border, and white background
|
|
12490
|
-
/* @__PURE__ */ jsxs(
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
{
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
class: "
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12699
|
+
/* @__PURE__ */ jsxs(
|
|
12700
|
+
"div",
|
|
12701
|
+
{
|
|
12702
|
+
id: `library-item-${library.name}`,
|
|
12703
|
+
class: "block px-4 py-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600",
|
|
12704
|
+
children: [
|
|
12705
|
+
/* @__PURE__ */ jsx("h3", { class: "text-lg font-medium text-gray-900 dark:text-white", children: /* @__PURE__ */ jsx(
|
|
12706
|
+
"a",
|
|
12707
|
+
{
|
|
12708
|
+
href: `/libraries/${encodeURIComponent(library.name)}`,
|
|
12709
|
+
class: "hover:underline",
|
|
12710
|
+
children: /* @__PURE__ */ jsx("span", { safe: true, children: library.name })
|
|
12711
|
+
}
|
|
12712
|
+
) }),
|
|
12713
|
+
latestVersion?.sourceUrl ? /* @__PURE__ */ jsx("div", { class: "text-sm text-gray-500 dark:text-gray-400 overflow-hidden h-5 @container", children: /* @__PURE__ */ jsx(
|
|
12714
|
+
"a",
|
|
12715
|
+
{
|
|
12716
|
+
href: latestVersion.sourceUrl,
|
|
12717
|
+
target: "_blank",
|
|
12718
|
+
class: "inline-block whitespace-nowrap hover:underline hover:animate-[scrollText_2s_ease-in-out_forwards]",
|
|
12719
|
+
title: latestVersion.sourceUrl,
|
|
12720
|
+
safe: true,
|
|
12721
|
+
children: latestVersion.sourceUrl
|
|
12722
|
+
}
|
|
12723
|
+
) }) : null,
|
|
12724
|
+
/* @__PURE__ */ jsx("div", { class: "mt-2", children: versions.length > 0 ? versions.map((v) => {
|
|
12725
|
+
const adapted = {
|
|
12726
|
+
id: -1,
|
|
12727
|
+
ref: { library: library.name, version: v.version },
|
|
12728
|
+
status: v.status,
|
|
12729
|
+
progress: v.progress,
|
|
12730
|
+
counts: {
|
|
12731
|
+
documents: v.documentCount,
|
|
12732
|
+
uniqueUrls: v.uniqueUrlCount
|
|
12733
|
+
},
|
|
12734
|
+
indexedAt: v.indexedAt,
|
|
12735
|
+
sourceUrl: v.sourceUrl ?? void 0
|
|
12736
|
+
};
|
|
12737
|
+
return /* @__PURE__ */ jsx(VersionDetailsRow, { libraryName: library.name, version: adapted });
|
|
12738
|
+
}) : (
|
|
12739
|
+
// Display message if no versions are indexed
|
|
12740
|
+
/* @__PURE__ */ jsx("p", { class: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No versions indexed." })
|
|
12741
|
+
) })
|
|
12742
|
+
]
|
|
12743
|
+
}
|
|
12744
|
+
)
|
|
12528
12745
|
);
|
|
12529
12746
|
};
|
|
12530
12747
|
const LibraryList = ({ libraries }) => {
|
|
12531
|
-
|
|
12748
|
+
if (libraries.length === 0) {
|
|
12749
|
+
return /* @__PURE__ */ jsx(
|
|
12750
|
+
Alert,
|
|
12751
|
+
{
|
|
12752
|
+
type: "info",
|
|
12753
|
+
title: "Welcome!",
|
|
12754
|
+
message: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12755
|
+
"To get started, click",
|
|
12756
|
+
" ",
|
|
12757
|
+
/* @__PURE__ */ jsx("span", { class: "font-semibold", children: "Add New Documentation" }),
|
|
12758
|
+
" above and enter the URL of a documentation site to index. For more information, check the",
|
|
12759
|
+
" ",
|
|
12760
|
+
/* @__PURE__ */ jsx(
|
|
12761
|
+
"a",
|
|
12762
|
+
{
|
|
12763
|
+
href: "https://grounded.tools",
|
|
12764
|
+
target: "_blank",
|
|
12765
|
+
rel: "noopener noreferrer",
|
|
12766
|
+
class: "font-medium underline hover:no-underline",
|
|
12767
|
+
children: "official website"
|
|
12768
|
+
}
|
|
12769
|
+
),
|
|
12770
|
+
"."
|
|
12771
|
+
] })
|
|
12772
|
+
}
|
|
12773
|
+
);
|
|
12774
|
+
}
|
|
12775
|
+
return /* @__PURE__ */ jsx(
|
|
12776
|
+
"div",
|
|
12777
|
+
{
|
|
12778
|
+
id: "library-list",
|
|
12779
|
+
class: "space-y-2 animate-[fadeSlideIn_0.2s_ease-out]",
|
|
12780
|
+
children: libraries.map((library) => /* @__PURE__ */ jsx(LibraryItem, { library }))
|
|
12781
|
+
}
|
|
12782
|
+
);
|
|
12532
12783
|
};
|
|
12533
|
-
function registerLibrariesRoutes(server, listLibrariesTool, removeTool) {
|
|
12784
|
+
function registerLibrariesRoutes(server, listLibrariesTool, removeTool, refreshVersionTool) {
|
|
12534
12785
|
server.get("/web/libraries", async (_request, reply) => {
|
|
12535
12786
|
try {
|
|
12536
12787
|
const result = await listLibrariesTool.execute();
|
|
@@ -12545,35 +12796,159 @@ function registerLibrariesRoutes(server, listLibrariesTool, removeTool) {
|
|
|
12545
12796
|
"/web/libraries/:libraryName/versions/:versionParam",
|
|
12546
12797
|
async (request, reply) => {
|
|
12547
12798
|
const { libraryName, versionParam } = request.params;
|
|
12548
|
-
const version = versionParam === "
|
|
12799
|
+
const version = versionParam === "latest" ? void 0 : versionParam;
|
|
12800
|
+
try {
|
|
12801
|
+
await removeTool.execute({ library: libraryName, version });
|
|
12802
|
+
const result = await listLibrariesTool.execute();
|
|
12803
|
+
const libraryStillExists = result.libraries.some(
|
|
12804
|
+
(lib) => lib.name.toLowerCase() === libraryName.toLowerCase()
|
|
12805
|
+
);
|
|
12806
|
+
if (!libraryStillExists) {
|
|
12807
|
+
reply.header("HX-Redirect", "/");
|
|
12808
|
+
}
|
|
12809
|
+
reply.status(204).send();
|
|
12810
|
+
} catch (error) {
|
|
12811
|
+
logger.error(
|
|
12812
|
+
`Failed to remove ${libraryName}@${versionParam}: ${error}`
|
|
12813
|
+
);
|
|
12814
|
+
reply.status(500).send({ message: error.message || "Failed to remove version." });
|
|
12815
|
+
}
|
|
12816
|
+
}
|
|
12817
|
+
);
|
|
12818
|
+
server.post(
|
|
12819
|
+
"/web/libraries/:libraryName/versions/:versionParam/refresh",
|
|
12820
|
+
async (request, reply) => {
|
|
12821
|
+
const { libraryName, versionParam } = request.params;
|
|
12822
|
+
const version = versionParam === "latest" || versionParam === "" ? void 0 : versionParam;
|
|
12549
12823
|
try {
|
|
12550
|
-
await
|
|
12824
|
+
await refreshVersionTool.execute({
|
|
12825
|
+
library: libraryName,
|
|
12826
|
+
version,
|
|
12827
|
+
waitForCompletion: false
|
|
12828
|
+
});
|
|
12829
|
+
const versionDisplay = version || "latest";
|
|
12830
|
+
reply.header(
|
|
12831
|
+
"HX-Trigger",
|
|
12832
|
+
JSON.stringify({
|
|
12833
|
+
toast: {
|
|
12834
|
+
message: `Refresh started for ${libraryName}@${versionDisplay}`,
|
|
12835
|
+
type: "success"
|
|
12836
|
+
}
|
|
12837
|
+
})
|
|
12838
|
+
);
|
|
12551
12839
|
reply.status(204).send();
|
|
12552
12840
|
} catch (error) {
|
|
12553
12841
|
logger.error(
|
|
12554
|
-
`Failed to
|
|
12842
|
+
`Failed to refresh ${libraryName}@${versionParam}: ${error}`
|
|
12555
12843
|
);
|
|
12556
|
-
|
|
12844
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to refresh version.";
|
|
12845
|
+
reply.header(
|
|
12846
|
+
"HX-Trigger",
|
|
12847
|
+
JSON.stringify({
|
|
12848
|
+
toast: {
|
|
12849
|
+
message: errorMessage,
|
|
12850
|
+
type: "error"
|
|
12851
|
+
}
|
|
12852
|
+
})
|
|
12853
|
+
);
|
|
12854
|
+
reply.status(500).send();
|
|
12557
12855
|
}
|
|
12558
12856
|
}
|
|
12559
12857
|
);
|
|
12560
12858
|
}
|
|
12859
|
+
function formatNumber(num) {
|
|
12860
|
+
if (num >= 1e9) {
|
|
12861
|
+
return `${(num / 1e9).toFixed(1)}B`;
|
|
12862
|
+
}
|
|
12863
|
+
if (num >= 1e6) {
|
|
12864
|
+
return `${(num / 1e6).toFixed(1)}M`;
|
|
12865
|
+
}
|
|
12866
|
+
if (num >= 1e3) {
|
|
12867
|
+
return `${(num / 1e3).toFixed(1)}K`;
|
|
12868
|
+
}
|
|
12869
|
+
return num.toString();
|
|
12870
|
+
}
|
|
12871
|
+
const AnalyticsCards = ({
|
|
12872
|
+
totalChunks,
|
|
12873
|
+
activeLibraries,
|
|
12874
|
+
activeVersions,
|
|
12875
|
+
indexedPages
|
|
12876
|
+
}) => /* @__PURE__ */ jsxs("div", { class: "grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4 animate-[fadeSlideIn_0.2s_ease-out]", children: [
|
|
12877
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600", children: /* @__PURE__ */ jsx("div", { class: "flex items-center", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
12878
|
+
/* @__PURE__ */ jsx("p", { class: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Total Knowledge Base" }),
|
|
12879
|
+
/* @__PURE__ */ jsxs("p", { class: "text-xl font-semibold text-gray-900 dark:text-white", safe: true, children: [
|
|
12880
|
+
formatNumber(totalChunks),
|
|
12881
|
+
" Chunks"
|
|
12882
|
+
] })
|
|
12883
|
+
] }) }) }),
|
|
12884
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600", children: /* @__PURE__ */ jsx("div", { class: "flex items-center", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
12885
|
+
/* @__PURE__ */ jsx("p", { class: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Libraries / Versions" }),
|
|
12886
|
+
/* @__PURE__ */ jsxs("p", { class: "text-xl font-semibold text-gray-900 dark:text-white", children: [
|
|
12887
|
+
activeLibraries,
|
|
12888
|
+
" / ",
|
|
12889
|
+
activeVersions
|
|
12890
|
+
] })
|
|
12891
|
+
] }) }) }),
|
|
12892
|
+
/* @__PURE__ */ jsx("div", { class: "p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600", children: /* @__PURE__ */ jsx("div", { class: "flex items-center", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
12893
|
+
/* @__PURE__ */ jsx("p", { class: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Indexed Pages" }),
|
|
12894
|
+
/* @__PURE__ */ jsx("p", { class: "text-xl font-semibold text-gray-900 dark:text-white", safe: true, children: formatNumber(indexedPages) })
|
|
12895
|
+
] }) }) })
|
|
12896
|
+
] });
|
|
12897
|
+
function registerStatsRoute(server, docService) {
|
|
12898
|
+
server.get("/web/stats", async (_request, reply) => {
|
|
12899
|
+
try {
|
|
12900
|
+
const libraries = await docService.listLibraries();
|
|
12901
|
+
let totalChunks = 0;
|
|
12902
|
+
let indexedPages = 0;
|
|
12903
|
+
let activeVersions = 0;
|
|
12904
|
+
for (const lib of libraries) {
|
|
12905
|
+
activeVersions += lib.versions.length;
|
|
12906
|
+
for (const version of lib.versions) {
|
|
12907
|
+
totalChunks += version.counts.documents;
|
|
12908
|
+
indexedPages += version.counts.uniqueUrls;
|
|
12909
|
+
}
|
|
12910
|
+
}
|
|
12911
|
+
const activeLibraries = libraries.length;
|
|
12912
|
+
reply.type("text/html; charset=utf-8");
|
|
12913
|
+
return /* @__PURE__ */ jsx(
|
|
12914
|
+
AnalyticsCards,
|
|
12915
|
+
{
|
|
12916
|
+
totalChunks,
|
|
12917
|
+
activeLibraries,
|
|
12918
|
+
activeVersions,
|
|
12919
|
+
indexedPages
|
|
12920
|
+
}
|
|
12921
|
+
);
|
|
12922
|
+
} catch (error) {
|
|
12923
|
+
logger.error(`Failed to fetch stats: ${error}`);
|
|
12924
|
+
reply.status(500).send("Internal Server Error");
|
|
12925
|
+
}
|
|
12926
|
+
});
|
|
12927
|
+
}
|
|
12561
12928
|
async function registerWebService(server, docService, pipeline, eventBus, config) {
|
|
12562
12929
|
const listLibrariesTool = new ListLibrariesTool(docService);
|
|
12563
12930
|
const listJobsTool = new ListJobsTool(pipeline);
|
|
12564
12931
|
const scrapeTool = new ScrapeTool(pipeline);
|
|
12565
12932
|
const removeTool = new RemoveTool(docService, pipeline);
|
|
12933
|
+
const refreshVersionTool = new RefreshVersionTool(pipeline);
|
|
12566
12934
|
const searchTool = new SearchTool(docService);
|
|
12567
12935
|
const cancelJobTool = new CancelJobTool(pipeline);
|
|
12568
12936
|
const clearCompletedJobsTool = new ClearCompletedJobsTool(pipeline);
|
|
12569
12937
|
registerIndexRoute(server, config);
|
|
12570
|
-
registerLibrariesRoutes(server, listLibrariesTool, removeTool);
|
|
12571
|
-
registerLibraryDetailRoutes(
|
|
12938
|
+
registerLibrariesRoutes(server, listLibrariesTool, removeTool, refreshVersionTool);
|
|
12939
|
+
registerLibraryDetailRoutes(
|
|
12940
|
+
server,
|
|
12941
|
+
listLibrariesTool,
|
|
12942
|
+
searchTool,
|
|
12943
|
+
scrapeTool,
|
|
12944
|
+
docService
|
|
12945
|
+
);
|
|
12572
12946
|
registerJobListRoutes(server, listJobsTool);
|
|
12573
12947
|
registerNewJobRoutes(server, scrapeTool);
|
|
12574
12948
|
registerCancelJobRoute(server, cancelJobTool);
|
|
12575
12949
|
registerClearCompletedJobsRoute(server, clearCompletedJobsTool);
|
|
12576
12950
|
registerEventsRoute(server, eventBus);
|
|
12951
|
+
registerStatsRoute(server, docService);
|
|
12577
12952
|
}
|
|
12578
12953
|
async function registerWorkerService(pipeline) {
|
|
12579
12954
|
await pipeline.start();
|
|
@@ -12598,7 +12973,6 @@ class AppServer {
|
|
|
12598
12973
|
mcpServer = null;
|
|
12599
12974
|
authManager = null;
|
|
12600
12975
|
config;
|
|
12601
|
-
embeddingConfig = null;
|
|
12602
12976
|
remoteEventProxy = null;
|
|
12603
12977
|
wss = null;
|
|
12604
12978
|
/**
|
|
@@ -12625,22 +12999,22 @@ class AppServer {
|
|
|
12625
12999
|
*/
|
|
12626
13000
|
async start() {
|
|
12627
13001
|
this.validateConfig();
|
|
12628
|
-
|
|
13002
|
+
const embeddingConfig = this.docService.getActiveEmbeddingConfig();
|
|
12629
13003
|
if (this.config.telemetry !== false && shouldEnableTelemetry()) {
|
|
12630
13004
|
try {
|
|
12631
13005
|
if (telemetry.isEnabled()) {
|
|
12632
13006
|
telemetry.setGlobalContext({
|
|
12633
|
-
appVersion: "1.
|
|
13007
|
+
appVersion: "1.31.0",
|
|
12634
13008
|
appPlatform: process.platform,
|
|
12635
13009
|
appNodeVersion: process.version,
|
|
12636
13010
|
appServicesEnabled: this.getActiveServicesList(),
|
|
12637
13011
|
appAuthEnabled: Boolean(this.config.auth),
|
|
12638
13012
|
appReadOnly: Boolean(this.config.readOnly),
|
|
12639
13013
|
// Add embedding configuration to global context
|
|
12640
|
-
...
|
|
12641
|
-
aiEmbeddingProvider:
|
|
12642
|
-
aiEmbeddingModel:
|
|
12643
|
-
aiEmbeddingDimensions:
|
|
13014
|
+
...embeddingConfig && {
|
|
13015
|
+
aiEmbeddingProvider: embeddingConfig.provider,
|
|
13016
|
+
aiEmbeddingModel: embeddingConfig.model,
|
|
13017
|
+
aiEmbeddingDimensions: embeddingConfig.dimensions
|
|
12644
13018
|
}
|
|
12645
13019
|
});
|
|
12646
13020
|
telemetry.track(TelemetryEvent.APP_STARTED, {
|
|
@@ -12944,28 +13318,38 @@ class AppServer {
|
|
|
12944
13318
|
* Log startup information showing which services are enabled.
|
|
12945
13319
|
*/
|
|
12946
13320
|
logStartupInfo(address) {
|
|
12947
|
-
|
|
13321
|
+
const isWorkerOnly = this.config.enableWorker && !this.config.enableWebInterface && !this.config.enableMcpServer;
|
|
13322
|
+
const isWebOnly = this.config.enableWebInterface && !this.config.enableWorker && !this.config.enableMcpServer;
|
|
13323
|
+
const isMcpOnly = this.config.enableMcpServer && !this.config.enableWebInterface && !this.config.enableWorker;
|
|
13324
|
+
if (isWorkerOnly) {
|
|
13325
|
+
logger.info(`🚀 Worker available at ${address}`);
|
|
13326
|
+
} else if (isWebOnly) {
|
|
13327
|
+
logger.info(`🚀 Web interface available at ${address}`);
|
|
13328
|
+
} else if (isMcpOnly) {
|
|
13329
|
+
logger.info(`🚀 MCP server available at ${address}`);
|
|
13330
|
+
} else {
|
|
13331
|
+
logger.info(`🚀 Grounded Docs available at ${address}`);
|
|
13332
|
+
}
|
|
13333
|
+
const isCombined = !isWorkerOnly && !isWebOnly && !isMcpOnly;
|
|
12948
13334
|
const enabledServices = [];
|
|
12949
|
-
if (this.config.enableWebInterface) {
|
|
13335
|
+
if (this.config.enableWebInterface && isCombined) {
|
|
12950
13336
|
enabledServices.push(`Web interface: ${address}`);
|
|
12951
13337
|
}
|
|
12952
13338
|
if (this.config.enableMcpServer) {
|
|
12953
13339
|
enabledServices.push(`MCP endpoints: ${address}/mcp, ${address}/sse`);
|
|
12954
13340
|
}
|
|
12955
|
-
if (this.config.
|
|
12956
|
-
enabledServices.push(`API: ${address}/api`);
|
|
12957
|
-
}
|
|
12958
|
-
if (this.config.enableWorker) {
|
|
12959
|
-
enabledServices.push("Worker: internal");
|
|
12960
|
-
} else if (this.config.externalWorkerUrl) {
|
|
13341
|
+
if (!this.config.enableWorker && this.config.externalWorkerUrl) {
|
|
12961
13342
|
enabledServices.push(`Worker: ${this.config.externalWorkerUrl}`);
|
|
12962
13343
|
}
|
|
12963
|
-
if (this.
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
13344
|
+
if (this.config.enableWorker) {
|
|
13345
|
+
const embeddingConfig = this.docService.getActiveEmbeddingConfig();
|
|
13346
|
+
if (embeddingConfig) {
|
|
13347
|
+
enabledServices.push(
|
|
13348
|
+
`Embeddings: ${embeddingConfig.provider}:${embeddingConfig.model}`
|
|
13349
|
+
);
|
|
13350
|
+
} else {
|
|
13351
|
+
enabledServices.push(`Embeddings: disabled (full text search only)`);
|
|
13352
|
+
}
|
|
12969
13353
|
}
|
|
12970
13354
|
for (const service of enabledServices) {
|
|
12971
13355
|
logger.info(` • ${service}`);
|
|
@@ -14284,11 +14668,11 @@ class PipelineWorker {
|
|
|
14284
14668
|
if (!scraperOptions.isRefresh) {
|
|
14285
14669
|
await this.store.removeAllDocuments(library, version);
|
|
14286
14670
|
logger.info(
|
|
14287
|
-
`💾 Cleared store for ${library}@${version || "
|
|
14671
|
+
`💾 Cleared store for ${library}@${version || "latest"} before scraping.`
|
|
14288
14672
|
);
|
|
14289
14673
|
} else {
|
|
14290
14674
|
logger.info(
|
|
14291
|
-
`🔄 Refresh operation - preserving existing data for ${library}@${version || "
|
|
14675
|
+
`🔄 Refresh operation - preserving existing data for ${library}@${version || "latest"}.`
|
|
14292
14676
|
);
|
|
14293
14677
|
}
|
|
14294
14678
|
await this.scraperService.scrape(
|
|
@@ -14440,7 +14824,7 @@ class PipelineManager {
|
|
|
14440
14824
|
for (const version of runningVersions) {
|
|
14441
14825
|
await this.store.updateVersionStatus(version.id, VersionStatus.QUEUED);
|
|
14442
14826
|
logger.info(
|
|
14443
|
-
`🔄 Reset interrupted job to QUEUED: ${version.library_name}@${version.name || "
|
|
14827
|
+
`🔄 Reset interrupted job to QUEUED: ${version.library_name}@${version.name || "latest"}`
|
|
14444
14828
|
);
|
|
14445
14829
|
}
|
|
14446
14830
|
const queuedVersions = await this.store.getVersionsByStatus([VersionStatus.QUEUED]);
|
|
@@ -14461,7 +14845,7 @@ class PipelineManager {
|
|
|
14461
14845
|
parsedScraperOptions = JSON.parse(version.scraper_options);
|
|
14462
14846
|
} catch (error) {
|
|
14463
14847
|
logger.warn(
|
|
14464
|
-
`⚠️
|
|
14848
|
+
`⚠️ Failed to parse scraper options for ${version.library_name}@${version.name || "latest"}: ${error}`
|
|
14465
14849
|
);
|
|
14466
14850
|
}
|
|
14467
14851
|
}
|
|
@@ -14568,7 +14952,7 @@ class PipelineManager {
|
|
|
14568
14952
|
this.jobMap.set(jobId, job);
|
|
14569
14953
|
this.jobQueue.push(jobId);
|
|
14570
14954
|
logger.info(
|
|
14571
|
-
`📝 Job enqueued: ${jobId} for ${library}${normalizedVersion ? `@${normalizedVersion}` : " (
|
|
14955
|
+
`📝 Job enqueued: ${jobId} for ${library}${normalizedVersion ? `@${normalizedVersion}` : " (latest)"}`
|
|
14572
14956
|
);
|
|
14573
14957
|
await this.updateJobStatus(job, PipelineJobStatus.QUEUED);
|
|
14574
14958
|
if (this.isRunning) {
|
|
@@ -14602,7 +14986,7 @@ class PipelineManager {
|
|
|
14602
14986
|
}
|
|
14603
14987
|
if (versionInfo && versionInfo.status !== VersionStatus.COMPLETED) {
|
|
14604
14988
|
logger.info(
|
|
14605
|
-
`⚠️ Version ${library}@${normalizedVersion || "
|
|
14989
|
+
`⚠️ Version ${library}@${normalizedVersion || "latest"} has status "${versionInfo.status}". Performing full re-scrape instead of refresh.`
|
|
14606
14990
|
);
|
|
14607
14991
|
return this.enqueueJobWithStoredOptions(library, normalizedVersion);
|
|
14608
14992
|
}
|
|
@@ -14614,11 +14998,11 @@ class PipelineManager {
|
|
|
14614
14998
|
}
|
|
14615
14999
|
if (pages.length === 0) {
|
|
14616
15000
|
throw new Error(
|
|
14617
|
-
`No pages found for ${library}@${normalizedVersion || "
|
|
15001
|
+
`No pages found for ${library}@${normalizedVersion || "latest"}. Use scrape_docs to index it first.`
|
|
14618
15002
|
);
|
|
14619
15003
|
}
|
|
14620
15004
|
logger.info(
|
|
14621
|
-
`🔄 Preparing refresh job for ${library}@${normalizedVersion || "
|
|
15005
|
+
`🔄 Preparing refresh job for ${library}@${normalizedVersion || "latest"} with ${pages.length} page(s)`
|
|
14622
15006
|
);
|
|
14623
15007
|
const initialQueue = pages.map((page) => ({
|
|
14624
15008
|
url: page.url,
|
|
@@ -14642,7 +15026,7 @@ class PipelineManager {
|
|
|
14642
15026
|
// Mark this as a refresh operation
|
|
14643
15027
|
};
|
|
14644
15028
|
logger.info(
|
|
14645
|
-
`📝 Enqueueing refresh job for ${library}@${normalizedVersion || "
|
|
15029
|
+
`📝 Enqueueing refresh job for ${library}@${normalizedVersion || "latest"}`
|
|
14646
15030
|
);
|
|
14647
15031
|
return this.enqueueScrapeJob(library, normalizedVersion, scraperOptions);
|
|
14648
15032
|
} catch (error) {
|
|
@@ -14664,7 +15048,7 @@ class PipelineManager {
|
|
|
14664
15048
|
const stored = await this.store.getScraperOptions(versionId);
|
|
14665
15049
|
if (!stored) {
|
|
14666
15050
|
throw new Error(
|
|
14667
|
-
`No stored scraper options found for ${library}@${normalizedVersion || "
|
|
15051
|
+
`No stored scraper options found for ${library}@${normalizedVersion || "latest"}`
|
|
14668
15052
|
);
|
|
14669
15053
|
}
|
|
14670
15054
|
const storedOptions = stored.options;
|
|
@@ -14675,7 +15059,7 @@ class PipelineManager {
|
|
|
14675
15059
|
...storedOptions
|
|
14676
15060
|
};
|
|
14677
15061
|
logger.info(
|
|
14678
|
-
`🔄 Re-indexing ${library}@${normalizedVersion || "
|
|
15062
|
+
`🔄 Re-indexing ${library}@${normalizedVersion || "latest"} with stored options from ${stored.sourceUrl}`
|
|
14679
15063
|
);
|
|
14680
15064
|
return this.enqueueScrapeJob(library, normalizedVersion, completeOptions);
|
|
14681
15065
|
} catch (error) {
|
|
@@ -14829,7 +15213,7 @@ class PipelineManager {
|
|
|
14829
15213
|
},
|
|
14830
15214
|
onJobError: async (internalJob, error, document2) => {
|
|
14831
15215
|
logger.warn(
|
|
14832
|
-
`⚠️
|
|
15216
|
+
`⚠️ Job ${internalJob.id} error ${document2 ? `on document ${document2.url}` : ""}: ${error.message}`
|
|
14833
15217
|
);
|
|
14834
15218
|
}
|
|
14835
15219
|
});
|
|
@@ -14910,7 +15294,7 @@ class PipelineManager {
|
|
|
14910
15294
|
);
|
|
14911
15295
|
} catch (optionsError) {
|
|
14912
15296
|
logger.warn(
|
|
14913
|
-
`⚠️
|
|
15297
|
+
`⚠️ Failed to store scraper options for job ${job.id}: ${optionsError}`
|
|
14914
15298
|
);
|
|
14915
15299
|
}
|
|
14916
15300
|
}
|
|
@@ -14979,6 +15363,217 @@ var PipelineFactory2;
|
|
|
14979
15363
|
}
|
|
14980
15364
|
PipelineFactory22.createPipeline = createPipeline;
|
|
14981
15365
|
})(PipelineFactory2 || (PipelineFactory2 = {}));
|
|
15366
|
+
function getGlobalOptions(command) {
|
|
15367
|
+
let rootCommand = command;
|
|
15368
|
+
while (rootCommand?.parent) {
|
|
15369
|
+
rootCommand = rootCommand.parent;
|
|
15370
|
+
}
|
|
15371
|
+
return rootCommand?.opts() || {};
|
|
15372
|
+
}
|
|
15373
|
+
function getEventBus(command) {
|
|
15374
|
+
const eventBus = command?._eventBus;
|
|
15375
|
+
if (!eventBus) {
|
|
15376
|
+
throw new Error("EventBusService not initialized");
|
|
15377
|
+
}
|
|
15378
|
+
return eventBus;
|
|
15379
|
+
}
|
|
15380
|
+
function ensurePlaywrightBrowsersInstalled() {
|
|
15381
|
+
if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === "1") {
|
|
15382
|
+
logger.debug(
|
|
15383
|
+
"PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD is set, skipping Playwright browser install."
|
|
15384
|
+
);
|
|
15385
|
+
return;
|
|
15386
|
+
}
|
|
15387
|
+
const chromiumEnvPath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH;
|
|
15388
|
+
if (chromiumEnvPath && existsSync(chromiumEnvPath)) {
|
|
15389
|
+
logger.debug(
|
|
15390
|
+
`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set to '${chromiumEnvPath}', skipping Playwright browser install.`
|
|
15391
|
+
);
|
|
15392
|
+
return;
|
|
15393
|
+
}
|
|
15394
|
+
try {
|
|
15395
|
+
const chromiumPath = chromium.executablePath();
|
|
15396
|
+
if (!chromiumPath || !existsSync(chromiumPath)) {
|
|
15397
|
+
throw new Error("Playwright Chromium browser not found");
|
|
15398
|
+
}
|
|
15399
|
+
} catch (error) {
|
|
15400
|
+
logger.debug(String(error));
|
|
15401
|
+
try {
|
|
15402
|
+
console.log(
|
|
15403
|
+
"🌐 Installing Playwright Chromium browser... (this may take a moment)"
|
|
15404
|
+
);
|
|
15405
|
+
execSync("npm exec -y playwright install --no-shell --with-deps chromium", {
|
|
15406
|
+
stdio: "ignore",
|
|
15407
|
+
// Suppress output
|
|
15408
|
+
cwd: getProjectRoot()
|
|
15409
|
+
});
|
|
15410
|
+
} catch (_installErr) {
|
|
15411
|
+
console.error(
|
|
15412
|
+
"❌ Failed to install Playwright browsers automatically. Please run:\n npx playwright install --no-shell --with-deps chromium\nand try again."
|
|
15413
|
+
);
|
|
15414
|
+
process.exit(1);
|
|
15415
|
+
}
|
|
15416
|
+
}
|
|
15417
|
+
}
|
|
15418
|
+
function resolveProtocol(protocol) {
|
|
15419
|
+
if (protocol === "auto") {
|
|
15420
|
+
if (!process.stdin.isTTY && !process.stdout.isTTY) {
|
|
15421
|
+
return "stdio";
|
|
15422
|
+
}
|
|
15423
|
+
return "http";
|
|
15424
|
+
}
|
|
15425
|
+
if (protocol === "stdio" || protocol === "http") {
|
|
15426
|
+
return protocol;
|
|
15427
|
+
}
|
|
15428
|
+
throw new Error(`Invalid protocol: ${protocol}. Must be 'auto', 'stdio', or 'http'`);
|
|
15429
|
+
}
|
|
15430
|
+
const formatOutput = (data) => JSON.stringify(data, null, 2);
|
|
15431
|
+
function setupLogging(options, protocol) {
|
|
15432
|
+
if (options.silent) {
|
|
15433
|
+
setLogLevel(LogLevel.ERROR);
|
|
15434
|
+
} else if (options.verbose) {
|
|
15435
|
+
setLogLevel(LogLevel.DEBUG);
|
|
15436
|
+
}
|
|
15437
|
+
}
|
|
15438
|
+
function validatePort(portString) {
|
|
15439
|
+
const port = Number.parseInt(portString, 10);
|
|
15440
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
15441
|
+
throw new Error("Invalid port number");
|
|
15442
|
+
}
|
|
15443
|
+
return port;
|
|
15444
|
+
}
|
|
15445
|
+
function validateHost(hostString) {
|
|
15446
|
+
const trimmed = hostString.trim();
|
|
15447
|
+
if (!trimmed) {
|
|
15448
|
+
throw new Error("Host cannot be empty");
|
|
15449
|
+
}
|
|
15450
|
+
if (trimmed.includes(" ") || trimmed.includes(" ") || trimmed.includes("\n")) {
|
|
15451
|
+
throw new Error("Host cannot contain whitespace");
|
|
15452
|
+
}
|
|
15453
|
+
return trimmed;
|
|
15454
|
+
}
|
|
15455
|
+
function createAppServerConfig(options) {
|
|
15456
|
+
return {
|
|
15457
|
+
enableWebInterface: options.enableWebInterface ?? false,
|
|
15458
|
+
enableMcpServer: options.enableMcpServer ?? true,
|
|
15459
|
+
enableApiServer: options.enableApiServer ?? false,
|
|
15460
|
+
enableWorker: options.enableWorker ?? true,
|
|
15461
|
+
port: options.port,
|
|
15462
|
+
host: options.host,
|
|
15463
|
+
externalWorkerUrl: options.externalWorkerUrl,
|
|
15464
|
+
readOnly: options.readOnly ?? false,
|
|
15465
|
+
auth: options.auth,
|
|
15466
|
+
startupContext: options.startupContext
|
|
15467
|
+
};
|
|
15468
|
+
}
|
|
15469
|
+
function parseHeaders(headerOptions) {
|
|
15470
|
+
const headers = {};
|
|
15471
|
+
if (Array.isArray(headerOptions)) {
|
|
15472
|
+
for (const entry of headerOptions) {
|
|
15473
|
+
const idx = entry.indexOf(":");
|
|
15474
|
+
if (idx > 0) {
|
|
15475
|
+
const name = entry.slice(0, idx).trim();
|
|
15476
|
+
const value = entry.slice(idx + 1).trim();
|
|
15477
|
+
if (name) headers[name] = value;
|
|
15478
|
+
}
|
|
15479
|
+
}
|
|
15480
|
+
}
|
|
15481
|
+
return headers;
|
|
15482
|
+
}
|
|
15483
|
+
function parseAuthConfig(options) {
|
|
15484
|
+
if (!options.authEnabled) {
|
|
15485
|
+
return void 0;
|
|
15486
|
+
}
|
|
15487
|
+
return {
|
|
15488
|
+
enabled: true,
|
|
15489
|
+
issuerUrl: options.authIssuerUrl,
|
|
15490
|
+
audience: options.authAudience,
|
|
15491
|
+
scopes: ["openid", "profile"]
|
|
15492
|
+
// Default scopes for OAuth2/OIDC
|
|
15493
|
+
};
|
|
15494
|
+
}
|
|
15495
|
+
function validateAuthConfig(authConfig) {
|
|
15496
|
+
if (!authConfig.enabled) {
|
|
15497
|
+
return;
|
|
15498
|
+
}
|
|
15499
|
+
const errors = [];
|
|
15500
|
+
if (!authConfig.issuerUrl) {
|
|
15501
|
+
errors.push("--auth-issuer-url is required when auth is enabled");
|
|
15502
|
+
} else {
|
|
15503
|
+
try {
|
|
15504
|
+
const url = new URL(authConfig.issuerUrl);
|
|
15505
|
+
if (url.protocol !== "https:") {
|
|
15506
|
+
errors.push("Issuer URL must use HTTPS protocol");
|
|
15507
|
+
}
|
|
15508
|
+
} catch {
|
|
15509
|
+
errors.push("Issuer URL must be a valid URL");
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15512
|
+
if (!authConfig.audience) {
|
|
15513
|
+
errors.push("--auth-audience is required when auth is enabled");
|
|
15514
|
+
} else {
|
|
15515
|
+
try {
|
|
15516
|
+
const url = new URL(authConfig.audience);
|
|
15517
|
+
if (url.protocol === "http:" && url.hostname !== "localhost") {
|
|
15518
|
+
logger.warn(
|
|
15519
|
+
"⚠️ Audience uses HTTP protocol - consider using HTTPS for production"
|
|
15520
|
+
);
|
|
15521
|
+
}
|
|
15522
|
+
if (url.hash) {
|
|
15523
|
+
errors.push("Audience must not contain URL fragments");
|
|
15524
|
+
}
|
|
15525
|
+
} catch {
|
|
15526
|
+
if (authConfig.audience.startsWith("urn:")) {
|
|
15527
|
+
const urnParts = authConfig.audience.split(":");
|
|
15528
|
+
if (urnParts.length < 3 || !urnParts[1] || !urnParts[2]) {
|
|
15529
|
+
errors.push("URN audience must follow format: urn:namespace:specific-string");
|
|
15530
|
+
}
|
|
15531
|
+
} else {
|
|
15532
|
+
errors.push(
|
|
15533
|
+
"Audience must be a valid absolute URL or URN (e.g., https://api.example.com or urn:company:service)"
|
|
15534
|
+
);
|
|
15535
|
+
}
|
|
15536
|
+
}
|
|
15537
|
+
}
|
|
15538
|
+
if (errors.length > 0) {
|
|
15539
|
+
throw new Error(`Auth configuration validation failed:
|
|
15540
|
+
${errors.join("\n")}`);
|
|
15541
|
+
}
|
|
15542
|
+
}
|
|
15543
|
+
function warnHttpUsage(authConfig, port) {
|
|
15544
|
+
if (!authConfig?.enabled) {
|
|
15545
|
+
return;
|
|
15546
|
+
}
|
|
15547
|
+
const isLocalhost = process.env.NODE_ENV !== "production" || port === 6280 || // default dev port
|
|
15548
|
+
process.env.HOSTNAME?.includes("localhost");
|
|
15549
|
+
if (!isLocalhost) {
|
|
15550
|
+
logger.warn(
|
|
15551
|
+
"⚠️ Authentication is enabled but running over HTTP in production. Consider using HTTPS for security."
|
|
15552
|
+
);
|
|
15553
|
+
}
|
|
15554
|
+
}
|
|
15555
|
+
function resolveEmbeddingContext(embeddingModel) {
|
|
15556
|
+
try {
|
|
15557
|
+
let modelSpec = embeddingModel;
|
|
15558
|
+
if (!modelSpec && process.env.OPENAI_API_KEY) {
|
|
15559
|
+
modelSpec = "text-embedding-3-small";
|
|
15560
|
+
logger.debug(
|
|
15561
|
+
"Using default OpenAI embedding model due to OPENAI_API_KEY presence."
|
|
15562
|
+
);
|
|
15563
|
+
}
|
|
15564
|
+
if (!modelSpec) {
|
|
15565
|
+
logger.debug(
|
|
15566
|
+
"No embedding model specified and OPENAI_API_KEY not found. Embeddings are disabled."
|
|
15567
|
+
);
|
|
15568
|
+
return null;
|
|
15569
|
+
}
|
|
15570
|
+
logger.debug(`Resolving embedding configuration for model: ${modelSpec}`);
|
|
15571
|
+
return EmbeddingConfig.parseEmbeddingConfig(modelSpec);
|
|
15572
|
+
} catch (error) {
|
|
15573
|
+
logger.debug(`Failed to resolve embedding configuration: ${error}`);
|
|
15574
|
+
return null;
|
|
15575
|
+
}
|
|
15576
|
+
}
|
|
14982
15577
|
function createDefaultAction(program) {
|
|
14983
15578
|
return program.addOption(
|
|
14984
15579
|
new Option("--protocol <protocol>", "Protocol for MCP server").env("DOCS_MCP_PROTOCOL").default("auto").choices(["auto", "stdio", "http"])
|
|
@@ -15324,7 +15919,6 @@ function createMcpCommand(program) {
|
|
|
15324
15919
|
);
|
|
15325
15920
|
if (resolvedProtocol === "stdio") {
|
|
15326
15921
|
logger.debug(`Auto-detected stdio protocol (no TTY)`);
|
|
15327
|
-
logger.info("🚀 Starting MCP server (stdio mode)");
|
|
15328
15922
|
await pipeline.start();
|
|
15329
15923
|
const mcpTools = await initializeTools(docService, pipeline);
|
|
15330
15924
|
const mcpServer = await startStdioServer(mcpTools, cmdOptions.readOnly);
|
|
@@ -15337,7 +15931,6 @@ function createMcpCommand(program) {
|
|
|
15337
15931
|
});
|
|
15338
15932
|
} else {
|
|
15339
15933
|
logger.debug(`Auto-detected http protocol (TTY available)`);
|
|
15340
|
-
logger.info("🚀 Starting MCP server (http mode)");
|
|
15341
15934
|
const config = createAppServerConfig({
|
|
15342
15935
|
enableWebInterface: false,
|
|
15343
15936
|
// Never enable web interface in mcp command
|
|
@@ -15502,7 +16095,7 @@ async function removeAction(library, options, command) {
|
|
|
15502
16095
|
function createRemoveCommand(program) {
|
|
15503
16096
|
return program.command("remove <library>").description("Remove documents for a specific library and version").option(
|
|
15504
16097
|
"-v, --version <string>",
|
|
15505
|
-
"Version to remove (optional, removes
|
|
16098
|
+
"Version to remove (optional, removes latest if omitted)"
|
|
15506
16099
|
).option(
|
|
15507
16100
|
"--server-url <url>",
|
|
15508
16101
|
"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)"
|
|
@@ -15806,9 +16399,6 @@ function createWebCommand(program) {
|
|
|
15806
16399
|
cliCommand: "web"
|
|
15807
16400
|
}
|
|
15808
16401
|
});
|
|
15809
|
-
logger.info(
|
|
15810
|
-
`🚀 Starting web interface${serverUrl ? ` connecting to worker at ${serverUrl}` : ""}`
|
|
15811
|
-
);
|
|
15812
16402
|
const appServer = await startAppServer(docService, pipeline, eventBus, config);
|
|
15813
16403
|
registerGlobalServices({
|
|
15814
16404
|
appServer,
|
|
@@ -15851,7 +16441,6 @@ function createWorkerCommand(program) {
|
|
|
15851
16441
|
const port = validatePort(cmdOptions.port);
|
|
15852
16442
|
const host = validateHost(cmdOptions.host);
|
|
15853
16443
|
try {
|
|
15854
|
-
logger.info(`🚀 Starting external pipeline worker on port ${port}`);
|
|
15855
16444
|
ensurePlaywrightBrowsersInstalled();
|
|
15856
16445
|
const embeddingConfig = resolveEmbeddingContext(cmdOptions.embeddingModel);
|
|
15857
16446
|
const globalOptions = program.opts();
|
|
@@ -15902,7 +16491,7 @@ function createCliProgram() {
|
|
|
15902
16491
|
const commandStartTimes = /* @__PURE__ */ new Map();
|
|
15903
16492
|
let globalEventBus = null;
|
|
15904
16493
|
let globalTelemetryService = null;
|
|
15905
|
-
program.name("docs-mcp-server").description("Unified CLI, MCP Server, and Web Interface for Docs MCP Server.").version("1.
|
|
16494
|
+
program.name("docs-mcp-server").description("Unified CLI, MCP Server, and Web Interface for Docs MCP Server.").version("1.31.0").addOption(
|
|
15906
16495
|
new Option("--verbose", "Enable verbose (debug) logging").conflicts("silent")
|
|
15907
16496
|
).addOption(new Option("--silent", "Disable all logging except errors")).addOption(
|
|
15908
16497
|
new Option("--telemetry", "Enable telemetry collection").env("DOCS_MCP_TELEMETRY").argParser((value) => {
|
|
@@ -15936,7 +16525,7 @@ function createCliProgram() {
|
|
|
15936
16525
|
if (shouldEnableTelemetry()) {
|
|
15937
16526
|
if (telemetry.isEnabled()) {
|
|
15938
16527
|
telemetry.setGlobalContext({
|
|
15939
|
-
appVersion: "1.
|
|
16528
|
+
appVersion: "1.31.0",
|
|
15940
16529
|
appPlatform: process.platform,
|
|
15941
16530
|
appNodeVersion: process.version,
|
|
15942
16531
|
appInterface: "cli",
|