@reverse-craft/ai-tools 1.0.0 → 1.0.2

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.
Files changed (34) hide show
  1. package/README.md +204 -77
  2. package/dist/__tests__/batchProcessing.property.test.d.ts +10 -0
  3. package/dist/__tests__/batchProcessing.property.test.d.ts.map +1 -0
  4. package/dist/__tests__/errorHandling.property.test.d.ts +11 -0
  5. package/dist/__tests__/errorHandling.property.test.d.ts.map +1 -0
  6. package/dist/__tests__/llmConfig.property.test.d.ts +48 -0
  7. package/dist/__tests__/llmConfig.property.test.d.ts.map +1 -0
  8. package/dist/__tests__/mergeResults.property.test.d.ts +12 -0
  9. package/dist/__tests__/mergeResults.property.test.d.ts.map +1 -0
  10. package/dist/__tests__/tokenizer.property.test.d.ts +20 -0
  11. package/dist/__tests__/tokenizer.property.test.d.ts.map +1 -0
  12. package/dist/index.d.ts +8 -3
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/jsvmpDetector.d.ts +70 -5
  15. package/dist/jsvmpDetector.d.ts.map +1 -1
  16. package/dist/llmConfig.d.ts +36 -1
  17. package/dist/llmConfig.d.ts.map +1 -1
  18. package/dist/server.d.ts +2 -0
  19. package/dist/server.d.ts.map +1 -0
  20. package/dist/server.js +614 -0
  21. package/dist/server.js.map +7 -0
  22. package/dist/tokenizer.d.ts +23 -0
  23. package/dist/tokenizer.d.ts.map +1 -0
  24. package/dist/tools/ToolDefinition.d.ts +24 -0
  25. package/dist/tools/ToolDefinition.d.ts.map +1 -0
  26. package/dist/tools/findJsvmpDispatcher.d.ts +41 -0
  27. package/dist/tools/findJsvmpDispatcher.d.ts.map +1 -0
  28. package/dist/tools/findJsvmpDispatcherTool.d.ts +29 -0
  29. package/dist/tools/findJsvmpDispatcherTool.d.ts.map +1 -0
  30. package/dist/tools/findJsvmpDispatcherTool.test.d.ts +7 -0
  31. package/dist/tools/findJsvmpDispatcherTool.test.d.ts.map +1 -0
  32. package/dist/tools/index.d.ts +20 -0
  33. package/dist/tools/index.d.ts.map +1 -0
  34. package/package.json +19 -11
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/server.ts", "../src/tools/findJsvmpDispatcherTool.ts", "../src/tools/ToolDefinition.ts", "../src/jsvmpDetector.ts", "../src/llmConfig.ts", "../src/tokenizer.ts", "../src/tools/index.ts"],
4
+ "sourcesContent": ["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { tools } from './tools/index.js';\n\n// Create MCP Server instance\nconst server = new McpServer({\n name: 'ai-tools-mcp',\n version: '1.0.0',\n});\n\n/**\n * Register a tool with the MCP server.\n */\nfunction registerTool(tool: {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (params: Record<string, unknown>) => Promise<string>;\n}): void {\n const zodSchema = z.object(tool.schema);\n\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.schema,\n },\n async (params, _extra) => {\n try {\n const validatedParams = zodSchema.parse(params);\n const result = await tool.handler(validatedParams as Record<string, unknown>);\n\n return {\n content: [{ type: 'text' as const, text: result }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n }\n }\n );\n}\n\n// Register all tools\nfor (const tool of tools) {\n registerTool(tool as unknown as {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (params: Record<string, unknown>) => Promise<string>;\n });\n}\n\n// Main entry point\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('AI Tools MCP Server running on stdio');\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n", "import { z } from 'zod';\nimport { defineTool } from './ToolDefinition.js';\nimport { findJsvmpDispatcher } from '../jsvmpDetector.js';\n\n/**\n * Input schema for find_jsvmp_dispatcher tool\n */\nexport const FindJsvmpDispatcherInputSchema = {\n filePath: z.string().describe('Path to the JavaScript file to analyze'),\n charLimit: z.number().int().positive().optional().describe('Character limit for string truncation (default: 300)'),\n maxTokensPerBatch: z.number().int().positive().optional().describe('Maximum tokens per batch for LLM analysis (default: 200000)'),\n};\n\n/**\n * MCP Tool: find_jsvmp_dispatcher\n * \n * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.\n * \n * JSVMP is a code protection technique that converts JavaScript to bytecode\n * executed by a virtual machine. This tool identifies:\n * - If-Else Dispatchers\n * - Switch Dispatchers \n * - Instruction Arrays\n * - Stack Operations\n * \n * Requires OPENAI_API_KEY environment variable to be set.\n */\nexport const findJsvmpDispatcherTool = defineTool({\n name: 'find_jsvmp_dispatcher',\n description: `Detect JSVMP (JavaScript Virtual Machine Protection) patterns in code using LLM analysis.\n\nJSVMP is a code protection technique that converts JavaScript to bytecode executed by a virtual machine. This tool identifies:\n- If-Else Dispatchers: Nested if-else chains for instruction dispatch\n- Switch Dispatchers: Large switch statements (>20 cases) for opcode handling\n- Instruction Arrays: Arrays storing bytecode instructions\n- Stack Operations: Virtual stack push/pop patterns\n\nAutomatically splits large files into batches based on token limits and merges results.\n\nReturns detection results with confidence levels (ultra_high, high, medium, low) and detailed descriptions.\n\nRequires OPENAI_API_KEY environment variable. Optional: OPENAI_BASE_URL, OPENAI_MODEL.`,\n schema: FindJsvmpDispatcherInputSchema,\n handler: async (params): Promise<string> => {\n const { filePath, charLimit, maxTokensPerBatch } = params;\n\n const result = await findJsvmpDispatcher(filePath, {\n charLimit: charLimit ?? 300,\n maxTokensPerBatch: maxTokensPerBatch ?? 200000,\n });\n\n if (!result.success) {\n throw new Error(result.error ?? 'Detection failed');\n }\n\n return result.formattedOutput ?? 'No output generated';\n },\n});\n", "import { z } from 'zod';\n\n/**\n * Tool definition interface for MCP tools.\n * Each tool has a name, description, schema (Zod raw shape), and async handler.\n */\nexport interface ToolDefinition<TSchema extends z.ZodRawShape = z.ZodRawShape> {\n /** Unique tool name (e.g., 'find_jsvmp_dispatcher') */\n name: string;\n /** Human-readable description of what the tool does */\n description: string;\n /** Zod schema object defining input parameters */\n schema: TSchema;\n /** Async handler function that processes the tool request */\n handler: (params: z.infer<z.ZodObject<TSchema>>) => Promise<string>;\n}\n\n/**\n * Helper function to create a type-safe tool definition.\n * Validates the tool definition structure at compile time.\n * \n * @param definition - The tool definition object\n * @returns The same definition with proper typing\n */\nexport function defineTool<TSchema extends z.ZodRawShape>(\n definition: ToolDefinition<TSchema>\n): ToolDefinition<TSchema> {\n return definition;\n}\n", "/**\n * JSVMP Detector Module\n * AI-powered detection of JSVMP (JavaScript Virtual Machine Protection) patterns\n */\n\nimport { SourceMapConsumer } from 'source-map-js';\nimport { ensureBeautified, truncateCodeHighPerf } from '@reverse-craft/smart-fs';\nimport { existsSync } from 'fs';\nimport { getLLMConfig, createLLMClient, LLMClient } from './llmConfig.js';\nimport { countTokens, splitByTokenLimit } from './tokenizer.js';\n\n/**\n * Formatted code result interface\n */\nexport interface FormattedCode {\n content: string; // \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\u5B57\u7B26\u4E32\n totalLines: number; // \u603B\u884C\u6570\n startLine: number; // \u5B9E\u9645\u8D77\u59CB\u884C\n endLine: number; // \u5B9E\u9645\u7ED3\u675F\u884C\n}\n\n/**\n * Detection type for JSVMP patterns\n */\nexport type DetectionType = \n | \"If-Else Dispatcher\" \n | \"Switch Dispatcher\" \n | \"Instruction Array\" \n | \"Stack Operation\";\n\n/**\n * Confidence level for detection results\n */\nexport type ConfidenceLevel = \"ultra_high\" | \"high\" | \"medium\" | \"low\";\n\n/**\n * A detected region in the code\n */\nexport interface DetectionRegion {\n start: number; // \u8D77\u59CB\u884C\u53F7\n end: number; // \u7ED3\u675F\u884C\u53F7\n type: DetectionType; // \u68C0\u6D4B\u7C7B\u578B\n confidence: ConfidenceLevel; // \u7F6E\u4FE1\u5EA6\n description: string; // \u63CF\u8FF0\uFF08\u4E2D\u6587\uFF09\n}\n\n/**\n * Complete detection result from LLM analysis\n */\nexport interface DetectionResult {\n summary: string; // \u5206\u6790\u6458\u8981\uFF08\u4E2D\u6587\uFF09\n regions: DetectionRegion[];\n}\n\n/**\n * Options for JSVMP detection\n */\nexport interface JsvmpDetectionOptions {\n charLimit?: number; // Default: 300\n maxTokensPerBatch?: number; // Default: 8000\n}\n\n/**\n * Result from findJsvmpDispatcher function\n */\nexport interface JsvmpDetectionResult {\n success: boolean;\n filePath: string;\n totalLines: number;\n batchCount: number;\n result?: DetectionResult;\n formattedOutput?: string;\n error?: string;\n partialErrors?: string[]; // Errors from failed batches\n}\n\n/**\n * Batch information for processing\n */\nexport interface BatchInfo {\n startLine: number; // \u6279\u6B21\u8D77\u59CB\u884C\u53F7 (1-based)\n endLine: number; // \u6279\u6B21\u7ED3\u675F\u884C\u53F7 (1-based)\n content: string; // \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\u5185\u5BB9\n tokenCount: number; // \u8BE5\u6279\u6B21\u7684 token \u6570\u91CF\n}\n\n/**\n * Result from formatEntireFile function\n */\nexport interface FormattedFileResult {\n lines: string[]; // \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\u884C\u6570\u7EC4\n totalLines: number; // \u603B\u884C\u6570\n}\n\n/**\n * Format source position as \"L{line}:{column}\" or empty placeholder\n */\nfunction formatSourcePosition(line: number | null, column: number | null): string {\n if (line !== null && column !== null) {\n return `L${line}:${column}`;\n }\n return '';\n}\n\n/**\n * Format a single code line with line number, source coordinates, and content\n * Format: \"LineNo SourceLoc Code\"\n */\nfunction formatCodeLine(lineNumber: number, sourcePos: string, code: string): string {\n const lineNumStr = String(lineNumber).padStart(5, ' ');\n const srcPosPadded = sourcePos ? sourcePos.padEnd(10, ' ') : ' ';\n return `${lineNumStr} ${srcPosPadded} ${code}`;\n}\n\n/**\n * \u683C\u5F0F\u5316\u4EE3\u7801\u4E3A LLM \u5206\u6790\u683C\u5F0F\n * \u683C\u5F0F: \"LineNo SourceLoc Code\"\n * \n * \u5904\u7406\u6D41\u7A0B\uFF1A\n * 1. \u8C03\u7528 ensureBeautified \u7F8E\u5316\u4EE3\u7801\n * 2. \u8C03\u7528 truncateCodeHighPerf \u622A\u65AD\u957F\u5B57\u7B26\u4E32\n * 3. \u4F7F\u7528 SourceMapConsumer \u83B7\u53D6\u539F\u59CB\u5750\u6807\n * 4. \u683C\u5F0F\u5316\u4E3A \"LineNo SourceLoc Code\" \u683C\u5F0F\n * \n * @param filePath - Path to the JavaScript file\n * @param startLine - Start line number (1-based)\n * @param endLine - End line number (1-based)\n * @param charLimit - Character limit for string truncation (default 300)\n * @returns FormattedCode object with formatted content and metadata\n */\nexport async function formatCodeForAnalysis(\n filePath: string,\n startLine: number,\n endLine: number,\n charLimit: number = 300\n): Promise<FormattedCode> {\n // Step 1: Beautify the file and get source map\n const beautifyResult = await ensureBeautified(filePath);\n const { code, rawMap } = beautifyResult;\n\n // Step 2: Truncate long strings\n const truncatedCode = truncateCodeHighPerf(code, charLimit);\n\n // Split into lines\n const lines = truncatedCode.split('\\n');\n const totalLines = lines.length;\n\n // Step 3: Adjust line range boundaries\n const effectiveStartLine = Math.max(1, Math.min(totalLines, startLine));\n const effectiveEndLine = Math.max(effectiveStartLine, Math.min(totalLines, endLine));\n\n // Step 4: Format each line with \"LineNo SourceLoc Code\" format\n const formattedLines: string[] = [];\n\n // Create source map consumer if available\n let consumer: SourceMapConsumer | null = null;\n if (rawMap && rawMap.sources && rawMap.names && rawMap.mappings) {\n consumer = new SourceMapConsumer({\n version: String(rawMap.version),\n sources: rawMap.sources,\n names: rawMap.names,\n mappings: rawMap.mappings,\n file: rawMap.file,\n sourceRoot: rawMap.sourceRoot,\n });\n }\n\n for (let lineNum = effectiveStartLine; lineNum <= effectiveEndLine; lineNum++) {\n const lineIndex = lineNum - 1;\n const lineContent = lines[lineIndex] ?? '';\n\n // Get original position from source map if available\n let sourcePos = '';\n if (consumer) {\n const originalPos = consumer.originalPositionFor({\n line: lineNum,\n column: 0,\n });\n sourcePos = formatSourcePosition(originalPos.line, originalPos.column);\n }\n \n formattedLines.push(formatCodeLine(lineNum, sourcePos, lineContent));\n }\n\n return {\n content: formattedLines.join('\\n'),\n totalLines,\n startLine: effectiveStartLine,\n endLine: effectiveEndLine,\n };\n}\n\n/**\n * \u683C\u5F0F\u5316\u6574\u4E2A\u6587\u4EF6\u4E3A LLM \u5206\u6790\u683C\u5F0F\n * \u683C\u5F0F: \"LineNo SourceLoc Code\"\n * \n * \u5904\u7406\u6D41\u7A0B\uFF1A\n * 1. \u8C03\u7528 ensureBeautified \u7F8E\u5316\u4EE3\u7801\n * 2. \u8C03\u7528 truncateCodeHighPerf \u622A\u65AD\u957F\u5B57\u7B26\u4E32\n * 3. \u4F7F\u7528 SourceMapConsumer \u83B7\u53D6\u539F\u59CB\u5750\u6807\n * 4. \u8FD4\u56DE\u683C\u5F0F\u5316\u540E\u7684\u884C\u6570\u7EC4\uFF08\u4FDD\u7559\u539F\u59CB\u884C\u53F7\uFF09\n * \n * @param filePath - Path to the JavaScript file\n * @param charLimit - Character limit for string truncation (default 300)\n * @returns FormattedFileResult with formatted lines array and metadata\n */\nexport async function formatEntireFile(\n filePath: string,\n charLimit: number = 300\n): Promise<FormattedFileResult> {\n // Step 1: Beautify the file and get source map\n const beautifyResult = await ensureBeautified(filePath);\n const { code, rawMap } = beautifyResult;\n\n // Step 2: Truncate long strings\n const truncatedCode = truncateCodeHighPerf(code, charLimit);\n\n // Split into lines\n const codeLines = truncatedCode.split('\\n');\n const totalLines = codeLines.length;\n\n // Step 3: Format each line with \"LineNo SourceLoc Code\" format\n const formattedLines: string[] = [];\n\n // Create source map consumer if available\n let consumer: SourceMapConsumer | null = null;\n if (rawMap && rawMap.sources && rawMap.names && rawMap.mappings) {\n consumer = new SourceMapConsumer({\n version: String(rawMap.version),\n sources: rawMap.sources,\n names: rawMap.names,\n mappings: rawMap.mappings,\n file: rawMap.file,\n sourceRoot: rawMap.sourceRoot,\n });\n }\n\n for (let lineNum = 1; lineNum <= totalLines; lineNum++) {\n const lineIndex = lineNum - 1;\n const lineContent = codeLines[lineIndex] ?? '';\n\n // Get original position from source map if available\n let sourcePos = '';\n if (consumer) {\n const originalPos = consumer.originalPositionFor({\n line: lineNum,\n column: 0,\n });\n sourcePos = formatSourcePosition(originalPos.line, originalPos.column);\n }\n \n formattedLines.push(formatCodeLine(lineNum, sourcePos, lineContent));\n }\n\n return {\n lines: formattedLines,\n totalLines,\n };\n}\n\n/**\n * Extract line number from a formatted code line\n * Format: \"LineNo SourceLoc Code\"\n * \n * @param formattedLine - A formatted code line\n * @returns The line number (1-based)\n */\nfunction extractLineNumber(formattedLine: string): number {\n const lineNumStr = formattedLine.substring(0, 5).trim();\n return parseInt(lineNumStr, 10);\n}\n\n/**\n * \u521B\u5EFA\u6279\u6B21\u7528\u4E8E\u5206\u6279\u5904\u7406\n * \u4F7F\u7528 tokenizer \u5206\u5272\u4EE3\u7801\uFF0C\u8BB0\u5F55\u6BCF\u4E2A\u6279\u6B21\u7684 startLine/endLine\n * \n * @param formattedLines - \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\u884C\u6570\u7EC4\n * @param maxTokensPerBatch - \u6BCF\u6279\u6B21\u6700\u5927 token \u6570\u91CF\n * @returns BatchInfo \u6570\u7EC4\n */\nexport function createBatches(\n formattedLines: string[],\n maxTokensPerBatch: number\n): BatchInfo[] {\n if (formattedLines.length === 0) {\n return [];\n }\n\n // Split lines into batches based on token limit\n const lineBatches = splitByTokenLimit(formattedLines, maxTokensPerBatch);\n \n const batches: BatchInfo[] = [];\n \n for (const batchLines of lineBatches) {\n if (batchLines.length === 0) continue;\n \n // Extract start and end line numbers from formatted lines\n const startLine = extractLineNumber(batchLines[0]);\n const endLine = extractLineNumber(batchLines[batchLines.length - 1]);\n \n // Join lines to create batch content\n const content = batchLines.join('\\n');\n \n // Calculate token count for this batch\n const tokenCount = countTokens(content);\n \n batches.push({\n startLine,\n endLine,\n content,\n tokenCount,\n });\n }\n \n return batches;\n}\n\n/**\n * Valid detection types for validation\n */\nconst VALID_DETECTION_TYPES: DetectionType[] = [\n \"If-Else Dispatcher\",\n \"Switch Dispatcher\",\n \"Instruction Array\",\n \"Stack Operation\"\n];\n\n/**\n * Valid confidence levels for validation\n */\nconst VALID_CONFIDENCE_LEVELS: ConfidenceLevel[] = [\n \"ultra_high\",\n \"high\",\n \"medium\",\n \"low\"\n];\n\n/**\n * Check if a value is a valid DetectionType\n */\nfunction isValidDetectionType(value: unknown): value is DetectionType {\n return VALID_DETECTION_TYPES.includes(value as DetectionType);\n}\n\n/**\n * Check if a value is a valid ConfidenceLevel\n */\nfunction isValidConfidenceLevel(value: unknown): value is ConfidenceLevel {\n return VALID_CONFIDENCE_LEVELS.includes(value as ConfidenceLevel);\n}\n\n/**\n * Parse and validate LLM detection result from JSON string\n * \n * Validates:\n * - JSON is parseable\n * - Required fields exist: summary, regions\n * - Each region has required fields: start, end, type, confidence, description\n * - Enum values are valid\n * \n * @param jsonString - JSON string from LLM response\n * @returns Parsed and validated DetectionResult\n * @throws Error if JSON is invalid or structure doesn't match expected format\n */\nexport function parseDetectionResult(jsonString: string): DetectionResult {\n // Parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonString);\n } catch (error) {\n throw new Error(`\u65E0\u6CD5\u89E3\u6790 LLM \u54CD\u5E94: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Validate required top-level fields\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u671F\u671B\u5BF9\u8C61\u7C7B\u578B');\n }\n\n const obj = parsed as Record<string, unknown>;\n\n if (typeof obj.summary !== 'string') {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: summary');\n }\n\n if (!Array.isArray(obj.regions)) {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: regions');\n }\n\n // Validate each region\n const validatedRegions: DetectionRegion[] = [];\n\n for (let i = 0; i < obj.regions.length; i++) {\n const region = obj.regions[i] as Record<string, unknown>;\n\n // Check region is an object\n if (typeof region !== 'object' || region === null) {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u4E0D\u662F\u5BF9\u8C61`);\n }\n\n // Validate required fields exist and have correct types\n if (typeof region.start !== 'number') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: start`);\n }\n\n if (typeof region.end !== 'number') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: end`);\n }\n\n if (typeof region.type !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: type`);\n }\n\n if (typeof region.confidence !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: confidence`);\n }\n\n if (typeof region.description !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: description`);\n }\n\n // Validate enum values\n if (!isValidDetectionType(region.type)) {\n throw new Error(\n `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].type \u503C\u65E0\u6548: \"${region.type}\". ` +\n `\u6709\u6548\u503C: ${VALID_DETECTION_TYPES.join(', ')}`\n );\n }\n\n if (!isValidConfidenceLevel(region.confidence)) {\n throw new Error(\n `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].confidence \u503C\u65E0\u6548: \"${region.confidence}\". ` +\n `\u6709\u6548\u503C: ${VALID_CONFIDENCE_LEVELS.join(', ')}`\n );\n }\n\n validatedRegions.push({\n start: region.start,\n end: region.end,\n type: region.type,\n confidence: region.confidence,\n description: region.description,\n });\n }\n\n return {\n summary: obj.summary,\n regions: validatedRegions,\n };\n}\n\n/**\n * Format detection result for display\n */\nfunction formatDetectionResultOutput(\n result: DetectionResult,\n filePath: string,\n totalLines: number,\n batchCount: number\n): string {\n const lines: string[] = [];\n \n lines.push('=== JSVMP Dispatcher Detection Result ===');\n lines.push(`File: ${filePath} (${totalLines} lines, ${batchCount} batch${batchCount > 1 ? 'es' : ''})`);\n lines.push('');\n lines.push(`Summary: ${result.summary}`);\n lines.push('');\n \n if (result.regions.length > 0) {\n lines.push('Detected Regions:');\n for (const region of result.regions) {\n lines.push(`[${region.confidence}] Lines ${region.start}-${region.end}: ${region.type}`);\n lines.push(` ${region.description}`);\n lines.push('');\n }\n } else {\n lines.push('No JSVMP dispatcher patterns detected.');\n }\n \n return lines.join('\\n');\n}\n\n/**\n * Merge detection results from multiple batches\n * - Combines all regions from all batches\n * - Combines summaries from all batches\n * - Sorts regions by start line\n * - Deduplicates overlapping regions (keeps higher confidence)\n * \n * @param results - Array of DetectionResult from each batch\n * @returns Merged DetectionResult\n */\nexport function mergeDetectionResults(results: DetectionResult[]): DetectionResult {\n if (results.length === 0) {\n return { summary: '', regions: [] };\n }\n \n if (results.length === 1) {\n // Still need to sort and deduplicate regions for single result\n const sortedRegions = [...results[0].regions].sort((a, b) => a.start - b.start);\n return { summary: results[0].summary, regions: sortedRegions };\n }\n \n // Combine summaries\n const summaries = results.map((r, i) => `[Batch ${i + 1}] ${r.summary}`);\n const combinedSummary = summaries.join('\\n');\n \n // Collect all regions\n const allRegions: DetectionRegion[] = [];\n for (const result of results) {\n allRegions.push(...result.regions);\n }\n \n // Sort by start line\n allRegions.sort((a, b) => a.start - b.start);\n \n // Deduplicate overlapping regions (keep higher confidence)\n const confidenceOrder: Record<ConfidenceLevel, number> = {\n 'ultra_high': 4,\n 'high': 3,\n 'medium': 2,\n 'low': 1,\n };\n \n const deduplicatedRegions: DetectionRegion[] = [];\n for (const region of allRegions) {\n // Check if this region overlaps with any existing region\n let overlappingIndex = -1;\n for (let i = 0; i < deduplicatedRegions.length; i++) {\n const existing = deduplicatedRegions[i];\n // Check for overlap: regions overlap if one starts before the other ends\n if (region.start <= existing.end && region.end >= existing.start) {\n overlappingIndex = i;\n break;\n }\n }\n \n if (overlappingIndex === -1) {\n // No overlap, add the region\n deduplicatedRegions.push(region);\n } else {\n // Overlap found, keep the one with higher confidence\n const existing = deduplicatedRegions[overlappingIndex];\n if (confidenceOrder[region.confidence] > confidenceOrder[existing.confidence]) {\n deduplicatedRegions[overlappingIndex] = region;\n }\n // If equal confidence, keep the first one (existing)\n }\n }\n \n return {\n summary: combinedSummary,\n regions: deduplicatedRegions,\n };\n}\n\n/**\n * Process a single batch through LLM\n * \n * @param client - LLM client\n * @param batch - Batch information\n * @returns DetectionResult from LLM analysis\n */\nasync function processBatch(\n client: LLMClient,\n batch: BatchInfo\n): Promise<DetectionResult> {\n const llmResponse = await client.analyzeJSVMP(batch.content);\n return parseDetectionResult(llmResponse);\n}\n\n/**\n * Process batches with error handling\n * - Continues processing if some batches fail\n * - Collects partial results and error information\n * \n * @param client - LLM client\n * @param batches - Array of BatchInfo\n * @returns Object with successful results and error messages\n */\nexport async function processBatchesWithErrorHandling(\n client: LLMClient,\n batches: BatchInfo[]\n): Promise<{ results: DetectionResult[]; errors: string[] }> {\n const results: DetectionResult[] = [];\n const errors: string[] = [];\n \n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n try {\n const result = await processBatch(client, batch);\n results.push(result);\n } catch (error) {\n const errorMsg = `Batch ${i + 1} (lines ${batch.startLine}-${batch.endLine}) failed: ${\n error instanceof Error ? error.message : String(error)\n }`;\n errors.push(errorMsg);\n }\n }\n \n return { results, errors };\n}\n\n/**\n * Find JSVMP dispatcher patterns in JavaScript code using LLM analysis\n * \n * @param filePath - Path to the JavaScript file to analyze\n * @param options - Optional configuration\n * @returns JsvmpDetectionResult with detection results or error\n */\nexport async function findJsvmpDispatcher(\n filePath: string,\n options?: JsvmpDetectionOptions\n): Promise<JsvmpDetectionResult> {\n const charLimit = options?.charLimit ?? 300;\n const maxTokensPerBatch = options?.maxTokensPerBatch ?? 8000;\n \n // Check LLM configuration\n const config = getLLMConfig();\n if (!config) {\n return {\n success: false,\n filePath,\n totalLines: 0,\n batchCount: 0,\n error: '\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'\n };\n }\n \n // Check file exists\n if (!existsSync(filePath)) {\n return {\n success: false,\n filePath,\n totalLines: 0,\n batchCount: 0,\n error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`\n };\n }\n \n try {\n // Format entire file for analysis\n const formattedCode = await formatEntireFile(filePath, charLimit);\n const totalLines = formattedCode.totalLines;\n \n // Create batches based on token limit\n const batches = createBatches(formattedCode.lines, maxTokensPerBatch);\n const batchCount = batches.length;\n \n // Create LLM client\n const client = createLLMClient(config);\n \n // Process batches with error handling\n const { results, errors } = await processBatchesWithErrorHandling(client, batches);\n \n // If all batches failed, return error\n if (results.length === 0) {\n return {\n success: false,\n filePath,\n totalLines,\n batchCount,\n error: `\u6240\u6709\u6279\u6B21\u5904\u7406\u5931\u8D25: ${errors.join('; ')}`,\n partialErrors: errors\n };\n }\n \n // Merge results from all successful batches\n const mergedResult = mergeDetectionResults(results);\n \n // Format output\n const formattedOutput = formatDetectionResultOutput(mergedResult, filePath, totalLines, batchCount);\n \n return {\n success: true,\n filePath,\n totalLines,\n batchCount,\n result: mergedResult,\n formattedOutput,\n partialErrors: errors.length > 0 ? errors : undefined\n };\n \n } catch (error) {\n return {\n success: false,\n filePath,\n totalLines: 0,\n batchCount: 0,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n", "/**\n * LLM Configuration Module\n * Handles reading and validating LLM configuration from environment variables\n */\n\nimport { generateText } from 'ai';\nimport { createOpenAI } from '@ai-sdk/openai';\nimport { createAnthropic } from '@ai-sdk/anthropic';\nimport { createGoogleGenerativeAI } from '@ai-sdk/google';\nimport type { LanguageModel } from 'ai';\n\n/**\n * Supported LLM providers\n */\nexport type LLMProvider = 'openai' | 'anthropic' | 'google';\n\n/**\n * Provider-specific default configurations\n */\nexport const PROVIDER_DEFAULTS: Record<LLMProvider, { model: string }> = {\n openai: { model: 'gpt-4o-mini' },\n anthropic: { model: 'claude-sonnet-4-20250514' },\n google: { model: 'gemini-2.0-flash' },\n};\n\n/**\n * Environment variable names for each provider\n */\nexport const PROVIDER_ENV_KEYS: Record<LLMProvider, { \n apiKey: string; \n model: string; \n baseUrl: string;\n}> = {\n openai: { \n apiKey: 'OPENAI_API_KEY', \n model: 'OPENAI_MODEL', \n baseUrl: 'OPENAI_BASE_URL' \n },\n anthropic: { \n apiKey: 'ANTHROPIC_API_KEY', \n model: 'ANTHROPIC_MODEL',\n baseUrl: 'ANTHROPIC_BASE_URL'\n },\n google: { \n apiKey: 'GOOGLE_API_KEY', \n model: 'GOOGLE_MODEL',\n baseUrl: 'GOOGLE_BASE_URL'\n },\n};\n\n/**\n * Extended LLM configuration with provider information\n */\nexport interface LLMConfig {\n provider: LLMProvider;\n apiKey: string;\n model: string;\n baseUrl?: string; // Custom base URL for all providers\n}\n\n/**\n * Validates provider string against valid values\n * @param value - The provider string to validate\n * @returns The validated LLMProvider or null if invalid\n */\nexport function validateProvider(value: string | undefined): LLMProvider | null {\n if (value === undefined) return null;\n if (value === 'openai' || value === 'anthropic' || value === 'google') {\n return value;\n }\n return null;\n}\n\n/**\n * \u4ECE\u73AF\u5883\u53D8\u91CF\u8BFB\u53D6 LLM \u914D\u7F6E\n * @returns LLMConfig | null (null \u8868\u793A\u672A\u914D\u7F6E)\n */\nexport function getLLMConfig(): LLMConfig | null {\n // 1. Determine provider (default to 'openai')\n const providerEnv = process.env.LLM_PROVIDER?.toLowerCase();\n const provider = validateProvider(providerEnv);\n \n if (provider === null && providerEnv !== undefined) {\n // Invalid provider specified\n console.warn(`Invalid LLM_PROVIDER: ${providerEnv}. Valid values: openai, anthropic, google`);\n return null;\n }\n \n const effectiveProvider = provider ?? 'openai';\n \n // 2. Get provider-specific environment variable names\n const envKeys = PROVIDER_ENV_KEYS[effectiveProvider];\n \n // 3. Read API key (required)\n const apiKey = process.env[envKeys.apiKey];\n if (!apiKey) {\n return null;\n }\n \n // 4. Read model: LLM_MODEL > provider-specific > default\n const model = process.env.LLM_MODEL \n || process.env[envKeys.model] \n || PROVIDER_DEFAULTS[effectiveProvider].model;\n \n // 5. Read base URL: LLM_BASE_URL > provider-specific > undefined\n const baseUrl = process.env.LLM_BASE_URL || process.env[envKeys.baseUrl];\n \n return {\n provider: effectiveProvider,\n apiKey,\n model,\n baseUrl,\n };\n}\n\n/**\n * \u68C0\u67E5 LLM \u662F\u5426\u5DF2\u914D\u7F6E\n */\nexport function isLLMConfigured(): boolean {\n return getLLMConfig() !== null;\n}\n\n/**\n * LLM Client Interface\n */\nexport interface LLMClient {\n /**\n * \u53D1\u9001 JSVMP \u68C0\u6D4B\u8BF7\u6C42\u5230 LLM\n * @param formattedCode \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\n * @returns LLM \u8FD4\u56DE\u7684\u539F\u59CB JSON \u5B57\u7B26\u4E32\n */\n analyzeJSVMP(formattedCode: string): Promise<string>;\n}\n\n/**\n * \u6784\u5EFA JSVMP \u68C0\u6D4B\u7CFB\u7EDF\u63D0\u793A\u8BCD\n */\nfunction buildJSVMPSystemPrompt(): string {\n return `You are a Senior JavaScript Reverse Engineer and De-obfuscation Expert. Your specialty is analyzing **JSVMP (JavaScript Virtual Machine Protection)**.\n\n**Context: What is JSVMP?**\nJSVMP is a protection technique where original JavaScript code is compiled into custom **bytecode** and executed by a custom **interpreter** (virtual machine) written in JavaScript.\n\nKey components of JSVMP code include:\n1. **The Virtual Stack:** A central array used to store operands and results (e.g., \\`stack[pointer++]\\` or \\`v[p--]\\`).\n2. **The Dispatcher:** A control flow structure inside a loop that decides which instruction to execute next based on the current bytecode (opcode).\n * *Common variants:* A massive \\`switch\\` statement, a deeply nested \\`if-else\\` chain (binary search style), or a function array mapping (\\`handlers[opcode]()\\`).\n3. **The Bytecode:** A large string or array of integers representing the program logic.\n\n**Task:**\nAnalyze the provided JavaScript code snippet to identify regions that match JSVMP structural patterns.\n\n**Input Data Format:**\nThe code is provided in a simplified format: \\`LineNo SourceLoc Code\\`.\n* **Example:** \\`10 L234:56 var x = stack[p++];\\`\n* **Instruction:** Focus on the **LineNo** (1st column) and **Code** (3rd column onwards). Ignore the \\`SourceLoc\\` (middle column).\n\n**Detection Rules & Confidence Levels:**\nPlease assign confidence based on the following criteria:\n\n* **Ultra High:**\n * A combination of a **Main Loop** + **Dispatcher** + **Stack Operations** appears in the same block.\n * *Example:* A \\`while(true)\\` loop containing a huge \\`if-else\\` chain where branches perform \\`stack[p++]\\` operations.\n\n* **High:**\n * Distinct **Dispatcher** structures found (e.g., a \\`switch\\` with >20 cases, or an \\`if-else\\` chain nested >10 levels deep checking integer values).\n * Large arrays containing only function definitions (Instruction Handlers).\n\n* **Medium:**\n * Isolated **Stack Operations** (e.g., \\`v2[p2] = v2[p2 - 1]\\`) without visible dispatchers nearby.\n * Suspicious \\`while\\` loops iterating over a string/array.\n\n* **Low:**\n * Generic obfuscation patterns (short variable names, comma operators) that *might* be part of a VM but lack specific structural proof.\n\n**Output Format:**\nReturn **ONLY valid JSON**. No markdown wrapper, no conversational text.\n\n**JSON Schema:**\n{\n \"summary\": \"Brief analysis of the code structure in chinese, shortly\",\n \"regions\": [\n {\n \"start\": <start_line>,\n \"end\": <end_line>,\n \"type\": \"<If-Else Dispatcher | Switch Dispatcher | Instruction Array | Stack Operation>\",\n \"confidence\": \"<ultra_high | high | medium | low>\",\n \"description\": \"<Why you flagged this. Mention specific variables like 'v2', 'p2' or structures. in chinese, shortly>\"\n }\n ]\n}`;\n}\n\n/**\n * Creates a provider-specific model instance using the AI SDK\n * @param config - The LLM configuration\n * @returns A LanguageModel instance for the configured provider\n */\nexport function createProviderModel(config: LLMConfig): LanguageModel {\n switch (config.provider) {\n case 'openai': {\n const openai = createOpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseUrl,\n });\n return openai(config.model);\n }\n case 'anthropic': {\n const anthropic = createAnthropic({\n apiKey: config.apiKey,\n baseURL: config.baseUrl,\n });\n return anthropic(config.model);\n }\n case 'google': {\n const google = createGoogleGenerativeAI({\n apiKey: config.apiKey,\n baseURL: config.baseUrl,\n });\n return google(config.model);\n }\n }\n}\n\n/**\n * \u521B\u5EFA LLM \u5BA2\u6237\u7AEF\u5B9E\u4F8B\n */\nexport function createLLMClient(config: LLMConfig): LLMClient {\n const model = createProviderModel(config);\n \n return {\n async analyzeJSVMP(formattedCode: string): Promise<string> {\n const systemPrompt = buildJSVMPSystemPrompt();\n \n try {\n const result = await generateText({\n model,\n system: systemPrompt,\n prompt: `\u8BF7\u5206\u6790\u4EE5\u4E0B\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u4FDD\u62A4\u7ED3\u6784\uFF1A\\n\\n${formattedCode}`,\n temperature: 0.1,\n });\n \n return result.text;\n } catch (error) {\n const providerName = config.provider.charAt(0).toUpperCase() + config.provider.slice(1);\n if (error instanceof Error) {\n throw new Error(`${providerName} LLM \u8BF7\u6C42\u5931\u8D25: ${error.message}`);\n }\n throw new Error(`${providerName} LLM \u8BF7\u6C42\u5931\u8D25: ${String(error)}`);\n }\n }\n };\n}\n", "/**\n * Tokenizer Module\n * Token counting and batch splitting utilities using tiktoken\n */\n\nimport { encoding_for_model, TiktokenModel } from 'tiktoken';\n\n/**\n * Default model for token counting\n */\nconst DEFAULT_MODEL: TiktokenModel = 'gpt-4o';\n\n/**\n * Calculate token count for text using tiktoken\n * \n * @param text - The text to count tokens for\n * @param model - Optional model name (default: gpt-4o)\n * @returns The number of tokens in the text\n */\nexport function countTokens(text: string, model?: string): number {\n const enc = encoding_for_model((model as TiktokenModel) ?? DEFAULT_MODEL);\n try {\n const tokens = enc.encode(text);\n return tokens.length;\n } finally {\n enc.free();\n }\n}\n\n/**\n * Split lines into batches based on token limit\n * Splits at line boundaries to preserve code structure\n * \n * @param lines - Array of code lines to split\n * @param maxTokens - Maximum tokens per batch\n * @param model - Optional model name (default: gpt-4o)\n * @returns Array of batches, where each batch is an array of lines\n */\nexport function splitByTokenLimit(\n lines: string[],\n maxTokens: number,\n model?: string\n): string[][] {\n if (lines.length === 0) {\n return [];\n }\n\n if (maxTokens <= 0) {\n throw new Error('maxTokens must be a positive number');\n }\n\n const batches: string[][] = [];\n let currentBatch: string[] = [];\n let currentTokenCount = 0;\n\n const enc = encoding_for_model((model as TiktokenModel) ?? DEFAULT_MODEL);\n \n try {\n for (const line of lines) {\n // Calculate tokens for this line (including newline)\n const lineWithNewline = line + '\\n';\n const lineTokens = enc.encode(lineWithNewline).length;\n\n // If a single line exceeds maxTokens, it goes in its own batch\n if (lineTokens > maxTokens) {\n // Flush current batch if not empty\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n currentBatch = [];\n currentTokenCount = 0;\n }\n // Add the oversized line as its own batch\n batches.push([line]);\n continue;\n }\n\n // Check if adding this line would exceed the limit\n if (currentTokenCount + lineTokens > maxTokens && currentBatch.length > 0) {\n // Flush current batch\n batches.push(currentBatch);\n currentBatch = [];\n currentTokenCount = 0;\n }\n\n // Add line to current batch\n currentBatch.push(line);\n currentTokenCount += lineTokens;\n }\n\n // Don't forget the last batch\n if (currentBatch.length > 0) {\n batches.push(currentBatch);\n }\n\n return batches;\n } finally {\n enc.free();\n }\n}\n", "/**\n * Tool aggregation module\n * Collects all tool definitions and exports them as a unified array.\n */\n\nimport { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema } from './findJsvmpDispatcherTool.js';\n\n/**\n * Array of all available MCP tool definitions.\n * To add a new tool:\n * 1. Create a new tool module in src/tools/\n * 2. Import it here\n * 3. Add it to this array\n */\nexport const tools = [\n findJsvmpDispatcherTool,\n] as const;\n\n// Re-export ToolDefinition interface and defineTool helper\nexport { ToolDefinition, defineTool } from './ToolDefinition.js';\n\n// Re-export tool and input schema\nexport { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema };\n"],
5
+ "mappings": "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",
6
+ "names": ["z", "z"]
7
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tokenizer Module
3
+ * Token counting and batch splitting utilities using tiktoken
4
+ */
5
+ /**
6
+ * Calculate token count for text using tiktoken
7
+ *
8
+ * @param text - The text to count tokens for
9
+ * @param model - Optional model name (default: gpt-4o)
10
+ * @returns The number of tokens in the text
11
+ */
12
+ export declare function countTokens(text: string, model?: string): number;
13
+ /**
14
+ * Split lines into batches based on token limit
15
+ * Splits at line boundaries to preserve code structure
16
+ *
17
+ * @param lines - Array of code lines to split
18
+ * @param maxTokens - Maximum tokens per batch
19
+ * @param model - Optional model name (default: gpt-4o)
20
+ * @returns Array of batches, where each batch is an array of lines
21
+ */
22
+ export declare function splitByTokenLimit(lines: string[], maxTokens: number, model?: string): string[][];
23
+ //# sourceMappingURL=tokenizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAQhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,EAAE,EAAE,CAwDZ"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Tool definition interface for MCP tools.
4
+ * Each tool has a name, description, schema (Zod raw shape), and async handler.
5
+ */
6
+ export interface ToolDefinition<TSchema extends z.ZodRawShape = z.ZodRawShape> {
7
+ /** Unique tool name (e.g., 'find_jsvmp_dispatcher') */
8
+ name: string;
9
+ /** Human-readable description of what the tool does */
10
+ description: string;
11
+ /** Zod schema object defining input parameters */
12
+ schema: TSchema;
13
+ /** Async handler function that processes the tool request */
14
+ handler: (params: z.infer<z.ZodObject<TSchema>>) => Promise<string>;
15
+ }
16
+ /**
17
+ * Helper function to create a type-safe tool definition.
18
+ * Validates the tool definition structure at compile time.
19
+ *
20
+ * @param definition - The tool definition object
21
+ * @returns The same definition with proper typing
22
+ */
23
+ export declare function defineTool<TSchema extends z.ZodRawShape>(definition: ToolDefinition<TSchema>): ToolDefinition<TSchema>;
24
+ //# sourceMappingURL=ToolDefinition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolDefinition.d.ts","sourceRoot":"","sources":["../../src/tools/ToolDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,OAAO,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IAC3E,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,WAAW,EACtD,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,GAClC,cAAc,CAAC,OAAO,CAAC,CAEzB"}
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Input schema for find_jsvmp_dispatcher tool
4
+ */
5
+ export declare const FindJsvmpDispatcherInputSchema: {
6
+ filePath: z.ZodString;
7
+ startLine: z.ZodNumber;
8
+ endLine: z.ZodNumber;
9
+ charLimit: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ /**
12
+ * MCP Tool: find_jsvmp_dispatcher
13
+ *
14
+ * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.
15
+ *
16
+ * JSVMP is a code protection technique that converts JavaScript to bytecode
17
+ * executed by a virtual machine. This tool identifies:
18
+ * - If-Else Dispatchers
19
+ * - Switch Dispatchers
20
+ * - Instruction Arrays
21
+ * - Stack Operations
22
+ *
23
+ * Requires OPENAI_API_KEY environment variable to be set.
24
+ */
25
+ export declare const findJsvmpDispatcherTool: {
26
+ name: string;
27
+ description: string;
28
+ schema: {
29
+ filePath: z.ZodString;
30
+ startLine: z.ZodNumber;
31
+ endLine: z.ZodNumber;
32
+ charLimit: z.ZodOptional<z.ZodNumber>;
33
+ };
34
+ handler: (params: {
35
+ filePath: string;
36
+ startLine: number;
37
+ endLine: number;
38
+ charLimit?: number;
39
+ }) => Promise<string>;
40
+ };
41
+ //# sourceMappingURL=findJsvmpDispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcher.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;GAEG;AACH,eAAO,MAAM,8BAA8B;;;;;CAK1C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;sBAcV;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAG,OAAO,CAAC,MAAM,CAAC;CAapB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Input schema for find_jsvmp_dispatcher tool
4
+ */
5
+ export declare const FindJsvmpDispatcherInputSchema: {
6
+ filePath: z.ZodString;
7
+ charLimit: z.ZodOptional<z.ZodNumber>;
8
+ maxTokensPerBatch: z.ZodOptional<z.ZodNumber>;
9
+ };
10
+ /**
11
+ * MCP Tool: find_jsvmp_dispatcher
12
+ *
13
+ * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.
14
+ *
15
+ * JSVMP is a code protection technique that converts JavaScript to bytecode
16
+ * executed by a virtual machine. This tool identifies:
17
+ * - If-Else Dispatchers
18
+ * - Switch Dispatchers
19
+ * - Instruction Arrays
20
+ * - Stack Operations
21
+ *
22
+ * Requires OPENAI_API_KEY environment variable to be set.
23
+ */
24
+ export declare const findJsvmpDispatcherTool: import("./ToolDefinition.js").ToolDefinition<{
25
+ filePath: z.ZodString;
26
+ charLimit: z.ZodOptional<z.ZodNumber>;
27
+ maxTokensPerBatch: z.ZodOptional<z.ZodNumber>;
28
+ }>;
29
+ //# sourceMappingURL=findJsvmpDispatcherTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcherTool.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcherTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;GAEG;AACH,eAAO,MAAM,8BAA8B;;;;CAI1C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB;;;;EA8BlC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Property-based tests for findJsvmpDispatcherTool
3
+ *
4
+ * Tests the MCP tool's input validation and response format properties.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=findJsvmpDispatcherTool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcherTool.test.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcherTool.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Tool aggregation module
3
+ * Collects all tool definitions and exports them as a unified array.
4
+ */
5
+ import { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema } from './findJsvmpDispatcherTool.js';
6
+ /**
7
+ * Array of all available MCP tool definitions.
8
+ * To add a new tool:
9
+ * 1. Create a new tool module in src/tools/
10
+ * 2. Import it here
11
+ * 3. Add it to this array
12
+ */
13
+ export declare const tools: readonly [import("./ToolDefinition.js").ToolDefinition<{
14
+ filePath: import("zod").ZodString;
15
+ charLimit: import("zod").ZodOptional<import("zod").ZodNumber>;
16
+ maxTokensPerBatch: import("zod").ZodOptional<import("zod").ZodNumber>;
17
+ }>];
18
+ export { ToolDefinition, defineTool } from './ToolDefinition.js';
19
+ export { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema };
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAEvG;;;;;;GAMG;AACH,eAAO,MAAM,KAAK;;;;GAER,CAAC;AAGX,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,23 +1,22 @@
1
1
  {
2
2
  "name": "@reverse-craft/ai-tools",
3
- "version": "1.0.0",
4
- "description": "AI-powered code analysis tools - LLM-driven functionality for smart-fs",
3
+ "version": "1.0.2",
4
+ "description": "MCP server for AI-powered JSVMP detection - provides LLM-driven code analysis tools",
5
5
  "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
12
- }
6
+ "main": "dist/server.js",
7
+ "bin": {
8
+ "ai-tools-mcp": "./dist/server.js"
13
9
  },
14
10
  "scripts": {
15
- "build": "tsc",
11
+ "build": "node build.js",
16
12
  "build:types": "tsc --emitDeclarationOnly",
13
+ "start": "node dist/server.js",
17
14
  "test": "vitest --run",
18
15
  "prepublishOnly": "npm run build"
19
16
  },
20
17
  "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
21
20
  "ai",
22
21
  "llm",
23
22
  "openai",
@@ -44,7 +43,14 @@
44
43
  "node": ">=18"
45
44
  },
46
45
  "dependencies": {
47
- "source-map-js": "^1.2.1"
46
+ "@ai-sdk/anthropic": "^3.0.1",
47
+ "@ai-sdk/google": "^3.0.1",
48
+ "@ai-sdk/openai": "^3.0.1",
49
+ "@modelcontextprotocol/sdk": "^1.25.1",
50
+ "ai": "^6.0.3",
51
+ "source-map-js": "^1.2.1",
52
+ "tiktoken": "^1.0.22",
53
+ "zod": "^4.2.1"
48
54
  },
49
55
  "peerDependencies": {
50
56
  "@reverse-craft/smart-fs": "^2.0.0"
@@ -52,6 +58,8 @@
52
58
  "devDependencies": {
53
59
  "@reverse-craft/smart-fs": "^2.0.0",
54
60
  "@types/node": "^22.15.29",
61
+ "esbuild": "^0.27.2",
62
+ "fast-check": "^4.5.2",
55
63
  "typescript": "^5.8.3",
56
64
  "vitest": "^4.0.16"
57
65
  }