| "use strict"; |
| |
| |
| |
| |
| |
| |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| var desc = Object.getOwnPropertyDescriptor(m, k); |
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { |
| desc = { enumerable: true, get: function() { return m[k]; } }; |
| } |
| Object.defineProperty(o, k2, desc); |
| }) : (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| o[k2] = m[k]; |
| })); |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| }) : function(o, v) { |
| o["default"] = v; |
| }); |
| var __importStar = (this && this.__importStar) || (function () { |
| var ownKeys = function(o) { |
| ownKeys = Object.getOwnPropertyNames || function (o) { |
| var ar = []; |
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; |
| return ar; |
| }; |
| return ownKeys(o); |
| }; |
| return function (mod) { |
| if (mod && mod.__esModule) return mod; |
| var result = {}; |
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); |
| __setModuleDefault(result, mod); |
| return result; |
| }; |
| })(); |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.applyPatch = applyPatch; |
| exports.validatePatchSemantically = validatePatchSemantically; |
| exports.rollbackPatch = rollbackPatch; |
| exports.cleanupOldBackups = cleanupOldBackups; |
| const fs = __importStar(require("fs")); |
| const path = __importStar(require("path")); |
| const child_process_1 = require("child_process"); |
| |
| |
| |
| |
| |
| function applyPatch(workingDir, request) { |
| const absPath = path.resolve(workingDir, request.filename); |
| if (!fs.existsSync(absPath)) { |
| return { |
| success: false, |
| error: `File not found: ${absPath}`, |
| originalCode: '', |
| patchedCode: '' |
| }; |
| } |
| try { |
| const originalCode = fs.readFileSync(absPath, 'utf8'); |
| const lines = originalCode.split('\n'); |
| |
| if (request.functionName && |
| request.functionStartLine && |
| request.functionEndLine) { |
| const result = applyFunctionLevelPatch(lines, request); |
| if (result.success) { |
| |
| const backupPath = `${absPath}.backup.${Date.now()}`; |
| fs.writeFileSync(backupPath, originalCode); |
| |
| fs.writeFileSync(absPath, result.patchedCode); |
| return { |
| ...result, |
| originalCode, |
| backupPath |
| }; |
| } |
| |
| } |
| |
| if (request.lineStart && request.lineEnd) { |
| const result = applyLineRangePatch(lines, request); |
| if (result.success) { |
| const backupPath = `${absPath}.backup.${Date.now()}`; |
| fs.writeFileSync(backupPath, originalCode); |
| fs.writeFileSync(absPath, result.patchedCode); |
| return { |
| ...result, |
| originalCode, |
| backupPath |
| }; |
| } |
| return result; |
| } |
| return { |
| success: false, |
| error: 'No valid patch strategy available', |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| catch (err) { |
| return { |
| success: false, |
| error: `Patch application failed: ${err.message}`, |
| originalCode: '', |
| patchedCode: '' |
| }; |
| } |
| } |
| |
| |
| |
| |
| function applyFunctionLevelPatch(lines, request) { |
| const originalCode = lines.join('\n'); |
| if (!request.functionStartLine || !request.functionEndLine) { |
| return { |
| success: false, |
| error: 'Function boundaries not specified', |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| |
| const startIdx = request.functionStartLine - 1; |
| const endIdx = request.functionEndLine; |
| if (startIdx < 0 || endIdx > lines.length || startIdx >= endIdx) { |
| return { |
| success: false, |
| error: `Invalid function boundaries: ${request.functionStartLine}-${request.functionEndLine}`, |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| |
| const originalFunction = lines.slice(startIdx, endIdx).join('\n'); |
| const functionName = request.functionName || 'unknown'; |
| |
| const firstLine = lines[startIdx].toLowerCase(); |
| const expectedName = functionName.toLowerCase(); |
| |
| const hasFunctionPattern = firstLine.includes('function') || |
| firstLine.includes('=>') || |
| firstLine.includes('def ') || |
| firstLine.includes(`${expectedName}`); |
| if (!hasFunctionPattern) { |
| return { |
| success: false, |
| error: `Function "${functionName}" not found at expected location. Heuristic check failed.`, |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| |
| const newCode = normalizeIndentation(request.newCode, lines[startIdx]); |
| |
| const beforeFunction = lines.slice(0, startIdx); |
| const afterFunction = lines.slice(endIdx); |
| const patchedLines = [...beforeFunction, newCode, ...afterFunction]; |
| const patchedCode = patchedLines.join('\n'); |
| |
| const syntaxError = quickSyntaxCheck(patchedCode, request.language); |
| if (syntaxError) { |
| return { |
| success: false, |
| error: `Syntax check failed: ${syntaxError}`, |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| return { |
| success: true, |
| originalCode, |
| patchedCode |
| }; |
| } |
| |
| |
| |
| |
| function applyLineRangePatch(lines, request) { |
| const originalCode = lines.join('\n'); |
| if (!request.lineStart || !request.lineEnd) { |
| return { |
| success: false, |
| error: 'Line range not specified', |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| |
| const startIdx = request.lineStart - 1; |
| const endIdx = request.lineEnd; |
| if (startIdx < 0 || endIdx > lines.length || startIdx >= endIdx) { |
| return { |
| success: false, |
| error: `Invalid line range: ${request.lineStart}-${request.lineEnd}`, |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| |
| const baseIndent = getIndentation(lines[startIdx]); |
| |
| const newCode = normalizeIndentation(request.newCode, lines[startIdx]); |
| const newCodeLines = newCode.split('\n'); |
| |
| const beforeRange = lines.slice(0, startIdx); |
| const afterRange = lines.slice(endIdx); |
| const patchedLines = [...beforeRange, ...newCodeLines, ...afterRange]; |
| const patchedCode = patchedLines.join('\n'); |
| |
| const syntaxError = quickSyntaxCheck(patchedCode, request.language); |
| if (syntaxError) { |
| return { |
| success: false, |
| error: `Syntax check failed: ${syntaxError}`, |
| originalCode, |
| patchedCode: originalCode |
| }; |
| } |
| return { |
| success: true, |
| originalCode, |
| patchedCode |
| }; |
| } |
| |
| |
| |
| function getIndentation(line) { |
| const match = line.match(/^(\s*)/); |
| return match ? match[1] : ''; |
| } |
| |
| |
| |
| function normalizeIndentation(newCode, referenceLine) { |
| const baseIndent = getIndentation(referenceLine); |
| const lines = newCode.split('\n'); |
| |
| let minIndent = Infinity; |
| for (const line of lines) { |
| if (line.trim()) { |
| const indent = getIndentation(line).length; |
| minIndent = Math.min(minIndent, indent); |
| } |
| } |
| if (minIndent === Infinity) |
| return newCode; |
| |
| const normalized = lines.map(line => { |
| if (!line.trim()) |
| return ''; |
| const currentIndent = getIndentation(line).length; |
| const relativeIndent = currentIndent - minIndent; |
| return baseIndent + ' '.repeat(relativeIndent) + line.trimStart(); |
| }); |
| return normalized.join('\n'); |
| } |
| async function validatePatchSemantically(workingDir, filename, language) { |
| |
| const validationCommands = []; |
| if (language === 'typescript') { |
| |
| validationCommands.push({ cmd: `npx tsc --noEmit ${filename}`, label: 'TypeScript compiler' }, { cmd: 'npx tsc --noEmit', label: 'Project-wide TypeScript check' }); |
| } |
| |
| validationCommands.push({ cmd: `npx eslint ${filename} --max-warnings=0`, label: 'ESLint' }, { cmd: 'npx eslint . --max-warnings=0', label: 'Project-wide ESLint' }); |
| |
| validationCommands.push({ |
| cmd: 'npm test -- --passWithNoTests --testPathPattern=' + filename, |
| label: 'npm test' |
| }); |
| for (const { cmd, label } of validationCommands) { |
| try { |
| console.log(`[VALIDATION] Running ${label}...`); |
| const stdout = (0, child_process_1.execSync)(cmd, { |
| cwd: workingDir, |
| encoding: 'utf8', |
| timeout: 60000, |
| stdio: ['pipe', 'pipe', 'pipe'] |
| }); |
| console.log(`[VALIDATION] ✓ ${label} passed`); |
| return { |
| passed: true, |
| command: cmd, |
| stdout, |
| stderr: '', |
| exitCode: 0 |
| }; |
| } |
| catch (err) { |
| |
| if (err.status === 127 || |
| err.message?.includes('not found') || |
| err.message?.includes('ENOENT')) { |
| console.log(`[VALIDATION] ${label} not available, skipping...`); |
| continue; |
| } |
| |
| console.log(`[VALIDATION] ✗ ${label} failed (exit ${err.status})`); |
| return { |
| passed: false, |
| command: cmd, |
| stdout: err.stdout || '', |
| stderr: err.stderr || '', |
| exitCode: err.status || 1 |
| }; |
| } |
| } |
| |
| console.log(`[VALIDATION] ⚠ No semantic validation tools available`); |
| return { |
| passed: true, |
| command: 'none', |
| stdout: '', |
| stderr: 'No validation tools available', |
| exitCode: 0 |
| }; |
| } |
| |
| |
| |
| |
| function quickSyntaxCheck(code, language) { |
| |
| let braceCount = 0; |
| let parenCount = 0; |
| let bracketCount = 0; |
| let inString = false; |
| let stringChar = ''; |
| for (let i = 0; i < code.length; i++) { |
| const char = code[i]; |
| const prevChar = i > 0 ? code[i - 1] : ''; |
| |
| if (!inString && (char === '"' || char === "'" || char === '`')) { |
| inString = true; |
| stringChar = char; |
| continue; |
| } |
| if (inString && char === stringChar && prevChar !== '\\') { |
| inString = false; |
| continue; |
| } |
| if (inString) |
| continue; |
| |
| if (char === '{') |
| braceCount++; |
| else if (char === '}') |
| braceCount--; |
| else if (char === '(') |
| parenCount++; |
| else if (char === ')') |
| parenCount--; |
| else if (char === '[') |
| bracketCount++; |
| else if (char === ']') |
| bracketCount--; |
| |
| if (braceCount < -2 || parenCount < -2 || bracketCount < -2) { |
| return `Unbalanced ${braceCount < 0 ? 'braces' : parenCount < 0 ? 'parentheses' : 'brackets'}`; |
| } |
| } |
| if (braceCount !== 0) |
| return `Unbalanced braces: ${braceCount > 0 ? 'missing' : 'extra'} ${Math.abs(braceCount)} }`; |
| if (parenCount !== 0) |
| return `Unbalanced parentheses: ${parenCount > 0 ? 'missing' : 'extra'} ${Math.abs(parenCount)} )`; |
| if (bracketCount !== 0) |
| return `Unbalanced brackets: ${bracketCount > 0 ? 'missing' : 'extra'} ${Math.abs(bracketCount)} ]`; |
| |
| if (language === 'typescript' || language === 'javascript') { |
| |
| if (/\bfunction\s*\([^)]*\)\s*[^=>{]/.test(code)) { |
| return 'Function without body'; |
| } |
| } |
| return null; |
| } |
| |
| |
| |
| function rollbackPatch(backupPath, originalPath) { |
| try { |
| if (!fs.existsSync(backupPath)) { |
| console.error(`[PATCH] Backup not found: ${backupPath}`); |
| return false; |
| } |
| fs.copyFileSync(backupPath, originalPath); |
| console.log(`[PATCH] Rolled back ${originalPath} from ${backupPath}`); |
| return true; |
| } |
| catch (err) { |
| console.error(`[PATCH] Rollback failed: ${err.message}`); |
| return false; |
| } |
| } |
| |
| |
| |
| function cleanupOldBackups(workingDir, maxAgeMs = 24 * 60 * 60 * 1000) { |
| try { |
| const files = fs.readdirSync(workingDir); |
| const now = Date.now(); |
| for (const file of files) { |
| if (file.includes('.backup.')) { |
| const backupPath = path.join(workingDir, file); |
| const stats = fs.statSync(backupPath); |
| const age = now - stats.mtimeMs; |
| if (age > maxAgeMs) { |
| fs.unlinkSync(backupPath); |
| console.log(`[PATCH] Cleaned up old backup: ${file}`); |
| } |
| } |
| } |
| } |
| catch (err) { |
| console.error(`[PATCH] Cleanup failed: ${err.message}`); |
| } |
| } |
| |