@reverse-craft/smart-fs 1.0.7 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -100
- package/README.zh-CN.md +181 -0
- 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 +1270 -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 +537 -41
- 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
|
}
|
|
@@ -816,17 +1015,17 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
816
1015
|
const traverse2 = await getTraverse();
|
|
817
1016
|
try {
|
|
818
1017
|
traverse2(ast, {
|
|
819
|
-
Identifier(
|
|
820
|
-
if (
|
|
1018
|
+
Identifier(path4) {
|
|
1019
|
+
if (path4.node.name !== identifier) {
|
|
821
1020
|
return;
|
|
822
1021
|
}
|
|
823
1022
|
if (isTargeted) {
|
|
824
|
-
const nodeLoc =
|
|
1023
|
+
const nodeLoc = path4.node.loc;
|
|
825
1024
|
if (!nodeLoc || nodeLoc.start.line !== targetLine) {
|
|
826
1025
|
return;
|
|
827
1026
|
}
|
|
828
1027
|
}
|
|
829
|
-
const binding =
|
|
1028
|
+
const binding = path4.scope.getBinding(identifier);
|
|
830
1029
|
if (!binding) {
|
|
831
1030
|
return;
|
|
832
1031
|
}
|
|
@@ -865,7 +1064,7 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
865
1064
|
const limitedReferences = allReferences.slice(0, maxReferences);
|
|
866
1065
|
let hitLocation;
|
|
867
1066
|
if (isTargeted) {
|
|
868
|
-
const nodeLoc =
|
|
1067
|
+
const nodeLoc = path4.node.loc;
|
|
869
1068
|
hitLocation = createLocationInfo(
|
|
870
1069
|
nodeLoc.start.line,
|
|
871
1070
|
nodeLoc.start.column,
|
|
@@ -882,7 +1081,7 @@ async function analyzeBindings(code, rawMap, identifier, options) {
|
|
|
882
1081
|
hitLocation
|
|
883
1082
|
});
|
|
884
1083
|
if (isTargeted) {
|
|
885
|
-
|
|
1084
|
+
path4.stop();
|
|
886
1085
|
}
|
|
887
1086
|
}
|
|
888
1087
|
});
|
|
@@ -1021,12 +1220,309 @@ var findUsageSmart = defineTool({
|
|
|
1021
1220
|
}
|
|
1022
1221
|
});
|
|
1023
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
|
+
|
|
1024
1519
|
// src/tools/index.ts
|
|
1025
1520
|
var tools = [
|
|
1026
1521
|
readCodeSmart,
|
|
1027
1522
|
applyCustomTransform2,
|
|
1028
1523
|
searchCodeSmart,
|
|
1029
|
-
findUsageSmart
|
|
1524
|
+
findUsageSmart,
|
|
1525
|
+
aiFindJsvmpDispatcher
|
|
1030
1526
|
];
|
|
1031
1527
|
|
|
1032
1528
|
// src/server.ts
|
|
@@ -1035,7 +1531,7 @@ var server = new McpServer({
|
|
|
1035
1531
|
version: "1.0.0"
|
|
1036
1532
|
});
|
|
1037
1533
|
function registerTool(tool) {
|
|
1038
|
-
const zodSchema =
|
|
1534
|
+
const zodSchema = z6.object(tool.schema);
|
|
1039
1535
|
server.registerTool(
|
|
1040
1536
|
tool.name,
|
|
1041
1537
|
{
|