@reverse-craft/smart-fs 1.0.6 → 2.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 +160 -13
- package/dist/analyzer.d.ts +91 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/beautifier.d.ts +120 -0
- package/dist/beautifier.d.ts.map +1 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1267 -0
- package/dist/index.js.map +7 -0
- package/dist/languageDetector.d.ts +74 -0
- package/dist/languageDetector.d.ts.map +1 -0
- package/dist/llmConfig.d.ts +34 -0
- package/dist/llmConfig.d.ts.map +1 -0
- package/dist/searcher.d.ts +113 -0
- package/dist/searcher.d.ts.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +571 -78
- package/dist/server.js.map +4 -4
- package/dist/tools/ToolDefinition.d.ts +24 -0
- package/dist/tools/ToolDefinition.d.ts.map +1 -0
- package/dist/tools/aiFindJsvmpDispatcher.d.ts +79 -0
- package/dist/tools/aiFindJsvmpDispatcher.d.ts.map +1 -0
- package/dist/tools/applyCustomTransform.d.ts +14 -0
- package/dist/tools/applyCustomTransform.d.ts.map +1 -0
- package/dist/tools/findUsageSmart.d.ts +16 -0
- package/dist/tools/findUsageSmart.d.ts.map +1 -0
- package/dist/tools/index.d.ts +43 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/readCodeSmart.d.ts +13 -0
- package/dist/tools/readCodeSmart.d.ts.map +1 -0
- package/dist/tools/searchCodeSmart.d.ts +18 -0
- package/dist/tools/searchCodeSmart.d.ts.map +1 -0
- package/dist/transformer.d.ts +119 -0
- package/dist/transformer.d.ts.map +1 -0
- package/dist/truncator.d.ts +69 -0
- package/dist/truncator.d.ts.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +16 -16
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import { z as
|
|
6
|
+
import { z as z6 } from "zod";
|
|
7
7
|
|
|
8
8
|
// src/tools/readCodeSmart.ts
|
|
9
9
|
import { z } from "zod";
|
|
@@ -18,9 +18,82 @@ function defineTool(definition) {
|
|
|
18
18
|
import * as esbuild from "esbuild";
|
|
19
19
|
import * as crypto from "crypto";
|
|
20
20
|
import * as fs from "fs/promises";
|
|
21
|
-
import * as
|
|
21
|
+
import * as path2 from "path";
|
|
22
22
|
import * as os from "os";
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
// src/languageDetector.ts
|
|
25
|
+
import * as path from "path";
|
|
26
|
+
var LANGUAGE_CONFIG = {
|
|
27
|
+
javascript: {
|
|
28
|
+
language: "javascript",
|
|
29
|
+
supportsAST: true,
|
|
30
|
+
supportsBeautify: true,
|
|
31
|
+
supportsSourceMap: true
|
|
32
|
+
},
|
|
33
|
+
typescript: {
|
|
34
|
+
language: "typescript",
|
|
35
|
+
supportsAST: true,
|
|
36
|
+
supportsBeautify: true,
|
|
37
|
+
supportsSourceMap: true
|
|
38
|
+
},
|
|
39
|
+
json: {
|
|
40
|
+
language: "json",
|
|
41
|
+
supportsAST: false,
|
|
42
|
+
supportsBeautify: true,
|
|
43
|
+
supportsSourceMap: false
|
|
44
|
+
},
|
|
45
|
+
html: {
|
|
46
|
+
language: "html",
|
|
47
|
+
supportsAST: false,
|
|
48
|
+
supportsBeautify: true,
|
|
49
|
+
supportsSourceMap: false
|
|
50
|
+
},
|
|
51
|
+
xml: {
|
|
52
|
+
language: "xml",
|
|
53
|
+
supportsAST: false,
|
|
54
|
+
supportsBeautify: true,
|
|
55
|
+
supportsSourceMap: false
|
|
56
|
+
},
|
|
57
|
+
css: {
|
|
58
|
+
language: "css",
|
|
59
|
+
supportsAST: false,
|
|
60
|
+
supportsBeautify: true,
|
|
61
|
+
supportsSourceMap: false
|
|
62
|
+
},
|
|
63
|
+
unknown: {
|
|
64
|
+
language: "unknown",
|
|
65
|
+
supportsAST: false,
|
|
66
|
+
supportsBeautify: false,
|
|
67
|
+
supportsSourceMap: false
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var EXTENSION_MAP = {
|
|
71
|
+
".js": "javascript",
|
|
72
|
+
".mjs": "javascript",
|
|
73
|
+
".cjs": "javascript",
|
|
74
|
+
".jsx": "javascript",
|
|
75
|
+
".ts": "typescript",
|
|
76
|
+
".tsx": "typescript",
|
|
77
|
+
".mts": "typescript",
|
|
78
|
+
".cts": "typescript",
|
|
79
|
+
".json": "json",
|
|
80
|
+
".html": "html",
|
|
81
|
+
".htm": "html",
|
|
82
|
+
".xml": "xml",
|
|
83
|
+
".svg": "xml",
|
|
84
|
+
".css": "css"
|
|
85
|
+
};
|
|
86
|
+
function detectLanguage(filePath) {
|
|
87
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
88
|
+
const language = EXTENSION_MAP[ext] ?? "unknown";
|
|
89
|
+
return LANGUAGE_CONFIG[language];
|
|
90
|
+
}
|
|
91
|
+
function getLanguageInfo(language) {
|
|
92
|
+
return LANGUAGE_CONFIG[language];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/beautifier.ts
|
|
96
|
+
var TEMP_DIR = path2.join(os.tmpdir(), "smart-fs-mcp-cache");
|
|
24
97
|
async function ensureCacheDir() {
|
|
25
98
|
await fs.mkdir(TEMP_DIR, { recursive: true });
|
|
26
99
|
}
|
|
@@ -29,17 +102,17 @@ function calculateCacheKey(originalPath, mtimeMs) {
|
|
|
29
102
|
return crypto.createHash("md5").update(fileKey).digest("hex");
|
|
30
103
|
}
|
|
31
104
|
function getCachePaths(originalPath, hash) {
|
|
32
|
-
const fileName =
|
|
33
|
-
const beautifiedPath =
|
|
105
|
+
const fileName = path2.basename(originalPath, ".js");
|
|
106
|
+
const beautifiedPath = path2.join(TEMP_DIR, `${fileName}.${hash}.beautified.js`);
|
|
34
107
|
const mapPath = `${beautifiedPath}.map`;
|
|
35
108
|
return { beautifiedPath, mapPath };
|
|
36
109
|
}
|
|
37
110
|
function getLocalPaths(originalPath) {
|
|
38
|
-
const absolutePath =
|
|
39
|
-
const dir =
|
|
40
|
-
const ext =
|
|
41
|
-
const baseName =
|
|
42
|
-
const beautifiedPath =
|
|
111
|
+
const absolutePath = path2.resolve(originalPath);
|
|
112
|
+
const dir = path2.dirname(absolutePath);
|
|
113
|
+
const ext = path2.extname(absolutePath);
|
|
114
|
+
const baseName = path2.basename(absolutePath, ext);
|
|
115
|
+
const beautifiedPath = path2.join(dir, `${baseName}.beautified.js`);
|
|
43
116
|
const mapPath = `${beautifiedPath}.map`;
|
|
44
117
|
return { beautifiedPath, mapPath };
|
|
45
118
|
}
|
|
@@ -55,7 +128,7 @@ async function isCacheValid(beautifiedPath, mapPath) {
|
|
|
55
128
|
}
|
|
56
129
|
}
|
|
57
130
|
async function isLocalCacheValid(originalPath) {
|
|
58
|
-
const absolutePath =
|
|
131
|
+
const absolutePath = path2.resolve(originalPath);
|
|
59
132
|
const { beautifiedPath } = getLocalPaths(absolutePath);
|
|
60
133
|
let originalStats;
|
|
61
134
|
try {
|
|
@@ -89,15 +162,122 @@ async function isLocalCacheValid(originalPath) {
|
|
|
89
162
|
isValid
|
|
90
163
|
};
|
|
91
164
|
}
|
|
165
|
+
function beautifyJson(content) {
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(content);
|
|
168
|
+
return { code: JSON.stringify(parsed, null, 2), parseFailed: false };
|
|
169
|
+
} catch {
|
|
170
|
+
return { code: content, parseFailed: true };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function beautifyHtml(content) {
|
|
174
|
+
try {
|
|
175
|
+
let formatted = "";
|
|
176
|
+
let indent = 0;
|
|
177
|
+
const indentStr = " ";
|
|
178
|
+
const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
179
|
+
const tokens = normalized.split(/(<[^>]+>)/g).filter((token) => token.trim() !== "");
|
|
180
|
+
for (const token of tokens) {
|
|
181
|
+
const trimmedToken = token.trim();
|
|
182
|
+
if (!trimmedToken) continue;
|
|
183
|
+
if (trimmedToken.startsWith("<")) {
|
|
184
|
+
if (trimmedToken.startsWith("<!") || trimmedToken.startsWith("<?") || trimmedToken.endsWith("/>")) {
|
|
185
|
+
formatted += indentStr.repeat(indent) + trimmedToken + "\n";
|
|
186
|
+
} else if (trimmedToken.startsWith("</")) {
|
|
187
|
+
indent = Math.max(0, indent - 1);
|
|
188
|
+
formatted += indentStr.repeat(indent) + trimmedToken + "\n";
|
|
189
|
+
} else {
|
|
190
|
+
formatted += indentStr.repeat(indent) + trimmedToken + "\n";
|
|
191
|
+
indent++;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
const textLines = trimmedToken.split("\n");
|
|
195
|
+
for (const line of textLines) {
|
|
196
|
+
const trimmedLine = line.trim();
|
|
197
|
+
if (trimmedLine) {
|
|
198
|
+
formatted += indentStr.repeat(indent) + trimmedLine + "\n";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return formatted.trimEnd();
|
|
204
|
+
} catch {
|
|
205
|
+
return content;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function beautifyCss(content) {
|
|
209
|
+
try {
|
|
210
|
+
let formatted = content;
|
|
211
|
+
formatted = formatted.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
212
|
+
formatted = formatted.replace(/\{/g, " {\n");
|
|
213
|
+
formatted = formatted.replace(/\}/g, "\n}\n");
|
|
214
|
+
formatted = formatted.replace(/;/g, ";\n");
|
|
215
|
+
formatted = formatted.replace(/\n\s*\n/g, "\n");
|
|
216
|
+
const lines = formatted.split("\n");
|
|
217
|
+
let indent = 0;
|
|
218
|
+
const indentStr = " ";
|
|
219
|
+
const result = [];
|
|
220
|
+
for (const line of lines) {
|
|
221
|
+
const trimmed = line.trim();
|
|
222
|
+
if (!trimmed) continue;
|
|
223
|
+
if (trimmed.startsWith("}")) {
|
|
224
|
+
indent = Math.max(0, indent - 1);
|
|
225
|
+
}
|
|
226
|
+
result.push(indentStr.repeat(indent) + trimmed);
|
|
227
|
+
if (trimmed.endsWith("{")) {
|
|
228
|
+
indent++;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return result.join("\n");
|
|
232
|
+
} catch {
|
|
233
|
+
return content;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function beautifyCode(code, language) {
|
|
237
|
+
const langInfo = getLanguageInfo(language);
|
|
238
|
+
if (!langInfo.supportsBeautify) {
|
|
239
|
+
return { code, usedFallback: true };
|
|
240
|
+
}
|
|
241
|
+
switch (language) {
|
|
242
|
+
case "json": {
|
|
243
|
+
const result = beautifyJson(code);
|
|
244
|
+
return { code: result.code, usedFallback: result.parseFailed };
|
|
245
|
+
}
|
|
246
|
+
case "html":
|
|
247
|
+
case "xml":
|
|
248
|
+
return { code: beautifyHtml(code), usedFallback: false };
|
|
249
|
+
case "css":
|
|
250
|
+
return { code: beautifyCss(code), usedFallback: false };
|
|
251
|
+
case "javascript":
|
|
252
|
+
case "typescript":
|
|
253
|
+
return { code, usedFallback: true };
|
|
254
|
+
default:
|
|
255
|
+
return { code, usedFallback: true };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
92
258
|
async function ensureBeautified(originalPath, options) {
|
|
93
|
-
const absolutePath =
|
|
259
|
+
const absolutePath = path2.resolve(originalPath);
|
|
94
260
|
let stats;
|
|
95
261
|
try {
|
|
96
262
|
stats = await fs.stat(absolutePath);
|
|
97
263
|
} catch {
|
|
98
264
|
throw new Error(`File not found: ${originalPath}`);
|
|
99
265
|
}
|
|
266
|
+
const langInfo = options?.language ? getLanguageInfo(options.language) : detectLanguage(absolutePath);
|
|
267
|
+
const language = langInfo.language;
|
|
100
268
|
const localPaths = getLocalPaths(absolutePath);
|
|
269
|
+
if (language !== "javascript" && language !== "typescript") {
|
|
270
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
271
|
+
const beautified = beautifyCode(content, language);
|
|
272
|
+
return {
|
|
273
|
+
code: beautified.code,
|
|
274
|
+
rawMap: null,
|
|
275
|
+
// No source map for non-JS/TS
|
|
276
|
+
localPath: localPaths.beautifiedPath,
|
|
277
|
+
localMapPath: localPaths.mapPath,
|
|
278
|
+
usedFallback: beautified.usedFallback
|
|
279
|
+
};
|
|
280
|
+
}
|
|
101
281
|
const localCacheCheck = await isLocalCacheValid(absolutePath);
|
|
102
282
|
if (localCacheCheck.isValid) {
|
|
103
283
|
try {
|
|
@@ -109,7 +289,8 @@ async function ensureBeautified(originalPath, options) {
|
|
|
109
289
|
code: code2,
|
|
110
290
|
rawMap: JSON.parse(mapContent),
|
|
111
291
|
localPath: localPaths.beautifiedPath,
|
|
112
|
-
localMapPath: localPaths.mapPath
|
|
292
|
+
localMapPath: localPaths.mapPath,
|
|
293
|
+
usedFallback: false
|
|
113
294
|
};
|
|
114
295
|
} catch {
|
|
115
296
|
}
|
|
@@ -126,7 +307,8 @@ async function ensureBeautified(originalPath, options) {
|
|
|
126
307
|
code: code2,
|
|
127
308
|
rawMap: JSON.parse(mapContent),
|
|
128
309
|
localPath: localPaths.beautifiedPath,
|
|
129
|
-
localMapPath: localPaths.mapPath
|
|
310
|
+
localMapPath: localPaths.mapPath,
|
|
311
|
+
usedFallback: false
|
|
130
312
|
};
|
|
131
313
|
await saveToLocal(result2, localPaths, mapContent);
|
|
132
314
|
return result2;
|
|
@@ -147,13 +329,26 @@ async function ensureBeautified(originalPath, options) {
|
|
|
147
329
|
treeShaking: false
|
|
148
330
|
});
|
|
149
331
|
} catch (err) {
|
|
150
|
-
const
|
|
151
|
-
|
|
332
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
333
|
+
return {
|
|
334
|
+
code: content,
|
|
335
|
+
rawMap: null,
|
|
336
|
+
localPath: localPaths.beautifiedPath,
|
|
337
|
+
localMapPath: localPaths.mapPath,
|
|
338
|
+
usedFallback: true
|
|
339
|
+
};
|
|
152
340
|
}
|
|
153
341
|
const codeFile = esbuildResult.outputFiles?.find((f) => f.path.endsWith(".js"));
|
|
154
342
|
const mapFile = esbuildResult.outputFiles?.find((f) => f.path.endsWith(".map"));
|
|
155
343
|
if (!codeFile || !mapFile) {
|
|
156
|
-
|
|
344
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
345
|
+
return {
|
|
346
|
+
code: content,
|
|
347
|
+
rawMap: null,
|
|
348
|
+
localPath: localPaths.beautifiedPath,
|
|
349
|
+
localMapPath: localPaths.mapPath,
|
|
350
|
+
usedFallback: true
|
|
351
|
+
};
|
|
157
352
|
}
|
|
158
353
|
const code = codeFile.text;
|
|
159
354
|
const rawMap = JSON.parse(mapFile.text);
|
|
@@ -166,7 +361,8 @@ async function ensureBeautified(originalPath, options) {
|
|
|
166
361
|
code,
|
|
167
362
|
rawMap,
|
|
168
363
|
localPath: localPaths.beautifiedPath,
|
|
169
|
-
localMapPath: localPaths.mapPath
|
|
364
|
+
localMapPath: localPaths.mapPath,
|
|
365
|
+
usedFallback: false
|
|
170
366
|
};
|
|
171
367
|
await saveToLocal(result, localPaths, mapText);
|
|
172
368
|
return result;
|
|
@@ -182,9 +378,9 @@ async function saveToLocal(result, localPaths, mapText) {
|
|
|
182
378
|
} catch (err) {
|
|
183
379
|
const error = err;
|
|
184
380
|
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
185
|
-
result.localSaveError = `Permission denied: Cannot write to ${
|
|
381
|
+
result.localSaveError = `Permission denied: Cannot write to ${path2.dirname(localPaths.beautifiedPath)}`;
|
|
186
382
|
} else if (error.code === "ENOSPC") {
|
|
187
|
-
result.localSaveError = `Insufficient disk space: Cannot write to ${
|
|
383
|
+
result.localSaveError = `Insufficient disk space: Cannot write to ${path2.dirname(localPaths.beautifiedPath)}`;
|
|
188
384
|
} else {
|
|
189
385
|
result.localSaveError = `Failed to save locally: ${error.message || String(err)}`;
|
|
190
386
|
}
|
|
@@ -213,7 +409,7 @@ function createTruncatedString(original, previewLength) {
|
|
|
213
409
|
const newlineStr = "\n".repeat(preservedNewlines);
|
|
214
410
|
return `${start}${marker}${newlineStr}${end}`;
|
|
215
411
|
}
|
|
216
|
-
function
|
|
412
|
+
function truncateCodeAST(sourceCode, limit = 200, previewLength = 50) {
|
|
217
413
|
let ast;
|
|
218
414
|
try {
|
|
219
415
|
ast = parse(sourceCode, {
|
|
@@ -224,7 +420,7 @@ function truncateCodeHighPerf(sourceCode, limit = 200, previewLength = 50) {
|
|
|
224
420
|
raw: true
|
|
225
421
|
});
|
|
226
422
|
} catch {
|
|
227
|
-
return sourceCode;
|
|
423
|
+
return { code: sourceCode, parseFailed: true };
|
|
228
424
|
}
|
|
229
425
|
const magicString = new MagicString(sourceCode);
|
|
230
426
|
walk(ast, {
|
|
@@ -251,7 +447,10 @@ function truncateCodeHighPerf(sourceCode, limit = 200, previewLength = 50) {
|
|
|
251
447
|
}
|
|
252
448
|
}
|
|
253
449
|
});
|
|
254
|
-
return magicString.toString();
|
|
450
|
+
return { code: magicString.toString(), parseFailed: false };
|
|
451
|
+
}
|
|
452
|
+
function truncateCodeHighPerf(sourceCode, limit = 200, previewLength = 50) {
|
|
453
|
+
return truncateCodeAST(sourceCode, limit, previewLength).code;
|
|
255
454
|
}
|
|
256
455
|
function truncateLongLines(code, maxLineChars = 500, previewRatio = 0.2) {
|
|
257
456
|
if (!code) {
|
|
@@ -357,11 +556,11 @@ var readCodeSmart = defineTool({
|
|
|
357
556
|
import { z as z2 } from "zod";
|
|
358
557
|
|
|
359
558
|
// src/transformer.ts
|
|
360
|
-
import * as
|
|
559
|
+
import * as path3 from "path";
|
|
361
560
|
import * as fs2 from "fs/promises";
|
|
362
561
|
import { transformSync } from "@babel/core";
|
|
363
562
|
function cleanBasename(filename) {
|
|
364
|
-
const base =
|
|
563
|
+
const base = path3.basename(filename);
|
|
365
564
|
let name = base.endsWith(".js") ? base.slice(0, -3) : base;
|
|
366
565
|
name = name.replace(/_deob[^/]*$/, "");
|
|
367
566
|
if (name.endsWith(".beautified")) {
|
|
@@ -370,15 +569,15 @@ function cleanBasename(filename) {
|
|
|
370
569
|
return name;
|
|
371
570
|
}
|
|
372
571
|
function getOutputPaths(targetFile, outputSuffix = "_deob") {
|
|
373
|
-
const absolutePath =
|
|
374
|
-
const dir =
|
|
572
|
+
const absolutePath = path3.resolve(targetFile);
|
|
573
|
+
const dir = path3.dirname(absolutePath);
|
|
375
574
|
const basename3 = cleanBasename(absolutePath);
|
|
376
|
-
const outputPath =
|
|
575
|
+
const outputPath = path3.join(dir, `${basename3}${outputSuffix}.js`);
|
|
377
576
|
const mapPath = `${outputPath}.map`;
|
|
378
577
|
return { outputPath, mapPath };
|
|
379
578
|
}
|
|
380
579
|
async function loadBabelPlugin(scriptPath) {
|
|
381
|
-
const absolutePath =
|
|
580
|
+
const absolutePath = path3.resolve(scriptPath);
|
|
382
581
|
try {
|
|
383
582
|
await fs2.access(absolutePath);
|
|
384
583
|
} catch {
|
|
@@ -436,7 +635,7 @@ function runBabelTransform(code, inputSourceMap, plugin, filename) {
|
|
|
436
635
|
}
|
|
437
636
|
async function applyCustomTransform(targetFile, options) {
|
|
438
637
|
const { scriptPath, outputSuffix = "_deob" } = options;
|
|
439
|
-
const absoluteTargetPath =
|
|
638
|
+
const absoluteTargetPath = path3.resolve(targetFile);
|
|
440
639
|
try {
|
|
441
640
|
await fs2.access(absoluteTargetPath);
|
|
442
641
|
} catch {
|
|
@@ -452,12 +651,12 @@ async function applyCustomTransform(targetFile, options) {
|
|
|
452
651
|
absoluteTargetPath
|
|
453
652
|
);
|
|
454
653
|
const { outputPath, mapPath } = getOutputPaths(absoluteTargetPath, outputSuffix);
|
|
455
|
-
const mapFileName =
|
|
654
|
+
const mapFileName = path3.basename(mapPath);
|
|
456
655
|
const outputCode = `${transformResult.code}
|
|
457
656
|
//# sourceMappingURL=${mapFileName}`;
|
|
458
657
|
const outputMap = {
|
|
459
658
|
...transformResult.map,
|
|
460
|
-
file:
|
|
659
|
+
file: path3.basename(outputPath)
|
|
461
660
|
};
|
|
462
661
|
try {
|
|
463
662
|
await Promise.all([
|
|
@@ -467,7 +666,7 @@ async function applyCustomTransform(targetFile, options) {
|
|
|
467
666
|
} catch (err) {
|
|
468
667
|
const error = err;
|
|
469
668
|
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
470
|
-
throw new Error(`Permission denied: Cannot write to ${
|
|
669
|
+
throw new Error(`Permission denied: Cannot write to ${path3.dirname(outputPath)}`);
|
|
471
670
|
}
|
|
472
671
|
throw new Error(`Failed to write output files: ${error.message || String(err)}`);
|
|
473
672
|
}
|
|
@@ -658,22 +857,27 @@ function formatSourcePosition2(line, column) {
|
|
|
658
857
|
}
|
|
659
858
|
return "";
|
|
660
859
|
}
|
|
860
|
+
function formatCodeLine2(lineNumber, sourcePos, code, maxLineNumWidth, prefix = " ") {
|
|
861
|
+
const lineNumStr = String(lineNumber).padStart(maxLineNumWidth, " ");
|
|
862
|
+
const srcPosPadded = sourcePos ? sourcePos.padEnd(10, " ") : " ";
|
|
863
|
+
return `${prefix}${lineNumStr} ${srcPosPadded} ${code}`;
|
|
864
|
+
}
|
|
661
865
|
function formatSearchResult(filePath, query, caseSensitive, result, maxMatches = 50, isRegex = false) {
|
|
662
866
|
const { matches, totalMatches, truncated } = result;
|
|
663
867
|
const outputParts = [];
|
|
664
868
|
const caseInfo = caseSensitive ? "case-sensitive" : "case-insensitive";
|
|
665
869
|
const modeInfo = isRegex ? "regex" : "literal";
|
|
666
|
-
outputParts.push(
|
|
667
|
-
outputParts.push(`
|
|
870
|
+
outputParts.push(`${filePath}`);
|
|
871
|
+
outputParts.push(`Query="${query}" (${modeInfo}, ${caseInfo})`);
|
|
872
|
+
outputParts.push(`Src=original position for breakpoints`);
|
|
668
873
|
if (totalMatches === 0) {
|
|
669
|
-
outputParts.push("
|
|
874
|
+
outputParts.push("Matches: None");
|
|
670
875
|
return outputParts.join("\n");
|
|
671
876
|
}
|
|
672
|
-
const matchInfo = truncated ? `
|
|
877
|
+
const matchInfo = truncated ? `Matches: ${totalMatches} (showing first ${maxMatches})` : `Matches: ${totalMatches}`;
|
|
673
878
|
outputParts.push(matchInfo);
|
|
674
|
-
outputParts.push("-".repeat(85));
|
|
675
879
|
for (const match of matches) {
|
|
676
|
-
outputParts.push(`---
|
|
880
|
+
outputParts.push(`--- Line ${match.lineNumber} ---`);
|
|
677
881
|
const allLineNumbers = [
|
|
678
882
|
...match.contextBefore.map((c) => c.lineNumber),
|
|
679
883
|
match.lineNumber,
|
|
@@ -681,25 +885,19 @@ function formatSearchResult(filePath, query, caseSensitive, result, maxMatches =
|
|
|
681
885
|
];
|
|
682
886
|
const maxLineNumWidth = Math.max(...allLineNumbers.map((n) => String(n).length));
|
|
683
887
|
for (const ctx of match.contextBefore) {
|
|
684
|
-
const lineNumStr = String(ctx.lineNumber).padStart(maxLineNumWidth, " ");
|
|
685
888
|
const srcPos = formatSourcePosition2(ctx.originalPosition.line, ctx.originalPosition.column);
|
|
686
|
-
|
|
687
|
-
outputParts.push(` ${lineNumStr} | [${srcPosPadded.padEnd(14, " ")}] | ${ctx.content}`);
|
|
889
|
+
outputParts.push(formatCodeLine2(ctx.lineNumber, srcPos, ctx.content, maxLineNumWidth, " "));
|
|
688
890
|
}
|
|
689
|
-
const matchLineNumStr = String(match.lineNumber).padStart(maxLineNumWidth, " ");
|
|
690
891
|
const matchSrcPos = formatSourcePosition2(match.originalPosition.line, match.originalPosition.column);
|
|
691
|
-
|
|
692
|
-
outputParts.push(`>> ${matchLineNumStr} | [${matchSrcPosPadded.padEnd(14, " ")}] | ${match.lineContent}`);
|
|
892
|
+
outputParts.push(formatCodeLine2(match.lineNumber, matchSrcPos, match.lineContent, maxLineNumWidth, ">>"));
|
|
693
893
|
for (const ctx of match.contextAfter) {
|
|
694
|
-
const lineNumStr = String(ctx.lineNumber).padStart(maxLineNumWidth, " ");
|
|
695
894
|
const srcPos = formatSourcePosition2(ctx.originalPosition.line, ctx.originalPosition.column);
|
|
696
|
-
|
|
697
|
-
outputParts.push(` ${lineNumStr} | [${srcPosPadded.padEnd(14, " ")}] | ${ctx.content}`);
|
|
895
|
+
outputParts.push(formatCodeLine2(ctx.lineNumber, srcPos, ctx.content, maxLineNumWidth, " "));
|
|
698
896
|
}
|
|
699
|
-
outputParts.push("");
|
|
700
897
|
}
|
|
701
898
|
if (truncated) {
|
|
702
|
-
outputParts.push(
|
|
899
|
+
outputParts.push(`
|
|
900
|
+
... (${totalMatches - maxMatches} more matches not shown)`);
|
|
703
901
|
}
|
|
704
902
|
return outputParts.join("\n");
|
|
705
903
|
}
|
|
@@ -817,17 +1015,17 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
817
1015
|
const traverse2 = await getTraverse();
|
|
818
1016
|
try {
|
|
819
1017
|
traverse2(ast, {
|
|
820
|
-
Identifier(
|
|
821
|
-
if (
|
|
1018
|
+
Identifier(path4) {
|
|
1019
|
+
if (path4.node.name !== identifier) {
|
|
822
1020
|
return;
|
|
823
1021
|
}
|
|
824
1022
|
if (isTargeted) {
|
|
825
|
-
const nodeLoc =
|
|
1023
|
+
const nodeLoc = path4.node.loc;
|
|
826
1024
|
if (!nodeLoc || nodeLoc.start.line !== targetLine) {
|
|
827
1025
|
return;
|
|
828
1026
|
}
|
|
829
1027
|
}
|
|
830
|
-
const binding =
|
|
1028
|
+
const binding = path4.scope.getBinding(identifier);
|
|
831
1029
|
if (!binding) {
|
|
832
1030
|
return;
|
|
833
1031
|
}
|
|
@@ -866,7 +1064,7 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
866
1064
|
const limitedReferences = allReferences.slice(0, maxReferences);
|
|
867
1065
|
let hitLocation;
|
|
868
1066
|
if (isTargeted) {
|
|
869
|
-
const nodeLoc =
|
|
1067
|
+
const nodeLoc = path4.node.loc;
|
|
870
1068
|
hitLocation = createLocationInfo(
|
|
871
1069
|
nodeLoc.start.line,
|
|
872
1070
|
nodeLoc.start.column,
|
|
@@ -883,7 +1081,7 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
883
1081
|
hitLocation
|
|
884
1082
|
});
|
|
885
1083
|
if (isTargeted) {
|
|
886
|
-
|
|
1084
|
+
path4.stop();
|
|
887
1085
|
}
|
|
888
1086
|
}
|
|
889
1087
|
});
|
|
@@ -904,36 +1102,41 @@ function formatSourcePosition3(line, column) {
|
|
|
904
1102
|
}
|
|
905
1103
|
return "";
|
|
906
1104
|
}
|
|
1105
|
+
function formatCodeLine3(lineNumber, sourcePos, code, prefix = " ") {
|
|
1106
|
+
const lineNumStr = String(lineNumber).padStart(5, " ");
|
|
1107
|
+
const srcPosPadded = sourcePos ? sourcePos.padEnd(10, " ") : " ";
|
|
1108
|
+
return `${prefix}${lineNumStr} ${srcPosPadded} ${code}`;
|
|
1109
|
+
}
|
|
907
1110
|
function locationsMatch(loc1, loc2) {
|
|
908
1111
|
return loc1.line === loc2.line && loc1.column === loc2.column;
|
|
909
1112
|
}
|
|
910
1113
|
function formatAnalysisResult(filePath, result, maxReferences = 10) {
|
|
911
1114
|
const { bindings, identifier, isTargeted, targetLine } = result;
|
|
912
1115
|
const outputParts = [];
|
|
913
|
-
outputParts.push(
|
|
914
|
-
outputParts.push(`
|
|
1116
|
+
outputParts.push(`${filePath}`);
|
|
1117
|
+
outputParts.push(`Identifier="${identifier}"`);
|
|
1118
|
+
outputParts.push(`Src=original position for breakpoints`);
|
|
915
1119
|
if (bindings.length === 0) {
|
|
916
1120
|
if (isTargeted && targetLine !== void 0) {
|
|
917
|
-
outputParts.push(`
|
|
1121
|
+
outputParts.push(`Bindings: None at line ${targetLine}`);
|
|
918
1122
|
outputParts.push(`The variable may be global, externally defined, or not present at this line.`);
|
|
919
1123
|
} else {
|
|
920
|
-
outputParts.push("
|
|
1124
|
+
outputParts.push("Bindings: None");
|
|
921
1125
|
}
|
|
922
1126
|
return outputParts.join("\n");
|
|
923
1127
|
}
|
|
924
1128
|
if (isTargeted) {
|
|
925
|
-
outputParts.push(`
|
|
1129
|
+
outputParts.push(`Bindings: 1 (Targeted at line ${targetLine})`);
|
|
926
1130
|
} else {
|
|
927
|
-
const scopeInfo = bindings.length > 1 ? " (
|
|
928
|
-
outputParts.push(`
|
|
1131
|
+
const scopeInfo = bindings.length > 1 ? " (different scopes)" : "";
|
|
1132
|
+
outputParts.push(`Bindings: ${bindings.length}${scopeInfo}`);
|
|
929
1133
|
}
|
|
930
|
-
outputParts.push("-".repeat(85));
|
|
931
1134
|
for (let i = 0; i < bindings.length; i++) {
|
|
932
1135
|
const binding = bindings[i];
|
|
933
1136
|
if (isTargeted) {
|
|
934
|
-
outputParts.push(
|
|
1137
|
+
outputParts.push(`--- Targeted Scope (${binding.kind}) ---`);
|
|
935
1138
|
} else {
|
|
936
|
-
outputParts.push(
|
|
1139
|
+
outputParts.push(`--- Scope #${i + 1} (${binding.kind}) ---`);
|
|
937
1140
|
}
|
|
938
1141
|
const defIsHit = isTargeted && binding.hitLocation && locationsMatch(binding.definition, binding.hitLocation);
|
|
939
1142
|
const defPrefix = defIsHit ? "\u{1F4CD} Definition (hit):" : "\u{1F4CD} Definition:";
|
|
@@ -942,11 +1145,8 @@ function formatAnalysisResult(filePath, result, maxReferences = 10) {
|
|
|
942
1145
|
binding.definition.originalPosition.line,
|
|
943
1146
|
binding.definition.originalPosition.column
|
|
944
1147
|
);
|
|
945
|
-
const defSrcPosPadded = defSrcPos ? `Src ${defSrcPos}` : "";
|
|
946
1148
|
const defMarker = defIsHit ? " \u25C0\u2500\u2500 hit" : "";
|
|
947
|
-
outputParts.push(
|
|
948
|
-
` ${binding.definition.line} | [${defSrcPosPadded.padEnd(14, " ")}] | ${binding.definition.lineContent}${defMarker}`
|
|
949
|
-
);
|
|
1149
|
+
outputParts.push(formatCodeLine3(binding.definition.line, defSrcPos, binding.definition.lineContent + defMarker, " "));
|
|
950
1150
|
const totalRefs = binding.totalReferences;
|
|
951
1151
|
if (totalRefs === 0) {
|
|
952
1152
|
outputParts.push("\u{1F50E} References: None");
|
|
@@ -958,18 +1158,14 @@ function formatAnalysisResult(filePath, result, maxReferences = 10) {
|
|
|
958
1158
|
ref.originalPosition.line,
|
|
959
1159
|
ref.originalPosition.column
|
|
960
1160
|
);
|
|
961
|
-
const refSrcPosPadded = refSrcPos ? `Src ${refSrcPos}` : "";
|
|
962
1161
|
const refMarker = refIsHit ? " \u25C0\u2500\u2500 hit" : "";
|
|
963
|
-
outputParts.push(
|
|
964
|
-
` ${ref.line} | [${refSrcPosPadded.padEnd(14, " ")}] | ${ref.lineContent}${refMarker}`
|
|
965
|
-
);
|
|
1162
|
+
outputParts.push(formatCodeLine3(ref.line, refSrcPos, ref.lineContent + refMarker, " "));
|
|
966
1163
|
}
|
|
967
1164
|
if (totalRefs > maxReferences) {
|
|
968
1165
|
const remaining = totalRefs - maxReferences;
|
|
969
|
-
outputParts.push(`
|
|
1166
|
+
outputParts.push(` ... (${remaining} more references not shown)`);
|
|
970
1167
|
}
|
|
971
1168
|
}
|
|
972
|
-
outputParts.push("");
|
|
973
1169
|
}
|
|
974
1170
|
return outputParts.join("\n");
|
|
975
1171
|
}
|
|
@@ -1024,12 +1220,309 @@ var findUsageSmart = defineTool({
|
|
|
1024
1220
|
}
|
|
1025
1221
|
});
|
|
1026
1222
|
|
|
1223
|
+
// src/tools/aiFindJsvmpDispatcher.ts
|
|
1224
|
+
import { SourceMapConsumer as SourceMapConsumer4 } from "source-map-js";
|
|
1225
|
+
import { z as z5 } from "zod";
|
|
1226
|
+
|
|
1227
|
+
// src/llmConfig.ts
|
|
1228
|
+
function getLLMConfig() {
|
|
1229
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
1230
|
+
if (!apiKey) {
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
|
|
1234
|
+
const model = process.env.OPENAI_MODEL || "gpt-4o-mini";
|
|
1235
|
+
return {
|
|
1236
|
+
apiKey,
|
|
1237
|
+
baseUrl,
|
|
1238
|
+
model
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
function buildJSVMPSystemPrompt() {
|
|
1242
|
+
return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 JavaScript \u9006\u5411\u5DE5\u7A0B\u4E13\u5BB6\uFF0C\u4E13\u95E8\u8BC6\u522B JSVMP\uFF08JavaScript Virtual Machine Protection\uFF09\u4FDD\u62A4\u4EE3\u7801\u3002
|
|
1243
|
+
|
|
1244
|
+
JSVMP \u662F\u4E00\u79CD\u4EE3\u7801\u4FDD\u62A4\u6280\u672F\uFF0C\u5C06 JavaScript \u4EE3\u7801\u8F6C\u6362\u4E3A\u5B57\u8282\u7801\uFF0C\u5E76\u901A\u8FC7\u865A\u62DF\u673A\u6267\u884C\u3002\u5178\u578B\u7279\u5F81\u5305\u62EC\uFF1A
|
|
1245
|
+
|
|
1246
|
+
1. **\u865A\u62DF\u6808\uFF08Virtual Stack\uFF09**\uFF1A\u4E2D\u592E\u6570\u7EC4\u7528\u4E8E\u5B58\u50A8\u64CD\u4F5C\u6570\u548C\u7ED3\u679C
|
|
1247
|
+
2. **\u5206\u53D1\u5668\uFF08Dispatcher\uFF09**\uFF1A\u5927\u578B switch \u8BED\u53E5\u6216\u5D4C\u5957 if-else \u94FE\uFF0C\u6839\u636E\u6307\u4EE4\u7801\u6267\u884C\u4E0D\u540C\u64CD\u4F5C
|
|
1248
|
+
3. **\u6307\u4EE4\u6570\u7EC4\uFF08Instruction Array\uFF09**\uFF1A\u5B58\u50A8\u5B57\u8282\u7801\u6307\u4EE4\u7684\u6570\u7EC4
|
|
1249
|
+
4. **\u4E3B\u5FAA\u73AF\uFF08Main Loop\uFF09**\uFF1Awhile \u5FAA\u73AF\u6301\u7EED\u6267\u884C\u6307\u4EE4
|
|
1250
|
+
|
|
1251
|
+
\u68C0\u6D4B\u89C4\u5219\uFF1A
|
|
1252
|
+
|
|
1253
|
+
**Ultra High \u7F6E\u4FE1\u5EA6**\uFF1A
|
|
1254
|
+
- \u540C\u65F6\u51FA\u73B0\uFF1A\u4E3B\u5FAA\u73AF + \u5206\u53D1\u5668 + \u6808\u64CD\u4F5C
|
|
1255
|
+
- \u5206\u53D1\u5668\u6709 >20 \u4E2A case \u6216 >10 \u5C42\u5D4C\u5957
|
|
1256
|
+
- \u660E\u786E\u7684\u6808\u64CD\u4F5C\u6A21\u5F0F\uFF08push/pop/\u6570\u7EC4\u7D22\u5F15\uFF09
|
|
1257
|
+
|
|
1258
|
+
**High \u7F6E\u4FE1\u5EA6**\uFF1A
|
|
1259
|
+
- \u72EC\u7ACB\u7684\u5927\u578B\u5206\u53D1\u5668\u7ED3\u6784\uFF08>20 case \u7684 switch \u6216 >10 \u5C42\u5D4C\u5957\u7684 if-else\uFF09
|
|
1260
|
+
- \u660E\u786E\u7684\u6307\u4EE4\u6570\u7EC4\u548C\u7A0B\u5E8F\u8BA1\u6570\u5668\u6A21\u5F0F
|
|
1261
|
+
|
|
1262
|
+
**Medium \u7F6E\u4FE1\u5EA6**\uFF1A
|
|
1263
|
+
- \u5B64\u7ACB\u7684\u6808\u64CD\u4F5C\u6216\u53EF\u7591\u7684 while \u5FAA\u73AF
|
|
1264
|
+
- \u90E8\u5206 JSVMP \u7279\u5F81\u4F46\u4E0D\u5B8C\u6574
|
|
1265
|
+
|
|
1266
|
+
**Low \u7F6E\u4FE1\u5EA6**\uFF1A
|
|
1267
|
+
- \u901A\u7528\u6DF7\u6DC6\u6A21\u5F0F
|
|
1268
|
+
- \u53EF\u80FD\u76F8\u5173\u4F46\u4E0D\u786E\u5B9A\u7684\u7ED3\u6784
|
|
1269
|
+
|
|
1270
|
+
\u8BF7\u5206\u6790\u63D0\u4F9B\u7684\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u76F8\u5173\u533A\u57DF\u3002\u8FD4\u56DE JSON \u683C\u5F0F\uFF1A
|
|
1271
|
+
|
|
1272
|
+
{
|
|
1273
|
+
"summary": "\u5206\u6790\u6458\u8981\uFF08\u4E2D\u6587\uFF09",
|
|
1274
|
+
"regions": [
|
|
1275
|
+
{
|
|
1276
|
+
"start": \u8D77\u59CB\u884C\u53F7,
|
|
1277
|
+
"end": \u7ED3\u675F\u884C\u53F7,
|
|
1278
|
+
"type": "If-Else Dispatcher" | "Switch Dispatcher" | "Instruction Array" | "Stack Operation",
|
|
1279
|
+
"confidence": "ultra_high" | "high" | "medium" | "low",
|
|
1280
|
+
"description": "\u8BE6\u7EC6\u63CF\u8FF0\uFF08\u4E2D\u6587\uFF09"
|
|
1281
|
+
}
|
|
1282
|
+
]
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
\u5982\u679C\u6CA1\u6709\u68C0\u6D4B\u5230 JSVMP \u7279\u5F81\uFF0C\u8FD4\u56DE\u7A7A\u7684 regions \u6570\u7EC4\u3002`;
|
|
1286
|
+
}
|
|
1287
|
+
function createLLMClient(config) {
|
|
1288
|
+
return {
|
|
1289
|
+
async analyzeJSVMP(formattedCode) {
|
|
1290
|
+
const systemPrompt = buildJSVMPSystemPrompt();
|
|
1291
|
+
const requestBody = {
|
|
1292
|
+
model: config.model,
|
|
1293
|
+
messages: [
|
|
1294
|
+
{
|
|
1295
|
+
role: "system",
|
|
1296
|
+
content: systemPrompt
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
role: "user",
|
|
1300
|
+
content: `\u8BF7\u5206\u6790\u4EE5\u4E0B\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u4FDD\u62A4\u7ED3\u6784\uFF1A
|
|
1301
|
+
|
|
1302
|
+
${formattedCode}`
|
|
1303
|
+
}
|
|
1304
|
+
],
|
|
1305
|
+
temperature: 0.1,
|
|
1306
|
+
response_format: { type: "json_object" }
|
|
1307
|
+
};
|
|
1308
|
+
try {
|
|
1309
|
+
const response = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
1310
|
+
method: "POST",
|
|
1311
|
+
headers: {
|
|
1312
|
+
"Content-Type": "application/json",
|
|
1313
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
1314
|
+
},
|
|
1315
|
+
body: JSON.stringify(requestBody)
|
|
1316
|
+
});
|
|
1317
|
+
if (!response.ok) {
|
|
1318
|
+
const errorText = await response.text();
|
|
1319
|
+
throw new Error(`API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);
|
|
1320
|
+
}
|
|
1321
|
+
const data = await response.json();
|
|
1322
|
+
if (!data.choices || !data.choices[0] || !data.choices[0].message) {
|
|
1323
|
+
throw new Error("API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1A\u7F3A\u5C11 choices \u6216 message \u5B57\u6BB5");
|
|
1324
|
+
}
|
|
1325
|
+
const content = data.choices[0].message.content;
|
|
1326
|
+
if (typeof content !== "string") {
|
|
1327
|
+
throw new Error("API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1Amessage.content \u4E0D\u662F\u5B57\u7B26\u4E32");
|
|
1328
|
+
}
|
|
1329
|
+
return content;
|
|
1330
|
+
} catch (error) {
|
|
1331
|
+
if (error instanceof Error) {
|
|
1332
|
+
throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${error.message}`);
|
|
1333
|
+
}
|
|
1334
|
+
throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${String(error)}`);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// src/tools/aiFindJsvmpDispatcher.ts
|
|
1341
|
+
import { existsSync } from "fs";
|
|
1342
|
+
function formatSourcePosition4(line, column) {
|
|
1343
|
+
if (line !== null && column !== null) {
|
|
1344
|
+
return `L${line}:${column}`;
|
|
1345
|
+
}
|
|
1346
|
+
return "";
|
|
1347
|
+
}
|
|
1348
|
+
function formatCodeLine4(lineNumber, sourcePos, code) {
|
|
1349
|
+
const lineNumStr = String(lineNumber).padStart(5, " ");
|
|
1350
|
+
const srcPosPadded = sourcePos ? sourcePos.padEnd(10, " ") : " ";
|
|
1351
|
+
return `${lineNumStr} ${srcPosPadded} ${code}`;
|
|
1352
|
+
}
|
|
1353
|
+
async function formatCodeForAnalysis(filePath, startLine, endLine, charLimit = 300) {
|
|
1354
|
+
const beautifyResult = await ensureBeautified(filePath);
|
|
1355
|
+
const { code, rawMap } = beautifyResult;
|
|
1356
|
+
const truncatedCode = truncateCodeHighPerf(code, charLimit);
|
|
1357
|
+
const lines = truncatedCode.split("\n");
|
|
1358
|
+
const totalLines = lines.length;
|
|
1359
|
+
const effectiveStartLine = Math.max(1, Math.min(totalLines, startLine));
|
|
1360
|
+
const effectiveEndLine = Math.max(effectiveStartLine, Math.min(totalLines, endLine));
|
|
1361
|
+
const consumer = new SourceMapConsumer4({
|
|
1362
|
+
...rawMap,
|
|
1363
|
+
version: String(rawMap.version)
|
|
1364
|
+
});
|
|
1365
|
+
const formattedLines = [];
|
|
1366
|
+
for (let lineNum = effectiveStartLine; lineNum <= effectiveEndLine; lineNum++) {
|
|
1367
|
+
const lineIndex = lineNum - 1;
|
|
1368
|
+
const lineContent = lines[lineIndex] ?? "";
|
|
1369
|
+
const originalPos = consumer.originalPositionFor({
|
|
1370
|
+
line: lineNum,
|
|
1371
|
+
column: 0
|
|
1372
|
+
});
|
|
1373
|
+
const sourcePos = formatSourcePosition4(originalPos.line, originalPos.column);
|
|
1374
|
+
formattedLines.push(formatCodeLine4(lineNum, sourcePos, lineContent));
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
content: formattedLines.join("\n"),
|
|
1378
|
+
totalLines,
|
|
1379
|
+
startLine: effectiveStartLine,
|
|
1380
|
+
endLine: effectiveEndLine
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
var VALID_DETECTION_TYPES = [
|
|
1384
|
+
"If-Else Dispatcher",
|
|
1385
|
+
"Switch Dispatcher",
|
|
1386
|
+
"Instruction Array",
|
|
1387
|
+
"Stack Operation"
|
|
1388
|
+
];
|
|
1389
|
+
var VALID_CONFIDENCE_LEVELS = [
|
|
1390
|
+
"ultra_high",
|
|
1391
|
+
"high",
|
|
1392
|
+
"medium",
|
|
1393
|
+
"low"
|
|
1394
|
+
];
|
|
1395
|
+
function isValidDetectionType(value) {
|
|
1396
|
+
return VALID_DETECTION_TYPES.includes(value);
|
|
1397
|
+
}
|
|
1398
|
+
function isValidConfidenceLevel(value) {
|
|
1399
|
+
return VALID_CONFIDENCE_LEVELS.includes(value);
|
|
1400
|
+
}
|
|
1401
|
+
function parseDetectionResult(jsonString) {
|
|
1402
|
+
let parsed;
|
|
1403
|
+
try {
|
|
1404
|
+
parsed = JSON.parse(jsonString);
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
throw new Error(`\u65E0\u6CD5\u89E3\u6790 LLM \u54CD\u5E94: ${error instanceof Error ? error.message : String(error)}`);
|
|
1407
|
+
}
|
|
1408
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1409
|
+
throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u671F\u671B\u5BF9\u8C61\u7C7B\u578B");
|
|
1410
|
+
}
|
|
1411
|
+
if (typeof parsed.summary !== "string") {
|
|
1412
|
+
throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: summary");
|
|
1413
|
+
}
|
|
1414
|
+
if (!Array.isArray(parsed.regions)) {
|
|
1415
|
+
throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: regions");
|
|
1416
|
+
}
|
|
1417
|
+
const validatedRegions = [];
|
|
1418
|
+
for (let i = 0; i < parsed.regions.length; i++) {
|
|
1419
|
+
const region = parsed.regions[i];
|
|
1420
|
+
if (typeof region !== "object" || region === null) {
|
|
1421
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u4E0D\u662F\u5BF9\u8C61`);
|
|
1422
|
+
}
|
|
1423
|
+
if (typeof region.start !== "number") {
|
|
1424
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: start`);
|
|
1425
|
+
}
|
|
1426
|
+
if (typeof region.end !== "number") {
|
|
1427
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: end`);
|
|
1428
|
+
}
|
|
1429
|
+
if (typeof region.type !== "string") {
|
|
1430
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: type`);
|
|
1431
|
+
}
|
|
1432
|
+
if (typeof region.confidence !== "string") {
|
|
1433
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: confidence`);
|
|
1434
|
+
}
|
|
1435
|
+
if (typeof region.description !== "string") {
|
|
1436
|
+
throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: description`);
|
|
1437
|
+
}
|
|
1438
|
+
if (!isValidDetectionType(region.type)) {
|
|
1439
|
+
throw new Error(
|
|
1440
|
+
`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].type \u503C\u65E0\u6548: "${region.type}". \u6709\u6548\u503C: ${VALID_DETECTION_TYPES.join(", ")}`
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
if (!isValidConfidenceLevel(region.confidence)) {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].confidence \u503C\u65E0\u6548: "${region.confidence}". \u6709\u6548\u503C: ${VALID_CONFIDENCE_LEVELS.join(", ")}`
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
validatedRegions.push({
|
|
1449
|
+
start: region.start,
|
|
1450
|
+
end: region.end,
|
|
1451
|
+
type: region.type,
|
|
1452
|
+
confidence: region.confidence,
|
|
1453
|
+
description: region.description
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
return {
|
|
1457
|
+
summary: parsed.summary,
|
|
1458
|
+
regions: validatedRegions
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
function formatDetectionResult(result, filePath, startLine, endLine) {
|
|
1462
|
+
const lines = [];
|
|
1463
|
+
lines.push("=== JSVMP Dispatcher Detection Result ===");
|
|
1464
|
+
lines.push(`File: ${filePath} (${startLine}-${endLine})`);
|
|
1465
|
+
lines.push("");
|
|
1466
|
+
lines.push(`Summary: ${result.summary}`);
|
|
1467
|
+
lines.push("");
|
|
1468
|
+
if (result.regions.length > 0) {
|
|
1469
|
+
lines.push("Detected Regions:");
|
|
1470
|
+
for (const region of result.regions) {
|
|
1471
|
+
lines.push(`[${region.confidence}] Lines ${region.start}-${region.end}: ${region.type}`);
|
|
1472
|
+
lines.push(` ${region.description}`);
|
|
1473
|
+
lines.push("");
|
|
1474
|
+
}
|
|
1475
|
+
} else {
|
|
1476
|
+
lines.push("No JSVMP dispatcher patterns detected.");
|
|
1477
|
+
}
|
|
1478
|
+
return lines.join("\n");
|
|
1479
|
+
}
|
|
1480
|
+
var AiFindJsvmpDispatcherInputSchema = z5.object({
|
|
1481
|
+
file_path: z5.string().describe("Path to the JavaScript file to analyze"),
|
|
1482
|
+
start_line: z5.number().int().positive().describe("Start line number (1-based)"),
|
|
1483
|
+
end_line: z5.number().int().positive().describe("End line number (1-based)"),
|
|
1484
|
+
char_limit: z5.number().int().positive().optional().describe("Character limit for string truncation (default: 300)")
|
|
1485
|
+
});
|
|
1486
|
+
var aiFindJsvmpDispatcher = defineTool({
|
|
1487
|
+
name: "ai_find_jsvmp_dispatcher",
|
|
1488
|
+
description: "AI-powered tool to find JSVMP (JavaScript Virtual Machine Protection) dispatcher patterns in code using LLM analysis. Identifies dispatchers (switch/if-else), virtual stacks, instruction arrays, and other obfuscation patterns. Requires OPENAI_API_KEY environment variable.",
|
|
1489
|
+
schema: AiFindJsvmpDispatcherInputSchema.shape,
|
|
1490
|
+
handler: async (params) => {
|
|
1491
|
+
const { file_path, start_line, end_line, char_limit } = params;
|
|
1492
|
+
const config = getLLMConfig();
|
|
1493
|
+
if (!config) {
|
|
1494
|
+
return "\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E LLM\u3002\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF OPENAI_API_KEY \u4EE5\u542F\u7528 JSVMP dispatcher \u68C0\u6D4B\u529F\u80FD\u3002";
|
|
1495
|
+
}
|
|
1496
|
+
if (!existsSync(file_path)) {
|
|
1497
|
+
return `\u9519\u8BEF\uFF1A\u6587\u4EF6\u4E0D\u5B58\u5728: ${file_path}`;
|
|
1498
|
+
}
|
|
1499
|
+
try {
|
|
1500
|
+
const formattedCode = await formatCodeForAnalysis(
|
|
1501
|
+
file_path,
|
|
1502
|
+
start_line,
|
|
1503
|
+
end_line,
|
|
1504
|
+
char_limit
|
|
1505
|
+
);
|
|
1506
|
+
const client = createLLMClient(config);
|
|
1507
|
+
const llmResponse = await client.analyzeJSVMP(formattedCode.content);
|
|
1508
|
+
const result = parseDetectionResult(llmResponse);
|
|
1509
|
+
return formatDetectionResult(result, file_path, start_line, end_line);
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
if (error instanceof Error) {
|
|
1512
|
+
return `\u9519\u8BEF\uFF1A${error.message}`;
|
|
1513
|
+
}
|
|
1514
|
+
return `\u9519\u8BEF\uFF1A${String(error)}`;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1027
1519
|
// src/tools/index.ts
|
|
1028
1520
|
var tools = [
|
|
1029
1521
|
readCodeSmart,
|
|
1030
1522
|
applyCustomTransform2,
|
|
1031
1523
|
searchCodeSmart,
|
|
1032
|
-
findUsageSmart
|
|
1524
|
+
findUsageSmart,
|
|
1525
|
+
aiFindJsvmpDispatcher
|
|
1033
1526
|
];
|
|
1034
1527
|
|
|
1035
1528
|
// src/server.ts
|
|
@@ -1038,7 +1531,7 @@ var server = new McpServer({
|
|
|
1038
1531
|
version: "1.0.0"
|
|
1039
1532
|
});
|
|
1040
1533
|
function registerTool(tool) {
|
|
1041
|
-
const zodSchema =
|
|
1534
|
+
const zodSchema = z6.object(tool.schema);
|
|
1042
1535
|
server.registerTool(
|
|
1043
1536
|
tool.name,
|
|
1044
1537
|
{
|