codesearch-mcp 0.2.3-alpha → 0.4.0-alpha
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/CHANGELOG.md +88 -5
- package/LICENSE +661 -21
- package/README.md +76 -40
- package/dist/app.js +17 -38
- package/dist/src/ast/CallExtractor.d.ts +8 -0
- package/dist/src/ast/CallExtractor.js +68 -0
- package/dist/src/ast/GraphqlNameExtractor.d.ts +22 -0
- package/dist/src/ast/GraphqlNameExtractor.js +240 -0
- package/dist/src/ast/ImportEdgeExtractor.d.ts +11 -0
- package/dist/src/ast/ImportEdgeExtractor.js +129 -0
- package/dist/src/ast/IndexedNodeResolver.d.ts +14 -0
- package/dist/src/ast/IndexedNodeResolver.js +217 -0
- package/dist/src/ast/MemberExtractor.d.ts +8 -0
- package/dist/src/ast/MemberExtractor.js +129 -0
- package/dist/src/ast/TopLevelSymbolExtractor.d.ts +5 -0
- package/dist/src/ast/TopLevelSymbolExtractor.js +86 -0
- package/dist/src/ast/graphql-grammar.d.ts +24 -0
- package/dist/src/ast/graphql-grammar.js +93 -0
- package/dist/src/ast/node-helpers.d.ts +11 -0
- package/dist/src/ast/node-helpers.js +55 -0
- package/dist/src/chunking/AstChunker.d.ts +12 -0
- package/dist/src/chunking/AstChunker.js +30 -0
- package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
- package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
- package/dist/src/chunking/FragmentMerger.d.ts +11 -0
- package/dist/src/chunking/FragmentMerger.js +61 -0
- package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
- package/dist/src/chunking/ImportOnlyFilter.js +14 -0
- package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
- package/dist/src/chunking/LangchainTextSplitter.js +133 -0
- package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
- package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
- package/dist/src/chunking/RunPacker.d.ts +12 -0
- package/dist/src/chunking/RunPacker.js +84 -0
- package/dist/src/chunking/create-chunker.d.ts +3 -0
- package/dist/src/chunking/create-chunker.js +15 -0
- package/dist/src/chunking/interfaces.d.ts +27 -0
- package/dist/src/commands/ConfigCommand.js +1 -9
- package/dist/src/commands/EvalCommand.d.ts +10 -5
- package/dist/src/commands/EvalCommand.js +60 -149
- package/dist/src/commands/IndexCommand.d.ts +10 -4
- package/dist/src/commands/IndexCommand.js +52 -146
- package/dist/src/commands/McpCommand.d.ts +6 -3
- package/dist/src/commands/McpCommand.js +19 -20
- package/dist/src/commands/WebCommand.d.ts +9 -3
- package/dist/src/commands/WebCommand.js +17 -11
- package/dist/src/commands/eval/baseline-comparison.d.ts +6 -0
- package/dist/src/commands/eval/baseline-comparison.js +54 -0
- package/dist/src/commands/eval/eval-args.d.ts +6 -0
- package/dist/src/commands/eval/eval-args.js +71 -0
- package/dist/src/commands/eval/eval-corpora.d.ts +6 -0
- package/dist/src/commands/eval/eval-corpora.js +76 -0
- package/dist/src/commands/eval/eval-metadata.d.ts +17 -0
- package/dist/src/commands/eval/eval-metadata.js +75 -0
- package/dist/src/commands/eval/index-inspector.d.ts +3 -0
- package/dist/src/commands/eval/index-inspector.js +21 -0
- package/dist/src/commands/eval/staged-search.d.ts +3 -0
- package/dist/src/commands/eval/staged-search.js +20 -0
- package/dist/src/commands/eval/suite-loader.d.ts +8 -0
- package/dist/src/commands/eval/suite-loader.js +52 -0
- package/dist/src/commands/index/backend-probe.d.ts +2 -0
- package/dist/src/commands/index/backend-probe.js +15 -0
- package/dist/src/commands/index/embedding-setup.d.ts +3 -0
- package/dist/src/commands/index/embedding-setup.js +16 -0
- package/dist/src/commands/index/index-summary.d.ts +2 -0
- package/dist/src/commands/index/index-summary.js +22 -0
- package/dist/src/commands/index/legacy-ollama-guard.d.ts +3 -0
- package/dist/src/commands/index/legacy-ollama-guard.js +23 -0
- package/dist/src/commands/index/prune-step.d.ts +3 -0
- package/dist/src/commands/index/prune-step.js +22 -0
- package/dist/src/commands/index/scan-targets.d.ts +8 -0
- package/dist/src/commands/index/scan-targets.js +107 -0
- package/dist/src/config-error.d.ts +0 -4
- package/dist/src/config-error.js +0 -4
- package/dist/src/config-store.d.ts +1 -55
- package/dist/src/config-store.js +11 -73
- package/dist/src/config.d.ts +9 -18
- package/dist/src/config.js +36 -35
- package/dist/src/constants.d.ts +17 -28
- package/dist/src/constants.js +35 -29
- package/dist/src/container.d.ts +10 -2
- package/dist/src/container.js +19 -8
- package/dist/src/db/drizzle-types.d.ts +7 -0
- package/dist/src/db/drizzle-types.js +2 -0
- package/dist/src/db/index.d.ts +1 -0
- package/dist/src/db/schema-steps.d.ts +8 -0
- package/dist/src/db/schema-steps.js +105 -0
- package/dist/src/db/schema.d.ts +17 -0
- package/dist/src/db/schema.js +1 -6
- package/dist/src/declarations/DeclarationTree.d.ts +19 -0
- package/dist/src/declarations/DeclarationTree.js +66 -0
- package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
- package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
- package/dist/src/declarations/SourceLines.d.ts +37 -0
- package/dist/src/declarations/SourceLines.js +415 -0
- package/dist/src/dependency-traversal.d.ts +0 -6
- package/dist/src/dependency-traversal.js +0 -6
- package/dist/src/enriched-prompt.d.ts +0 -6
- package/dist/src/enriched-prompt.js +0 -6
- package/dist/src/error-record.d.ts +12 -0
- package/dist/src/error-record.js +248 -0
- package/dist/src/eval-corpus.d.ts +16 -0
- package/dist/src/eval-corpus.js +169 -0
- package/dist/src/eval-results.d.ts +6 -3
- package/dist/src/eval-results.js +323 -26
- package/dist/src/eval-vector-index.d.ts +5 -0
- package/dist/src/eval-vector-index.js +60 -0
- package/dist/src/file-utils.d.ts +0 -5
- package/dist/src/file-utils.js +0 -5
- package/dist/src/graph.d.ts +0 -25
- package/dist/src/graph.js +0 -28
- package/dist/src/help.js +9 -1
- package/dist/src/hnsw-settings.d.ts +3 -0
- package/dist/src/hnsw-settings.js +24 -0
- package/dist/src/index-args.d.ts +13 -0
- package/dist/src/index-args.js +48 -0
- package/dist/src/indexing/AnalysisBatcher.d.ts +12 -0
- package/dist/src/indexing/AnalysisBatcher.js +28 -0
- package/dist/src/indexing/ChunkEmbedder.d.ts +24 -0
- package/dist/src/indexing/ChunkEmbedder.js +37 -0
- package/dist/src/indexing/ConsoleScanReporter.d.ts +10 -0
- package/dist/src/indexing/ConsoleScanReporter.js +73 -0
- package/dist/src/indexing/EmbeddingSchemaPreparer.d.ts +14 -0
- package/dist/src/indexing/EmbeddingSchemaPreparer.js +66 -0
- package/dist/src/indexing/FileIngestor.d.ts +22 -0
- package/dist/src/indexing/FileIngestor.js +116 -0
- package/dist/src/indexing/IndexStore.d.ts +24 -0
- package/dist/src/indexing/IndexStore.js +69 -0
- package/dist/src/indexing/StaleFilePruner.d.ts +8 -0
- package/dist/src/indexing/StaleFilePruner.js +55 -0
- package/dist/src/indexing/analysis-rows.d.ts +4 -0
- package/dist/src/indexing/analysis-rows.js +65 -0
- package/dist/src/indexing/project-notices.d.ts +2 -0
- package/dist/src/indexing/project-notices.js +63 -0
- package/dist/src/interfaces/IAstParser.d.ts +4 -40
- package/dist/src/interfaces/ICallGraph.d.ts +28 -0
- package/dist/src/interfaces/ICallGraph.js +2 -0
- package/dist/src/interfaces/IChunkSearch.d.ts +42 -0
- package/dist/src/interfaces/IChunkSearch.js +2 -0
- package/dist/src/interfaces/IChunker.d.ts +3 -12
- package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
- package/dist/src/interfaces/ICorpusInspector.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +8 -24
- package/dist/src/interfaces/IDependencyGraph.d.ts +24 -0
- package/dist/src/interfaces/IDependencyGraph.js +2 -0
- package/dist/src/interfaces/IEmbedder.d.ts +0 -9
- package/dist/src/interfaces/IIndexIdentityGuard.d.ts +3 -0
- package/dist/src/interfaces/IIndexIdentityGuard.js +2 -0
- package/dist/src/interfaces/IIndexedNodeResolver.d.ts +10 -0
- package/dist/src/interfaces/IIndexedNodeResolver.js +2 -0
- package/dist/src/interfaces/ILogger.d.ts +1 -11
- package/dist/src/interfaces/IMcpReadiness.d.ts +12 -0
- package/dist/src/interfaces/IMcpReadiness.js +2 -0
- package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
- package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
- package/dist/src/interfaces/IProjectCatalog.d.ts +13 -0
- package/dist/src/interfaces/IProjectCatalog.js +2 -0
- package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
- package/dist/src/interfaces/IProjectRoots.d.ts +3 -0
- package/dist/src/interfaces/IProjectRoots.js +2 -0
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +153 -12
- package/dist/src/interfaces/IScanReporter.d.ts +13 -0
- package/dist/src/interfaces/IScanReporter.js +2 -0
- package/dist/src/interfaces/ISymbolLookup.d.ts +49 -0
- package/dist/src/interfaces/ISymbolLookup.js +2 -0
- package/dist/src/interfaces/IToolPaths.d.ts +62 -0
- package/dist/src/interfaces/IToolPaths.js +2 -0
- package/dist/src/interfaces/IWorkspaceFiles.d.ts +28 -0
- package/dist/src/interfaces/IWorkspaceFiles.js +2 -0
- package/dist/src/interfaces/SymbolKind.d.ts +1 -0
- package/dist/src/interfaces/SymbolKind.js +2 -0
- package/dist/src/interfaces/index.d.ts +12 -1
- package/dist/src/interrupt.js +0 -2
- package/dist/src/owner-path.d.ts +9 -10
- package/dist/src/owner-path.js +11 -23
- package/dist/src/project-scope.d.ts +1 -12
- package/dist/src/project-scope.js +5 -12
- package/dist/src/project-selector.d.ts +0 -5
- package/dist/src/project-selector.js +0 -11
- package/dist/src/retrieval-eval.d.ts +15 -6
- package/dist/src/retrieval-eval.js +388 -59
- package/dist/src/search/CallGraph.d.ts +9 -0
- package/dist/src/search/CallGraph.js +84 -0
- package/dist/src/search/ChunkSearch.d.ts +23 -0
- package/dist/src/search/ChunkSearch.js +115 -0
- package/dist/src/search/DependencyGraph.d.ts +19 -0
- package/dist/src/search/DependencyGraph.js +180 -0
- package/dist/src/search/IndexIdentityGuard.d.ts +11 -0
- package/dist/src/search/IndexIdentityGuard.js +37 -0
- package/dist/src/search/ProjectCatalog.d.ts +11 -0
- package/dist/src/search/ProjectCatalog.js +38 -0
- package/dist/src/search/SymbolLookup.d.ts +24 -0
- package/dist/src/search/SymbolLookup.js +97 -0
- package/dist/src/search/chunks/CandidateFetcher.d.ts +19 -0
- package/dist/src/search/chunks/CandidateFetcher.js +40 -0
- package/dist/src/search/chunks/IterativeScanGate.d.ts +10 -0
- package/dist/src/search/chunks/IterativeScanGate.js +42 -0
- package/dist/src/search/chunks/RerankStage.d.ts +15 -0
- package/dist/src/search/chunks/RerankStage.js +42 -0
- package/dist/src/search/chunks/search-metrics.d.ts +18 -0
- package/dist/src/search/chunks/search-metrics.js +32 -0
- package/dist/src/search/chunks/search-plan.d.ts +18 -0
- package/dist/src/search/chunks/search-plan.js +19 -0
- package/dist/src/search/create-search-services.d.ts +26 -0
- package/dist/src/search/create-search-services.js +29 -0
- package/dist/src/search/index-paths.d.ts +1 -0
- package/dist/src/search/index-paths.js +5 -0
- package/dist/src/search/result-shaping.d.ts +4 -0
- package/dist/src/search/result-shaping.js +19 -0
- package/dist/src/search/symbol-sql.d.ts +13 -0
- package/dist/src/search/symbol-sql.js +66 -0
- package/dist/src/search/symbols/SymbolGrepFallback.d.ts +19 -0
- package/dist/src/search/symbols/SymbolGrepFallback.js +106 -0
- package/dist/src/search/symbols/SymbolIndexQuery.d.ts +14 -0
- package/dist/src/search/symbols/SymbolIndexQuery.js +52 -0
- package/dist/src/search/symbols/SymbolTextSearch.d.ts +13 -0
- package/dist/src/search/symbols/SymbolTextSearch.js +84 -0
- package/dist/src/search/symbols/TargetColumnGate.d.ts +7 -0
- package/dist/src/search/symbols/TargetColumnGate.js +23 -0
- package/dist/src/search/symbols/find-symbol-constants.d.ts +4 -0
- package/dist/src/search/symbols/find-symbol-constants.js +8 -0
- package/dist/src/search/symbols/grep-args.d.ts +3 -0
- package/dist/src/search/symbols/grep-args.js +7 -0
- package/dist/src/search/symbols/grep-patterns.d.ts +4 -0
- package/dist/src/search/symbols/grep-patterns.js +60 -0
- package/dist/src/services/AstParserService.d.ts +10 -106
- package/dist/src/services/AstParserService.js +37 -1068
- package/dist/src/services/DatabaseService.d.ts +10 -17
- package/dist/src/services/DatabaseService.js +71 -115
- package/dist/src/services/EmbedderFactory.d.ts +0 -4
- package/dist/src/services/EmbedderFactory.js +0 -5
- package/dist/src/services/IndexerService.d.ts +7 -26
- package/dist/src/services/IndexerService.js +38 -286
- package/dist/src/services/InfinityEmbedder.d.ts +0 -21
- package/dist/src/services/InfinityEmbedder.js +0 -15
- package/dist/src/services/LoggerService.d.ts +7 -5
- package/dist/src/services/LoggerService.js +37 -41
- package/dist/src/services/McpReadiness.d.ts +18 -0
- package/dist/src/services/McpReadiness.js +73 -0
- package/dist/src/services/McpServer.d.ts +50 -19
- package/dist/src/services/McpServer.js +132 -62
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
- package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
- package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
- package/dist/src/services/NxMonorepoResolver.js +5 -56
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
- package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
- package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
- package/dist/src/services/ProjectDiscoveryService.js +0 -4
- package/dist/src/services/VoyageEmbedder.d.ts +0 -6
- package/dist/src/services/VoyageEmbedder.js +0 -6
- package/dist/src/services/WebServerService.d.ts +5 -56
- package/dist/src/services/WebServerService.js +25 -63
- package/dist/src/services/http-retry.d.ts +0 -7
- package/dist/src/services/http-retry.js +1 -10
- package/dist/src/services/infinity-provisioning.d.ts +0 -74
- package/dist/src/services/infinity-provisioning.js +5 -88
- package/dist/src/services/legacy-ollama-index.d.ts +0 -16
- package/dist/src/services/legacy-ollama-index.js +0 -23
- package/dist/src/services/reranker.d.ts +0 -9
- package/dist/src/services/reranker.js +46 -12
- package/dist/src/setup.d.ts +24 -24
- package/dist/src/setup.js +205 -103
- package/dist/src/symbol-name.d.ts +1 -12
- package/dist/src/symbol-name.js +16 -25
- package/dist/src/symbol-rows.d.ts +0 -14
- package/dist/src/symbol-rows.js +0 -14
- package/dist/src/tool-call-log.d.ts +2 -0
- package/dist/src/tool-call-log.js +24 -0
- package/dist/src/tools/FindSymbolTool.d.ts +17 -7
- package/dist/src/tools/FindSymbolTool.js +54 -35
- package/dist/src/tools/GetCallersTool.d.ts +16 -8
- package/dist/src/tools/GetCallersTool.js +65 -35
- package/dist/src/tools/GetDependenciesTool.d.ts +16 -8
- package/dist/src/tools/GetDependenciesTool.js +36 -29
- package/dist/src/tools/GetDependentsTool.d.ts +20 -8
- package/dist/src/tools/GetDependentsTool.js +48 -34
- package/dist/src/tools/ListFilesTool.d.ts +16 -8
- package/dist/src/tools/ListFilesTool.js +20 -26
- package/dist/src/tools/ListProjectsTool.d.ts +11 -7
- package/dist/src/tools/ListProjectsTool.js +13 -11
- package/dist/src/tools/McpTool.d.ts +17 -0
- package/dist/src/tools/McpTool.js +28 -0
- package/dist/src/tools/ReadFileRangeTool.d.ts +16 -8
- package/dist/src/tools/ReadFileRangeTool.js +23 -25
- package/dist/src/tools/ReadFileTool.d.ts +15 -8
- package/dist/src/tools/ReadFileTool.js +19 -24
- package/dist/src/tools/SearchCodebaseTool.d.ts +22 -10
- package/dist/src/tools/SearchCodebaseTool.js +71 -50
- package/dist/src/tools/format-helpers.d.ts +9 -9
- package/dist/src/tools/format-helpers.js +146 -28
- package/dist/src/tools/tool-args.d.ts +38 -0
- package/dist/src/tools/tool-args.js +95 -0
- package/dist/src/web/dashboard.d.ts +0 -8
- package/dist/src/web/dashboard.js +7 -10
- package/dist/src/workspace/GrepRunner.d.ts +15 -0
- package/dist/src/workspace/GrepRunner.js +104 -0
- package/dist/src/workspace/ProjectRoots.d.ts +22 -0
- package/dist/src/workspace/ProjectRoots.js +102 -0
- package/dist/src/workspace/ToolPaths.d.ts +31 -0
- package/dist/src/workspace/ToolPaths.js +312 -0
- package/dist/src/workspace/WorkspaceFiles.d.ts +19 -0
- package/dist/src/workspace/WorkspaceFiles.js +285 -0
- package/dist/src/workspace/fs-access.d.ts +9 -0
- package/dist/src/workspace/fs-access.js +47 -0
- package/dist/src/workspace/path-helpers.d.ts +3 -0
- package/dist/src/workspace/path-helpers.js +43 -0
- package/package.json +5 -4
- package/dist/src/interfaces/ISearcher.d.ts +0 -35
- package/dist/src/services/AstChunker.d.ts +0 -42
- package/dist/src/services/AstChunker.js +0 -158
- package/dist/src/services/SearcherService.d.ts +0 -342
- package/dist/src/services/SearcherService.js +0 -1199
- package/docs/web-dashboard.md +0 -206
- /package/dist/src/{interfaces/ISearcher.js → chunking/interfaces.js} +0 -0
|
@@ -37,27 +37,6 @@ exports.NxMonorepoResolver = void 0;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const file_utils_1 = require("../file-utils");
|
|
40
|
-
/**
|
|
41
|
-
* Nx monorepo resolver.
|
|
42
|
-
*
|
|
43
|
-
* Detection: presence of `nx.json` at the workspace root.
|
|
44
|
-
*
|
|
45
|
-
* Project discovery strategies (results deduplicated by absolute path):
|
|
46
|
-
* 1. Read `workspace.json` or `angular.json` → projects map
|
|
47
|
-
* 2. Scan standard Nx directories (`apps/`, `libs/`, `packages/`) for
|
|
48
|
-
* `project.json`, then `package.json` (only when strategy 1 found nothing)
|
|
49
|
-
* 3. `tsconfig.base.json` `paths` — root-level libs referenced only by path
|
|
50
|
-
* alias (e.g. `contracts/`, `interfaces/`) that the directory scan misses
|
|
51
|
-
* 4. Root-level fallback: treat the workspace root itself as a project
|
|
52
|
-
* (standalone Nx setup) when nothing else matched
|
|
53
|
-
*
|
|
54
|
-
* tsconfig resolution:
|
|
55
|
-
* - Returns `tsconfig.base.json` at workspace root (Nx convention)
|
|
56
|
-
* - Falls back to `tsconfig.json` at workspace root
|
|
57
|
-
*
|
|
58
|
-
* Cross-project import resolution is handled by the AST parser via the
|
|
59
|
-
* workspace-wide tsconfig `paths` (tsconfig.base.json), so no scope map is built here.
|
|
60
|
-
*/
|
|
61
40
|
class NxMonorepoResolver {
|
|
62
41
|
logger;
|
|
63
42
|
constructor(logger) {
|
|
@@ -67,15 +46,12 @@ class NxMonorepoResolver {
|
|
|
67
46
|
return fs.existsSync(path.join(rootDir, 'nx.json'));
|
|
68
47
|
}
|
|
69
48
|
discoverProjects(rootDir) {
|
|
70
|
-
// Deduplicate across strategies by absolute project path.
|
|
71
49
|
const byPath = new Map();
|
|
72
50
|
const add = (p) => { if (p && !byPath.has(p.path))
|
|
73
51
|
byPath.set(p.path, p); };
|
|
74
|
-
// Strategy 1: workspace.json / angular.json (authoritative when present)
|
|
75
52
|
const workspaceProjects = this.readWorkspaceJson(rootDir);
|
|
76
53
|
for (const p of workspaceProjects)
|
|
77
54
|
add(p);
|
|
78
|
-
// Strategy 2 & 3: scan standard directories (only when no workspace.json map)
|
|
79
55
|
if (byPath.size === 0) {
|
|
80
56
|
for (const dir of ['apps', 'libs', 'packages']) {
|
|
81
57
|
const fullDir = path.join(rootDir, dir);
|
|
@@ -86,13 +62,8 @@ class NxMonorepoResolver {
|
|
|
86
62
|
add(p);
|
|
87
63
|
}
|
|
88
64
|
}
|
|
89
|
-
// Strategy 4: tsconfig.base.json `paths` — captures libs that live at the
|
|
90
|
-
// repo root (e.g. `contracts/`, `interfaces/`) referenced by path aliases,
|
|
91
|
-
// which the apps/libs/packages scan misses. Only real Nx projects (with a
|
|
92
|
-
// project.json/package.json) are added; dedup handles overlap with the scan.
|
|
93
65
|
for (const p of this.discoverFromTsconfigPaths(rootDir))
|
|
94
66
|
add(p);
|
|
95
|
-
// Root-level fallback (standalone Nx setup)
|
|
96
67
|
if (byPath.size === 0) {
|
|
97
68
|
const rootProject = this.tryReadProject(rootDir, rootDir);
|
|
98
69
|
if (rootProject)
|
|
@@ -102,13 +73,6 @@ class NxMonorepoResolver {
|
|
|
102
73
|
this.logger.debug('NxMonorepoResolver: discovered projects', { count: projects.length });
|
|
103
74
|
return projects;
|
|
104
75
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Discover projects from `tsconfig.base.json` `compilerOptions.paths`. Each
|
|
107
|
-
* alias (e.g. `@scope/interfaces` → `interfaces/src/index.ts`) resolves to a
|
|
108
|
-
* lib root (the path before `/src/`), which for real Nx libs contains a
|
|
109
|
-
* project.json. Aliases pointing at non-projects (no project.json/package.json,
|
|
110
|
-
* e.g. `@env` → `environments/environment.ts`) are skipped.
|
|
111
|
-
*/
|
|
112
76
|
discoverFromTsconfigPaths(rootDir) {
|
|
113
77
|
const tsconfigPath = this.getRootTsConfigPath(rootDir);
|
|
114
78
|
if (!tsconfigPath)
|
|
@@ -118,7 +82,7 @@ class NxMonorepoResolver {
|
|
|
118
82
|
json = this.parseJsonc(fs.readFileSync(tsconfigPath, 'utf-8'));
|
|
119
83
|
}
|
|
120
84
|
catch (err) {
|
|
121
|
-
this.logger.warn('NxMonorepoResolver: failed to parse tsconfig paths', { file: tsconfigPath,
|
|
85
|
+
this.logger.warn('NxMonorepoResolver: failed to parse tsconfig paths', { file: tsconfigPath, err });
|
|
122
86
|
return [];
|
|
123
87
|
}
|
|
124
88
|
const co = json?.compilerOptions ?? {};
|
|
@@ -134,13 +98,12 @@ class NxMonorepoResolver {
|
|
|
134
98
|
const srcIdx = target.indexOf('/src/');
|
|
135
99
|
const relRoot = srcIdx >= 0 ? target.slice(0, srcIdx) : path.dirname(target);
|
|
136
100
|
const libRoot = path.resolve(rootDir, baseUrl, relRoot);
|
|
137
|
-
const project = this.tryReadProject(libRoot, rootDir);
|
|
101
|
+
const project = this.tryReadProject(libRoot, rootDir);
|
|
138
102
|
if (project)
|
|
139
103
|
projects.push(project);
|
|
140
104
|
}
|
|
141
105
|
return projects;
|
|
142
106
|
}
|
|
143
|
-
/** Parse JSON that may contain comments / trailing commas (tsconfig style). */
|
|
144
107
|
parseJsonc(text) {
|
|
145
108
|
try {
|
|
146
109
|
return JSON.parse(text);
|
|
@@ -154,17 +117,14 @@ class NxMonorepoResolver {
|
|
|
154
117
|
}
|
|
155
118
|
}
|
|
156
119
|
getRootTsConfigPath(rootDir) {
|
|
157
|
-
// Nx convention: tsconfig.base.json at workspace root
|
|
158
120
|
const basePath = path.join(rootDir, 'tsconfig.base.json');
|
|
159
121
|
if (fs.existsSync(basePath))
|
|
160
122
|
return basePath;
|
|
161
|
-
// Fallback: tsconfig.json at workspace root
|
|
162
123
|
const rootPath = path.join(rootDir, 'tsconfig.json');
|
|
163
124
|
if (fs.existsSync(rootPath))
|
|
164
125
|
return rootPath;
|
|
165
126
|
return null;
|
|
166
127
|
}
|
|
167
|
-
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
168
128
|
readWorkspaceJson(rootDir) {
|
|
169
129
|
const projects = [];
|
|
170
130
|
for (const filename of ['workspace.json', 'angular.json']) {
|
|
@@ -175,9 +135,6 @@ class NxMonorepoResolver {
|
|
|
175
135
|
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
176
136
|
const projectsMap = content.projects ?? {};
|
|
177
137
|
for (const [name, value] of Object.entries(projectsMap)) {
|
|
178
|
-
// value can be a string (path) or an object with a `root` property.
|
|
179
|
-
// Guard per-entry: a malformed entry (object without `root`) must not
|
|
180
|
-
// abort discovery of the remaining projects in this file.
|
|
181
138
|
const rawRoot = typeof value === 'string'
|
|
182
139
|
? value
|
|
183
140
|
: value?.root;
|
|
@@ -196,7 +153,7 @@ class NxMonorepoResolver {
|
|
|
196
153
|
catch (err) {
|
|
197
154
|
this.logger.warn('NxMonorepoResolver: failed to parse workspace file', {
|
|
198
155
|
file: filePath,
|
|
199
|
-
|
|
156
|
+
err,
|
|
200
157
|
});
|
|
201
158
|
}
|
|
202
159
|
}
|
|
@@ -222,14 +179,12 @@ class NxMonorepoResolver {
|
|
|
222
179
|
projects.push(project);
|
|
223
180
|
continue;
|
|
224
181
|
}
|
|
225
|
-
// Check for nested grouping directories (e.g. libs/shared/utils)
|
|
226
182
|
const nested = this.scanDirectory(projectPath, defaultType, rootDir);
|
|
227
183
|
projects.push(...nested);
|
|
228
184
|
}
|
|
229
185
|
return projects;
|
|
230
186
|
}
|
|
231
187
|
tryReadProject(projectPath, rootDir, defaultType = 'lib') {
|
|
232
|
-
// Check for project.json (Nx project marker)
|
|
233
188
|
const projectJsonPath = path.join(projectPath, 'project.json');
|
|
234
189
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
235
190
|
const hasProjectJson = fs.existsSync(projectJsonPath);
|
|
@@ -239,7 +194,6 @@ class NxMonorepoResolver {
|
|
|
239
194
|
let name = path.relative(rootDir, projectPath).replace(/\\/g, '/');
|
|
240
195
|
let type = defaultType;
|
|
241
196
|
let packageName = null;
|
|
242
|
-
// Read project.json for name and type
|
|
243
197
|
if (hasProjectJson) {
|
|
244
198
|
try {
|
|
245
199
|
const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
|
|
@@ -253,14 +207,12 @@ class NxMonorepoResolver {
|
|
|
253
207
|
catch (err) {
|
|
254
208
|
this.logger.warn('NxMonorepoResolver: failed to parse project.json', {
|
|
255
209
|
file: projectJsonPath,
|
|
256
|
-
|
|
210
|
+
err,
|
|
257
211
|
});
|
|
258
212
|
}
|
|
259
213
|
}
|
|
260
|
-
// Read package.json for npm scope
|
|
261
214
|
if (hasPackageJson) {
|
|
262
215
|
packageName = this.readPackageName(projectPath);
|
|
263
|
-
// If no project.json name, use package name without scope
|
|
264
216
|
if (!hasProjectJson && packageName) {
|
|
265
217
|
const shortName = packageName.replace(/^@[^/]+\//, '');
|
|
266
218
|
name = shortName;
|
|
@@ -281,7 +233,6 @@ class NxMonorepoResolver {
|
|
|
281
233
|
}
|
|
282
234
|
}
|
|
283
235
|
inferProjectType(projectPath, name) {
|
|
284
|
-
// Check project.json
|
|
285
236
|
const projectJsonPath = path.join(projectPath, 'project.json');
|
|
286
237
|
if (fs.existsSync(projectJsonPath)) {
|
|
287
238
|
try {
|
|
@@ -291,15 +242,13 @@ class NxMonorepoResolver {
|
|
|
291
242
|
if (projectJson.projectType === 'library')
|
|
292
243
|
return 'lib';
|
|
293
244
|
}
|
|
294
|
-
catch {
|
|
245
|
+
catch { }
|
|
295
246
|
}
|
|
296
|
-
// Infer from path
|
|
297
247
|
const rel = projectPath.replace(/\\/g, '/');
|
|
298
248
|
if (rel.includes('/apps/') || rel.includes('/app/'))
|
|
299
249
|
return 'app';
|
|
300
250
|
if (rel.includes('/libs/') || rel.includes('/lib/') || rel.includes('/packages/'))
|
|
301
251
|
return 'lib';
|
|
302
|
-
// Infer from name
|
|
303
252
|
if (name.includes('lib') || name.includes('shared') || name.includes('util'))
|
|
304
253
|
return 'lib';
|
|
305
254
|
return 'app';
|
|
@@ -5,12 +5,6 @@ export interface EmbedderDeps {
|
|
|
5
5
|
fetchImpl?: typeof fetch;
|
|
6
6
|
baseDelayMs?: number;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Embedder for any server exposing an OpenAI-style `/embeddings` endpoint:
|
|
10
|
-
* OpenAI cloud, Infinity, TEI, vLLM, LM Studio, LocalAI. One request carries all
|
|
11
|
-
* inputs (`{ input: string[], model, dimensions? }`) and returns
|
|
12
|
-
* `{ data: [{ embedding, index }] }`, re-ordered to input order.
|
|
13
|
-
*/
|
|
14
8
|
export declare class OpenAICompatibleEmbedder implements IEmbedder {
|
|
15
9
|
protected config: Config;
|
|
16
10
|
private logger;
|
|
@@ -6,12 +6,6 @@ const config_1 = require("../config");
|
|
|
6
6
|
const reranker_1 = require("./reranker");
|
|
7
7
|
const http_retry_1 = require("./http-retry");
|
|
8
8
|
const constants_1 = require("../constants");
|
|
9
|
-
/**
|
|
10
|
-
* Embedder for any server exposing an OpenAI-style `/embeddings` endpoint:
|
|
11
|
-
* OpenAI cloud, Infinity, TEI, vLLM, LM Studio, LocalAI. One request carries all
|
|
12
|
-
* inputs (`{ input: string[], model, dimensions? }`) and returns
|
|
13
|
-
* `{ data: [{ embedding, index }] }`, re-ordered to input order.
|
|
14
|
-
*/
|
|
15
9
|
class OpenAICompatibleEmbedder {
|
|
16
10
|
config;
|
|
17
11
|
logger;
|
|
@@ -24,7 +18,6 @@ class OpenAICompatibleEmbedder {
|
|
|
24
18
|
this.config = config;
|
|
25
19
|
this.logger = logger;
|
|
26
20
|
const base = config.embedding.baseURL.replace(/\/+$/, '');
|
|
27
|
-
// Allow a baseURL that already includes the path (…/v1) or not.
|
|
28
21
|
this.url = /\/embeddings$/.test(base) ? base : `${base}/embeddings`;
|
|
29
22
|
this.doFetch = deps.fetchImpl ?? fetch;
|
|
30
23
|
this.baseDelayMs = deps.baseDelayMs;
|
|
@@ -40,8 +33,6 @@ class OpenAICompatibleEmbedder {
|
|
|
40
33
|
return [];
|
|
41
34
|
return (0, http_retry_1.withRetry)(async () => {
|
|
42
35
|
const headers = { 'Content-Type': 'application/json' };
|
|
43
|
-
// Resolve the key lazily — an unset `env:` ref throws here (request time),
|
|
44
|
-
// not at construction, so `rag-web`/`rag-mcp` still start.
|
|
45
36
|
const apiKey = (0, config_1.resolveEnvRef)(this.config.embedding.apiKey);
|
|
46
37
|
if (apiKey)
|
|
47
38
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
@@ -51,7 +42,6 @@ class OpenAICompatibleEmbedder {
|
|
|
51
42
|
const res = await this.doFetch(this.url, {
|
|
52
43
|
method: 'POST',
|
|
53
44
|
headers,
|
|
54
|
-
// CPU inference of one batch of token-dense text (e.g. pseudo-localized strings) takes well over 2 min.
|
|
55
45
|
signal: AbortSignal.timeout(240_000),
|
|
56
46
|
body: JSON.stringify(body),
|
|
57
47
|
});
|
|
@@ -60,13 +50,9 @@ class OpenAICompatibleEmbedder {
|
|
|
60
50
|
throw new http_retry_1.HttpError(res.status, `embeddings request failed: ${res.status} ${errBody}`);
|
|
61
51
|
}
|
|
62
52
|
const data = await res.json();
|
|
63
|
-
// Guard against a partial/mismatched response — a missing vector would
|
|
64
|
-
// otherwise be inserted as "[undefined]" and fail the whole file, or (with
|
|
65
|
-
// reordering + no index) silently misalign vectors to the wrong chunks.
|
|
66
53
|
if (!Array.isArray(data.data) || data.data.length !== texts.length) {
|
|
67
54
|
throw new Error(`embeddings response returned ${data.data?.length ?? 0} vectors for ${texts.length} inputs`);
|
|
68
55
|
}
|
|
69
|
-
// Sort by `index` so vectors line up with input order regardless of server ordering.
|
|
70
56
|
return [...data.data]
|
|
71
57
|
.sort((a, b) => (a.index ?? 0) - (b.index ?? 0))
|
|
72
58
|
.map(d => d.embedding);
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { IProjectDiscovery, DiscoveredProject } from '../interfaces/IProjectDiscovery';
|
|
2
|
-
/**
|
|
3
|
-
* Default project discovery: finds immediate subdirectories
|
|
4
|
-
* of the workspace root that contain a package.json.
|
|
5
|
-
*/
|
|
6
2
|
export declare class ProjectDiscoveryService implements IProjectDiscovery {
|
|
7
3
|
discover(rootDir: string): DiscoveredProject[];
|
|
8
4
|
}
|
|
@@ -37,10 +37,6 @@ exports.ProjectDiscoveryService = void 0;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const file_utils_1 = require("../file-utils");
|
|
40
|
-
/**
|
|
41
|
-
* Default project discovery: finds immediate subdirectories
|
|
42
|
-
* of the workspace root that contain a package.json.
|
|
43
|
-
*/
|
|
44
40
|
class ProjectDiscoveryService {
|
|
45
41
|
discover(rootDir) {
|
|
46
42
|
return fs.readdirSync(rootDir, { withFileTypes: true })
|
|
@@ -2,12 +2,6 @@ import type { Config } from '../config';
|
|
|
2
2
|
import type { IEmbedder, InputType } from '../interfaces/IEmbedder';
|
|
3
3
|
import type { ILogger } from '../interfaces/ILogger';
|
|
4
4
|
import type { EmbedderDeps } from './OpenAICompatibleEmbedder';
|
|
5
|
-
/**
|
|
6
|
-
* Voyage AI embedder. Voyage's schema differs from OpenAI's: `input` is an array
|
|
7
|
-
* of strings, `input_type` (`query` | `document`) is required for best quality,
|
|
8
|
-
* and the optional `output_dimension` requests Matryoshka truncation. Response
|
|
9
|
-
* is `{ data: [{ embedding, index }] }`.
|
|
10
|
-
*/
|
|
11
5
|
export declare class VoyageEmbedder implements IEmbedder {
|
|
12
6
|
private config;
|
|
13
7
|
private logger;
|
|
@@ -6,12 +6,6 @@ const config_1 = require("../config");
|
|
|
6
6
|
const reranker_1 = require("./reranker");
|
|
7
7
|
const http_retry_1 = require("./http-retry");
|
|
8
8
|
const constants_1 = require("../constants");
|
|
9
|
-
/**
|
|
10
|
-
* Voyage AI embedder. Voyage's schema differs from OpenAI's: `input` is an array
|
|
11
|
-
* of strings, `input_type` (`query` | `document`) is required for best quality,
|
|
12
|
-
* and the optional `output_dimension` requests Matryoshka truncation. Response
|
|
13
|
-
* is `{ data: [{ embedding, index }] }`.
|
|
14
|
-
*/
|
|
15
9
|
class VoyageEmbedder {
|
|
16
10
|
config;
|
|
17
11
|
logger;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Config } from '../config';
|
|
2
|
-
import type {
|
|
2
|
+
import type { IProjectCatalog, IDependencyGraph } from '../interfaces';
|
|
3
3
|
import type { IEmbedder } from '../interfaces';
|
|
4
4
|
import type { ILogger } from '../interfaces';
|
|
5
5
|
import { type JsonConfig } from '../config-store';
|
|
@@ -17,47 +17,20 @@ export interface BackendStatus {
|
|
|
17
17
|
finishedAt: string | null;
|
|
18
18
|
}
|
|
19
19
|
export interface WebServerDeps {
|
|
20
|
-
/** Path to the managed docker-compose.yml. Defaults to `CONFIG_DIR/docker-compose.yml`. */
|
|
21
20
|
composePath?: string;
|
|
22
|
-
/** Injection seams (fetchImpl/runCommand/pollIntervalMs/readinessTimeoutMs) for the readiness primitive. */
|
|
23
21
|
provisioning?: ProvisioningDeps;
|
|
24
|
-
/** Reads the just-saved config back — defaults to `() => new Config()`. Injectable for tests. */
|
|
25
22
|
readSavedConfig?: () => Config;
|
|
26
23
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Per-field choice lists for the config form. `strict` → render a dropdown that
|
|
29
|
-
* only accepts a listed value (e.g. the embedding model, tied to the vector
|
|
30
|
-
* dimension). Non-strict → a combobox: the values are suggestions but free text
|
|
31
|
-
* is still allowed (e.g. any model served by a self-hosted backend).
|
|
32
|
-
*/
|
|
33
24
|
export interface FieldOption {
|
|
34
25
|
values: string[];
|
|
35
26
|
strict: boolean;
|
|
36
27
|
}
|
|
37
|
-
/** Placeholder shown to the browser instead of a real secret. */
|
|
38
28
|
export declare const PASSWORD_MASK = "********";
|
|
39
|
-
/**
|
|
40
|
-
* Merge a config submitted from the browser with the stored one. Any secret
|
|
41
|
-
* field (DB password, embedding/reranker API keys) still holding the mask means
|
|
42
|
-
* the user did not change it — restore the stored value rather than overwrite it
|
|
43
|
-
* with the placeholder. `env:` references are not masked and round-trip as-is.
|
|
44
|
-
* Pure — no I/O. Exported for unit testing.
|
|
45
|
-
*/
|
|
46
29
|
export declare function mergeConfigForSave(incoming: JsonConfig, existing: JsonConfig | null): JsonConfig;
|
|
47
|
-
/**
|
|
48
|
-
* Return a copy of the config with raw secrets (DB password, raw API keys)
|
|
49
|
-
* replaced by the mask, so real secrets never reach the browser. `env:`
|
|
50
|
-
* references are left verbatim — they are not secret and the dashboard shows
|
|
51
|
-
* them as "set via <VAR>". The masked value round-trips through
|
|
52
|
-
* `mergeConfigForSave`. Pure — no I/O. Exported for unit testing.
|
|
53
|
-
*/
|
|
54
30
|
export declare function maskConfig(cfg: JsonConfig): JsonConfig;
|
|
55
|
-
/**
|
|
56
|
-
* Local HTTP dashboard. Serves a schema-driven config editor, a project
|
|
57
|
-
* overview, and the dependency graph. Bind to 127.0.0.1 only — no auth.
|
|
58
|
-
*/
|
|
59
31
|
export declare class WebServer {
|
|
60
|
-
private readonly
|
|
32
|
+
private readonly catalog;
|
|
33
|
+
private readonly dependencies;
|
|
61
34
|
private readonly config;
|
|
62
35
|
private readonly logger;
|
|
63
36
|
private readonly embedder;
|
|
@@ -68,46 +41,22 @@ export declare class WebServer {
|
|
|
68
41
|
private backendStatus;
|
|
69
42
|
private taskInFlight;
|
|
70
43
|
private pending;
|
|
71
|
-
constructor(
|
|
44
|
+
constructor(catalog: IProjectCatalog, dependencies: IDependencyGraph, config: Config, logger: ILogger, embedder: IEmbedder, deps?: WebServerDeps);
|
|
72
45
|
start(port: number, host: string): Promise<void>;
|
|
73
46
|
stop(): Promise<void>;
|
|
74
|
-
/** The bound address once started (e.g. `.port` after listen(0)); null otherwise. */
|
|
75
47
|
address(): {
|
|
76
48
|
port: number;
|
|
77
49
|
} | null;
|
|
78
50
|
private handle;
|
|
79
51
|
private maskedConfig;
|
|
80
52
|
private saveConfig;
|
|
81
|
-
/**
|
|
82
|
-
* After a successful config write, decide the Infinity backend action per
|
|
83
|
-
* the first-match decision table (`local-backend-provisioning`). Computes
|
|
84
|
-
* the required set from the SAVED config (a fresh `Config` re-read from the
|
|
85
|
-
* file just written — never the server's startup config/embedder). Never
|
|
86
|
-
* throws — an unexpected error becomes `{ action: 'failed', message }`; the
|
|
87
|
-
* saved config and `GET /api/backend-status` are unaffected either way.
|
|
88
|
-
*/
|
|
89
53
|
private evaluateBackendAfterSave;
|
|
90
|
-
/** Kick off (or continue, via a follow-up) a background Infinity recreate. */
|
|
91
54
|
private startBackgroundTask;
|
|
92
|
-
|
|
93
|
-
* Choice lists for model fields. Both are non-strict comboboxes (free text
|
|
94
|
-
* allowed): the embedding model suggests the known registry models plus any
|
|
95
|
-
* locally-installed models; the reranker model suggests installed models.
|
|
96
|
-
* Backends beyond the registry (e.g. arbitrary HuggingFace models on Infinity)
|
|
97
|
-
* still work — their dimension is probed at index time.
|
|
98
|
-
*/
|
|
55
|
+
private defaults;
|
|
99
56
|
private fieldOptions;
|
|
100
57
|
private installedModels;
|
|
101
|
-
/** Tabbed graph views (Workspace + one per nested Nx repo). */
|
|
102
58
|
private graphViews;
|
|
103
|
-
/** `/api/graph` — the Workspace view's raw `{ nodes, edges }` (back-compat). */
|
|
104
59
|
private graphData;
|
|
105
|
-
/**
|
|
106
|
-
* The full interactive graph page — the same HTML as the standalone
|
|
107
|
-
* `project-graph.html` (inner tabs, filters, legend, project toggles). The
|
|
108
|
-
* dashboard's Graph tab embeds this via an iframe rather than re-implementing
|
|
109
|
-
* vis-network.
|
|
110
|
-
*/
|
|
111
60
|
private graphPage;
|
|
112
61
|
private readBody;
|
|
113
62
|
private json;
|
|
@@ -47,20 +47,11 @@ const infinity_provisioning_1 = require("./infinity-provisioning");
|
|
|
47
47
|
function sameModelSet(a, b) {
|
|
48
48
|
return a.join(';') === b.join(';');
|
|
49
49
|
}
|
|
50
|
-
/** Placeholder shown to the browser instead of a real secret. */
|
|
51
50
|
exports.PASSWORD_MASK = '********';
|
|
52
|
-
const MAX_BODY = 1_000_000;
|
|
53
|
-
/** A secret worth masking: a non-empty string that is not an `env:` reference. */
|
|
51
|
+
const MAX_BODY = 1_000_000;
|
|
54
52
|
function isRawSecret(v) {
|
|
55
53
|
return typeof v === 'string' && v !== '' && !v.startsWith('env:');
|
|
56
54
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Merge a config submitted from the browser with the stored one. Any secret
|
|
59
|
-
* field (DB password, embedding/reranker API keys) still holding the mask means
|
|
60
|
-
* the user did not change it — restore the stored value rather than overwrite it
|
|
61
|
-
* with the placeholder. `env:` references are not masked and round-trip as-is.
|
|
62
|
-
* Pure — no I/O. Exported for unit testing.
|
|
63
|
-
*/
|
|
64
55
|
function mergeConfigForSave(incoming, existing) {
|
|
65
56
|
const merged = JSON.parse(JSON.stringify(incoming ?? {}));
|
|
66
57
|
const restore = (grp, field) => {
|
|
@@ -78,13 +69,6 @@ function mergeConfigForSave(incoming, existing) {
|
|
|
78
69
|
restore('reranker', 'apiKey');
|
|
79
70
|
return merged;
|
|
80
71
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Return a copy of the config with raw secrets (DB password, raw API keys)
|
|
83
|
-
* replaced by the mask, so real secrets never reach the browser. `env:`
|
|
84
|
-
* references are left verbatim — they are not secret and the dashboard shows
|
|
85
|
-
* them as "set via <VAR>". The masked value round-trips through
|
|
86
|
-
* `mergeConfigForSave`. Pure — no I/O. Exported for unit testing.
|
|
87
|
-
*/
|
|
88
72
|
function maskConfig(cfg) {
|
|
89
73
|
const clone = JSON.parse(JSON.stringify(cfg ?? {}));
|
|
90
74
|
if (clone.db?.password)
|
|
@@ -95,12 +79,9 @@ function maskConfig(cfg) {
|
|
|
95
79
|
clone.reranker.apiKey = exports.PASSWORD_MASK;
|
|
96
80
|
return clone;
|
|
97
81
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Local HTTP dashboard. Serves a schema-driven config editor, a project
|
|
100
|
-
* overview, and the dependency graph. Bind to 127.0.0.1 only — no auth.
|
|
101
|
-
*/
|
|
102
82
|
class WebServer {
|
|
103
|
-
|
|
83
|
+
catalog;
|
|
84
|
+
dependencies;
|
|
104
85
|
config;
|
|
105
86
|
logger;
|
|
106
87
|
embedder;
|
|
@@ -108,13 +89,12 @@ class WebServer {
|
|
|
108
89
|
composePath;
|
|
109
90
|
provisioningDeps;
|
|
110
91
|
readSavedConfig;
|
|
111
|
-
// Background Infinity-recreate task state (D6). In-memory, per-process — see
|
|
112
|
-
// `GET /api/backend-status`. `pending` holds at most one queued follow-up.
|
|
113
92
|
backendStatus = { state: 'idle', message: null, startedAt: null, finishedAt: null };
|
|
114
93
|
taskInFlight = false;
|
|
115
94
|
pending = null;
|
|
116
|
-
constructor(
|
|
117
|
-
this.
|
|
95
|
+
constructor(catalog, dependencies, config, logger, embedder, deps = {}) {
|
|
96
|
+
this.catalog = catalog;
|
|
97
|
+
this.dependencies = dependencies;
|
|
118
98
|
this.config = config;
|
|
119
99
|
this.logger = logger;
|
|
120
100
|
this.embedder = embedder;
|
|
@@ -125,7 +105,7 @@ class WebServer {
|
|
|
125
105
|
async start(port, host) {
|
|
126
106
|
this.server = http.createServer((req, res) => {
|
|
127
107
|
this.handle(req, res).catch(err => {
|
|
128
|
-
this.logger.error('WebServer: request failed', {
|
|
108
|
+
this.logger.error('WebServer: request failed', { err });
|
|
129
109
|
if (!res.headersSent) {
|
|
130
110
|
res.writeHead(500);
|
|
131
111
|
res.end('Internal error');
|
|
@@ -144,7 +124,6 @@ class WebServer {
|
|
|
144
124
|
await new Promise(resolve => this.server.close(() => resolve()));
|
|
145
125
|
this.server = undefined;
|
|
146
126
|
}
|
|
147
|
-
/** The bound address once started (e.g. `.port` after listen(0)); null otherwise. */
|
|
148
127
|
address() {
|
|
149
128
|
const a = this.server?.address();
|
|
150
129
|
return a && typeof a === 'object' ? { port: a.port } : null;
|
|
@@ -158,7 +137,7 @@ class WebServer {
|
|
|
158
137
|
if (m === 'GET' && p === '/api/schema')
|
|
159
138
|
return this.json(res, 200, (0, config_store_1.buildJsonSchema)());
|
|
160
139
|
if (m === 'GET' && p === '/api/defaults')
|
|
161
|
-
return this.json(res, 200,
|
|
140
|
+
return this.json(res, 200, this.defaults());
|
|
162
141
|
if (m === 'GET' && p === '/api/field-options')
|
|
163
142
|
return this.json(res, 200, await this.fieldOptions());
|
|
164
143
|
if (m === 'GET' && p === '/api/config')
|
|
@@ -168,7 +147,7 @@ class WebServer {
|
|
|
168
147
|
if (m === 'GET' && p === '/api/backend-status')
|
|
169
148
|
return this.json(res, 200, this.backendStatus);
|
|
170
149
|
if (m === 'GET' && p === '/api/projects')
|
|
171
|
-
return this.json(res, 200, await this.
|
|
150
|
+
return this.json(res, 200, await this.catalog.listProjects());
|
|
172
151
|
if (m === 'GET' && p === '/api/graph')
|
|
173
152
|
return this.json(res, 200, await this.graphData());
|
|
174
153
|
if (m === 'GET' && p === '/graph')
|
|
@@ -184,7 +163,6 @@ class WebServer {
|
|
|
184
163
|
body = await this.readBody(req);
|
|
185
164
|
}
|
|
186
165
|
catch (e) {
|
|
187
|
-
// Distinguish the size limit from other stream errors.
|
|
188
166
|
return this.json(res, 413, { error: e instanceof Error ? e.message : 'Request body too large' });
|
|
189
167
|
}
|
|
190
168
|
let incoming;
|
|
@@ -207,14 +185,6 @@ class WebServer {
|
|
|
207
185
|
const backend = await this.evaluateBackendAfterSave(validated);
|
|
208
186
|
this.json(res, 200, { ok: true, backend });
|
|
209
187
|
}
|
|
210
|
-
/**
|
|
211
|
-
* After a successful config write, decide the Infinity backend action per
|
|
212
|
-
* the first-match decision table (`local-backend-provisioning`). Computes
|
|
213
|
-
* the required set from the SAVED config (a fresh `Config` re-read from the
|
|
214
|
-
* file just written — never the server's startup config/embedder). Never
|
|
215
|
-
* throws — an unexpected error becomes `{ action: 'failed', message }`; the
|
|
216
|
-
* saved config and `GET /api/backend-status` are unaffected either way.
|
|
217
|
-
*/
|
|
218
188
|
async evaluateBackendAfterSave(saved) {
|
|
219
189
|
const provider = saved.embedding?.provider ?? config_store_1.CONFIG_DEFAULTS['embedding.provider'];
|
|
220
190
|
if (provider !== 'infinity') {
|
|
@@ -232,7 +202,6 @@ class WebServer {
|
|
|
232
202
|
this.pending = { modelSet, baseURL, composePath };
|
|
233
203
|
return { action: 'already-running', message: 'A backend recreate is already in progress; this configuration will be applied next.' };
|
|
234
204
|
}
|
|
235
|
-
// No longer targets the managed container the in-flight task owns.
|
|
236
205
|
this.pending = null;
|
|
237
206
|
}
|
|
238
207
|
const evaluation = await (0, infinity_provisioning_1.evaluateBackend)(baseURL, composePath, modelSet, this.provisioningDeps);
|
|
@@ -252,7 +221,6 @@ class WebServer {
|
|
|
252
221
|
return { action: 'failed', message: err instanceof Error ? err.message : String(err) };
|
|
253
222
|
}
|
|
254
223
|
}
|
|
255
|
-
/** Kick off (or continue, via a follow-up) a background Infinity recreate. */
|
|
256
224
|
startBackgroundTask(baseURL, composePath, modelSet) {
|
|
257
225
|
this.taskInFlight = true;
|
|
258
226
|
this.backendStatus = { state: 'recreating', message: null, startedAt: new Date().toISOString(), finishedAt: null };
|
|
@@ -276,19 +244,23 @@ class WebServer {
|
|
|
276
244
|
}
|
|
277
245
|
});
|
|
278
246
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
247
|
+
defaults() {
|
|
248
|
+
const saved = (0, config_store_1.readConfigJson)() ?? {};
|
|
249
|
+
const ctx = (0, config_1.rerankContext)(saved);
|
|
250
|
+
return {
|
|
251
|
+
...config_store_1.CONFIG_DEFAULTS,
|
|
252
|
+
'reranker.enabled': ctx.wanted && !ctx.missingKey,
|
|
253
|
+
'reranker.model': ctx.model,
|
|
254
|
+
'search.similarityThreshold': (0, constants_1.resolveThreshold)({ model: saved.embedding?.model ?? config_store_1.CONFIG_DEFAULTS['embedding.model'] }).value,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
286
257
|
async fieldOptions() {
|
|
287
258
|
const installed = await this.installedModels();
|
|
288
259
|
const registryModels = Object.keys(constants_1.MODEL_REGISTRY);
|
|
260
|
+
const rerankModels = (0, config_store_1.readConfigJson)()?.reranker?.provider === 'voyage' ? [...constants_1.VOYAGE_RERANK_MODELS] : installed;
|
|
289
261
|
return {
|
|
290
262
|
'embedding.model': { values: [...new Set([...registryModels, ...installed])], strict: false },
|
|
291
|
-
'reranker.model': { values:
|
|
263
|
+
'reranker.model': { values: rerankModels, strict: false },
|
|
292
264
|
};
|
|
293
265
|
}
|
|
294
266
|
async installedModels() {
|
|
@@ -296,32 +268,22 @@ class WebServer {
|
|
|
296
268
|
return (await this.embedder.listModels?.()) ?? [];
|
|
297
269
|
}
|
|
298
270
|
catch (err) {
|
|
299
|
-
this.logger.warn('WebServer: could not list models', {
|
|
271
|
+
this.logger.warn('WebServer: could not list models', { err });
|
|
300
272
|
return [];
|
|
301
273
|
}
|
|
302
274
|
}
|
|
303
|
-
/** Tabbed graph views (Workspace + one per nested Nx repo). */
|
|
304
275
|
async graphViews() {
|
|
305
|
-
const projects = (await this.
|
|
276
|
+
const projects = (await this.catalog.listProjects()).map(p => ({
|
|
306
277
|
name: p.serviceName,
|
|
307
|
-
// Real project root (resolved via discovery — handles nested-monorepo
|
|
308
|
-
// names). Fall back to the flat assumption if listProjects omits it.
|
|
309
278
|
path: p.path ?? path.join(this.config.workspaceRoot, p.serviceName),
|
|
310
279
|
}));
|
|
311
|
-
const projectEdges = await this.
|
|
280
|
+
const projectEdges = await this.dependencies.getProjectEdges();
|
|
312
281
|
return (0, graph_1.buildGraphViews)(projects, projectEdges, this.config.workspaceRoot);
|
|
313
282
|
}
|
|
314
|
-
/** `/api/graph` — the Workspace view's raw `{ nodes, edges }` (back-compat). */
|
|
315
283
|
async graphData() {
|
|
316
284
|
const views = await this.graphViews();
|
|
317
285
|
return { nodes: views[0].nodes, edges: views[0].edges };
|
|
318
286
|
}
|
|
319
|
-
/**
|
|
320
|
-
* The full interactive graph page — the same HTML as the standalone
|
|
321
|
-
* `project-graph.html` (inner tabs, filters, legend, project toggles). The
|
|
322
|
-
* dashboard's Graph tab embeds this via an iframe rather than re-implementing
|
|
323
|
-
* vis-network.
|
|
324
|
-
*/
|
|
325
287
|
async graphPage() {
|
|
326
288
|
return (0, graph_1.buildHtml)(await this.graphViews());
|
|
327
289
|
}
|
|
@@ -331,7 +293,7 @@ class WebServer {
|
|
|
331
293
|
let tooLarge = false;
|
|
332
294
|
req.on('data', chunk => {
|
|
333
295
|
if (tooLarge)
|
|
334
|
-
return;
|
|
296
|
+
return;
|
|
335
297
|
data += chunk;
|
|
336
298
|
if (data.length > MAX_BODY) {
|
|
337
299
|
tooLarge = true;
|
|
@@ -4,16 +4,9 @@ export declare class HttpError extends Error {
|
|
|
4
4
|
constructor(status: number, message: string);
|
|
5
5
|
}
|
|
6
6
|
export interface RetryOpts {
|
|
7
|
-
/** Total attempts including the first (default 4). */
|
|
8
7
|
attempts?: number;
|
|
9
|
-
/** Base backoff in ms; grows exponentially (default 500). Set 0 in tests. */
|
|
10
8
|
baseDelayMs?: number;
|
|
11
9
|
logger?: ILogger;
|
|
12
10
|
label?: string;
|
|
13
11
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Run `fn` with bounded exponential backoff on HTTP 429/5xx and network errors.
|
|
16
|
-
* Re-throws the last error once attempts are exhausted or the error is not
|
|
17
|
-
* transient.
|
|
18
|
-
*/
|
|
19
12
|
export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOpts): Promise<T>;
|