@vohongtho.infotech/code-intel 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +552 -117
- package/dist/cli/main.js +4140 -1400
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +71 -13
- package/dist/index.js +1276 -811
- package/dist/index.js.map +1 -1
- package/dist/web/assets/{es-J7AmFCht.js → es-DIfCC5I3.js} +1 -1
- package/dist/web/assets/index-QSOOiRQm.js +352 -0
- package/dist/web/assets/index-XjZQJMiV.css +2 -0
- package/dist/web/index.html +17 -11
- package/package.json +1 -1
- package/dist/web/assets/index-DSIgTcZc.css +0 -2
- package/dist/web/assets/index-upRm-kxQ.js +0 -348
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,8 +6,14 @@ 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';
|
|
14
|
-
import
|
|
15
|
-
import
|
|
9
|
+
import fs25, { existsSync } from 'fs';
|
|
10
|
+
import path32 from 'path';
|
|
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 { execSync } from 'child_process';
|
|
16
|
+
import Database2 from 'better-sqlite3';
|
|
16
17
|
import bcrypt from 'bcrypt';
|
|
17
18
|
import crypto5 from 'crypto';
|
|
18
19
|
import { v4 } from 'uuid';
|
|
@@ -24,7 +25,6 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
24
25
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
26
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
26
27
|
import { Database, Connection } from '@ladybugdb/core';
|
|
27
|
-
import { execSync } from 'child_process';
|
|
28
28
|
import express from 'express';
|
|
29
29
|
import compression from 'compression';
|
|
30
30
|
import cors from 'cors';
|
|
@@ -68,218 +68,551 @@ var init_id_generator = __esm({
|
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
// src/
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
// src/observability/tracing.ts
|
|
72
|
+
var tracing_exports = {};
|
|
73
|
+
__export(tracing_exports, {
|
|
74
|
+
SpanStatusCode: () => SpanStatusCode,
|
|
75
|
+
context: () => context,
|
|
76
|
+
getActiveTraceContext: () => getActiveTraceContext,
|
|
77
|
+
getTracer: () => getTracer,
|
|
78
|
+
initTracing: () => initTracing,
|
|
79
|
+
isTracingEnabled: () => isTracingEnabled,
|
|
80
|
+
sanitizeAttrs: () => sanitizeAttrs,
|
|
81
|
+
shutdownTracing: () => shutdownTracing,
|
|
82
|
+
trace: () => trace,
|
|
83
|
+
withSpan: () => withSpan
|
|
75
84
|
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
function detectLanguage(filePath) {
|
|
79
|
-
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
80
|
-
return EXTENSION_MAP[ext] ?? null;
|
|
85
|
+
function isTracingEnabled() {
|
|
86
|
+
return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
|
|
81
87
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
88
|
+
function initTracing() {
|
|
89
|
+
if (!isTracingEnabled()) return;
|
|
90
|
+
if (_sdk) return;
|
|
91
|
+
const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
|
|
92
|
+
const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
|
|
93
|
+
const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
|
|
94
|
+
const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
|
|
95
|
+
_sdk = new NodeSDK({
|
|
96
|
+
resource: resourceFromAttributes({
|
|
97
|
+
[SEMRESATTRS_SERVICE_NAME]: serviceName,
|
|
98
|
+
[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
|
|
99
|
+
}),
|
|
100
|
+
traceExporter: exporter,
|
|
101
|
+
instrumentations: [
|
|
102
|
+
getNodeAutoInstrumentations({
|
|
103
|
+
// Disable noisy file-system instrumentation
|
|
104
|
+
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
_sdk.start();
|
|
84
109
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
EXTENSION_MAP = {
|
|
90
|
-
".ts": "typescript" /* TypeScript */,
|
|
91
|
-
".tsx": "typescript" /* TypeScript */,
|
|
92
|
-
".mts": "typescript" /* TypeScript */,
|
|
93
|
-
".cts": "typescript" /* TypeScript */,
|
|
94
|
-
".js": "javascript" /* JavaScript */,
|
|
95
|
-
".jsx": "javascript" /* JavaScript */,
|
|
96
|
-
".mjs": "javascript" /* JavaScript */,
|
|
97
|
-
".cjs": "javascript" /* JavaScript */,
|
|
98
|
-
".py": "python" /* Python */,
|
|
99
|
-
".pyi": "python" /* Python */,
|
|
100
|
-
".java": "java" /* Java */,
|
|
101
|
-
".go": "go" /* Go */,
|
|
102
|
-
".c": "c" /* C */,
|
|
103
|
-
".h": "c" /* C */,
|
|
104
|
-
".cpp": "cpp" /* Cpp */,
|
|
105
|
-
".cxx": "cpp" /* Cpp */,
|
|
106
|
-
".cc": "cpp" /* Cpp */,
|
|
107
|
-
".hpp": "cpp" /* Cpp */,
|
|
108
|
-
".hxx": "cpp" /* Cpp */,
|
|
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 */
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// src/shared/index.ts
|
|
122
|
-
var init_shared = __esm({
|
|
123
|
-
"src/shared/index.ts"() {
|
|
124
|
-
init_languages();
|
|
125
|
-
init_detection();
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
function findBundledWasmDir() {
|
|
129
|
-
const fileDir = path31.dirname(fileURLToPath(import.meta.url));
|
|
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;
|
|
110
|
+
async function shutdownTracing() {
|
|
111
|
+
if (_sdk) {
|
|
112
|
+
await _sdk.shutdown();
|
|
113
|
+
_sdk = null;
|
|
138
114
|
}
|
|
139
|
-
return candidates[0];
|
|
140
115
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
|
|
147
|
-
["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
|
|
148
|
-
["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
|
|
149
|
-
["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
|
|
150
|
-
["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
|
|
151
|
-
["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
|
|
152
|
-
["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
|
|
153
|
-
["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
|
|
154
|
-
// These are optional dependencies; their packages may or may not include
|
|
155
|
-
// a WASM. If require.resolve fails we fall back to the bundled wasm/.
|
|
156
|
-
["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
|
|
157
|
-
["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
|
|
158
|
-
["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
|
|
159
|
-
};
|
|
160
|
-
const BUNDLED_WASM_MAP = {
|
|
161
|
-
["swift" /* Swift */]: "tree-sitter-swift.wasm",
|
|
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) {
|
|
116
|
+
function getTracer() {
|
|
117
|
+
return trace.getTracer(TRACER_NAME);
|
|
118
|
+
}
|
|
119
|
+
async function withSpan(name, attrs, fn) {
|
|
120
|
+
return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
|
|
167
121
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
122
|
+
const result = await fn(span);
|
|
123
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
124
|
+
return result;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
span.setStatus({
|
|
127
|
+
code: SpanStatusCode.ERROR,
|
|
128
|
+
message: err instanceof Error ? err.message : String(err)
|
|
129
|
+
});
|
|
130
|
+
span.recordException(err);
|
|
131
|
+
throw err;
|
|
132
|
+
} finally {
|
|
133
|
+
span.end();
|
|
170
134
|
}
|
|
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
|
-
}
|
|
135
|
+
});
|
|
201
136
|
}
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
parser = new Parser();
|
|
208
|
-
parserCache.set(lang, parser);
|
|
137
|
+
function sanitizeAttrs(attrs) {
|
|
138
|
+
const safe = {};
|
|
139
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
140
|
+
if (BLOCKED_ATTR_KEYS.test(k)) continue;
|
|
141
|
+
safe[k] = v;
|
|
209
142
|
}
|
|
210
|
-
|
|
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);
|
|
143
|
+
return safe;
|
|
217
144
|
}
|
|
218
|
-
|
|
219
|
-
|
|
145
|
+
function getActiveTraceContext() {
|
|
146
|
+
const span = trace.getActiveSpan();
|
|
147
|
+
if (!span) return { traceId: "", spanId: "" };
|
|
148
|
+
const ctx = span.spanContext();
|
|
149
|
+
return { traceId: ctx.traceId, spanId: ctx.spanId };
|
|
220
150
|
}
|
|
221
|
-
var
|
|
222
|
-
var
|
|
223
|
-
"src/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
initPromise = null;
|
|
228
|
-
languageCache = /* @__PURE__ */ new Map();
|
|
229
|
-
parserCache = /* @__PURE__ */ new Map();
|
|
151
|
+
var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
|
|
152
|
+
var init_tracing = __esm({
|
|
153
|
+
"src/observability/tracing.ts"() {
|
|
154
|
+
_sdk = null;
|
|
155
|
+
TRACER_NAME = "code-intel";
|
|
156
|
+
BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
|
|
230
157
|
}
|
|
231
158
|
});
|
|
232
|
-
function
|
|
233
|
-
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
return q;
|
|
159
|
+
function getActiveTraceCtx() {
|
|
160
|
+
if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
|
|
161
|
+
if (_getTraceCtx) return _getTraceCtx();
|
|
162
|
+
_getTraceCtx = "pending";
|
|
163
|
+
Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
|
|
164
|
+
_getTraceCtx = mod.getActiveTraceContext;
|
|
165
|
+
}).catch(() => {
|
|
166
|
+
_getTraceCtx = null;
|
|
167
|
+
});
|
|
168
|
+
return { traceId: "", spanId: "" };
|
|
244
169
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
170
|
+
var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
|
|
171
|
+
var init_logger = __esm({
|
|
172
|
+
"src/shared/logger.ts"() {
|
|
173
|
+
_getTraceCtx = null;
|
|
174
|
+
SENSITIVE_KEYS = [
|
|
175
|
+
"password",
|
|
176
|
+
"passwd",
|
|
177
|
+
"pass",
|
|
178
|
+
"pwd",
|
|
179
|
+
"secret",
|
|
180
|
+
"secretkey",
|
|
181
|
+
"secret_key",
|
|
182
|
+
"secretaccesskey",
|
|
183
|
+
"accesskeyid",
|
|
184
|
+
"credentials",
|
|
185
|
+
"auth",
|
|
186
|
+
"authentication",
|
|
187
|
+
"login",
|
|
188
|
+
"api_key",
|
|
189
|
+
"apikey",
|
|
190
|
+
"api",
|
|
191
|
+
"access_key",
|
|
192
|
+
"access_token",
|
|
193
|
+
"accesskey",
|
|
194
|
+
"auth_key",
|
|
195
|
+
"auth_token",
|
|
196
|
+
"authkey",
|
|
197
|
+
"token",
|
|
198
|
+
"jwt",
|
|
199
|
+
"bearer_token",
|
|
200
|
+
"refresh_token",
|
|
201
|
+
"session_token",
|
|
202
|
+
"session_key",
|
|
203
|
+
"oauth_token",
|
|
204
|
+
"connection_string",
|
|
205
|
+
"conn_string",
|
|
206
|
+
"db_uri",
|
|
207
|
+
"db_url",
|
|
208
|
+
"database_url",
|
|
209
|
+
"mongodb_uri",
|
|
210
|
+
"mysql_uri",
|
|
211
|
+
"postgres_uri",
|
|
212
|
+
"sql_uri",
|
|
213
|
+
"db_username",
|
|
214
|
+
"db_password",
|
|
215
|
+
"db_host",
|
|
216
|
+
"db_port",
|
|
217
|
+
"db_name",
|
|
218
|
+
"encryption_key",
|
|
219
|
+
"crypto_key",
|
|
220
|
+
"private_key",
|
|
221
|
+
"public_key",
|
|
222
|
+
"ssl_key",
|
|
223
|
+
"ssh_key",
|
|
224
|
+
"pgp_key",
|
|
225
|
+
"rsa_key",
|
|
226
|
+
"aes_key",
|
|
227
|
+
"email",
|
|
228
|
+
"phone",
|
|
229
|
+
"telephone",
|
|
230
|
+
"mobile",
|
|
231
|
+
"ssn",
|
|
232
|
+
"social_security",
|
|
233
|
+
"credit_card",
|
|
234
|
+
"cc_number",
|
|
235
|
+
"card_number",
|
|
236
|
+
"cvv",
|
|
237
|
+
"expiry_date",
|
|
238
|
+
"birth_date",
|
|
239
|
+
"dob",
|
|
240
|
+
"address",
|
|
241
|
+
"zip_code",
|
|
242
|
+
"postal_code",
|
|
243
|
+
"bank_account",
|
|
244
|
+
"iban",
|
|
245
|
+
"swift_code",
|
|
246
|
+
"routing_number",
|
|
247
|
+
"tax_id",
|
|
248
|
+
"vat_number",
|
|
249
|
+
"financial_id",
|
|
250
|
+
"certificate",
|
|
251
|
+
"client_cert",
|
|
252
|
+
"server_cert",
|
|
253
|
+
"ca_cert",
|
|
254
|
+
"aws_key",
|
|
255
|
+
"aws_secret",
|
|
256
|
+
"azure_key",
|
|
257
|
+
"gcp_key",
|
|
258
|
+
"s3_key",
|
|
259
|
+
"cloudinary_key",
|
|
260
|
+
"stripe_key",
|
|
261
|
+
"paypal_key",
|
|
262
|
+
"twilio_key",
|
|
263
|
+
"app_secret",
|
|
264
|
+
"client_secret",
|
|
265
|
+
"consumer_secret",
|
|
266
|
+
"encryption_secret",
|
|
267
|
+
"master_key",
|
|
268
|
+
"root_password",
|
|
269
|
+
"admin_password",
|
|
270
|
+
"config_secret",
|
|
271
|
+
"env_secret",
|
|
272
|
+
"deploy_key",
|
|
273
|
+
"ci_key",
|
|
274
|
+
"session_id",
|
|
275
|
+
"cookie_secret",
|
|
276
|
+
"csrf_token",
|
|
277
|
+
"xsrf_token",
|
|
278
|
+
"license_key",
|
|
279
|
+
"product_key",
|
|
280
|
+
"serial_number",
|
|
281
|
+
"activation_code"
|
|
282
|
+
];
|
|
283
|
+
SENSITIVE_PATTERNS = [
|
|
284
|
+
/(?:password|passwd|secret|api_key|access_token|auth_token|token)\s*[:=]\s*([^\s,]+)/gi,
|
|
285
|
+
/\b\d{16}\b/gi,
|
|
286
|
+
/\b\d{3}-\d{2}-\d{4}\b/gi,
|
|
287
|
+
/\b[A-Za-z0-9]{32}\b/gi,
|
|
288
|
+
/\b[A-Za-z0-9_-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b/gi,
|
|
289
|
+
/\b\d{10}\b/gi,
|
|
290
|
+
/\b[A-Za-z0-9]{64}\b/gi,
|
|
291
|
+
/(?:connection_string|db_uri|db_url|mongodb_uri)\s*[:=]\s*([^\s,]+)/gi,
|
|
292
|
+
/(?:apikey|api_key|auth_key)\s*[:=]\s*([^\s,]+)/gi,
|
|
293
|
+
/(?:bearer\s+)([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)/gi
|
|
294
|
+
];
|
|
295
|
+
SENSITIVE_KEYS_REGEX = new RegExp(`^(${SENSITIVE_KEYS.join("|")})$`, "i");
|
|
296
|
+
Logger = class _Logger {
|
|
297
|
+
static instance = null;
|
|
298
|
+
static maskSensitiveData(value) {
|
|
299
|
+
if (typeof value === "string" && value.length > 5) {
|
|
300
|
+
const firstChar = value.at(0);
|
|
301
|
+
const lastChar = value.at(-1);
|
|
302
|
+
return firstChar + "*".repeat(value.length - 2) + lastChar;
|
|
303
|
+
}
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
static maskSensitive(message, args = []) {
|
|
307
|
+
const maskString = (input) => {
|
|
308
|
+
if (typeof input !== "string") return input;
|
|
309
|
+
return SENSITIVE_PATTERNS.reduce((str, pattern) => {
|
|
310
|
+
return str.replace(
|
|
311
|
+
pattern,
|
|
312
|
+
(match, value) => value ? match.replace(value, _Logger.maskSensitiveData(value)) : match
|
|
313
|
+
);
|
|
314
|
+
}, input);
|
|
315
|
+
};
|
|
316
|
+
const deepMask = (obj) => {
|
|
317
|
+
if (typeof obj === "string") return maskString(obj);
|
|
318
|
+
if (Array.isArray(obj)) return obj.map((item) => deepMask(item));
|
|
319
|
+
if (typeof obj === "object" && obj !== null) {
|
|
320
|
+
return Object.entries(obj).reduce(
|
|
321
|
+
(acc, [key, value]) => {
|
|
322
|
+
if (value === void 0) return acc;
|
|
323
|
+
const isSensitiveKey = SENSITIVE_KEYS_REGEX.test(key);
|
|
324
|
+
acc[key] = isSensitiveKey && typeof value === "string" ? _Logger.maskSensitiveData(value) : deepMask(value);
|
|
325
|
+
return acc;
|
|
326
|
+
},
|
|
327
|
+
{}
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
return obj;
|
|
331
|
+
};
|
|
332
|
+
return {
|
|
333
|
+
maskedMessage: maskString(message),
|
|
334
|
+
maskedArgs: args.map((arg) => deepMask(arg))
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** Global log directory: ~/.code-intel/logs */
|
|
338
|
+
static LOG_DIR = path32.join(os13.homedir(), ".code-intel", "logs");
|
|
339
|
+
static getLogger() {
|
|
340
|
+
if (!_Logger.instance) {
|
|
341
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
342
|
+
const logLevel = process.env.LOG_LEVEL ?? "info";
|
|
343
|
+
const transports = [];
|
|
344
|
+
transports.push(new winston.transports.Console());
|
|
345
|
+
if (!isProduction) {
|
|
346
|
+
try {
|
|
347
|
+
if (!fs25.existsSync(_Logger.LOG_DIR)) {
|
|
348
|
+
fs25.mkdirSync(_Logger.LOG_DIR, { recursive: true });
|
|
349
|
+
}
|
|
350
|
+
transports.push(
|
|
351
|
+
new DailyRotateFile({
|
|
352
|
+
filename: path32.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
|
|
353
|
+
datePattern: "YYYY-MM-DD",
|
|
354
|
+
maxSize: "20m",
|
|
355
|
+
maxFiles: "14d"
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
} catch {
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
_Logger.instance = winston.createLogger({
|
|
362
|
+
level: logLevel,
|
|
363
|
+
format: winston.format.combine(
|
|
364
|
+
winston.format.timestamp(),
|
|
365
|
+
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
366
|
+
const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
|
|
367
|
+
const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
|
|
368
|
+
const formattedArgs = maskedArgs.map(
|
|
369
|
+
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
370
|
+
);
|
|
371
|
+
const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
|
|
372
|
+
let traceCtx = "";
|
|
373
|
+
try {
|
|
374
|
+
const { traceId, spanId } = getActiveTraceCtx();
|
|
375
|
+
if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
|
|
379
|
+
})
|
|
380
|
+
),
|
|
381
|
+
transports
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return _Logger.instance;
|
|
385
|
+
}
|
|
386
|
+
static info(message, ...args) {
|
|
387
|
+
_Logger.getLogger().info(message, ...args);
|
|
388
|
+
}
|
|
389
|
+
static warn(message, ...args) {
|
|
390
|
+
_Logger.getLogger().warn(message, ...args);
|
|
391
|
+
}
|
|
392
|
+
static error(message, ...args) {
|
|
393
|
+
_Logger.getLogger().error(message, ...args);
|
|
394
|
+
}
|
|
395
|
+
static debug(message, ...args) {
|
|
396
|
+
_Logger.getLogger().debug(message, ...args);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
logger_default = Logger;
|
|
400
|
+
Logger.getLogger();
|
|
257
401
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return raw.map((m) => ({
|
|
264
|
-
patternIndex: m.patternIndex,
|
|
265
|
-
captures: m.captures.map((c) => ({
|
|
266
|
-
name: c.name,
|
|
267
|
-
node: c.node,
|
|
268
|
-
text: c.node.text
|
|
269
|
-
}))
|
|
270
|
-
}));
|
|
271
|
-
}
|
|
272
|
-
var _queryCache;
|
|
273
|
-
var init_query_runner = __esm({
|
|
274
|
-
"src/parsing/query-runner.ts"() {
|
|
275
|
-
_queryCache = /* @__PURE__ */ new WeakMap();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// src/shared/languages.ts
|
|
405
|
+
var init_languages = __esm({
|
|
406
|
+
"src/shared/languages.ts"() {
|
|
276
407
|
}
|
|
277
408
|
});
|
|
278
409
|
|
|
279
|
-
// src/
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
410
|
+
// src/shared/detection.ts
|
|
411
|
+
function detectLanguage(filePath) {
|
|
412
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
413
|
+
return EXTENSION_MAP[ext] ?? null;
|
|
414
|
+
}
|
|
415
|
+
function getSupportedExtensions() {
|
|
416
|
+
return Object.keys(EXTENSION_MAP);
|
|
417
|
+
}
|
|
418
|
+
var EXTENSION_MAP;
|
|
419
|
+
var init_detection = __esm({
|
|
420
|
+
"src/shared/detection.ts"() {
|
|
421
|
+
init_languages();
|
|
422
|
+
EXTENSION_MAP = {
|
|
423
|
+
".ts": "typescript" /* TypeScript */,
|
|
424
|
+
".tsx": "typescript" /* TypeScript */,
|
|
425
|
+
".mts": "typescript" /* TypeScript */,
|
|
426
|
+
".cts": "typescript" /* TypeScript */,
|
|
427
|
+
".js": "javascript" /* JavaScript */,
|
|
428
|
+
".jsx": "javascript" /* JavaScript */,
|
|
429
|
+
".mjs": "javascript" /* JavaScript */,
|
|
430
|
+
".cjs": "javascript" /* JavaScript */,
|
|
431
|
+
".py": "python" /* Python */,
|
|
432
|
+
".pyi": "python" /* Python */,
|
|
433
|
+
".java": "java" /* Java */,
|
|
434
|
+
".go": "go" /* Go */,
|
|
435
|
+
".c": "c" /* C */,
|
|
436
|
+
".h": "c" /* C */,
|
|
437
|
+
".cpp": "cpp" /* Cpp */,
|
|
438
|
+
".cxx": "cpp" /* Cpp */,
|
|
439
|
+
".cc": "cpp" /* Cpp */,
|
|
440
|
+
".hpp": "cpp" /* Cpp */,
|
|
441
|
+
".hxx": "cpp" /* Cpp */,
|
|
442
|
+
".cs": "csharp" /* CSharp */,
|
|
443
|
+
".rs": "rust" /* Rust */,
|
|
444
|
+
".php": "php" /* PHP */,
|
|
445
|
+
".kt": "kotlin" /* Kotlin */,
|
|
446
|
+
".kts": "kotlin" /* Kotlin */,
|
|
447
|
+
".rb": "ruby" /* Ruby */,
|
|
448
|
+
".swift": "swift" /* Swift */,
|
|
449
|
+
".dart": "dart" /* Dart */
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// src/shared/index.ts
|
|
455
|
+
var init_shared = __esm({
|
|
456
|
+
"src/shared/index.ts"() {
|
|
457
|
+
init_languages();
|
|
458
|
+
init_detection();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
function findBundledWasmDir() {
|
|
462
|
+
const fileDir = path32.dirname(fileURLToPath(import.meta.url));
|
|
463
|
+
const candidates = [
|
|
464
|
+
path32.join(fileDir, "wasm"),
|
|
465
|
+
// dist/index.js → dist/wasm/
|
|
466
|
+
path32.join(fileDir, "../wasm")
|
|
467
|
+
// dist/cli/main.js → dist/wasm/
|
|
468
|
+
];
|
|
469
|
+
for (const candidate of candidates) {
|
|
470
|
+
if (existsSync(candidate)) return candidate;
|
|
471
|
+
}
|
|
472
|
+
return candidates[0];
|
|
473
|
+
}
|
|
474
|
+
function wasmPath(lang) {
|
|
475
|
+
const WASM_PACKAGE_MAP = {
|
|
476
|
+
["typescript" /* TypeScript */]: "tree-sitter-typescript/tree-sitter-typescript.wasm",
|
|
477
|
+
["javascript" /* JavaScript */]: "tree-sitter-javascript/tree-sitter-javascript.wasm",
|
|
478
|
+
["python" /* Python */]: "tree-sitter-python/tree-sitter-python.wasm",
|
|
479
|
+
["java" /* Java */]: "tree-sitter-java/tree-sitter-java.wasm",
|
|
480
|
+
["go" /* Go */]: "tree-sitter-go/tree-sitter-go.wasm",
|
|
481
|
+
["c" /* C */]: "tree-sitter-c/tree-sitter-c.wasm",
|
|
482
|
+
["cpp" /* Cpp */]: "tree-sitter-cpp/tree-sitter-cpp.wasm",
|
|
483
|
+
["csharp" /* CSharp */]: "tree-sitter-c-sharp/tree-sitter-c_sharp.wasm",
|
|
484
|
+
["rust" /* Rust */]: "tree-sitter-rust/tree-sitter-rust.wasm",
|
|
485
|
+
["php" /* PHP */]: "tree-sitter-php/tree-sitter-php.wasm",
|
|
486
|
+
["ruby" /* Ruby */]: "tree-sitter-ruby/tree-sitter-ruby.wasm",
|
|
487
|
+
// These are optional dependencies; their packages may or may not include
|
|
488
|
+
// a WASM. If require.resolve fails we fall back to the bundled wasm/.
|
|
489
|
+
["swift" /* Swift */]: "tree-sitter-swift/tree-sitter-swift.wasm",
|
|
490
|
+
["kotlin" /* Kotlin */]: "tree-sitter-kotlin/tree-sitter-kotlin.wasm",
|
|
491
|
+
["dart" /* Dart */]: "tree-sitter-dart/tree-sitter-dart.wasm"
|
|
492
|
+
};
|
|
493
|
+
const BUNDLED_WASM_MAP = {
|
|
494
|
+
["swift" /* Swift */]: "tree-sitter-swift.wasm",
|
|
495
|
+
["kotlin" /* Kotlin */]: "tree-sitter-kotlin.wasm",
|
|
496
|
+
["dart" /* Dart */]: "tree-sitter-dart.wasm"
|
|
497
|
+
};
|
|
498
|
+
const relative = WASM_PACKAGE_MAP[lang];
|
|
499
|
+
if (relative) {
|
|
500
|
+
try {
|
|
501
|
+
return _require.resolve(relative);
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const bundled = BUNDLED_WASM_MAP[lang];
|
|
506
|
+
if (bundled) {
|
|
507
|
+
const bundledPath = path32.join(_bundledWasmDir, bundled);
|
|
508
|
+
if (existsSync(bundledPath)) return bundledPath;
|
|
509
|
+
}
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
async function initParser() {
|
|
513
|
+
if (!initPromise) {
|
|
514
|
+
initPromise = Parser.init();
|
|
515
|
+
}
|
|
516
|
+
return initPromise;
|
|
517
|
+
}
|
|
518
|
+
async function getLanguage(lang) {
|
|
519
|
+
if (languageCache.has(lang)) return languageCache.get(lang);
|
|
520
|
+
const path33 = wasmPath(lang);
|
|
521
|
+
if (!path33) {
|
|
522
|
+
languageCache.set(lang, null);
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
await initParser();
|
|
527
|
+
const language = await Language.load(path33);
|
|
528
|
+
languageCache.set(lang, language);
|
|
529
|
+
return language;
|
|
530
|
+
} catch {
|
|
531
|
+
languageCache.set(lang, null);
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function getParser(lang) {
|
|
536
|
+
const language = await getLanguage(lang);
|
|
537
|
+
if (!language) return null;
|
|
538
|
+
let parser = parserCache.get(lang);
|
|
539
|
+
if (!parser) {
|
|
540
|
+
parser = new Parser();
|
|
541
|
+
parserCache.set(lang, parser);
|
|
542
|
+
}
|
|
543
|
+
parser.setLanguage(language);
|
|
544
|
+
return parser;
|
|
545
|
+
}
|
|
546
|
+
async function parseSource(lang, source) {
|
|
547
|
+
const parser = await getParser(lang);
|
|
548
|
+
if (!parser) return null;
|
|
549
|
+
return parser.parse(source);
|
|
550
|
+
}
|
|
551
|
+
async function isTreeSitterAvailable(lang) {
|
|
552
|
+
return await getLanguage(lang) !== null;
|
|
553
|
+
}
|
|
554
|
+
var _require, _bundledWasmDir, initPromise, languageCache, parserCache;
|
|
555
|
+
var init_parser_manager = __esm({
|
|
556
|
+
"src/parsing/parser-manager.ts"() {
|
|
557
|
+
init_shared();
|
|
558
|
+
_require = createRequire(import.meta.url);
|
|
559
|
+
_bundledWasmDir = findBundledWasmDir();
|
|
560
|
+
initPromise = null;
|
|
561
|
+
languageCache = /* @__PURE__ */ new Map();
|
|
562
|
+
parserCache = /* @__PURE__ */ new Map();
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
function getOrCompileQuery(language, querySource) {
|
|
566
|
+
let langMap = _queryCache.get(language);
|
|
567
|
+
if (!langMap) {
|
|
568
|
+
langMap = /* @__PURE__ */ new Map();
|
|
569
|
+
_queryCache.set(language, langMap);
|
|
570
|
+
}
|
|
571
|
+
let q = langMap.get(querySource);
|
|
572
|
+
if (!q) {
|
|
573
|
+
q = new Query(language, querySource);
|
|
574
|
+
langMap.set(querySource, q);
|
|
575
|
+
}
|
|
576
|
+
return q;
|
|
577
|
+
}
|
|
578
|
+
function runQuery(tree, language, querySource) {
|
|
579
|
+
const query = getOrCompileQuery(language, querySource);
|
|
580
|
+
const matches = query.matches(tree.rootNode);
|
|
581
|
+
const captures = [];
|
|
582
|
+
for (const match of matches) {
|
|
583
|
+
for (const capture of match.captures) {
|
|
584
|
+
captures.push({
|
|
585
|
+
name: capture.name,
|
|
586
|
+
node: capture.node,
|
|
587
|
+
text: capture.node.text
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return captures;
|
|
592
|
+
}
|
|
593
|
+
function runQueryMatches(tree, language, querySource) {
|
|
594
|
+
const query = getOrCompileQuery(language, querySource);
|
|
595
|
+
const raw = query.matches(tree.rootNode);
|
|
596
|
+
return raw.map((m) => ({
|
|
597
|
+
patternIndex: m.patternIndex,
|
|
598
|
+
captures: m.captures.map((c) => ({
|
|
599
|
+
name: c.name,
|
|
600
|
+
node: c.node,
|
|
601
|
+
text: c.node.text
|
|
602
|
+
}))
|
|
603
|
+
}));
|
|
604
|
+
}
|
|
605
|
+
var _queryCache;
|
|
606
|
+
var init_query_runner = __esm({
|
|
607
|
+
"src/parsing/query-runner.ts"() {
|
|
608
|
+
_queryCache = /* @__PURE__ */ new WeakMap();
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// src/parsing/queries/typescript.ts
|
|
613
|
+
var typescriptQueries;
|
|
614
|
+
var init_typescript = __esm({
|
|
615
|
+
"src/parsing/queries/typescript.ts"() {
|
|
283
616
|
typescriptQueries = `
|
|
284
617
|
;; Class declaration
|
|
285
618
|
(class_declaration
|
|
@@ -890,341 +1223,8 @@ var init_swift = __esm({
|
|
|
890
1223
|
;; Property declaration
|
|
891
1224
|
(property_declaration
|
|
892
1225
|
(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;
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
function getActiveTraceCtx() {
|
|
987
|
-
if (_getTraceCtx === "pending") return { traceId: "", spanId: "" };
|
|
988
|
-
if (_getTraceCtx) return _getTraceCtx();
|
|
989
|
-
_getTraceCtx = "pending";
|
|
990
|
-
Promise.resolve().then(() => (init_tracing(), tracing_exports)).then((mod) => {
|
|
991
|
-
_getTraceCtx = mod.getActiveTraceContext;
|
|
992
|
-
}).catch(() => {
|
|
993
|
-
_getTraceCtx = null;
|
|
994
|
-
});
|
|
995
|
-
return { traceId: "", spanId: "" };
|
|
996
|
-
}
|
|
997
|
-
var _getTraceCtx, SENSITIVE_KEYS, SENSITIVE_PATTERNS, SENSITIVE_KEYS_REGEX, Logger, logger_default;
|
|
998
|
-
var init_logger = __esm({
|
|
999
|
-
"src/shared/logger.ts"() {
|
|
1000
|
-
_getTraceCtx = null;
|
|
1001
|
-
SENSITIVE_KEYS = [
|
|
1002
|
-
"password",
|
|
1003
|
-
"passwd",
|
|
1004
|
-
"pass",
|
|
1005
|
-
"pwd",
|
|
1006
|
-
"secret",
|
|
1007
|
-
"secretkey",
|
|
1008
|
-
"secret_key",
|
|
1009
|
-
"secretaccesskey",
|
|
1010
|
-
"accesskeyid",
|
|
1011
|
-
"credentials",
|
|
1012
|
-
"auth",
|
|
1013
|
-
"authentication",
|
|
1014
|
-
"login",
|
|
1015
|
-
"api_key",
|
|
1016
|
-
"apikey",
|
|
1017
|
-
"api",
|
|
1018
|
-
"access_key",
|
|
1019
|
-
"access_token",
|
|
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(os12.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();
|
|
1226
|
+
(simple_identifier) @def.property.name)) @def.property
|
|
1227
|
+
`;
|
|
1228
1228
|
}
|
|
1229
1229
|
});
|
|
1230
1230
|
|
|
@@ -1961,7 +1961,7 @@ var init_parse_phase = __esm({
|
|
|
1961
1961
|
const batch = filePaths.slice(i, i + CONCURRENCY);
|
|
1962
1962
|
await Promise.all(batch.map(async (filePath) => {
|
|
1963
1963
|
try {
|
|
1964
|
-
const source = await
|
|
1964
|
+
const source = await fs25.promises.readFile(filePath, "utf-8");
|
|
1965
1965
|
context2.fileCache.set(filePath, source);
|
|
1966
1966
|
} catch {
|
|
1967
1967
|
}
|
|
@@ -1974,14 +1974,14 @@ var init_parse_phase = __esm({
|
|
|
1974
1974
|
const lang = detectLanguage(filePath);
|
|
1975
1975
|
if (!lang) {
|
|
1976
1976
|
if (context2.verbose) {
|
|
1977
|
-
const relativePath2 =
|
|
1977
|
+
const relativePath2 = path32.relative(context2.workspaceRoot, filePath);
|
|
1978
1978
|
logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
|
|
1979
1979
|
}
|
|
1980
1980
|
continue;
|
|
1981
1981
|
}
|
|
1982
1982
|
const source = context2.fileCache.get(filePath);
|
|
1983
1983
|
if (!source) continue;
|
|
1984
|
-
const relativePath =
|
|
1984
|
+
const relativePath = path32.relative(context2.workspaceRoot, filePath);
|
|
1985
1985
|
const fileNodeId = generateNodeId("file", relativePath, relativePath);
|
|
1986
1986
|
const fileNode = context2.graph.getNode(fileNodeId);
|
|
1987
1987
|
if (fileNode) {
|
|
@@ -2221,11 +2221,11 @@ var init_resolve_phase = __esm({
|
|
|
2221
2221
|
let heritageEdges = 0;
|
|
2222
2222
|
const fileIndex = /* @__PURE__ */ new Map();
|
|
2223
2223
|
for (const fp of filePaths) {
|
|
2224
|
-
const rel =
|
|
2224
|
+
const rel = path32.relative(workspaceRoot, fp);
|
|
2225
2225
|
fileIndex.set(rel, fp);
|
|
2226
2226
|
const noExt = rel.replace(/\.\w+$/, "");
|
|
2227
2227
|
if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
|
|
2228
|
-
const base =
|
|
2228
|
+
const base = path32.basename(rel, path32.extname(rel));
|
|
2229
2229
|
if (!fileIndex.has(base)) fileIndex.set(base, fp);
|
|
2230
2230
|
}
|
|
2231
2231
|
const symbolIndex = /* @__PURE__ */ new Map();
|
|
@@ -2256,7 +2256,7 @@ var init_resolve_phase = __esm({
|
|
|
2256
2256
|
for (const filePath of filePaths) {
|
|
2257
2257
|
const lang = detectLanguage(filePath);
|
|
2258
2258
|
if (!lang) continue;
|
|
2259
|
-
const relativePath =
|
|
2259
|
+
const relativePath = path32.relative(workspaceRoot, filePath);
|
|
2260
2260
|
const fileNodeId = generateNodeId("file", relativePath, relativePath);
|
|
2261
2261
|
const source = fileCache.get(filePath);
|
|
2262
2262
|
if (!source) continue;
|
|
@@ -2269,13 +2269,13 @@ var init_resolve_phase = __esm({
|
|
|
2269
2269
|
let resolvedRelPath = null;
|
|
2270
2270
|
if (cleaned.startsWith(".")) {
|
|
2271
2271
|
const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
|
|
2272
|
-
const fromDir =
|
|
2272
|
+
const fromDir = path32.dirname(relativePath);
|
|
2273
2273
|
for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", "/index.ts", "/index.js"]) {
|
|
2274
|
-
const candidate =
|
|
2275
|
-
const normalized =
|
|
2274
|
+
const candidate = path32.join(fromDir, cleanedNoJs + ext);
|
|
2275
|
+
const normalized = path32.normalize(candidate);
|
|
2276
2276
|
if (fileIndex.has(normalized)) {
|
|
2277
2277
|
const absPath = fileIndex.get(normalized);
|
|
2278
|
-
resolvedRelPath =
|
|
2278
|
+
resolvedRelPath = path32.relative(workspaceRoot, absPath);
|
|
2279
2279
|
break;
|
|
2280
2280
|
}
|
|
2281
2281
|
}
|
|
@@ -2589,27 +2589,27 @@ __export(group_registry_exports, {
|
|
|
2589
2589
|
saveSyncResult: () => saveSyncResult
|
|
2590
2590
|
});
|
|
2591
2591
|
function groupFile(name) {
|
|
2592
|
-
return
|
|
2592
|
+
return path32.join(GROUPS_DIR, `${name}.json`);
|
|
2593
2593
|
}
|
|
2594
2594
|
function loadGroup(name) {
|
|
2595
2595
|
try {
|
|
2596
|
-
return JSON.parse(
|
|
2596
|
+
return JSON.parse(fs25.readFileSync(groupFile(name), "utf-8"));
|
|
2597
2597
|
} catch {
|
|
2598
2598
|
return null;
|
|
2599
2599
|
}
|
|
2600
2600
|
}
|
|
2601
2601
|
function saveGroup(group) {
|
|
2602
|
-
|
|
2603
|
-
|
|
2602
|
+
fs25.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
2603
|
+
fs25.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
|
|
2604
2604
|
}
|
|
2605
2605
|
function listGroups() {
|
|
2606
2606
|
const groups = [];
|
|
2607
2607
|
try {
|
|
2608
|
-
for (const file of
|
|
2608
|
+
for (const file of fs25.readdirSync(GROUPS_DIR)) {
|
|
2609
2609
|
if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
|
|
2610
2610
|
try {
|
|
2611
2611
|
const g = JSON.parse(
|
|
2612
|
-
|
|
2612
|
+
fs25.readFileSync(path32.join(GROUPS_DIR, file), "utf-8")
|
|
2613
2613
|
);
|
|
2614
2614
|
groups.push(g);
|
|
2615
2615
|
} catch {
|
|
@@ -2621,16 +2621,16 @@ function listGroups() {
|
|
|
2621
2621
|
}
|
|
2622
2622
|
function deleteGroup(name) {
|
|
2623
2623
|
try {
|
|
2624
|
-
|
|
2624
|
+
fs25.unlinkSync(groupFile(name));
|
|
2625
2625
|
} catch {
|
|
2626
2626
|
}
|
|
2627
2627
|
try {
|
|
2628
|
-
|
|
2628
|
+
fs25.unlinkSync(path32.join(GROUPS_DIR, `${name}.sync.json`));
|
|
2629
2629
|
} catch {
|
|
2630
2630
|
}
|
|
2631
2631
|
}
|
|
2632
2632
|
function groupExists(name) {
|
|
2633
|
-
return
|
|
2633
|
+
return fs25.existsSync(groupFile(name));
|
|
2634
2634
|
}
|
|
2635
2635
|
function addMember(groupName, member) {
|
|
2636
2636
|
const group = loadGroup(groupName);
|
|
@@ -2656,16 +2656,16 @@ function removeMember(groupName, groupPath) {
|
|
|
2656
2656
|
return group;
|
|
2657
2657
|
}
|
|
2658
2658
|
function saveSyncResult(result) {
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2659
|
+
fs25.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
2660
|
+
fs25.writeFileSync(
|
|
2661
|
+
path32.join(GROUPS_DIR, `${result.groupName}.sync.json`),
|
|
2662
2662
|
JSON.stringify(result, null, 2) + "\n"
|
|
2663
2663
|
);
|
|
2664
2664
|
}
|
|
2665
2665
|
function loadSyncResult(groupName) {
|
|
2666
2666
|
try {
|
|
2667
2667
|
return JSON.parse(
|
|
2668
|
-
|
|
2668
|
+
fs25.readFileSync(path32.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
|
|
2669
2669
|
);
|
|
2670
2670
|
} catch {
|
|
2671
2671
|
return null;
|
|
@@ -2674,7 +2674,7 @@ function loadSyncResult(groupName) {
|
|
|
2674
2674
|
var GROUPS_DIR;
|
|
2675
2675
|
var init_group_registry = __esm({
|
|
2676
2676
|
"src/multi-repo/group-registry.ts"() {
|
|
2677
|
-
GROUPS_DIR =
|
|
2677
|
+
GROUPS_DIR = path32.join(os13.homedir(), ".code-intel", "groups");
|
|
2678
2678
|
}
|
|
2679
2679
|
});
|
|
2680
2680
|
|
|
@@ -2964,7 +2964,7 @@ __export(gql_parser_exports, {
|
|
|
2964
2964
|
function isGQLParseError(v) {
|
|
2965
2965
|
return v.type === "GQLParseError";
|
|
2966
2966
|
}
|
|
2967
|
-
function
|
|
2967
|
+
function tokenize2(input) {
|
|
2968
2968
|
const tokens = [];
|
|
2969
2969
|
let i = 0;
|
|
2970
2970
|
const len = input.length;
|
|
@@ -3059,7 +3059,7 @@ function tokenize(input) {
|
|
|
3059
3059
|
return tokens;
|
|
3060
3060
|
}
|
|
3061
3061
|
function parseGQL(input) {
|
|
3062
|
-
const tokens =
|
|
3062
|
+
const tokens = tokenize2(input.trim());
|
|
3063
3063
|
if (!Array.isArray(tokens)) return tokens;
|
|
3064
3064
|
const parser = new Parser2(tokens);
|
|
3065
3065
|
return parser.parse();
|
|
@@ -3852,7 +3852,7 @@ function isTestFile(filePath) {
|
|
|
3852
3852
|
if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
|
|
3853
3853
|
if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
|
|
3854
3854
|
if (filePath.includes("__tests__")) return true;
|
|
3855
|
-
const base =
|
|
3855
|
+
const base = path32.basename(filePath);
|
|
3856
3856
|
if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
|
|
3857
3857
|
return false;
|
|
3858
3858
|
}
|
|
@@ -3897,7 +3897,7 @@ function computeCoverage(graph, scope) {
|
|
|
3897
3897
|
}
|
|
3898
3898
|
const baseNameToTestFiles = /* @__PURE__ */ new Map();
|
|
3899
3899
|
for (const testPath of testFilePaths) {
|
|
3900
|
-
const base =
|
|
3900
|
+
const base = path32.basename(testPath);
|
|
3901
3901
|
const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
|
|
3902
3902
|
const existing = baseNameToTestFiles.get(stripped) ?? [];
|
|
3903
3903
|
existing.push(testPath);
|
|
@@ -3930,7 +3930,7 @@ function computeCoverage(graph, scope) {
|
|
|
3930
3930
|
}
|
|
3931
3931
|
}
|
|
3932
3932
|
}
|
|
3933
|
-
const nodeBase =
|
|
3933
|
+
const nodeBase = path32.basename(node.filePath).replace(/\.[^.]+$/, "");
|
|
3934
3934
|
const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
|
|
3935
3935
|
for (const tf of matchingTestFiles) {
|
|
3936
3936
|
if (!testFiles.includes(tf)) testFiles.push(tf);
|
|
@@ -4000,7 +4000,7 @@ var init_secret_scanner = __esm({
|
|
|
4000
4000
|
const ignorePatterns = [...options?.ignorePatterns ?? []];
|
|
4001
4001
|
if (options?.workspaceRoot) {
|
|
4002
4002
|
try {
|
|
4003
|
-
const raw =
|
|
4003
|
+
const raw = fs25.readFileSync(path32.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
|
|
4004
4004
|
for (const line of raw.split("\n")) {
|
|
4005
4005
|
const trimmed = line.trim();
|
|
4006
4006
|
if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
|
|
@@ -4245,12 +4245,11 @@ var init_vulnerability_detector = __esm({
|
|
|
4245
4245
|
};
|
|
4246
4246
|
}
|
|
4247
4247
|
});
|
|
4248
|
-
|
|
4249
|
-
// src/errors/codes.ts
|
|
4250
4248
|
var ErrorCodes, AppError;
|
|
4251
4249
|
var init_codes = __esm({
|
|
4252
4250
|
"src/errors/codes.ts"() {
|
|
4253
4251
|
ErrorCodes = {
|
|
4252
|
+
// Auth (CI-1xxx)
|
|
4254
4253
|
UNAUTHORIZED: "CI-1000",
|
|
4255
4254
|
FORBIDDEN: "CI-1001",
|
|
4256
4255
|
NOT_FOUND: "CI-1002",
|
|
@@ -4260,6 +4259,16 @@ var init_codes = __esm({
|
|
|
4260
4259
|
RATE_LIMIT_EXCEEDED: "CI-1100",
|
|
4261
4260
|
PAYLOAD_TOO_LARGE: "CI-1101",
|
|
4262
4261
|
INVALID_REQUEST: "CI-1200",
|
|
4262
|
+
// Config (CI-2xxx)
|
|
4263
|
+
CONFIG_INVALID: "CI-2000",
|
|
4264
|
+
CONFIG_NOT_FOUND: "CI-2001",
|
|
4265
|
+
// Analysis (CI-3xxx)
|
|
4266
|
+
ANALYSIS_FAILED: "CI-3000",
|
|
4267
|
+
// DB (CI-4xxx)
|
|
4268
|
+
DB_ERROR: "CI-4000",
|
|
4269
|
+
// Network (CI-5xxx)
|
|
4270
|
+
NETWORK_ERROR: "CI-5001",
|
|
4271
|
+
// Internal (CI-9xxx)
|
|
4263
4272
|
INTERNAL_ERROR: "CI-5000"
|
|
4264
4273
|
};
|
|
4265
4274
|
AppError = class extends Error {
|
|
@@ -4279,10 +4288,10 @@ var init_codes = __esm({
|
|
|
4279
4288
|
}
|
|
4280
4289
|
});
|
|
4281
4290
|
function secureMkdir(dir) {
|
|
4282
|
-
|
|
4291
|
+
fs25.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
4283
4292
|
if (process.platform !== "win32") {
|
|
4284
4293
|
try {
|
|
4285
|
-
|
|
4294
|
+
fs25.chmodSync(dir, SECURE_DIR_MODE);
|
|
4286
4295
|
} catch {
|
|
4287
4296
|
}
|
|
4288
4297
|
}
|
|
@@ -4290,17 +4299,17 @@ function secureMkdir(dir) {
|
|
|
4290
4299
|
function secureChmodFile(file) {
|
|
4291
4300
|
if (process.platform === "win32") return;
|
|
4292
4301
|
try {
|
|
4293
|
-
|
|
4302
|
+
fs25.chmodSync(file, SECURE_FILE_MODE);
|
|
4294
4303
|
} catch {
|
|
4295
4304
|
}
|
|
4296
4305
|
}
|
|
4297
4306
|
function tightenDbFiles(dir) {
|
|
4298
4307
|
if (process.platform === "win32") return;
|
|
4299
|
-
if (!
|
|
4300
|
-
for (const name of
|
|
4308
|
+
if (!fs25.existsSync(dir)) return;
|
|
4309
|
+
for (const name of fs25.readdirSync(dir)) {
|
|
4301
4310
|
if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
|
|
4302
4311
|
try {
|
|
4303
|
-
|
|
4312
|
+
fs25.chmodSync(path32.join(dir, name), SECURE_FILE_MODE);
|
|
4304
4313
|
} catch {
|
|
4305
4314
|
}
|
|
4306
4315
|
}
|
|
@@ -4314,7 +4323,7 @@ var init_fs_secure = __esm({
|
|
|
4314
4323
|
}
|
|
4315
4324
|
});
|
|
4316
4325
|
function getUsersDBPath() {
|
|
4317
|
-
return process.env["CODE_INTEL_USERS_DB_PATH"] ??
|
|
4326
|
+
return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "users.db");
|
|
4318
4327
|
}
|
|
4319
4328
|
function getOrCreateUsersDB() {
|
|
4320
4329
|
if (!_usersDB) {
|
|
@@ -4330,9 +4339,9 @@ var init_users_db = __esm({
|
|
|
4330
4339
|
UsersDB = class {
|
|
4331
4340
|
db;
|
|
4332
4341
|
constructor(dbPath) {
|
|
4333
|
-
const dir =
|
|
4342
|
+
const dir = path32.dirname(dbPath);
|
|
4334
4343
|
secureMkdir(dir);
|
|
4335
|
-
this.db = new
|
|
4344
|
+
this.db = new Database2(dbPath);
|
|
4336
4345
|
this.db.pragma("journal_mode = WAL");
|
|
4337
4346
|
this.db.pragma("foreign_keys = ON");
|
|
4338
4347
|
this.createTables();
|
|
@@ -4607,17 +4616,17 @@ function getScryptN() {
|
|
|
4607
4616
|
return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
|
|
4608
4617
|
}
|
|
4609
4618
|
function getSecretsPath() {
|
|
4610
|
-
return process.env["CODE_INTEL_SECRETS_PATH"] ??
|
|
4619
|
+
return process.env["CODE_INTEL_SECRETS_PATH"] ?? path32.join(os13.homedir(), ".code-intel", ".secrets");
|
|
4611
4620
|
}
|
|
4612
4621
|
function getMasterPassword() {
|
|
4613
4622
|
const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
|
|
4614
4623
|
if (fromEnv && fromEnv.length >= 16) return fromEnv;
|
|
4615
4624
|
let username = "unknown";
|
|
4616
4625
|
try {
|
|
4617
|
-
username =
|
|
4626
|
+
username = os13.userInfo().username;
|
|
4618
4627
|
} catch {
|
|
4619
4628
|
}
|
|
4620
|
-
return `code-intel-machine:${
|
|
4629
|
+
return `code-intel-machine:${os13.hostname()}:${username}:${os13.platform()}`;
|
|
4621
4630
|
}
|
|
4622
4631
|
function deriveKey(password, salt) {
|
|
4623
4632
|
return crypto5.scryptSync(password, salt, KEY_LEN, { N: getScryptN(), r: 8, p: 1 });
|
|
@@ -4638,8 +4647,8 @@ function decryptSecrets(encrypted) {
|
|
|
4638
4647
|
return JSON.parse(plaintext.toString("utf8"));
|
|
4639
4648
|
}
|
|
4640
4649
|
function loadSecrets(secretsPath = getSecretsPath()) {
|
|
4641
|
-
if (!
|
|
4642
|
-
const blob =
|
|
4650
|
+
if (!fs25.existsSync(secretsPath)) return {};
|
|
4651
|
+
const blob = fs25.readFileSync(secretsPath);
|
|
4643
4652
|
return decryptSecrets(blob);
|
|
4644
4653
|
}
|
|
4645
4654
|
function getSecret(key, secretsPath = getSecretsPath()) {
|
|
@@ -5149,6 +5158,71 @@ function createKnowledgeGraph() {
|
|
|
5149
5158
|
// src/graph/index.ts
|
|
5150
5159
|
init_id_generator();
|
|
5151
5160
|
|
|
5161
|
+
// src/storage/schema.ts
|
|
5162
|
+
var NODE_TABLE_MAP = {
|
|
5163
|
+
file: "file_nodes",
|
|
5164
|
+
directory: "dir_nodes",
|
|
5165
|
+
function: "func_nodes",
|
|
5166
|
+
class: "class_nodes",
|
|
5167
|
+
interface: "iface_nodes",
|
|
5168
|
+
method: "method_nodes",
|
|
5169
|
+
constructor: "ctor_nodes",
|
|
5170
|
+
variable: "var_nodes",
|
|
5171
|
+
property: "prop_nodes",
|
|
5172
|
+
struct: "struct_nodes",
|
|
5173
|
+
enum: "enum_nodes",
|
|
5174
|
+
trait: "trait_nodes",
|
|
5175
|
+
namespace: "ns_nodes",
|
|
5176
|
+
module: "mod_nodes",
|
|
5177
|
+
type_alias: "type_nodes",
|
|
5178
|
+
constant: "const_nodes",
|
|
5179
|
+
route: "route_nodes",
|
|
5180
|
+
cluster: "cluster_nodes",
|
|
5181
|
+
flow: "flow_nodes",
|
|
5182
|
+
vulnerability: "vuln_nodes"
|
|
5183
|
+
};
|
|
5184
|
+
var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
|
|
5185
|
+
function getCreateNodeTableDDL(tableName) {
|
|
5186
|
+
return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
|
|
5187
|
+
id STRING,
|
|
5188
|
+
name STRING,
|
|
5189
|
+
file_path STRING,
|
|
5190
|
+
start_line INT64,
|
|
5191
|
+
end_line INT64,
|
|
5192
|
+
exported BOOLEAN,
|
|
5193
|
+
content STRING,
|
|
5194
|
+
metadata STRING,
|
|
5195
|
+
PRIMARY KEY (id)
|
|
5196
|
+
)`;
|
|
5197
|
+
}
|
|
5198
|
+
function getCreateEdgeTableDDL() {
|
|
5199
|
+
const uniqueTables = ALL_NODE_TABLES;
|
|
5200
|
+
const fromToPairs = [];
|
|
5201
|
+
for (const from of uniqueTables) {
|
|
5202
|
+
for (const to of uniqueTables) {
|
|
5203
|
+
fromToPairs.push(`FROM ${from} TO ${to}`);
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
|
|
5207
|
+
${fromToPairs.join(",\n ")},
|
|
5208
|
+
kind STRING,
|
|
5209
|
+
weight DOUBLE,
|
|
5210
|
+
label STRING
|
|
5211
|
+
)`];
|
|
5212
|
+
}
|
|
5213
|
+
|
|
5214
|
+
// src/graph/lazy-knowledge-graph.ts
|
|
5215
|
+
init_logger();
|
|
5216
|
+
Object.fromEntries(
|
|
5217
|
+
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
5218
|
+
);
|
|
5219
|
+
function isLazyGraph(g) {
|
|
5220
|
+
return "lazy" in g && g.lazy === true;
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
// src/graph/compact-knowledge-graph.ts
|
|
5224
|
+
init_logger();
|
|
5225
|
+
|
|
5152
5226
|
// src/parsing/index.ts
|
|
5153
5227
|
init_parser_manager();
|
|
5154
5228
|
init_query_runner();
|
|
@@ -5206,7 +5280,7 @@ init_shared();
|
|
|
5206
5280
|
init_shared();
|
|
5207
5281
|
init_typescript();
|
|
5208
5282
|
function resolveRelative(rawPath, fromFile, workspace) {
|
|
5209
|
-
const fromDir =
|
|
5283
|
+
const fromDir = path32.dirname(fromFile);
|
|
5210
5284
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5211
5285
|
const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
|
|
5212
5286
|
const resolved = workspace.resolve(fromDir, cleaned);
|
|
@@ -5258,7 +5332,7 @@ var pythonModule = {
|
|
|
5258
5332
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5259
5333
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5260
5334
|
const parts = cleaned.split(".");
|
|
5261
|
-
const fromDir =
|
|
5335
|
+
const fromDir = path32.dirname(fromFile);
|
|
5262
5336
|
const relPath = parts.join("/");
|
|
5263
5337
|
for (const suffix of ["/__init__.py", ".py"]) {
|
|
5264
5338
|
const r = workspace.resolve(fromDir, relPath + suffix);
|
|
@@ -5337,7 +5411,7 @@ var cModule = {
|
|
|
5337
5411
|
inheritanceStrategy: "none",
|
|
5338
5412
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5339
5413
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5340
|
-
const fromDir =
|
|
5414
|
+
const fromDir = path32.dirname(fromFile);
|
|
5341
5415
|
return workspace.resolve(fromDir, cleaned);
|
|
5342
5416
|
},
|
|
5343
5417
|
isExported(_node) {
|
|
@@ -5360,7 +5434,7 @@ var cppModule = {
|
|
|
5360
5434
|
inheritanceStrategy: "depth-first",
|
|
5361
5435
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5362
5436
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5363
|
-
const fromDir =
|
|
5437
|
+
const fromDir = path32.dirname(fromFile);
|
|
5364
5438
|
return workspace.resolve(fromDir, cleaned);
|
|
5365
5439
|
},
|
|
5366
5440
|
isExported(_node) {
|
|
@@ -5522,7 +5596,7 @@ var dartModule = {
|
|
|
5522
5596
|
const pkg = cleaned.replace("package:", "");
|
|
5523
5597
|
return workspace.findByPackage(pkg);
|
|
5524
5598
|
}
|
|
5525
|
-
const fromDir =
|
|
5599
|
+
const fromDir = path32.dirname(fromFile);
|
|
5526
5600
|
return workspace.resolve(fromDir, cleaned);
|
|
5527
5601
|
},
|
|
5528
5602
|
isExported(node) {
|
|
@@ -5877,25 +5951,25 @@ function validateDAG(phases) {
|
|
|
5877
5951
|
const visiting = /* @__PURE__ */ new Set();
|
|
5878
5952
|
const visited = /* @__PURE__ */ new Set();
|
|
5879
5953
|
const phaseMap = new Map(phases.map((p) => [p.name, p]));
|
|
5880
|
-
function dfs(name,
|
|
5954
|
+
function dfs(name, path33) {
|
|
5881
5955
|
if (visiting.has(name)) {
|
|
5882
|
-
const cycleStart =
|
|
5883
|
-
const cycle =
|
|
5956
|
+
const cycleStart = path33.indexOf(name);
|
|
5957
|
+
const cycle = path33.slice(cycleStart).concat(name);
|
|
5884
5958
|
errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
|
|
5885
5959
|
return true;
|
|
5886
5960
|
}
|
|
5887
5961
|
if (visited.has(name)) return false;
|
|
5888
5962
|
visiting.add(name);
|
|
5889
|
-
|
|
5963
|
+
path33.push(name);
|
|
5890
5964
|
const phase = phaseMap.get(name);
|
|
5891
5965
|
if (phase) {
|
|
5892
5966
|
for (const dep of phase.dependencies) {
|
|
5893
|
-
if (dfs(dep,
|
|
5967
|
+
if (dfs(dep, path33)) return true;
|
|
5894
5968
|
}
|
|
5895
5969
|
}
|
|
5896
5970
|
visiting.delete(name);
|
|
5897
5971
|
visited.add(name);
|
|
5898
|
-
|
|
5972
|
+
path33.pop();
|
|
5899
5973
|
return false;
|
|
5900
5974
|
}
|
|
5901
5975
|
for (const phase of phases) {
|
|
@@ -6023,6 +6097,7 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6023
6097
|
for (const phase of sorted) {
|
|
6024
6098
|
context2.onProgress?.(phase.name, "running");
|
|
6025
6099
|
const phaseStart = Date.now();
|
|
6100
|
+
const memBefore = context2.profile ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0;
|
|
6026
6101
|
const runPhase = async () => {
|
|
6027
6102
|
const depResults = /* @__PURE__ */ new Map();
|
|
6028
6103
|
for (const dep of phase.dependencies) {
|
|
@@ -6046,6 +6121,10 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6046
6121
|
}
|
|
6047
6122
|
const durationSec = (Date.now() - phaseStart) / 1e3;
|
|
6048
6123
|
pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: result.status }, durationSec);
|
|
6124
|
+
if (memBefore !== void 0) {
|
|
6125
|
+
result.memoryBeforeMB = memBefore;
|
|
6126
|
+
result.memoryAfterMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
6127
|
+
}
|
|
6049
6128
|
results.set(phase.name, result);
|
|
6050
6129
|
context2.onProgress?.(phase.name, result.status);
|
|
6051
6130
|
if (result.status === "failed") {
|
|
@@ -6056,7 +6135,9 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6056
6135
|
const result = {
|
|
6057
6136
|
status: "failed",
|
|
6058
6137
|
duration: Date.now() - phaseStart,
|
|
6059
|
-
message: err instanceof Error ? err.message : String(err)
|
|
6138
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6139
|
+
memoryBeforeMB: memBefore,
|
|
6140
|
+
memoryAfterMB: memBefore !== void 0 ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0
|
|
6060
6141
|
};
|
|
6061
6142
|
pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: "failed" }, (Date.now() - phaseStart) / 1e3);
|
|
6062
6143
|
results.set(phase.name, result);
|
|
@@ -6108,7 +6189,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
6108
6189
|
]);
|
|
6109
6190
|
function loadIgnorePatterns(workspaceRoot) {
|
|
6110
6191
|
try {
|
|
6111
|
-
const raw =
|
|
6192
|
+
const raw = fs25.readFileSync(path32.join(workspaceRoot, ".codeintelignore"), "utf-8");
|
|
6112
6193
|
const extras = /* @__PURE__ */ new Set();
|
|
6113
6194
|
for (const line of raw.split("\n")) {
|
|
6114
6195
|
const trimmed = line.trim();
|
|
@@ -6132,7 +6213,7 @@ var scanPhase = {
|
|
|
6132
6213
|
function walk(dir) {
|
|
6133
6214
|
let entries;
|
|
6134
6215
|
try {
|
|
6135
|
-
entries =
|
|
6216
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
6136
6217
|
} catch {
|
|
6137
6218
|
return;
|
|
6138
6219
|
}
|
|
@@ -6141,15 +6222,15 @@ var scanPhase = {
|
|
|
6141
6222
|
if (entry.name.startsWith(".")) continue;
|
|
6142
6223
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6143
6224
|
if (extraIgnore.has(entry.name)) continue;
|
|
6144
|
-
walk(
|
|
6225
|
+
walk(path32.join(dir, entry.name));
|
|
6145
6226
|
} else if (entry.isFile()) {
|
|
6146
6227
|
const name = entry.name;
|
|
6147
6228
|
if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
|
|
6148
|
-
const ext =
|
|
6229
|
+
const ext = path32.extname(name);
|
|
6149
6230
|
if (!extensions.has(ext)) continue;
|
|
6150
|
-
const fullPath =
|
|
6231
|
+
const fullPath = path32.join(dir, name);
|
|
6151
6232
|
try {
|
|
6152
|
-
const stat =
|
|
6233
|
+
const stat = fs25.statSync(fullPath);
|
|
6153
6234
|
if (stat.size > MAX_FILE_SIZE_BYTES) continue;
|
|
6154
6235
|
} catch {
|
|
6155
6236
|
continue;
|
|
@@ -6176,20 +6257,20 @@ var structurePhase = {
|
|
|
6176
6257
|
const dirs = /* @__PURE__ */ new Set();
|
|
6177
6258
|
let structDone = 0;
|
|
6178
6259
|
for (const filePath of context2.filePaths) {
|
|
6179
|
-
const relativePath =
|
|
6260
|
+
const relativePath = path32.relative(context2.workspaceRoot, filePath);
|
|
6180
6261
|
const lang = detectLanguage(filePath);
|
|
6181
6262
|
context2.graph.addNode({
|
|
6182
6263
|
id: generateNodeId("file", relativePath, relativePath),
|
|
6183
6264
|
kind: "file",
|
|
6184
|
-
name:
|
|
6265
|
+
name: path32.basename(filePath),
|
|
6185
6266
|
filePath: relativePath,
|
|
6186
6267
|
metadata: lang ? { language: lang } : void 0
|
|
6187
6268
|
});
|
|
6188
|
-
let dir =
|
|
6269
|
+
let dir = path32.dirname(relativePath);
|
|
6189
6270
|
while (dir && dir !== "." && dir !== "") {
|
|
6190
6271
|
if (dirs.has(dir)) break;
|
|
6191
6272
|
dirs.add(dir);
|
|
6192
|
-
dir =
|
|
6273
|
+
dir = path32.dirname(dir);
|
|
6193
6274
|
}
|
|
6194
6275
|
structDone++;
|
|
6195
6276
|
context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
|
|
@@ -6198,7 +6279,7 @@ var structurePhase = {
|
|
|
6198
6279
|
context2.graph.addNode({
|
|
6199
6280
|
id: generateNodeId("directory", dir, dir),
|
|
6200
6281
|
kind: "directory",
|
|
6201
|
-
name:
|
|
6282
|
+
name: path32.basename(dir),
|
|
6202
6283
|
filePath: dir
|
|
6203
6284
|
});
|
|
6204
6285
|
}
|
|
@@ -6309,22 +6390,22 @@ var flowPhase = {
|
|
|
6309
6390
|
const queue = [{ nodeId: ep.id, path: [ep.id] }];
|
|
6310
6391
|
const visited = /* @__PURE__ */ new Set();
|
|
6311
6392
|
while (queue.length > 0 && flowCount < maxFlows) {
|
|
6312
|
-
const { nodeId, path:
|
|
6313
|
-
if (
|
|
6393
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6394
|
+
if (path33.length > maxDepth) continue;
|
|
6314
6395
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6315
|
-
if (callEdges.length === 0 &&
|
|
6396
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6316
6397
|
const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
|
|
6317
6398
|
graph.addNode({
|
|
6318
6399
|
id: flowId,
|
|
6319
6400
|
kind: "flow",
|
|
6320
6401
|
name: `${ep.name} flow ${flowCount}`,
|
|
6321
6402
|
filePath: ep.filePath,
|
|
6322
|
-
metadata: { steps:
|
|
6403
|
+
metadata: { steps: path33, entryPoint: ep.name }
|
|
6323
6404
|
});
|
|
6324
|
-
for (let i = 0; i <
|
|
6405
|
+
for (let i = 0; i < path33.length; i++) {
|
|
6325
6406
|
graph.addEdge({
|
|
6326
|
-
id: generateEdgeId(
|
|
6327
|
-
source:
|
|
6407
|
+
id: generateEdgeId(path33[i], flowId, `step_of_${i}`),
|
|
6408
|
+
source: path33[i],
|
|
6328
6409
|
target: flowId,
|
|
6329
6410
|
kind: "step_of",
|
|
6330
6411
|
weight: 1,
|
|
@@ -6337,7 +6418,7 @@ var flowPhase = {
|
|
|
6337
6418
|
for (const edge of callEdges) {
|
|
6338
6419
|
if (visited.has(edge.target)) continue;
|
|
6339
6420
|
visited.add(edge.target);
|
|
6340
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6421
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6341
6422
|
}
|
|
6342
6423
|
}
|
|
6343
6424
|
}
|
|
@@ -6355,7 +6436,7 @@ var LLMGovernanceLogger = class {
|
|
|
6355
6436
|
}
|
|
6356
6437
|
/** Path to the JSONL log file. */
|
|
6357
6438
|
getLogPath() {
|
|
6358
|
-
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ??
|
|
6439
|
+
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path32.join(os13.homedir(), ".code-intel", "llm-governance.jsonl");
|
|
6359
6440
|
}
|
|
6360
6441
|
/**
|
|
6361
6442
|
* Append an entry to the governance log.
|
|
@@ -6371,8 +6452,8 @@ var LLMGovernanceLogger = class {
|
|
|
6371
6452
|
...entry
|
|
6372
6453
|
};
|
|
6373
6454
|
const logPath = this.getLogPath();
|
|
6374
|
-
|
|
6375
|
-
|
|
6455
|
+
fs25.mkdirSync(path32.dirname(logPath), { recursive: true });
|
|
6456
|
+
fs25.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
|
|
6376
6457
|
} catch {
|
|
6377
6458
|
}
|
|
6378
6459
|
}
|
|
@@ -6382,7 +6463,7 @@ var LLMGovernanceLogger = class {
|
|
|
6382
6463
|
*/
|
|
6383
6464
|
readLog(limit = 100) {
|
|
6384
6465
|
try {
|
|
6385
|
-
const raw =
|
|
6466
|
+
const raw = fs25.readFileSync(this.getLogPath(), "utf-8");
|
|
6386
6467
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
|
|
6387
6468
|
return lines.map((l) => JSON.parse(l));
|
|
6388
6469
|
} catch {
|
|
@@ -6392,6 +6473,9 @@ var LLMGovernanceLogger = class {
|
|
|
6392
6473
|
};
|
|
6393
6474
|
var governanceLogger = new LLMGovernanceLogger();
|
|
6394
6475
|
|
|
6476
|
+
// src/pipeline/phases/summarize-phase.ts
|
|
6477
|
+
init_logger();
|
|
6478
|
+
|
|
6395
6479
|
// src/pipeline/workers/parse-phase-parallel.ts
|
|
6396
6480
|
init_shared();
|
|
6397
6481
|
init_id_generator();
|
|
@@ -6526,17 +6610,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
|
|
|
6526
6610
|
const queue = [{ nodeId: entryId, path: [entryId] }];
|
|
6527
6611
|
const visited = /* @__PURE__ */ new Set();
|
|
6528
6612
|
while (queue.length > 0 && flows.length < maxFlows) {
|
|
6529
|
-
const { nodeId, path:
|
|
6530
|
-
if (
|
|
6613
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6614
|
+
if (path33.length > maxDepth) continue;
|
|
6531
6615
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6532
|
-
if (callEdges.length === 0 &&
|
|
6533
|
-
flows.push({ entryPointId: entryId, steps: [...
|
|
6616
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6617
|
+
flows.push({ entryPointId: entryId, steps: [...path33] });
|
|
6534
6618
|
continue;
|
|
6535
6619
|
}
|
|
6536
6620
|
for (const edge of callEdges) {
|
|
6537
6621
|
if (visited.has(edge.target)) continue;
|
|
6538
6622
|
visited.add(edge.target);
|
|
6539
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6623
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6540
6624
|
}
|
|
6541
6625
|
}
|
|
6542
6626
|
}
|
|
@@ -6637,7 +6721,7 @@ var VectorIndex = class {
|
|
|
6637
6721
|
this.sqlitePath = sqlitePath;
|
|
6638
6722
|
}
|
|
6639
6723
|
async init() {
|
|
6640
|
-
this.db = new
|
|
6724
|
+
this.db = new Database2(this.sqlitePath);
|
|
6641
6725
|
this.db.pragma("journal_mode = WAL");
|
|
6642
6726
|
this.db.exec(`
|
|
6643
6727
|
CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
|
|
@@ -6768,9 +6852,9 @@ function siftDown(arr, i, score) {
|
|
|
6768
6852
|
}
|
|
6769
6853
|
init_embedder();
|
|
6770
6854
|
async function hybridSearch(graph, query, limit, options = {}) {
|
|
6771
|
-
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
|
|
6772
|
-
const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
6773
|
-
const hasVectorDb = Boolean(vectorDbPath &&
|
|
6855
|
+
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
|
|
6856
|
+
const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
6857
|
+
const hasVectorDb = Boolean(vectorDbPath && fs25.existsSync(vectorDbPath));
|
|
6774
6858
|
if (!hasVectorDb) {
|
|
6775
6859
|
const bm25Results2 = await bm25Promise;
|
|
6776
6860
|
return {
|
|
@@ -6819,16 +6903,282 @@ async function runVectorSearch(vectorDbPath, query, topK) {
|
|
|
6819
6903
|
return null;
|
|
6820
6904
|
}
|
|
6821
6905
|
}
|
|
6906
|
+
|
|
6907
|
+
// src/search/bm25-index.ts
|
|
6908
|
+
init_logger();
|
|
6909
|
+
var K1 = 1.2;
|
|
6910
|
+
var B = 0.75;
|
|
6911
|
+
function tokenize(text) {
|
|
6912
|
+
return text.toLowerCase().split(/[\s\-_./\\:(){}[\]<>,"'`~!@#$%^&*+=|;?]+/).filter((t) => t.length >= 2 && t.length <= 64);
|
|
6913
|
+
}
|
|
6914
|
+
function nodeToDoc(node) {
|
|
6915
|
+
return [
|
|
6916
|
+
node.name,
|
|
6917
|
+
node.kind,
|
|
6918
|
+
node.filePath,
|
|
6919
|
+
(node.content ?? "").slice(0, 1e3)
|
|
6920
|
+
].join(" ");
|
|
6921
|
+
}
|
|
6922
|
+
var Bm25Index = class {
|
|
6923
|
+
constructor(dbPath) {
|
|
6924
|
+
this.dbPath = dbPath;
|
|
6925
|
+
}
|
|
6926
|
+
dbPath;
|
|
6927
|
+
/** In-memory inverted index (populated after `load()`). */
|
|
6928
|
+
invertedIndex = /* @__PURE__ */ new Map();
|
|
6929
|
+
docLengths = /* @__PURE__ */ new Map();
|
|
6930
|
+
nodeMeta = /* @__PURE__ */ new Map();
|
|
6931
|
+
avgdl = 1;
|
|
6932
|
+
docCount = 0;
|
|
6933
|
+
_loaded = false;
|
|
6934
|
+
get isLoaded() {
|
|
6935
|
+
return this._loaded;
|
|
6936
|
+
}
|
|
6937
|
+
// ── Build ───────────────────────────────────────────────────────────────────
|
|
6938
|
+
/**
|
|
6939
|
+
* Build the inverted index from a KnowledgeGraph and persist to SQLite.
|
|
6940
|
+
* Called once at analysis time after the main pipeline completes.
|
|
6941
|
+
*/
|
|
6942
|
+
build(graph) {
|
|
6943
|
+
const nodeTermFreqs = /* @__PURE__ */ new Map();
|
|
6944
|
+
const docLengths = /* @__PURE__ */ new Map();
|
|
6945
|
+
const nodeMeta = /* @__PURE__ */ new Map();
|
|
6946
|
+
for (const node of graph.allNodes()) {
|
|
6947
|
+
if (["directory", "cluster", "flow"].includes(node.kind)) continue;
|
|
6948
|
+
const terms = tokenize(nodeToDoc(node));
|
|
6949
|
+
const tf = /* @__PURE__ */ new Map();
|
|
6950
|
+
for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
6951
|
+
nodeTermFreqs.set(node.id, tf);
|
|
6952
|
+
docLengths.set(node.id, terms.length);
|
|
6953
|
+
nodeMeta.set(node.id, {
|
|
6954
|
+
name: node.name,
|
|
6955
|
+
kind: node.kind,
|
|
6956
|
+
filePath: node.filePath,
|
|
6957
|
+
snippet: node.content?.slice(0, 200)
|
|
6958
|
+
});
|
|
6959
|
+
}
|
|
6960
|
+
const docCount = nodeTermFreqs.size;
|
|
6961
|
+
const totalLen = [...docLengths.values()].reduce((a, b) => a + b, 0);
|
|
6962
|
+
const avgdl = docCount > 0 ? totalLen / docCount : 1;
|
|
6963
|
+
const invertedIndex = /* @__PURE__ */ new Map();
|
|
6964
|
+
for (const [nodeId, tf] of nodeTermFreqs) {
|
|
6965
|
+
for (const [term, count] of tf) {
|
|
6966
|
+
let postings = invertedIndex.get(term);
|
|
6967
|
+
if (!postings) {
|
|
6968
|
+
postings = [];
|
|
6969
|
+
invertedIndex.set(term, postings);
|
|
6970
|
+
}
|
|
6971
|
+
postings.push({ nodeId, tf: count });
|
|
6972
|
+
}
|
|
6973
|
+
}
|
|
6974
|
+
fs25.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
6975
|
+
for (const f of [this.dbPath, `${this.dbPath}-shm`, `${this.dbPath}-wal`]) {
|
|
6976
|
+
try {
|
|
6977
|
+
if (fs25.existsSync(f)) fs25.unlinkSync(f);
|
|
6978
|
+
} catch {
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6981
|
+
const db = new Database2(this.dbPath);
|
|
6982
|
+
db.pragma("journal_mode = WAL");
|
|
6983
|
+
db.exec(`
|
|
6984
|
+
CREATE TABLE bm25_index (term TEXT PRIMARY KEY, postings TEXT NOT NULL);
|
|
6985
|
+
CREATE TABLE bm25_doclen (node_id TEXT PRIMARY KEY, doclen INTEGER NOT NULL);
|
|
6986
|
+
CREATE TABLE bm25_nodemeta(node_id TEXT PRIMARY KEY, name TEXT, kind TEXT, file_path TEXT, snippet TEXT);
|
|
6987
|
+
CREATE TABLE bm25_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
6988
|
+
`);
|
|
6989
|
+
const insPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
|
|
6990
|
+
const insDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
|
|
6991
|
+
const insNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
|
|
6992
|
+
const insMeta = db.prepare("INSERT OR REPLACE INTO bm25_meta VALUES (?, ?)");
|
|
6993
|
+
db.transaction(() => {
|
|
6994
|
+
for (const [term, postings] of invertedIndex) {
|
|
6995
|
+
insPosting.run(term, JSON.stringify(postings));
|
|
6996
|
+
}
|
|
6997
|
+
for (const [nodeId, len] of docLengths) {
|
|
6998
|
+
insDoclen.run(nodeId, len);
|
|
6999
|
+
}
|
|
7000
|
+
for (const [nodeId, meta] of nodeMeta) {
|
|
7001
|
+
insNodeMeta.run(nodeId, meta.name, meta.kind, meta.filePath, meta.snippet ?? null);
|
|
7002
|
+
}
|
|
7003
|
+
insMeta.run("avgdl", String(avgdl));
|
|
7004
|
+
insMeta.run("docCount", String(docCount));
|
|
7005
|
+
})();
|
|
7006
|
+
db.close();
|
|
7007
|
+
logger_default.info(` [bm25] Index built: ${invertedIndex.size} terms, ${docCount} documents`);
|
|
7008
|
+
}
|
|
7009
|
+
// ── Load into memory ────────────────────────────────────────────────────────
|
|
7010
|
+
/**
|
|
7011
|
+
* Load the full inverted index into memory.
|
|
7012
|
+
* Called once on `serve` startup.
|
|
7013
|
+
*/
|
|
7014
|
+
load() {
|
|
7015
|
+
if (!fs25.existsSync(this.dbPath)) return;
|
|
7016
|
+
const db = new Database2(this.dbPath, { readonly: true });
|
|
7017
|
+
try {
|
|
7018
|
+
const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
|
|
7019
|
+
this.avgdl = parseFloat(getMeta.get("avgdl")?.value ?? "1");
|
|
7020
|
+
this.docCount = parseInt(getMeta.get("docCount")?.value ?? "0", 10);
|
|
7021
|
+
this.invertedIndex.clear();
|
|
7022
|
+
const postingRows = db.prepare("SELECT term, postings FROM bm25_index").all();
|
|
7023
|
+
for (const row of postingRows) {
|
|
7024
|
+
this.invertedIndex.set(row.term, JSON.parse(row.postings));
|
|
7025
|
+
}
|
|
7026
|
+
this.docLengths.clear();
|
|
7027
|
+
const dlRows = db.prepare("SELECT node_id, doclen FROM bm25_doclen").all();
|
|
7028
|
+
for (const row of dlRows) {
|
|
7029
|
+
this.docLengths.set(row.node_id, row.doclen);
|
|
7030
|
+
}
|
|
7031
|
+
this.nodeMeta.clear();
|
|
7032
|
+
const metaRows = db.prepare("SELECT node_id, name, kind, file_path, snippet FROM bm25_nodemeta").all();
|
|
7033
|
+
for (const row of metaRows) {
|
|
7034
|
+
this.nodeMeta.set(row.node_id, {
|
|
7035
|
+
name: row.name,
|
|
7036
|
+
kind: row.kind,
|
|
7037
|
+
filePath: row.file_path,
|
|
7038
|
+
snippet: row.snippet ?? void 0
|
|
7039
|
+
});
|
|
7040
|
+
}
|
|
7041
|
+
this._loaded = true;
|
|
7042
|
+
logger_default.info(` [bm25] Index loaded (${this.invertedIndex.size} terms)`);
|
|
7043
|
+
} finally {
|
|
7044
|
+
db.close();
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7047
|
+
// ── Search ──────────────────────────────────────────────────────────────────
|
|
7048
|
+
/**
|
|
7049
|
+
* BM25 search. LIMIT pushdown: scores only the posting lists for query terms,
|
|
7050
|
+
* then partial-sorts to return only the top `limit` results.
|
|
7051
|
+
*/
|
|
7052
|
+
search(query, limit) {
|
|
7053
|
+
if (!this._loaded || this.invertedIndex.size === 0) return [];
|
|
7054
|
+
const queryTerms = [...new Set(tokenize(query))];
|
|
7055
|
+
if (queryTerms.length === 0) return [];
|
|
7056
|
+
const scores = /* @__PURE__ */ new Map();
|
|
7057
|
+
const N = this.docCount;
|
|
7058
|
+
const avgdl = this.avgdl;
|
|
7059
|
+
for (const term of queryTerms) {
|
|
7060
|
+
const postings = this.invertedIndex.get(term);
|
|
7061
|
+
if (!postings) continue;
|
|
7062
|
+
const df = postings.length;
|
|
7063
|
+
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
7064
|
+
for (const { nodeId, tf } of postings) {
|
|
7065
|
+
const dl = this.docLengths.get(nodeId) ?? avgdl;
|
|
7066
|
+
const score = idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (dl / avgdl)));
|
|
7067
|
+
scores.set(nodeId, (scores.get(nodeId) ?? 0) + score);
|
|
7068
|
+
}
|
|
7069
|
+
}
|
|
7070
|
+
const entries = [...scores.entries()];
|
|
7071
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
7072
|
+
const topK = entries.slice(0, limit);
|
|
7073
|
+
return topK.map(([nodeId, score]) => {
|
|
7074
|
+
const meta = this.nodeMeta.get(nodeId);
|
|
7075
|
+
return {
|
|
7076
|
+
nodeId,
|
|
7077
|
+
name: meta?.name ?? nodeId,
|
|
7078
|
+
kind: meta?.kind ?? "unknown",
|
|
7079
|
+
filePath: meta?.filePath ?? "",
|
|
7080
|
+
score,
|
|
7081
|
+
snippet: meta?.snippet
|
|
7082
|
+
};
|
|
7083
|
+
});
|
|
7084
|
+
}
|
|
7085
|
+
// ── Incremental update ──────────────────────────────────────────────────────
|
|
7086
|
+
/**
|
|
7087
|
+
* Incrementally update index for a set of changed/added nodes.
|
|
7088
|
+
* Only terms that overlap with the changed nodes are rewritten.
|
|
7089
|
+
* Works even if `load()` was not called (reads affected terms directly from DB).
|
|
7090
|
+
*/
|
|
7091
|
+
updateNodes(nodes) {
|
|
7092
|
+
if (!fs25.existsSync(this.dbPath)) return;
|
|
7093
|
+
if (nodes.length === 0) return;
|
|
7094
|
+
const changedIds = new Set(nodes.map((n) => n.id));
|
|
7095
|
+
const newTermFreqs = /* @__PURE__ */ new Map();
|
|
7096
|
+
for (const node of nodes) {
|
|
7097
|
+
if (["directory", "cluster", "flow"].includes(node.kind)) continue;
|
|
7098
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7099
|
+
const tf = /* @__PURE__ */ new Map();
|
|
7100
|
+
for (const t of terms) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
7101
|
+
newTermFreqs.set(node.id, tf);
|
|
7102
|
+
}
|
|
7103
|
+
const newTermSet = new Set([...newTermFreqs.values()].flatMap((m) => [...m.keys()]));
|
|
7104
|
+
const db = new Database2(this.dbPath);
|
|
7105
|
+
db.pragma("journal_mode = WAL");
|
|
7106
|
+
const termsToRewrite = /* @__PURE__ */ new Map();
|
|
7107
|
+
for (const term of newTermSet) {
|
|
7108
|
+
const row = db.prepare("SELECT postings FROM bm25_index WHERE term = ?").get(term);
|
|
7109
|
+
const existing = row ? JSON.parse(row.postings) : [];
|
|
7110
|
+
termsToRewrite.set(term, existing.filter((p) => !changedIds.has(p.nodeId)));
|
|
7111
|
+
}
|
|
7112
|
+
for (const nodeId of changedIds) {
|
|
7113
|
+
const rows = db.prepare("SELECT term, postings FROM bm25_index WHERE postings LIKE ?").all(`%${nodeId}%`);
|
|
7114
|
+
for (const row of rows) {
|
|
7115
|
+
if (termsToRewrite.has(row.term)) continue;
|
|
7116
|
+
const postings = JSON.parse(row.postings);
|
|
7117
|
+
if (postings.some((p) => changedIds.has(p.nodeId))) {
|
|
7118
|
+
termsToRewrite.set(row.term, postings.filter((p) => !changedIds.has(p.nodeId)));
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
for (const [nodeId, tf] of newTermFreqs) {
|
|
7123
|
+
for (const [term, count] of tf) {
|
|
7124
|
+
const postings = termsToRewrite.get(term) ?? [];
|
|
7125
|
+
postings.push({ nodeId, tf: count });
|
|
7126
|
+
termsToRewrite.set(term, postings);
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
const upsertPosting = db.prepare("INSERT OR REPLACE INTO bm25_index VALUES (?, ?)");
|
|
7130
|
+
const upsertDoclen = db.prepare("INSERT OR REPLACE INTO bm25_doclen VALUES (?, ?)");
|
|
7131
|
+
const upsertNodeMeta = db.prepare("INSERT OR REPLACE INTO bm25_nodemeta VALUES (?, ?, ?, ?, ?)");
|
|
7132
|
+
db.transaction(() => {
|
|
7133
|
+
for (const [term, postings] of termsToRewrite) {
|
|
7134
|
+
if (postings.length === 0) {
|
|
7135
|
+
db.prepare("DELETE FROM bm25_index WHERE term = ?").run(term);
|
|
7136
|
+
} else {
|
|
7137
|
+
upsertPosting.run(term, JSON.stringify(postings));
|
|
7138
|
+
}
|
|
7139
|
+
}
|
|
7140
|
+
for (const node of nodes) {
|
|
7141
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7142
|
+
upsertDoclen.run(node.id, terms.length);
|
|
7143
|
+
upsertNodeMeta.run(node.id, node.name, node.kind, node.filePath, node.content?.slice(0, 200) ?? null);
|
|
7144
|
+
}
|
|
7145
|
+
})();
|
|
7146
|
+
db.close();
|
|
7147
|
+
if (this._loaded) {
|
|
7148
|
+
for (const [term, postings] of termsToRewrite) {
|
|
7149
|
+
if (postings.length === 0) this.invertedIndex.delete(term);
|
|
7150
|
+
else this.invertedIndex.set(term, postings);
|
|
7151
|
+
}
|
|
7152
|
+
for (const node of nodes) {
|
|
7153
|
+
const terms = tokenize(nodeToDoc(node));
|
|
7154
|
+
this.docLengths.set(node.id, terms.length);
|
|
7155
|
+
this.nodeMeta.set(node.id, {
|
|
7156
|
+
name: node.name,
|
|
7157
|
+
kind: node.kind,
|
|
7158
|
+
filePath: node.filePath,
|
|
7159
|
+
snippet: node.content?.slice(0, 200)
|
|
7160
|
+
});
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
}
|
|
7164
|
+
};
|
|
7165
|
+
function getBm25DbPath(workspaceRoot) {
|
|
7166
|
+
return path32.join(workspaceRoot, ".code-intel", "bm25.db");
|
|
7167
|
+
}
|
|
6822
7168
|
var DbManager = class {
|
|
6823
7169
|
db = null;
|
|
6824
7170
|
conn = null;
|
|
6825
7171
|
dbPath;
|
|
6826
|
-
|
|
7172
|
+
readOnly;
|
|
7173
|
+
constructor(dbPath, readOnly = false) {
|
|
6827
7174
|
this.dbPath = dbPath;
|
|
7175
|
+
this.readOnly = readOnly;
|
|
6828
7176
|
}
|
|
6829
7177
|
async init() {
|
|
6830
|
-
|
|
6831
|
-
|
|
7178
|
+
if (!this.readOnly) {
|
|
7179
|
+
fs25.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
7180
|
+
}
|
|
7181
|
+
this.db = new Database(this.dbPath, 0, true, this.readOnly);
|
|
6832
7182
|
await this.db.init();
|
|
6833
7183
|
this.conn = new Connection(this.db);
|
|
6834
7184
|
await this.conn.init();
|
|
@@ -6863,61 +7213,8 @@ var DbManager = class {
|
|
|
6863
7213
|
return this.conn !== null;
|
|
6864
7214
|
}
|
|
6865
7215
|
};
|
|
6866
|
-
|
|
6867
|
-
// src/storage/schema.ts
|
|
6868
|
-
var NODE_TABLE_MAP = {
|
|
6869
|
-
file: "file_nodes",
|
|
6870
|
-
directory: "dir_nodes",
|
|
6871
|
-
function: "func_nodes",
|
|
6872
|
-
class: "class_nodes",
|
|
6873
|
-
interface: "iface_nodes",
|
|
6874
|
-
method: "method_nodes",
|
|
6875
|
-
constructor: "ctor_nodes",
|
|
6876
|
-
variable: "var_nodes",
|
|
6877
|
-
property: "prop_nodes",
|
|
6878
|
-
struct: "struct_nodes",
|
|
6879
|
-
enum: "enum_nodes",
|
|
6880
|
-
trait: "trait_nodes",
|
|
6881
|
-
namespace: "ns_nodes",
|
|
6882
|
-
module: "mod_nodes",
|
|
6883
|
-
type_alias: "type_nodes",
|
|
6884
|
-
constant: "const_nodes",
|
|
6885
|
-
route: "route_nodes",
|
|
6886
|
-
cluster: "cluster_nodes",
|
|
6887
|
-
flow: "flow_nodes",
|
|
6888
|
-
vulnerability: "vuln_nodes"
|
|
6889
|
-
};
|
|
6890
|
-
var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
|
|
6891
|
-
function getCreateNodeTableDDL(tableName) {
|
|
6892
|
-
return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
|
|
6893
|
-
id STRING,
|
|
6894
|
-
name STRING,
|
|
6895
|
-
file_path STRING,
|
|
6896
|
-
start_line INT64,
|
|
6897
|
-
end_line INT64,
|
|
6898
|
-
exported BOOLEAN,
|
|
6899
|
-
content STRING,
|
|
6900
|
-
metadata STRING,
|
|
6901
|
-
PRIMARY KEY (id)
|
|
6902
|
-
)`;
|
|
6903
|
-
}
|
|
6904
|
-
function getCreateEdgeTableDDL() {
|
|
6905
|
-
const uniqueTables = ALL_NODE_TABLES;
|
|
6906
|
-
const fromToPairs = [];
|
|
6907
|
-
for (const from of uniqueTables) {
|
|
6908
|
-
for (const to of uniqueTables) {
|
|
6909
|
-
fromToPairs.push(`FROM ${from} TO ${to}`);
|
|
6910
|
-
}
|
|
6911
|
-
}
|
|
6912
|
-
return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
|
|
6913
|
-
${fromToPairs.join(",\n ")},
|
|
6914
|
-
kind STRING,
|
|
6915
|
-
weight DOUBLE,
|
|
6916
|
-
label STRING
|
|
6917
|
-
)`];
|
|
6918
|
-
}
|
|
6919
7216
|
function writeNodeCSVs(graph, outputDir) {
|
|
6920
|
-
|
|
7217
|
+
fs25.mkdirSync(outputDir, { recursive: true });
|
|
6921
7218
|
const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
|
|
6922
7219
|
const tableBuffers = /* @__PURE__ */ new Map();
|
|
6923
7220
|
const tableFilePaths = /* @__PURE__ */ new Map();
|
|
@@ -6925,7 +7222,7 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6925
7222
|
const table = NODE_TABLE_MAP[node.kind];
|
|
6926
7223
|
if (!tableBuffers.has(table)) {
|
|
6927
7224
|
tableBuffers.set(table, [header]);
|
|
6928
|
-
tableFilePaths.set(table,
|
|
7225
|
+
tableFilePaths.set(table, path32.join(outputDir, `${table}.csv`));
|
|
6929
7226
|
}
|
|
6930
7227
|
tableBuffers.get(table).push(
|
|
6931
7228
|
csvRow([
|
|
@@ -6945,12 +7242,12 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6945
7242
|
);
|
|
6946
7243
|
}
|
|
6947
7244
|
for (const [table, lines] of tableBuffers) {
|
|
6948
|
-
|
|
7245
|
+
fs25.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
|
|
6949
7246
|
}
|
|
6950
7247
|
return tableFilePaths;
|
|
6951
7248
|
}
|
|
6952
7249
|
function writeEdgeCSV(graph, outputDir) {
|
|
6953
|
-
|
|
7250
|
+
fs25.mkdirSync(outputDir, { recursive: true });
|
|
6954
7251
|
const header = "from_id,to_id,kind,weight,label\n";
|
|
6955
7252
|
const groups = /* @__PURE__ */ new Map();
|
|
6956
7253
|
for (const edge of graph.allEdges()) {
|
|
@@ -6961,7 +7258,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6961
7258
|
const toTable = NODE_TABLE_MAP[targetNode.kind];
|
|
6962
7259
|
const key = `${fromTable}->${toTable}`;
|
|
6963
7260
|
if (!groups.has(key)) {
|
|
6964
|
-
const filePath =
|
|
7261
|
+
const filePath = path32.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
|
|
6965
7262
|
groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
|
|
6966
7263
|
}
|
|
6967
7264
|
groups.get(key).lines.push(
|
|
@@ -6976,7 +7273,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6976
7273
|
}
|
|
6977
7274
|
const result = [];
|
|
6978
7275
|
for (const group of groups.values()) {
|
|
6979
|
-
|
|
7276
|
+
fs25.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
|
|
6980
7277
|
result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
|
|
6981
7278
|
}
|
|
6982
7279
|
return result;
|
|
@@ -7004,7 +7301,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7004
7301
|
} catch {
|
|
7005
7302
|
}
|
|
7006
7303
|
}
|
|
7007
|
-
const tmpDir =
|
|
7304
|
+
const tmpDir = fs25.mkdtempSync(path32.join(os13.tmpdir(), "code-intel-csv-"));
|
|
7008
7305
|
try {
|
|
7009
7306
|
const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
|
|
7010
7307
|
const edgeGroups = writeEdgeCSV(graph, tmpDir);
|
|
@@ -7023,8 +7320,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7023
7320
|
}
|
|
7024
7321
|
let nodeCount = 0;
|
|
7025
7322
|
for (const [table, csvPath] of nodeTableFiles) {
|
|
7026
|
-
if (!
|
|
7027
|
-
const stat =
|
|
7323
|
+
if (!fs25.existsSync(csvPath)) continue;
|
|
7324
|
+
const stat = fs25.statSync(csvPath);
|
|
7028
7325
|
if (stat.size < 50) continue;
|
|
7029
7326
|
try {
|
|
7030
7327
|
await dbManager.execute(
|
|
@@ -7037,8 +7334,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7037
7334
|
}
|
|
7038
7335
|
let edgeCount = 0;
|
|
7039
7336
|
for (const group of edgeGroups) {
|
|
7040
|
-
if (!
|
|
7041
|
-
const stat =
|
|
7337
|
+
if (!fs25.existsSync(group.filePath)) continue;
|
|
7338
|
+
const stat = fs25.statSync(group.filePath);
|
|
7042
7339
|
if (stat.size < 50) continue;
|
|
7043
7340
|
try {
|
|
7044
7341
|
await dbManager.execute(
|
|
@@ -7052,7 +7349,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7052
7349
|
return { nodeCount, edgeCount };
|
|
7053
7350
|
} finally {
|
|
7054
7351
|
try {
|
|
7055
|
-
|
|
7352
|
+
fs25.rmSync(tmpDir, { recursive: true, force: true });
|
|
7056
7353
|
} catch {
|
|
7057
7354
|
}
|
|
7058
7355
|
}
|
|
@@ -7104,19 +7401,19 @@ function buildNodeProps(node) {
|
|
|
7104
7401
|
function escCypher(s) {
|
|
7105
7402
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
|
|
7106
7403
|
}
|
|
7107
|
-
var GLOBAL_DIR =
|
|
7108
|
-
var REPOS_FILE =
|
|
7404
|
+
var GLOBAL_DIR = path32.join(os13.homedir(), ".code-intel");
|
|
7405
|
+
var REPOS_FILE = path32.join(GLOBAL_DIR, "repos.json");
|
|
7109
7406
|
function loadRegistry() {
|
|
7110
7407
|
try {
|
|
7111
|
-
const data =
|
|
7408
|
+
const data = fs25.readFileSync(REPOS_FILE, "utf-8");
|
|
7112
7409
|
return JSON.parse(data);
|
|
7113
7410
|
} catch {
|
|
7114
7411
|
return [];
|
|
7115
7412
|
}
|
|
7116
7413
|
}
|
|
7117
7414
|
function saveRegistry(entries) {
|
|
7118
|
-
|
|
7119
|
-
|
|
7415
|
+
fs25.mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
7416
|
+
fs25.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
|
|
7120
7417
|
}
|
|
7121
7418
|
function upsertRepo(entry) {
|
|
7122
7419
|
const entries = loadRegistry();
|
|
@@ -7133,30 +7430,30 @@ function removeRepo(repoPath) {
|
|
|
7133
7430
|
saveRegistry(entries);
|
|
7134
7431
|
}
|
|
7135
7432
|
function saveMetadata(repoDir, metadata) {
|
|
7136
|
-
const metaDir =
|
|
7137
|
-
|
|
7138
|
-
|
|
7433
|
+
const metaDir = path32.join(repoDir, ".code-intel");
|
|
7434
|
+
fs25.mkdirSync(metaDir, { recursive: true });
|
|
7435
|
+
fs25.writeFileSync(path32.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
|
|
7139
7436
|
}
|
|
7140
7437
|
function loadMetadata(repoDir) {
|
|
7141
7438
|
try {
|
|
7142
|
-
const data =
|
|
7439
|
+
const data = fs25.readFileSync(path32.join(repoDir, ".code-intel", "meta.json"), "utf-8");
|
|
7143
7440
|
return JSON.parse(data);
|
|
7144
7441
|
} catch {
|
|
7145
7442
|
return null;
|
|
7146
7443
|
}
|
|
7147
7444
|
}
|
|
7148
7445
|
function getDbPath(repoDir) {
|
|
7149
|
-
return
|
|
7446
|
+
return path32.join(repoDir, ".code-intel", "graph.db");
|
|
7150
7447
|
}
|
|
7151
7448
|
function getVectorDbPath(repoDir) {
|
|
7152
|
-
return
|
|
7449
|
+
return path32.join(repoDir, ".code-intel", "vector.db");
|
|
7153
7450
|
}
|
|
7154
7451
|
|
|
7155
7452
|
// src/mcp-server/server.ts
|
|
7156
7453
|
init_group_registry();
|
|
7157
7454
|
|
|
7158
7455
|
// src/multi-repo/graph-from-db.ts
|
|
7159
|
-
var
|
|
7456
|
+
var TABLE_TO_KIND2 = Object.fromEntries(
|
|
7160
7457
|
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
7161
7458
|
);
|
|
7162
7459
|
function parseRow(row, kind) {
|
|
@@ -7180,7 +7477,7 @@ function parseRow(row, kind) {
|
|
|
7180
7477
|
}
|
|
7181
7478
|
async function loadGraphFromDB(graph, db) {
|
|
7182
7479
|
for (const table of ALL_NODE_TABLES) {
|
|
7183
|
-
const kind =
|
|
7480
|
+
const kind = TABLE_TO_KIND2[table];
|
|
7184
7481
|
if (!kind) continue;
|
|
7185
7482
|
let rows = [];
|
|
7186
7483
|
try {
|
|
@@ -7233,12 +7530,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
|
|
|
7233
7530
|
if (depth > maxDepth) return;
|
|
7234
7531
|
let entries;
|
|
7235
7532
|
try {
|
|
7236
|
-
entries =
|
|
7533
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
7237
7534
|
} catch {
|
|
7238
7535
|
return;
|
|
7239
7536
|
}
|
|
7240
7537
|
for (const entry of entries) {
|
|
7241
|
-
const full =
|
|
7538
|
+
const full = path32.join(dir, entry.name);
|
|
7242
7539
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
7243
7540
|
walk(full, depth + 1);
|
|
7244
7541
|
} else if (entry.isFile() && matcher(entry.name)) {
|
|
@@ -7261,8 +7558,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
7261
7558
|
]);
|
|
7262
7559
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
7263
7560
|
function tryParseFile(filePath) {
|
|
7264
|
-
const ext =
|
|
7265
|
-
const content =
|
|
7561
|
+
const ext = path32.extname(filePath).toLowerCase();
|
|
7562
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7266
7563
|
if (ext === ".json") {
|
|
7267
7564
|
try {
|
|
7268
7565
|
return JSON.parse(content);
|
|
@@ -7316,7 +7613,7 @@ async function parseGraphQLContracts(repoRoot) {
|
|
|
7316
7613
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
|
|
7317
7614
|
const contracts = [];
|
|
7318
7615
|
for (const filePath of files) {
|
|
7319
|
-
const content =
|
|
7616
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7320
7617
|
const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7321
7618
|
let match;
|
|
7322
7619
|
while ((match = typeRegex.exec(content)) !== null) {
|
|
@@ -7347,7 +7644,7 @@ async function parseProtoContracts(repoRoot) {
|
|
|
7347
7644
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
|
|
7348
7645
|
const contracts = [];
|
|
7349
7646
|
for (const filePath of files) {
|
|
7350
|
-
const content =
|
|
7647
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7351
7648
|
const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7352
7649
|
let serviceMatch;
|
|
7353
7650
|
while ((serviceMatch = serviceRegex.exec(content)) !== null) {
|
|
@@ -7552,13 +7849,13 @@ async function syncGroup(group) {
|
|
|
7552
7849
|
logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
|
|
7553
7850
|
continue;
|
|
7554
7851
|
}
|
|
7555
|
-
const dbPath =
|
|
7556
|
-
if (!
|
|
7852
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
7853
|
+
if (!fs25.existsSync(dbPath)) {
|
|
7557
7854
|
logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
|
|
7558
7855
|
continue;
|
|
7559
7856
|
}
|
|
7560
7857
|
const graph = createKnowledgeGraph();
|
|
7561
|
-
const db = new DbManager(dbPath);
|
|
7858
|
+
const db = new DbManager(dbPath, true);
|
|
7562
7859
|
try {
|
|
7563
7860
|
await db.init();
|
|
7564
7861
|
await loadGraphFromDB(graph, db);
|
|
@@ -7626,10 +7923,10 @@ async function queryGroup(group, query, limit = 20) {
|
|
|
7626
7923
|
for (const member of group.members) {
|
|
7627
7924
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
7628
7925
|
if (!regEntry) continue;
|
|
7629
|
-
const dbPath =
|
|
7630
|
-
if (!
|
|
7926
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
7927
|
+
if (!fs25.existsSync(dbPath)) continue;
|
|
7631
7928
|
const graph = createKnowledgeGraph();
|
|
7632
|
-
const db = new DbManager(dbPath);
|
|
7929
|
+
const db = new DbManager(dbPath, true);
|
|
7633
7930
|
try {
|
|
7634
7931
|
await db.init();
|
|
7635
7932
|
await loadGraphFromDB(graph, db);
|
|
@@ -8127,22 +8424,22 @@ function suggestTests(graph, symbolName) {
|
|
|
8127
8424
|
const callPaths = [];
|
|
8128
8425
|
const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
|
|
8129
8426
|
while (pathQueue.length > 0 && callPaths.length < 5) {
|
|
8130
|
-
const { id, path:
|
|
8427
|
+
const { id, path: path33, depth } = pathQueue.shift();
|
|
8131
8428
|
let hasCallers2 = false;
|
|
8132
8429
|
for (const edge of graph.findEdgesTo(id)) {
|
|
8133
8430
|
if (edge.kind !== "calls") continue;
|
|
8134
8431
|
const callerNode = graph.getNode(edge.source);
|
|
8135
8432
|
if (!callerNode) continue;
|
|
8136
8433
|
hasCallers2 = true;
|
|
8137
|
-
const newPath = [callerNode.name, ...
|
|
8434
|
+
const newPath = [callerNode.name, ...path33];
|
|
8138
8435
|
if (depth + 1 >= 3 || callPaths.length >= 5) {
|
|
8139
8436
|
if (callPaths.length < 5) callPaths.push(newPath);
|
|
8140
8437
|
continue;
|
|
8141
8438
|
}
|
|
8142
8439
|
pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
|
|
8143
8440
|
}
|
|
8144
|
-
if (!hasCallers2 &&
|
|
8145
|
-
callPaths.push(
|
|
8441
|
+
if (!hasCallers2 && path33.length > 1) {
|
|
8442
|
+
callPaths.push(path33);
|
|
8146
8443
|
}
|
|
8147
8444
|
}
|
|
8148
8445
|
if (callPaths.length === 0) {
|
|
@@ -8699,24 +8996,44 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8699
8996
|
}
|
|
8700
8997
|
const startMs = Date.now();
|
|
8701
8998
|
const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot);
|
|
8999
|
+
const MCP_TIMEOUT_MS = parseInt(process.env["CODE_INTEL_MCP_TIMEOUT_MS"] ?? "30000", 10);
|
|
9000
|
+
let timeoutHandle = null;
|
|
9001
|
+
let timedOut = false;
|
|
9002
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
9003
|
+
timeoutHandle = setTimeout(() => {
|
|
9004
|
+
timedOut = true;
|
|
9005
|
+
reject(new Error(`MCP tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`));
|
|
9006
|
+
}, MCP_TIMEOUT_MS);
|
|
9007
|
+
});
|
|
8702
9008
|
let result;
|
|
8703
9009
|
let status = "success";
|
|
8704
9010
|
try {
|
|
8705
9011
|
if (isTracingEnabled()) {
|
|
8706
|
-
result = await
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
9012
|
+
result = await Promise.race([
|
|
9013
|
+
withSpan(
|
|
9014
|
+
`mcp.tool.${name}`,
|
|
9015
|
+
sanitizeAttrs({ "mcp.tool": name, "mcp.repo": repoName }),
|
|
9016
|
+
dispatch
|
|
9017
|
+
),
|
|
9018
|
+
timeoutPromise
|
|
9019
|
+
]);
|
|
8711
9020
|
} else {
|
|
8712
|
-
result = await dispatch();
|
|
9021
|
+
result = await Promise.race([dispatch(), timeoutPromise]);
|
|
8713
9022
|
}
|
|
8714
9023
|
if (result.isError) status = "error";
|
|
8715
9024
|
} catch (err) {
|
|
8716
9025
|
status = "error";
|
|
8717
9026
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8718
9027
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
9028
|
+
if (timedOut) {
|
|
9029
|
+
return {
|
|
9030
|
+
content: [{ type: "text", text: JSON.stringify({ truncated: true, reason: `Tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`, partialResults: [] }) }],
|
|
9031
|
+
isError: false
|
|
9032
|
+
};
|
|
9033
|
+
}
|
|
8719
9034
|
throw err;
|
|
9035
|
+
} finally {
|
|
9036
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
8720
9037
|
}
|
|
8721
9038
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8722
9039
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
@@ -9104,7 +9421,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9104
9421
|
for (const { filePath: changedFile, changedLines } of changedFiles) {
|
|
9105
9422
|
for (const node of graph.allNodes()) {
|
|
9106
9423
|
if (!node.filePath) continue;
|
|
9107
|
-
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot +
|
|
9424
|
+
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path32.sep, "");
|
|
9108
9425
|
const normChanged = changedFile.replace(/^a\/|^b\//, "");
|
|
9109
9426
|
if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
|
|
9110
9427
|
if (node.startLine !== void 0 && node.endLine !== void 0) {
|
|
@@ -9527,8 +9844,8 @@ var STUCK_THRESHOLD_MINUTES = 30;
|
|
|
9527
9844
|
var JobsDB = class {
|
|
9528
9845
|
db;
|
|
9529
9846
|
constructor(dbPath) {
|
|
9530
|
-
|
|
9531
|
-
this.db = new
|
|
9847
|
+
fs25.mkdirSync(path32.dirname(dbPath), { recursive: true });
|
|
9848
|
+
this.db = new Database2(dbPath);
|
|
9532
9849
|
this.db.pragma("journal_mode = WAL");
|
|
9533
9850
|
this.db.pragma("foreign_keys = ON");
|
|
9534
9851
|
this.createTables();
|
|
@@ -9669,7 +9986,7 @@ var JobsDB = class {
|
|
|
9669
9986
|
}
|
|
9670
9987
|
};
|
|
9671
9988
|
function getJobsDBPath() {
|
|
9672
|
-
return
|
|
9989
|
+
return path32.join(os13.homedir(), ".code-intel", "jobs.db");
|
|
9673
9990
|
}
|
|
9674
9991
|
var _jobsDB = null;
|
|
9675
9992
|
function getOrCreateJobsDB() {
|
|
@@ -9761,14 +10078,14 @@ var BACKUP_VERSION = "1.0";
|
|
|
9761
10078
|
var ALGORITHM = "aes-256-gcm";
|
|
9762
10079
|
var IV_LENGTH = 16;
|
|
9763
10080
|
function getBackupDir() {
|
|
9764
|
-
return
|
|
10081
|
+
return path32.join(os13.homedir(), ".code-intel", "backups");
|
|
9765
10082
|
}
|
|
9766
10083
|
function getBackupKey() {
|
|
9767
10084
|
const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
|
|
9768
10085
|
if (keyHex && keyHex.length >= 64) {
|
|
9769
10086
|
return Buffer.from(keyHex.slice(0, 64), "hex");
|
|
9770
10087
|
}
|
|
9771
|
-
const seed = `code-intel-backup-${
|
|
10088
|
+
const seed = `code-intel-backup-${os13.hostname()}-${os13.homedir()}`;
|
|
9772
10089
|
return crypto5.createHash("sha256").update(seed).digest();
|
|
9773
10090
|
}
|
|
9774
10091
|
function encryptBuffer(data, key) {
|
|
@@ -9792,30 +10109,30 @@ var BackupService = class {
|
|
|
9792
10109
|
constructor(backupDir) {
|
|
9793
10110
|
this.backupDir = backupDir ?? getBackupDir();
|
|
9794
10111
|
this.key = getBackupKey();
|
|
9795
|
-
|
|
10112
|
+
fs25.mkdirSync(this.backupDir, { recursive: true });
|
|
9796
10113
|
}
|
|
9797
10114
|
/**
|
|
9798
10115
|
* Create a backup for a repository.
|
|
9799
10116
|
* Returns the backup entry.
|
|
9800
10117
|
*/
|
|
9801
10118
|
createBackup(repoPath) {
|
|
9802
|
-
const codeIntelDir =
|
|
10119
|
+
const codeIntelDir = path32.join(repoPath, ".code-intel");
|
|
9803
10120
|
const id = v4();
|
|
9804
10121
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9805
10122
|
const filesToBackup = [];
|
|
9806
10123
|
const candidates = ["graph.db", "vector.db", "meta.json"];
|
|
9807
10124
|
for (const f of candidates) {
|
|
9808
|
-
const fp =
|
|
9809
|
-
if (
|
|
10125
|
+
const fp = path32.join(codeIntelDir, f);
|
|
10126
|
+
if (fs25.existsSync(fp)) {
|
|
9810
10127
|
filesToBackup.push({ name: f, localPath: fp });
|
|
9811
10128
|
}
|
|
9812
10129
|
}
|
|
9813
|
-
const registryPath =
|
|
9814
|
-
if (
|
|
10130
|
+
const registryPath = path32.join(os13.homedir(), ".code-intel", "registry.json");
|
|
10131
|
+
if (fs25.existsSync(registryPath)) {
|
|
9815
10132
|
filesToBackup.push({ name: "registry.json", localPath: registryPath });
|
|
9816
10133
|
}
|
|
9817
|
-
const usersDbPath =
|
|
9818
|
-
if (
|
|
10134
|
+
const usersDbPath = path32.join(os13.homedir(), ".code-intel", "users.db");
|
|
10135
|
+
if (fs25.existsSync(usersDbPath)) {
|
|
9819
10136
|
filesToBackup.push({ name: "users.db", localPath: usersDbPath });
|
|
9820
10137
|
}
|
|
9821
10138
|
if (filesToBackup.length === 0) {
|
|
@@ -9826,7 +10143,7 @@ var BackupService = class {
|
|
|
9826
10143
|
createdAt,
|
|
9827
10144
|
version: BACKUP_VERSION,
|
|
9828
10145
|
files: filesToBackup.map((f) => {
|
|
9829
|
-
const data =
|
|
10146
|
+
const data = fs25.readFileSync(f.localPath);
|
|
9830
10147
|
return {
|
|
9831
10148
|
name: f.name,
|
|
9832
10149
|
sha256: crypto5.createHash("sha256").update(data).digest("hex"),
|
|
@@ -9840,7 +10157,7 @@ var BackupService = class {
|
|
|
9840
10157
|
manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
|
|
9841
10158
|
parts.push(manifestLenBuf, manifestBuf);
|
|
9842
10159
|
for (const f of filesToBackup) {
|
|
9843
|
-
const data =
|
|
10160
|
+
const data = fs25.readFileSync(f.localPath);
|
|
9844
10161
|
const nameBuf = Buffer.from(f.name, "utf-8");
|
|
9845
10162
|
const nameLenBuf = Buffer.alloc(2);
|
|
9846
10163
|
nameLenBuf.writeUInt16BE(nameBuf.length, 0);
|
|
@@ -9851,8 +10168,8 @@ var BackupService = class {
|
|
|
9851
10168
|
const plaintext = Buffer.concat(parts);
|
|
9852
10169
|
const encrypted = encryptBuffer(plaintext, this.key);
|
|
9853
10170
|
const backupFileName = `backup-${id}.cib`;
|
|
9854
|
-
const backupPath =
|
|
9855
|
-
|
|
10171
|
+
const backupPath = path32.join(this.backupDir, backupFileName);
|
|
10172
|
+
fs25.writeFileSync(backupPath, encrypted);
|
|
9856
10173
|
const entry = {
|
|
9857
10174
|
id,
|
|
9858
10175
|
createdAt,
|
|
@@ -9879,9 +10196,9 @@ var BackupService = class {
|
|
|
9879
10196
|
async uploadToS3(entry) {
|
|
9880
10197
|
const cfg = getS3Config();
|
|
9881
10198
|
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.");
|
|
9882
|
-
const fileName =
|
|
10199
|
+
const fileName = path32.basename(entry.path);
|
|
9883
10200
|
const s3Key = `${cfg.prefix}${fileName}`;
|
|
9884
|
-
const body =
|
|
10201
|
+
const body = fs25.readFileSync(entry.path);
|
|
9885
10202
|
const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
|
|
9886
10203
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9887
10204
|
throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
@@ -9898,8 +10215,8 @@ var BackupService = class {
|
|
|
9898
10215
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9899
10216
|
throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
9900
10217
|
}
|
|
9901
|
-
|
|
9902
|
-
|
|
10218
|
+
fs25.mkdirSync(path32.dirname(destPath), { recursive: true });
|
|
10219
|
+
fs25.writeFileSync(destPath, Buffer.from(result.body, "binary"));
|
|
9903
10220
|
}
|
|
9904
10221
|
/**
|
|
9905
10222
|
* List backup objects in S3 with the configured prefix.
|
|
@@ -9945,10 +10262,10 @@ var BackupService = class {
|
|
|
9945
10262
|
if (!entry) {
|
|
9946
10263
|
throw new Error(`Backup "${backupId}" not found.`);
|
|
9947
10264
|
}
|
|
9948
|
-
if (!
|
|
10265
|
+
if (!fs25.existsSync(entry.path)) {
|
|
9949
10266
|
throw new Error(`Backup file not found at: ${entry.path}`);
|
|
9950
10267
|
}
|
|
9951
|
-
const encrypted =
|
|
10268
|
+
const encrypted = fs25.readFileSync(entry.path);
|
|
9952
10269
|
let plaintext;
|
|
9953
10270
|
try {
|
|
9954
10271
|
plaintext = decryptBuffer(encrypted, this.key);
|
|
@@ -9962,8 +10279,8 @@ var BackupService = class {
|
|
|
9962
10279
|
offset += manifestLen;
|
|
9963
10280
|
const manifest = JSON.parse(manifestStr);
|
|
9964
10281
|
const restoreBase = targetRepoPath ?? entry.repoPath;
|
|
9965
|
-
const codeIntelDir =
|
|
9966
|
-
|
|
10282
|
+
const codeIntelDir = path32.join(restoreBase, ".code-intel");
|
|
10283
|
+
fs25.mkdirSync(codeIntelDir, { recursive: true });
|
|
9967
10284
|
for (const fileEntry of manifest.files) {
|
|
9968
10285
|
const nameLen = plaintext.readUInt16BE(offset);
|
|
9969
10286
|
offset += 2;
|
|
@@ -9980,18 +10297,18 @@ var BackupService = class {
|
|
|
9980
10297
|
}
|
|
9981
10298
|
let destPath;
|
|
9982
10299
|
if (name === "registry.json" || name === "users.db") {
|
|
9983
|
-
destPath =
|
|
10300
|
+
destPath = path32.join(os13.homedir(), ".code-intel", name);
|
|
9984
10301
|
} else {
|
|
9985
|
-
destPath =
|
|
10302
|
+
destPath = path32.join(codeIntelDir, name);
|
|
9986
10303
|
}
|
|
9987
|
-
|
|
10304
|
+
fs25.writeFileSync(destPath, data);
|
|
9988
10305
|
}
|
|
9989
10306
|
}
|
|
9990
10307
|
/**
|
|
9991
10308
|
* Apply retention policy: keep N daily, M weekly, L monthly backups.
|
|
9992
10309
|
*/
|
|
9993
10310
|
applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
|
|
9994
|
-
const entries = this._loadIndex().filter((e) =>
|
|
10311
|
+
const entries = this._loadIndex().filter((e) => fs25.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
9995
10312
|
const keep = /* @__PURE__ */ new Set();
|
|
9996
10313
|
const now = /* @__PURE__ */ new Date();
|
|
9997
10314
|
const dailyCutoff = new Date(now);
|
|
@@ -10021,7 +10338,7 @@ var BackupService = class {
|
|
|
10021
10338
|
for (const e of entries) {
|
|
10022
10339
|
if (!keep.has(e.id)) {
|
|
10023
10340
|
try {
|
|
10024
|
-
|
|
10341
|
+
fs25.unlinkSync(e.path);
|
|
10025
10342
|
deleted++;
|
|
10026
10343
|
} catch {
|
|
10027
10344
|
}
|
|
@@ -10033,17 +10350,17 @@ var BackupService = class {
|
|
|
10033
10350
|
}
|
|
10034
10351
|
// ── Index helpers ──────────────────────────────────────────────────────────
|
|
10035
10352
|
_indexPath() {
|
|
10036
|
-
return
|
|
10353
|
+
return path32.join(this.backupDir, "index.json");
|
|
10037
10354
|
}
|
|
10038
10355
|
_loadIndex() {
|
|
10039
10356
|
try {
|
|
10040
|
-
return JSON.parse(
|
|
10357
|
+
return JSON.parse(fs25.readFileSync(this._indexPath(), "utf-8"));
|
|
10041
10358
|
} catch {
|
|
10042
10359
|
return [];
|
|
10043
10360
|
}
|
|
10044
10361
|
}
|
|
10045
10362
|
_saveIndex(entries) {
|
|
10046
|
-
|
|
10363
|
+
fs25.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
|
|
10047
10364
|
}
|
|
10048
10365
|
_appendIndex(entry) {
|
|
10049
10366
|
const entries = this._loadIndex();
|
|
@@ -10836,11 +11153,11 @@ var openApiSpec = {
|
|
|
10836
11153
|
};
|
|
10837
11154
|
|
|
10838
11155
|
// src/http/app.ts
|
|
10839
|
-
var __dirname$1 =
|
|
11156
|
+
var __dirname$1 = path32.dirname(fileURLToPath(import.meta.url));
|
|
10840
11157
|
var WEB_DIST = (() => {
|
|
10841
|
-
const bundled =
|
|
10842
|
-
if (
|
|
10843
|
-
return
|
|
11158
|
+
const bundled = path32.resolve(__dirname$1, "..", "web");
|
|
11159
|
+
if (fs25.existsSync(bundled)) return bundled;
|
|
11160
|
+
return path32.resolve(__dirname$1, "..", "..", "..", "web", "dist");
|
|
10844
11161
|
})();
|
|
10845
11162
|
function getAllowedOrigins() {
|
|
10846
11163
|
const env = process.env["CODE_INTEL_CORS_ORIGINS"];
|
|
@@ -10849,13 +11166,17 @@ function getAllowedOrigins() {
|
|
|
10849
11166
|
}
|
|
10850
11167
|
function createDefaultLimiter() {
|
|
10851
11168
|
const max = parseInt(process.env["CODE_INTEL_RATE_LIMIT_MAX"] ?? "100", 10);
|
|
10852
|
-
const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${
|
|
11169
|
+
const windowMs = parseInt(process.env["CODE_INTEL_RATE_LIMIT_WINDOW_MS"] ?? `${60 * 1e3}`, 10);
|
|
10853
11170
|
return rateLimit({
|
|
10854
11171
|
windowMs,
|
|
10855
11172
|
max,
|
|
10856
11173
|
standardHeaders: true,
|
|
10857
11174
|
legacyHeaders: false,
|
|
10858
|
-
|
|
11175
|
+
// Skip health checks, metrics, and read-only listing/pagination endpoints.
|
|
11176
|
+
// The node pagination and group/repo listing endpoints are hit many times
|
|
11177
|
+
// when loading a large graph — rate-limiting them only hurts the user's own
|
|
11178
|
+
// session without providing meaningful abuse protection.
|
|
11179
|
+
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),
|
|
10859
11180
|
message: {
|
|
10860
11181
|
error: {
|
|
10861
11182
|
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
|
|
@@ -10914,12 +11235,31 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10914
11235
|
});
|
|
10915
11236
|
app.use(requestIdMiddleware);
|
|
10916
11237
|
app.use(authMiddleware);
|
|
11238
|
+
let dbUnavailableSince = null;
|
|
10917
11239
|
app.use((_req, res, next) => {
|
|
10918
11240
|
if (workspaceRoot) {
|
|
11241
|
+
const metaFilePath = path32.join(workspaceRoot, ".code-intel", "meta.json");
|
|
11242
|
+
let metaOk = false;
|
|
10919
11243
|
try {
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
11244
|
+
if (fs25.existsSync(metaFilePath)) {
|
|
11245
|
+
const raw = fs25.readFileSync(metaFilePath, "utf-8");
|
|
11246
|
+
const meta = JSON.parse(raw);
|
|
11247
|
+
if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
|
|
11248
|
+
}
|
|
11249
|
+
metaOk = true;
|
|
11250
|
+
if (dbUnavailableSince !== null) {
|
|
11251
|
+
dbUnavailableSince = null;
|
|
11252
|
+
logger_default.info("[serve] DB back online \u2014 cleared stale flag");
|
|
11253
|
+
}
|
|
11254
|
+
} catch (err) {
|
|
11255
|
+
if (dbUnavailableSince === null) {
|
|
11256
|
+
dbUnavailableSince = (/* @__PURE__ */ new Date()).toISOString();
|
|
11257
|
+
logger_default.warn(`[serve] DB unavailable since ${dbUnavailableSince}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11258
|
+
}
|
|
11259
|
+
}
|
|
11260
|
+
if (!metaOk) {
|
|
11261
|
+
res.setHeader("X-Stale", "true");
|
|
11262
|
+
res.setHeader("X-Stale-Since", dbUnavailableSince);
|
|
10923
11263
|
}
|
|
10924
11264
|
}
|
|
10925
11265
|
next();
|
|
@@ -10975,6 +11315,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10975
11315
|
let vectorIndex = null;
|
|
10976
11316
|
let vectorIndexBuilding = false;
|
|
10977
11317
|
let vectorIndexReady = false;
|
|
11318
|
+
let bm25Index = null;
|
|
11319
|
+
function ensureBm25Index() {
|
|
11320
|
+
if (bm25Index) return bm25Index;
|
|
11321
|
+
if (!workspaceRoot) return null;
|
|
11322
|
+
const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
|
|
11323
|
+
idx.load();
|
|
11324
|
+
if (idx.isLoaded) {
|
|
11325
|
+
bm25Index = idx;
|
|
11326
|
+
return idx;
|
|
11327
|
+
}
|
|
11328
|
+
return null;
|
|
11329
|
+
}
|
|
11330
|
+
if (workspaceRoot && process.env["NODE_ENV"] !== "test") {
|
|
11331
|
+
setImmediate(() => ensureBm25Index());
|
|
11332
|
+
}
|
|
10978
11333
|
async function ensureVectorIndex() {
|
|
10979
11334
|
if (vectorIndexReady && vectorIndex) return vectorIndex;
|
|
10980
11335
|
if (!workspaceRoot || vectorIndexBuilding) return null;
|
|
@@ -11371,10 +11726,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11371
11726
|
const registry = loadRegistry();
|
|
11372
11727
|
const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
|
|
11373
11728
|
if (!entry) return null;
|
|
11374
|
-
const dbPath =
|
|
11375
|
-
if (!
|
|
11729
|
+
const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
|
|
11730
|
+
if (!fs25.existsSync(dbPath)) return null;
|
|
11376
11731
|
const repoGraph = createKnowledgeGraph();
|
|
11377
|
-
const db = new DbManager(dbPath);
|
|
11732
|
+
const db = new DbManager(dbPath, true);
|
|
11378
11733
|
try {
|
|
11379
11734
|
await db.init();
|
|
11380
11735
|
await loadGraphFromDB(repoGraph, db);
|
|
@@ -11385,10 +11740,33 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11385
11740
|
return null;
|
|
11386
11741
|
}
|
|
11387
11742
|
}
|
|
11743
|
+
async function loadGroupGraph(groupName) {
|
|
11744
|
+
const group = loadGroup(groupName);
|
|
11745
|
+
if (!group) return null;
|
|
11746
|
+
const registry = loadRegistry();
|
|
11747
|
+
const mergedGraph = createKnowledgeGraph();
|
|
11748
|
+
for (const member of group.members) {
|
|
11749
|
+
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11750
|
+
if (!regEntry) continue;
|
|
11751
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
11752
|
+
if (!fs25.existsSync(dbPath)) continue;
|
|
11753
|
+
const db = new DbManager(dbPath, true);
|
|
11754
|
+
try {
|
|
11755
|
+
await db.init();
|
|
11756
|
+
await loadGraphFromDB(mergedGraph, db);
|
|
11757
|
+
db.close();
|
|
11758
|
+
} catch {
|
|
11759
|
+
db.close();
|
|
11760
|
+
}
|
|
11761
|
+
}
|
|
11762
|
+
return mergedGraph.size.nodes > 0 ? mergedGraph : null;
|
|
11763
|
+
}
|
|
11388
11764
|
async function getGraphForRepo(requestedRepo) {
|
|
11389
11765
|
if (!requestedRepo || requestedRepo === repoName) return graph;
|
|
11390
11766
|
const g = await loadRepoGraph(requestedRepo);
|
|
11391
|
-
|
|
11767
|
+
if (g) return g;
|
|
11768
|
+
const gg = await loadGroupGraph(requestedRepo);
|
|
11769
|
+
return gg ?? graph;
|
|
11392
11770
|
}
|
|
11393
11771
|
app.get("/api/v1/graph/:repo", requireRepoAccess((req) => {
|
|
11394
11772
|
const p = req.params["repo"];
|
|
@@ -11412,11 +11790,56 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11412
11790
|
}
|
|
11413
11791
|
res.json({ nodes: [...g.allNodes()], edges: [...g.allEdges()] });
|
|
11414
11792
|
});
|
|
11793
|
+
app.get("/api/v1/graph/:repo/nodes", requireRepoAccess((req) => {
|
|
11794
|
+
const p = req.params["repo"];
|
|
11795
|
+
const repo = Array.isArray(p) ? p[0] : p;
|
|
11796
|
+
return repo ? decodeURIComponent(repo) : void 0;
|
|
11797
|
+
}), async (req, res) => {
|
|
11798
|
+
const rawRepo = req.params["repo"];
|
|
11799
|
+
const requestedRepo = decodeURIComponent(Array.isArray(rawRepo) ? rawRepo[0] ?? "" : rawRepo ?? "");
|
|
11800
|
+
const limit = Math.min(parseInt(req.query["limit"] ?? "200", 10), 1e3);
|
|
11801
|
+
const offset = Math.max(parseInt(req.query["offset"] ?? "0", 10), 0);
|
|
11802
|
+
const g = requestedRepo === repoName ? graph : await loadRepoGraph(requestedRepo);
|
|
11803
|
+
if (!g) {
|
|
11804
|
+
res.status(404).json({
|
|
11805
|
+
error: {
|
|
11806
|
+
code: ErrorCodes.NOT_FOUND,
|
|
11807
|
+
message: `Repo "${requestedRepo}" not found or not indexed`,
|
|
11808
|
+
hint: `Run: code-intel analyze <path>`,
|
|
11809
|
+
requestId: req.requestId,
|
|
11810
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11811
|
+
}
|
|
11812
|
+
});
|
|
11813
|
+
return;
|
|
11814
|
+
}
|
|
11815
|
+
let nodes;
|
|
11816
|
+
if (isLazyGraph(g)) {
|
|
11817
|
+
nodes = await g.getNodePage(offset, limit);
|
|
11818
|
+
} else {
|
|
11819
|
+
const eager = g;
|
|
11820
|
+
if (!eager._nodeArray) {
|
|
11821
|
+
eager._nodeArray = [...g.allNodes()];
|
|
11822
|
+
}
|
|
11823
|
+
nodes = eager._nodeArray.slice(offset, offset + limit);
|
|
11824
|
+
}
|
|
11825
|
+
res.json({
|
|
11826
|
+
nodes,
|
|
11827
|
+
offset,
|
|
11828
|
+
limit,
|
|
11829
|
+
total: g.size.nodes,
|
|
11830
|
+
hasMore: offset + nodes.length < g.size.nodes
|
|
11831
|
+
});
|
|
11832
|
+
});
|
|
11415
11833
|
app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
|
|
11416
11834
|
const { query, limit, repo } = req.body;
|
|
11417
11835
|
const g = await getGraphForRepo(repo);
|
|
11418
11836
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
11419
|
-
const
|
|
11837
|
+
const bm25 = !repo || repo === repoName ? ensureBm25Index() : null;
|
|
11838
|
+
const bm25Results = bm25 ? bm25.search(query ?? "", (limit ?? 20) * 3) : null;
|
|
11839
|
+
const { results, searchMode } = await hybridSearch(g, query ?? "", limit ?? 20, {
|
|
11840
|
+
vectorDbPath: vdbPath,
|
|
11841
|
+
bm25Results: bm25Results ?? void 0
|
|
11842
|
+
});
|
|
11420
11843
|
res.json({ results, searchMode });
|
|
11421
11844
|
});
|
|
11422
11845
|
app.post("/api/v1/vector-search", async (req, res) => {
|
|
@@ -11459,7 +11882,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11459
11882
|
return;
|
|
11460
11883
|
}
|
|
11461
11884
|
try {
|
|
11462
|
-
const content =
|
|
11885
|
+
const content = fs25.readFileSync(file_path, "utf-8");
|
|
11463
11886
|
res.json({ content });
|
|
11464
11887
|
} catch {
|
|
11465
11888
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
|
|
@@ -11496,7 +11919,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11496
11919
|
if (workspaceRoot) {
|
|
11497
11920
|
try {
|
|
11498
11921
|
const dbPath = getDbPath(workspaceRoot);
|
|
11499
|
-
const dbm = new DbManager(dbPath);
|
|
11922
|
+
const dbm = new DbManager(dbPath, true);
|
|
11500
11923
|
await dbm.init();
|
|
11501
11924
|
const rows = await dbm.query(q);
|
|
11502
11925
|
dbm.close();
|
|
@@ -11537,33 +11960,53 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11537
11960
|
app.get("/api/v1/nodes/:id", async (req, res) => {
|
|
11538
11961
|
const nodeId = decodeURIComponent(req.params.id);
|
|
11539
11962
|
const g = await getGraphForRepo(req.query["repo"]);
|
|
11540
|
-
const node = g.getNode(nodeId);
|
|
11963
|
+
const node = isLazyGraph(g) ? await g.getNodeAsync(nodeId) : g.getNode(nodeId);
|
|
11541
11964
|
if (!node) {
|
|
11542
11965
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Node not found", requestId: req.requestId } });
|
|
11543
11966
|
return;
|
|
11544
11967
|
}
|
|
11545
11968
|
const incoming = [...g.findEdgesTo(nodeId)];
|
|
11546
11969
|
const outgoing = [...g.findEdgesFrom(nodeId)];
|
|
11970
|
+
const resolveName = isLazyGraph(g) ? async (id) => {
|
|
11971
|
+
const n = g.getNode(id) ?? await g.getNodeAsync(id);
|
|
11972
|
+
return n?.name;
|
|
11973
|
+
} : (id) => Promise.resolve(g.getNode(id)?.name);
|
|
11974
|
+
const resolveKind = isLazyGraph(g) ? async (id) => {
|
|
11975
|
+
const n = g.getNode(id) ?? await g.getNodeAsync(id);
|
|
11976
|
+
return n?.kind;
|
|
11977
|
+
} : (id) => Promise.resolve(g.getNode(id)?.kind);
|
|
11547
11978
|
res.json({
|
|
11548
11979
|
node,
|
|
11549
|
-
callers: incoming.filter((e) => e.kind === "calls").map((e) => ({ id: e.source, name:
|
|
11550
|
-
callees: outgoing.filter((e) => e.kind === "calls").map((e) => ({ id: e.target, name:
|
|
11551
|
-
imports: outgoing.filter((e) => e.kind === "imports").map((e) => ({ id: e.target, name:
|
|
11552
|
-
importedBy: incoming.filter((e) => e.kind === "imports").map((e) => ({ id: e.source, name:
|
|
11553
|
-
extends: outgoing.filter((e) => e.kind === "extends").map((e) => ({ id: e.target, name:
|
|
11554
|
-
implementsEdges: outgoing.filter((e) => e.kind === "implements").map((e) => ({ id: e.target, name:
|
|
11555
|
-
members: outgoing.filter((e) => e.kind === "has_member").map((e) => ({ id: e.target, name:
|
|
11556
|
-
cluster: incoming.filter((e) => e.kind === "belongs_to").map((e) =>
|
|
11980
|
+
callers: await Promise.all(incoming.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.source, name: await resolveName(e.source), weight: e.weight }))),
|
|
11981
|
+
callees: await Promise.all(outgoing.filter((e) => e.kind === "calls").map(async (e) => ({ id: e.target, name: await resolveName(e.target), weight: e.weight }))),
|
|
11982
|
+
imports: await Promise.all(outgoing.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
11983
|
+
importedBy: await Promise.all(incoming.filter((e) => e.kind === "imports").map(async (e) => ({ id: e.source, name: await resolveName(e.source) }))),
|
|
11984
|
+
extends: await Promise.all(outgoing.filter((e) => e.kind === "extends").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
11985
|
+
implementsEdges: await Promise.all(outgoing.filter((e) => e.kind === "implements").map(async (e) => ({ id: e.target, name: await resolveName(e.target) }))),
|
|
11986
|
+
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) }))),
|
|
11987
|
+
cluster: (await Promise.all(incoming.filter((e) => e.kind === "belongs_to").map(async (e) => resolveName(e.target))))[0]
|
|
11557
11988
|
});
|
|
11558
11989
|
});
|
|
11559
11990
|
app.post("/api/v1/blast-radius", async (req, res) => {
|
|
11560
11991
|
const { target, direction = "both", max_hops = 5, repo } = req.body;
|
|
11561
11992
|
const g = await getGraphForRepo(repo);
|
|
11562
11993
|
let targetNode = null;
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11994
|
+
if (isLazyGraph(g) && target) {
|
|
11995
|
+
targetNode = g.getNode(target) ?? await g.getNodeAsync(target) ?? null;
|
|
11996
|
+
if (!targetNode) {
|
|
11997
|
+
for await (const node of g.allNodesAsync()) {
|
|
11998
|
+
if (node.name === target || node.id === target) {
|
|
11999
|
+
targetNode = node;
|
|
12000
|
+
break;
|
|
12001
|
+
}
|
|
12002
|
+
}
|
|
12003
|
+
}
|
|
12004
|
+
} else {
|
|
12005
|
+
for (const node of g.allNodes()) {
|
|
12006
|
+
if (node.name === target || node.id === target) {
|
|
12007
|
+
targetNode = node;
|
|
12008
|
+
break;
|
|
12009
|
+
}
|
|
11567
12010
|
}
|
|
11568
12011
|
}
|
|
11569
12012
|
if (!targetNode) {
|
|
@@ -11717,9 +12160,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11717
12160
|
for (const member of group.members) {
|
|
11718
12161
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11719
12162
|
if (!regEntry) continue;
|
|
11720
|
-
const dbPath =
|
|
11721
|
-
if (!
|
|
11722
|
-
const db = new DbManager(dbPath);
|
|
12163
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
12164
|
+
if (!fs25.existsSync(dbPath)) continue;
|
|
12165
|
+
const db = new DbManager(dbPath, true);
|
|
11723
12166
|
try {
|
|
11724
12167
|
await db.init();
|
|
11725
12168
|
await loadGraphFromDB(mergedGraph, db);
|
|
@@ -11744,10 +12187,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11744
12187
|
let nodeCount = 0;
|
|
11745
12188
|
let edgeCount = 0;
|
|
11746
12189
|
if (regEntry) {
|
|
11747
|
-
const dbPath =
|
|
11748
|
-
if (
|
|
12190
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
12191
|
+
if (fs25.existsSync(dbPath)) {
|
|
11749
12192
|
try {
|
|
11750
|
-
const db = new DbManager(dbPath);
|
|
12193
|
+
const db = new DbManager(dbPath, true);
|
|
11751
12194
|
await db.init();
|
|
11752
12195
|
const g = createKnowledgeGraph();
|
|
11753
12196
|
await loadGraphFromDB(g, db);
|
|
@@ -11770,7 +12213,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11770
12213
|
res.json({ repos, edges });
|
|
11771
12214
|
});
|
|
11772
12215
|
app.get("/api/v1/source", requireAuth, requireRole("viewer"), (req, res) => {
|
|
11773
|
-
const { file, startLine: startLineStr, endLine: endLineStr } = req.query;
|
|
12216
|
+
const { file, startLine: startLineStr, endLine: endLineStr, repo } = req.query;
|
|
11774
12217
|
if (!file) {
|
|
11775
12218
|
res.status(400).json({
|
|
11776
12219
|
error: {
|
|
@@ -11794,14 +12237,36 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11794
12237
|
});
|
|
11795
12238
|
return;
|
|
11796
12239
|
}
|
|
11797
|
-
let
|
|
11798
|
-
if (
|
|
11799
|
-
|
|
12240
|
+
let baseDir = workspaceRoot;
|
|
12241
|
+
if (repo && repo !== repoName) {
|
|
12242
|
+
const registry = loadRegistry();
|
|
12243
|
+
const entry = registry.find((r) => r.name === repo || r.path === repo);
|
|
12244
|
+
if (entry) {
|
|
12245
|
+
baseDir = entry.path;
|
|
12246
|
+
} else {
|
|
12247
|
+
const group = loadGroup(repo);
|
|
12248
|
+
if (group) {
|
|
12249
|
+
const normalizedFile = path32.normalize(file);
|
|
12250
|
+
for (const member of group.members) {
|
|
12251
|
+
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
12252
|
+
if (!regEntry) continue;
|
|
12253
|
+
const candidate = path32.resolve(path32.join(regEntry.path, normalizedFile));
|
|
12254
|
+
if (fs25.existsSync(candidate)) {
|
|
12255
|
+
baseDir = regEntry.path;
|
|
12256
|
+
break;
|
|
12257
|
+
}
|
|
12258
|
+
}
|
|
12259
|
+
}
|
|
12260
|
+
}
|
|
12261
|
+
}
|
|
12262
|
+
let rawResolved = path32.normalize(file);
|
|
12263
|
+
if (!path32.isAbsolute(rawResolved) && baseDir) {
|
|
12264
|
+
rawResolved = path32.join(baseDir, rawResolved);
|
|
11800
12265
|
}
|
|
11801
|
-
const resolvedFile =
|
|
12266
|
+
const resolvedFile = path32.resolve(rawResolved);
|
|
11802
12267
|
function isInsideDir(fileAbs, dir) {
|
|
11803
|
-
const rel =
|
|
11804
|
-
return !rel.startsWith("..") && !
|
|
12268
|
+
const rel = path32.relative(path32.resolve(dir), fileAbs);
|
|
12269
|
+
return !rel.startsWith("..") && !path32.isAbsolute(rel);
|
|
11805
12270
|
}
|
|
11806
12271
|
if (workspaceRoot) {
|
|
11807
12272
|
if (!isInsideDir(resolvedFile, workspaceRoot)) {
|
|
@@ -11838,7 +12303,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11838
12303
|
}
|
|
11839
12304
|
let fileContent;
|
|
11840
12305
|
try {
|
|
11841
|
-
fileContent =
|
|
12306
|
+
fileContent = fs25.readFileSync(resolvedFile, "utf-8");
|
|
11842
12307
|
} catch {
|
|
11843
12308
|
res.status(404).json({
|
|
11844
12309
|
error: {
|
|
@@ -11869,7 +12334,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11869
12334
|
const contextStart = Math.max(1, startLine - 20);
|
|
11870
12335
|
const contextEnd = Math.min(lines.length, endLine + 20);
|
|
11871
12336
|
const content = lines.slice(contextStart - 1, contextEnd).join("\n");
|
|
11872
|
-
const ext =
|
|
12337
|
+
const ext = path32.extname(resolvedFile).toLowerCase();
|
|
11873
12338
|
const languageMap = {
|
|
11874
12339
|
".ts": "typescript",
|
|
11875
12340
|
".tsx": "typescript",
|
|
@@ -12004,10 +12469,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12004
12469
|
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() } });
|
|
12005
12470
|
}
|
|
12006
12471
|
});
|
|
12007
|
-
if (
|
|
12472
|
+
if (fs25.existsSync(WEB_DIST)) {
|
|
12008
12473
|
app.use(express.static(WEB_DIST));
|
|
12009
12474
|
app.get("/{*path}", (_req, res) => {
|
|
12010
|
-
res.sendFile(
|
|
12475
|
+
res.sendFile(path32.join(WEB_DIST, "index.html"));
|
|
12011
12476
|
});
|
|
12012
12477
|
}
|
|
12013
12478
|
app.use("/admin", requireRole("admin"));
|
|
@@ -12167,6 +12632,6 @@ function mergeSearchResults(...perRepoResults) {
|
|
|
12167
12632
|
return reciprocalRankFusion(...perRepoResults);
|
|
12168
12633
|
}
|
|
12169
12634
|
|
|
12170
|
-
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 };
|
|
12635
|
+
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 };
|
|
12171
12636
|
//# sourceMappingURL=index.js.map
|
|
12172
12637
|
//# sourceMappingURL=index.js.map
|