@vohongtho.infotech/code-intel 0.9.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 -125
- package/dist/cli/main.js +1836 -577
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +71 -13
- package/dist/index.js +1263 -807
- package/dist/index.js.map +1 -1
- package/dist/web/assets/{es-CkND97V_.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-DExLzJ89.js +0 -348
- package/dist/web/assets/index-DSIgTcZc.css +0 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import { createRequire } from 'module';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
import path31 from 'path';
|
|
4
|
-
import fs24, { existsSync } from 'fs';
|
|
5
|
-
import { Parser, Language, Query } from 'web-tree-sitter';
|
|
6
1
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
7
2
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
8
3
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
@@ -11,9 +6,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';
|
|
9
|
+
import fs25, { existsSync } from 'fs';
|
|
10
|
+
import path32 from 'path';
|
|
14
11
|
import os13 from 'os';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { Parser, Language, Query } from 'web-tree-sitter';
|
|
15
15
|
import { execSync } from 'child_process';
|
|
16
|
-
import
|
|
16
|
+
import Database2 from 'better-sqlite3';
|
|
17
17
|
import bcrypt from 'bcrypt';
|
|
18
18
|
import crypto5 from 'crypto';
|
|
19
19
|
import { v4 } from 'uuid';
|
|
@@ -68,220 +68,553 @@ 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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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"() {
|
|
616
|
+
typescriptQueries = `
|
|
617
|
+
;; Class declaration
|
|
285
618
|
(class_declaration
|
|
286
619
|
name: (type_identifier) @def.class.name) @def.class
|
|
287
620
|
|
|
@@ -889,342 +1222,9 @@ var init_swift = __esm({
|
|
|
889
1222
|
|
|
890
1223
|
;; Property declaration
|
|
891
1224
|
(property_declaration
|
|
892
|
-
(pattern
|
|
893
|
-
(simple_identifier) @def.property.name)) @def.property
|
|
894
|
-
`;
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
// src/observability/tracing.ts
|
|
899
|
-
var tracing_exports = {};
|
|
900
|
-
__export(tracing_exports, {
|
|
901
|
-
SpanStatusCode: () => SpanStatusCode,
|
|
902
|
-
context: () => context,
|
|
903
|
-
getActiveTraceContext: () => getActiveTraceContext,
|
|
904
|
-
getTracer: () => getTracer,
|
|
905
|
-
initTracing: () => initTracing,
|
|
906
|
-
isTracingEnabled: () => isTracingEnabled,
|
|
907
|
-
sanitizeAttrs: () => sanitizeAttrs,
|
|
908
|
-
shutdownTracing: () => shutdownTracing,
|
|
909
|
-
trace: () => trace,
|
|
910
|
-
withSpan: () => withSpan
|
|
911
|
-
});
|
|
912
|
-
function isTracingEnabled() {
|
|
913
|
-
return process.env["CODE_INTEL_OTEL_ENABLED"] === "true";
|
|
914
|
-
}
|
|
915
|
-
function initTracing() {
|
|
916
|
-
if (!isTracingEnabled()) return;
|
|
917
|
-
if (_sdk) return;
|
|
918
|
-
const endpoint = process.env["CODE_INTEL_OTEL_ENDPOINT"] ?? "http://localhost:4318";
|
|
919
|
-
const serviceName = process.env["CODE_INTEL_OTEL_SERVICE"] ?? "code-intel";
|
|
920
|
-
const deploymentEnv = process.env["CODE_INTEL_OTEL_ENV"] ?? process.env["NODE_ENV"] ?? "development";
|
|
921
|
-
const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });
|
|
922
|
-
_sdk = new NodeSDK({
|
|
923
|
-
resource: resourceFromAttributes({
|
|
924
|
-
[SEMRESATTRS_SERVICE_NAME]: serviceName,
|
|
925
|
-
[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: deploymentEnv
|
|
926
|
-
}),
|
|
927
|
-
traceExporter: exporter,
|
|
928
|
-
instrumentations: [
|
|
929
|
-
getNodeAutoInstrumentations({
|
|
930
|
-
// Disable noisy file-system instrumentation
|
|
931
|
-
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
932
|
-
})
|
|
933
|
-
]
|
|
934
|
-
});
|
|
935
|
-
_sdk.start();
|
|
936
|
-
}
|
|
937
|
-
async function shutdownTracing() {
|
|
938
|
-
if (_sdk) {
|
|
939
|
-
await _sdk.shutdown();
|
|
940
|
-
_sdk = null;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
function getTracer() {
|
|
944
|
-
return trace.getTracer(TRACER_NAME);
|
|
945
|
-
}
|
|
946
|
-
async function withSpan(name, attrs, fn) {
|
|
947
|
-
return getTracer().startActiveSpan(name, { attributes: sanitizeAttrs(attrs) }, async (span) => {
|
|
948
|
-
try {
|
|
949
|
-
const result = await fn(span);
|
|
950
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
951
|
-
return result;
|
|
952
|
-
} catch (err) {
|
|
953
|
-
span.setStatus({
|
|
954
|
-
code: SpanStatusCode.ERROR,
|
|
955
|
-
message: err instanceof Error ? err.message : String(err)
|
|
956
|
-
});
|
|
957
|
-
span.recordException(err);
|
|
958
|
-
throw err;
|
|
959
|
-
} finally {
|
|
960
|
-
span.end();
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
function sanitizeAttrs(attrs) {
|
|
965
|
-
const safe = {};
|
|
966
|
-
for (const [k, v] of Object.entries(attrs)) {
|
|
967
|
-
if (BLOCKED_ATTR_KEYS.test(k)) continue;
|
|
968
|
-
safe[k] = v;
|
|
969
|
-
}
|
|
970
|
-
return safe;
|
|
971
|
-
}
|
|
972
|
-
function getActiveTraceContext() {
|
|
973
|
-
const span = trace.getActiveSpan();
|
|
974
|
-
if (!span) return { traceId: "", spanId: "" };
|
|
975
|
-
const ctx = span.spanContext();
|
|
976
|
-
return { traceId: ctx.traceId, spanId: ctx.spanId };
|
|
977
|
-
}
|
|
978
|
-
var _sdk, TRACER_NAME, BLOCKED_ATTR_KEYS;
|
|
979
|
-
var init_tracing = __esm({
|
|
980
|
-
"src/observability/tracing.ts"() {
|
|
981
|
-
_sdk = null;
|
|
982
|
-
TRACER_NAME = "code-intel";
|
|
983
|
-
BLOCKED_ATTR_KEYS = /secret|password|token|key|auth|credential/i;
|
|
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(os13.homedir(), ".code-intel", "logs");
|
|
1166
|
-
static getLogger() {
|
|
1167
|
-
if (!_Logger.instance) {
|
|
1168
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
1169
|
-
const logLevel = process.env.LOG_LEVEL ?? "info";
|
|
1170
|
-
const transports = [];
|
|
1171
|
-
transports.push(new winston.transports.Console());
|
|
1172
|
-
if (!isProduction) {
|
|
1173
|
-
try {
|
|
1174
|
-
if (!fs24.existsSync(_Logger.LOG_DIR)) {
|
|
1175
|
-
fs24.mkdirSync(_Logger.LOG_DIR, { recursive: true });
|
|
1176
|
-
}
|
|
1177
|
-
transports.push(
|
|
1178
|
-
new DailyRotateFile({
|
|
1179
|
-
filename: path31.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
|
|
1180
|
-
datePattern: "YYYY-MM-DD",
|
|
1181
|
-
maxSize: "20m",
|
|
1182
|
-
maxFiles: "14d"
|
|
1183
|
-
})
|
|
1184
|
-
);
|
|
1185
|
-
} catch {
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
_Logger.instance = winston.createLogger({
|
|
1189
|
-
level: logLevel,
|
|
1190
|
-
format: winston.format.combine(
|
|
1191
|
-
winston.format.timestamp(),
|
|
1192
|
-
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
1193
|
-
const args = meta[/* @__PURE__ */ Symbol.for("splat")] || [];
|
|
1194
|
-
const { maskedMessage, maskedArgs } = _Logger.maskSensitive(message, args);
|
|
1195
|
-
const formattedArgs = maskedArgs.map(
|
|
1196
|
-
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
1197
|
-
);
|
|
1198
|
-
const suffix = formattedArgs.length ? " " + formattedArgs.join(" ") : "";
|
|
1199
|
-
let traceCtx = "";
|
|
1200
|
-
try {
|
|
1201
|
-
const { traceId, spanId } = getActiveTraceCtx();
|
|
1202
|
-
if (traceId) traceCtx = ` [trace=${traceId} span=${spanId}]`;
|
|
1203
|
-
} catch {
|
|
1204
|
-
}
|
|
1205
|
-
return `${timestamp} [${level.toUpperCase()}]${traceCtx}: ${maskedMessage}${suffix}`;
|
|
1206
|
-
})
|
|
1207
|
-
),
|
|
1208
|
-
transports
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
return _Logger.instance;
|
|
1212
|
-
}
|
|
1213
|
-
static info(message, ...args) {
|
|
1214
|
-
_Logger.getLogger().info(message, ...args);
|
|
1215
|
-
}
|
|
1216
|
-
static warn(message, ...args) {
|
|
1217
|
-
_Logger.getLogger().warn(message, ...args);
|
|
1218
|
-
}
|
|
1219
|
-
static error(message, ...args) {
|
|
1220
|
-
_Logger.getLogger().error(message, ...args);
|
|
1221
|
-
}
|
|
1222
|
-
static debug(message, ...args) {
|
|
1223
|
-
_Logger.getLogger().debug(message, ...args);
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
logger_default = Logger;
|
|
1227
|
-
Logger.getLogger();
|
|
1225
|
+
(pattern
|
|
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);
|
|
@@ -4288,10 +4288,10 @@ var init_codes = __esm({
|
|
|
4288
4288
|
}
|
|
4289
4289
|
});
|
|
4290
4290
|
function secureMkdir(dir) {
|
|
4291
|
-
|
|
4291
|
+
fs25.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
4292
4292
|
if (process.platform !== "win32") {
|
|
4293
4293
|
try {
|
|
4294
|
-
|
|
4294
|
+
fs25.chmodSync(dir, SECURE_DIR_MODE);
|
|
4295
4295
|
} catch {
|
|
4296
4296
|
}
|
|
4297
4297
|
}
|
|
@@ -4299,17 +4299,17 @@ function secureMkdir(dir) {
|
|
|
4299
4299
|
function secureChmodFile(file) {
|
|
4300
4300
|
if (process.platform === "win32") return;
|
|
4301
4301
|
try {
|
|
4302
|
-
|
|
4302
|
+
fs25.chmodSync(file, SECURE_FILE_MODE);
|
|
4303
4303
|
} catch {
|
|
4304
4304
|
}
|
|
4305
4305
|
}
|
|
4306
4306
|
function tightenDbFiles(dir) {
|
|
4307
4307
|
if (process.platform === "win32") return;
|
|
4308
|
-
if (!
|
|
4309
|
-
for (const name of
|
|
4308
|
+
if (!fs25.existsSync(dir)) return;
|
|
4309
|
+
for (const name of fs25.readdirSync(dir)) {
|
|
4310
4310
|
if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
|
|
4311
4311
|
try {
|
|
4312
|
-
|
|
4312
|
+
fs25.chmodSync(path32.join(dir, name), SECURE_FILE_MODE);
|
|
4313
4313
|
} catch {
|
|
4314
4314
|
}
|
|
4315
4315
|
}
|
|
@@ -4323,7 +4323,7 @@ var init_fs_secure = __esm({
|
|
|
4323
4323
|
}
|
|
4324
4324
|
});
|
|
4325
4325
|
function getUsersDBPath() {
|
|
4326
|
-
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");
|
|
4327
4327
|
}
|
|
4328
4328
|
function getOrCreateUsersDB() {
|
|
4329
4329
|
if (!_usersDB) {
|
|
@@ -4339,9 +4339,9 @@ var init_users_db = __esm({
|
|
|
4339
4339
|
UsersDB = class {
|
|
4340
4340
|
db;
|
|
4341
4341
|
constructor(dbPath) {
|
|
4342
|
-
const dir =
|
|
4342
|
+
const dir = path32.dirname(dbPath);
|
|
4343
4343
|
secureMkdir(dir);
|
|
4344
|
-
this.db = new
|
|
4344
|
+
this.db = new Database2(dbPath);
|
|
4345
4345
|
this.db.pragma("journal_mode = WAL");
|
|
4346
4346
|
this.db.pragma("foreign_keys = ON");
|
|
4347
4347
|
this.createTables();
|
|
@@ -4616,7 +4616,7 @@ function getScryptN() {
|
|
|
4616
4616
|
return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
|
|
4617
4617
|
}
|
|
4618
4618
|
function getSecretsPath() {
|
|
4619
|
-
return process.env["CODE_INTEL_SECRETS_PATH"] ??
|
|
4619
|
+
return process.env["CODE_INTEL_SECRETS_PATH"] ?? path32.join(os13.homedir(), ".code-intel", ".secrets");
|
|
4620
4620
|
}
|
|
4621
4621
|
function getMasterPassword() {
|
|
4622
4622
|
const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
|
|
@@ -4647,8 +4647,8 @@ function decryptSecrets(encrypted) {
|
|
|
4647
4647
|
return JSON.parse(plaintext.toString("utf8"));
|
|
4648
4648
|
}
|
|
4649
4649
|
function loadSecrets(secretsPath = getSecretsPath()) {
|
|
4650
|
-
if (!
|
|
4651
|
-
const blob =
|
|
4650
|
+
if (!fs25.existsSync(secretsPath)) return {};
|
|
4651
|
+
const blob = fs25.readFileSync(secretsPath);
|
|
4652
4652
|
return decryptSecrets(blob);
|
|
4653
4653
|
}
|
|
4654
4654
|
function getSecret(key, secretsPath = getSecretsPath()) {
|
|
@@ -5158,6 +5158,71 @@ function createKnowledgeGraph() {
|
|
|
5158
5158
|
// src/graph/index.ts
|
|
5159
5159
|
init_id_generator();
|
|
5160
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
|
+
|
|
5161
5226
|
// src/parsing/index.ts
|
|
5162
5227
|
init_parser_manager();
|
|
5163
5228
|
init_query_runner();
|
|
@@ -5215,7 +5280,7 @@ init_shared();
|
|
|
5215
5280
|
init_shared();
|
|
5216
5281
|
init_typescript();
|
|
5217
5282
|
function resolveRelative(rawPath, fromFile, workspace) {
|
|
5218
|
-
const fromDir =
|
|
5283
|
+
const fromDir = path32.dirname(fromFile);
|
|
5219
5284
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5220
5285
|
const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
|
|
5221
5286
|
const resolved = workspace.resolve(fromDir, cleaned);
|
|
@@ -5267,7 +5332,7 @@ var pythonModule = {
|
|
|
5267
5332
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5268
5333
|
const cleaned = rawPath.replace(/['"]/g, "");
|
|
5269
5334
|
const parts = cleaned.split(".");
|
|
5270
|
-
const fromDir =
|
|
5335
|
+
const fromDir = path32.dirname(fromFile);
|
|
5271
5336
|
const relPath = parts.join("/");
|
|
5272
5337
|
for (const suffix of ["/__init__.py", ".py"]) {
|
|
5273
5338
|
const r = workspace.resolve(fromDir, relPath + suffix);
|
|
@@ -5346,7 +5411,7 @@ var cModule = {
|
|
|
5346
5411
|
inheritanceStrategy: "none",
|
|
5347
5412
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5348
5413
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5349
|
-
const fromDir =
|
|
5414
|
+
const fromDir = path32.dirname(fromFile);
|
|
5350
5415
|
return workspace.resolve(fromDir, cleaned);
|
|
5351
5416
|
},
|
|
5352
5417
|
isExported(_node) {
|
|
@@ -5369,7 +5434,7 @@ var cppModule = {
|
|
|
5369
5434
|
inheritanceStrategy: "depth-first",
|
|
5370
5435
|
resolveImport(rawPath, fromFile, workspace) {
|
|
5371
5436
|
const cleaned = rawPath.replace(/[<>"']/g, "");
|
|
5372
|
-
const fromDir =
|
|
5437
|
+
const fromDir = path32.dirname(fromFile);
|
|
5373
5438
|
return workspace.resolve(fromDir, cleaned);
|
|
5374
5439
|
},
|
|
5375
5440
|
isExported(_node) {
|
|
@@ -5531,7 +5596,7 @@ var dartModule = {
|
|
|
5531
5596
|
const pkg = cleaned.replace("package:", "");
|
|
5532
5597
|
return workspace.findByPackage(pkg);
|
|
5533
5598
|
}
|
|
5534
|
-
const fromDir =
|
|
5599
|
+
const fromDir = path32.dirname(fromFile);
|
|
5535
5600
|
return workspace.resolve(fromDir, cleaned);
|
|
5536
5601
|
},
|
|
5537
5602
|
isExported(node) {
|
|
@@ -5886,25 +5951,25 @@ function validateDAG(phases) {
|
|
|
5886
5951
|
const visiting = /* @__PURE__ */ new Set();
|
|
5887
5952
|
const visited = /* @__PURE__ */ new Set();
|
|
5888
5953
|
const phaseMap = new Map(phases.map((p) => [p.name, p]));
|
|
5889
|
-
function dfs(name,
|
|
5954
|
+
function dfs(name, path33) {
|
|
5890
5955
|
if (visiting.has(name)) {
|
|
5891
|
-
const cycleStart =
|
|
5892
|
-
const cycle =
|
|
5956
|
+
const cycleStart = path33.indexOf(name);
|
|
5957
|
+
const cycle = path33.slice(cycleStart).concat(name);
|
|
5893
5958
|
errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
|
|
5894
5959
|
return true;
|
|
5895
5960
|
}
|
|
5896
5961
|
if (visited.has(name)) return false;
|
|
5897
5962
|
visiting.add(name);
|
|
5898
|
-
|
|
5963
|
+
path33.push(name);
|
|
5899
5964
|
const phase = phaseMap.get(name);
|
|
5900
5965
|
if (phase) {
|
|
5901
5966
|
for (const dep of phase.dependencies) {
|
|
5902
|
-
if (dfs(dep,
|
|
5967
|
+
if (dfs(dep, path33)) return true;
|
|
5903
5968
|
}
|
|
5904
5969
|
}
|
|
5905
5970
|
visiting.delete(name);
|
|
5906
5971
|
visited.add(name);
|
|
5907
|
-
|
|
5972
|
+
path33.pop();
|
|
5908
5973
|
return false;
|
|
5909
5974
|
}
|
|
5910
5975
|
for (const phase of phases) {
|
|
@@ -6032,6 +6097,7 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6032
6097
|
for (const phase of sorted) {
|
|
6033
6098
|
context2.onProgress?.(phase.name, "running");
|
|
6034
6099
|
const phaseStart = Date.now();
|
|
6100
|
+
const memBefore = context2.profile ? Math.round(process.memoryUsage().heapUsed / 1024 / 1024) : void 0;
|
|
6035
6101
|
const runPhase = async () => {
|
|
6036
6102
|
const depResults = /* @__PURE__ */ new Map();
|
|
6037
6103
|
for (const dep of phase.dependencies) {
|
|
@@ -6055,6 +6121,10 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6055
6121
|
}
|
|
6056
6122
|
const durationSec = (Date.now() - phaseStart) / 1e3;
|
|
6057
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
|
+
}
|
|
6058
6128
|
results.set(phase.name, result);
|
|
6059
6129
|
context2.onProgress?.(phase.name, result.status);
|
|
6060
6130
|
if (result.status === "failed") {
|
|
@@ -6065,7 +6135,9 @@ ${errors.map((e) => e.message).join("\n")}`);
|
|
|
6065
6135
|
const result = {
|
|
6066
6136
|
status: "failed",
|
|
6067
6137
|
duration: Date.now() - phaseStart,
|
|
6068
|
-
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
|
|
6069
6141
|
};
|
|
6070
6142
|
pipelinePhaseDurationSeconds.observe({ phase: phase.name, status: "failed" }, (Date.now() - phaseStart) / 1e3);
|
|
6071
6143
|
results.set(phase.name, result);
|
|
@@ -6117,7 +6189,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
6117
6189
|
]);
|
|
6118
6190
|
function loadIgnorePatterns(workspaceRoot) {
|
|
6119
6191
|
try {
|
|
6120
|
-
const raw =
|
|
6192
|
+
const raw = fs25.readFileSync(path32.join(workspaceRoot, ".codeintelignore"), "utf-8");
|
|
6121
6193
|
const extras = /* @__PURE__ */ new Set();
|
|
6122
6194
|
for (const line of raw.split("\n")) {
|
|
6123
6195
|
const trimmed = line.trim();
|
|
@@ -6141,7 +6213,7 @@ var scanPhase = {
|
|
|
6141
6213
|
function walk(dir) {
|
|
6142
6214
|
let entries;
|
|
6143
6215
|
try {
|
|
6144
|
-
entries =
|
|
6216
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
6145
6217
|
} catch {
|
|
6146
6218
|
return;
|
|
6147
6219
|
}
|
|
@@ -6150,15 +6222,15 @@ var scanPhase = {
|
|
|
6150
6222
|
if (entry.name.startsWith(".")) continue;
|
|
6151
6223
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6152
6224
|
if (extraIgnore.has(entry.name)) continue;
|
|
6153
|
-
walk(
|
|
6225
|
+
walk(path32.join(dir, entry.name));
|
|
6154
6226
|
} else if (entry.isFile()) {
|
|
6155
6227
|
const name = entry.name;
|
|
6156
6228
|
if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
|
|
6157
|
-
const ext =
|
|
6229
|
+
const ext = path32.extname(name);
|
|
6158
6230
|
if (!extensions.has(ext)) continue;
|
|
6159
|
-
const fullPath =
|
|
6231
|
+
const fullPath = path32.join(dir, name);
|
|
6160
6232
|
try {
|
|
6161
|
-
const stat =
|
|
6233
|
+
const stat = fs25.statSync(fullPath);
|
|
6162
6234
|
if (stat.size > MAX_FILE_SIZE_BYTES) continue;
|
|
6163
6235
|
} catch {
|
|
6164
6236
|
continue;
|
|
@@ -6185,20 +6257,20 @@ var structurePhase = {
|
|
|
6185
6257
|
const dirs = /* @__PURE__ */ new Set();
|
|
6186
6258
|
let structDone = 0;
|
|
6187
6259
|
for (const filePath of context2.filePaths) {
|
|
6188
|
-
const relativePath =
|
|
6260
|
+
const relativePath = path32.relative(context2.workspaceRoot, filePath);
|
|
6189
6261
|
const lang = detectLanguage(filePath);
|
|
6190
6262
|
context2.graph.addNode({
|
|
6191
6263
|
id: generateNodeId("file", relativePath, relativePath),
|
|
6192
6264
|
kind: "file",
|
|
6193
|
-
name:
|
|
6265
|
+
name: path32.basename(filePath),
|
|
6194
6266
|
filePath: relativePath,
|
|
6195
6267
|
metadata: lang ? { language: lang } : void 0
|
|
6196
6268
|
});
|
|
6197
|
-
let dir =
|
|
6269
|
+
let dir = path32.dirname(relativePath);
|
|
6198
6270
|
while (dir && dir !== "." && dir !== "") {
|
|
6199
6271
|
if (dirs.has(dir)) break;
|
|
6200
6272
|
dirs.add(dir);
|
|
6201
|
-
dir =
|
|
6273
|
+
dir = path32.dirname(dir);
|
|
6202
6274
|
}
|
|
6203
6275
|
structDone++;
|
|
6204
6276
|
context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
|
|
@@ -6207,7 +6279,7 @@ var structurePhase = {
|
|
|
6207
6279
|
context2.graph.addNode({
|
|
6208
6280
|
id: generateNodeId("directory", dir, dir),
|
|
6209
6281
|
kind: "directory",
|
|
6210
|
-
name:
|
|
6282
|
+
name: path32.basename(dir),
|
|
6211
6283
|
filePath: dir
|
|
6212
6284
|
});
|
|
6213
6285
|
}
|
|
@@ -6318,22 +6390,22 @@ var flowPhase = {
|
|
|
6318
6390
|
const queue = [{ nodeId: ep.id, path: [ep.id] }];
|
|
6319
6391
|
const visited = /* @__PURE__ */ new Set();
|
|
6320
6392
|
while (queue.length > 0 && flowCount < maxFlows) {
|
|
6321
|
-
const { nodeId, path:
|
|
6322
|
-
if (
|
|
6393
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6394
|
+
if (path33.length > maxDepth) continue;
|
|
6323
6395
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6324
|
-
if (callEdges.length === 0 &&
|
|
6396
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6325
6397
|
const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
|
|
6326
6398
|
graph.addNode({
|
|
6327
6399
|
id: flowId,
|
|
6328
6400
|
kind: "flow",
|
|
6329
6401
|
name: `${ep.name} flow ${flowCount}`,
|
|
6330
6402
|
filePath: ep.filePath,
|
|
6331
|
-
metadata: { steps:
|
|
6403
|
+
metadata: { steps: path33, entryPoint: ep.name }
|
|
6332
6404
|
});
|
|
6333
|
-
for (let i = 0; i <
|
|
6405
|
+
for (let i = 0; i < path33.length; i++) {
|
|
6334
6406
|
graph.addEdge({
|
|
6335
|
-
id: generateEdgeId(
|
|
6336
|
-
source:
|
|
6407
|
+
id: generateEdgeId(path33[i], flowId, `step_of_${i}`),
|
|
6408
|
+
source: path33[i],
|
|
6337
6409
|
target: flowId,
|
|
6338
6410
|
kind: "step_of",
|
|
6339
6411
|
weight: 1,
|
|
@@ -6346,7 +6418,7 @@ var flowPhase = {
|
|
|
6346
6418
|
for (const edge of callEdges) {
|
|
6347
6419
|
if (visited.has(edge.target)) continue;
|
|
6348
6420
|
visited.add(edge.target);
|
|
6349
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6421
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6350
6422
|
}
|
|
6351
6423
|
}
|
|
6352
6424
|
}
|
|
@@ -6364,7 +6436,7 @@ var LLMGovernanceLogger = class {
|
|
|
6364
6436
|
}
|
|
6365
6437
|
/** Path to the JSONL log file. */
|
|
6366
6438
|
getLogPath() {
|
|
6367
|
-
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");
|
|
6368
6440
|
}
|
|
6369
6441
|
/**
|
|
6370
6442
|
* Append an entry to the governance log.
|
|
@@ -6380,8 +6452,8 @@ var LLMGovernanceLogger = class {
|
|
|
6380
6452
|
...entry
|
|
6381
6453
|
};
|
|
6382
6454
|
const logPath = this.getLogPath();
|
|
6383
|
-
|
|
6384
|
-
|
|
6455
|
+
fs25.mkdirSync(path32.dirname(logPath), { recursive: true });
|
|
6456
|
+
fs25.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
|
|
6385
6457
|
} catch {
|
|
6386
6458
|
}
|
|
6387
6459
|
}
|
|
@@ -6391,7 +6463,7 @@ var LLMGovernanceLogger = class {
|
|
|
6391
6463
|
*/
|
|
6392
6464
|
readLog(limit = 100) {
|
|
6393
6465
|
try {
|
|
6394
|
-
const raw =
|
|
6466
|
+
const raw = fs25.readFileSync(this.getLogPath(), "utf-8");
|
|
6395
6467
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
|
|
6396
6468
|
return lines.map((l) => JSON.parse(l));
|
|
6397
6469
|
} catch {
|
|
@@ -6401,6 +6473,9 @@ var LLMGovernanceLogger = class {
|
|
|
6401
6473
|
};
|
|
6402
6474
|
var governanceLogger = new LLMGovernanceLogger();
|
|
6403
6475
|
|
|
6476
|
+
// src/pipeline/phases/summarize-phase.ts
|
|
6477
|
+
init_logger();
|
|
6478
|
+
|
|
6404
6479
|
// src/pipeline/workers/parse-phase-parallel.ts
|
|
6405
6480
|
init_shared();
|
|
6406
6481
|
init_id_generator();
|
|
@@ -6535,17 +6610,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
|
|
|
6535
6610
|
const queue = [{ nodeId: entryId, path: [entryId] }];
|
|
6536
6611
|
const visited = /* @__PURE__ */ new Set();
|
|
6537
6612
|
while (queue.length > 0 && flows.length < maxFlows) {
|
|
6538
|
-
const { nodeId, path:
|
|
6539
|
-
if (
|
|
6613
|
+
const { nodeId, path: path33 } = queue.shift();
|
|
6614
|
+
if (path33.length > maxDepth) continue;
|
|
6540
6615
|
const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
|
|
6541
|
-
if (callEdges.length === 0 &&
|
|
6542
|
-
flows.push({ entryPointId: entryId, steps: [...
|
|
6616
|
+
if (callEdges.length === 0 && path33.length >= 3) {
|
|
6617
|
+
flows.push({ entryPointId: entryId, steps: [...path33] });
|
|
6543
6618
|
continue;
|
|
6544
6619
|
}
|
|
6545
6620
|
for (const edge of callEdges) {
|
|
6546
6621
|
if (visited.has(edge.target)) continue;
|
|
6547
6622
|
visited.add(edge.target);
|
|
6548
|
-
queue.push({ nodeId: edge.target, path: [...
|
|
6623
|
+
queue.push({ nodeId: edge.target, path: [...path33, edge.target] });
|
|
6549
6624
|
}
|
|
6550
6625
|
}
|
|
6551
6626
|
}
|
|
@@ -6646,7 +6721,7 @@ var VectorIndex = class {
|
|
|
6646
6721
|
this.sqlitePath = sqlitePath;
|
|
6647
6722
|
}
|
|
6648
6723
|
async init() {
|
|
6649
|
-
this.db = new
|
|
6724
|
+
this.db = new Database2(this.sqlitePath);
|
|
6650
6725
|
this.db.pragma("journal_mode = WAL");
|
|
6651
6726
|
this.db.exec(`
|
|
6652
6727
|
CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
|
|
@@ -6777,9 +6852,9 @@ function siftDown(arr, i, score) {
|
|
|
6777
6852
|
}
|
|
6778
6853
|
init_embedder();
|
|
6779
6854
|
async function hybridSearch(graph, query, limit, options = {}) {
|
|
6780
|
-
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
|
|
6781
|
-
const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
6782
|
-
const hasVectorDb = Boolean(vectorDbPath &&
|
|
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));
|
|
6783
6858
|
if (!hasVectorDb) {
|
|
6784
6859
|
const bm25Results2 = await bm25Promise;
|
|
6785
6860
|
return {
|
|
@@ -6828,16 +6903,282 @@ async function runVectorSearch(vectorDbPath, query, topK) {
|
|
|
6828
6903
|
return null;
|
|
6829
6904
|
}
|
|
6830
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
|
+
}
|
|
6831
7168
|
var DbManager = class {
|
|
6832
7169
|
db = null;
|
|
6833
7170
|
conn = null;
|
|
6834
7171
|
dbPath;
|
|
6835
|
-
|
|
7172
|
+
readOnly;
|
|
7173
|
+
constructor(dbPath, readOnly = false) {
|
|
6836
7174
|
this.dbPath = dbPath;
|
|
7175
|
+
this.readOnly = readOnly;
|
|
6837
7176
|
}
|
|
6838
7177
|
async init() {
|
|
6839
|
-
|
|
6840
|
-
|
|
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);
|
|
6841
7182
|
await this.db.init();
|
|
6842
7183
|
this.conn = new Connection(this.db);
|
|
6843
7184
|
await this.conn.init();
|
|
@@ -6872,61 +7213,8 @@ var DbManager = class {
|
|
|
6872
7213
|
return this.conn !== null;
|
|
6873
7214
|
}
|
|
6874
7215
|
};
|
|
6875
|
-
|
|
6876
|
-
// src/storage/schema.ts
|
|
6877
|
-
var NODE_TABLE_MAP = {
|
|
6878
|
-
file: "file_nodes",
|
|
6879
|
-
directory: "dir_nodes",
|
|
6880
|
-
function: "func_nodes",
|
|
6881
|
-
class: "class_nodes",
|
|
6882
|
-
interface: "iface_nodes",
|
|
6883
|
-
method: "method_nodes",
|
|
6884
|
-
constructor: "ctor_nodes",
|
|
6885
|
-
variable: "var_nodes",
|
|
6886
|
-
property: "prop_nodes",
|
|
6887
|
-
struct: "struct_nodes",
|
|
6888
|
-
enum: "enum_nodes",
|
|
6889
|
-
trait: "trait_nodes",
|
|
6890
|
-
namespace: "ns_nodes",
|
|
6891
|
-
module: "mod_nodes",
|
|
6892
|
-
type_alias: "type_nodes",
|
|
6893
|
-
constant: "const_nodes",
|
|
6894
|
-
route: "route_nodes",
|
|
6895
|
-
cluster: "cluster_nodes",
|
|
6896
|
-
flow: "flow_nodes",
|
|
6897
|
-
vulnerability: "vuln_nodes"
|
|
6898
|
-
};
|
|
6899
|
-
var ALL_NODE_TABLES = [...new Set(Object.values(NODE_TABLE_MAP))];
|
|
6900
|
-
function getCreateNodeTableDDL(tableName) {
|
|
6901
|
-
return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
|
|
6902
|
-
id STRING,
|
|
6903
|
-
name STRING,
|
|
6904
|
-
file_path STRING,
|
|
6905
|
-
start_line INT64,
|
|
6906
|
-
end_line INT64,
|
|
6907
|
-
exported BOOLEAN,
|
|
6908
|
-
content STRING,
|
|
6909
|
-
metadata STRING,
|
|
6910
|
-
PRIMARY KEY (id)
|
|
6911
|
-
)`;
|
|
6912
|
-
}
|
|
6913
|
-
function getCreateEdgeTableDDL() {
|
|
6914
|
-
const uniqueTables = ALL_NODE_TABLES;
|
|
6915
|
-
const fromToPairs = [];
|
|
6916
|
-
for (const from of uniqueTables) {
|
|
6917
|
-
for (const to of uniqueTables) {
|
|
6918
|
-
fromToPairs.push(`FROM ${from} TO ${to}`);
|
|
6919
|
-
}
|
|
6920
|
-
}
|
|
6921
|
-
return [`CREATE REL TABLE GROUP IF NOT EXISTS code_edges (
|
|
6922
|
-
${fromToPairs.join(",\n ")},
|
|
6923
|
-
kind STRING,
|
|
6924
|
-
weight DOUBLE,
|
|
6925
|
-
label STRING
|
|
6926
|
-
)`];
|
|
6927
|
-
}
|
|
6928
7216
|
function writeNodeCSVs(graph, outputDir) {
|
|
6929
|
-
|
|
7217
|
+
fs25.mkdirSync(outputDir, { recursive: true });
|
|
6930
7218
|
const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
|
|
6931
7219
|
const tableBuffers = /* @__PURE__ */ new Map();
|
|
6932
7220
|
const tableFilePaths = /* @__PURE__ */ new Map();
|
|
@@ -6934,7 +7222,7 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6934
7222
|
const table = NODE_TABLE_MAP[node.kind];
|
|
6935
7223
|
if (!tableBuffers.has(table)) {
|
|
6936
7224
|
tableBuffers.set(table, [header]);
|
|
6937
|
-
tableFilePaths.set(table,
|
|
7225
|
+
tableFilePaths.set(table, path32.join(outputDir, `${table}.csv`));
|
|
6938
7226
|
}
|
|
6939
7227
|
tableBuffers.get(table).push(
|
|
6940
7228
|
csvRow([
|
|
@@ -6954,12 +7242,12 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
6954
7242
|
);
|
|
6955
7243
|
}
|
|
6956
7244
|
for (const [table, lines] of tableBuffers) {
|
|
6957
|
-
|
|
7245
|
+
fs25.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
|
|
6958
7246
|
}
|
|
6959
7247
|
return tableFilePaths;
|
|
6960
7248
|
}
|
|
6961
7249
|
function writeEdgeCSV(graph, outputDir) {
|
|
6962
|
-
|
|
7250
|
+
fs25.mkdirSync(outputDir, { recursive: true });
|
|
6963
7251
|
const header = "from_id,to_id,kind,weight,label\n";
|
|
6964
7252
|
const groups = /* @__PURE__ */ new Map();
|
|
6965
7253
|
for (const edge of graph.allEdges()) {
|
|
@@ -6970,7 +7258,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6970
7258
|
const toTable = NODE_TABLE_MAP[targetNode.kind];
|
|
6971
7259
|
const key = `${fromTable}->${toTable}`;
|
|
6972
7260
|
if (!groups.has(key)) {
|
|
6973
|
-
const filePath =
|
|
7261
|
+
const filePath = path32.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
|
|
6974
7262
|
groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
|
|
6975
7263
|
}
|
|
6976
7264
|
groups.get(key).lines.push(
|
|
@@ -6985,7 +7273,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
6985
7273
|
}
|
|
6986
7274
|
const result = [];
|
|
6987
7275
|
for (const group of groups.values()) {
|
|
6988
|
-
|
|
7276
|
+
fs25.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
|
|
6989
7277
|
result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
|
|
6990
7278
|
}
|
|
6991
7279
|
return result;
|
|
@@ -7013,7 +7301,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7013
7301
|
} catch {
|
|
7014
7302
|
}
|
|
7015
7303
|
}
|
|
7016
|
-
const tmpDir =
|
|
7304
|
+
const tmpDir = fs25.mkdtempSync(path32.join(os13.tmpdir(), "code-intel-csv-"));
|
|
7017
7305
|
try {
|
|
7018
7306
|
const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
|
|
7019
7307
|
const edgeGroups = writeEdgeCSV(graph, tmpDir);
|
|
@@ -7032,8 +7320,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7032
7320
|
}
|
|
7033
7321
|
let nodeCount = 0;
|
|
7034
7322
|
for (const [table, csvPath] of nodeTableFiles) {
|
|
7035
|
-
if (!
|
|
7036
|
-
const stat =
|
|
7323
|
+
if (!fs25.existsSync(csvPath)) continue;
|
|
7324
|
+
const stat = fs25.statSync(csvPath);
|
|
7037
7325
|
if (stat.size < 50) continue;
|
|
7038
7326
|
try {
|
|
7039
7327
|
await dbManager.execute(
|
|
@@ -7046,8 +7334,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7046
7334
|
}
|
|
7047
7335
|
let edgeCount = 0;
|
|
7048
7336
|
for (const group of edgeGroups) {
|
|
7049
|
-
if (!
|
|
7050
|
-
const stat =
|
|
7337
|
+
if (!fs25.existsSync(group.filePath)) continue;
|
|
7338
|
+
const stat = fs25.statSync(group.filePath);
|
|
7051
7339
|
if (stat.size < 50) continue;
|
|
7052
7340
|
try {
|
|
7053
7341
|
await dbManager.execute(
|
|
@@ -7061,7 +7349,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
7061
7349
|
return { nodeCount, edgeCount };
|
|
7062
7350
|
} finally {
|
|
7063
7351
|
try {
|
|
7064
|
-
|
|
7352
|
+
fs25.rmSync(tmpDir, { recursive: true, force: true });
|
|
7065
7353
|
} catch {
|
|
7066
7354
|
}
|
|
7067
7355
|
}
|
|
@@ -7113,19 +7401,19 @@ function buildNodeProps(node) {
|
|
|
7113
7401
|
function escCypher(s) {
|
|
7114
7402
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
|
|
7115
7403
|
}
|
|
7116
|
-
var GLOBAL_DIR =
|
|
7117
|
-
var REPOS_FILE =
|
|
7404
|
+
var GLOBAL_DIR = path32.join(os13.homedir(), ".code-intel");
|
|
7405
|
+
var REPOS_FILE = path32.join(GLOBAL_DIR, "repos.json");
|
|
7118
7406
|
function loadRegistry() {
|
|
7119
7407
|
try {
|
|
7120
|
-
const data =
|
|
7408
|
+
const data = fs25.readFileSync(REPOS_FILE, "utf-8");
|
|
7121
7409
|
return JSON.parse(data);
|
|
7122
7410
|
} catch {
|
|
7123
7411
|
return [];
|
|
7124
7412
|
}
|
|
7125
7413
|
}
|
|
7126
7414
|
function saveRegistry(entries) {
|
|
7127
|
-
|
|
7128
|
-
|
|
7415
|
+
fs25.mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
7416
|
+
fs25.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
|
|
7129
7417
|
}
|
|
7130
7418
|
function upsertRepo(entry) {
|
|
7131
7419
|
const entries = loadRegistry();
|
|
@@ -7142,30 +7430,30 @@ function removeRepo(repoPath) {
|
|
|
7142
7430
|
saveRegistry(entries);
|
|
7143
7431
|
}
|
|
7144
7432
|
function saveMetadata(repoDir, metadata) {
|
|
7145
|
-
const metaDir =
|
|
7146
|
-
|
|
7147
|
-
|
|
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));
|
|
7148
7436
|
}
|
|
7149
7437
|
function loadMetadata(repoDir) {
|
|
7150
7438
|
try {
|
|
7151
|
-
const data =
|
|
7439
|
+
const data = fs25.readFileSync(path32.join(repoDir, ".code-intel", "meta.json"), "utf-8");
|
|
7152
7440
|
return JSON.parse(data);
|
|
7153
7441
|
} catch {
|
|
7154
7442
|
return null;
|
|
7155
7443
|
}
|
|
7156
7444
|
}
|
|
7157
7445
|
function getDbPath(repoDir) {
|
|
7158
|
-
return
|
|
7446
|
+
return path32.join(repoDir, ".code-intel", "graph.db");
|
|
7159
7447
|
}
|
|
7160
7448
|
function getVectorDbPath(repoDir) {
|
|
7161
|
-
return
|
|
7449
|
+
return path32.join(repoDir, ".code-intel", "vector.db");
|
|
7162
7450
|
}
|
|
7163
7451
|
|
|
7164
7452
|
// src/mcp-server/server.ts
|
|
7165
7453
|
init_group_registry();
|
|
7166
7454
|
|
|
7167
7455
|
// src/multi-repo/graph-from-db.ts
|
|
7168
|
-
var
|
|
7456
|
+
var TABLE_TO_KIND2 = Object.fromEntries(
|
|
7169
7457
|
Object.entries(NODE_TABLE_MAP).map(([kind, table]) => [table, kind])
|
|
7170
7458
|
);
|
|
7171
7459
|
function parseRow(row, kind) {
|
|
@@ -7189,7 +7477,7 @@ function parseRow(row, kind) {
|
|
|
7189
7477
|
}
|
|
7190
7478
|
async function loadGraphFromDB(graph, db) {
|
|
7191
7479
|
for (const table of ALL_NODE_TABLES) {
|
|
7192
|
-
const kind =
|
|
7480
|
+
const kind = TABLE_TO_KIND2[table];
|
|
7193
7481
|
if (!kind) continue;
|
|
7194
7482
|
let rows = [];
|
|
7195
7483
|
try {
|
|
@@ -7242,12 +7530,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
|
|
|
7242
7530
|
if (depth > maxDepth) return;
|
|
7243
7531
|
let entries;
|
|
7244
7532
|
try {
|
|
7245
|
-
entries =
|
|
7533
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
7246
7534
|
} catch {
|
|
7247
7535
|
return;
|
|
7248
7536
|
}
|
|
7249
7537
|
for (const entry of entries) {
|
|
7250
|
-
const full =
|
|
7538
|
+
const full = path32.join(dir, entry.name);
|
|
7251
7539
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
7252
7540
|
walk(full, depth + 1);
|
|
7253
7541
|
} else if (entry.isFile() && matcher(entry.name)) {
|
|
@@ -7270,8 +7558,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
7270
7558
|
]);
|
|
7271
7559
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
7272
7560
|
function tryParseFile(filePath) {
|
|
7273
|
-
const ext =
|
|
7274
|
-
const content =
|
|
7561
|
+
const ext = path32.extname(filePath).toLowerCase();
|
|
7562
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7275
7563
|
if (ext === ".json") {
|
|
7276
7564
|
try {
|
|
7277
7565
|
return JSON.parse(content);
|
|
@@ -7325,7 +7613,7 @@ async function parseGraphQLContracts(repoRoot) {
|
|
|
7325
7613
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
|
|
7326
7614
|
const contracts = [];
|
|
7327
7615
|
for (const filePath of files) {
|
|
7328
|
-
const content =
|
|
7616
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7329
7617
|
const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7330
7618
|
let match;
|
|
7331
7619
|
while ((match = typeRegex.exec(content)) !== null) {
|
|
@@ -7356,7 +7644,7 @@ async function parseProtoContracts(repoRoot) {
|
|
|
7356
7644
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
|
|
7357
7645
|
const contracts = [];
|
|
7358
7646
|
for (const filePath of files) {
|
|
7359
|
-
const content =
|
|
7647
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
7360
7648
|
const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
|
|
7361
7649
|
let serviceMatch;
|
|
7362
7650
|
while ((serviceMatch = serviceRegex.exec(content)) !== null) {
|
|
@@ -7561,13 +7849,13 @@ async function syncGroup(group) {
|
|
|
7561
7849
|
logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
|
|
7562
7850
|
continue;
|
|
7563
7851
|
}
|
|
7564
|
-
const dbPath =
|
|
7565
|
-
if (!
|
|
7852
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
7853
|
+
if (!fs25.existsSync(dbPath)) {
|
|
7566
7854
|
logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
|
|
7567
7855
|
continue;
|
|
7568
7856
|
}
|
|
7569
7857
|
const graph = createKnowledgeGraph();
|
|
7570
|
-
const db = new DbManager(dbPath);
|
|
7858
|
+
const db = new DbManager(dbPath, true);
|
|
7571
7859
|
try {
|
|
7572
7860
|
await db.init();
|
|
7573
7861
|
await loadGraphFromDB(graph, db);
|
|
@@ -7635,10 +7923,10 @@ async function queryGroup(group, query, limit = 20) {
|
|
|
7635
7923
|
for (const member of group.members) {
|
|
7636
7924
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
7637
7925
|
if (!regEntry) continue;
|
|
7638
|
-
const dbPath =
|
|
7639
|
-
if (!
|
|
7926
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
7927
|
+
if (!fs25.existsSync(dbPath)) continue;
|
|
7640
7928
|
const graph = createKnowledgeGraph();
|
|
7641
|
-
const db = new DbManager(dbPath);
|
|
7929
|
+
const db = new DbManager(dbPath, true);
|
|
7642
7930
|
try {
|
|
7643
7931
|
await db.init();
|
|
7644
7932
|
await loadGraphFromDB(graph, db);
|
|
@@ -8136,22 +8424,22 @@ function suggestTests(graph, symbolName) {
|
|
|
8136
8424
|
const callPaths = [];
|
|
8137
8425
|
const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
|
|
8138
8426
|
while (pathQueue.length > 0 && callPaths.length < 5) {
|
|
8139
|
-
const { id, path:
|
|
8427
|
+
const { id, path: path33, depth } = pathQueue.shift();
|
|
8140
8428
|
let hasCallers2 = false;
|
|
8141
8429
|
for (const edge of graph.findEdgesTo(id)) {
|
|
8142
8430
|
if (edge.kind !== "calls") continue;
|
|
8143
8431
|
const callerNode = graph.getNode(edge.source);
|
|
8144
8432
|
if (!callerNode) continue;
|
|
8145
8433
|
hasCallers2 = true;
|
|
8146
|
-
const newPath = [callerNode.name, ...
|
|
8434
|
+
const newPath = [callerNode.name, ...path33];
|
|
8147
8435
|
if (depth + 1 >= 3 || callPaths.length >= 5) {
|
|
8148
8436
|
if (callPaths.length < 5) callPaths.push(newPath);
|
|
8149
8437
|
continue;
|
|
8150
8438
|
}
|
|
8151
8439
|
pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
|
|
8152
8440
|
}
|
|
8153
|
-
if (!hasCallers2 &&
|
|
8154
|
-
callPaths.push(
|
|
8441
|
+
if (!hasCallers2 && path33.length > 1) {
|
|
8442
|
+
callPaths.push(path33);
|
|
8155
8443
|
}
|
|
8156
8444
|
}
|
|
8157
8445
|
if (callPaths.length === 0) {
|
|
@@ -8708,24 +8996,44 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8708
8996
|
}
|
|
8709
8997
|
const startMs = Date.now();
|
|
8710
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
|
+
});
|
|
8711
9008
|
let result;
|
|
8712
9009
|
let status = "success";
|
|
8713
9010
|
try {
|
|
8714
9011
|
if (isTracingEnabled()) {
|
|
8715
|
-
result = await
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
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
|
+
]);
|
|
8720
9020
|
} else {
|
|
8721
|
-
result = await dispatch();
|
|
9021
|
+
result = await Promise.race([dispatch(), timeoutPromise]);
|
|
8722
9022
|
}
|
|
8723
9023
|
if (result.isError) status = "error";
|
|
8724
9024
|
} catch (err) {
|
|
8725
9025
|
status = "error";
|
|
8726
9026
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8727
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
|
+
}
|
|
8728
9034
|
throw err;
|
|
9035
|
+
} finally {
|
|
9036
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
8729
9037
|
}
|
|
8730
9038
|
mcpToolCallsTotal.inc({ tool: name, status });
|
|
8731
9039
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
@@ -9113,7 +9421,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
9113
9421
|
for (const { filePath: changedFile, changedLines } of changedFiles) {
|
|
9114
9422
|
for (const node of graph.allNodes()) {
|
|
9115
9423
|
if (!node.filePath) continue;
|
|
9116
|
-
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot +
|
|
9424
|
+
const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path32.sep, "");
|
|
9117
9425
|
const normChanged = changedFile.replace(/^a\/|^b\//, "");
|
|
9118
9426
|
if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
|
|
9119
9427
|
if (node.startLine !== void 0 && node.endLine !== void 0) {
|
|
@@ -9536,8 +9844,8 @@ var STUCK_THRESHOLD_MINUTES = 30;
|
|
|
9536
9844
|
var JobsDB = class {
|
|
9537
9845
|
db;
|
|
9538
9846
|
constructor(dbPath) {
|
|
9539
|
-
|
|
9540
|
-
this.db = new
|
|
9847
|
+
fs25.mkdirSync(path32.dirname(dbPath), { recursive: true });
|
|
9848
|
+
this.db = new Database2(dbPath);
|
|
9541
9849
|
this.db.pragma("journal_mode = WAL");
|
|
9542
9850
|
this.db.pragma("foreign_keys = ON");
|
|
9543
9851
|
this.createTables();
|
|
@@ -9678,7 +9986,7 @@ var JobsDB = class {
|
|
|
9678
9986
|
}
|
|
9679
9987
|
};
|
|
9680
9988
|
function getJobsDBPath() {
|
|
9681
|
-
return
|
|
9989
|
+
return path32.join(os13.homedir(), ".code-intel", "jobs.db");
|
|
9682
9990
|
}
|
|
9683
9991
|
var _jobsDB = null;
|
|
9684
9992
|
function getOrCreateJobsDB() {
|
|
@@ -9770,7 +10078,7 @@ var BACKUP_VERSION = "1.0";
|
|
|
9770
10078
|
var ALGORITHM = "aes-256-gcm";
|
|
9771
10079
|
var IV_LENGTH = 16;
|
|
9772
10080
|
function getBackupDir() {
|
|
9773
|
-
return
|
|
10081
|
+
return path32.join(os13.homedir(), ".code-intel", "backups");
|
|
9774
10082
|
}
|
|
9775
10083
|
function getBackupKey() {
|
|
9776
10084
|
const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
|
|
@@ -9801,30 +10109,30 @@ var BackupService = class {
|
|
|
9801
10109
|
constructor(backupDir) {
|
|
9802
10110
|
this.backupDir = backupDir ?? getBackupDir();
|
|
9803
10111
|
this.key = getBackupKey();
|
|
9804
|
-
|
|
10112
|
+
fs25.mkdirSync(this.backupDir, { recursive: true });
|
|
9805
10113
|
}
|
|
9806
10114
|
/**
|
|
9807
10115
|
* Create a backup for a repository.
|
|
9808
10116
|
* Returns the backup entry.
|
|
9809
10117
|
*/
|
|
9810
10118
|
createBackup(repoPath) {
|
|
9811
|
-
const codeIntelDir =
|
|
10119
|
+
const codeIntelDir = path32.join(repoPath, ".code-intel");
|
|
9812
10120
|
const id = v4();
|
|
9813
10121
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9814
10122
|
const filesToBackup = [];
|
|
9815
10123
|
const candidates = ["graph.db", "vector.db", "meta.json"];
|
|
9816
10124
|
for (const f of candidates) {
|
|
9817
|
-
const fp =
|
|
9818
|
-
if (
|
|
10125
|
+
const fp = path32.join(codeIntelDir, f);
|
|
10126
|
+
if (fs25.existsSync(fp)) {
|
|
9819
10127
|
filesToBackup.push({ name: f, localPath: fp });
|
|
9820
10128
|
}
|
|
9821
10129
|
}
|
|
9822
|
-
const registryPath =
|
|
9823
|
-
if (
|
|
10130
|
+
const registryPath = path32.join(os13.homedir(), ".code-intel", "registry.json");
|
|
10131
|
+
if (fs25.existsSync(registryPath)) {
|
|
9824
10132
|
filesToBackup.push({ name: "registry.json", localPath: registryPath });
|
|
9825
10133
|
}
|
|
9826
|
-
const usersDbPath =
|
|
9827
|
-
if (
|
|
10134
|
+
const usersDbPath = path32.join(os13.homedir(), ".code-intel", "users.db");
|
|
10135
|
+
if (fs25.existsSync(usersDbPath)) {
|
|
9828
10136
|
filesToBackup.push({ name: "users.db", localPath: usersDbPath });
|
|
9829
10137
|
}
|
|
9830
10138
|
if (filesToBackup.length === 0) {
|
|
@@ -9835,7 +10143,7 @@ var BackupService = class {
|
|
|
9835
10143
|
createdAt,
|
|
9836
10144
|
version: BACKUP_VERSION,
|
|
9837
10145
|
files: filesToBackup.map((f) => {
|
|
9838
|
-
const data =
|
|
10146
|
+
const data = fs25.readFileSync(f.localPath);
|
|
9839
10147
|
return {
|
|
9840
10148
|
name: f.name,
|
|
9841
10149
|
sha256: crypto5.createHash("sha256").update(data).digest("hex"),
|
|
@@ -9849,7 +10157,7 @@ var BackupService = class {
|
|
|
9849
10157
|
manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
|
|
9850
10158
|
parts.push(manifestLenBuf, manifestBuf);
|
|
9851
10159
|
for (const f of filesToBackup) {
|
|
9852
|
-
const data =
|
|
10160
|
+
const data = fs25.readFileSync(f.localPath);
|
|
9853
10161
|
const nameBuf = Buffer.from(f.name, "utf-8");
|
|
9854
10162
|
const nameLenBuf = Buffer.alloc(2);
|
|
9855
10163
|
nameLenBuf.writeUInt16BE(nameBuf.length, 0);
|
|
@@ -9860,8 +10168,8 @@ var BackupService = class {
|
|
|
9860
10168
|
const plaintext = Buffer.concat(parts);
|
|
9861
10169
|
const encrypted = encryptBuffer(plaintext, this.key);
|
|
9862
10170
|
const backupFileName = `backup-${id}.cib`;
|
|
9863
|
-
const backupPath =
|
|
9864
|
-
|
|
10171
|
+
const backupPath = path32.join(this.backupDir, backupFileName);
|
|
10172
|
+
fs25.writeFileSync(backupPath, encrypted);
|
|
9865
10173
|
const entry = {
|
|
9866
10174
|
id,
|
|
9867
10175
|
createdAt,
|
|
@@ -9888,9 +10196,9 @@ var BackupService = class {
|
|
|
9888
10196
|
async uploadToS3(entry) {
|
|
9889
10197
|
const cfg = getS3Config();
|
|
9890
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.");
|
|
9891
|
-
const fileName =
|
|
10199
|
+
const fileName = path32.basename(entry.path);
|
|
9892
10200
|
const s3Key = `${cfg.prefix}${fileName}`;
|
|
9893
|
-
const body =
|
|
10201
|
+
const body = fs25.readFileSync(entry.path);
|
|
9894
10202
|
const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
|
|
9895
10203
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9896
10204
|
throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
@@ -9907,8 +10215,8 @@ var BackupService = class {
|
|
|
9907
10215
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
9908
10216
|
throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
9909
10217
|
}
|
|
9910
|
-
|
|
9911
|
-
|
|
10218
|
+
fs25.mkdirSync(path32.dirname(destPath), { recursive: true });
|
|
10219
|
+
fs25.writeFileSync(destPath, Buffer.from(result.body, "binary"));
|
|
9912
10220
|
}
|
|
9913
10221
|
/**
|
|
9914
10222
|
* List backup objects in S3 with the configured prefix.
|
|
@@ -9954,10 +10262,10 @@ var BackupService = class {
|
|
|
9954
10262
|
if (!entry) {
|
|
9955
10263
|
throw new Error(`Backup "${backupId}" not found.`);
|
|
9956
10264
|
}
|
|
9957
|
-
if (!
|
|
10265
|
+
if (!fs25.existsSync(entry.path)) {
|
|
9958
10266
|
throw new Error(`Backup file not found at: ${entry.path}`);
|
|
9959
10267
|
}
|
|
9960
|
-
const encrypted =
|
|
10268
|
+
const encrypted = fs25.readFileSync(entry.path);
|
|
9961
10269
|
let plaintext;
|
|
9962
10270
|
try {
|
|
9963
10271
|
plaintext = decryptBuffer(encrypted, this.key);
|
|
@@ -9971,8 +10279,8 @@ var BackupService = class {
|
|
|
9971
10279
|
offset += manifestLen;
|
|
9972
10280
|
const manifest = JSON.parse(manifestStr);
|
|
9973
10281
|
const restoreBase = targetRepoPath ?? entry.repoPath;
|
|
9974
|
-
const codeIntelDir =
|
|
9975
|
-
|
|
10282
|
+
const codeIntelDir = path32.join(restoreBase, ".code-intel");
|
|
10283
|
+
fs25.mkdirSync(codeIntelDir, { recursive: true });
|
|
9976
10284
|
for (const fileEntry of manifest.files) {
|
|
9977
10285
|
const nameLen = plaintext.readUInt16BE(offset);
|
|
9978
10286
|
offset += 2;
|
|
@@ -9989,18 +10297,18 @@ var BackupService = class {
|
|
|
9989
10297
|
}
|
|
9990
10298
|
let destPath;
|
|
9991
10299
|
if (name === "registry.json" || name === "users.db") {
|
|
9992
|
-
destPath =
|
|
10300
|
+
destPath = path32.join(os13.homedir(), ".code-intel", name);
|
|
9993
10301
|
} else {
|
|
9994
|
-
destPath =
|
|
10302
|
+
destPath = path32.join(codeIntelDir, name);
|
|
9995
10303
|
}
|
|
9996
|
-
|
|
10304
|
+
fs25.writeFileSync(destPath, data);
|
|
9997
10305
|
}
|
|
9998
10306
|
}
|
|
9999
10307
|
/**
|
|
10000
10308
|
* Apply retention policy: keep N daily, M weekly, L monthly backups.
|
|
10001
10309
|
*/
|
|
10002
10310
|
applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
|
|
10003
|
-
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());
|
|
10004
10312
|
const keep = /* @__PURE__ */ new Set();
|
|
10005
10313
|
const now = /* @__PURE__ */ new Date();
|
|
10006
10314
|
const dailyCutoff = new Date(now);
|
|
@@ -10030,7 +10338,7 @@ var BackupService = class {
|
|
|
10030
10338
|
for (const e of entries) {
|
|
10031
10339
|
if (!keep.has(e.id)) {
|
|
10032
10340
|
try {
|
|
10033
|
-
|
|
10341
|
+
fs25.unlinkSync(e.path);
|
|
10034
10342
|
deleted++;
|
|
10035
10343
|
} catch {
|
|
10036
10344
|
}
|
|
@@ -10042,17 +10350,17 @@ var BackupService = class {
|
|
|
10042
10350
|
}
|
|
10043
10351
|
// ── Index helpers ──────────────────────────────────────────────────────────
|
|
10044
10352
|
_indexPath() {
|
|
10045
|
-
return
|
|
10353
|
+
return path32.join(this.backupDir, "index.json");
|
|
10046
10354
|
}
|
|
10047
10355
|
_loadIndex() {
|
|
10048
10356
|
try {
|
|
10049
|
-
return JSON.parse(
|
|
10357
|
+
return JSON.parse(fs25.readFileSync(this._indexPath(), "utf-8"));
|
|
10050
10358
|
} catch {
|
|
10051
10359
|
return [];
|
|
10052
10360
|
}
|
|
10053
10361
|
}
|
|
10054
10362
|
_saveIndex(entries) {
|
|
10055
|
-
|
|
10363
|
+
fs25.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
|
|
10056
10364
|
}
|
|
10057
10365
|
_appendIndex(entry) {
|
|
10058
10366
|
const entries = this._loadIndex();
|
|
@@ -10845,11 +11153,11 @@ var openApiSpec = {
|
|
|
10845
11153
|
};
|
|
10846
11154
|
|
|
10847
11155
|
// src/http/app.ts
|
|
10848
|
-
var __dirname$1 =
|
|
11156
|
+
var __dirname$1 = path32.dirname(fileURLToPath(import.meta.url));
|
|
10849
11157
|
var WEB_DIST = (() => {
|
|
10850
|
-
const bundled =
|
|
10851
|
-
if (
|
|
10852
|
-
return
|
|
11158
|
+
const bundled = path32.resolve(__dirname$1, "..", "web");
|
|
11159
|
+
if (fs25.existsSync(bundled)) return bundled;
|
|
11160
|
+
return path32.resolve(__dirname$1, "..", "..", "..", "web", "dist");
|
|
10853
11161
|
})();
|
|
10854
11162
|
function getAllowedOrigins() {
|
|
10855
11163
|
const env = process.env["CODE_INTEL_CORS_ORIGINS"];
|
|
@@ -10858,13 +11166,17 @@ function getAllowedOrigins() {
|
|
|
10858
11166
|
}
|
|
10859
11167
|
function createDefaultLimiter() {
|
|
10860
11168
|
const max = parseInt(process.env["CODE_INTEL_RATE_LIMIT_MAX"] ?? "100", 10);
|
|
10861
|
-
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);
|
|
10862
11170
|
return rateLimit({
|
|
10863
11171
|
windowMs,
|
|
10864
11172
|
max,
|
|
10865
11173
|
standardHeaders: true,
|
|
10866
11174
|
legacyHeaders: false,
|
|
10867
|
-
|
|
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),
|
|
10868
11180
|
message: {
|
|
10869
11181
|
error: {
|
|
10870
11182
|
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
|
|
@@ -10923,12 +11235,31 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10923
11235
|
});
|
|
10924
11236
|
app.use(requestIdMiddleware);
|
|
10925
11237
|
app.use(authMiddleware);
|
|
11238
|
+
let dbUnavailableSince = null;
|
|
10926
11239
|
app.use((_req, res, next) => {
|
|
10927
11240
|
if (workspaceRoot) {
|
|
11241
|
+
const metaFilePath = path32.join(workspaceRoot, ".code-intel", "meta.json");
|
|
11242
|
+
let metaOk = false;
|
|
10928
11243
|
try {
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
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);
|
|
10932
11263
|
}
|
|
10933
11264
|
}
|
|
10934
11265
|
next();
|
|
@@ -10984,6 +11315,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
10984
11315
|
let vectorIndex = null;
|
|
10985
11316
|
let vectorIndexBuilding = false;
|
|
10986
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
|
+
}
|
|
10987
11333
|
async function ensureVectorIndex() {
|
|
10988
11334
|
if (vectorIndexReady && vectorIndex) return vectorIndex;
|
|
10989
11335
|
if (!workspaceRoot || vectorIndexBuilding) return null;
|
|
@@ -11380,10 +11726,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11380
11726
|
const registry = loadRegistry();
|
|
11381
11727
|
const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
|
|
11382
11728
|
if (!entry) return null;
|
|
11383
|
-
const dbPath =
|
|
11384
|
-
if (!
|
|
11729
|
+
const dbPath = path32.join(entry.path, ".code-intel", "graph.db");
|
|
11730
|
+
if (!fs25.existsSync(dbPath)) return null;
|
|
11385
11731
|
const repoGraph = createKnowledgeGraph();
|
|
11386
|
-
const db = new DbManager(dbPath);
|
|
11732
|
+
const db = new DbManager(dbPath, true);
|
|
11387
11733
|
try {
|
|
11388
11734
|
await db.init();
|
|
11389
11735
|
await loadGraphFromDB(repoGraph, db);
|
|
@@ -11394,10 +11740,33 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11394
11740
|
return null;
|
|
11395
11741
|
}
|
|
11396
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
|
+
}
|
|
11397
11764
|
async function getGraphForRepo(requestedRepo) {
|
|
11398
11765
|
if (!requestedRepo || requestedRepo === repoName) return graph;
|
|
11399
11766
|
const g = await loadRepoGraph(requestedRepo);
|
|
11400
|
-
|
|
11767
|
+
if (g) return g;
|
|
11768
|
+
const gg = await loadGroupGraph(requestedRepo);
|
|
11769
|
+
return gg ?? graph;
|
|
11401
11770
|
}
|
|
11402
11771
|
app.get("/api/v1/graph/:repo", requireRepoAccess((req) => {
|
|
11403
11772
|
const p = req.params["repo"];
|
|
@@ -11421,11 +11790,56 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11421
11790
|
}
|
|
11422
11791
|
res.json({ nodes: [...g.allNodes()], edges: [...g.allEdges()] });
|
|
11423
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
|
+
});
|
|
11424
11833
|
app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
|
|
11425
11834
|
const { query, limit, repo } = req.body;
|
|
11426
11835
|
const g = await getGraphForRepo(repo);
|
|
11427
11836
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
11428
|
-
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
|
+
});
|
|
11429
11843
|
res.json({ results, searchMode });
|
|
11430
11844
|
});
|
|
11431
11845
|
app.post("/api/v1/vector-search", async (req, res) => {
|
|
@@ -11468,7 +11882,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11468
11882
|
return;
|
|
11469
11883
|
}
|
|
11470
11884
|
try {
|
|
11471
|
-
const content =
|
|
11885
|
+
const content = fs25.readFileSync(file_path, "utf-8");
|
|
11472
11886
|
res.json({ content });
|
|
11473
11887
|
} catch {
|
|
11474
11888
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
|
|
@@ -11505,7 +11919,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11505
11919
|
if (workspaceRoot) {
|
|
11506
11920
|
try {
|
|
11507
11921
|
const dbPath = getDbPath(workspaceRoot);
|
|
11508
|
-
const dbm = new DbManager(dbPath);
|
|
11922
|
+
const dbm = new DbManager(dbPath, true);
|
|
11509
11923
|
await dbm.init();
|
|
11510
11924
|
const rows = await dbm.query(q);
|
|
11511
11925
|
dbm.close();
|
|
@@ -11546,33 +11960,53 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11546
11960
|
app.get("/api/v1/nodes/:id", async (req, res) => {
|
|
11547
11961
|
const nodeId = decodeURIComponent(req.params.id);
|
|
11548
11962
|
const g = await getGraphForRepo(req.query["repo"]);
|
|
11549
|
-
const node = g.getNode(nodeId);
|
|
11963
|
+
const node = isLazyGraph(g) ? await g.getNodeAsync(nodeId) : g.getNode(nodeId);
|
|
11550
11964
|
if (!node) {
|
|
11551
11965
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Node not found", requestId: req.requestId } });
|
|
11552
11966
|
return;
|
|
11553
11967
|
}
|
|
11554
11968
|
const incoming = [...g.findEdgesTo(nodeId)];
|
|
11555
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);
|
|
11556
11978
|
res.json({
|
|
11557
11979
|
node,
|
|
11558
|
-
callers: incoming.filter((e) => e.kind === "calls").map((e) => ({ id: e.source, name:
|
|
11559
|
-
callees: outgoing.filter((e) => e.kind === "calls").map((e) => ({ id: e.target, name:
|
|
11560
|
-
imports: outgoing.filter((e) => e.kind === "imports").map((e) => ({ id: e.target, name:
|
|
11561
|
-
importedBy: incoming.filter((e) => e.kind === "imports").map((e) => ({ id: e.source, name:
|
|
11562
|
-
extends: outgoing.filter((e) => e.kind === "extends").map((e) => ({ id: e.target, name:
|
|
11563
|
-
implementsEdges: outgoing.filter((e) => e.kind === "implements").map((e) => ({ id: e.target, name:
|
|
11564
|
-
members: outgoing.filter((e) => e.kind === "has_member").map((e) => ({ id: e.target, name:
|
|
11565
|
-
cluster: incoming.filter((e) => e.kind === "belongs_to").map((e) =>
|
|
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]
|
|
11566
11988
|
});
|
|
11567
11989
|
});
|
|
11568
11990
|
app.post("/api/v1/blast-radius", async (req, res) => {
|
|
11569
11991
|
const { target, direction = "both", max_hops = 5, repo } = req.body;
|
|
11570
11992
|
const g = await getGraphForRepo(repo);
|
|
11571
11993
|
let targetNode = null;
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
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
|
+
}
|
|
11576
12010
|
}
|
|
11577
12011
|
}
|
|
11578
12012
|
if (!targetNode) {
|
|
@@ -11726,9 +12160,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11726
12160
|
for (const member of group.members) {
|
|
11727
12161
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11728
12162
|
if (!regEntry) continue;
|
|
11729
|
-
const dbPath =
|
|
11730
|
-
if (!
|
|
11731
|
-
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);
|
|
11732
12166
|
try {
|
|
11733
12167
|
await db.init();
|
|
11734
12168
|
await loadGraphFromDB(mergedGraph, db);
|
|
@@ -11753,10 +12187,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11753
12187
|
let nodeCount = 0;
|
|
11754
12188
|
let edgeCount = 0;
|
|
11755
12189
|
if (regEntry) {
|
|
11756
|
-
const dbPath =
|
|
11757
|
-
if (
|
|
12190
|
+
const dbPath = path32.join(regEntry.path, ".code-intel", "graph.db");
|
|
12191
|
+
if (fs25.existsSync(dbPath)) {
|
|
11758
12192
|
try {
|
|
11759
|
-
const db = new DbManager(dbPath);
|
|
12193
|
+
const db = new DbManager(dbPath, true);
|
|
11760
12194
|
await db.init();
|
|
11761
12195
|
const g = createKnowledgeGraph();
|
|
11762
12196
|
await loadGraphFromDB(g, db);
|
|
@@ -11779,7 +12213,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11779
12213
|
res.json({ repos, edges });
|
|
11780
12214
|
});
|
|
11781
12215
|
app.get("/api/v1/source", requireAuth, requireRole("viewer"), (req, res) => {
|
|
11782
|
-
const { file, startLine: startLineStr, endLine: endLineStr } = req.query;
|
|
12216
|
+
const { file, startLine: startLineStr, endLine: endLineStr, repo } = req.query;
|
|
11783
12217
|
if (!file) {
|
|
11784
12218
|
res.status(400).json({
|
|
11785
12219
|
error: {
|
|
@@ -11803,14 +12237,36 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11803
12237
|
});
|
|
11804
12238
|
return;
|
|
11805
12239
|
}
|
|
11806
|
-
let
|
|
11807
|
-
if (
|
|
11808
|
-
|
|
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);
|
|
11809
12265
|
}
|
|
11810
|
-
const resolvedFile =
|
|
12266
|
+
const resolvedFile = path32.resolve(rawResolved);
|
|
11811
12267
|
function isInsideDir(fileAbs, dir) {
|
|
11812
|
-
const rel =
|
|
11813
|
-
return !rel.startsWith("..") && !
|
|
12268
|
+
const rel = path32.relative(path32.resolve(dir), fileAbs);
|
|
12269
|
+
return !rel.startsWith("..") && !path32.isAbsolute(rel);
|
|
11814
12270
|
}
|
|
11815
12271
|
if (workspaceRoot) {
|
|
11816
12272
|
if (!isInsideDir(resolvedFile, workspaceRoot)) {
|
|
@@ -11847,7 +12303,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11847
12303
|
}
|
|
11848
12304
|
let fileContent;
|
|
11849
12305
|
try {
|
|
11850
|
-
fileContent =
|
|
12306
|
+
fileContent = fs25.readFileSync(resolvedFile, "utf-8");
|
|
11851
12307
|
} catch {
|
|
11852
12308
|
res.status(404).json({
|
|
11853
12309
|
error: {
|
|
@@ -11878,7 +12334,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11878
12334
|
const contextStart = Math.max(1, startLine - 20);
|
|
11879
12335
|
const contextEnd = Math.min(lines.length, endLine + 20);
|
|
11880
12336
|
const content = lines.slice(contextStart - 1, contextEnd).join("\n");
|
|
11881
|
-
const ext =
|
|
12337
|
+
const ext = path32.extname(resolvedFile).toLowerCase();
|
|
11882
12338
|
const languageMap = {
|
|
11883
12339
|
".ts": "typescript",
|
|
11884
12340
|
".tsx": "typescript",
|
|
@@ -12013,10 +12469,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12013
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() } });
|
|
12014
12470
|
}
|
|
12015
12471
|
});
|
|
12016
|
-
if (
|
|
12472
|
+
if (fs25.existsSync(WEB_DIST)) {
|
|
12017
12473
|
app.use(express.static(WEB_DIST));
|
|
12018
12474
|
app.get("/{*path}", (_req, res) => {
|
|
12019
|
-
res.sendFile(
|
|
12475
|
+
res.sendFile(path32.join(WEB_DIST, "index.html"));
|
|
12020
12476
|
});
|
|
12021
12477
|
}
|
|
12022
12478
|
app.use("/admin", requireRole("admin"));
|
|
@@ -12176,6 +12632,6 @@ function mergeSearchResults(...perRepoResults) {
|
|
|
12176
12632
|
return reciprocalRankFusion(...perRepoResults);
|
|
12177
12633
|
}
|
|
12178
12634
|
|
|
12179
|
-
export { AstCache, BindingTracker, DbManager, addBinding, addClustersToGraph, addMember, buildCallEdges, buildHeritageEdges, classifyCall, clusterPhase, computeMRO, createApp, createKnowledgeGraph, createMcpServer, createScope, deleteGroup, detectCommunities, detectOverrides, findEntryPoints, flowPhase, generateEdgeId, generateNodeId, getAllLanguageModules, getDbPath, getLanguage, getLanguageModule, getParser, groupExists, initParser, isTreeSitterAvailable, listGroups, loadGraphToDB, loadGroup, loadMetadata, loadRegistry, loadSyncResult, mergeSearchResults, parsePhase, parseSource, queryGroup, reciprocalRankFusion, removeMember, removeRepo, resolveBinding, resolveImports, resolvePhase, runPipeline, runQuery, runQueryMatches, saveGroup, saveMetadata, saveSyncResult, scanPhase, startHttpServer, startMcpStdio, structurePhase, syncGroup, textSearch, topologicalSort, traceFlow, upsertRepo, validateDAG };
|
|
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 };
|
|
12180
12636
|
//# sourceMappingURL=index.js.map
|
|
12181
12637
|
//# sourceMappingURL=index.js.map
|