codesearch-mcp 0.0.17-alpha.1 → 0.1.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 +48 -0
- package/README.md +95 -6
- package/dist/app.js +70 -10
- package/dist/src/commands/ConfigCommand.d.ts +1 -0
- package/dist/src/commands/ConfigCommand.js +84 -4
- package/dist/src/commands/IndexCommand.js +35 -7
- package/dist/src/commands/McpCommand.js +9 -4
- package/dist/src/commands/{SetupCommand.d.ts → WebCommand.d.ts} +3 -2
- package/dist/src/commands/WebCommand.js +56 -0
- package/dist/src/config-store.d.ts +85 -4
- package/dist/src/config-store.js +157 -32
- package/dist/src/config.d.ts +32 -8
- package/dist/src/config.js +93 -30
- package/dist/src/constants.d.ts +35 -0
- package/dist/src/constants.js +57 -4
- package/dist/src/container.d.ts +10 -6
- package/dist/src/container.js +10 -5
- package/dist/src/db/index.d.ts +1 -1
- package/dist/src/db/index.js +2 -1
- package/dist/src/db/schema.d.ts +111 -0
- package/dist/src/db/schema.js +15 -1
- package/dist/src/graph.d.ts +60 -0
- package/dist/src/graph.js +239 -120
- package/dist/src/help.js +20 -5
- package/dist/src/interfaces/IAstParser.d.ts +20 -3
- package/dist/src/interfaces/IChunker.d.ts +24 -0
- package/dist/src/interfaces/IChunker.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +27 -0
- package/dist/src/interfaces/IDatabase.js +2 -0
- package/dist/src/interfaces/IEmbedder.d.ts +15 -2
- package/dist/src/interfaces/ILogger.d.ts +10 -0
- package/dist/src/interfaces/ILogger.js +2 -0
- package/dist/src/interfaces/IMcpToolHandler.d.ts +22 -0
- package/dist/src/interfaces/IMcpToolHandler.js +2 -0
- package/dist/src/interfaces/IMonorepoResolver.d.ts +33 -0
- package/dist/src/interfaces/IMonorepoResolver.js +2 -0
- package/dist/src/interfaces/IProjectDiscovery.d.ts +28 -0
- package/dist/src/interfaces/IProjectDiscovery.js +2 -0
- package/dist/src/interfaces/ISearcher.d.ts +37 -0
- package/dist/src/interfaces/ISearcher.js +2 -0
- package/dist/src/interfaces/index.d.ts +8 -0
- package/dist/src/interfaces/index.js +2 -0
- package/dist/src/owner-path.d.ts +10 -0
- package/dist/src/owner-path.js +27 -0
- package/dist/src/project-selector.d.ts +1 -1
- package/dist/src/services/AstChunker.d.ts +31 -0
- package/dist/src/services/AstChunker.js +114 -0
- package/dist/src/services/AstParserService.d.ts +10 -6
- package/dist/src/services/AstParserService.js +60 -15
- package/dist/src/services/DatabaseService.d.ts +14 -1
- package/dist/src/services/DatabaseService.js +70 -25
- package/dist/src/services/EmbedderFactory.d.ts +9 -0
- package/dist/src/services/EmbedderFactory.js +29 -0
- package/dist/src/services/IndexerService.d.ts +22 -12
- package/dist/src/services/IndexerService.js +106 -46
- package/dist/src/services/InfinityEmbedder.d.ts +79 -0
- package/dist/src/services/InfinityEmbedder.js +199 -0
- package/dist/src/services/LoggerService.d.ts +2 -1
- package/dist/src/services/McpServer.d.ts +16 -7
- package/dist/src/services/McpServer.js +54 -367
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +62 -0
- package/dist/src/services/MonorepoAwareDiscoveryService.js +125 -0
- package/dist/src/services/NxMonorepoResolver.d.ts +45 -0
- package/dist/src/services/NxMonorepoResolver.js +308 -0
- package/dist/src/services/OllamaEmbedder.d.ts +19 -5
- package/dist/src/services/OllamaEmbedder.js +91 -36
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +25 -0
- package/dist/src/services/OpenAICompatibleEmbedder.js +75 -0
- package/dist/src/services/ProjectDiscoveryService.d.ts +8 -0
- package/dist/src/{env-loader.js → services/ProjectDiscoveryService.js} +16 -5
- package/dist/src/services/SearcherService.d.ts +46 -7
- package/dist/src/services/SearcherService.js +183 -53
- package/dist/src/services/VoyageEmbedder.d.ts +22 -0
- package/dist/src/services/VoyageEmbedder.js +72 -0
- package/dist/src/services/WebServerService.d.ts +77 -0
- package/dist/src/services/WebServerService.js +267 -0
- package/dist/src/services/http-retry.d.ts +19 -0
- package/dist/src/services/http-retry.js +54 -0
- package/dist/src/services/reranker.d.ts +15 -0
- package/dist/src/services/reranker.js +44 -0
- package/dist/src/setup.d.ts +6 -0
- package/dist/src/setup.js +249 -31
- package/dist/src/tools/FindSymbolTool.d.ts +8 -0
- package/dist/src/tools/FindSymbolTool.js +45 -0
- package/dist/src/tools/GetCallersTool.d.ts +8 -0
- package/dist/src/tools/GetCallersTool.js +42 -0
- package/dist/src/tools/GetDependenciesTool.d.ts +8 -0
- package/dist/src/tools/GetDependenciesTool.js +38 -0
- package/dist/src/tools/GetDependentsTool.d.ts +8 -0
- package/dist/src/tools/GetDependentsTool.js +57 -0
- package/dist/src/tools/ListFilesTool.d.ts +8 -0
- package/dist/src/tools/ListFilesTool.js +39 -0
- package/dist/src/tools/ListProjectsTool.d.ts +8 -0
- package/dist/src/tools/ListProjectsTool.js +44 -0
- package/dist/src/tools/ReadFileRangeTool.d.ts +8 -0
- package/dist/src/tools/ReadFileRangeTool.js +44 -0
- package/dist/src/tools/ReadFileTool.d.ts +8 -0
- package/dist/src/tools/ReadFileTool.js +42 -0
- package/dist/src/tools/SearchCodebaseTool.d.ts +13 -0
- package/dist/src/tools/SearchCodebaseTool.js +81 -0
- package/dist/src/tools/format-helpers.d.ts +8 -0
- package/dist/src/tools/format-helpers.js +58 -0
- package/dist/src/tools/index.d.ts +9 -0
- package/dist/src/tools/index.js +21 -0
- package/dist/src/web/dashboard.d.ts +9 -0
- package/dist/src/web/dashboard.js +342 -0
- package/docker/docker-compose.infinity-gpu.yml +19 -0
- package/docker/docker-compose.yml +25 -0
- package/docs/web-dashboard.md +187 -0
- package/package.json +25 -3
- package/dist/src/ast.d.ts +0 -2
- package/dist/src/ast.js +0 -110
- package/dist/src/commands/SetupCommand.js +0 -14
- package/dist/src/db/data-source.d.ts +0 -2
- package/dist/src/db/data-source.js +0 -13
- package/dist/src/db/entities/code-chunk.entity.d.ts +0 -9
- package/dist/src/db/entities/code-chunk.entity.js +0 -54
- package/dist/src/db/entities/code-dependency.entity.d.ts +0 -9
- package/dist/src/db/entities/code-dependency.entity.js +0 -54
- package/dist/src/db/entities/code-symbol.entity.d.ts +0 -10
- package/dist/src/db/entities/code-symbol.entity.js +0 -59
- package/dist/src/db/init.d.ts +0 -2
- package/dist/src/db/init.js +0 -62
- package/dist/src/env-loader.d.ts +0 -1
- package/dist/src/indexer.d.ts +0 -17
- package/dist/src/indexer.js +0 -242
- package/dist/src/mcp.d.ts +0 -1
- package/dist/src/mcp.js +0 -403
- package/dist/src/ollama.d.ts +0 -2
- package/dist/src/ollama.js +0 -6
- package/dist/src/searcher.d.ts +0 -74
- package/dist/src/searcher.js +0 -446
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -9
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebCommand = void 0;
|
|
4
|
+
const WebServerService_1 = require("../services/WebServerService");
|
|
5
|
+
const DEFAULT_PORT = 4319;
|
|
6
|
+
class WebCommand {
|
|
7
|
+
container;
|
|
8
|
+
port;
|
|
9
|
+
constructor(container, args = []) {
|
|
10
|
+
this.container = container;
|
|
11
|
+
this.port = parsePort(args) ?? DEFAULT_PORT;
|
|
12
|
+
}
|
|
13
|
+
async execute() {
|
|
14
|
+
const { db, searcher, config, logger, embedder } = this.container;
|
|
15
|
+
try {
|
|
16
|
+
await db.initDb();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
20
|
+
logger.error('WebCommand: database connection failed', { reason });
|
|
21
|
+
console.error(`Database connection failed: ${reason}`);
|
|
22
|
+
// Continue: config editing still works; project/graph tabs will error in-page.
|
|
23
|
+
}
|
|
24
|
+
const server = new WebServerService_1.WebServer(searcher, config, logger, embedder);
|
|
25
|
+
const host = '127.0.0.1';
|
|
26
|
+
try {
|
|
27
|
+
await server.start(this.port, host);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
31
|
+
logger.error('WebCommand: server failed to start', { reason });
|
|
32
|
+
console.error(`Failed to start web server on ${host}:${this.port}: ${reason}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log(`RAG dashboard: http://${host}:${this.port} (Ctrl+C to stop)`);
|
|
36
|
+
// Keep execute() pending until a shutdown signal, otherwise app.ts tears
|
|
37
|
+
// down the DB and exits immediately (mirrors McpCommand's keep-alive).
|
|
38
|
+
await new Promise(resolve => {
|
|
39
|
+
const shutdown = () => { void server.stop().then(resolve); };
|
|
40
|
+
process.once('SIGINT', shutdown);
|
|
41
|
+
process.once('SIGTERM', shutdown);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.WebCommand = WebCommand;
|
|
46
|
+
function parsePort(args) {
|
|
47
|
+
const i = args.indexOf('--port');
|
|
48
|
+
if (i === -1)
|
|
49
|
+
return undefined;
|
|
50
|
+
const raw = args[i + 1];
|
|
51
|
+
const n = Number(raw);
|
|
52
|
+
if (!raw || !Number.isInteger(n) || n < 1 || n > 65535) {
|
|
53
|
+
throw new Error(`Invalid --port value: ${raw ?? '(missing)'}`);
|
|
54
|
+
}
|
|
55
|
+
return n;
|
|
56
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const CONFIG_DIR: string;
|
|
3
3
|
export declare const CONFIG_FILE: string;
|
|
4
|
+
export declare const SCHEMA_FILE: string;
|
|
5
|
+
export declare const ENV_FILE: string;
|
|
4
6
|
declare const jsonSchema: z.ZodObject<{
|
|
5
7
|
db: z.ZodOptional<z.ZodObject<{
|
|
6
8
|
host: z.ZodOptional<z.ZodString>;
|
|
@@ -9,25 +11,41 @@ declare const jsonSchema: z.ZodObject<{
|
|
|
9
11
|
password: z.ZodOptional<z.ZodString>;
|
|
10
12
|
name: z.ZodOptional<z.ZodString>;
|
|
11
13
|
}, z.core.$strip>>;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
embedding: z.ZodOptional<z.ZodObject<{
|
|
15
|
+
provider: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
ollama: "ollama";
|
|
17
|
+
"openai-compatible": "openai-compatible";
|
|
18
|
+
voyage: "voyage";
|
|
19
|
+
infinity: "infinity";
|
|
20
|
+
}>>;
|
|
21
|
+
baseURL: z.ZodOptional<z.ZodString>;
|
|
22
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
14
23
|
model: z.ZodOptional<z.ZodString>;
|
|
24
|
+
dimensions: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
batchSize: z.ZodOptional<z.ZodNumber>;
|
|
15
26
|
}, z.core.$strip>>;
|
|
16
27
|
indexing: z.ZodOptional<z.ZodObject<{
|
|
17
28
|
chunkSize: z.ZodOptional<z.ZodNumber>;
|
|
18
29
|
chunkOverlap: z.ZodOptional<z.ZodNumber>;
|
|
19
30
|
embedBatchSize: z.ZodOptional<z.ZodNumber>;
|
|
20
31
|
astBatchSize: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
chunkStrategy: z.ZodOptional<z.ZodEnum<{
|
|
33
|
+
ast: "ast";
|
|
34
|
+
character: "character";
|
|
35
|
+
}>>;
|
|
21
36
|
}, z.core.$strip>>;
|
|
22
37
|
search: z.ZodOptional<z.ZodObject<{
|
|
23
38
|
similarityThreshold: z.ZodOptional<z.ZodNumber>;
|
|
24
39
|
defaultLimit: z.ZodOptional<z.ZodNumber>;
|
|
25
40
|
maxLimit: z.ZodOptional<z.ZodNumber>;
|
|
26
|
-
autoTranslate: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
-
translateModel: z.ZodOptional<z.ZodString>;
|
|
28
41
|
}, z.core.$strip>>;
|
|
29
42
|
reranker: z.ZodOptional<z.ZodObject<{
|
|
30
43
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
provider: z.ZodOptional<z.ZodEnum<{
|
|
45
|
+
"openai-compatible": "openai-compatible";
|
|
46
|
+
}>>;
|
|
47
|
+
baseURL: z.ZodOptional<z.ZodString>;
|
|
48
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
31
49
|
model: z.ZodOptional<z.ZodString>;
|
|
32
50
|
url: z.ZodOptional<z.ZodString>;
|
|
33
51
|
overfetch: z.ZodOptional<z.ZodNumber>;
|
|
@@ -45,8 +63,71 @@ declare const jsonSchema: z.ZodObject<{
|
|
|
45
63
|
}, z.core.$strip>;
|
|
46
64
|
export type JsonConfig = z.infer<typeof jsonSchema>;
|
|
47
65
|
export declare const ALL_KEYS: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Single source of truth for built-in defaults, keyed by raw config.json dotted
|
|
68
|
+
* key. Consumed both by the web dashboard (`GET /api/defaults`) and by
|
|
69
|
+
* `resolvedSchema` in `src/config.ts`, whose `.default(...)` calls reference
|
|
70
|
+
* these values — so the runtime default and the dashboard-displayed default
|
|
71
|
+
* cannot diverge. `as const` keeps the literal types so those `.default()`
|
|
72
|
+
* lookups stay type-safe. Keys omitted here have no static default: required
|
|
73
|
+
* fields (db.host/user/password) and values derived at runtime — db.name →
|
|
74
|
+
* rag_<user>, and the provider-dependent URLs (embedding.baseURL,
|
|
75
|
+
* reranker.baseURL, reranker.url), which are computed in the `Config`
|
|
76
|
+
* constructor per provider rather than through a schema `.default(...)`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const CONFIG_DEFAULTS: {
|
|
79
|
+
readonly 'db.port': 5433;
|
|
80
|
+
readonly 'embedding.provider': "ollama";
|
|
81
|
+
readonly 'embedding.apiKey': "";
|
|
82
|
+
readonly 'embedding.model': "bge-m3";
|
|
83
|
+
readonly 'embedding.batchSize': 32;
|
|
84
|
+
readonly 'indexing.chunkSize': 1500;
|
|
85
|
+
readonly 'indexing.chunkOverlap': 200;
|
|
86
|
+
readonly 'indexing.embedBatchSize': 5;
|
|
87
|
+
readonly 'indexing.astBatchSize': 3000;
|
|
88
|
+
readonly 'indexing.chunkStrategy': "ast";
|
|
89
|
+
readonly 'search.similarityThreshold': 0.6;
|
|
90
|
+
readonly 'search.defaultLimit': 5;
|
|
91
|
+
readonly 'search.maxLimit': 20;
|
|
92
|
+
readonly 'reranker.enabled': false;
|
|
93
|
+
readonly 'reranker.provider': "openai-compatible";
|
|
94
|
+
readonly 'reranker.apiKey': "";
|
|
95
|
+
readonly 'reranker.model': "";
|
|
96
|
+
readonly 'reranker.overfetch': 3;
|
|
97
|
+
readonly 'logging.level': "error";
|
|
98
|
+
};
|
|
48
99
|
export declare function readConfigJson(): JsonConfig | null;
|
|
100
|
+
/**
|
|
101
|
+
* Migrate a legacy raw config object to the current shape, in place of a
|
|
102
|
+
* versioned migration system. Pure — returns a new object, never mutates input.
|
|
103
|
+
*
|
|
104
|
+
* - Legacy `ollama.host`/`ollama.model` → `embedding.{baseURL,model}` with
|
|
105
|
+
* `provider: 'ollama'` (only when `embedding` is not already present).
|
|
106
|
+
* - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
|
|
107
|
+
* are dropped so they neither validate-fail nor take effect.
|
|
108
|
+
* - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
|
|
109
|
+
* API) is rewritten to `openai-compatible`, and a stale `/api/rerank` URL is
|
|
110
|
+
* dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
|
|
111
|
+
* fail validation on existing configs and lock the user out.
|
|
112
|
+
*
|
|
113
|
+
* `ollama` itself is left for the schema to strip. Exported for unit testing.
|
|
114
|
+
*/
|
|
115
|
+
export declare function migrateRawConfig(raw: unknown): unknown;
|
|
116
|
+
/**
|
|
117
|
+
* Validate a parsed config object against the Zod schema. Unknown keys (e.g.
|
|
118
|
+
* the "$schema" editor reference) are stripped. Throws with a caller-friendly
|
|
119
|
+
* message on validation failure. Pure — no file I/O.
|
|
120
|
+
*/
|
|
121
|
+
export declare function parseConfig(raw: unknown, source?: string): JsonConfig;
|
|
122
|
+
/** Build the JSON Schema object generated from the Zod config schema. Pure. */
|
|
123
|
+
export declare function buildJsonSchema(): Record<string, unknown>;
|
|
49
124
|
export declare function writeConfigJson(data: JsonConfig): void;
|
|
125
|
+
/**
|
|
126
|
+
* Generate a JSON Schema from the Zod config schema and write it next to
|
|
127
|
+
* config.json. Editors resolve it via the "$schema" reference and provide
|
|
128
|
+
* key autocomplete, type hints, and inline validation while editing.
|
|
129
|
+
*/
|
|
130
|
+
export declare function writeSchemaFile(): void;
|
|
50
131
|
export declare function getNestedValue(cfg: JsonConfig, key: string): unknown;
|
|
51
132
|
export declare function setNestedValue(cfg: JsonConfig, key: string, raw: string): JsonConfig;
|
|
52
133
|
export declare function deleteNestedValue(cfg: JsonConfig, key: string): JsonConfig;
|
package/dist/src/config-store.js
CHANGED
|
@@ -33,9 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ALL_KEYS = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
|
|
36
|
+
exports.CONFIG_DEFAULTS = exports.ALL_KEYS = exports.ENV_FILE = exports.SCHEMA_FILE = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
|
|
37
37
|
exports.readConfigJson = readConfigJson;
|
|
38
|
+
exports.migrateRawConfig = migrateRawConfig;
|
|
39
|
+
exports.parseConfig = parseConfig;
|
|
40
|
+
exports.buildJsonSchema = buildJsonSchema;
|
|
38
41
|
exports.writeConfigJson = writeConfigJson;
|
|
42
|
+
exports.writeSchemaFile = writeSchemaFile;
|
|
39
43
|
exports.getNestedValue = getNestedValue;
|
|
40
44
|
exports.setNestedValue = setNestedValue;
|
|
41
45
|
exports.deleteNestedValue = deleteNestedValue;
|
|
@@ -43,45 +47,58 @@ const fs = __importStar(require("fs"));
|
|
|
43
47
|
const path = __importStar(require("path"));
|
|
44
48
|
const os = __importStar(require("os"));
|
|
45
49
|
const zod_1 = require("zod");
|
|
50
|
+
const constants_1 = require("./constants");
|
|
46
51
|
exports.CONFIG_DIR = path.join(os.homedir(), '.config', 'codesearch-mcp');
|
|
47
52
|
exports.CONFIG_FILE = path.join(exports.CONFIG_DIR, 'config.json');
|
|
53
|
+
exports.SCHEMA_FILE = path.join(exports.CONFIG_DIR, 'config.schema.json');
|
|
54
|
+
exports.ENV_FILE = path.join(exports.CONFIG_DIR, '.env');
|
|
55
|
+
// Relative $schema reference written into config.json so editors resolve the
|
|
56
|
+
// sibling schema file for autocomplete / inline validation.
|
|
57
|
+
const SCHEMA_REF = './config.schema.json';
|
|
48
58
|
// All fields optional — defaults live in config.ts. The file stays minimal.
|
|
59
|
+
// Descriptions surface as tooltips in schema-aware editors (VS Code, JetBrains).
|
|
49
60
|
const jsonSchema = zod_1.z.object({
|
|
50
61
|
db: zod_1.z.object({
|
|
51
|
-
host: zod_1.z.string().optional(),
|
|
52
|
-
port: zod_1.z.number().optional(),
|
|
53
|
-
user: zod_1.z.string().optional(),
|
|
54
|
-
password: zod_1.z.string().optional(),
|
|
55
|
-
name: zod_1.z.string().optional(),
|
|
62
|
+
host: zod_1.z.string().describe('PostgreSQL host').optional(),
|
|
63
|
+
port: zod_1.z.number().int().describe('PostgreSQL port (default 5433)').optional(),
|
|
64
|
+
user: zod_1.z.string().describe('PostgreSQL user').optional(),
|
|
65
|
+
password: zod_1.z.string().describe('PostgreSQL password').optional(),
|
|
66
|
+
name: zod_1.z.string().describe('Database name; defaults to rag_<os-username>, auto-created on first run').optional(),
|
|
56
67
|
}).optional(),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
embedding: zod_1.z.object({
|
|
69
|
+
provider: zod_1.z.enum(['ollama', 'openai-compatible', 'voyage', 'infinity']).describe('Embedding backend (default ollama)').optional(),
|
|
70
|
+
baseURL: zod_1.z.string().describe('Backend base URL. ollama: http://localhost:11434 · OpenAI: https://api.openai.com/v1 · Infinity/TEI/vLLM: http://localhost:7997 · Voyage: https://api.voyageai.com/v1').optional(),
|
|
71
|
+
apiKey: zod_1.z.string().describe('API key. Use env:VAR_NAME to read from ~/.config/codesearch-mcp/.env or the environment (recommended). Empty for local servers.').optional(),
|
|
72
|
+
model: zod_1.z.string().describe('Embedding model (default bge-m3)').optional(),
|
|
73
|
+
dimensions: zod_1.z.number().int().describe('Vector width override; required for models whose native width exceeds 2000 (Matryoshka reduction). Omit to derive from the model/registry/probe.').optional(),
|
|
74
|
+
batchSize: zod_1.z.number().int().describe('Texts per batch embedding request (default 32)').optional(),
|
|
60
75
|
}).optional(),
|
|
61
76
|
indexing: zod_1.z.object({
|
|
62
|
-
chunkSize: zod_1.z.number().optional(),
|
|
63
|
-
chunkOverlap: zod_1.z.number().optional(),
|
|
64
|
-
embedBatchSize: zod_1.z.number().optional(),
|
|
65
|
-
astBatchSize: zod_1.z.number().optional(),
|
|
77
|
+
chunkSize: zod_1.z.number().int().describe('Max chars per chunk (default 1500)').optional(),
|
|
78
|
+
chunkOverlap: zod_1.z.number().int().describe('Overlap between chunks (default 200)').optional(),
|
|
79
|
+
embedBatchSize: zod_1.z.number().int().describe('Rows per DB-insert batch when writing chunks (default 5). Embedding request size is embedding.batchSize.').optional(),
|
|
80
|
+
astBatchSize: zod_1.z.number().int().describe('Max files per ts-morph batch; larger projects skip dependency resolution (default 3000)').optional(),
|
|
81
|
+
chunkStrategy: zod_1.z.enum(['ast', 'character']).describe('How TS/JS files are chunked: ast = symbol-aligned boundaries, character = fixed-size recursive splitter (default ast)').optional(),
|
|
66
82
|
}).optional(),
|
|
67
83
|
search: zod_1.z.object({
|
|
68
|
-
similarityThreshold: zod_1.z.number().optional(),
|
|
69
|
-
defaultLimit: zod_1.z.number().optional(),
|
|
70
|
-
maxLimit: zod_1.z.number().optional(),
|
|
71
|
-
autoTranslate: zod_1.z.boolean().optional(),
|
|
72
|
-
translateModel: zod_1.z.string().optional(),
|
|
84
|
+
similarityThreshold: zod_1.z.number().min(0).max(1).describe('Max cosine distance 0–1; lower = stricter (default 0.6)').optional(),
|
|
85
|
+
defaultLimit: zod_1.z.number().int().describe('Default result count (default 5)').optional(),
|
|
86
|
+
maxLimit: zod_1.z.number().int().describe('Hard cap on results (default 20)').optional(),
|
|
73
87
|
}).optional(),
|
|
74
88
|
reranker: zod_1.z.object({
|
|
75
|
-
enabled: zod_1.z.boolean().optional(),
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
enabled: zod_1.z.boolean().describe('Enable cross-encoder reranking; requires reranker.model').optional(),
|
|
90
|
+
provider: zod_1.z.enum(['openai-compatible']).describe('Rerank backend. Only openai-compatible /rerank servers (Infinity, TEI, vLLM) — Ollama has no rerank API.').optional(),
|
|
91
|
+
baseURL: zod_1.z.string().describe('Rerank server base URL (defaults to embedding.baseURL — Infinity serves both).').optional(),
|
|
92
|
+
apiKey: zod_1.z.string().describe('API key, or env:VAR_NAME. Empty for local servers.').optional(),
|
|
93
|
+
model: zod_1.z.string().describe('Reranker model name (e.g. qllama/bge-reranker-v2-m3)').optional(),
|
|
94
|
+
url: zod_1.z.string().describe('Full URL of the rerank endpoint (default derived from provider + baseURL)').optional(),
|
|
95
|
+
overfetch: zod_1.z.number().int().min(1).max(20).describe('Candidate multiplier before reranking (limit × N, default 3)').optional(),
|
|
79
96
|
}).optional(),
|
|
80
97
|
logging: zod_1.z.object({
|
|
81
|
-
level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).optional(),
|
|
82
|
-
dir: zod_1.z.string().optional(),
|
|
98
|
+
level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).describe('File log level (default error)').optional(),
|
|
99
|
+
dir: zod_1.z.string().describe('Base log directory; "false" to disable file logging').optional(),
|
|
83
100
|
}).optional(),
|
|
84
|
-
workspaceRoot: zod_1.z.string().optional(),
|
|
101
|
+
workspaceRoot: zod_1.z.string().describe('Full path to the directory containing all your projects').optional(),
|
|
85
102
|
});
|
|
86
103
|
const FIELD_TYPES = {
|
|
87
104
|
'db.host': 'string',
|
|
@@ -89,18 +106,24 @@ const FIELD_TYPES = {
|
|
|
89
106
|
'db.user': 'string',
|
|
90
107
|
'db.password': 'string',
|
|
91
108
|
'db.name': 'string',
|
|
92
|
-
'
|
|
93
|
-
'
|
|
109
|
+
'embedding.provider': 'string',
|
|
110
|
+
'embedding.baseURL': 'string',
|
|
111
|
+
'embedding.apiKey': 'string',
|
|
112
|
+
'embedding.model': 'string',
|
|
113
|
+
'embedding.dimensions': 'number',
|
|
114
|
+
'embedding.batchSize': 'number',
|
|
94
115
|
'indexing.chunkSize': 'number',
|
|
95
116
|
'indexing.chunkOverlap': 'number',
|
|
96
117
|
'indexing.embedBatchSize': 'number',
|
|
97
118
|
'indexing.astBatchSize': 'number',
|
|
119
|
+
'indexing.chunkStrategy': 'string',
|
|
98
120
|
'search.similarityThreshold': 'number',
|
|
99
121
|
'search.defaultLimit': 'number',
|
|
100
122
|
'search.maxLimit': 'number',
|
|
101
|
-
'search.autoTranslate': 'boolean',
|
|
102
|
-
'search.translateModel': 'string',
|
|
103
123
|
'reranker.enabled': 'boolean',
|
|
124
|
+
'reranker.provider': 'string',
|
|
125
|
+
'reranker.baseURL': 'string',
|
|
126
|
+
'reranker.apiKey': 'string',
|
|
104
127
|
'reranker.model': 'string',
|
|
105
128
|
'reranker.url': 'string',
|
|
106
129
|
'reranker.overfetch': 'number',
|
|
@@ -109,6 +132,39 @@ const FIELD_TYPES = {
|
|
|
109
132
|
'workspaceRoot': 'string',
|
|
110
133
|
};
|
|
111
134
|
exports.ALL_KEYS = Object.keys(FIELD_TYPES);
|
|
135
|
+
/**
|
|
136
|
+
* Single source of truth for built-in defaults, keyed by raw config.json dotted
|
|
137
|
+
* key. Consumed both by the web dashboard (`GET /api/defaults`) and by
|
|
138
|
+
* `resolvedSchema` in `src/config.ts`, whose `.default(...)` calls reference
|
|
139
|
+
* these values — so the runtime default and the dashboard-displayed default
|
|
140
|
+
* cannot diverge. `as const` keeps the literal types so those `.default()`
|
|
141
|
+
* lookups stay type-safe. Keys omitted here have no static default: required
|
|
142
|
+
* fields (db.host/user/password) and values derived at runtime — db.name →
|
|
143
|
+
* rag_<user>, and the provider-dependent URLs (embedding.baseURL,
|
|
144
|
+
* reranker.baseURL, reranker.url), which are computed in the `Config`
|
|
145
|
+
* constructor per provider rather than through a schema `.default(...)`.
|
|
146
|
+
*/
|
|
147
|
+
exports.CONFIG_DEFAULTS = {
|
|
148
|
+
'db.port': 5433,
|
|
149
|
+
'embedding.provider': 'ollama',
|
|
150
|
+
'embedding.apiKey': '',
|
|
151
|
+
'embedding.model': constants_1.DEFAULT_EMBED_MODEL,
|
|
152
|
+
'embedding.batchSize': 32,
|
|
153
|
+
'indexing.chunkSize': 1500,
|
|
154
|
+
'indexing.chunkOverlap': 200,
|
|
155
|
+
'indexing.embedBatchSize': 5,
|
|
156
|
+
'indexing.astBatchSize': 3000,
|
|
157
|
+
'indexing.chunkStrategy': 'ast',
|
|
158
|
+
'search.similarityThreshold': 0.6,
|
|
159
|
+
'search.defaultLimit': 5,
|
|
160
|
+
'search.maxLimit': 20,
|
|
161
|
+
'reranker.enabled': false,
|
|
162
|
+
'reranker.provider': 'openai-compatible',
|
|
163
|
+
'reranker.apiKey': '',
|
|
164
|
+
'reranker.model': '',
|
|
165
|
+
'reranker.overfetch': 3,
|
|
166
|
+
'logging.level': 'error',
|
|
167
|
+
};
|
|
112
168
|
function readConfigJson() {
|
|
113
169
|
if (!fs.existsSync(exports.CONFIG_FILE))
|
|
114
170
|
return null;
|
|
@@ -127,17 +183,86 @@ function readConfigJson() {
|
|
|
127
183
|
throw new Error(`Invalid JSON in ${exports.CONFIG_FILE}: ${e.message}\n` +
|
|
128
184
|
`Fix the file manually or delete it and run: rag-setup`);
|
|
129
185
|
}
|
|
130
|
-
|
|
186
|
+
return parseConfig(migrateRawConfig(parsed), exports.CONFIG_FILE);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Migrate a legacy raw config object to the current shape, in place of a
|
|
190
|
+
* versioned migration system. Pure — returns a new object, never mutates input.
|
|
191
|
+
*
|
|
192
|
+
* - Legacy `ollama.host`/`ollama.model` → `embedding.{baseURL,model}` with
|
|
193
|
+
* `provider: 'ollama'` (only when `embedding` is not already present).
|
|
194
|
+
* - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
|
|
195
|
+
* are dropped so they neither validate-fail nor take effect.
|
|
196
|
+
* - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
|
|
197
|
+
* API) is rewritten to `openai-compatible`, and a stale `/api/rerank` URL is
|
|
198
|
+
* dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
|
|
199
|
+
* fail validation on existing configs and lock the user out.
|
|
200
|
+
*
|
|
201
|
+
* `ollama` itself is left for the schema to strip. Exported for unit testing.
|
|
202
|
+
*/
|
|
203
|
+
function migrateRawConfig(raw) {
|
|
204
|
+
if (raw == null || typeof raw !== 'object')
|
|
205
|
+
return raw;
|
|
206
|
+
const obj = JSON.parse(JSON.stringify(raw));
|
|
207
|
+
const legacyOllama = obj.ollama;
|
|
208
|
+
if (!obj.embedding && legacyOllama && typeof legacyOllama === 'object') {
|
|
209
|
+
const embedding = { provider: 'ollama' };
|
|
210
|
+
if (typeof legacyOllama.host === 'string')
|
|
211
|
+
embedding.baseURL = legacyOllama.host;
|
|
212
|
+
if (typeof legacyOllama.model === 'string')
|
|
213
|
+
embedding.model = legacyOllama.model;
|
|
214
|
+
obj.embedding = embedding;
|
|
215
|
+
}
|
|
216
|
+
delete obj.ollama;
|
|
217
|
+
if (obj.search && typeof obj.search === 'object') {
|
|
218
|
+
delete obj.search.autoTranslate;
|
|
219
|
+
delete obj.search.translateModel;
|
|
220
|
+
}
|
|
221
|
+
if (obj.reranker && typeof obj.reranker === 'object') {
|
|
222
|
+
if (obj.reranker.provider === 'ollama')
|
|
223
|
+
obj.reranker.provider = 'openai-compatible';
|
|
224
|
+
if (typeof obj.reranker.url === 'string' && /\/api\/rerank\/?(\?.*)?$/.test(obj.reranker.url))
|
|
225
|
+
delete obj.reranker.url;
|
|
226
|
+
}
|
|
227
|
+
return obj;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Validate a parsed config object against the Zod schema. Unknown keys (e.g.
|
|
231
|
+
* the "$schema" editor reference) are stripped. Throws with a caller-friendly
|
|
232
|
+
* message on validation failure. Pure — no file I/O.
|
|
233
|
+
*/
|
|
234
|
+
function parseConfig(raw, source = exports.CONFIG_FILE) {
|
|
235
|
+
const result = jsonSchema.safeParse(raw);
|
|
131
236
|
if (!result.success) {
|
|
132
237
|
const issues = result.error.issues.map(i => ` ${i.path.join('.')}: ${i.message}`).join('\n');
|
|
133
|
-
throw new Error(`Invalid config in ${
|
|
238
|
+
throw new Error(`Invalid config in ${source}:\n${issues}\n` +
|
|
134
239
|
`Fix with: rag-config set <key> <value>`);
|
|
135
240
|
}
|
|
136
241
|
return result.data;
|
|
137
242
|
}
|
|
243
|
+
/** Build the JSON Schema object generated from the Zod config schema. Pure. */
|
|
244
|
+
function buildJsonSchema() {
|
|
245
|
+
return {
|
|
246
|
+
$id: 'https://github.com/oleksandr-antoniuk/RAG/config.schema.json',
|
|
247
|
+
title: 'codesearch-mcp config',
|
|
248
|
+
...zod_1.z.toJSONSchema(jsonSchema),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
138
251
|
function writeConfigJson(data) {
|
|
139
252
|
fs.mkdirSync(exports.CONFIG_DIR, { recursive: true });
|
|
140
|
-
|
|
253
|
+
writeSchemaFile();
|
|
254
|
+
// $schema first so editors pick it up; readConfigJson strips it on validation.
|
|
255
|
+
const withSchema = { $schema: SCHEMA_REF, ...data };
|
|
256
|
+
fs.writeFileSync(exports.CONFIG_FILE, JSON.stringify(withSchema, null, 2) + '\n', { mode: 0o600 });
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Generate a JSON Schema from the Zod config schema and write it next to
|
|
260
|
+
* config.json. Editors resolve it via the "$schema" reference and provide
|
|
261
|
+
* key autocomplete, type hints, and inline validation while editing.
|
|
262
|
+
*/
|
|
263
|
+
function writeSchemaFile() {
|
|
264
|
+
fs.mkdirSync(exports.CONFIG_DIR, { recursive: true });
|
|
265
|
+
fs.writeFileSync(exports.SCHEMA_FILE, JSON.stringify(buildJsonSchema(), null, 2) + '\n');
|
|
141
266
|
}
|
|
142
267
|
function getNestedValue(cfg, key) {
|
|
143
268
|
const parts = key.split('.');
|
package/dist/src/config.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export declare class ConfigError extends Error {
|
|
2
2
|
constructor(message: string);
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a config value that may reference an environment variable. A value of
|
|
6
|
+
* the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
|
|
7
|
+
* (including empty) is returned verbatim. A referenced-but-unset variable throws
|
|
8
|
+
* a clear error naming the variable and the `.env` file. Pure aside from the
|
|
9
|
+
* injected env map. See `src/app.ts` for how `.env` is loaded into the process.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveEnvRef(value: string, env?: NodeJS.ProcessEnv): string;
|
|
4
12
|
export declare class Config {
|
|
5
13
|
private readonly data;
|
|
6
14
|
get db(): {
|
|
@@ -10,26 +18,42 @@ export declare class Config {
|
|
|
10
18
|
password: string;
|
|
11
19
|
database: string;
|
|
12
20
|
};
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Embedding config. `apiKey` is returned **raw** (possibly an `env:VAR` ref) —
|
|
23
|
+
* callers resolve it lazily via `resolveEnvRef` at the moment they build a
|
|
24
|
+
* request, so an unset referenced variable fails only when the key is actually
|
|
25
|
+
* needed (during embed/search) rather than crashing container construction and
|
|
26
|
+
* locking the user out of `rag-web`/`rag-mcp`.
|
|
27
|
+
*/
|
|
28
|
+
get embedding(): {
|
|
29
|
+
provider: "ollama" | "openai-compatible" | "voyage" | "infinity";
|
|
30
|
+
baseURL: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
model: string;
|
|
33
|
+
dimensions: number | null;
|
|
34
|
+
batchSize: number;
|
|
35
|
+
};
|
|
36
|
+
/** Reranker config. `apiKey` is raw — resolve lazily with `resolveEnvRef`. */
|
|
37
|
+
get reranker(): {
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
provider: "openai-compatible";
|
|
40
|
+
baseURL: string;
|
|
41
|
+
apiKey: string;
|
|
15
42
|
model: string;
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
url: string;
|
|
44
|
+
overfetch: number;
|
|
18
45
|
};
|
|
19
46
|
get indexer(): {
|
|
20
47
|
chunkSize: number;
|
|
21
48
|
chunkOverlap: number;
|
|
22
49
|
batchSize: number;
|
|
23
50
|
astBatchSize: number;
|
|
51
|
+
chunkStrategy: "ast" | "character";
|
|
24
52
|
};
|
|
25
53
|
get search(): {
|
|
26
54
|
similarityThreshold: number;
|
|
27
55
|
defaultLimit: number;
|
|
28
56
|
maxLimit: number;
|
|
29
|
-
autoTranslate: boolean;
|
|
30
|
-
translateModel: string;
|
|
31
|
-
rerankerEnabled: boolean;
|
|
32
|
-
rerankerOverfetch: number;
|
|
33
57
|
};
|
|
34
58
|
get logging(): {
|
|
35
59
|
level: "error" | "debug" | "info" | "warn";
|