@vohongtho.infotech/code-intel 0.9.0 → 1.0.1
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 +555 -125
- package/dist/cli/main.js +2497 -679
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +76 -13
- package/dist/index.js +1883 -1251
- package/dist/index.js.map +1 -1
- package/dist/web/assets/{es-CkND97V_.js → es-Bu8iwdFw.js} +1 -1
- package/dist/web/assets/index-C9M6YLlS.css +2 -0
- package/dist/web/assets/index-CKc3HEpe.js +354 -0
- package/dist/web/index.html +17 -11
- package/package.json +1 -1
- package/dist/web/assets/index-DExLzJ89.js +0 -348
- package/dist/web/assets/index-DSIgTcZc.css +0 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import { createRequire } from 'module';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
import path31 from 'path';
|
|
4
|
-
import fs24, { existsSync } from 'fs';
|
|
5
|
-
import { Parser, Language, Query } from 'web-tree-sitter';
|
|
6
1
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
7
2
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
8
3
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
@@ -11,9 +6,15 @@ import { SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, SEMRESATTRS_SERVICE_NAME } from '@o
|
|
|
11
6
|
import { trace, context, SpanStatusCode } from '@opentelemetry/api';
|
|
12
7
|
import winston from 'winston';
|
|
13
8
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
9
|
+
import fs26, { existsSync } from 'fs';
|
|
10
|
+
import path32 from 'path';
|
|
14
11
|
import os13 from 'os';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { Parser, Language, Query } from 'web-tree-sitter';
|
|
15
|
+
import { Database, Connection } from '@ladybugdb/core';
|
|
15
16
|
import { execSync } from 'child_process';
|
|
16
|
-
import
|
|
17
|
+
import Database2 from 'better-sqlite3';
|
|
17
18
|
import bcrypt from 'bcrypt';
|
|
18
19
|
import crypto5 from 'crypto';
|
|
19
20
|
import { v4 } from 'uuid';
|
|
@@ -24,7 +25,6 @@ import 'events';
|
|
|
24
25
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
25
26
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
26
27
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
27
|
-
import { Database, Connection } from '@ladybugdb/core';
|
|
28
28
|
import express from 'express';
|
|
29
29
|
import compression from 'compression';
|
|
30
30
|
import cors from 'cors';
|
|
@@ -56,6 +56,135 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
56
56
|
};
|
|
57
57
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
58
58
|
|
|
59
|
+
// src/graph/knowledge-graph.ts
|
|
60
|
+
var knowledge_graph_exports = {};
|
|
61
|
+
__export(knowledge_graph_exports, {
|
|
62
|
+
createKnowledgeGraph: () => createKnowledgeGraph
|
|
63
|
+
});
|
|
64
|
+
function createKnowledgeGraph() {
|
|
65
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
66
|
+
const edges = /* @__PURE__ */ new Map();
|
|
67
|
+
const edgesByKind = /* @__PURE__ */ new Map();
|
|
68
|
+
const edgesFromNode = /* @__PURE__ */ new Map();
|
|
69
|
+
const edgesToNode = /* @__PURE__ */ new Map();
|
|
70
|
+
function indexEdge(edge) {
|
|
71
|
+
let kindSet = edgesByKind.get(edge.kind);
|
|
72
|
+
if (!kindSet) {
|
|
73
|
+
kindSet = /* @__PURE__ */ new Set();
|
|
74
|
+
edgesByKind.set(edge.kind, kindSet);
|
|
75
|
+
}
|
|
76
|
+
kindSet.add(edge.id);
|
|
77
|
+
let fromSet = edgesFromNode.get(edge.source);
|
|
78
|
+
if (!fromSet) {
|
|
79
|
+
fromSet = /* @__PURE__ */ new Set();
|
|
80
|
+
edgesFromNode.set(edge.source, fromSet);
|
|
81
|
+
}
|
|
82
|
+
fromSet.add(edge.id);
|
|
83
|
+
let toSet = edgesToNode.get(edge.target);
|
|
84
|
+
if (!toSet) {
|
|
85
|
+
toSet = /* @__PURE__ */ new Set();
|
|
86
|
+
edgesToNode.set(edge.target, toSet);
|
|
87
|
+
}
|
|
88
|
+
toSet.add(edge.id);
|
|
89
|
+
}
|
|
90
|
+
function unindexEdge(edge) {
|
|
91
|
+
edgesByKind.get(edge.kind)?.delete(edge.id);
|
|
92
|
+
edgesFromNode.get(edge.source)?.delete(edge.id);
|
|
93
|
+
edgesToNode.get(edge.target)?.delete(edge.id);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
addNode(node) {
|
|
97
|
+
nodes.set(node.id, node);
|
|
98
|
+
},
|
|
99
|
+
addEdge(edge) {
|
|
100
|
+
edges.set(edge.id, edge);
|
|
101
|
+
indexEdge(edge);
|
|
102
|
+
},
|
|
103
|
+
getNode(id) {
|
|
104
|
+
return nodes.get(id);
|
|
105
|
+
},
|
|
106
|
+
getEdge(id) {
|
|
107
|
+
return edges.get(id);
|
|
108
|
+
},
|
|
109
|
+
*findEdgesByKind(kind) {
|
|
110
|
+
const ids = edgesByKind.get(kind);
|
|
111
|
+
if (!ids) return;
|
|
112
|
+
for (const id of ids) {
|
|
113
|
+
const edge = edges.get(id);
|
|
114
|
+
if (edge) yield edge;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
*findEdgesFrom(sourceId) {
|
|
118
|
+
const ids = edgesFromNode.get(sourceId);
|
|
119
|
+
if (!ids) return;
|
|
120
|
+
for (const id of ids) {
|
|
121
|
+
const edge = edges.get(id);
|
|
122
|
+
if (edge) yield edge;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
*findEdgesTo(targetId) {
|
|
126
|
+
const ids = edgesToNode.get(targetId);
|
|
127
|
+
if (!ids) return;
|
|
128
|
+
for (const id of ids) {
|
|
129
|
+
const edge = edges.get(id);
|
|
130
|
+
if (edge) yield edge;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
removeNodeCascade(id) {
|
|
134
|
+
const fromEdges = edgesFromNode.get(id);
|
|
135
|
+
if (fromEdges) {
|
|
136
|
+
for (const edgeId of [...fromEdges]) {
|
|
137
|
+
const edge = edges.get(edgeId);
|
|
138
|
+
if (edge) {
|
|
139
|
+
unindexEdge(edge);
|
|
140
|
+
edges.delete(edgeId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const toEdges = edgesToNode.get(id);
|
|
145
|
+
if (toEdges) {
|
|
146
|
+
for (const edgeId of [...toEdges]) {
|
|
147
|
+
const edge = edges.get(edgeId);
|
|
148
|
+
if (edge) {
|
|
149
|
+
unindexEdge(edge);
|
|
150
|
+
edges.delete(edgeId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
edgesFromNode.delete(id);
|
|
155
|
+
edgesToNode.delete(id);
|
|
156
|
+
nodes.delete(id);
|
|
157
|
+
},
|
|
158
|
+
removeEdge(id) {
|
|
159
|
+
const edge = edges.get(id);
|
|
160
|
+
if (edge) {
|
|
161
|
+
unindexEdge(edge);
|
|
162
|
+
edges.delete(id);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
*allNodes() {
|
|
166
|
+
yield* nodes.values();
|
|
167
|
+
},
|
|
168
|
+
*allEdges() {
|
|
169
|
+
yield* edges.values();
|
|
170
|
+
},
|
|
171
|
+
get size() {
|
|
172
|
+
return { nodes: nodes.size, edges: edges.size };
|
|
173
|
+
},
|
|
174
|
+
clear() {
|
|
175
|
+
nodes.clear();
|
|
176
|
+
edges.clear();
|
|
177
|
+
edgesByKind.clear();
|
|
178
|
+
edgesFromNode.clear();
|
|
179
|
+
edgesToNode.clear();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
var init_knowledge_graph = __esm({
|
|
184
|
+
"src/graph/knowledge-graph.ts"() {
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
59
188
|
// src/graph/id-generator.ts
|
|
60
189
|
function generateNodeId(kind, filePath, qualifiedName) {
|
|
61
190
|
return `${kind}:${filePath}:${qualifiedName}`;
|
|
@@ -68,310 +197,701 @@ var init_id_generator = __esm({
|
|
|
68
197
|
}
|
|
69
198
|
});
|
|
70
199
|
|
|
71
|
-
// src/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
200
|
+
// src/storage/schema.ts
|
|
201
|
+
function getCreateNodeTableDDL(tableName) {
|
|
202
|
+
return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
|
|
203
|
+
id STRING,
|
|
204
|
+
name STRING,
|
|
205
|
+
file_path STRING,
|
|
206
|
+
start_line INT64,
|
|
207
|
+
end_line INT64,
|
|
208
|
+
exported BOOLEAN,
|
|
209
|
+
content STRING,
|
|
210
|
+
metadata STRING,
|
|
211
|
+
PRIMARY KEY (id)
|
|
212
|
+
)`;
|
|
81
213
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
214
|
+
function getCreateEdgeTableDDL() {
|
|
215
|
+
const uniqueTables = ALL_NODE_TABLES;
|
|
216
|
+
const fromToPairs = [];
|
|
217
|
+
for (const from of uniqueTables) {
|
|
218
|
+
for (const to of uniqueTables) {
|
|
219
|
+
fromToPairs.push(`FROM ${from} TO ${to}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
|
|
223
|
+
${fromToPairs.join(",\n ")},
|
|
224
|
+
kind STRING,
|
|
225
|
+
weight DOUBLE,
|
|
226
|
+
label STRING
|
|
227
|
+
)`];
|
|
84
228
|
}
|
|
85
|
-
var
|
|
86
|
-
var
|
|
87
|
-
"src/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
".cs": "csharp" /* CSharp */,
|
|
110
|
-
".rs": "rust" /* Rust */,
|
|
111
|
-
".php": "php" /* PHP */,
|
|
112
|
-
".kt": "kotlin" /* Kotlin */,
|
|
113
|
-
".kts": "kotlin" /* Kotlin */,
|
|
114
|
-
".rb": "ruby" /* Ruby */,
|
|
115
|
-
".swift": "swift" /* Swift */,
|
|
116
|
-
".dart": "dart" /* Dart */
|
|
229
|
+
var NODE_TABLE_MAP, ALL_NODE_TABLES;
|
|
230
|
+
var init_schema = __esm({
|
|
231
|
+
"src/storage/schema.ts"() {
|
|
232
|
+
NODE_TABLE_MAP = {
|
|
233
|
+
file: "file_nodes",
|
|
234
|
+
directory: "dir_nodes",
|
|
235
|
+
function: "func_nodes",
|
|
236
|
+
class: "class_nodes",
|
|
237
|
+
interface: "iface_nodes",
|
|
238
|
+
method: "method_nodes",
|
|
239
|
+
constructor: "ctor_nodes",
|
|
240
|
+
variable: "var_nodes",
|
|
241
|
+
property: "prop_nodes",
|
|
242
|
+
struct: "struct_nodes",
|
|
243
|
+
enum: "enum_nodes",
|
|
244
|
+
trait: "trait_nodes",
|
|
245
|
+
namespace: "ns_nodes",
|
|
246
|
+
module: "mod_nodes",
|
|
247
|
+
type_alias: "type_nodes",
|
|
248
|
+
constant: "const_nodes",
|
|
249
|
+
route: "route_nodes",
|
|
250
|
+
cluster: "cluster_nodes",
|
|
251
|
+
flow: "flow_nodes",
|
|
252
|
+
vulnerability: "vuln_nodes"
|
|
117
253
|
};
|
|
254
|
+
ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
|
|
118
255
|
}
|
|
119
256
|
});
|
|
120
257
|
|
|
121
|
-
// src/
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
258
|
+
// src/observability/tracing.ts
|
|
259
|
+
var tracing_exports = {};
|
|
260
|
+
__export(tracing_exports, {
|
|
261
|
+
SpanStatusCode: () => SpanStatusCode,
|
|
262
|
+
context: () => context,
|
|
263
|
+
getActiveTraceContext: () => getActiveTraceContext,
|
|
264
|
+
getTracer: () => getTracer,
|
|
265
|
+
initTracing: () => initTracing,
|
|
266
|
+
isTracingEnabled: () => isTracingEnabled,
|
|
267
|
+
sanitizeAttrs: () => sanitizeAttrs,
|
|
268
|
+
shutdownTracing: () => shutdownTracing,
|
|
269
|
+
trace: () => trace,
|
|
270
|
+
withSpan: () => withSpan
|
|
127
271
|
});
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
const candidates = [
|
|
131
|
-
path31.join(fileDir, "wasm"),
|
|
132
|
-
// dist/index.js → dist/wasm/
|
|
133
|
-
path31.join(fileDir, "../wasm")
|
|
134
|
-
// dist/cli/main.js → dist/wasm/
|
|
135
|
-
];
|
|
136
|
-
for (const candidate of candidates) {
|
|
137
|
-
if (existsSync(candidate)) return candidate;
|
|
138
|
-
}
|
|
139
|
-
return candidates[0];
|
|
272
|
+
function isTracingEnabled() {
|
|
273
|
+
return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
|
|
140
274
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
|
|
163
|
-
["dart" /* Dart */]: "tree-sitter-dart.wasm"
|
|
164
|
-
};
|
|
165
|
-
const relative = WASM_PACKAGE_MAP[lang];
|
|
166
|
-
if (relative) {
|
|
167
|
-
try {
|
|
168
|
-
return _require.resolve(relative);
|
|
169
|
-
} catch {
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const bundled = BUNDLED_WASM_MAP[lang];
|
|
173
|
-
if (bundled) {
|
|
174
|
-
const bundledPath = path31.join(_bundledWasmDir, bundled);
|
|
175
|
-
if (existsSync(bundledPath)) return bundledPath;
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
async function initParser() {
|
|
180
|
-
if (!initPromise) {
|
|
181
|
-
initPromise = Parser.init();
|
|
182
|
-
}
|
|
183
|
-
return initPromise;
|
|
184
|
-
}
|
|
185
|
-
async function getLanguage(lang) {
|
|
186
|
-
if (languageCache.has(lang)) return languageCache.get(lang);
|
|
187
|
-
const path32 = wasmPath(lang);
|
|
188
|
-
if (!path32) {
|
|
189
|
-
languageCache.set(lang, null);
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
await initParser();
|
|
194
|
-
const language = await Language.load(path32);
|
|
195
|
-
languageCache.set(lang, language);
|
|
196
|
-
return language;
|
|
197
|
-
} catch {
|
|
198
|
-
languageCache.set(lang, null);
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
275
|
+
function initTracing() {
|
|
276
|
+
if (!isTracingEnabled()) return;
|
|
277
|
+
if (_sdk) return;
|
|
278
|
+
const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
|
|
279
|
+
const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
|
|
280
|
+
const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
|
|
281
|
+
const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
|
|
282
|
+
_sdk = new NodeSDK({
|
|
283
|
+
resource: resourceFromAttributes({
|
|
284
|
+
[SEMRESATTRS_SERVICE_NAME]: serviceName,
|
|
285
|
+
[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
|
|
286
|
+
}),
|
|
287
|
+
traceExporter: exporter,
|
|
288
|
+
instrumentations: [
|
|
289
|
+
getNodeAutoInstrumentations({
|
|
290
|
+
// Disable noisy file-system instrumentation
|
|
291
|
+
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
292
|
+
})
|
|
293
|
+
]
|
|
294
|
+
});
|
|
295
|
+
_sdk.start();
|
|
201
296
|
}
|
|
202
|
-
async function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (!parser) {
|
|
207
|
-
parser = new Parser();
|
|
208
|
-
parserCache.set(lang, parser);
|
|
297
|
+
async function shutdownTracing() {
|
|
298
|
+
if (_sdk) {
|
|
299
|
+
await _sdk.shutdown();
|
|
300
|
+
_sdk = null;
|
|
209
301
|
}
|
|
210
|
-
parser.setLanguage(language);
|
|
211
|
-
return parser;
|
|
212
|
-
}
|
|
213
|
-
async function parseSource(lang, source) {
|
|
214
|
-
const parser = await getParser(lang);
|
|
215
|
-
if (!parser) return null;
|
|
216
|
-
return parser.parse(source);
|
|
217
302
|
}
|
|
218
|
-
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
|
|
222
|
-
var init_parser_manager = __esm({
|
|
223
|
-
"src/parsing/parser-manager.ts"() {
|
|
224
|
-
init_shared();
|
|
225
|
-
_require = createRequire(import.meta.url);
|
|
226
|
-
_bundledWasmDir = findBundledWasmDir();
|
|
227
|
-
initPromise = null;
|
|
228
|
-
languageCache = /* @__PURE__ */ new Map();
|
|
229
|
-
parserCache = /* @__PURE__ */ new Map();
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
function getOrCompileQuery(language, querySource) {
|
|
233
|
-
let langMap = _queryCache.get(language);
|
|
234
|
-
if (!langMap) {
|
|
235
|
-
langMap = /* @__PURE__ */ new Map();
|
|
236
|
-
_queryCache.set(language, langMap);
|
|
237
|
-
}
|
|
238
|
-
let q = langMap.get(querySource);
|
|
239
|
-
if (!q) {
|
|
240
|
-
q = new Query(language, querySource);
|
|
241
|
-
langMap.set(querySource, q);
|
|
242
|
-
}
|
|
243
|
-
return q;
|
|
303
|
+
function getTracer() {
|
|
304
|
+
return trace.getTracer(TRACER_NAME);
|
|
244
305
|
}
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
306
|
+
async function withSpan(name, attrs, fn) {
|
|
307
|
+
return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
|
|
308
|
+
try {
|
|
309
|
+
const result = await fn(span);
|
|
310
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
311
|
+
return result;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
span.setStatus({
|
|
314
|
+
code: SpanStatusCode.ERROR,
|
|
315
|
+
message: err instanceof Error ? err.message : String(err)
|
|
255
316
|
});
|
|
317
|
+
span.recordException(err);
|
|
318
|
+
throw err;
|
|
319
|
+
} finally {
|
|
320
|
+
span.end();
|
|
256
321
|
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function sanitizeAttrs(attrs) {
|
|
325
|
+
const safe = {};
|
|
326
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
327
|
+
if (BLOCKED_ATTR_KEYS.test(k)) continue;
|
|
328
|
+
safe[k] = v;
|
|
257
329
|
}
|
|
258
|
-
return
|
|
330
|
+
return safe;
|
|
259
331
|
}
|
|
260
|
-
function
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
captures: m.captures.map((c) => ({
|
|
266
|
-
name: c.name,
|
|
267
|
-
node: c.node,
|
|
268
|
-
text: c.node.text
|
|
269
|
-
}))
|
|
270
|
-
}));
|
|
332
|
+
function getActiveTraceContext() {
|
|
333
|
+
const span = trace.getActiveSpan();
|
|
334
|
+
if (!span) return { traceId: "", spanId: "" };
|
|
335
|
+
const ctx = span.spanContext();
|
|
336
|
+
return { traceId: ctx.traceId, spanId: ctx.spanId };
|
|
271
337
|
}
|
|
272
|
-
var
|
|
273
|
-
var
|
|
274
|
-
"src/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// src/parsing/queries/typescript.ts
|
|
280
|
-
var typescriptQueries;
|
|
281
|
-
var init_typescript = __esm({
|
|
282
|
-
"src/parsing/queries/typescript.ts"() {
|
|
283
|
-
typescriptQueries = `
|
|
284
|
-
;; Class declaration
|
|
285
|
-
(class_declaration
|
|
286
|
-
name: (type_identifier) @def.class.name) @def.class
|
|
287
|
-
|
|
288
|
-
;; Abstract class
|
|
289
|
-
(abstract_class_declaration
|
|
290
|
-
name: (type_identifier) @def.class.name) @def.class
|
|
291
|
-
|
|
292
|
-
;; Interface declaration
|
|
293
|
-
(interface_declaration
|
|
294
|
-
name: (type_identifier) @def.interface.name) @def.interface
|
|
295
|
-
|
|
296
|
-
;; Type alias
|
|
297
|
-
(type_alias_declaration
|
|
298
|
-
name: (type_identifier) @def.type_alias.name) @def.type_alias
|
|
299
|
-
|
|
300
|
-
;; Enum
|
|
301
|
-
(enum_declaration
|
|
302
|
-
name: (identifier) @def.enum.name) @def.enum
|
|
303
|
-
|
|
304
|
-
;; Function declaration
|
|
305
|
-
(function_declaration
|
|
306
|
-
name: (identifier) @def.func.name) @def.func
|
|
307
|
-
|
|
308
|
-
;; Arrow function in variable
|
|
309
|
-
(lexical_declaration
|
|
310
|
-
(variable_declarator
|
|
311
|
-
name: (identifier) @def.func.name
|
|
312
|
-
value: (arrow_function))) @def.func
|
|
313
|
-
|
|
314
|
-
;; Const/let/var variable (non-function)
|
|
315
|
-
(lexical_declaration
|
|
316
|
-
(variable_declarator
|
|
317
|
-
name: (identifier) @def.var.name
|
|
318
|
-
value: (_) @def.var.value)) @def.var
|
|
319
|
-
|
|
320
|
-
;; Method definition
|
|
321
|
-
(method_definition
|
|
322
|
-
name: (property_identifier) @def.method.name) @def.method
|
|
323
|
-
|
|
324
|
-
;; Public field definition
|
|
325
|
-
(public_field_definition
|
|
326
|
-
name: (property_identifier) @def.property.name) @def.property
|
|
327
|
-
|
|
328
|
-
;; Import statement
|
|
329
|
-
(import_statement
|
|
330
|
-
source: (string) @imp.source) @imp
|
|
331
|
-
|
|
332
|
-
;; Import specifier
|
|
333
|
-
(import_specifier
|
|
334
|
-
name: (identifier) @imp.name)
|
|
335
|
-
|
|
336
|
-
;; Namespace import
|
|
337
|
-
(namespace_import (identifier) @imp.namespace)
|
|
338
|
-
|
|
339
|
-
;; Export statement
|
|
340
|
-
(export_statement) @export
|
|
341
|
-
|
|
342
|
-
;; Call expression \u2014 function call
|
|
343
|
-
(call_expression
|
|
344
|
-
function: (identifier) @call.name) @call
|
|
345
|
-
|
|
346
|
-
;; Call expression \u2014 member call
|
|
347
|
-
(call_expression
|
|
348
|
-
function: (member_expression
|
|
349
|
-
object: (_) @call.receiver
|
|
350
|
-
property: (property_identifier) @call.method)) @call.member
|
|
351
|
-
|
|
352
|
-
;; New expression
|
|
353
|
-
(new_expression
|
|
354
|
-
constructor: (identifier) @call.constructor) @call.new
|
|
355
|
-
|
|
356
|
-
;; Class heritage \u2014 extends
|
|
357
|
-
(class_heritage
|
|
358
|
-
(extends_clause
|
|
359
|
-
value: (identifier) @inherit.extends))
|
|
360
|
-
|
|
361
|
-
;; Class heritage \u2014 implements
|
|
362
|
-
(class_heritage
|
|
363
|
-
(implements_clause
|
|
364
|
-
(type_identifier) @inherit.implements))
|
|
365
|
-
`;
|
|
338
|
+
var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
|
|
339
|
+
var init_tracing = __esm({
|
|
340
|
+
"src/observability/tracing.ts"() {
|
|
341
|
+
_sdk = null;
|
|
342
|
+
TRACER_NAME = "code-intel";
|
|
343
|
+
BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
|
|
366
344
|
}
|
|
367
345
|
});
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
346
|
+
function getActiveTraceCtx() {
|
|
347
|
+
if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
|
|
348
|
+
if (_getTraceCtx) return _getTraceCtx();
|
|
349
|
+
_getTraceCtx = "pending";
|
|
350
|
+
Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
|
|
351
|
+
_getTraceCtx = mod.getActiveTraceContext;
|
|
352
|
+
}).catch(() => {
|
|
353
|
+
_getTraceCtx = null;
|
|
354
|
+
});
|
|
355
|
+
return { traceId: "", spanId: "" };
|
|
356
|
+
}
|
|
357
|
+
var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
|
|
358
|
+
var init_logger = __esm({
|
|
359
|
+
"src/shared/logger.ts"() {
|
|
360
|
+
_getTraceCtx = null;
|
|
361
|
+
SENSITIVE_KEYS = [
|
|
362
|
+
"password",
|
|
363
|
+
"passwd",
|
|
364
|
+
"pass",
|
|
365
|
+
"pwd",
|
|
366
|
+
"secret",
|
|
367
|
+
"secretkey",
|
|
368
|
+
"secret_key",
|
|
369
|
+
"secretaccesskey",
|
|
370
|
+
"accesskeyid",
|
|
371
|
+
"credentials",
|
|
372
|
+
"auth",
|
|
373
|
+
"authentication",
|
|
374
|
+
"login",
|
|
375
|
+
"api_key",
|
|
376
|
+
"apikey",
|
|
377
|
+
"api",
|
|
378
|
+
"access_key",
|
|
379
|
+
"access_token",
|
|
380
|
+
"accesskey",
|
|
381
|
+
"auth_key",
|
|
382
|
+
"auth_token",
|
|
383
|
+
"authkey",
|
|
384
|
+
"token",
|
|
385
|
+
"jwt",
|
|
386
|
+
"bearer_token",
|
|
387
|
+
"refresh_token",
|
|
388
|
+
"session_token",
|
|
389
|
+
"session_key",
|
|
390
|
+
"oauth_token",
|
|
391
|
+
"connection_string",
|
|
392
|
+
"conn_string",
|
|
393
|
+
"db_uri",
|
|
394
|
+
"db_url",
|
|
395
|
+
"database_url",
|
|
396
|
+
"mongodb_uri",
|
|
397
|
+
"mysql_uri",
|
|
398
|
+
"postgres_uri",
|
|
399
|
+
"sql_uri",
|
|
400
|
+
"db_username",
|
|
401
|
+
"db_password",
|
|
402
|
+
"db_host",
|
|
403
|
+
"db_port",
|
|
404
|
+
"db_name",
|
|
405
|
+
"encryption_key",
|
|
406
|
+
"crypto_key",
|
|
407
|
+
"private_key",
|
|
408
|
+
"public_key",
|
|
409
|
+
"ssl_key",
|
|
410
|
+
"ssh_key",
|
|
411
|
+
"pgp_key",
|
|
412
|
+
"rsa_key",
|
|
413
|
+
"aes_key",
|
|
414
|
+
"email",
|
|
415
|
+
"phone",
|
|
416
|
+
"telephone",
|
|
417
|
+
"mobile",
|
|
418
|
+
"ssn",
|
|
419
|
+
"social_security",
|
|
420
|
+
"credit_card",
|
|
421
|
+
"cc_number",
|
|
422
|
+
"card_number",
|
|
423
|
+
"cvv",
|
|
424
|
+
"expiry_date",
|
|
425
|
+
"birth_date",
|
|
426
|
+
"dob",
|
|
427
|
+
"address",
|
|
428
|
+
"zip_code",
|
|
429
|
+
"postal_code",
|
|
430
|
+
"bank_account",
|
|
431
|
+
"iban",
|
|
432
|
+
"swift_code",
|
|
433
|
+
"routing_number",
|
|
434
|
+
"tax_id",
|
|
435
|
+
"vat_number",
|
|
436
|
+
"financial_id",
|
|
437
|
+
"certificate",
|
|
438
|
+
"client_cert",
|
|
439
|
+
"server_cert",
|
|
440
|
+
"ca_cert",
|
|
441
|
+
"aws_key",
|
|
442
|
+
"aws_secret",
|
|
443
|
+
"azure_key",
|
|
444
|
+
"gcp_key",
|
|
445
|
+
"s3_key",
|
|
446
|
+
"cloudinary_key",
|
|
447
|
+
"stripe_key",
|
|
448
|
+
"paypal_key",
|
|
449
|
+
"twilio_key",
|
|
450
|
+
"app_secret",
|
|
451
|
+
"client_secret",
|
|
452
|
+
"consumer_secret",
|
|
453
|
+
"encryption_secret",
|
|
454
|
+
"master_key",
|
|
455
|
+
"root_password",
|
|
456
|
+
"admin_password",
|
|
457
|
+
"config_secret",
|
|
458
|
+
"env_secret",
|
|
459
|
+
"deploy_key",
|
|
460
|
+
"ci_key",
|
|
461
|
+
"session_id",
|
|
462
|
+
"cookie_secret",
|
|
463
|
+
"csrf_token",
|
|
464
|
+
"xsrf_token",
|
|
465
|
+
"license_key",
|
|
466
|
+
"product_key",
|
|
467
|
+
"serial_number",
|
|
468
|
+
"activation_code"
|
|
469
|
+
];
|
|
470
|
+
SENSITIVE_PATTERNS = [
|
|
471
|
+
/(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
|
|
472
|
+
/\b\d{16}\b/gi,
|
|
473
|
+
/\b\d{3}-\d{2}-\d{4}\b/gi,
|
|
474
|
+
/\b[A-Za-z0-9]{32}\b/gi,
|
|
475
|
+
/\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
|
|
476
|
+
/\b\d{10}\b/gi,
|
|
477
|
+
/\b[A-Za-z0-9]{64}\b/gi,
|
|
478
|
+
/(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
|
|
479
|
+
/(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
|
|
480
|
+
/(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
|
|
481
|
+
];
|
|
482
|
+
SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
|
|
483
|
+
Logger = class _Logger {
|
|
484
|
+
static instance = null;
|
|
485
|
+
static maskSensitiveData(value) {
|
|
486
|
+
if (typeof value === "string" && value.length > 5) {
|
|
487
|
+
const firstChar = value.at(0);
|
|
488
|
+
const lastChar = value.at(-1);
|
|
489
|
+
return firstChar + "*".repeat(value.length - 2) + lastChar;
|
|
490
|
+
}
|
|
491
|
+
return value;
|
|
492
|
+
}
|
|
493
|
+
static maskSensitive(message, args = []) {
|
|
494
|
+
const maskString = (input) => {
|
|
495
|
+
if (typeof input !== "string") return input;
|
|
496
|
+
return SENSITIVE_PATTERNS.reduce((str, pattern) => {
|
|
497
|
+
return str.replace(
|
|
498
|
+
pattern,
|
|
499
|
+
(match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
|
|
500
|
+
);
|
|
501
|
+
}, input);
|
|
502
|
+
};
|
|
503
|
+
const deepMask = (obj) => {
|
|
504
|
+
if (typeof obj === "string") return maskString(obj);
|
|
505
|
+
if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
|
|
506
|
+
if (typeof obj === "object" && obj !== null) {
|
|
507
|
+
return Object.entries(obj).reduce(
|
|
508
|
+
(acc, [key, value]) => {
|
|
509
|
+
if (value === void 0) return acc;
|
|
510
|
+
const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
|
|
511
|
+
acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
|
|
512
|
+
return acc;
|
|
513
|
+
},
|
|
514
|
+
{}
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return obj;
|
|
518
|
+
};
|
|
519
|
+
return {
|
|
520
|
+
maskedMessage: maskString(message),
|
|
521
|
+
maskedArgs: args.map((arg) => deepMask(arg))
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/** Global log directory: ~/.code-intel/logs */
|
|
525
|
+
static LOG_DIR = path32.join(os13.homedir(), ".code-intel", "logs");
|
|
526
|
+
static getLogger() {
|
|
527
|
+
if (!_Logger.instance) {
|
|
528
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
529
|
+
const logLevel = process.env.LOG_LEVEL ?? "info";
|
|
530
|
+
const transports = [];
|
|
531
|
+
transports.push(new winston.transports.Console());
|
|
532
|
+
if (!isProduction) {
|
|
533
|
+
try {
|
|
534
|
+
if (!fs26.existsSync(_Logger.LOG_DIR)) {
|
|
535
|
+
fs26.mkdirSync(_Logger.LOG_DIR, { recursive: true });
|
|
536
|
+
}
|
|
537
|
+
transports.push(
|
|
538
|
+
new DailyRotateFile({
|
|
539
|
+
filename: path32.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
|
|
540
|
+
datePattern: "YYYY-MM-DD",
|
|
541
|
+
maxSize: "20m",
|
|
542
|
+
maxFiles: "14d"
|
|
543
|
+
})
|
|
544
|
+
);
|
|
545
|
+
} catch {
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
_Logger.instance = winston.createLogger({
|
|
549
|
+
level: logLevel,
|
|
550
|
+
format: winston.format.combine(
|
|
551
|
+
winston.format.timestamp(),
|
|
552
|
+
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
553
|
+
const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
|
|
554
|
+
const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
|
|
555
|
+
const formattedArgs = maskedArgs.map(
|
|
556
|
+
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
557
|
+
);
|
|
558
|
+
const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
|
|
559
|
+
let traceCtx = "";
|
|
560
|
+
try {
|
|
561
|
+
const { traceId, spanId } = getActiveTraceCtx();
|
|
562
|
+
if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
|
|
566
|
+
})
|
|
567
|
+
),
|
|
568
|
+
transports
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return _Logger.instance;
|
|
572
|
+
}
|
|
573
|
+
static info(message, ...args) {
|
|
574
|
+
_Logger.getLogger().info(message, ...args);
|
|
575
|
+
}
|
|
576
|
+
static warn(message, ...args) {
|
|
577
|
+
_Logger.getLogger().warn(message, ...args);
|
|
578
|
+
}
|
|
579
|
+
static error(message, ...args) {
|
|
580
|
+
_Logger.getLogger().error(message, ...args);
|
|
581
|
+
}
|
|
582
|
+
static debug(message, ...args) {
|
|
583
|
+
_Logger.getLogger().debug(message, ...args);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
logger_default = Logger;
|
|
587
|
+
Logger.getLogger();
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// src/shared/languages.ts
|
|
592
|
+
var init_languages = __esm({
|
|
593
|
+
"src/shared/languages.ts"() {
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// src/shared/detection.ts
|
|
598
|
+
function detectLanguage(filePath) {
|
|
599
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
600
|
+
return EXTENSION_MAP[ext] ?? null;
|
|
601
|
+
}
|
|
602
|
+
function getSupportedExtensions() {
|
|
603
|
+
return Object.keys(EXTENSION_MAP);
|
|
604
|
+
}
|
|
605
|
+
var EXTENSION_MAP;
|
|
606
|
+
var init_detection = __esm({
|
|
607
|
+
"src/shared/detection.ts"() {
|
|
608
|
+
init_languages();
|
|
609
|
+
EXTENSION_MAP = {
|
|
610
|
+
".ts": "typescript" /* TypeScript */,
|
|
611
|
+
".tsx": "typescript" /* TypeScript */,
|
|
612
|
+
".mts": "typescript" /* TypeScript */,
|
|
613
|
+
".cts": "typescript" /* TypeScript */,
|
|
614
|
+
".js": "javascript" /* JavaScript */,
|
|
615
|
+
".jsx": "javascript" /* JavaScript */,
|
|
616
|
+
".mjs": "javascript" /* JavaScript */,
|
|
617
|
+
".cjs": "javascript" /* JavaScript */,
|
|
618
|
+
".py": "python" /* Python */,
|
|
619
|
+
".pyi": "python" /* Python */,
|
|
620
|
+
".java": "java" /* Java */,
|
|
621
|
+
".go": "go" /* Go */,
|
|
622
|
+
".c": "c" /* C */,
|
|
623
|
+
".h": "c" /* C */,
|
|
624
|
+
".cpp": "cpp" /* Cpp */,
|
|
625
|
+
".cxx": "cpp" /* Cpp */,
|
|
626
|
+
".cc": "cpp" /* Cpp */,
|
|
627
|
+
".hpp": "cpp" /* Cpp */,
|
|
628
|
+
".hxx": "cpp" /* Cpp */,
|
|
629
|
+
".cs": "csharp" /* CSharp */,
|
|
630
|
+
".rs": "rust" /* Rust */,
|
|
631
|
+
".php": "php" /* PHP */,
|
|
632
|
+
".kt": "kotlin" /* Kotlin */,
|
|
633
|
+
".kts": "kotlin" /* Kotlin */,
|
|
634
|
+
".rb": "ruby" /* Ruby */,
|
|
635
|
+
".swift": "swift" /* Swift */,
|
|
636
|
+
".dart": "dart" /* Dart */
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// src/shared/index.ts
|
|
642
|
+
var init_shared = __esm({
|
|
643
|
+
"src/shared/index.ts"() {
|
|
644
|
+
init_languages();
|
|
645
|
+
init_detection();
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
function findBundledWasmDir() {
|
|
649
|
+
const fileDir = path32.dirname(fileURLToPath(import.meta.url));
|
|
650
|
+
const candidates = [
|
|
651
|
+
path32.join(fileDir, "wasm"),
|
|
652
|
+
// dist/index.js → dist/wasm/
|
|
653
|
+
path32.join(fileDir, "../wasm")
|
|
654
|
+
// dist/cli/main.js → dist/wasm/
|
|
655
|
+
];
|
|
656
|
+
for (const candidate of candidates) {
|
|
657
|
+
if (existsSync(candidate)) return candidate;
|
|
658
|
+
}
|
|
659
|
+
return candidates[0];
|
|
660
|
+
}
|
|
661
|
+
function wasmPath(lang) {
|
|
662
|
+
const WASM_PACKAGE_MAP = {
|
|
663
|
+
["typescript" /* TypeScript */]: "tree-sitter-typescript/tree-sitter-typescript.wasm",
|
|
664
|
+
["javascript" /* JavaScript */]: "tree-sitter-javascript/tree-sitter-javascript.wasm",
|
|
665
|
+
["python" /* Python */]: "tree-sitter-python/tree-sitter-python.wasm",
|
|
666
|
+
["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
|
|
667
|
+
["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
|
|
668
|
+
["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
|
|
669
|
+
["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
|
|
670
|
+
["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
|
|
671
|
+
["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
|
|
672
|
+
["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
|
|
673
|
+
["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
|
|
674
|
+
// These are optional dependencies; their packages may or may not include
|
|
675
|
+
// a WASM. If require.resolve fails we fall back to the bundled wasm/.
|
|
676
|
+
["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
|
|
677
|
+
["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
|
|
678
|
+
["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
|
|
679
|
+
};
|
|
680
|
+
const BUNDLED_WASM_MAP = {
|
|
681
|
+
["swift" /* Swift */]: "tree-sitter-swift.wasm",
|
|
682
|
+
["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
|
|
683
|
+
["dart" /* Dart */]: "tree-sitter-dart.wasm"
|
|
684
|
+
};
|
|
685
|
+
const relative = WASM_PACKAGE_MAP[lang];
|
|
686
|
+
if (relative) {
|
|
687
|
+
try {
|
|
688
|
+
return _require.resolve(relative);
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const bundled = BUNDLED_WASM_MAP[lang];
|
|
693
|
+
if (bundled) {
|
|
694
|
+
const bundledPath = path32.join(_bundledWasmDir, bundled);
|
|
695
|
+
if (existsSync(bundledPath)) return bundledPath;
|
|
696
|
+
}
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
async function initParser() {
|
|
700
|
+
if (!initPromise) {
|
|
701
|
+
initPromise = Parser.init();
|
|
702
|
+
}
|
|
703
|
+
return initPromise;
|
|
704
|
+
}
|
|
705
|
+
async function getLanguage(lang) {
|
|
706
|
+
if (languageCache.has(lang)) return languageCache.get(lang);
|
|
707
|
+
const path33 = wasmPath(lang);
|
|
708
|
+
if (!path33) {
|
|
709
|
+
languageCache.set(lang, null);
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
await initParser();
|
|
714
|
+
const language = await Language.load(path33);
|
|
715
|
+
languageCache.set(lang, language);
|
|
716
|
+
return language;
|
|
717
|
+
} catch {
|
|
718
|
+
languageCache.set(lang, null);
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async function getParser(lang) {
|
|
723
|
+
const language = await getLanguage(lang);
|
|
724
|
+
if (!language) return null;
|
|
725
|
+
let parser = parserCache.get(lang);
|
|
726
|
+
if (!parser) {
|
|
727
|
+
parser = new Parser();
|
|
728
|
+
parserCache.set(lang, parser);
|
|
729
|
+
}
|
|
730
|
+
parser.setLanguage(language);
|
|
731
|
+
return parser;
|
|
732
|
+
}
|
|
733
|
+
async function parseSource(lang, source) {
|
|
734
|
+
const parser = await getParser(lang);
|
|
735
|
+
if (!parser) return null;
|
|
736
|
+
return parser.parse(source);
|
|
737
|
+
}
|
|
738
|
+
async function isTreeSitterAvailable(lang) {
|
|
739
|
+
return await getLanguage(lang) !== null;
|
|
740
|
+
}
|
|
741
|
+
var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
|
|
742
|
+
var init_parser_manager = __esm({
|
|
743
|
+
"src/parsing/parser-manager.ts"() {
|
|
744
|
+
init_shared();
|
|
745
|
+
_require = createRequire(import.meta.url);
|
|
746
|
+
_bundledWasmDir = findBundledWasmDir();
|
|
747
|
+
initPromise = null;
|
|
748
|
+
languageCache = /* @__PURE__ */ new Map();
|
|
749
|
+
parserCache = /* @__PURE__ */ new Map();
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
function getOrCompileQuery(language, querySource) {
|
|
753
|
+
let langMap = _queryCache.get(language);
|
|
754
|
+
if (!langMap) {
|
|
755
|
+
langMap = /* @__PURE__ */ new Map();
|
|
756
|
+
_queryCache.set(language, langMap);
|
|
757
|
+
}
|
|
758
|
+
let q = langMap.get(querySource);
|
|
759
|
+
if (!q) {
|
|
760
|
+
q = new Query(language, querySource);
|
|
761
|
+
langMap.set(querySource, q);
|
|
762
|
+
}
|
|
763
|
+
return q;
|
|
764
|
+
}
|
|
765
|
+
function runQuery(tree, language, querySource) {
|
|
766
|
+
const query = getOrCompileQuery(language, querySource);
|
|
767
|
+
const matches = query.matches(tree.rootNode);
|
|
768
|
+
const captures = [];
|
|
769
|
+
for (const match of matches) {
|
|
770
|
+
for (const capture of match.captures) {
|
|
771
|
+
captures.push({
|
|
772
|
+
name: capture.name,
|
|
773
|
+
node: capture.node,
|
|
774
|
+
text: capture.node.text
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return captures;
|
|
779
|
+
}
|
|
780
|
+
function runQueryMatches(tree, language, querySource) {
|
|
781
|
+
const query = getOrCompileQuery(language, querySource);
|
|
782
|
+
const raw = query.matches(tree.rootNode);
|
|
783
|
+
return raw.map((m) => ({
|
|
784
|
+
patternIndex: m.patternIndex,
|
|
785
|
+
captures: m.captures.map((c) => ({
|
|
786
|
+
name: c.name,
|
|
787
|
+
node: c.node,
|
|
788
|
+
text: c.node.text
|
|
789
|
+
}))
|
|
790
|
+
}));
|
|
791
|
+
}
|
|
792
|
+
var _queryCache;
|
|
793
|
+
var init_query_runner = __esm({
|
|
794
|
+
"src/parsing/query-runner.ts"() {
|
|
795
|
+
_queryCache = /* @__PURE__ */ new WeakMap();
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
// src/parsing/queries/typescript.ts
|
|
800
|
+
var typescriptQueries;
|
|
801
|
+
var init_typescript = __esm({
|
|
802
|
+
"src/parsing/queries/typescript.ts"() {
|
|
803
|
+
typescriptQueries = `
|
|
804
|
+
;; Class declaration
|
|
805
|
+
(class_declaration
|
|
806
|
+
name: (type_identifier) @def.class.name) @def.class
|
|
807
|
+
|
|
808
|
+
;; Abstract class
|
|
809
|
+
(abstract_class_declaration
|
|
810
|
+
name: (type_identifier) @def.class.name) @def.class
|
|
811
|
+
|
|
812
|
+
;; Interface declaration
|
|
813
|
+
(interface_declaration
|
|
814
|
+
name: (type_identifier) @def.interface.name) @def.interface
|
|
815
|
+
|
|
816
|
+
;; Type alias
|
|
817
|
+
(type_alias_declaration
|
|
818
|
+
name: (type_identifier) @def.type_alias.name) @def.type_alias
|
|
819
|
+
|
|
820
|
+
;; Enum
|
|
821
|
+
(enum_declaration
|
|
822
|
+
name: (identifier) @def.enum.name) @def.enum
|
|
823
|
+
|
|
824
|
+
;; Function declaration
|
|
825
|
+
(function_declaration
|
|
826
|
+
name: (identifier) @def.func.name) @def.func
|
|
827
|
+
|
|
828
|
+
;; Arrow function in variable
|
|
829
|
+
(lexical_declaration
|
|
830
|
+
(variable_declarator
|
|
831
|
+
name: (identifier) @def.func.name
|
|
832
|
+
value: (arrow_function))) @def.func
|
|
833
|
+
|
|
834
|
+
;; Const/let/var variable (non-function)
|
|
835
|
+
(lexical_declaration
|
|
836
|
+
(variable_declarator
|
|
837
|
+
name: (identifier) @def.var.name
|
|
838
|
+
value: (_) @def.var.value)) @def.var
|
|
839
|
+
|
|
840
|
+
;; Method definition
|
|
841
|
+
(method_definition
|
|
842
|
+
name: (property_identifier) @def.method.name) @def.method
|
|
843
|
+
|
|
844
|
+
;; Public field definition
|
|
845
|
+
(public_field_definition
|
|
846
|
+
name: (property_identifier) @def.property.name) @def.property
|
|
847
|
+
|
|
848
|
+
;; Import statement
|
|
849
|
+
(import_statement
|
|
850
|
+
source: (string) @imp.source) @imp
|
|
851
|
+
|
|
852
|
+
;; Import specifier
|
|
853
|
+
(import_specifier
|
|
854
|
+
name: (identifier) @imp.name)
|
|
855
|
+
|
|
856
|
+
;; Namespace import
|
|
857
|
+
(namespace_import (identifier) @imp.namespace)
|
|
858
|
+
|
|
859
|
+
;; Export statement
|
|
860
|
+
(export_statement) @export
|
|
861
|
+
|
|
862
|
+
;; Call expression \u2014 function call
|
|
863
|
+
(call_expression
|
|
864
|
+
function: (identifier) @call.name) @call
|
|
865
|
+
|
|
866
|
+
;; Call expression \u2014 member call
|
|
867
|
+
(call_expression
|
|
868
|
+
function: (member_expression
|
|
869
|
+
object: (_) @call.receiver
|
|
870
|
+
property: (property_identifier) @call.method)) @call.member
|
|
871
|
+
|
|
872
|
+
;; New expression
|
|
873
|
+
(new_expression
|
|
874
|
+
constructor: (identifier) @call.constructor) @call.new
|
|
875
|
+
|
|
876
|
+
;; Class heritage \u2014 extends
|
|
877
|
+
(class_heritage
|
|
878
|
+
(extends_clause
|
|
879
|
+
value: (identifier) @inherit.extends))
|
|
880
|
+
|
|
881
|
+
;; Class heritage \u2014 implements
|
|
882
|
+
(class_heritage
|
|
883
|
+
(implements_clause
|
|
884
|
+
(type_identifier) @inherit.implements))
|
|
885
|
+
`;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
// src/parsing/queries/python.ts
|
|
890
|
+
var pythonQueries;
|
|
891
|
+
var init_python = __esm({
|
|
892
|
+
"src/parsing/queries/python.ts"() {
|
|
893
|
+
pythonQueries = `
|
|
894
|
+
;; Class definition
|
|
375
895
|
(class_definition
|
|
376
896
|
name: (identifier) @def.class.name) @def.class
|
|
377
897
|
|
|
@@ -850,381 +1370,48 @@ var init_ruby = __esm({
|
|
|
850
1370
|
|
|
851
1371
|
;; Call
|
|
852
1372
|
(call
|
|
853
|
-
method: (identifier) @call.name) @call
|
|
854
|
-
|
|
855
|
-
;; Superclass
|
|
856
|
-
(superclass
|
|
857
|
-
(constant) @inherit.extends)
|
|
858
|
-
`;
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
// src/parsing/queries/swift.ts
|
|
863
|
-
var swiftQueries;
|
|
864
|
-
var init_swift = __esm({
|
|
865
|
-
"src/parsing/queries/swift.ts"() {
|
|
866
|
-
swiftQueries = `
|
|
867
|
-
;; In tree-sitter-swift, structs/classes/enums/actors all use class_declaration.
|
|
868
|
-
;; Distinguish them with a keyword anchor.
|
|
869
|
-
|
|
870
|
-
;; Struct declaration
|
|
871
|
-
(class_declaration "struct"
|
|
872
|
-
(type_identifier) @def.struct.name) @def.struct
|
|
873
|
-
|
|
874
|
-
;; Class declaration
|
|
875
|
-
(class_declaration "class"
|
|
876
|
-
(type_identifier) @def.class.name) @def.class
|
|
877
|
-
|
|
878
|
-
;; Enum declaration
|
|
879
|
-
(class_declaration "enum"
|
|
880
|
-
(type_identifier) @def.enum.name) @def.enum
|
|
881
|
-
|
|
882
|
-
;; Protocol declaration
|
|
883
|
-
(protocol_declaration
|
|
884
|
-
(type_identifier) @def.interface.name) @def.interface
|
|
885
|
-
|
|
886
|
-
;; Function declaration (no name: field in this grammar; positional match)
|
|
887
|
-
(function_declaration
|
|
888
|
-
(simple_identifier) @def.func.name) @def.func
|
|
889
|
-
|
|
890
|
-
;; Property declaration
|
|
891
|
-
(property_declaration
|
|
892
|
-
(pattern
|
|
893
|
-
(simple_identifier) @def.property.name)) @def.property
|
|
894
|
-
`;
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
// src/observability/tracing.ts
|
|
899
|
-
var tracing_exports = {};
|
|
900
|
-
__export(tracing_exports, {
|
|
901
|
-
SpanStatusCode: () => SpanStatusCode,
|
|
902
|
-
context: () => context,
|
|
903
|
-
getActiveTraceContext: () => getActiveTraceContext,
|
|
904
|
-
getTracer: () => getTracer,
|
|
905
|
-
initTracing: () => initTracing,
|
|
906
|
-
isTracingEnabled: () => isTracingEnabled,
|
|
907
|
-
sanitizeAttrs: () => sanitizeAttrs,
|
|
908
|
-
shutdownTracing: () => shutdownTracing,
|
|
909
|
-
trace: () => trace,
|
|
910
|
-
withSpan: () => withSpan
|
|
911
|
-
});
|
|
912
|
-
function isTracingEnabled() {
|
|
913
|
-
return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
|
|
914
|
-
}
|
|
915
|
-
function initTracing() {
|
|
916
|
-
if (!isTracingEnabled()) return;
|
|
917
|
-
if (_sdk) return;
|
|
918
|
-
const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
|
|
919
|
-
const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
|
|
920
|
-
const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
|
|
921
|
-
const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
|
|
922
|
-
_sdk = new NodeSDK({
|
|
923
|
-
resource: resourceFromAttributes({
|
|
924
|
-
[SEMRESATTRS_SERVICE_NAME]: serviceName,
|
|
925
|
-
[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
|
|
926
|
-
}),
|
|
927
|
-
traceExporter: exporter,
|
|
928
|
-
instrumentations: [
|
|
929
|
-
getNodeAutoInstrumentations({
|
|
930
|
-
// Disable noisy file-system instrumentation
|
|
931
|
-
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
932
|
-
})
|
|
933
|
-
]
|
|
934
|
-
});
|
|
935
|
-
_sdk.start();
|
|
936
|
-
}
|
|
937
|
-
async function shutdownTracing() {
|
|
938
|
-
if (_sdk) {
|
|
939
|
-
await _sdk.shutdown();
|
|
940
|
-
_sdk = null;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
function getTracer() {
|
|
944
|
-
return trace.getTracer(TRACER_NAME);
|
|
945
|
-
}
|
|
946
|
-
async function withSpan(name, attrs, fn) {
|
|
947
|
-
return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
|
|
948
|
-
try {
|
|
949
|
-
const result = await fn(span);
|
|
950
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
951
|
-
return result;
|
|
952
|
-
} catch (err) {
|
|
953
|
-
span.setStatus({
|
|
954
|
-
code: SpanStatusCode.ERROR,
|
|
955
|
-
message: err instanceof Error ? err.message : String(err)
|
|
956
|
-
});
|
|
957
|
-
span.recordException(err);
|
|
958
|
-
throw err;
|
|
959
|
-
} finally {
|
|
960
|
-
span.end();
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
function sanitizeAttrs(attrs) {
|
|
965
|
-
const safe = {};
|
|
966
|
-
for (const [k, v] of Object.entries(attrs)) {
|
|
967
|
-
if (BLOCKED_ATTR_KEYS.test(k)) continue;
|
|
968
|
-
safe[k] = v;
|
|
969
|
-
}
|
|
970
|
-
return safe;
|
|
971
|
-
}
|
|
972
|
-
function getActiveTraceContext() {
|
|
973
|
-
const span = trace.getActiveSpan();
|
|
974
|
-
if (!span) return { traceId: "", spanId: "" };
|
|
975
|
-
const ctx = span.spanContext();
|
|
976
|
-
return { traceId: ctx.traceId, spanId: ctx.spanId };
|
|
977
|
-
}
|
|
978
|
-
var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
|
|
979
|
-
var init_tracing = __esm({
|
|
980
|
-
"src/observability/tracing.ts"() {
|
|
981
|
-
_sdk = null;
|
|
982
|
-
TRACER_NAME = "code-intel";
|
|
983
|
-
BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
|
|
1373
|
+
method: (identifier) @call.name) @call
|
|
1374
|
+
|
|
1375
|
+
;; Superclass
|
|
1376
|
+
(superclass
|
|
1377
|
+
(constant) @inherit.extends)
|
|
1378
|
+
`;
|
|
984
1379
|
}
|
|
985
1380
|
});
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
"accesskey",
|
|
1021
|
-
"auth_key",
|
|
1022
|
-
"auth_token",
|
|
1023
|
-
"authkey",
|
|
1024
|
-
"token",
|
|
1025
|
-
"jwt",
|
|
1026
|
-
"bearer_token",
|
|
1027
|
-
"refresh_token",
|
|
1028
|
-
"session_token",
|
|
1029
|
-
"session_key",
|
|
1030
|
-
"oauth_token",
|
|
1031
|
-
"connection_string",
|
|
1032
|
-
"conn_string",
|
|
1033
|
-
"db_uri",
|
|
1034
|
-
"db_url",
|
|
1035
|
-
"database_url",
|
|
1036
|
-
"mongodb_uri",
|
|
1037
|
-
"mysql_uri",
|
|
1038
|
-
"postgres_uri",
|
|
1039
|
-
"sql_uri",
|
|
1040
|
-
"db_username",
|
|
1041
|
-
"db_password",
|
|
1042
|
-
"db_host",
|
|
1043
|
-
"db_port",
|
|
1044
|
-
"db_name",
|
|
1045
|
-
"encryption_key",
|
|
1046
|
-
"crypto_key",
|
|
1047
|
-
"private_key",
|
|
1048
|
-
"public_key",
|
|
1049
|
-
"ssl_key",
|
|
1050
|
-
"ssh_key",
|
|
1051
|
-
"pgp_key",
|
|
1052
|
-
"rsa_key",
|
|
1053
|
-
"aes_key",
|
|
1054
|
-
"email",
|
|
1055
|
-
"phone",
|
|
1056
|
-
"telephone",
|
|
1057
|
-
"mobile",
|
|
1058
|
-
"ssn",
|
|
1059
|
-
"social_security",
|
|
1060
|
-
"credit_card",
|
|
1061
|
-
"cc_number",
|
|
1062
|
-
"card_number",
|
|
1063
|
-
"cvv",
|
|
1064
|
-
"expiry_date",
|
|
1065
|
-
"birth_date",
|
|
1066
|
-
"dob",
|
|
1067
|
-
"address",
|
|
1068
|
-
"zip_code",
|
|
1069
|
-
"postal_code",
|
|
1070
|
-
"bank_account",
|
|
1071
|
-
"iban",
|
|
1072
|
-
"swift_code",
|
|
1073
|
-
"routing_number",
|
|
1074
|
-
"tax_id",
|
|
1075
|
-
"vat_number",
|
|
1076
|
-
"financial_id",
|
|
1077
|
-
"certificate",
|
|
1078
|
-
"client_cert",
|
|
1079
|
-
"server_cert",
|
|
1080
|
-
"ca_cert",
|
|
1081
|
-
"aws_key",
|
|
1082
|
-
"aws_secret",
|
|
1083
|
-
"azure_key",
|
|
1084
|
-
"gcp_key",
|
|
1085
|
-
"s3_key",
|
|
1086
|
-
"cloudinary_key",
|
|
1087
|
-
"stripe_key",
|
|
1088
|
-
"paypal_key",
|
|
1089
|
-
"twilio_key",
|
|
1090
|
-
"app_secret",
|
|
1091
|
-
"client_secret",
|
|
1092
|
-
"consumer_secret",
|
|
1093
|
-
"encryption_secret",
|
|
1094
|
-
"master_key",
|
|
1095
|
-
"root_password",
|
|
1096
|
-
"admin_password",
|
|
1097
|
-
"config_secret",
|
|
1098
|
-
"env_secret",
|
|
1099
|
-
"deploy_key",
|
|
1100
|
-
"ci_key",
|
|
1101
|
-
"session_id",
|
|
1102
|
-
"cookie_secret",
|
|
1103
|
-
"csrf_token",
|
|
1104
|
-
"xsrf_token",
|
|
1105
|
-
"license_key",
|
|
1106
|
-
"product_key",
|
|
1107
|
-
"serial_number",
|
|
1108
|
-
"activation_code"
|
|
1109
|
-
];
|
|
1110
|
-
SENSITIVE_PATTERNS = [
|
|
1111
|
-
/(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
|
|
1112
|
-
/\b\d{16}\b/gi,
|
|
1113
|
-
/\b\d{3}-\d{2}-\d{4}\b/gi,
|
|
1114
|
-
/\b[A-Za-z0-9]{32}\b/gi,
|
|
1115
|
-
/\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
|
|
1116
|
-
/\b\d{10}\b/gi,
|
|
1117
|
-
/\b[A-Za-z0-9]{64}\b/gi,
|
|
1118
|
-
/(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
|
|
1119
|
-
/(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
|
|
1120
|
-
/(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
|
|
1121
|
-
];
|
|
1122
|
-
SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
|
|
1123
|
-
Logger = class _Logger {
|
|
1124
|
-
static instance = null;
|
|
1125
|
-
static maskSensitiveData(value) {
|
|
1126
|
-
if (typeof value === "string" && value.length > 5) {
|
|
1127
|
-
const firstChar = value.at(0);
|
|
1128
|
-
const lastChar = value.at(-1);
|
|
1129
|
-
return firstChar + "*".repeat(value.length - 2) + lastChar;
|
|
1130
|
-
}
|
|
1131
|
-
return value;
|
|
1132
|
-
}
|
|
1133
|
-
static maskSensitive(message, args = []) {
|
|
1134
|
-
const maskString = (input) => {
|
|
1135
|
-
if (typeof input !== "string") return input;
|
|
1136
|
-
return SENSITIVE_PATTERNS.reduce((str, pattern) => {
|
|
1137
|
-
return str.replace(
|
|
1138
|
-
pattern,
|
|
1139
|
-
(match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
|
|
1140
|
-
);
|
|
1141
|
-
}, input);
|
|
1142
|
-
};
|
|
1143
|
-
const deepMask = (obj) => {
|
|
1144
|
-
if (typeof obj === "string") return maskString(obj);
|
|
1145
|
-
if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
|
|
1146
|
-
if (typeof obj === "object" && obj !== null) {
|
|
1147
|
-
return Object.entries(obj).reduce(
|
|
1148
|
-
(acc, [key, value]) => {
|
|
1149
|
-
if (value === void 0) return acc;
|
|
1150
|
-
const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
|
|
1151
|
-
acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
|
|
1152
|
-
return acc;
|
|
1153
|
-
},
|
|
1154
|
-
{}
|
|
1155
|
-
);
|
|
1156
|
-
}
|
|
1157
|
-
return obj;
|
|
1158
|
-
};
|
|
1159
|
-
return {
|
|
1160
|
-
maskedMessage: maskString(message),
|
|
1161
|
-
maskedArgs: args.map((arg) => deepMask(arg))
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
/** Global log directory: ~/.code-intel/logs */
|
|
1165
|
-
static LOG_DIR = path31.join(os13.homedir(), ".code-intel", "logs");
|
|
1166
|
-
static getLogger() {
|
|
1167
|
-
if (!_Logger.instance) {
|
|
1168
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
1169
|
-
const logLevel = process.env.LOG_LEVEL ?? "info";
|
|
1170
|
-
const transports = [];
|
|
1171
|
-
transports.push(new winston.transports.Console());
|
|
1172
|
-
if (!isProduction) {
|
|
1173
|
-
try {
|
|
1174
|
-
if (!fs24.existsSync(_Logger.LOG_DIR)) {
|
|
1175
|
-
fs24.mkdirSync(_Logger.LOG_DIR, { recursive: true });
|
|
1176
|
-
}
|
|
1177
|
-
transports.push(
|
|
1178
|
-
new DailyRotateFile({
|
|
1179
|
-
filename: path31.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
|
|
1180
|
-
datePattern: "YYYY-MM-DD",
|
|
1181
|
-
maxSize: "20m",
|
|
1182
|
-
maxFiles: "14d"
|
|
1183
|
-
})
|
|
1184
|
-
);
|
|
1185
|
-
} catch {
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
_Logger.instance = winston.createLogger({
|
|
1189
|
-
level: logLevel,
|
|
1190
|
-
format: winston.format.combine(
|
|
1191
|
-
winston.format.timestamp(),
|
|
1192
|
-
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
1193
|
-
const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
|
|
1194
|
-
const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
|
|
1195
|
-
const formattedArgs = maskedArgs.map(
|
|
1196
|
-
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
1197
|
-
);
|
|
1198
|
-
const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
|
|
1199
|
-
let traceCtx = "";
|
|
1200
|
-
try {
|
|
1201
|
-
const { traceId, spanId } = getActiveTraceCtx();
|
|
1202
|
-
if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
|
|
1203
|
-
} catch {
|
|
1204
|
-
}
|
|
1205
|
-
return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
|
|
1206
|
-
})
|
|
1207
|
-
),
|
|
1208
|
-
transports
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
return _Logger.instance;
|
|
1212
|
-
}
|
|
1213
|
-
static info(message, ...args) {
|
|
1214
|
-
_Logger.getLogger().info(message, ...args);
|
|
1215
|
-
}
|
|
1216
|
-
static warn(message, ...args) {
|
|
1217
|
-
_Logger.getLogger().warn(message, ...args);
|
|
1218
|
-
}
|
|
1219
|
-
static error(message, ...args) {
|
|
1220
|
-
_Logger.getLogger().error(message, ...args);
|
|
1221
|
-
}
|
|
1222
|
-
static debug(message, ...args) {
|
|
1223
|
-
_Logger.getLogger().debug(message, ...args);
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
logger_default = Logger;
|
|
1227
|
-
Logger.getLogger();
|
|
1381
|
+
|
|
1382
|
+
// src/parsing/queries/swift.ts
|
|
1383
|
+
var swiftQueries;
|
|
1384
|
+
var init_swift = __esm({
|
|
1385
|
+
"src/parsing/queries/swift.ts"() {
|
|
1386
|
+
swiftQueries = `
|
|
1387
|
+
;; In tree-sitter-swift, structs/classes/enums/actors all use class_declaration.
|
|
1388
|
+
;; Distinguish them with a keyword anchor.
|
|
1389
|
+
|
|
1390
|
+
;; Struct declaration
|
|
1391
|
+
(class_declaration "struct"
|
|
1392
|
+
(type_identifier) @def.struct.name) @def.struct
|
|
1393
|
+
|
|
1394
|
+
;; Class declaration
|
|
1395
|
+
(class_declaration "class"
|
|
1396
|
+
(type_identifier) @def.class.name) @def.class
|
|
1397
|
+
|
|
1398
|
+
;; Enum declaration
|
|
1399
|
+
(class_declaration "enum"
|
|
1400
|
+
(type_identifier) @def.enum.name) @def.enum
|
|
1401
|
+
|
|
1402
|
+
;; Protocol declaration
|
|
1403
|
+
(protocol_declaration
|
|
1404
|
+
(type_identifier) @def.interface.name) @def.interface
|
|
1405
|
+
|
|
1406
|
+
;; Function declaration (no name: field in this grammar; positional match)
|
|
1407
|
+
(function_declaration
|
|
1408
|
+
(simple_identifier) @def.func.name) @def.func
|
|
1409
|
+
|
|
1410
|
+
;; Property declaration
|
|
1411
|
+
(property_declaration
|
|
1412
|
+
(pattern
|
|
1413
|
+
(simple_identifier) @def.property.name)) @def.property
|
|
1414
|
+
`;
|
|
1228
1415
|
}
|
|
1229
1416
|
});
|
|
1230
1417
|
|
|
@@ -1961,7 +2148,7 @@ var init_parse_phase = __esm({
|
|
|
1961
2148
|
const batch = filePaths.slice(i, i + CONCURRENCY);
|
|
1962
2149
|
await Promise.all(batch.map(async (filePath) => {
|
|
1963
2150
|
try {
|
|
1964
|
-
const source = await
|
|
2151
|
+
const source = await fs26.promises.readFile(filePath, "utf-8");
|
|
1965
2152
|
context2.fileCache.set(filePath, source);
|
|
1966
2153
|
} catch {
|
|
1967
2154
|
}
|
|
@@ -1974,14 +2161,14 @@ var init_parse_phase = __esm({
|
|
|
1974
2161
|
const lang = detectLanguage(filePath);
|
|
1975
2162
|
if (!lang) {
|
|
1976
2163
|
if (context2.verbose) {
|
|
1977
|
-
const relativePath2 =
|
|
2164
|
+
const relativePath2 = path32.relative(context2.workspaceRoot, filePath);
|
|
1978
2165
|
logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
|
|
1979
2166
|
}
|
|
1980
2167
|
continue;
|
|
1981
2168
|
}
|
|
1982
2169
|
const source = context2.fileCache.get(filePath);
|
|
1983
2170
|
if (!source) continue;
|
|
1984
|
-
const relativePath =
|
|
2171
|
+
const relativePath = path32.relative(context2.workspaceRoot, filePath);
|
|
1985
2172
|
const fileNodeId = generateNodeId("file", relativePath, relativePath);
|
|
1986
2173
|
const fileNode = context2.graph.getNode(fileNodeId);
|
|
1987
2174
|
if (fileNode) {
|
|
@@ -2221,11 +2408,11 @@ var init_resolve_phase = __esm({
|
|
|
2221
2408
|
let heritageEdges = 0;
|
|
2222
2409
|
const fileIndex = /* @__PURE__ */ new Map();
|
|
2223
2410
|
for (const fp of filePaths) {
|
|
2224
|
-
const rel =
|
|
2411
|
+
const rel = path32.relative(workspaceRoot, fp);
|
|
2225
2412
|
fileIndex.set(rel, fp);
|
|
2226
2413
|
const noExt = rel.replace(/\.\w+$/, "");
|
|
2227
2414
|
if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
|
|
2228
|
-
const base =
|
|
2415
|
+
const base = path32.basename(rel, path32.extname(rel));
|
|
2229
2416
|
if (!fileIndex.has(base)) fileIndex.set(base, fp);
|
|
2230
2417
|
}
|
|
2231
2418
|
const symbolIndex = /* @__PURE__ */ new Map();
|
|
@@ -2256,7 +2443,7 @@ var init_resolve_phase = __esm({
|
|
|
2256
2443
|
for (const filePath of filePaths) {
|
|
2257
2444
|
const lang = detectLanguage(filePath);
|
|
2258
2445
|
if (!lang) continue;
|
|
2259
|
-
const relativePath =
|
|
2446
|
+
const relativePath = path32.relative(workspaceRoot, filePath);
|
|
2260
2447
|
const fileNodeId = generateNodeId("file", relativePath, relativePath);
|
|
2261
2448
|
const source = fileCache.get(filePath);
|
|
2262
2449
|
if (!source) continue;
|
|
@@ -2269,13 +2456,13 @@ var init_resolve_phase = __esm({
|
|
|
2269
2456
|
let resolvedRelPath = null;
|
|
2270
2457
|
if (cleaned.startsWith(".")) {
|
|
2271
2458
|
const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
|
|
2272
|
-
const fromDir =
|
|
2459
|
+
const fromDir = path32.dirname(relativePath);
|
|
2273
2460
|
for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", "/index.ts", "/index.js"]) {
|
|
2274
|
-
const candidate =
|
|
2275
|
-
const normalized =
|
|
2461
|
+
const candidate = path32.join(fromDir, cleanedNoJs + ext);
|
|
2462
|
+
const normalized = path32.normalize(candidate);
|
|
2276
2463
|
if (fileIndex.has(normalized)) {
|
|
2277
2464
|
const absPath = fileIndex.get(normalized);
|
|
2278
|
-
resolvedRelPath =
|
|
2465
|
+
resolvedRelPath = path32.relative(workspaceRoot, absPath);
|
|
2279
2466
|
break;
|
|
2280
2467
|
}
|
|
2281
2468
|
}
|
|
@@ -2575,6 +2762,65 @@ var init_embedder = __esm({
|
|
|
2575
2762
|
}
|
|
2576
2763
|
});
|
|
2577
2764
|
|
|
2765
|
+
// src/storage/db-manager.ts
|
|
2766
|
+
var db_manager_exports = {};
|
|
2767
|
+
__export(db_manager_exports, {
|
|
2768
|
+
DbManager: () => DbManager
|
|
2769
|
+
});
|
|
2770
|
+
var DbManager;
|
|
2771
|
+
var init_db_manager = __esm({
|
|
2772
|
+
"src/storage/db-manager.ts"() {
|
|
2773
|
+
DbManager = class {
|
|
2774
|
+
db = null;
|
|
2775
|
+
conn = null;
|
|
2776
|
+
dbPath;
|
|
2777
|
+
readOnly;
|
|
2778
|
+
constructor(dbPath, readOnly = false) {
|
|
2779
|
+
this.dbPath = dbPath;
|
|
2780
|
+
this.readOnly = readOnly;
|
|
2781
|
+
}
|
|
2782
|
+
async init() {
|
|
2783
|
+
if (!this.readOnly) {
|
|
2784
|
+
fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
2785
|
+
}
|
|
2786
|
+
this.db = new Database(this.dbPath, 0, true, this.readOnly);
|
|
2787
|
+
await this.db.init();
|
|
2788
|
+
this.conn = new Connection(this.db);
|
|
2789
|
+
await this.conn.init();
|
|
2790
|
+
}
|
|
2791
|
+
async query(cypher) {
|
|
2792
|
+
if (!this.conn) throw new Error("Database not initialized");
|
|
2793
|
+
const result = await this.conn.query(cypher);
|
|
2794
|
+
const qr = Array.isArray(result) ? result[0] : result;
|
|
2795
|
+
const rows = await qr.getAll();
|
|
2796
|
+
qr.close();
|
|
2797
|
+
return rows;
|
|
2798
|
+
}
|
|
2799
|
+
async execute(cypher) {
|
|
2800
|
+
if (!this.conn) throw new Error("Database not initialized");
|
|
2801
|
+
const result = await this.conn.query(cypher);
|
|
2802
|
+
const qr = Array.isArray(result) ? result[0] : result;
|
|
2803
|
+
qr.close();
|
|
2804
|
+
}
|
|
2805
|
+
close() {
|
|
2806
|
+
try {
|
|
2807
|
+
this.conn?.closeSync();
|
|
2808
|
+
} catch {
|
|
2809
|
+
}
|
|
2810
|
+
try {
|
|
2811
|
+
this.db?.closeSync();
|
|
2812
|
+
} catch {
|
|
2813
|
+
}
|
|
2814
|
+
this.conn = null;
|
|
2815
|
+
this.db = null;
|
|
2816
|
+
}
|
|
2817
|
+
get isOpen() {
|
|
2818
|
+
return this.conn !== null;
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
});
|
|
2823
|
+
|
|
2578
2824
|
// src/multi-repo/group-registry.ts
|
|
2579
2825
|
var group_registry_exports = {};
|
|
2580
2826
|
__export(group_registry_exports, {
|
|
@@ -2589,27 +2835,27 @@ __export(group_registry_exports, {
|
|
|
2589
2835
|
saveSyncResult: () => saveSyncResult
|
|
2590
2836
|
});
|
|
2591
2837
|
function groupFile(name) {
|
|
2592
|
-
return
|
|
2838
|
+
return path32.join(GROUPS_DIR, `${name}.json`);
|
|
2593
2839
|
}
|
|
2594
2840
|
function loadGroup(name) {
|
|
2595
2841
|
try {
|
|
2596
|
-
return JSON.parse(
|
|
2842
|
+
return JSON.parse(fs26.readFileSync(groupFile(name), "utf-8"));
|
|
2597
2843
|
} catch {
|
|
2598
2844
|
return null;
|
|
2599
2845
|
}
|
|
2600
2846
|
}
|
|
2601
2847
|
function saveGroup(group) {
|
|
2602
|
-
|
|
2603
|
-
|
|
2848
|
+
fs26.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
2849
|
+
fs26.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
|
|
2604
2850
|
}
|
|
2605
2851
|
function listGroups() {
|
|
2606
2852
|
const groups = [];
|
|
2607
2853
|
try {
|
|
2608
|
-
for (const file of
|
|
2854
|
+
for (const file of fs26.readdirSync(GROUPS_DIR)) {
|
|
2609
2855
|
if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
|
|
2610
2856
|
try {
|
|
2611
2857
|
const g = JSON.parse(
|
|
2612
|
-
|
|
2858
|
+
fs26.readFileSync(path32.join(GROUPS_DIR, file), "utf-8")
|
|
2613
2859
|
);
|
|
2614
2860
|
groups.push(g);
|
|
2615
2861
|
} catch {
|
|
@@ -2621,16 +2867,16 @@ function listGroups() {
|
|
|
2621
2867
|
}
|
|
2622
2868
|
function deleteGroup(name) {
|
|
2623
2869
|
try {
|
|
2624
|
-
|
|
2870
|
+
fs26.unlinkSync(groupFile(name));
|
|
2625
2871
|
} catch {
|
|
2626
2872
|
}
|
|
2627
2873
|
try {
|
|
2628
|
-
|
|
2874
|
+
fs26.unlinkSync(path32.join(GROUPS_DIR, `${name}.sync.json`));
|
|
2629
2875
|
} catch {
|
|
2630
2876
|
}
|
|
2631
2877
|
}
|
|
2632
2878
|
function groupExists(name) {
|
|
2633
|
-
return
|
|
2879
|
+
return fs26.existsSync(groupFile(name));
|
|
2634
2880
|
}
|
|
2635
2881
|
function addMember(groupName, member) {
|
|
2636
2882
|
const group = loadGroup(groupName);
|
|
@@ -2656,16 +2902,16 @@ function removeMember(groupName, groupPath) {
|
|
|
2656
2902
|
return group;
|
|
2657
2903
|
}
|
|
2658
2904
|
function saveSyncResult(result) {
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2905
|
+
fs26.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
2906
|
+
fs26.writeFileSync(
|
|
2907
|
+
path32.join(GROUPS_DIR, `${result.groupName}.sync.json`),
|
|
2662
2908
|
JSON.stringify(result, null, 2) + "\n"
|
|
2663
2909
|
);
|
|
2664
2910
|
}
|
|
2665
2911
|
function loadSyncResult(groupName) {
|
|
2666
2912
|
try {
|
|
2667
2913
|
return JSON.parse(
|
|
2668
|
-
|
|
2914
|
+
fs26.readFileSync(path32.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
|
|
2669
2915
|
);
|
|
2670
2916
|
} catch {
|
|
2671
2917
|
return null;
|
|
@@ -2674,7 +2920,87 @@ function loadSyncResult(groupName) {
|
|
|
2674
2920
|
var GROUPS_DIR;
|
|
2675
2921
|
var init_group_registry = __esm({
|
|
2676
2922
|
"src/multi-repo/group-registry.ts"() {
|
|
2677
|
-
GROUPS_DIR =
|
|
2923
|
+
GROUPS_DIR = path32.join(os13.homedir(), ".code-intel", "groups");
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
// src/multi-repo/graph-from-db.ts
|
|
2928
|
+
var graph_from_db_exports = {};
|
|
2929
|
+
__export(graph_from_db_exports, {
|
|
2930
|
+
loadGraphFromDB: () => loadGraphFromDB
|
|
2931
|
+
});
|
|
2932
|
+
function parseRow(row, kind) {
|
|
2933
|
+
return {
|
|
2934
|
+
id: String(row["id"] ?? ""),
|
|
2935
|
+
kind,
|
|
2936
|
+
name: String(row["name"] ?? ""),
|
|
2937
|
+
filePath: String(row["file_path"] ?? ""),
|
|
2938
|
+
startLine: row["start_line"] != null ? Number(row["start_line"]) : void 0,
|
|
2939
|
+
endLine: row["end_line"] != null ? Number(row["end_line"]) : void 0,
|
|
2940
|
+
exported: row["exported"] != null ? Boolean(row["exported"]) : void 0,
|
|
2941
|
+
content: row["content"] ? String(row["content"]) : void 0,
|
|
2942
|
+
metadata: row["metadata"] ? (() => {
|
|
2943
|
+
try {
|
|
2944
|
+
return JSON.parse(String(row["metadata"]));
|
|
2945
|
+
} catch {
|
|
2946
|
+
return void 0;
|
|
2947
|
+
}
|
|
2948
|
+
})() : void 0
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2951
|
+
async function loadGraphFromDB(graph, db) {
|
|
2952
|
+
for (const table of ALL_NODE_TABLES) {
|
|
2953
|
+
const kind = TABLE_TO_KIND2[table];
|
|
2954
|
+
if (!kind) continue;
|
|
2955
|
+
let rows = [];
|
|
2956
|
+
try {
|
|
2957
|
+
rows = await db.query(`MATCH (n:${table}) RETURN n.id, n.name, n.file_path, n.start_line, n.end_line, n.exported, n.content, n.metadata`);
|
|
2958
|
+
} catch {
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
for (const row of rows) {
|
|
2962
|
+
const node = parseRow({
|
|
2963
|
+
id: row["n.id"],
|
|
2964
|
+
name: row["n.name"],
|
|
2965
|
+
file_path: row["n.file_path"],
|
|
2966
|
+
start_line: row["n.start_line"],
|
|
2967
|
+
end_line: row["n.end_line"],
|
|
2968
|
+
exported: row["n.exported"],
|
|
2969
|
+
content: row["n.content"],
|
|
2970
|
+
metadata: row["n.metadata"]
|
|
2971
|
+
}, kind);
|
|
2972
|
+
if (node.id && node.name) graph.addNode(node);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
try {
|
|
2976
|
+
const edgeRows = await db.query(
|
|
2977
|
+
`MATCH (a)-[e:code_edges]->(b) RETURN a.id, b.id, e.kind, e.weight, e.label`
|
|
2978
|
+
);
|
|
2979
|
+
for (const row of edgeRows) {
|
|
2980
|
+
const sourceId = String(row["a.id"] ?? "");
|
|
2981
|
+
const targetId = String(row["b.id"] ?? "");
|
|
2982
|
+
const kind = String(row["e.kind"] ?? "");
|
|
2983
|
+
if (!sourceId || !targetId || !kind) continue;
|
|
2984
|
+
const edge = {
|
|
2985
|
+
id: `${sourceId}::${kind}::${targetId}`,
|
|
2986
|
+
source: sourceId,
|
|
2987
|
+
target: targetId,
|
|
2988
|
+
kind,
|
|
2989
|
+
weight: row["e.weight"] != null ? Number(row["e.weight"]) : void 0,
|
|
2990
|
+
label: row["e.label"] ? String(row["e.label"]) : void 0
|
|
2991
|
+
};
|
|
2992
|
+
graph.addEdge(edge);
|
|
2993
|
+
}
|
|
2994
|
+
} catch {
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
var TABLE_TO_KIND2;
|
|
2998
|
+
var init_graph_from_db = __esm({
|
|
2999
|
+
"src/multi-repo/graph-from-db.ts"() {
|
|
3000
|
+
init_schema();
|
|
3001
|
+
TABLE_TO_KIND2 = Object.fromEntries(
|
|
3002
|
+
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
3003
|
+
);
|
|
2678
3004
|
}
|
|
2679
3005
|
});
|
|
2680
3006
|
|
|
@@ -2964,7 +3290,7 @@ __export(gql_parser_exports, {
|
|
|
2964
3290
|
function isGQLParseError(v) {
|
|
2965
3291
|
return v.type === "GQLParseError";
|
|
2966
3292
|
}
|
|
2967
|
-
function
|
|
3293
|
+
function tokenize2(input) {
|
|
2968
3294
|
const tokens = [];
|
|
2969
3295
|
let i = 0;
|
|
2970
3296
|
const len = input.length;
|
|
@@ -3059,7 +3385,7 @@ function tokenize(input) {
|
|
|
3059
3385
|
return tokens;
|
|
3060
3386
|
}
|
|
3061
3387
|
function parseGQL(input) {
|
|
3062
|
-
const tokens =
|
|
3388
|
+
const tokens = tokenize2(input.trim());
|
|
3063
3389
|
if (!Array.isArray(tokens)) return tokens;
|
|
3064
3390
|
const parser = new Parser2(tokens);
|
|
3065
3391
|
return parser.parse();
|
|
@@ -3852,7 +4178,7 @@ function isTestFile(filePath) {
|
|
|
3852
4178
|
if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
|
|
3853
4179
|
if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
|
|
3854
4180
|
if (filePath.includes("__tests__")) return true;
|
|
3855
|
-
const base =
|
|
4181
|
+
const base = path32.basename(filePath);
|
|
3856
4182
|
if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
|
|
3857
4183
|
return false;
|
|
3858
4184
|
}
|
|
@@ -3897,7 +4223,7 @@ function computeCoverage(graph, scope) {
|
|
|
3897
4223
|
}
|
|
3898
4224
|
const baseNameToTestFiles = /* @__PURE__ */ new Map();
|
|
3899
4225
|
for (const testPath of testFilePaths) {
|
|
3900
|
-
const base =
|
|
4226
|
+
const base = path32.basename(testPath);
|
|
3901
4227
|
const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
|
|
3902
4228
|
const existing = baseNameToTestFiles.get(stripped) ?? [];
|
|
3903
4229
|
existing.push(testPath);
|
|
@@ -3930,7 +4256,7 @@ function computeCoverage(graph, scope) {
|
|
|
3930
4256
|
}
|
|
3931
4257
|
}
|
|
3932
4258
|
}
|
|
3933
|
-
const nodeBase =
|
|
4259
|
+
const nodeBase = path32.basename(node.filePath).replace(/\.[^.]+$/, "");
|
|
3934
4260
|
const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
|
|
3935
4261
|
for (const tf of matchingTestFiles) {
|
|
3936
4262
|
if (!testFiles.includes(tf)) testFiles.push(tf);
|
|
@@ -4000,7 +4326,7 @@ var init_secret_scanner = __esm({
|
|
|
4000
4326
|
const ignorePatterns = [...options?.ignorePatterns ?? []];
|
|
4001
4327
|
if (options?.workspaceRoot) {
|
|
4002
4328
|
try {
|
|
4003
|
-
const raw =
|
|
4329
|
+
const raw = fs26.readFileSync(path32.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
|
|
4004
4330
|
for (const line of raw.split("\n")) {
|
|
4005
4331
|
const trimmed = line.trim();
|
|
4006
4332
|
if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
|
|
@@ -4288,10 +4614,10 @@ var init_codes = __esm({
|
|
|
4288
4614
|
}
|
|
4289
4615
|
});
|
|
4290
4616
|
function secureMkdir(dir) {
|
|
4291
|
-
|
|
4617
|
+
fs26.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
4292
4618
|
if (process.platform !== "win32") {
|
|
4293
4619
|
try {
|
|
4294
|
-
|
|
4620
|
+
fs26.chmodSync(dir, SECURE_DIR_MODE);
|
|
4295
4621
|
} catch {
|
|
4296
4622
|
}
|
|
4297
4623
|
}
|
|
@@ -4299,17 +4625,17 @@ function secureMkdir(dir) {
|
|
|
4299
4625
|
function secureChmodFile(file) {
|
|
4300
4626
|
if (process.platform === "win32") return;
|
|
4301
4627
|
try {
|
|
4302
|
-
|
|
4628
|
+
fs26.chmodSync(file, SECURE_FILE_MODE);
|
|
4303
4629
|
} catch {
|
|
4304
4630
|
}
|
|
4305
4631
|
}
|
|
4306
4632
|
function tightenDbFiles(dir) {
|
|
4307
4633
|
if (process.platform === "win32") return;
|
|
4308
|
-
if (!
|
|
4309
|
-
for (const name of
|
|
4634
|
+
if (!fs26.existsSync(dir)) return;
|
|
4635
|
+
for (const name of fs26.readdirSync(dir)) {
|
|
4310
4636
|
if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
|
|
4311
4637
|
try {
|
|
4312
|
-
|
|
4638
|
+
fs26.chmodSync(path32.join(dir, name), SECURE_FILE_MODE);
|
|
4313
4639
|
} catch {
|
|
4314
4640
|
}
|
|
4315
4641
|
}
|
|
@@ -4323,7 +4649,7 @@ var init_fs_secure = __esm({
|
|
|
4323
4649
|
}
|
|
4324
4650
|
});
|
|
4325
4651
|
function getUsersDBPath() {
|
|
4326
|
-
return process.env["CODE_INTEL_USERS_DB_PATH"] ??
|
|
4652
|
+
return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "users.db");
|
|
4327
4653
|
}
|
|
4328
4654
|
function getOrCreateUsersDB() {
|
|
4329
4655
|
if (!_usersDB) {
|
|
@@ -4339,9 +4665,9 @@ var init_users_db = __esm({
|
|
|
4339
4665
|
UsersDB = class {
|
|
4340
4666
|
db;
|
|
4341
4667
|
constructor(dbPath) {
|
|
4342
|
-
const dir =
|
|
4668
|
+
const dir = path32.dirname(dbPath);
|
|
4343
4669
|
secureMkdir(dir);
|
|
4344
|
-
this.db = new
|
|
4670
|
+
this.db = new Database2(dbPath);
|
|
4345
4671
|
this.db.pragma("journal_mode = WAL");
|
|
4346
4672
|
this.db.pragma("foreign_keys = ON");
|
|
4347
4673
|
this.createTables();
|
|
@@ -4616,7 +4942,7 @@ function getScryptN() {
|
|
|
4616
4942
|
return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
|
|
4617
4943
|
}
|
|
4618
4944
|
function getSecretsPath() {
|
|
4619
|
-
return process.env["CODE_INTEL_SECRETS_PATH"] ??
|
|
4945
|
+
return process.env["CODE_INTEL_SECRETS_PATH"] ?? path32.join(os13.homedir(), ".code-intel", ".secrets");
|
|
4620
4946
|
}
|
|
4621
4947
|
function getMasterPassword() {
|
|
4622
4948
|
const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
|
|
@@ -4647,8 +4973,8 @@ function decryptSecrets(encrypted) {
|
|
|
4647
4973
|
return JSON.parse(plaintext.toString("utf8"));
|
|
4648
4974
|
}
|
|
4649
4975
|
function loadSecrets(secretsPath = getSecretsPath()) {
|
|
4650
|
-
if (!
|
|
4651
|
-
const blob =
|
|
4976
|
+
if (!fs26.existsSync(secretsPath)) return {};
|
|
4977
|
+
const blob = fs26.readFileSync(secretsPath);
|
|
4652
4978
|
return decryptSecrets(blob);
|
|
4653
4979
|
}
|
|
4654
4980
|
function getSecret(key, secretsPath = getSecretsPath()) {
|
|
@@ -4669,11 +4995,18 @@ function getSessionTtlMs() {
|
|
|
4669
4995
|
const hours = parseInt(process.env["CODE_INTEL_SESSION_TTL_HOURS"] ?? "8", 10);
|
|
4670
4996
|
return (isNaN(hours) ? 8 : hours) * 60 * 60 * 1e3;
|
|
4671
4997
|
}
|
|
4672
|
-
function createSession(user) {
|
|
4998
|
+
function createSession(user, rememberMe = false) {
|
|
4673
4999
|
const sessionId = v4();
|
|
4674
|
-
const
|
|
4675
|
-
|
|
4676
|
-
|
|
5000
|
+
const ttlMs = rememberMe ? REMEMBER_ME_TTL_MS : getSessionTtlMs();
|
|
5001
|
+
const expiresAt = Date.now() + ttlMs;
|
|
5002
|
+
sessionStore.set(sessionId, {
|
|
5003
|
+
userId: user.id,
|
|
5004
|
+
username: user.username,
|
|
5005
|
+
role: user.role,
|
|
5006
|
+
expiresAt,
|
|
5007
|
+
ttlMs
|
|
5008
|
+
});
|
|
5009
|
+
return { sessionId, ttlMs };
|
|
4677
5010
|
}
|
|
4678
5011
|
function getSession(sessionId) {
|
|
4679
5012
|
const entry = sessionStore.get(sessionId);
|
|
@@ -4682,10 +5015,9 @@ function getSession(sessionId) {
|
|
|
4682
5015
|
sessionStore.delete(sessionId);
|
|
4683
5016
|
return null;
|
|
4684
5017
|
}
|
|
4685
|
-
const ttlMs = getSessionTtlMs();
|
|
4686
5018
|
const remaining = entry.expiresAt - Date.now();
|
|
4687
|
-
if (remaining < ttlMs * 0.75) {
|
|
4688
|
-
entry.expiresAt = Date.now() + ttlMs;
|
|
5019
|
+
if (remaining < entry.ttlMs * 0.75) {
|
|
5020
|
+
entry.expiresAt = Date.now() + entry.ttlMs;
|
|
4689
5021
|
}
|
|
4690
5022
|
return entry;
|
|
4691
5023
|
}
|
|
@@ -4705,7 +5037,7 @@ function authMiddleware(req, res, next) {
|
|
|
4705
5037
|
const session = getSession(sessionId);
|
|
4706
5038
|
if (session) {
|
|
4707
5039
|
req.user = { id: session.userId, username: session.username, role: session.role, authMethod: "session" };
|
|
4708
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
5040
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, session.ttlMs));
|
|
4709
5041
|
next();
|
|
4710
5042
|
return;
|
|
4711
5043
|
}
|
|
@@ -4875,9 +5207,9 @@ function parseCookies(cookieHeader) {
|
|
|
4875
5207
|
}
|
|
4876
5208
|
return result;
|
|
4877
5209
|
}
|
|
4878
|
-
function buildSessionCookie(sessionId) {
|
|
5210
|
+
function buildSessionCookie(sessionId, ttlMs) {
|
|
4879
5211
|
const isProduction = process.env["NODE_ENV"] === "production";
|
|
4880
|
-
const maxAge = Math.floor(getSessionTtlMs() / 1e3);
|
|
5212
|
+
const maxAge = Math.floor((ttlMs ?? getSessionTtlMs()) / 1e3);
|
|
4881
5213
|
const parts = [
|
|
4882
5214
|
`${SESSION_COOKIE_NAME}=${encodeURIComponent(sessionId)}`,
|
|
4883
5215
|
`HttpOnly`,
|
|
@@ -4894,7 +5226,7 @@ function clearSessionCookie() {
|
|
|
4894
5226
|
async function verifyPassword(plain, hash) {
|
|
4895
5227
|
return bcrypt.compare(plain, hash);
|
|
4896
5228
|
}
|
|
4897
|
-
var sessionStore, SESSION_COOKIE_NAME, ROLE_RANK;
|
|
5229
|
+
var sessionStore, SESSION_COOKIE_NAME, REMEMBER_ME_TTL_MS, ROLE_RANK;
|
|
4898
5230
|
var init_middleware = __esm({
|
|
4899
5231
|
"src/auth/middleware.ts"() {
|
|
4900
5232
|
init_users_db();
|
|
@@ -4902,6 +5234,7 @@ var init_middleware = __esm({
|
|
|
4902
5234
|
init_secret_store();
|
|
4903
5235
|
sessionStore = /* @__PURE__ */ new Map();
|
|
4904
5236
|
SESSION_COOKIE_NAME = "code_intel_session";
|
|
5237
|
+
REMEMBER_ME_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
4905
5238
|
ROLE_RANK = {
|
|
4906
5239
|
viewer: 1,
|
|
4907
5240
|
"repo-owner": 2,
|
|
@@ -5034,130 +5367,23 @@ var init_websocket_server = __esm({
|
|
|
5034
5367
|
}
|
|
5035
5368
|
});
|
|
5036
5369
|
|
|
5037
|
-
// src/graph/knowledge-graph.ts
|
|
5038
|
-
function createKnowledgeGraph() {
|
|
5039
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
5040
|
-
const edges = /* @__PURE__ */ new Map();
|
|
5041
|
-
const edgesByKind = /* @__PURE__ */ new Map();
|
|
5042
|
-
const edgesFromNode = /* @__PURE__ */ new Map();
|
|
5043
|
-
const edgesToNode = /* @__PURE__ */ new Map();
|
|
5044
|
-
function indexEdge(edge) {
|
|
5045
|
-
let kindSet = edgesByKind.get(edge.kind);
|
|
5046
|
-
if (!kindSet) {
|
|
5047
|
-
kindSet = /* @__PURE__ */ new Set();
|
|
5048
|
-
edgesByKind.set(edge.kind, kindSet);
|
|
5049
|
-
}
|
|
5050
|
-
kindSet.add(edge.id);
|
|
5051
|
-
let fromSet = edgesFromNode.get(edge.source);
|
|
5052
|
-
if (!fromSet) {
|
|
5053
|
-
fromSet = /* @__PURE__ */ new Set();
|
|
5054
|
-
edgesFromNode.set(edge.source, fromSet);
|
|
5055
|
-
}
|
|
5056
|
-
fromSet.add(edge.id);
|
|
5057
|
-
let toSet = edgesToNode.get(edge.target);
|
|
5058
|
-
if (!toSet) {
|
|
5059
|
-
toSet = /* @__PURE__ */ new Set();
|
|
5060
|
-
edgesToNode.set(edge.target, toSet);
|
|
5061
|
-
}
|
|
5062
|
-
toSet.add(edge.id);
|
|
5063
|
-
}
|
|
5064
|
-
function unindexEdge(edge) {
|
|
5065
|
-
edgesByKind.get(edge.kind)?.delete(edge.id);
|
|
5066
|
-
edgesFromNode.get(edge.source)?.delete(edge.id);
|
|
5067
|
-
edgesToNode.get(edge.target)?.delete(edge.id);
|
|
5068
|
-
}
|
|
5069
|
-
return {
|
|
5070
|
-
addNode(node) {
|
|
5071
|
-
nodes.set(node.id, node);
|
|
5072
|
-
},
|
|
5073
|
-
addEdge(edge) {
|
|
5074
|
-
edges.set(edge.id, edge);
|
|
5075
|
-
indexEdge(edge);
|
|
5076
|
-
},
|
|
5077
|
-
getNode(id) {
|
|
5078
|
-
return nodes.get(id);
|
|
5079
|
-
},
|
|
5080
|
-
getEdge(id) {
|
|
5081
|
-
return edges.get(id);
|
|
5082
|
-
},
|
|
5083
|
-
*findEdgesByKind(kind) {
|
|
5084
|
-
const ids = edgesByKind.get(kind);
|
|
5085
|
-
if (!ids) return;
|
|
5086
|
-
for (const id of ids) {
|
|
5087
|
-
const edge = edges.get(id);
|
|
5088
|
-
if (edge) yield edge;
|
|
5089
|
-
}
|
|
5090
|
-
},
|
|
5091
|
-
*findEdgesFrom(sourceId) {
|
|
5092
|
-
const ids = edgesFromNode.get(sourceId);
|
|
5093
|
-
if (!ids) return;
|
|
5094
|
-
for (const id of ids) {
|
|
5095
|
-
const edge = edges.get(id);
|
|
5096
|
-
if (edge) yield edge;
|
|
5097
|
-
}
|
|
5098
|
-
},
|
|
5099
|
-
*findEdgesTo(targetId) {
|
|
5100
|
-
const ids = edgesToNode.get(targetId);
|
|
5101
|
-
if (!ids) return;
|
|
5102
|
-
for (const id of ids) {
|
|
5103
|
-
const edge = edges.get(id);
|
|
5104
|
-
if (edge) yield edge;
|
|
5105
|
-
}
|
|
5106
|
-
},
|
|
5107
|
-
removeNodeCascade(id) {
|
|
5108
|
-
const fromEdges = edgesFromNode.get(id);
|
|
5109
|
-
if (fromEdges) {
|
|
5110
|
-
for (const edgeId of [...fromEdges]) {
|
|
5111
|
-
const edge = edges.get(edgeId);
|
|
5112
|
-
if (edge) {
|
|
5113
|
-
unindexEdge(edge);
|
|
5114
|
-
edges.delete(edgeId);
|
|
5115
|
-
}
|
|
5116
|
-
}
|
|
5117
|
-
}
|
|
5118
|
-
const toEdges = edgesToNode.get(id);
|
|
5119
|
-
if (toEdges) {
|
|
5120
|
-
for (const edgeId of [...toEdges]) {
|
|
5121
|
-
const edge = edges.get(edgeId);
|
|
5122
|
-
if (edge) {
|
|
5123
|
-
unindexEdge(edge);
|
|
5124
|
-
edges.delete(edgeId);
|
|
5125
|
-
}
|
|
5126
|
-
}
|
|
5127
|
-
}
|
|
5128
|
-
edgesFromNode.delete(id);
|
|
5129
|
-
edgesToNode.delete(id);
|
|
5130
|
-
nodes.delete(id);
|
|
5131
|
-
},
|
|
5132
|
-
removeEdge(id) {
|
|
5133
|
-
const edge = edges.get(id);
|
|
5134
|
-
if (edge) {
|
|
5135
|
-
unindexEdge(edge);
|
|
5136
|
-
edges.delete(id);
|
|
5137
|
-
}
|
|
5138
|
-
},
|
|
5139
|
-
*allNodes() {
|
|
5140
|
-
yield* nodes.values();
|
|
5141
|
-
},
|
|
5142
|
-
*allEdges() {
|
|
5143
|
-
yield* edges.values();
|
|
5144
|
-
},
|
|
5145
|
-
get size() {
|
|
5146
|
-
return { nodes: nodes.size, edges: edges.size };
|
|
5147
|
-
},
|
|
5148
|
-
clear() {
|
|
5149
|
-
nodes.clear();
|
|
5150
|
-
edges.clear();
|
|
5151
|
-
edgesByKind.clear();
|
|
5152
|
-
edgesFromNode.clear();
|
|
5153
|
-
edgesToNode.clear();
|
|
5154
|
-
}
|
|
5155
|
-
};
|
|
5156
|
-
}
|
|
5157
|
-
|
|
5158
5370
|
// src/graph/index.ts
|
|
5371
|
+
init_knowledge_graph();
|
|
5159
5372
|
init_id_generator();
|
|
5160
5373
|
|
|
5374
|
+
// src/graph/lazy-knowledge-graph.ts
|
|
5375
|
+
init_schema();
|
|
5376
|
+
init_logger();
|
|
5377
|
+
Object.fromEntries(
|
|
5378
|
+
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
5379
|
+
);
|
|
5380
|
+
function isLazyGraph(g) {
|
|
5381
|
+
return "lazy" in g && g.lazy === true;
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
// src/graph/compact-knowledge-graph.ts
|
|
5385
|
+
init_logger();
|
|
5386
|
+
|
|
5161
5387
|
// src/parsing/index.ts
|
|
5162
5388
|
init_parser_manager();
|
|
5163
5389
|
init_query_runner();
|
|
@@ -5215,7 +5441,7 @@ init_shared();
|
|
|
5215
5441
|
init_shared();
|
|
5216
5442
|
init_typescript();
|
|
5217
5443
|
function resolveRelative(rawPath, fromFile, workspace) {
|
|
5218
|
-
const fromDir =
|
|
5444
|
+
const fromDir = path32.dirname(fromFile);
|
|
5219
5445
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5220
5446
|
const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
|
|
5221
5447
|
const resolved = workspace.resolve(fromDir, cleaned);
|
|
@@ -5267,7 +5493,7 @@ var pythonModule = {
|
|
|
5267
5493
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5268
5494
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5269
5495
|
const parts = cleaned.split(".");
|
|
5270
|
-
const fromDir =
|
|
5496
|
+
const fromDir = path32.dirname(fromFile);
|
|
5271
5497
|
const relPath = parts.join("/");
|
|
5272
5498
|
for (const suffix of ["/__init__.py", ".py"]) {
|
|
5273
5499
|
const r = workspace.resolve(fromDir, relPath + suffix);
|
|
@@ -5346,7 +5572,7 @@ var cModule = {
|
|
|
5346
5572
|
inheritanceStrategy: "none",
|
|
5347
5573
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5348
5574
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5349
|
-
const fromDir =
|
|
5575
|
+
const fromDir = path32.dirname(fromFile);
|
|
5350
5576
|
return workspace.resolve(fromDir, cleaned);
|
|
5351
5577
|
},
|
|
5352
5578
|
isExported(_node) {
|
|
@@ -5369,7 +5595,7 @@ var cppModule = {
|
|
|
5369
5595
|
inheritanceStrategy: "depth-first",
|
|
5370
5596
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5371
5597
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5372
|
-
const fromDir =
|
|
5598
|
+
const fromDir = path32.dirname(fromFile);
|
|
5373
5599
|
return workspace.resolve(fromDir, cleaned);
|
|
5374
5600
|
},
|
|
5375
5601
|
isExported(_node) {
|
|
@@ -5531,7 +5757,7 @@ var dartModule = {
|
|
|
5531
5757
|
const pkg = cleaned.replace("package:", "");
|
|
5532
5758
|
return workspace.findByPackage(pkg);
|
|
5533
5759
|
}
|
|
5534
|
-
const fromDir =
|
|
5760
|
+
const fromDir = path32.dirname(fromFile);
|
|
5535
5761
|
return workspace.resolve(fromDir, cleaned);
|
|
5536
5762
|
},
|
|
5537
5763
|
isExported(node) {
|
|
@@ -5886,25 +6112,25 @@ function validateDAG(phases) {
|
|
|
5886
6112
|
const visiting = /* @__PURE__ */ new Set();
|
|
5887
6113
|
const visited = /* @__PURE__ */ new Set();
|
|
5888
6114
|
const phaseMap = new Map(phases.map((p) => [p.name, p]));
|
|
5889
|
-
function dfs(name,
|
|
6115
|
+
function dfs(name, path33) {
|
|
5890
6116
|
if (visiting.has(name)) {
|
|
5891
|
-
const cycleStart =
|
|
5892
|
-
const cycle =
|
|
6117
|
+
const cycleStart = path33.indexOf(name);
|
|
6118
|
+
const cycle = path33.slice(cycleStart).concat(name);
|
|
5893
6119
|
errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
|
|
5894
6120
|
return true;
|
|
5895
6121
|
}
|
|
5896
6122
|
if (visited.has(name)) return false;
|
|
5897
6123
|
visiting.add(name);
|
|
5898
|
-
|
|
6124
|
+
path33.push(name);
|
|
5899
6125
|
const phase = phaseMap.get(name);
|
|
5900
6126
|
if (phase) {
|
|
5901
6127
|
for (const dep of phase.dependencies) {
|
|
5902
|
-
if (dfs(dep,
|
|
6128
|
+
if (dfs(dep, path33)) return true;
|
|
5903
6129
|
}
|
|
5904
6130
|
}
|
|
5905
6131
|
visiting.delete(name);
|
|
5906
6132
|
visited.add(name);
|
|
5907
|
-
|
|
6133
|
+
path33.pop();
|
|
5908
6134
|
return false;
|
|
5909
6135
|
}
|
|
5910
6136
|
for (const phase of phases) {
|
|
@@ -6032,6 +6258,7 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6032
6258
|
for (const phase of sorted) {
|
|
6033
6259
|
context2.onProgress?.(phase.name, "running");
|
|
6034
6260
|
const phaseStart = Date.now();
|
|
6261
|
+
const memBefore = context2.profile ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0;
|
|
6035
6262
|
const runPhase = async () => {
|
|
6036
6263
|
const depResults = /* @__PURE__ */ new Map();
|
|
6037
6264
|
for (const dep of phase.dependencies) {
|
|
@@ -6055,6 +6282,10 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6055
6282
|
}
|
|
6056
6283
|
const durationSec = (Date.now() - phaseStart) / 1e3;
|
|
6057
6284
|
pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: result.status }, durationSec);
|
|
6285
|
+
if (memBefore !== void 0) {
|
|
6286
|
+
result.memoryBeforeMB = memBefore;
|
|
6287
|
+
result.memoryAfterMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
6288
|
+
}
|
|
6058
6289
|
results.set(phase.name, result);
|
|
6059
6290
|
context2.onProgress?.(phase.name, result.status);
|
|
6060
6291
|
if (result.status === "failed") {
|
|
@@ -6065,7 +6296,9 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6065
6296
|
const result = {
|
|
6066
6297
|
status: "failed",
|
|
6067
6298
|
duration: Date.now() - phaseStart,
|
|
6068
|
-
message: err instanceof Error ? err.message : String(err)
|
|
6299
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6300
|
+
memoryBeforeMB: memBefore,
|
|
6301
|
+
memoryAfterMB: memBefore !== void 0 ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0
|
|
6069
6302
|
};
|
|
6070
6303
|
pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: "failed" }, (Date.now() - phaseStart) / 1e3);
|
|
6071
6304
|
results.set(phase.name, result);
|
|
@@ -6117,7 +6350,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
6117
6350
|
]);
|
|
6118
6351
|
function loadIgnorePatterns(workspaceRoot) {
|
|
6119
6352
|
try {
|
|
6120
|
-
const raw =
|
|
6353
|
+
const raw = fs26.readFileSync(path32.join(workspaceRoot, ".codeintelignore"), "utf-8");
|
|
6121
6354
|
const extras = /* @__PURE__ */ new Set();
|
|
6122
6355
|
for (const line of raw.split("\n")) {
|
|
6123
6356
|
const trimmed = line.trim();
|
|
@@ -6141,7 +6374,7 @@ var scanPhase = {
|
|
|
6141
6374
|
function walk(dir) {
|
|
6142
6375
|
let entries;
|
|
6143
6376
|
try {
|
|
6144
|
-
entries =
|
|
6377
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
6145
6378
|
} catch {
|
|
6146
6379
|
return;
|
|
6147
6380
|
}
|
|
@@ -6150,15 +6383,15 @@ var scanPhase = {
|
|
|
6150
6383
|
if (entry.name.startsWith(".")) continue;
|
|
6151
6384
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6152
6385
|
if (extraIgnore.has(entry.name)) continue;
|
|
6153
|
-
walk(
|
|
6386
|
+
walk(path32.join(dir, entry.name));
|
|
6154
6387
|
} else if (entry.isFile()) {
|
|
6155
6388
|
const name = entry.name;
|
|
6156
6389
|
if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
|
|
6157
|
-
const ext =
|
|
6390
|
+
const ext = path32.extname(name);
|
|
6158
6391
|
if (!extensions.has(ext)) continue;
|
|
6159
|
-
const fullPath =
|
|
6392
|
+
const fullPath = path32.join(dir, name);
|
|
6160
6393
|
try {
|
|
6161
|
-
const stat =
|
|
6394
|
+
const stat = fs26.statSync(fullPath);
|
|
6162
6395
|
if (stat.size > MAX_FILE_SIZE_BYTES) continue;
|
|
6163
6396
|
} catch {
|
|
6164
6397
|
continue;
|
|
@@ -6185,20 +6418,20 @@ var structurePhase = {
|
|
|
6185
6418
|
const dirs = /* @__PURE__ */ new Set();
|
|
6186
6419
|
let structDone = 0;
|
|
6187
6420
|
for (const filePath of context2.filePaths) {
|
|
6188
|
-
const relativePath =
|
|
6421
|
+
const relativePath = path32.relative(context2.workspaceRoot, filePath);
|
|
6189
6422
|
const lang = detectLanguage(filePath);
|
|
6190
6423
|
context2.graph.addNode({
|
|
6191
6424
|
id: generateNodeId("file", relativePath, relativePath),
|
|
6192
6425
|
kind: "file",
|
|
6193
|
-
name:
|
|
6426
|
+
name: path32.basename(filePath),
|
|
6194
6427
|
filePath: relativePath,
|
|
6195
6428
|
metadata: lang ? { language: lang } : void 0
|
|
6196
6429
|
});
|
|
6197
|
-
let dir =
|
|
6430
|
+
let dir = path32.dirname(relativePath);
|
|
6198
6431
|
while (dir && dir !== "." && dir !== "") {
|
|
6199
6432
|
if (dirs.has(dir)) break;
|
|
6200
6433
|
dirs.add(dir);
|
|
6201
|
-
dir =
|
|
6434
|
+
dir = path32.dirname(dir);
|
|
6202
6435
|
}
|
|
6203
6436
|
structDone++;
|
|
6204
6437
|
context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
|
|
@@ -6207,7 +6440,7 @@ var structurePhase = {
|
|
|
6207
6440
|
context2.graph.addNode({
|
|
6208
6441
|
id: generateNodeId("directory", dir, dir),
|
|
6209
6442
|
kind: "directory",
|
|
6210
|
-
name:
|
|
6443
|
+
name: path32.basename(dir),
|
|
6211
6444
|
filePath: dir
|
|
6212
6445
|
});
|
|
6213
6446
|
}
|
|
@@ -6318,22 +6551,22 @@ var flowPhase = {
|
|
|
6318
6551
|
const queue = [{ nodeId: ep.id, path: [ep.id] }];
|
|
6319
6552
|
const visited = /* @__PURE__ */ new Set();
|
|
6320
6553
|
while (queue.length > 0 && flowCount < maxFlows) {
|
|
6321
|
-
const { nodeId, path:
|
|
6322
|
-
if (
|
|
6554
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6555
|
+
if (path33.length > maxDepth) continue;
|
|
6323
6556
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6324
|
-
if (callEdges.length === 0 &&
|
|
6557
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6325
6558
|
const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
|
|
6326
6559
|
graph.addNode({
|
|
6327
6560
|
id: flowId,
|
|
6328
6561
|
kind: "flow",
|
|
6329
6562
|
name: `${ep.name} flow ${flowCount}`,
|
|
6330
6563
|
filePath: ep.filePath,
|
|
6331
|
-
metadata: { steps:
|
|
6564
|
+
metadata: { steps: path33, entryPoint: ep.name }
|
|
6332
6565
|
});
|
|
6333
|
-
for (let i = 0; i <
|
|
6566
|
+
for (let i = 0; i < path33.length; i++) {
|
|
6334
6567
|
graph.addEdge({
|
|
6335
|
-
id: generateEdgeId(
|
|
6336
|
-
source:
|
|
6568
|
+
id: generateEdgeId(path33[i], flowId, `step_of_${i}`),
|
|
6569
|
+
source: path33[i],
|
|
6337
6570
|
target: flowId,
|
|
6338
6571
|
kind: "step_of",
|
|
6339
6572
|
weight: 1,
|
|
@@ -6346,7 +6579,7 @@ var flowPhase = {
|
|
|
6346
6579
|
for (const edge of callEdges) {
|
|
6347
6580
|
if (visited.has(edge.target)) continue;
|
|
6348
6581
|
visited.add(edge.target);
|
|
6349
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6582
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6350
6583
|
}
|
|
6351
6584
|
}
|
|
6352
6585
|
}
|
|
@@ -6364,7 +6597,7 @@ var LLMGovernanceLogger = class {
|
|
|
6364
6597
|
}
|
|
6365
6598
|
/** Path to the JSONL log file. */
|
|
6366
6599
|
getLogPath() {
|
|
6367
|
-
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ??
|
|
6600
|
+
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "llm-governance.jsonl");
|
|
6368
6601
|
}
|
|
6369
6602
|
/**
|
|
6370
6603
|
* Append an entry to the governance log.
|
|
@@ -6380,8 +6613,8 @@ var LLMGovernanceLogger = class {
|
|
|
6380
6613
|
...entry
|
|
6381
6614
|
};
|
|
6382
6615
|
const logPath = this.getLogPath();
|
|
6383
|
-
|
|
6384
|
-
|
|
6616
|
+
fs26.mkdirSync(path32.dirname(logPath), { recursive: true });
|
|
6617
|
+
fs26.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
|
|
6385
6618
|
} catch {
|
|
6386
6619
|
}
|
|
6387
6620
|
}
|
|
@@ -6391,7 +6624,7 @@ var LLMGovernanceLogger = class {
|
|
|
6391
6624
|
*/
|
|
6392
6625
|
readLog(limit = 100) {
|
|
6393
6626
|
try {
|
|
6394
|
-
const raw =
|
|
6627
|
+
const raw = fs26.readFileSync(this.getLogPath(), "utf-8");
|
|
6395
6628
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
|
|
6396
6629
|
return lines.map((l) => JSON.parse(l));
|
|
6397
6630
|
} catch {
|
|
@@ -6401,6 +6634,9 @@ var LLMGovernanceLogger = class {
|
|
|
6401
6634
|
};
|
|
6402
6635
|
var governanceLogger = new LLMGovernanceLogger();
|
|
6403
6636
|
|
|
6637
|
+
// src/pipeline/phases/summarize-phase.ts
|
|
6638
|
+
init_logger();
|
|
6639
|
+
|
|
6404
6640
|
// src/pipeline/workers/parse-phase-parallel.ts
|
|
6405
6641
|
init_shared();
|
|
6406
6642
|
init_id_generator();
|
|
@@ -6535,17 +6771,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
|
|
|
6535
6771
|
const queue = [{ nodeId: entryId, path: [entryId] }];
|
|
6536
6772
|
const visited = /* @__PURE__ */ new Set();
|
|
6537
6773
|
while (queue.length > 0 && flows.length < maxFlows) {
|
|
6538
|
-
const { nodeId, path:
|
|
6539
|
-
if (
|
|
6774
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6775
|
+
if (path33.length > maxDepth) continue;
|
|
6540
6776
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6541
|
-
if (callEdges.length === 0 &&
|
|
6542
|
-
flows.push({ entryPointId: entryId, steps: [...
|
|
6777
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6778
|
+
flows.push({ entryPointId: entryId, steps: [...path33] });
|
|
6543
6779
|
continue;
|
|
6544
6780
|
}
|
|
6545
6781
|
for (const edge of callEdges) {
|
|
6546
6782
|
if (visited.has(edge.target)) continue;
|
|
6547
6783
|
visited.add(edge.target);
|
|
6548
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6784
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6549
6785
|
}
|
|
6550
6786
|
}
|
|
6551
6787
|
}
|
|
@@ -6646,7 +6882,7 @@ var VectorIndex = class {
|
|
|
6646
6882
|
this.sqlitePath = sqlitePath;
|
|
6647
6883
|
}
|
|
6648
6884
|
async init() {
|
|
6649
|
-
this.db = new
|
|
6885
|
+
this.db = new Database2(this.sqlitePath);
|
|
6650
6886
|
this.db.pragma("journal_mode = WAL");
|
|
6651
6887
|
this.db.exec(`
|
|
6652
6888
|
CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
|
|
@@ -6777,9 +7013,9 @@ function siftDown(arr, i, score) {
|
|
|
6777
7013
|
}
|
|
6778
7014
|
init_embedder();
|
|
6779
7015
|
async function hybridSearch(graph, query, limit, options = {}) {
|
|
6780
|
-
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
|
|
6781
|
-
const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
6782
|
-
const hasVectorDb = Boolean(vectorDbPath &&
|
|
7016
|
+
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
|
|
7017
|
+
const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
7018
|
+
const hasVectorDb = Boolean(vectorDbPath && fs26.existsSync(vectorDbPath));
|
|
6783
7019
|
if (!hasVectorDb) {
|
|
6784
7020
|
const bm25Results2 = await bm25Promise;
|
|
6785
7021
|
return {
|
|
@@ -6828,105 +7064,317 @@ async function runVectorSearch(vectorDbPath, query, topK) {
|
|
|
6828
7064
|
return null;
|
|
6829
7065
|
}
|
|
6830
7066
|
}
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
7067
|
+
|
|
7068
|
+
// src/search/bm25-index.ts
|
|
7069
|
+
init_logger();
|
|
7070
|
+
var K1 = 1.2;
|
|
7071
|
+
var B = 0.75;
|
|
7072
|
+
function tokenize(text) {
|
|
7073
|
+
return text.toLowerCase().split(/[\s\-_./\\:(){}[\]<>,"'`~!@#$%^&*+=|;?]+/).filter((t) => t.length >= 2 && t.length <= 64);
|
|
7074
|
+
}
|
|
7075
|
+
function nodeToDoc(node) {
|
|
7076
|
+
return [
|
|
7077
|
+
node.name,
|
|
7078
|
+
node.kind,
|
|
7079
|
+
node.filePath,
|
|
7080
|
+
(node.content ?? "").slice(0, 1e3)
|
|
7081
|
+
].join(" ");
|
|
7082
|
+
}
|
|
7083
|
+
function heapTopK(scores, k) {
|
|
7084
|
+
if (k <= 0) return [];
|
|
7085
|
+
const heap = [];
|
|
7086
|
+
function heapifyUp(i) {
|
|
7087
|
+
while (i > 0) {
|
|
7088
|
+
const parent = i - 1 >> 1;
|
|
7089
|
+
if (heap[parent][1] > heap[i][1]) {
|
|
7090
|
+
[heap[parent], heap[i]] = [heap[i], heap[parent]];
|
|
7091
|
+
i = parent;
|
|
7092
|
+
} else break;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
function heapifyDown(i) {
|
|
7096
|
+
const n = heap.length;
|
|
7097
|
+
while (true) {
|
|
7098
|
+
let smallest = i;
|
|
7099
|
+
const l = 2 * i + 1, r = 2 * i + 2;
|
|
7100
|
+
if (l < n && heap[l][1] < heap[smallest][1]) smallest = l;
|
|
7101
|
+
if (r < n && heap[r][1] < heap[smallest][1]) smallest = r;
|
|
7102
|
+
if (smallest === i) break;
|
|
7103
|
+
[heap[smallest], heap[i]] = [heap[i], heap[smallest]];
|
|
7104
|
+
i = smallest;
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
for (const [nodeId, score] of scores) {
|
|
7108
|
+
if (heap.length < k) {
|
|
7109
|
+
heap.push([nodeId, score]);
|
|
7110
|
+
heapifyUp(heap.length - 1);
|
|
7111
|
+
} else if (score > heap[0][1]) {
|
|
7112
|
+
heap[0] = [nodeId, score];
|
|
7113
|
+
heapifyDown(0);
|
|
7114
|
+
}
|
|
7115
|
+
}
|
|
7116
|
+
return heap.sort((a, b) => b[1] - a[1]);
|
|
7117
|
+
}
|
|
7118
|
+
var Bm25Index = class {
|
|
6835
7119
|
constructor(dbPath) {
|
|
6836
7120
|
this.dbPath = dbPath;
|
|
6837
7121
|
}
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
const
|
|
6856
|
-
const
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
7122
|
+
dbPath;
|
|
7123
|
+
/** In-memory inverted index (populated after `load()`). */
|
|
7124
|
+
invertedIndex = /* @__PURE__ */ new Map();
|
|
7125
|
+
docLengths = /* @__PURE__ */ new Map();
|
|
7126
|
+
nodeMeta = /* @__PURE__ */ new Map();
|
|
7127
|
+
avgdl = 1;
|
|
7128
|
+
docCount = 0;
|
|
7129
|
+
_loaded = false;
|
|
7130
|
+
get isLoaded() {
|
|
7131
|
+
return this._loaded;
|
|
7132
|
+
}
|
|
7133
|
+
// ── Build ───────────────────────────────────────────────────────────────────
|
|
7134
|
+
/**
|
|
7135
|
+
* Build the inverted index from a KnowledgeGraph and persist to SQLite.
|
|
7136
|
+
* Called once at analysis time after the main pipeline completes.
|
|
7137
|
+
*/
|
|
7138
|
+
build(graph) {
|
|
7139
|
+
const nodeTermFreqs = /* @__PURE__ */ new Map();
|
|
7140
|
+
const docLengths = /* @__PURE__ */ new Map();
|
|
7141
|
+
const nodeMeta = /* @__PURE__ */ new Map();
|
|
7142
|
+
for (const node of graph.allNodes()) {
|
|
7143
|
+
if (["directory", "cluster", "flow"].includes(node.kind)) continue;
|
|
7144
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7145
|
+
const tf = /* @__PURE__ */ new Map();
|
|
7146
|
+
for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
7147
|
+
nodeTermFreqs.set(node.id, tf);
|
|
7148
|
+
docLengths.set(node.id, terms.length);
|
|
7149
|
+
nodeMeta.set(node.id, {
|
|
7150
|
+
name: node.name,
|
|
7151
|
+
kind: node.kind,
|
|
7152
|
+
filePath: node.filePath,
|
|
7153
|
+
snippet: node.content?.slice(0, 200)
|
|
7154
|
+
});
|
|
7155
|
+
}
|
|
7156
|
+
const docCount = nodeTermFreqs.size;
|
|
7157
|
+
const totalLen = [...docLengths.values()].reduce((a, b) => a + b, 0);
|
|
7158
|
+
const avgdl = docCount > 0 ? totalLen / docCount : 1;
|
|
7159
|
+
const invertedIndex = /* @__PURE__ */ new Map();
|
|
7160
|
+
for (const [nodeId, tf] of nodeTermFreqs) {
|
|
7161
|
+
for (const [term, count] of tf) {
|
|
7162
|
+
let postings = invertedIndex.get(term);
|
|
7163
|
+
if (!postings) {
|
|
7164
|
+
postings = [];
|
|
7165
|
+
invertedIndex.set(term, postings);
|
|
7166
|
+
}
|
|
7167
|
+
postings.push({ nodeId, tf: count });
|
|
7168
|
+
}
|
|
7169
|
+
}
|
|
7170
|
+
fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
7171
|
+
for (const f of [this.dbPath, `${this.dbPath}-shm`, `${this.dbPath}-wal`]) {
|
|
7172
|
+
try {
|
|
7173
|
+
if (fs26.existsSync(f)) fs26.unlinkSync(f);
|
|
7174
|
+
} catch {
|
|
7175
|
+
}
|
|
6863
7176
|
}
|
|
7177
|
+
const db = new Database2(this.dbPath);
|
|
7178
|
+
db.pragma("journal_mode = WAL");
|
|
7179
|
+
db.exec(`
|
|
7180
|
+
CREATE TABLE bm25_index (term TEXT PRIMARY KEY, postings TEXT NOT NULL);
|
|
7181
|
+
CREATE TABLE bm25_doclen (node_id TEXT PRIMARY KEY, doclen INTEGER NOT NULL);
|
|
7182
|
+
CREATE TABLE bm25_nodemeta(node_id TEXT PRIMARY KEY, name TEXT, kind TEXT, file_path TEXT, snippet TEXT);
|
|
7183
|
+
CREATE TABLE bm25_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
7184
|
+
`);
|
|
7185
|
+
const insPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
|
|
7186
|
+
const insDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
|
|
7187
|
+
const insNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
|
|
7188
|
+
const insMeta = db.prepare("INSERT OR REPLACE INTO bm25_meta VALUES (?, ?)");
|
|
7189
|
+
db.transaction(() => {
|
|
7190
|
+
for (const [term, postings] of invertedIndex) {
|
|
7191
|
+
insPosting.run(term, JSON.stringify(postings));
|
|
7192
|
+
}
|
|
7193
|
+
for (const [nodeId, len] of docLengths) {
|
|
7194
|
+
insDoclen.run(nodeId, len);
|
|
7195
|
+
}
|
|
7196
|
+
for (const [nodeId, meta] of nodeMeta) {
|
|
7197
|
+
insNodeMeta.run(nodeId, meta.name, meta.kind, meta.filePath, meta.snippet ?? null);
|
|
7198
|
+
}
|
|
7199
|
+
insMeta.run("avgdl", String(avgdl));
|
|
7200
|
+
insMeta.run("docCount", String(docCount));
|
|
7201
|
+
})();
|
|
7202
|
+
db.close();
|
|
7203
|
+
logger_default.info(` [bm25] Index built: ${invertedIndex.size} terms, ${docCount} documents`);
|
|
7204
|
+
}
|
|
7205
|
+
// ── Load into memory ────────────────────────────────────────────────────────
|
|
7206
|
+
/**
|
|
7207
|
+
* Load the full inverted index into memory.
|
|
7208
|
+
* Called once on `serve` startup.
|
|
7209
|
+
*/
|
|
7210
|
+
load() {
|
|
7211
|
+
if (!fs26.existsSync(this.dbPath)) return;
|
|
7212
|
+
const db = new Database2(this.dbPath, { readonly: true });
|
|
6864
7213
|
try {
|
|
6865
|
-
|
|
6866
|
-
|
|
7214
|
+
const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
|
|
7215
|
+
this.avgdl = parseFloat(getMeta.get("avgdl")?.value ?? "1");
|
|
7216
|
+
this.docCount = parseInt(getMeta.get("docCount")?.value ?? "0", 10);
|
|
7217
|
+
this.invertedIndex.clear();
|
|
7218
|
+
const postingRows = db.prepare("SELECT term, postings FROM bm25_index").all();
|
|
7219
|
+
for (const row of postingRows) {
|
|
7220
|
+
this.invertedIndex.set(row.term, JSON.parse(row.postings));
|
|
7221
|
+
}
|
|
7222
|
+
this.docLengths.clear();
|
|
7223
|
+
const dlRows = db.prepare("SELECT node_id, doclen FROM bm25_doclen").all();
|
|
7224
|
+
for (const row of dlRows) {
|
|
7225
|
+
this.docLengths.set(row.node_id, row.doclen);
|
|
7226
|
+
}
|
|
7227
|
+
this.nodeMeta.clear();
|
|
7228
|
+
const metaRows = db.prepare("SELECT node_id, name, kind, file_path, snippet FROM bm25_nodemeta").all();
|
|
7229
|
+
for (const row of metaRows) {
|
|
7230
|
+
this.nodeMeta.set(row.node_id, {
|
|
7231
|
+
name: row.name,
|
|
7232
|
+
kind: row.kind,
|
|
7233
|
+
filePath: row.file_path,
|
|
7234
|
+
snippet: row.snippet ?? void 0
|
|
7235
|
+
});
|
|
7236
|
+
}
|
|
7237
|
+
this._loaded = true;
|
|
7238
|
+
logger_default.info(` [bm25] Index loaded (${this.invertedIndex.size} terms)`);
|
|
7239
|
+
} finally {
|
|
7240
|
+
db.close();
|
|
6867
7241
|
}
|
|
6868
|
-
this.conn = null;
|
|
6869
|
-
this.db = null;
|
|
6870
7242
|
}
|
|
6871
|
-
|
|
6872
|
-
|
|
7243
|
+
// ── Search ──────────────────────────────────────────────────────────────────
|
|
7244
|
+
/**
|
|
7245
|
+
* BM25 search.
|
|
7246
|
+
*
|
|
7247
|
+
* Performance strategy:
|
|
7248
|
+
* 1. Skip ultra-high-df terms (df/N > 0.6) — near-zero IDF, dominate posting
|
|
7249
|
+
* lists for common words like "function", "return", "export" in large repos.
|
|
7250
|
+
* 2. Min-heap top-K selection — O(n log k) instead of full O(n log n) sort.
|
|
7251
|
+
* For k=10 and n=30,000 candidates this is ~10× faster than Array.sort.
|
|
7252
|
+
*/
|
|
7253
|
+
search(query, limit) {
|
|
7254
|
+
if (!this._loaded || this.invertedIndex.size === 0) return [];
|
|
7255
|
+
const queryTerms = [...new Set(tokenize(query))];
|
|
7256
|
+
if (queryTerms.length === 0) return [];
|
|
7257
|
+
const scores = /* @__PURE__ */ new Map();
|
|
7258
|
+
const N = this.docCount;
|
|
7259
|
+
const avgdl = this.avgdl;
|
|
7260
|
+
for (const term of queryTerms) {
|
|
7261
|
+
const postings = this.invertedIndex.get(term);
|
|
7262
|
+
if (!postings) continue;
|
|
7263
|
+
const df = postings.length;
|
|
7264
|
+
if (N > 100 && df / N > 0.6) continue;
|
|
7265
|
+
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
7266
|
+
for (const { nodeId, tf } of postings) {
|
|
7267
|
+
const dl = this.docLengths.get(nodeId) ?? avgdl;
|
|
7268
|
+
const score = idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (dl / avgdl)));
|
|
7269
|
+
scores.set(nodeId, (scores.get(nodeId) ?? 0) + score);
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
if (scores.size === 0) return [];
|
|
7273
|
+
const topEntries = heapTopK(scores, limit);
|
|
7274
|
+
return topEntries.map(([nodeId, score]) => {
|
|
7275
|
+
const meta = this.nodeMeta.get(nodeId);
|
|
7276
|
+
return {
|
|
7277
|
+
nodeId,
|
|
7278
|
+
name: meta?.name ?? nodeId,
|
|
7279
|
+
kind: meta?.kind ?? "unknown",
|
|
7280
|
+
filePath: meta?.filePath ?? "",
|
|
7281
|
+
score,
|
|
7282
|
+
snippet: meta?.snippet
|
|
7283
|
+
};
|
|
7284
|
+
});
|
|
6873
7285
|
}
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
)
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
7286
|
+
// ── Incremental update ──────────────────────────────────────────────────────
|
|
7287
|
+
/**
|
|
7288
|
+
* Incrementally update index for a set of changed/added nodes.
|
|
7289
|
+
* Only terms that overlap with the changed nodes are rewritten.
|
|
7290
|
+
* Works even if `load()` was not called (reads affected terms directly from DB).
|
|
7291
|
+
*/
|
|
7292
|
+
updateNodes(nodes) {
|
|
7293
|
+
if (!fs26.existsSync(this.dbPath)) return;
|
|
7294
|
+
if (nodes.length === 0) return;
|
|
7295
|
+
const changedIds = new Set(nodes.map((n) => n.id));
|
|
7296
|
+
const newTermFreqs = /* @__PURE__ */ new Map();
|
|
7297
|
+
for (const node of nodes) {
|
|
7298
|
+
if (["directory", "cluster", "flow"].includes(node.kind)) continue;
|
|
7299
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7300
|
+
const tf = /* @__PURE__ */ new Map();
|
|
7301
|
+
for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
7302
|
+
newTermFreqs.set(node.id, tf);
|
|
7303
|
+
}
|
|
7304
|
+
const newTermSet = new Set([...newTermFreqs.values()].flatMap((m) => [...m.keys()]));
|
|
7305
|
+
const db = new Database2(this.dbPath);
|
|
7306
|
+
db.pragma("journal_mode = WAL");
|
|
7307
|
+
const termsToRewrite = /* @__PURE__ */ new Map();
|
|
7308
|
+
for (const term of newTermSet) {
|
|
7309
|
+
const row = db.prepare("SELECT postings FROM bm25_index WHERE term = ?").get(term);
|
|
7310
|
+
const existing = row ? JSON.parse(row.postings) : [];
|
|
7311
|
+
termsToRewrite.set(term, existing.filter((p) => !changedIds.has(p.nodeId)));
|
|
7312
|
+
}
|
|
7313
|
+
for (const nodeId of changedIds) {
|
|
7314
|
+
const rows = db.prepare("SELECT term, postings FROM bm25_index WHERE postings LIKE ?").all(`%${nodeId}%`);
|
|
7315
|
+
for (const row of rows) {
|
|
7316
|
+
if (termsToRewrite.has(row.term)) continue;
|
|
7317
|
+
const postings = JSON.parse(row.postings);
|
|
7318
|
+
if (postings.some((p) => changedIds.has(p.nodeId))) {
|
|
7319
|
+
termsToRewrite.set(row.term, postings.filter((p) => !changedIds.has(p.nodeId)));
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
for (const [nodeId, tf] of newTermFreqs) {
|
|
7324
|
+
for (const [term, count] of tf) {
|
|
7325
|
+
const postings = termsToRewrite.get(term) ?? [];
|
|
7326
|
+
postings.push({ nodeId, tf: count });
|
|
7327
|
+
termsToRewrite.set(term, postings);
|
|
7328
|
+
}
|
|
7329
|
+
}
|
|
7330
|
+
const upsertPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
|
|
7331
|
+
const upsertDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
|
|
7332
|
+
const upsertNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
|
|
7333
|
+
db.transaction(() => {
|
|
7334
|
+
for (const [term, postings] of termsToRewrite) {
|
|
7335
|
+
if (postings.length === 0) {
|
|
7336
|
+
db.prepare("DELETE FROM bm25_index WHERE term = ?").run(term);
|
|
7337
|
+
} else {
|
|
7338
|
+
upsertPosting.run(term, JSON.stringify(postings));
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
for (const node of nodes) {
|
|
7342
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7343
|
+
upsertDoclen.run(node.id, terms.length);
|
|
7344
|
+
upsertNodeMeta.run(node.id, node.name, node.kind, node.filePath, node.content?.slice(0, 200) ?? null);
|
|
7345
|
+
}
|
|
7346
|
+
})();
|
|
7347
|
+
db.close();
|
|
7348
|
+
if (this._loaded) {
|
|
7349
|
+
for (const [term, postings] of termsToRewrite) {
|
|
7350
|
+
if (postings.length === 0) this.invertedIndex.delete(term);
|
|
7351
|
+
else this.invertedIndex.set(term, postings);
|
|
7352
|
+
}
|
|
7353
|
+
for (const node of nodes) {
|
|
7354
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7355
|
+
this.docLengths.set(node.id, terms.length);
|
|
7356
|
+
this.nodeMeta.set(node.id, {
|
|
7357
|
+
name: node.name,
|
|
7358
|
+
kind: node.kind,
|
|
7359
|
+
filePath: node.filePath,
|
|
7360
|
+
snippet: node.content?.slice(0, 200)
|
|
7361
|
+
});
|
|
7362
|
+
}
|
|
6919
7363
|
}
|
|
6920
7364
|
}
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
weight DOUBLE,
|
|
6925
|
-
label STRING
|
|
6926
|
-
)`];
|
|
7365
|
+
};
|
|
7366
|
+
function getBm25DbPath(workspaceRoot) {
|
|
7367
|
+
return path32.join(workspaceRoot, ".code-intel", "bm25.db");
|
|
6927
7368
|
}
|
|
7369
|
+
|
|
7370
|
+
// src/storage/index.ts
|
|
7371
|
+
init_db_manager();
|
|
7372
|
+
init_schema();
|
|
7373
|
+
|
|
7374
|
+
// src/storage/csv-writer.ts
|
|
7375
|
+
init_schema();
|
|
6928
7376
|
function writeNodeCSVs(graph, outputDir) {
|
|
6929
|
-
|
|
7377
|
+
fs26.mkdirSync(outputDir, { recursive: true });
|
|
6930
7378
|
const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
|
|
6931
7379
|
const tableBuffers = /* @__PURE__ */ new Map();
|
|
6932
7380
|
const tableFilePaths = /* @__PURE__ */ new Map();
|
|
@@ -6934,7 +7382,7 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6934
7382
|
const table = NODE_TABLE_MAP[node.kind];
|
|
6935
7383
|
if (!tableBuffers.has(table)) {
|
|
6936
7384
|
tableBuffers.set(table, [header]);
|
|
6937
|
-
tableFilePaths.set(table,
|
|
7385
|
+
tableFilePaths.set(table, path32.join(outputDir, `${table}.csv`));
|
|
6938
7386
|
}
|
|
6939
7387
|
tableBuffers.get(table).push(
|
|
6940
7388
|
csvRow([
|
|
@@ -6954,12 +7402,12 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6954
7402
|
);
|
|
6955
7403
|
}
|
|
6956
7404
|
for (const [table, lines] of tableBuffers) {
|
|
6957
|
-
|
|
7405
|
+
fs26.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
|
|
6958
7406
|
}
|
|
6959
7407
|
return tableFilePaths;
|
|
6960
7408
|
}
|
|
6961
7409
|
function writeEdgeCSV(graph, outputDir) {
|
|
6962
|
-
|
|
7410
|
+
fs26.mkdirSync(outputDir, { recursive: true });
|
|
6963
7411
|
const header = "from_id,to_id,kind,weight,label\n";
|
|
6964
7412
|
const groups = /* @__PURE__ */ new Map();
|
|
6965
7413
|
for (const edge of graph.allEdges()) {
|
|
@@ -6970,7 +7418,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6970
7418
|
const toTable = NODE_TABLE_MAP[targetNode.kind];
|
|
6971
7419
|
const key = `${fromTable}->${toTable}`;
|
|
6972
7420
|
if (!groups.has(key)) {
|
|
6973
|
-
const filePath =
|
|
7421
|
+
const filePath = path32.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
|
|
6974
7422
|
groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
|
|
6975
7423
|
}
|
|
6976
7424
|
groups.get(key).lines.push(
|
|
@@ -6985,7 +7433,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6985
7433
|
}
|
|
6986
7434
|
const result = [];
|
|
6987
7435
|
for (const group of groups.values()) {
|
|
6988
|
-
|
|
7436
|
+
fs26.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
|
|
6989
7437
|
result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
|
|
6990
7438
|
}
|
|
6991
7439
|
return result;
|
|
@@ -7002,6 +7450,9 @@ function escapeNewlines(s) {
|
|
|
7002
7450
|
if (!s.includes("\n") && !s.includes("\r")) return s;
|
|
7003
7451
|
return s.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
7004
7452
|
}
|
|
7453
|
+
|
|
7454
|
+
// src/storage/graph-loader.ts
|
|
7455
|
+
init_schema();
|
|
7005
7456
|
async function loadGraphToDB(graph, dbManager) {
|
|
7006
7457
|
for (const table of ALL_NODE_TABLES) {
|
|
7007
7458
|
await dbManager.execute(getCreateNodeTableDDL(table));
|
|
@@ -7013,7 +7464,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7013
7464
|
} catch {
|
|
7014
7465
|
}
|
|
7015
7466
|
}
|
|
7016
|
-
const tmpDir =
|
|
7467
|
+
const tmpDir = fs26.mkdtempSync(path32.join(os13.tmpdir(), "code-intel-csv-"));
|
|
7017
7468
|
try {
|
|
7018
7469
|
const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
|
|
7019
7470
|
const edgeGroups = writeEdgeCSV(graph, tmpDir);
|
|
@@ -7032,8 +7483,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7032
7483
|
}
|
|
7033
7484
|
let nodeCount = 0;
|
|
7034
7485
|
for (const [table, csvPath] of nodeTableFiles) {
|
|
7035
|
-
if (!
|
|
7036
|
-
const stat =
|
|
7486
|
+
if (!fs26.existsSync(csvPath)) continue;
|
|
7487
|
+
const stat = fs26.statSync(csvPath);
|
|
7037
7488
|
if (stat.size < 50) continue;
|
|
7038
7489
|
try {
|
|
7039
7490
|
await dbManager.execute(
|
|
@@ -7046,8 +7497,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7046
7497
|
}
|
|
7047
7498
|
let edgeCount = 0;
|
|
7048
7499
|
for (const group of edgeGroups) {
|
|
7049
|
-
if (!
|
|
7050
|
-
const stat =
|
|
7500
|
+
if (!fs26.existsSync(group.filePath)) continue;
|
|
7501
|
+
const stat = fs26.statSync(group.filePath);
|
|
7051
7502
|
if (stat.size < 50) continue;
|
|
7052
7503
|
try {
|
|
7053
7504
|
await dbManager.execute(
|
|
@@ -7061,7 +7512,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7061
7512
|
return { nodeCount, edgeCount };
|
|
7062
7513
|
} finally {
|
|
7063
7514
|
try {
|
|
7064
|
-
|
|
7515
|
+
fs26.rmSync(tmpDir, { recursive: true, force: true });
|
|
7065
7516
|
} catch {
|
|
7066
7517
|
}
|
|
7067
7518
|
}
|
|
@@ -7113,19 +7564,19 @@ function buildNodeProps(node) {
|
|
|
7113
7564
|
function escCypher(s) {
|
|
7114
7565
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
|
|
7115
7566
|
}
|
|
7116
|
-
var GLOBAL_DIR =
|
|
7117
|
-
var REPOS_FILE =
|
|
7567
|
+
var GLOBAL_DIR = path32.join(os13.homedir(), ".code-intel");
|
|
7568
|
+
var REPOS_FILE = path32.join(GLOBAL_DIR, "repos.json");
|
|
7118
7569
|
function loadRegistry() {
|
|
7119
7570
|
try {
|
|
7120
|
-
const data =
|
|
7571
|
+
const data = fs26.readFileSync(REPOS_FILE, "utf-8");
|
|
7121
7572
|
return JSON.parse(data);
|
|
7122
7573
|
} catch {
|
|
7123
7574
|
return [];
|
|
7124
7575
|
}
|
|
7125
7576
|
}
|
|
7126
7577
|
function saveRegistry(entries) {
|
|
7127
|
-
|
|
7128
|
-
|
|
7578
|
+
fs26.mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
7579
|
+
fs26.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
|
|
7129
7580
|
}
|
|
7130
7581
|
function upsertRepo(entry) {
|
|
7131
7582
|
const entries = loadRegistry();
|
|
@@ -7142,99 +7593,30 @@ function removeRepo(repoPath) {
|
|
|
7142
7593
|
saveRegistry(entries);
|
|
7143
7594
|
}
|
|
7144
7595
|
function saveMetadata(repoDir, metadata) {
|
|
7145
|
-
const metaDir =
|
|
7146
|
-
|
|
7147
|
-
|
|
7596
|
+
const metaDir = path32.join(repoDir, ".code-intel");
|
|
7597
|
+
fs26.mkdirSync(metaDir, { recursive: true });
|
|
7598
|
+
fs26.writeFileSync(path32.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
|
|
7148
7599
|
}
|
|
7149
7600
|
function loadMetadata(repoDir) {
|
|
7150
7601
|
try {
|
|
7151
|
-
const data =
|
|
7602
|
+
const data = fs26.readFileSync(path32.join(repoDir, ".code-intel", "meta.json"), "utf-8");
|
|
7152
7603
|
return JSON.parse(data);
|
|
7153
7604
|
} catch {
|
|
7154
7605
|
return null;
|
|
7155
7606
|
}
|
|
7156
7607
|
}
|
|
7157
7608
|
function getDbPath(repoDir) {
|
|
7158
|
-
return
|
|
7609
|
+
return path32.join(repoDir, ".code-intel", "graph.db");
|
|
7159
7610
|
}
|
|
7160
7611
|
function getVectorDbPath(repoDir) {
|
|
7161
|
-
return
|
|
7612
|
+
return path32.join(repoDir, ".code-intel", "vector.db");
|
|
7162
7613
|
}
|
|
7163
7614
|
|
|
7164
7615
|
// src/mcp-server/server.ts
|
|
7165
7616
|
init_group_registry();
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
7170
|
-
);
|
|
7171
|
-
function parseRow(row, kind) {
|
|
7172
|
-
return {
|
|
7173
|
-
id: String(row["id"] ?? ""),
|
|
7174
|
-
kind,
|
|
7175
|
-
name: String(row["name"] ?? ""),
|
|
7176
|
-
filePath: String(row["file_path"] ?? ""),
|
|
7177
|
-
startLine: row["start_line"] != null ? Number(row["start_line"]) : void 0,
|
|
7178
|
-
endLine: row["end_line"] != null ? Number(row["end_line"]) : void 0,
|
|
7179
|
-
exported: row["exported"] != null ? Boolean(row["exported"]) : void 0,
|
|
7180
|
-
content: row["content"] ? String(row["content"]) : void 0,
|
|
7181
|
-
metadata: row["metadata"] ? (() => {
|
|
7182
|
-
try {
|
|
7183
|
-
return JSON.parse(String(row["metadata"]));
|
|
7184
|
-
} catch {
|
|
7185
|
-
return void 0;
|
|
7186
|
-
}
|
|
7187
|
-
})() : void 0
|
|
7188
|
-
};
|
|
7189
|
-
}
|
|
7190
|
-
async function loadGraphFromDB(graph, db) {
|
|
7191
|
-
for (const table of ALL_NODE_TABLES) {
|
|
7192
|
-
const kind = TABLE_TO_KIND[table];
|
|
7193
|
-
if (!kind) continue;
|
|
7194
|
-
let rows = [];
|
|
7195
|
-
try {
|
|
7196
|
-
rows = await db.query(`MATCH (n:${table}) RETURN n.id, n.name, n.file_path, n.start_line, n.end_line, n.exported, n.content, n.metadata`);
|
|
7197
|
-
} catch {
|
|
7198
|
-
continue;
|
|
7199
|
-
}
|
|
7200
|
-
for (const row of rows) {
|
|
7201
|
-
const node = parseRow({
|
|
7202
|
-
id: row["n.id"],
|
|
7203
|
-
name: row["n.name"],
|
|
7204
|
-
file_path: row["n.file_path"],
|
|
7205
|
-
start_line: row["n.start_line"],
|
|
7206
|
-
end_line: row["n.end_line"],
|
|
7207
|
-
exported: row["n.exported"],
|
|
7208
|
-
content: row["n.content"],
|
|
7209
|
-
metadata: row["n.metadata"]
|
|
7210
|
-
}, kind);
|
|
7211
|
-
if (node.id && node.name) graph.addNode(node);
|
|
7212
|
-
}
|
|
7213
|
-
}
|
|
7214
|
-
try {
|
|
7215
|
-
const edgeRows = await db.query(
|
|
7216
|
-
`MATCH (a)-[e:code_edges]->(b) RETURN a.id, b.id, e.kind, e.weight, e.label`
|
|
7217
|
-
);
|
|
7218
|
-
for (const row of edgeRows) {
|
|
7219
|
-
const sourceId = String(row["a.id"] ?? "");
|
|
7220
|
-
const targetId = String(row["b.id"] ?? "");
|
|
7221
|
-
const kind = String(row["e.kind"] ?? "");
|
|
7222
|
-
if (!sourceId || !targetId || !kind) continue;
|
|
7223
|
-
const edge = {
|
|
7224
|
-
id: `${sourceId}::${kind}::${targetId}`,
|
|
7225
|
-
source: sourceId,
|
|
7226
|
-
target: targetId,
|
|
7227
|
-
kind,
|
|
7228
|
-
weight: row["e.weight"] != null ? Number(row["e.weight"]) : void 0,
|
|
7229
|
-
label: row["e.label"] ? String(row["e.label"]) : void 0
|
|
7230
|
-
};
|
|
7231
|
-
graph.addEdge(edge);
|
|
7232
|
-
}
|
|
7233
|
-
} catch {
|
|
7234
|
-
}
|
|
7235
|
-
}
|
|
7236
|
-
|
|
7237
|
-
// src/multi-repo/group-sync.ts
|
|
7617
|
+
init_db_manager();
|
|
7618
|
+
init_knowledge_graph();
|
|
7619
|
+
init_graph_from_db();
|
|
7238
7620
|
init_logger();
|
|
7239
7621
|
function scanForFiles(root, matcher, maxDepth = 2) {
|
|
7240
7622
|
const results = [];
|
|
@@ -7242,12 +7624,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
|
|
|
7242
7624
|
if (depth > maxDepth) return;
|
|
7243
7625
|
let entries;
|
|
7244
7626
|
try {
|
|
7245
|
-
entries =
|
|
7627
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
7246
7628
|
} catch {
|
|
7247
7629
|
return;
|
|
7248
7630
|
}
|
|
7249
7631
|
for (const entry of entries) {
|
|
7250
|
-
const full =
|
|
7632
|
+
const full = path32.join(dir, entry.name);
|
|
7251
7633
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
7252
7634
|
walk(full, depth + 1);
|
|
7253
7635
|
} else if (entry.isFile() && matcher(entry.name)) {
|
|
@@ -7270,8 +7652,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
7270
7652
|
]);
|
|
7271
7653
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
7272
7654
|
function tryParseFile(filePath) {
|
|
7273
|
-
const ext =
|
|
7274
|
-
const content =
|
|
7655
|
+
const ext = path32.extname(filePath).toLowerCase();
|
|
7656
|
+
const content = fs26.readFileSync(filePath, "utf-8");
|
|
7275
7657
|
if (ext === ".json") {
|
|
7276
7658
|
try {
|
|
7277
7659
|
return JSON.parse(content);
|
|
@@ -7325,7 +7707,7 @@ async function parseGraphQLContracts(repoRoot) {
|
|
|
7325
7707
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
|
|
7326
7708
|
const contracts = [];
|
|
7327
7709
|
for (const filePath of files) {
|
|
7328
|
-
const content =
|
|
7710
|
+
const content = fs26.readFileSync(filePath, "utf-8");
|
|
7329
7711
|
const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7330
7712
|
let match;
|
|
7331
7713
|
while ((match = typeRegex.exec(content)) !== null) {
|
|
@@ -7356,7 +7738,7 @@ async function parseProtoContracts(repoRoot) {
|
|
|
7356
7738
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
|
|
7357
7739
|
const contracts = [];
|
|
7358
7740
|
for (const filePath of files) {
|
|
7359
|
-
const content =
|
|
7741
|
+
const content = fs26.readFileSync(filePath, "utf-8");
|
|
7360
7742
|
const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7361
7743
|
let serviceMatch;
|
|
7362
7744
|
while ((serviceMatch = serviceRegex.exec(content)) !== null) {
|
|
@@ -7561,13 +7943,13 @@ async function syncGroup(group) {
|
|
|
7561
7943
|
logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
|
|
7562
7944
|
continue;
|
|
7563
7945
|
}
|
|
7564
|
-
const dbPath =
|
|
7565
|
-
if (!
|
|
7946
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
7947
|
+
if (!fs26.existsSync(dbPath)) {
|
|
7566
7948
|
logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
|
|
7567
7949
|
continue;
|
|
7568
7950
|
}
|
|
7569
7951
|
const graph = createKnowledgeGraph();
|
|
7570
|
-
const db = new DbManager(dbPath);
|
|
7952
|
+
const db = new DbManager(dbPath, true);
|
|
7571
7953
|
try {
|
|
7572
7954
|
await db.init();
|
|
7573
7955
|
await loadGraphFromDB(graph, db);
|
|
@@ -7628,6 +8010,9 @@ async function syncGroup(group) {
|
|
|
7628
8010
|
links
|
|
7629
8011
|
};
|
|
7630
8012
|
}
|
|
8013
|
+
init_db_manager();
|
|
8014
|
+
init_knowledge_graph();
|
|
8015
|
+
init_graph_from_db();
|
|
7631
8016
|
async function queryGroup(group, query, limit = 20) {
|
|
7632
8017
|
const registry = loadRegistry();
|
|
7633
8018
|
const perRepo = [];
|
|
@@ -7635,10 +8020,10 @@ async function queryGroup(group, query, limit = 20) {
|
|
|
7635
8020
|
for (const member of group.members) {
|
|
7636
8021
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
7637
8022
|
if (!regEntry) continue;
|
|
7638
|
-
const dbPath =
|
|
7639
|
-
if (!
|
|
8023
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
8024
|
+
if (!fs26.existsSync(dbPath)) continue;
|
|
7640
8025
|
const graph = createKnowledgeGraph();
|
|
7641
|
-
const db = new DbManager(dbPath);
|
|
8026
|
+
const db = new DbManager(dbPath, true);
|
|
7642
8027
|
try {
|
|
7643
8028
|
await db.init();
|
|
7644
8029
|
await loadGraphFromDB(graph, db);
|
|
@@ -8136,22 +8521,22 @@ function suggestTests(graph, symbolName) {
|
|
|
8136
8521
|
const callPaths = [];
|
|
8137
8522
|
const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
|
|
8138
8523
|
while (pathQueue.length > 0 && callPaths.length < 5) {
|
|
8139
|
-
const { id, path:
|
|
8524
|
+
const { id, path: path33, depth } = pathQueue.shift();
|
|
8140
8525
|
let hasCallers2 = false;
|
|
8141
8526
|
for (const edge of graph.findEdgesTo(id)) {
|
|
8142
8527
|
if (edge.kind !== "calls") continue;
|
|
8143
8528
|
const callerNode = graph.getNode(edge.source);
|
|
8144
8529
|
if (!callerNode) continue;
|
|
8145
8530
|
hasCallers2 = true;
|
|
8146
|
-
const newPath = [callerNode.name, ...
|
|
8531
|
+
const newPath = [callerNode.name, ...path33];
|
|
8147
8532
|
if (depth + 1 >= 3 || callPaths.length >= 5) {
|
|
8148
8533
|
if (callPaths.length < 5) callPaths.push(newPath);
|
|
8149
8534
|
continue;
|
|
8150
8535
|
}
|
|
8151
8536
|
pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
|
|
8152
8537
|
}
|
|
8153
|
-
if (!hasCallers2 &&
|
|
8154
|
-
callPaths.push(
|
|
8538
|
+
if (!hasCallers2 && path33.length > 1) {
|
|
8539
|
+
callPaths.push(path33);
|
|
8155
8540
|
}
|
|
8156
8541
|
}
|
|
8157
8542
|
if (callPaths.length === 0) {
|
|
@@ -8284,11 +8669,30 @@ function summarizeCluster(graph, cluster) {
|
|
|
8284
8669
|
}
|
|
8285
8670
|
|
|
8286
8671
|
// src/mcp-server/server.ts
|
|
8672
|
+
function compact(obj) {
|
|
8673
|
+
return JSON.stringify(obj, (_key, value) => value === null || value === void 0 ? void 0 : value);
|
|
8674
|
+
}
|
|
8287
8675
|
function createMcpServer(graph, repoName, workspaceRoot) {
|
|
8288
8676
|
const server = new Server(
|
|
8289
8677
|
{ name: "code-intel", version: "0.1.0" },
|
|
8290
8678
|
{ capabilities: { tools: {}, resources: {} } }
|
|
8291
8679
|
);
|
|
8680
|
+
let bm25Index = null;
|
|
8681
|
+
function ensureBm25Index() {
|
|
8682
|
+
if (bm25Index) return bm25Index;
|
|
8683
|
+
if (!workspaceRoot) return null;
|
|
8684
|
+
try {
|
|
8685
|
+
const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
|
|
8686
|
+
idx.load();
|
|
8687
|
+
bm25Index = idx;
|
|
8688
|
+
return bm25Index;
|
|
8689
|
+
} catch {
|
|
8690
|
+
return null;
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
if (workspaceRoot) {
|
|
8694
|
+
setImmediate(() => ensureBm25Index());
|
|
8695
|
+
}
|
|
8292
8696
|
const _tokenProp = {
|
|
8293
8697
|
_token: { type: "string", description: "Required if CODE_INTEL_TOKEN is configured" }
|
|
8294
8698
|
};
|
|
@@ -8308,13 +8712,15 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8308
8712
|
// ── Search & inspect ─────────────────────────────────────────────────
|
|
8309
8713
|
{
|
|
8310
8714
|
name: "search",
|
|
8311
|
-
description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc.",
|
|
8715
|
+
description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc. Optionally scope to a specific repo or group.",
|
|
8312
8716
|
inputSchema: {
|
|
8313
8717
|
type: "object",
|
|
8314
8718
|
properties: {
|
|
8315
8719
|
query: { type: "string", description: "Search query (symbol name, keyword, or partial match)" },
|
|
8316
8720
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
8317
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
8721
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
8722
|
+
repo: { type: "string", description: "Scope search to a specific indexed repo name (optional; defaults to current repo)" },
|
|
8723
|
+
group: { type: "string", description: "Scope search across all repos in a group via cross-repo RRF merge (optional; overrides repo)" },
|
|
8318
8724
|
..._tokenProp
|
|
8319
8725
|
},
|
|
8320
8726
|
required: ["query"]
|
|
@@ -8344,7 +8750,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8344
8750
|
enum: ["callers", "callees", "both"],
|
|
8345
8751
|
description: "Which direction to trace \u2014 callers (who depends on it), callees (what it depends on), or both (default: both)"
|
|
8346
8752
|
},
|
|
8347
|
-
max_hops: { type: "number", description: "Maximum traversal depth (default:
|
|
8753
|
+
max_hops: { type: "number", description: "Maximum traversal depth (default: 2, max: 10)" },
|
|
8348
8754
|
..._tokenProp
|
|
8349
8755
|
},
|
|
8350
8756
|
required: ["target"]
|
|
@@ -8358,7 +8764,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8358
8764
|
properties: {
|
|
8359
8765
|
file_path: { type: "string", description: 'File path (partial match is supported, e.g. "auth/login.ts")' },
|
|
8360
8766
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
8361
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
8767
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
8362
8768
|
..._tokenProp
|
|
8363
8769
|
},
|
|
8364
8770
|
required: ["file_path"]
|
|
@@ -8389,7 +8795,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8389
8795
|
description: "Filter by node kind: function | class | interface | method | type_alias | constant | enum (optional)"
|
|
8390
8796
|
},
|
|
8391
8797
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
8392
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
8798
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
8393
8799
|
..._tokenProp
|
|
8394
8800
|
}
|
|
8395
8801
|
}
|
|
@@ -8407,7 +8813,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8407
8813
|
type: "object",
|
|
8408
8814
|
properties: {
|
|
8409
8815
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
8410
|
-
limit: { type: "number", description: "Max clusters per page (default:
|
|
8816
|
+
limit: { type: "number", description: "Max clusters per page (default: 10, max: 500)" },
|
|
8411
8817
|
..._tokenProp
|
|
8412
8818
|
}
|
|
8413
8819
|
}
|
|
@@ -8419,7 +8825,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8419
8825
|
type: "object",
|
|
8420
8826
|
properties: {
|
|
8421
8827
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
8422
|
-
limit: { type: "number", description: "Max flows per page (default:
|
|
8828
|
+
limit: { type: "number", description: "Max flows per page (default: 10, max: 500)" },
|
|
8423
8829
|
..._tokenProp
|
|
8424
8830
|
}
|
|
8425
8831
|
}
|
|
@@ -8573,7 +8979,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8573
8979
|
},
|
|
8574
8980
|
maxHops: {
|
|
8575
8981
|
type: "number",
|
|
8576
|
-
description: "Maximum BFS depth for blast radius (default:
|
|
8982
|
+
description: "Maximum BFS depth for blast radius (default: 2, max: 10)"
|
|
8577
8983
|
},
|
|
8578
8984
|
..._tokenProp
|
|
8579
8985
|
}
|
|
@@ -8701,31 +9107,51 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8701
9107
|
const providedToken = a._token;
|
|
8702
9108
|
if (providedToken !== expectedToken) {
|
|
8703
9109
|
return {
|
|
8704
|
-
content: [{ type: "text", text:
|
|
9110
|
+
content: [{ type: "text", text: compact({ error: "Unauthorized: invalid or missing CODE_INTEL_TOKEN" }) }],
|
|
8705
9111
|
isError: true
|
|
8706
9112
|
};
|
|
8707
9113
|
}
|
|
8708
9114
|
}
|
|
8709
9115
|
const startMs = Date.now();
|
|
8710
|
-
const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot);
|
|
9116
|
+
const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot, ensureBm25Index);
|
|
9117
|
+
const MCP_TIMEOUT_MS = parseInt(process.env["CODE_INTEL_MCP_TIMEOUT_MS"] ?? "30000", 10);
|
|
9118
|
+
let timeoutHandle = null;
|
|
9119
|
+
let timedOut = false;
|
|
9120
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
9121
|
+
timeoutHandle = setTimeout(() => {
|
|
9122
|
+
timedOut = true;
|
|
9123
|
+
reject(new Error(`MCP tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`));
|
|
9124
|
+
}, MCP_TIMEOUT_MS);
|
|
9125
|
+
});
|
|
8711
9126
|
let result;
|
|
8712
9127
|
let status = "success";
|
|
8713
9128
|
try {
|
|
8714
9129
|
if (isTracingEnabled()) {
|
|
8715
|
-
result = await
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
9130
|
+
result = await Promise.race([
|
|
9131
|
+
withSpan(
|
|
9132
|
+
`mcp.tool.${name}`,
|
|
9133
|
+
sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
|
|
9134
|
+
dispatch
|
|
9135
|
+
),
|
|
9136
|
+
timeoutPromise
|
|
9137
|
+
]);
|
|
8720
9138
|
} else {
|
|
8721
|
-
result = await dispatch();
|
|
9139
|
+
result = await Promise.race([dispatch(), timeoutPromise]);
|
|
8722
9140
|
}
|
|
8723
9141
|
if (result.isError) status = "error";
|
|
8724
9142
|
} catch (err) {
|
|
8725
9143
|
status = "error";
|
|
8726
9144
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8727
9145
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
9146
|
+
if (timedOut) {
|
|
9147
|
+
return {
|
|
9148
|
+
content: [{ type: "text", text: compact({ truncated: true, reason: `Tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`, partialResults: [] }) }],
|
|
9149
|
+
isError: false
|
|
9150
|
+
};
|
|
9151
|
+
}
|
|
8728
9152
|
throw err;
|
|
9153
|
+
} finally {
|
|
9154
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
8729
9155
|
}
|
|
8730
9156
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8731
9157
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
@@ -8734,7 +9160,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8734
9160
|
registerResources(server, graph, repoName);
|
|
8735
9161
|
return server;
|
|
8736
9162
|
}
|
|
8737
|
-
async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
9163
|
+
async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolver) {
|
|
8738
9164
|
switch (name) {
|
|
8739
9165
|
// ── repos ──────────────────────────────────────────────────────────────
|
|
8740
9166
|
case "repos": {
|
|
@@ -8742,10 +9168,8 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8742
9168
|
return {
|
|
8743
9169
|
content: [{
|
|
8744
9170
|
type: "text",
|
|
8745
|
-
text:
|
|
8746
|
-
registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats }))
|
|
8747
|
-
null,
|
|
8748
|
-
2
|
|
9171
|
+
text: compact(
|
|
9172
|
+
registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats }))
|
|
8749
9173
|
)
|
|
8750
9174
|
}]
|
|
8751
9175
|
};
|
|
@@ -8773,13 +9197,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8773
9197
|
return {
|
|
8774
9198
|
content: [{
|
|
8775
9199
|
type: "text",
|
|
8776
|
-
text:
|
|
9200
|
+
text: compact({
|
|
8777
9201
|
repo: repoName,
|
|
8778
9202
|
stats: graph.size,
|
|
8779
9203
|
nodeCounts: kindCounts,
|
|
8780
9204
|
edgeCounts,
|
|
8781
9205
|
health
|
|
8782
|
-
}
|
|
9206
|
+
})
|
|
8783
9207
|
}]
|
|
8784
9208
|
};
|
|
8785
9209
|
}
|
|
@@ -8787,15 +9211,59 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8787
9211
|
case "search": {
|
|
8788
9212
|
const query = a.query;
|
|
8789
9213
|
const offset = a.offset ?? 0;
|
|
8790
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
9214
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
9215
|
+
if (a.group) {
|
|
9216
|
+
const grp = loadGroup(a.group);
|
|
9217
|
+
if (!grp) {
|
|
9218
|
+
return { content: [{ type: "text", text: `Group "${a.group}" not found. Use list_groups to see available groups.` }] };
|
|
9219
|
+
}
|
|
9220
|
+
const { perRepo, merged } = await queryGroup(grp, query, effectiveLimit + offset);
|
|
9221
|
+
const paged = merged.slice(offset, offset + effectiveLimit);
|
|
9222
|
+
return {
|
|
9223
|
+
content: [{
|
|
9224
|
+
type: "text",
|
|
9225
|
+
text: compact({
|
|
9226
|
+
results: paged,
|
|
9227
|
+
perRepo,
|
|
9228
|
+
searchMode: "bm25-cross-repo",
|
|
9229
|
+
group: a.group,
|
|
9230
|
+
total: merged.length,
|
|
9231
|
+
offset,
|
|
9232
|
+
limit: effectiveLimit,
|
|
9233
|
+
hasMore: offset + effectiveLimit < merged.length
|
|
9234
|
+
})
|
|
9235
|
+
}]
|
|
9236
|
+
};
|
|
9237
|
+
}
|
|
9238
|
+
const repoGraph = a.repo ? await (async () => {
|
|
9239
|
+
const registry = loadRegistry();
|
|
9240
|
+
const entry = registry.find((r) => r.name === a.repo || r.path === a.repo);
|
|
9241
|
+
if (!entry) return graph;
|
|
9242
|
+
const { DbManager: DbMgr } = await Promise.resolve().then(() => (init_db_manager(), db_manager_exports));
|
|
9243
|
+
const { loadGraphFromDB: loadG } = await Promise.resolve().then(() => (init_graph_from_db(), graph_from_db_exports));
|
|
9244
|
+
const { createKnowledgeGraph: createG } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
9245
|
+
const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
|
|
9246
|
+
if (!fs26.existsSync(dbPath)) return graph;
|
|
9247
|
+
const db = new DbMgr(dbPath, true);
|
|
9248
|
+
await db.init();
|
|
9249
|
+
const g = createG();
|
|
9250
|
+
await loadG(g, db);
|
|
9251
|
+
db.close();
|
|
9252
|
+
return g;
|
|
9253
|
+
})() : graph;
|
|
8791
9254
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
8792
9255
|
const fetchLimit = Math.min(offset + effectiveLimit, 500);
|
|
8793
|
-
const
|
|
9256
|
+
const bm25 = !a.repo || a.repo === repoName ? bm25Resolver ? bm25Resolver() : null : null;
|
|
9257
|
+
const bm25Results = bm25 ? bm25.search(query, fetchLimit * 3) : void 0;
|
|
9258
|
+
const { results: allResults, searchMode } = await hybridSearch(repoGraph, query, fetchLimit, {
|
|
9259
|
+
vectorDbPath: vdbPath,
|
|
9260
|
+
bm25Results: bm25Results ?? void 0
|
|
9261
|
+
});
|
|
8794
9262
|
const total = allResults.length;
|
|
8795
9263
|
const results = allResults.slice(offset, offset + effectiveLimit);
|
|
8796
9264
|
const hasMore = offset + effectiveLimit < total;
|
|
8797
9265
|
const suggestNextTools = [];
|
|
8798
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
9266
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
8799
9267
|
if (suggestEnabled && results.length > 0) {
|
|
8800
9268
|
const topName = results[0].name;
|
|
8801
9269
|
suggestNextTools.push(
|
|
@@ -8806,15 +9274,16 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8806
9274
|
return {
|
|
8807
9275
|
content: [{
|
|
8808
9276
|
type: "text",
|
|
8809
|
-
text:
|
|
9277
|
+
text: compact({
|
|
8810
9278
|
results,
|
|
8811
9279
|
searchMode,
|
|
9280
|
+
repo: a.repo ?? repoName,
|
|
8812
9281
|
total,
|
|
8813
9282
|
offset,
|
|
8814
9283
|
limit: effectiveLimit,
|
|
8815
9284
|
hasMore,
|
|
8816
9285
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
8817
|
-
}
|
|
9286
|
+
})
|
|
8818
9287
|
}]
|
|
8819
9288
|
};
|
|
8820
9289
|
}
|
|
@@ -8836,7 +9305,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8836
9305
|
file: graph.getNode(e.target)?.filePath
|
|
8837
9306
|
}));
|
|
8838
9307
|
const cluster = incoming.filter((e) => e.kind === "belongs_to").map((e) => graph.getNode(e.target)?.name)[0];
|
|
8839
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
9308
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
8840
9309
|
const suggestNextTools = [];
|
|
8841
9310
|
if (suggestEnabled) {
|
|
8842
9311
|
const topCallerName = callers[0]?.name;
|
|
@@ -8848,7 +9317,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8848
9317
|
return {
|
|
8849
9318
|
content: [{
|
|
8850
9319
|
type: "text",
|
|
8851
|
-
text:
|
|
9320
|
+
text: compact({
|
|
8852
9321
|
node: {
|
|
8853
9322
|
id: node.id,
|
|
8854
9323
|
kind: node.kind,
|
|
@@ -8871,7 +9340,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8871
9340
|
cluster,
|
|
8872
9341
|
content: node.content?.slice(0, 500),
|
|
8873
9342
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
8874
|
-
}
|
|
9343
|
+
})
|
|
8875
9344
|
}]
|
|
8876
9345
|
};
|
|
8877
9346
|
}
|
|
@@ -8879,7 +9348,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8879
9348
|
case "blast_radius": {
|
|
8880
9349
|
const target = a.target;
|
|
8881
9350
|
const direction = a.direction ?? "both";
|
|
8882
|
-
const maxHops = a.max_hops ??
|
|
9351
|
+
const maxHops = a.max_hops ?? 2;
|
|
8883
9352
|
const node = findNodeByName(graph, target);
|
|
8884
9353
|
if (!node) return { content: [{ type: "text", text: `Symbol "${target}" not found.` }] };
|
|
8885
9354
|
const affected = /* @__PURE__ */ new Set();
|
|
@@ -8906,7 +9375,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8906
9375
|
return n ? { id, name: n.name, kind: n.kind, filePath: n.filePath } : { id };
|
|
8907
9376
|
});
|
|
8908
9377
|
const risk = affected.size > 10 ? "HIGH" : affected.size > 5 ? "MEDIUM" : "LOW";
|
|
8909
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
9378
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
8910
9379
|
const suggestNextTools = [];
|
|
8911
9380
|
if (suggestEnabled) {
|
|
8912
9381
|
const highestRiskSymbol = node.name;
|
|
@@ -8919,13 +9388,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8919
9388
|
return {
|
|
8920
9389
|
content: [{
|
|
8921
9390
|
type: "text",
|
|
8922
|
-
text:
|
|
9391
|
+
text: compact({
|
|
8923
9392
|
target: node.name,
|
|
8924
9393
|
affectedCount: affected.size,
|
|
8925
9394
|
riskLevel: risk,
|
|
8926
9395
|
affected: affectedDetails,
|
|
8927
9396
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
8928
|
-
}
|
|
9397
|
+
})
|
|
8929
9398
|
}]
|
|
8930
9399
|
};
|
|
8931
9400
|
}
|
|
@@ -8933,7 +9402,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8933
9402
|
case "file_symbols": {
|
|
8934
9403
|
const filePath = a.file_path;
|
|
8935
9404
|
const offset = a.offset ?? 0;
|
|
8936
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
9405
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
8937
9406
|
const allMatches = [];
|
|
8938
9407
|
for (const node of graph.allNodes()) {
|
|
8939
9408
|
if (node.filePath && node.filePath.includes(filePath)) {
|
|
@@ -8950,7 +9419,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8950
9419
|
return {
|
|
8951
9420
|
content: [{
|
|
8952
9421
|
type: "text",
|
|
8953
|
-
text:
|
|
9422
|
+
text: compact({ symbols: matches, total, offset, limit: effectiveLimit, hasMore })
|
|
8954
9423
|
}]
|
|
8955
9424
|
};
|
|
8956
9425
|
}
|
|
@@ -8991,7 +9460,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8991
9460
|
return {
|
|
8992
9461
|
content: [{
|
|
8993
9462
|
type: "text",
|
|
8994
|
-
text:
|
|
9463
|
+
text: compact({ from: fromName, to: toName, hops: foundPath.length - 1, path: pathDetails })
|
|
8995
9464
|
}]
|
|
8996
9465
|
};
|
|
8997
9466
|
}
|
|
@@ -8999,7 +9468,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
8999
9468
|
case "list_exports": {
|
|
9000
9469
|
const kindFilter = a.kind;
|
|
9001
9470
|
const offset = a.offset ?? 0;
|
|
9002
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
9471
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
9003
9472
|
const allExports = [];
|
|
9004
9473
|
for (const node of graph.allNodes()) {
|
|
9005
9474
|
if (!node.exported) continue;
|
|
@@ -9012,7 +9481,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9012
9481
|
return {
|
|
9013
9482
|
content: [{
|
|
9014
9483
|
type: "text",
|
|
9015
|
-
text:
|
|
9484
|
+
text: compact({ exports: exports$1, total, offset, limit: effectiveLimit, hasMore })
|
|
9016
9485
|
}]
|
|
9017
9486
|
};
|
|
9018
9487
|
}
|
|
@@ -9024,12 +9493,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9024
9493
|
routes.push({ name: node.name, filePath: node.filePath, startLine: node.startLine });
|
|
9025
9494
|
}
|
|
9026
9495
|
}
|
|
9027
|
-
return { content: [{ type: "text", text:
|
|
9496
|
+
return { content: [{ type: "text", text: compact(routes) }] };
|
|
9028
9497
|
}
|
|
9029
9498
|
// ── clusters ───────────────────────────────────────────────────────────
|
|
9030
9499
|
case "clusters": {
|
|
9031
9500
|
const offset = a.offset ?? 0;
|
|
9032
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
9501
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
9033
9502
|
const allClusters = [];
|
|
9034
9503
|
for (const node of graph.allNodes()) {
|
|
9035
9504
|
if (node.kind === "cluster") {
|
|
@@ -9056,14 +9525,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9056
9525
|
return {
|
|
9057
9526
|
content: [{
|
|
9058
9527
|
type: "text",
|
|
9059
|
-
text:
|
|
9528
|
+
text: compact({ clusters, total, offset, limit: effectiveLimit, hasMore })
|
|
9060
9529
|
}]
|
|
9061
9530
|
};
|
|
9062
9531
|
}
|
|
9063
9532
|
// ── flows ──────────────────────────────────────────────────────────────
|
|
9064
9533
|
case "flows": {
|
|
9065
9534
|
const offset = a.offset ?? 0;
|
|
9066
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
9535
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
9067
9536
|
const allFlows = [];
|
|
9068
9537
|
for (const node of graph.allNodes()) {
|
|
9069
9538
|
if (node.kind === "flow") {
|
|
@@ -9083,7 +9552,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9083
9552
|
return {
|
|
9084
9553
|
content: [{
|
|
9085
9554
|
type: "text",
|
|
9086
|
-
text:
|
|
9555
|
+
text: compact({ flows, total, offset, limit: effectiveLimit, hasMore })
|
|
9087
9556
|
}]
|
|
9088
9557
|
};
|
|
9089
9558
|
}
|
|
@@ -9113,7 +9582,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9113
9582
|
for (const { filePath: changedFile, changedLines } of changedFiles) {
|
|
9114
9583
|
for (const node of graph.allNodes()) {
|
|
9115
9584
|
if (!node.filePath) continue;
|
|
9116
|
-
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot +
|
|
9585
|
+
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path32.sep, "");
|
|
9117
9586
|
const normChanged = changedFile.replace(/^a\/|^b\//, "");
|
|
9118
9587
|
if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
|
|
9119
9588
|
if (node.startLine !== void 0 && node.endLine !== void 0) {
|
|
@@ -9151,14 +9620,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9151
9620
|
return {
|
|
9152
9621
|
content: [{
|
|
9153
9622
|
type: "text",
|
|
9154
|
-
text:
|
|
9623
|
+
text: compact({
|
|
9155
9624
|
baseRef,
|
|
9156
9625
|
changedFiles: changedFiles.map((f) => f.filePath),
|
|
9157
9626
|
directlyChangedSymbols: changedSymbols,
|
|
9158
9627
|
transitivelyAffectedSymbols: affectedSymbols,
|
|
9159
9628
|
totalAffected: allAffected.size,
|
|
9160
9629
|
riskLevel: risk
|
|
9161
|
-
}
|
|
9630
|
+
})
|
|
9162
9631
|
}]
|
|
9163
9632
|
};
|
|
9164
9633
|
}
|
|
@@ -9166,14 +9635,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9166
9635
|
case "query": {
|
|
9167
9636
|
const gqlInput = a.gql;
|
|
9168
9637
|
if (!gqlInput) {
|
|
9169
|
-
return { content: [{ type: "text", text:
|
|
9638
|
+
return { content: [{ type: "text", text: compact({ error: "Missing required parameter: gql" }) }], isError: true };
|
|
9170
9639
|
}
|
|
9171
9640
|
const { parseGQL: parseGQL2, isGQLParseError: isGQLParseError2 } = await Promise.resolve().then(() => (init_gql_parser(), gql_parser_exports));
|
|
9172
9641
|
const { executeGQL: executeGQL2 } = await Promise.resolve().then(() => (init_gql_executor(), gql_executor_exports));
|
|
9173
9642
|
const ast = parseGQL2(gqlInput);
|
|
9174
9643
|
if (isGQLParseError2(ast)) {
|
|
9175
9644
|
return {
|
|
9176
|
-
content: [{ type: "text", text:
|
|
9645
|
+
content: [{ type: "text", text: compact({ error: `GQL parse error: ${ast.message}`, pos: ast.pos, expected: ast.expected, got: ast.got }) }],
|
|
9177
9646
|
isError: true
|
|
9178
9647
|
};
|
|
9179
9648
|
}
|
|
@@ -9184,7 +9653,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9184
9653
|
return {
|
|
9185
9654
|
content: [{
|
|
9186
9655
|
type: "text",
|
|
9187
|
-
text:
|
|
9656
|
+
text: compact({
|
|
9188
9657
|
nodes: result.nodes,
|
|
9189
9658
|
edges: result.edges,
|
|
9190
9659
|
groups: result.groups,
|
|
@@ -9192,7 +9661,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9192
9661
|
executionTimeMs: result.executionTimeMs,
|
|
9193
9662
|
truncated: result.truncated,
|
|
9194
9663
|
totalCount: result.totalCount
|
|
9195
|
-
}
|
|
9664
|
+
})
|
|
9196
9665
|
}]
|
|
9197
9666
|
};
|
|
9198
9667
|
}
|
|
@@ -9206,7 +9675,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9206
9675
|
for (const node of graph.allNodes()) {
|
|
9207
9676
|
if (node.name === nameMatch[1]) results.push(node);
|
|
9208
9677
|
}
|
|
9209
|
-
return { content: [{ type: "text", text:
|
|
9678
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
|
|
9210
9679
|
}
|
|
9211
9680
|
const kindMatch = q?.match(/:\s*(\w+)/);
|
|
9212
9681
|
if (kindMatch) {
|
|
@@ -9215,9 +9684,9 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9215
9684
|
if (node.kind === kindMatch[1]) results.push(node);
|
|
9216
9685
|
if (results.length >= 50) break;
|
|
9217
9686
|
}
|
|
9218
|
-
return { content: [{ type: "text", text:
|
|
9687
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
|
|
9219
9688
|
}
|
|
9220
|
-
return { content: [{ type: "text", text:
|
|
9689
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, error: "Query not recognized. Use name='X' or :kind syntax. Or use the query tool with GQL instead." }) }] };
|
|
9221
9690
|
}
|
|
9222
9691
|
// ── group_list ─────────────────────────────────────────────────────────
|
|
9223
9692
|
case "group_list": {
|
|
@@ -9225,16 +9694,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9225
9694
|
if (groupName) {
|
|
9226
9695
|
const group = loadGroup(groupName);
|
|
9227
9696
|
if (!group) return { content: [{ type: "text", text: `Group "${groupName}" not found.` }] };
|
|
9228
|
-
return { content: [{ type: "text", text:
|
|
9697
|
+
return { content: [{ type: "text", text: compact(group) }] };
|
|
9229
9698
|
}
|
|
9230
9699
|
const groups = listGroups();
|
|
9231
9700
|
return {
|
|
9232
9701
|
content: [{
|
|
9233
9702
|
type: "text",
|
|
9234
|
-
text:
|
|
9235
|
-
groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members }))
|
|
9236
|
-
null,
|
|
9237
|
-
2
|
|
9703
|
+
text: compact(
|
|
9704
|
+
groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members }))
|
|
9238
9705
|
)
|
|
9239
9706
|
}]
|
|
9240
9707
|
};
|
|
@@ -9252,14 +9719,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9252
9719
|
return {
|
|
9253
9720
|
content: [{
|
|
9254
9721
|
type: "text",
|
|
9255
|
-
text:
|
|
9722
|
+
text: compact({
|
|
9256
9723
|
groupName: result.groupName,
|
|
9257
9724
|
syncedAt: result.syncedAt,
|
|
9258
9725
|
memberCount: result.memberCount,
|
|
9259
9726
|
contractCount: result.contracts.length,
|
|
9260
9727
|
linkCount: result.links.length,
|
|
9261
9728
|
topLinks: result.links.slice(0, 20)
|
|
9262
|
-
}
|
|
9729
|
+
})
|
|
9263
9730
|
}]
|
|
9264
9731
|
};
|
|
9265
9732
|
}
|
|
@@ -9279,7 +9746,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9279
9746
|
return {
|
|
9280
9747
|
content: [{
|
|
9281
9748
|
type: "text",
|
|
9282
|
-
text:
|
|
9749
|
+
text: compact({ syncedAt: result.syncedAt, contracts, links })
|
|
9283
9750
|
}]
|
|
9284
9751
|
};
|
|
9285
9752
|
}
|
|
@@ -9294,7 +9761,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9294
9761
|
return {
|
|
9295
9762
|
content: [{
|
|
9296
9763
|
type: "text",
|
|
9297
|
-
text:
|
|
9764
|
+
text: compact({ query, merged, perRepo })
|
|
9298
9765
|
}]
|
|
9299
9766
|
};
|
|
9300
9767
|
}
|
|
@@ -9326,12 +9793,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9326
9793
|
return {
|
|
9327
9794
|
content: [{
|
|
9328
9795
|
type: "text",
|
|
9329
|
-
text:
|
|
9796
|
+
text: compact({
|
|
9330
9797
|
group: groupName,
|
|
9331
9798
|
lastSync: group.lastSync ?? null,
|
|
9332
9799
|
syncAgeMinutes: syncAge,
|
|
9333
9800
|
members: memberStatus
|
|
9334
|
-
}
|
|
9801
|
+
})
|
|
9335
9802
|
}]
|
|
9336
9803
|
};
|
|
9337
9804
|
}
|
|
@@ -9340,11 +9807,11 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9340
9807
|
const fromName = a.from;
|
|
9341
9808
|
const toName = a.to;
|
|
9342
9809
|
const result = explainRelationship(graph, fromName, toName);
|
|
9343
|
-
return { content: [{ type: "text", text:
|
|
9810
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9344
9811
|
}
|
|
9345
9812
|
// ── pr_impact ──────────────────────────────────────────────────────────
|
|
9346
9813
|
case "pr_impact": {
|
|
9347
|
-
const maxHops = a.maxHops ??
|
|
9814
|
+
const maxHops = a.maxHops ?? 2;
|
|
9348
9815
|
let changedFiles = a.changedFiles ?? [];
|
|
9349
9816
|
if (a.diff && typeof a.diff === "string") {
|
|
9350
9817
|
const diffFiles = parseDiffFiles(a.diff);
|
|
@@ -9354,37 +9821,37 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9354
9821
|
return {
|
|
9355
9822
|
content: [{
|
|
9356
9823
|
type: "text",
|
|
9357
|
-
text:
|
|
9824
|
+
text: compact({ error: 'No changed files provided. Supply "changedFiles" or "diff".' })
|
|
9358
9825
|
}]
|
|
9359
9826
|
};
|
|
9360
9827
|
}
|
|
9361
9828
|
const result = computePRImpact(graph, changedFiles, maxHops);
|
|
9362
|
-
return { content: [{ type: "text", text:
|
|
9829
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9363
9830
|
}
|
|
9364
9831
|
// ── similar_symbols ────────────────────────────────────────────────────
|
|
9365
9832
|
case "similar_symbols": {
|
|
9366
9833
|
const symbolName = a.symbol;
|
|
9367
9834
|
const limit = a.limit ?? 10;
|
|
9368
9835
|
const result = findSimilarSymbols(graph, symbolName, limit);
|
|
9369
|
-
return { content: [{ type: "text", text:
|
|
9836
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9370
9837
|
}
|
|
9371
9838
|
// ── health_report ──────────────────────────────────────────────────────
|
|
9372
9839
|
case "health_report": {
|
|
9373
9840
|
const scope = a.scope ?? ".";
|
|
9374
9841
|
const result = computeHealthReport(graph, scope);
|
|
9375
|
-
return { content: [{ type: "text", text:
|
|
9842
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9376
9843
|
}
|
|
9377
9844
|
// ── suggest_tests ──────────────────────────────────────────────────────
|
|
9378
9845
|
case "suggest_tests": {
|
|
9379
9846
|
const sym = a.symbol;
|
|
9380
9847
|
const result = suggestTests(graph, sym);
|
|
9381
|
-
return { content: [{ type: "text", text:
|
|
9848
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9382
9849
|
}
|
|
9383
9850
|
// ── cluster_summary ────────────────────────────────────────────────────
|
|
9384
9851
|
case "cluster_summary": {
|
|
9385
9852
|
const cluster = a.cluster;
|
|
9386
9853
|
const result = summarizeCluster(graph, cluster);
|
|
9387
|
-
return { content: [{ type: "text", text:
|
|
9854
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
9388
9855
|
}
|
|
9389
9856
|
case "deprecated_usage": {
|
|
9390
9857
|
const scope = a.scope;
|
|
@@ -9392,7 +9859,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9392
9859
|
const detector = new DeprecatedDetector2();
|
|
9393
9860
|
detector.tagDeprecated(graph);
|
|
9394
9861
|
const findings = detector.detect(graph, scope);
|
|
9395
|
-
return { content: [{ type: "text", text:
|
|
9862
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
9396
9863
|
}
|
|
9397
9864
|
// ── complexity_hotspots ────────────────────────────────────────────────
|
|
9398
9865
|
case "complexity_hotspots": {
|
|
@@ -9400,7 +9867,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9400
9867
|
const scope = a.scope;
|
|
9401
9868
|
const limit = typeof a.limit === "number" ? a.limit : 20;
|
|
9402
9869
|
const hotspots = computeComplexity2(graph, scope).slice(0, limit);
|
|
9403
|
-
return { content: [{ type: "text", text:
|
|
9870
|
+
return { content: [{ type: "text", text: compact({ hotspots, total: hotspots.length }) }] };
|
|
9404
9871
|
}
|
|
9405
9872
|
// ── coverage_gaps ──────────────────────────────────────────────────────
|
|
9406
9873
|
case "coverage_gaps": {
|
|
@@ -9412,12 +9879,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9412
9879
|
return {
|
|
9413
9880
|
content: [{
|
|
9414
9881
|
type: "text",
|
|
9415
|
-
text:
|
|
9882
|
+
text: compact({
|
|
9416
9883
|
untestedByRisk,
|
|
9417
9884
|
coveragePct: summary.coveragePct,
|
|
9418
9885
|
totalExported: summary.totalExported,
|
|
9419
9886
|
testedExported: summary.testedExported
|
|
9420
|
-
}
|
|
9887
|
+
})
|
|
9421
9888
|
}]
|
|
9422
9889
|
};
|
|
9423
9890
|
}
|
|
@@ -9428,7 +9895,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9428
9895
|
const scope = a.scope;
|
|
9429
9896
|
const includeTestFiles = a.includeTestFiles ?? false;
|
|
9430
9897
|
const findings = scanner.scan(graph, { scope, includeTestFiles });
|
|
9431
|
-
return { content: [{ type: "text", text:
|
|
9898
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
9432
9899
|
}
|
|
9433
9900
|
// ── vulnerability_scan ─────────────────────────────────────────────────
|
|
9434
9901
|
case "vulnerability_scan": {
|
|
@@ -9441,7 +9908,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9441
9908
|
const minRank = sevRank[minSev] ?? 1;
|
|
9442
9909
|
let findings = detector.detect(graph, { scope, types });
|
|
9443
9910
|
findings = findings.filter((f) => (sevRank[f.severity] ?? 1) >= minRank);
|
|
9444
|
-
return { content: [{ type: "text", text:
|
|
9911
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
9445
9912
|
}
|
|
9446
9913
|
default:
|
|
9447
9914
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
|
@@ -9462,21 +9929,21 @@ function registerResources(server, graph, repoName) {
|
|
|
9462
9929
|
for (const node of graph.allNodes()) {
|
|
9463
9930
|
kindCounts[node.kind] = (kindCounts[node.kind] ?? 0) + 1;
|
|
9464
9931
|
}
|
|
9465
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
9932
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact({ repo: repoName, stats: graph.size, nodeCounts: kindCounts }) }] };
|
|
9466
9933
|
}
|
|
9467
9934
|
if (uri.endsWith("/clusters")) {
|
|
9468
9935
|
const clusters = [];
|
|
9469
9936
|
for (const node of graph.allNodes()) {
|
|
9470
9937
|
if (node.kind === "cluster") clusters.push({ id: node.id, name: node.name, memberCount: node.metadata?.memberCount });
|
|
9471
9938
|
}
|
|
9472
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
9939
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact(clusters) }] };
|
|
9473
9940
|
}
|
|
9474
9941
|
if (uri.endsWith("/flows")) {
|
|
9475
9942
|
const flows = [];
|
|
9476
9943
|
for (const node of graph.allNodes()) {
|
|
9477
9944
|
if (node.kind === "flow") flows.push({ id: node.id, name: node.name, steps: node.metadata?.steps, entryPoint: node.metadata?.entryPoint });
|
|
9478
9945
|
}
|
|
9479
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
9946
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact(flows) }] };
|
|
9480
9947
|
}
|
|
9481
9948
|
throw new Error(`Unknown resource: ${uri}`);
|
|
9482
9949
|
});
|
|
@@ -9526,6 +9993,8 @@ function parseDiff(diffText) {
|
|
|
9526
9993
|
return result;
|
|
9527
9994
|
}
|
|
9528
9995
|
init_group_registry();
|
|
9996
|
+
init_knowledge_graph();
|
|
9997
|
+
init_graph_from_db();
|
|
9529
9998
|
init_logger();
|
|
9530
9999
|
init_codes();
|
|
9531
10000
|
init_middleware();
|
|
@@ -9536,8 +10005,8 @@ var STUCK_THRESHOLD_MINUTES = 30;
|
|
|
9536
10005
|
var JobsDB = class {
|
|
9537
10006
|
db;
|
|
9538
10007
|
constructor(dbPath) {
|
|
9539
|
-
|
|
9540
|
-
this.db = new
|
|
10008
|
+
fs26.mkdirSync(path32.dirname(dbPath), { recursive: true });
|
|
10009
|
+
this.db = new Database2(dbPath);
|
|
9541
10010
|
this.db.pragma("journal_mode = WAL");
|
|
9542
10011
|
this.db.pragma("foreign_keys = ON");
|
|
9543
10012
|
this.createTables();
|
|
@@ -9678,7 +10147,7 @@ var JobsDB = class {
|
|
|
9678
10147
|
}
|
|
9679
10148
|
};
|
|
9680
10149
|
function getJobsDBPath() {
|
|
9681
|
-
return
|
|
10150
|
+
return path32.join(os13.homedir(), ".code-intel", "jobs.db");
|
|
9682
10151
|
}
|
|
9683
10152
|
var _jobsDB = null;
|
|
9684
10153
|
function getOrCreateJobsDB() {
|
|
@@ -9770,7 +10239,7 @@ var BACKUP_VERSION = "1.0";
|
|
|
9770
10239
|
var ALGORITHM = "aes-256-gcm";
|
|
9771
10240
|
var IV_LENGTH = 16;
|
|
9772
10241
|
function getBackupDir() {
|
|
9773
|
-
return
|
|
10242
|
+
return path32.join(os13.homedir(), ".code-intel", "backups");
|
|
9774
10243
|
}
|
|
9775
10244
|
function getBackupKey() {
|
|
9776
10245
|
const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
|
|
@@ -9801,30 +10270,30 @@ var BackupService = class {
|
|
|
9801
10270
|
constructor(backupDir) {
|
|
9802
10271
|
this.backupDir = backupDir ?? getBackupDir();
|
|
9803
10272
|
this.key = getBackupKey();
|
|
9804
|
-
|
|
10273
|
+
fs26.mkdirSync(this.backupDir, { recursive: true });
|
|
9805
10274
|
}
|
|
9806
10275
|
/**
|
|
9807
10276
|
* Create a backup for a repository.
|
|
9808
10277
|
* Returns the backup entry.
|
|
9809
10278
|
*/
|
|
9810
10279
|
createBackup(repoPath) {
|
|
9811
|
-
const codeIntelDir =
|
|
10280
|
+
const codeIntelDir = path32.join(repoPath, ".code-intel");
|
|
9812
10281
|
const id = v4();
|
|
9813
10282
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9814
10283
|
const filesToBackup = [];
|
|
9815
10284
|
const candidates = ["graph.db", "vector.db", "meta.json"];
|
|
9816
10285
|
for (const f of candidates) {
|
|
9817
|
-
const fp =
|
|
9818
|
-
if (
|
|
10286
|
+
const fp = path32.join(codeIntelDir, f);
|
|
10287
|
+
if (fs26.existsSync(fp)) {
|
|
9819
10288
|
filesToBackup.push({ name: f, localPath: fp });
|
|
9820
10289
|
}
|
|
9821
10290
|
}
|
|
9822
|
-
const registryPath =
|
|
9823
|
-
if (
|
|
10291
|
+
const registryPath = path32.join(os13.homedir(), ".code-intel", "registry.json");
|
|
10292
|
+
if (fs26.existsSync(registryPath)) {
|
|
9824
10293
|
filesToBackup.push({ name: "registry.json", localPath: registryPath });
|
|
9825
10294
|
}
|
|
9826
|
-
const usersDbPath =
|
|
9827
|
-
if (
|
|
10295
|
+
const usersDbPath = path32.join(os13.homedir(), ".code-intel", "users.db");
|
|
10296
|
+
if (fs26.existsSync(usersDbPath)) {
|
|
9828
10297
|
filesToBackup.push({ name: "users.db", localPath: usersDbPath });
|
|
9829
10298
|
}
|
|
9830
10299
|
if (filesToBackup.length === 0) {
|
|
@@ -9835,7 +10304,7 @@ var BackupService = class {
|
|
|
9835
10304
|
createdAt,
|
|
9836
10305
|
version: BACKUP_VERSION,
|
|
9837
10306
|
files: filesToBackup.map((f) => {
|
|
9838
|
-
const data =
|
|
10307
|
+
const data = fs26.readFileSync(f.localPath);
|
|
9839
10308
|
return {
|
|
9840
10309
|
name: f.name,
|
|
9841
10310
|
sha256: crypto5.createHash("sha256").update(data).digest("hex"),
|
|
@@ -9849,7 +10318,7 @@ var BackupService = class {
|
|
|
9849
10318
|
manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
|
|
9850
10319
|
parts.push(manifestLenBuf, manifestBuf);
|
|
9851
10320
|
for (const f of filesToBackup) {
|
|
9852
|
-
const data =
|
|
10321
|
+
const data = fs26.readFileSync(f.localPath);
|
|
9853
10322
|
const nameBuf = Buffer.from(f.name, "utf-8");
|
|
9854
10323
|
const nameLenBuf = Buffer.alloc(2);
|
|
9855
10324
|
nameLenBuf.writeUInt16BE(nameBuf.length, 0);
|
|
@@ -9860,8 +10329,8 @@ var BackupService = class {
|
|
|
9860
10329
|
const plaintext = Buffer.concat(parts);
|
|
9861
10330
|
const encrypted = encryptBuffer(plaintext, this.key);
|
|
9862
10331
|
const backupFileName = `backup-${id}.cib`;
|
|
9863
|
-
const backupPath =
|
|
9864
|
-
|
|
10332
|
+
const backupPath = path32.join(this.backupDir, backupFileName);
|
|
10333
|
+
fs26.writeFileSync(backupPath, encrypted);
|
|
9865
10334
|
const entry = {
|
|
9866
10335
|
id,
|
|
9867
10336
|
createdAt,
|
|
@@ -9888,9 +10357,9 @@ var BackupService = class {
|
|
|
9888
10357
|
async uploadToS3(entry) {
|
|
9889
10358
|
const cfg = getS3Config();
|
|
9890
10359
|
if (!cfg) throw new Error("S3 not configured. Set CODE_INTEL_BACKUP_S3_BUCKET, CODE_INTEL_BACKUP_S3_ACCESS_KEY_ID, CODE_INTEL_BACKUP_S3_SECRET_ACCESS_KEY.");
|
|
9891
|
-
const fileName =
|
|
10360
|
+
const fileName = path32.basename(entry.path);
|
|
9892
10361
|
const s3Key = `${cfg.prefix}${fileName}`;
|
|
9893
|
-
const body =
|
|
10362
|
+
const body = fs26.readFileSync(entry.path);
|
|
9894
10363
|
const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
|
|
9895
10364
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9896
10365
|
throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
@@ -9907,8 +10376,8 @@ var BackupService = class {
|
|
|
9907
10376
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9908
10377
|
throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
9909
10378
|
}
|
|
9910
|
-
|
|
9911
|
-
|
|
10379
|
+
fs26.mkdirSync(path32.dirname(destPath), { recursive: true });
|
|
10380
|
+
fs26.writeFileSync(destPath, Buffer.from(result.body, "binary"));
|
|
9912
10381
|
}
|
|
9913
10382
|
/**
|
|
9914
10383
|
* List backup objects in S3 with the configured prefix.
|
|
@@ -9954,10 +10423,10 @@ var BackupService = class {
|
|
|
9954
10423
|
if (!entry) {
|
|
9955
10424
|
throw new Error(`Backup "${backupId}" not found.`);
|
|
9956
10425
|
}
|
|
9957
|
-
if (!
|
|
10426
|
+
if (!fs26.existsSync(entry.path)) {
|
|
9958
10427
|
throw new Error(`Backup file not found at: ${entry.path}`);
|
|
9959
10428
|
}
|
|
9960
|
-
const encrypted =
|
|
10429
|
+
const encrypted = fs26.readFileSync(entry.path);
|
|
9961
10430
|
let plaintext;
|
|
9962
10431
|
try {
|
|
9963
10432
|
plaintext = decryptBuffer(encrypted, this.key);
|
|
@@ -9971,8 +10440,8 @@ var BackupService = class {
|
|
|
9971
10440
|
offset += manifestLen;
|
|
9972
10441
|
const manifest = JSON.parse(manifestStr);
|
|
9973
10442
|
const restoreBase = targetRepoPath ?? entry.repoPath;
|
|
9974
|
-
const codeIntelDir =
|
|
9975
|
-
|
|
10443
|
+
const codeIntelDir = path32.join(restoreBase, ".code-intel");
|
|
10444
|
+
fs26.mkdirSync(codeIntelDir, { recursive: true });
|
|
9976
10445
|
for (const fileEntry of manifest.files) {
|
|
9977
10446
|
const nameLen = plaintext.readUInt16BE(offset);
|
|
9978
10447
|
offset += 2;
|
|
@@ -9989,18 +10458,18 @@ var BackupService = class {
|
|
|
9989
10458
|
}
|
|
9990
10459
|
let destPath;
|
|
9991
10460
|
if (name === "registry.json" || name === "users.db") {
|
|
9992
|
-
destPath =
|
|
10461
|
+
destPath = path32.join(os13.homedir(), ".code-intel", name);
|
|
9993
10462
|
} else {
|
|
9994
|
-
destPath =
|
|
10463
|
+
destPath = path32.join(codeIntelDir, name);
|
|
9995
10464
|
}
|
|
9996
|
-
|
|
10465
|
+
fs26.writeFileSync(destPath, data);
|
|
9997
10466
|
}
|
|
9998
10467
|
}
|
|
9999
10468
|
/**
|
|
10000
10469
|
* Apply retention policy: keep N daily, M weekly, L monthly backups.
|
|
10001
10470
|
*/
|
|
10002
10471
|
applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
|
|
10003
|
-
const entries = this._loadIndex().filter((e) =>
|
|
10472
|
+
const entries = this._loadIndex().filter((e) => fs26.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
10004
10473
|
const keep = /* @__PURE__ */ new Set();
|
|
10005
10474
|
const now = /* @__PURE__ */ new Date();
|
|
10006
10475
|
const dailyCutoff = new Date(now);
|
|
@@ -10030,7 +10499,7 @@ var BackupService = class {
|
|
|
10030
10499
|
for (const e of entries) {
|
|
10031
10500
|
if (!keep.has(e.id)) {
|
|
10032
10501
|
try {
|
|
10033
|
-
|
|
10502
|
+
fs26.unlinkSync(e.path);
|
|
10034
10503
|
deleted++;
|
|
10035
10504
|
} catch {
|
|
10036
10505
|
}
|
|
@@ -10042,17 +10511,17 @@ var BackupService = class {
|
|
|
10042
10511
|
}
|
|
10043
10512
|
// ── Index helpers ──────────────────────────────────────────────────────────
|
|
10044
10513
|
_indexPath() {
|
|
10045
|
-
return
|
|
10514
|
+
return path32.join(this.backupDir, "index.json");
|
|
10046
10515
|
}
|
|
10047
10516
|
_loadIndex() {
|
|
10048
10517
|
try {
|
|
10049
|
-
return JSON.parse(
|
|
10518
|
+
return JSON.parse(fs26.readFileSync(this._indexPath(), "utf-8"));
|
|
10050
10519
|
} catch {
|
|
10051
10520
|
return [];
|
|
10052
10521
|
}
|
|
10053
10522
|
}
|
|
10054
10523
|
_saveIndex(entries) {
|
|
10055
|
-
|
|
10524
|
+
fs26.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
|
|
10056
10525
|
}
|
|
10057
10526
|
_appendIndex(entry) {
|
|
10058
10527
|
const entries = this._loadIndex();
|
|
@@ -10845,11 +11314,11 @@ var openApiSpec = {
|
|
|
10845
11314
|
};
|
|
10846
11315
|
|
|
10847
11316
|
// src/http/app.ts
|
|
10848
|
-
var __dirname$1 =
|
|
11317
|
+
var __dirname$1 = path32.dirname(fileURLToPath(import.meta.url));
|
|
10849
11318
|
var WEB_DIST = (() => {
|
|
10850
|
-
const bundled =
|
|
10851
|
-
if (
|
|
10852
|
-
return
|
|
11319
|
+
const bundled = path32.resolve(__dirname$1, "..", "web");
|
|
11320
|
+
if (fs26.existsSync(bundled)) return bundled;
|
|
11321
|
+
return path32.resolve(__dirname$1, "..", "..", "..", "web", "dist");
|
|
10853
11322
|
})();
|
|
10854
11323
|
function getAllowedOrigins() {
|
|
10855
11324
|
const env = process.env["CODE_INTEL_CORS_ORIGINS"];
|
|
@@ -10858,13 +11327,17 @@ function getAllowedOrigins() {
|
|
|
10858
11327
|
}
|
|
10859
11328
|
function createDefaultLimiter() {
|
|
10860
11329
|
const max = parseInt(process.env["CODE_INTEL_RATE_LIMIT_MAX"] ?? "100", 10);
|
|
10861
|
-
const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${
|
|
11330
|
+
const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${60 * 1e3}`, 10);
|
|
10862
11331
|
return rateLimit({
|
|
10863
11332
|
windowMs,
|
|
10864
11333
|
max,
|
|
10865
11334
|
standardHeaders: true,
|
|
10866
11335
|
legacyHeaders: false,
|
|
10867
|
-
|
|
11336
|
+
// Skip health checks, metrics, and read-only listing/pagination endpoints.
|
|
11337
|
+
// The node pagination and group/repo listing endpoints are hit many times
|
|
11338
|
+
// when loading a large graph — rate-limiting them only hurts the user's own
|
|
11339
|
+
// session without providing meaningful abuse protection.
|
|
11340
|
+
skip: (req) => req.path.startsWith("/health") || req.path === "/metrics" || req.path === "/api/v1/repos" || req.path === "/api/v1/groups" || req.method === "GET" && req.path.startsWith("/api/v1/groups/") || /^\/api\/v1\/graph\/[^/]+\/nodes$/.test(req.path),
|
|
10868
11341
|
message: {
|
|
10869
11342
|
error: {
|
|
10870
11343
|
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
|
|
@@ -10923,12 +11396,31 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10923
11396
|
});
|
|
10924
11397
|
app.use(requestIdMiddleware);
|
|
10925
11398
|
app.use(authMiddleware);
|
|
11399
|
+
let dbUnavailableSince = null;
|
|
10926
11400
|
app.use((_req, res, next) => {
|
|
10927
11401
|
if (workspaceRoot) {
|
|
11402
|
+
const metaFilePath = path32.join(workspaceRoot, ".code-intel", "meta.json");
|
|
11403
|
+
let metaOk = false;
|
|
10928
11404
|
try {
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
11405
|
+
if (fs26.existsSync(metaFilePath)) {
|
|
11406
|
+
const raw = fs26.readFileSync(metaFilePath, "utf-8");
|
|
11407
|
+
const meta = JSON.parse(raw);
|
|
11408
|
+
if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
|
|
11409
|
+
}
|
|
11410
|
+
metaOk = true;
|
|
11411
|
+
if (dbUnavailableSince !== null) {
|
|
11412
|
+
dbUnavailableSince = null;
|
|
11413
|
+
logger_default.info("[serve] DB back online \u2014 cleared stale flag");
|
|
11414
|
+
}
|
|
11415
|
+
} catch (err) {
|
|
11416
|
+
if (dbUnavailableSince === null) {
|
|
11417
|
+
dbUnavailableSince = (/* @__PURE__ */ new Date()).toISOString();
|
|
11418
|
+
logger_default.warn(`[serve] DB unavailable since ${dbUnavailableSince}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
if (!metaOk) {
|
|
11422
|
+
res.setHeader("X-Stale", "true");
|
|
11423
|
+
res.setHeader("X-Stale-Since", dbUnavailableSince);
|
|
10932
11424
|
}
|
|
10933
11425
|
}
|
|
10934
11426
|
next();
|
|
@@ -10984,6 +11476,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10984
11476
|
let vectorIndex = null;
|
|
10985
11477
|
let vectorIndexBuilding = false;
|
|
10986
11478
|
let vectorIndexReady = false;
|
|
11479
|
+
let bm25Index = null;
|
|
11480
|
+
function ensureBm25Index() {
|
|
11481
|
+
if (bm25Index) return bm25Index;
|
|
11482
|
+
if (!workspaceRoot) return null;
|
|
11483
|
+
const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
|
|
11484
|
+
idx.load();
|
|
11485
|
+
if (idx.isLoaded) {
|
|
11486
|
+
bm25Index = idx;
|
|
11487
|
+
return idx;
|
|
11488
|
+
}
|
|
11489
|
+
return null;
|
|
11490
|
+
}
|
|
11491
|
+
if (workspaceRoot && process.env["NODE_ENV"] !== "test") {
|
|
11492
|
+
setImmediate(() => ensureBm25Index());
|
|
11493
|
+
}
|
|
10987
11494
|
async function ensureVectorIndex() {
|
|
10988
11495
|
if (vectorIndexReady && vectorIndex) return vectorIndex;
|
|
10989
11496
|
if (!workspaceRoot || vectorIndexBuilding) return null;
|
|
@@ -11087,12 +11594,12 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11087
11594
|
return;
|
|
11088
11595
|
}
|
|
11089
11596
|
const user = db.createUser(username, password, "admin");
|
|
11090
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11091
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11597
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11598
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11092
11599
|
res.status(201).json({ user: { id: user.id, username: user.username, role: user.role } });
|
|
11093
11600
|
});
|
|
11094
11601
|
app.post("/auth/login", async (req, res) => {
|
|
11095
|
-
const { username, password } = req.body;
|
|
11602
|
+
const { username, password, rememberMe } = req.body;
|
|
11096
11603
|
if (!username || !password) {
|
|
11097
11604
|
res.status(400).json({
|
|
11098
11605
|
error: {
|
|
@@ -11136,10 +11643,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11136
11643
|
});
|
|
11137
11644
|
return;
|
|
11138
11645
|
}
|
|
11139
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11646
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role }, rememberMe === true);
|
|
11140
11647
|
db.logAccess(user.id, "/auth/login", "login", "allow", req.ip ?? "unknown");
|
|
11141
11648
|
authAttemptsTotal.inc({ method: "local", outcome: "success" });
|
|
11142
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11649
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11143
11650
|
res.json({ user: { id: user.id, username: user.username, role: user.role } });
|
|
11144
11651
|
});
|
|
11145
11652
|
app.post("/auth/logout", (req, res) => {
|
|
@@ -11261,9 +11768,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11261
11768
|
authAttemptsTotal.inc({ method: "oidc", outcome: "success" });
|
|
11262
11769
|
logger_default.info(`[oidc] Auto-provisioned new user: ${finalUsername} (${cfg.defaultRole})`);
|
|
11263
11770
|
}
|
|
11264
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11771
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11265
11772
|
db.logAccess(user.id, "/auth/callback", "oidc-login", "allow", req.ip ?? "unknown");
|
|
11266
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11773
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11267
11774
|
res.redirect(302, "/");
|
|
11268
11775
|
} catch (err) {
|
|
11269
11776
|
logger_default.warn("[oidc] Callback failed:", err instanceof Error ? err.message : err);
|
|
@@ -11380,10 +11887,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11380
11887
|
const registry = loadRegistry();
|
|
11381
11888
|
const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
|
|
11382
11889
|
if (!entry) return null;
|
|
11383
|
-
const dbPath =
|
|
11384
|
-
if (!
|
|
11890
|
+
const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
|
|
11891
|
+
if (!fs26.existsSync(dbPath)) return null;
|
|
11385
11892
|
const repoGraph = createKnowledgeGraph();
|
|
11386
|
-
const db = new DbManager(dbPath);
|
|
11893
|
+
const db = new DbManager(dbPath, true);
|
|
11387
11894
|
try {
|
|
11388
11895
|
await db.init();
|
|
11389
11896
|
await loadGraphFromDB(repoGraph, db);
|
|
@@ -11394,10 +11901,33 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11394
11901
|
return null;
|
|
11395
11902
|
}
|
|
11396
11903
|
}
|
|
11904
|
+
async function loadGroupGraph(groupName) {
|
|
11905
|
+
const group = loadGroup(groupName);
|
|
11906
|
+
if (!group) return null;
|
|
11907
|
+
const registry = loadRegistry();
|
|
11908
|
+
const mergedGraph = createKnowledgeGraph();
|
|
11909
|
+
for (const member of group.members) {
|
|
11910
|
+
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11911
|
+
if (!regEntry) continue;
|
|
11912
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
11913
|
+
if (!fs26.existsSync(dbPath)) continue;
|
|
11914
|
+
const db = new DbManager(dbPath, true);
|
|
11915
|
+
try {
|
|
11916
|
+
await db.init();
|
|
11917
|
+
await loadGraphFromDB(mergedGraph, db);
|
|
11918
|
+
db.close();
|
|
11919
|
+
} catch {
|
|
11920
|
+
db.close();
|
|
11921
|
+
}
|
|
11922
|
+
}
|
|
11923
|
+
return mergedGraph.size.nodes > 0 ? mergedGraph : null;
|
|
11924
|
+
}
|
|
11397
11925
|
async function getGraphForRepo(requestedRepo) {
|
|
11398
11926
|
if (!requestedRepo || requestedRepo === repoName) return graph;
|
|
11399
11927
|
const g = await loadRepoGraph(requestedRepo);
|
|
11400
|
-
|
|
11928
|
+
if (g) return g;
|
|
11929
|
+
const gg = await loadGroupGraph(requestedRepo);
|
|
11930
|
+
return gg ?? graph;
|
|
11401
11931
|
}
|
|
11402
11932
|
app.get("/api/v1/graph/:repo", requireRepoAccess((req) => {
|
|
11403
11933
|
const p = req.params["repo"];
|
|
@@ -11421,12 +11951,71 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11421
11951
|
}
|
|
11422
11952
|
res.json({ nodes: [...g.allNodes()], edges: [...g.allEdges()] });
|
|
11423
11953
|
});
|
|
11954
|
+
app.get("/api/v1/graph/:repo/nodes", requireRepoAccess((req) => {
|
|
11955
|
+
const p = req.params["repo"];
|
|
11956
|
+
const repo = Array.isArray(p) ? p[0] : p;
|
|
11957
|
+
return repo ? decodeURIComponent(repo) : void 0;
|
|
11958
|
+
}), async (req, res) => {
|
|
11959
|
+
const rawRepo = req.params["repo"];
|
|
11960
|
+
const requestedRepo = decodeURIComponent(Array.isArray(rawRepo) ? rawRepo[0] ?? "" : rawRepo ?? "");
|
|
11961
|
+
const limit = Math.min(parseInt(req.query["limit"] ?? "200", 10), 1e3);
|
|
11962
|
+
const offset = Math.max(parseInt(req.query["offset"] ?? "0", 10), 0);
|
|
11963
|
+
const g = requestedRepo === repoName ? graph : await loadRepoGraph(requestedRepo);
|
|
11964
|
+
if (!g) {
|
|
11965
|
+
res.status(404).json({
|
|
11966
|
+
error: {
|
|
11967
|
+
code: ErrorCodes.NOT_FOUND,
|
|
11968
|
+
message: `Repo "${requestedRepo}" not found or not indexed`,
|
|
11969
|
+
hint: `Run: code-intel analyze <path>`,
|
|
11970
|
+
requestId: req.requestId,
|
|
11971
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11972
|
+
}
|
|
11973
|
+
});
|
|
11974
|
+
return;
|
|
11975
|
+
}
|
|
11976
|
+
let nodes;
|
|
11977
|
+
if (isLazyGraph(g)) {
|
|
11978
|
+
nodes = await g.getNodePage(offset, limit);
|
|
11979
|
+
} else {
|
|
11980
|
+
const eager = g;
|
|
11981
|
+
if (!eager._nodeArray) {
|
|
11982
|
+
eager._nodeArray = [...g.allNodes()];
|
|
11983
|
+
}
|
|
11984
|
+
nodes = eager._nodeArray.slice(offset, offset + limit);
|
|
11985
|
+
}
|
|
11986
|
+
res.json({
|
|
11987
|
+
nodes,
|
|
11988
|
+
offset,
|
|
11989
|
+
limit,
|
|
11990
|
+
total: g.size.nodes,
|
|
11991
|
+
hasMore: offset + nodes.length < g.size.nodes
|
|
11992
|
+
});
|
|
11993
|
+
});
|
|
11424
11994
|
app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
|
|
11425
|
-
const { query, limit, repo } = req.body;
|
|
11995
|
+
const { query, limit, repo, group } = req.body;
|
|
11996
|
+
if (group) {
|
|
11997
|
+
const grp = loadGroup(group);
|
|
11998
|
+
if (!grp) {
|
|
11999
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: `Group '${group}' not found`, hint: "Use /api/v1/groups to list available groups" } });
|
|
12000
|
+
return;
|
|
12001
|
+
}
|
|
12002
|
+
try {
|
|
12003
|
+
const { perRepo, merged } = await queryGroup(grp, query ?? "", limit ?? 20);
|
|
12004
|
+
res.json({ results: merged, perRepo, searchMode: "bm25", group });
|
|
12005
|
+
} catch (err) {
|
|
12006
|
+
res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err) } });
|
|
12007
|
+
}
|
|
12008
|
+
return;
|
|
12009
|
+
}
|
|
11426
12010
|
const g = await getGraphForRepo(repo);
|
|
11427
12011
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
11428
|
-
const
|
|
11429
|
-
|
|
12012
|
+
const bm25 = !repo || repo === repoName ? ensureBm25Index() : null;
|
|
12013
|
+
const bm25Results = bm25 ? bm25.search(query ?? "", (limit ?? 20) * 3) : null;
|
|
12014
|
+
const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, {
|
|
12015
|
+
vectorDbPath: vdbPath,
|
|
12016
|
+
bm25Results: bm25Results ?? void 0
|
|
12017
|
+
});
|
|
12018
|
+
res.json({ results, searchMode, repo: repo ?? repoName });
|
|
11430
12019
|
});
|
|
11431
12020
|
app.post("/api/v1/vector-search", async (req, res) => {
|
|
11432
12021
|
const { query, limit = 10 } = req.body;
|
|
@@ -11468,7 +12057,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11468
12057
|
return;
|
|
11469
12058
|
}
|
|
11470
12059
|
try {
|
|
11471
|
-
const content =
|
|
12060
|
+
const content = fs26.readFileSync(file_path, "utf-8");
|
|
11472
12061
|
res.json({ content });
|
|
11473
12062
|
} catch {
|
|
11474
12063
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
|
|
@@ -11505,7 +12094,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11505
12094
|
if (workspaceRoot) {
|
|
11506
12095
|
try {
|
|
11507
12096
|
const dbPath = getDbPath(workspaceRoot);
|
|
11508
|
-
const dbm = new DbManager(dbPath);
|
|
12097
|
+
const dbm = new DbManager(dbPath, true);
|
|
11509
12098
|
await dbm.init();
|
|
11510
12099
|
const rows = await dbm.query(q);
|
|
11511
12100
|
dbm.close();
|
|
@@ -11546,33 +12135,53 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11546
12135
|
app.get("/api/v1/nodes/:id", async (req, res) => {
|
|
11547
12136
|
const nodeId = decodeURIComponent(req.params.id);
|
|
11548
12137
|
const g = await getGraphForRepo(req.query["repo"]);
|
|
11549
|
-
const node = g.getNode(nodeId);
|
|
12138
|
+
const node = isLazyGraph(g) ? await g.getNodeAsync(nodeId) : g.getNode(nodeId);
|
|
11550
12139
|
if (!node) {
|
|
11551
12140
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Node not found", requestId: req.requestId } });
|
|
11552
12141
|
return;
|
|
11553
12142
|
}
|
|
11554
12143
|
const incoming = [...g.findEdgesTo(nodeId)];
|
|
11555
12144
|
const outgoing = [...g.findEdgesFrom(nodeId)];
|
|
12145
|
+
const resolveName = isLazyGraph(g) ? async (id) => {
|
|
12146
|
+
const n = g.getNode(id) ?? await g.getNodeAsync(id);
|
|
12147
|
+
return n?.name;
|
|
12148
|
+
} : (id) => Promise.resolve(g.getNode(id)?.name);
|
|
12149
|
+
const resolveKind = isLazyGraph(g) ? async (id) => {
|
|
12150
|
+
const n = g.getNode(id) ?? await g.getNodeAsync(id);
|
|
12151
|
+
return n?.kind;
|
|
12152
|
+
} : (id) => Promise.resolve(g.getNode(id)?.kind);
|
|
11556
12153
|
res.json({
|
|
11557
12154
|
node,
|
|
11558
|
-
callers: incoming.filter((e) => e.kind === "calls").map((e) => ({ id: e.source, name:
|
|
11559
|
-
callees: outgoing.filter((e) => e.kind === "calls").map((e) => ({ id: e.target, name:
|
|
11560
|
-
imports: outgoing.filter((e) => e.kind === "imports").map((e) => ({ id: e.target, name:
|
|
11561
|
-
importedBy: incoming.filter((e) => e.kind === "imports").map((e) => ({ id: e.source, name:
|
|
11562
|
-
extends: outgoing.filter((e) => e.kind === "extends").map((e) => ({ id: e.target, name:
|
|
11563
|
-
implementsEdges: outgoing.filter((e) => e.kind === "implements").map((e) => ({ id: e.target, name:
|
|
11564
|
-
members: outgoing.filter((e) => e.kind === "has_member").map((e) => ({ id: e.target, name:
|
|
11565
|
-
cluster: incoming.filter((e) => e.kind === "belongs_to").map((e) =>
|
|
12155
|
+
callers: await Promise.all(incoming.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.source, name: await resolveName(e.source), weight: e.weight }))),
|
|
12156
|
+
callees: await Promise.all(outgoing.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.target, name: await resolveName(e.target), weight: e.weight }))),
|
|
12157
|
+
imports: await Promise.all(outgoing.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
12158
|
+
importedBy: await Promise.all(incoming.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.source, name: await resolveName(e.source) }))),
|
|
12159
|
+
extends: await Promise.all(outgoing.filter((e) => e.kind === "extends").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
12160
|
+
implementsEdges: await Promise.all(outgoing.filter((e) => e.kind === "implements").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
12161
|
+
members: await Promise.all(outgoing.filter((e) => e.kind === "has_member").map(async (e) => ({ id: e.target, name: await resolveName(e.target), kind: await resolveKind(e.target) }))),
|
|
12162
|
+
cluster: (await Promise.all(incoming.filter((e) => e.kind === "belongs_to").map(async (e) => resolveName(e.target))))[0]
|
|
11566
12163
|
});
|
|
11567
12164
|
});
|
|
11568
12165
|
app.post("/api/v1/blast-radius", async (req, res) => {
|
|
11569
12166
|
const { target, direction = "both", max_hops = 5, repo } = req.body;
|
|
11570
12167
|
const g = await getGraphForRepo(repo);
|
|
11571
12168
|
let targetNode = null;
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
12169
|
+
if (isLazyGraph(g) && target) {
|
|
12170
|
+
targetNode = g.getNode(target) ?? await g.getNodeAsync(target) ?? null;
|
|
12171
|
+
if (!targetNode) {
|
|
12172
|
+
for await (const node of g.allNodesAsync()) {
|
|
12173
|
+
if (node.name === target || node.id === target) {
|
|
12174
|
+
targetNode = node;
|
|
12175
|
+
break;
|
|
12176
|
+
}
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12179
|
+
} else {
|
|
12180
|
+
for (const node of g.allNodes()) {
|
|
12181
|
+
if (node.name === target || node.id === target) {
|
|
12182
|
+
targetNode = node;
|
|
12183
|
+
break;
|
|
12184
|
+
}
|
|
11576
12185
|
}
|
|
11577
12186
|
}
|
|
11578
12187
|
if (!targetNode) {
|
|
@@ -11726,9 +12335,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11726
12335
|
for (const member of group.members) {
|
|
11727
12336
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11728
12337
|
if (!regEntry) continue;
|
|
11729
|
-
const dbPath =
|
|
11730
|
-
if (!
|
|
11731
|
-
const db = new DbManager(dbPath);
|
|
12338
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
12339
|
+
if (!fs26.existsSync(dbPath)) continue;
|
|
12340
|
+
const db = new DbManager(dbPath, true);
|
|
11732
12341
|
try {
|
|
11733
12342
|
await db.init();
|
|
11734
12343
|
await loadGraphFromDB(mergedGraph, db);
|
|
@@ -11753,10 +12362,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11753
12362
|
let nodeCount = 0;
|
|
11754
12363
|
let edgeCount = 0;
|
|
11755
12364
|
if (regEntry) {
|
|
11756
|
-
const dbPath =
|
|
11757
|
-
if (
|
|
12365
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
12366
|
+
if (fs26.existsSync(dbPath)) {
|
|
11758
12367
|
try {
|
|
11759
|
-
const db = new DbManager(dbPath);
|
|
12368
|
+
const db = new DbManager(dbPath, true);
|
|
11760
12369
|
await db.init();
|
|
11761
12370
|
const g = createKnowledgeGraph();
|
|
11762
12371
|
await loadGraphFromDB(g, db);
|
|
@@ -11779,7 +12388,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11779
12388
|
res.json({ repos, edges });
|
|
11780
12389
|
});
|
|
11781
12390
|
app.get("/api/v1/source", requireAuth, requireRole("viewer"), (req, res) => {
|
|
11782
|
-
const { file, startLine: startLineStr, endLine: endLineStr } = req.query;
|
|
12391
|
+
const { file, startLine: startLineStr, endLine: endLineStr, repo } = req.query;
|
|
11783
12392
|
if (!file) {
|
|
11784
12393
|
res.status(400).json({
|
|
11785
12394
|
error: {
|
|
@@ -11803,14 +12412,36 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11803
12412
|
});
|
|
11804
12413
|
return;
|
|
11805
12414
|
}
|
|
11806
|
-
let
|
|
11807
|
-
if (
|
|
11808
|
-
|
|
12415
|
+
let baseDir = workspaceRoot;
|
|
12416
|
+
if (repo && repo !== repoName) {
|
|
12417
|
+
const registry = loadRegistry();
|
|
12418
|
+
const entry = registry.find((r) => r.name === repo || r.path === repo);
|
|
12419
|
+
if (entry) {
|
|
12420
|
+
baseDir = entry.path;
|
|
12421
|
+
} else {
|
|
12422
|
+
const group = loadGroup(repo);
|
|
12423
|
+
if (group) {
|
|
12424
|
+
const normalizedFile = path32.normalize(file);
|
|
12425
|
+
for (const member of group.members) {
|
|
12426
|
+
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
12427
|
+
if (!regEntry) continue;
|
|
12428
|
+
const candidate = path32.resolve(path32.join(regEntry.path, normalizedFile));
|
|
12429
|
+
if (fs26.existsSync(candidate)) {
|
|
12430
|
+
baseDir = regEntry.path;
|
|
12431
|
+
break;
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
}
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
let rawResolved = path32.normalize(file);
|
|
12438
|
+
if (!path32.isAbsolute(rawResolved) && baseDir) {
|
|
12439
|
+
rawResolved = path32.join(baseDir, rawResolved);
|
|
11809
12440
|
}
|
|
11810
|
-
const resolvedFile =
|
|
12441
|
+
const resolvedFile = path32.resolve(rawResolved);
|
|
11811
12442
|
function isInsideDir(fileAbs, dir) {
|
|
11812
|
-
const rel =
|
|
11813
|
-
return !rel.startsWith("..") && !
|
|
12443
|
+
const rel = path32.relative(path32.resolve(dir), fileAbs);
|
|
12444
|
+
return !rel.startsWith("..") && !path32.isAbsolute(rel);
|
|
11814
12445
|
}
|
|
11815
12446
|
if (workspaceRoot) {
|
|
11816
12447
|
if (!isInsideDir(resolvedFile, workspaceRoot)) {
|
|
@@ -11847,7 +12478,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11847
12478
|
}
|
|
11848
12479
|
let fileContent;
|
|
11849
12480
|
try {
|
|
11850
|
-
fileContent =
|
|
12481
|
+
fileContent = fs26.readFileSync(resolvedFile, "utf-8");
|
|
11851
12482
|
} catch {
|
|
11852
12483
|
res.status(404).json({
|
|
11853
12484
|
error: {
|
|
@@ -11878,7 +12509,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11878
12509
|
const contextStart = Math.max(1, startLine - 20);
|
|
11879
12510
|
const contextEnd = Math.min(lines.length, endLine + 20);
|
|
11880
12511
|
const content = lines.slice(contextStart - 1, contextEnd).join("\n");
|
|
11881
|
-
const ext =
|
|
12512
|
+
const ext = path32.extname(resolvedFile).toLowerCase();
|
|
11882
12513
|
const languageMap = {
|
|
11883
12514
|
".ts": "typescript",
|
|
11884
12515
|
".tsx": "typescript",
|
|
@@ -12013,10 +12644,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12013
12644
|
res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err), requestId: req.requestId, timestamp: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
12014
12645
|
}
|
|
12015
12646
|
});
|
|
12016
|
-
if (
|
|
12647
|
+
if (fs26.existsSync(WEB_DIST)) {
|
|
12017
12648
|
app.use(express.static(WEB_DIST));
|
|
12018
12649
|
app.get("/{*path}", (_req, res) => {
|
|
12019
|
-
res.sendFile(
|
|
12650
|
+
res.sendFile(path32.join(WEB_DIST, "index.html"));
|
|
12020
12651
|
});
|
|
12021
12652
|
}
|
|
12022
12653
|
app.use("/admin", requireRole("admin"));
|
|
@@ -12170,12 +12801,13 @@ async function startHttpServer(graph, repoName, port = 4747, workspaceRoot, watc
|
|
|
12170
12801
|
|
|
12171
12802
|
// src/multi-repo/index.ts
|
|
12172
12803
|
init_group_registry();
|
|
12804
|
+
init_graph_from_db();
|
|
12173
12805
|
|
|
12174
12806
|
// src/multi-repo/cross-repo-search.ts
|
|
12175
12807
|
function mergeSearchResults(...perRepoResults) {
|
|
12176
12808
|
return reciprocalRankFusion(...perRepoResults);
|
|
12177
12809
|
}
|
|
12178
12810
|
|
|
12179
|
-
export { AstCache, BindingTracker, DbManager, addBinding, addClustersToGraph, addMember, buildCallEdges, buildHeritageEdges, classifyCall, clusterPhase, computeMRO, createApp, createKnowledgeGraph, createMcpServer, createScope, deleteGroup, detectCommunities, detectOverrides, findEntryPoints, flowPhase, generateEdgeId, generateNodeId, getAllLanguageModules, getDbPath, getLanguage, getLanguageModule, getParser, groupExists, initParser, isTreeSitterAvailable, listGroups, loadGraphToDB, loadGroup, loadMetadata, loadRegistry, loadSyncResult, mergeSearchResults, parsePhase, parseSource, queryGroup, reciprocalRankFusion, removeMember, removeRepo, resolveBinding, resolveImports, resolvePhase, runPipeline, runQuery, runQueryMatches, saveGroup, saveMetadata, saveSyncResult, scanPhase, startHttpServer, startMcpStdio, structurePhase, syncGroup, textSearch, topologicalSort, traceFlow, upsertRepo, validateDAG };
|
|
12811
|
+
export { AstCache, BindingTracker, Bm25Index, DbManager, addBinding, addClustersToGraph, addMember, buildCallEdges, buildHeritageEdges, classifyCall, clusterPhase, computeMRO, createApp, createKnowledgeGraph, createMcpServer, createScope, deleteGroup, detectCommunities, detectOverrides, findEntryPoints, flowPhase, generateEdgeId, generateNodeId, getAllLanguageModules, getBm25DbPath, getDbPath, getLanguage, getLanguageModule, getParser, groupExists, initParser, isTreeSitterAvailable, listGroups, loadGraphToDB, loadGroup, loadMetadata, loadRegistry, loadSyncResult, mergeSearchResults, parsePhase, parseSource, queryGroup, reciprocalRankFusion, removeMember, removeRepo, resolveBinding, resolveImports, resolvePhase, runPipeline, runQuery, runQueryMatches, saveGroup, saveMetadata, saveSyncResult, scanPhase, startHttpServer, startMcpStdio, structurePhase, syncGroup, textSearch, topologicalSort, traceFlow, upsertRepo, validateDAG };
|
|
12180
12812
|
//# sourceMappingURL=index.js.map
|
|
12181
12813
|
//# sourceMappingURL=index.js.map
|