UNPKG

@clawdcc/cvm-benchmark

Version:

Comprehensive benchmarking and performance analysis tools for Claude Code versions

203 lines (199 loc) 6.67 kB
#!/usr/bin/env node // src/benchmarks/interactive-pty.ts import * as pty from "node-pty"; // src/utils/logger.ts import chalk from "chalk"; var Logger = class { level = "info"; silent = false; setLevel(level) { this.level = level; } setSilent(silent) { this.silent = silent; } debug(message, ...args) { if (!this.silent && this.shouldLog("debug")) { console.log(chalk.gray(`[DEBUG] ${message}`), ...args); } } info(message, ...args) { if (!this.silent && this.shouldLog("info")) { console.log(chalk.blue(`\u2139 ${message}`), ...args); } } warn(message, ...args) { if (!this.silent && this.shouldLog("warn")) { console.warn(chalk.yellow(`\u26A0 ${message}`), ...args); } } error(message, ...args) { if (!this.silent && this.shouldLog("error")) { console.error(chalk.red(`\u2716 ${message}`), ...args); } } success(message, ...args) { if (!this.silent && this.shouldLog("success")) { console.log(chalk.green(`\u2713 ${message}`), ...args); } } shouldLog(level) { const levels = ["debug", "info", "warn", "error", "success"]; return levels.indexOf(level) >= levels.indexOf(this.level); } }; var logger = new Logger(); // src/benchmarks/interactive-pty.ts async function benchmarkInteractive(options) { const { claudePath: claudePath2, cwd: cwd2, timeout: timeout2 = 3e4 } = options; return new Promise((resolve) => { const startTime = Date.now(); let output = ""; const signals = { bracketedPaste: false, focusEvents: false, prompt: false }; let readyDetected = false; let errorDetected = false; let sessionId = void 0; let trustPromptHandled = false; let ptyProcess; try { ptyProcess = pty.spawn(claudePath2, [], { name: "xterm-256color", cols: 80, rows: 30, cwd: cwd2, env: process.env }); } catch (error) { resolve({ time: Date.now() - startTime, result: "failed", reason: `PTY spawn failed: ${error instanceof Error ? error.message : String(error)}`, signals, sessionId }); return; } const timeoutId = setTimeout(() => { ptyProcess.kill(); resolve({ time: Date.now() - startTime, result: "timeout", reason: `Benchmark timed out after ${timeout2}ms`, signals, sessionId }); }, timeout2); ptyProcess.onData((data) => { output += data; if (!trustPromptHandled && output.includes("Do you trust the files")) { trustPromptHandled = true; logger.debug("Trust prompt detected, auto-accepting..."); setTimeout(() => ptyProcess.write("\r"), 100); } if (!sessionId) { const sessionMatch = output.match(/"session_id":"([a-f0-9-]+)"/); if (sessionMatch) { sessionId = sessionMatch[1]; } } if ((output.includes("needs update") || output.includes("newer version") || output.includes("requires") || output.includes("minimum version")) && !errorDetected) { errorDetected = true; const cleanOutput = output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, ""); const lines = cleanOutput.split("\n"); const errorStartIdx = lines.findIndex( (l) => l.includes("needs update") || l.includes("newer version") || l.includes("requires") || l.includes("minimum version") ); const errorLines = lines.slice(Math.max(0, errorStartIdx - 1), errorStartIdx + 6); const fullErrorMessage = errorLines.join("\n").trim(); const versionMatch = cleanOutput.match(/(\d+\.\d+\.\d+)\s+or higher/i) || cleanOutput.match(/version\s+\((\d+\.\d+\.\d+)/i) || cleanOutput.match(/requires\s+(\d+\.\d+\.\d+)/i) || cleanOutput.match(/minimum\s+version[:\s]+(\d+\.\d+\.\d+)/i) || cleanOutput.match(/v?(\d+\.\d+\.\d+)\+/); const minVersion = versionMatch ? versionMatch[1] : null; const EXPECTED_MIN_VERSION = "1.0.24"; if (minVersion && minVersion !== EXPECTED_MIN_VERSION) { logger.warn(`Minimum version changed: expected ${EXPECTED_MIN_VERSION}, found ${minVersion}`); } clearTimeout(timeoutId); ptyProcess.kill(); resolve({ time: Date.now() - startTime, result: "error_detected", reason: "version_requirement_not_met", minVersionRequired: minVersion || EXPECTED_MIN_VERSION, errorMessage: fullErrorMessage, rawOutput: cleanOutput.substring(0, 2e3), sessionId }); return; } if (data.includes("\x1B[?2004h") && !signals.bracketedPaste) { signals.bracketedPaste = true; } if (data.includes("\x1B[?1004h") && !signals.focusEvents) { signals.focusEvents = true; } const stripped = data.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, ""); if (/>\s/.test(stripped) && !signals.prompt) { signals.prompt = true; } if (signals.bracketedPaste && signals.focusEvents && signals.prompt && !readyDetected) { readyDetected = true; setTimeout(() => { clearTimeout(timeoutId); ptyProcess.kill(); resolve({ time: Date.now() - startTime, result: "ready", reason: "all terminal signals received and process stable", signals: { ...signals }, sessionId }); }, 500); } }); ptyProcess.onExit(({ exitCode }) => { if (errorDetected || readyDetected) return; clearTimeout(timeoutId); const elapsed = Date.now() - startTime; if (signals.prompt) { resolve({ time: elapsed, result: "ui_then_exit", reason: "showed prompt but immediately exited", signals: { ...signals }, exitCode, sessionId }); return; } resolve({ time: elapsed, result: "exited_early", reason: "process exited before showing prompt", signals: { ...signals }, exitCode, sessionId }); }); }); } // src/benchmarks/interactive-worker.ts var [claudePath, cwd, timeout] = process.argv.slice(2); if (!claudePath || !cwd) { console.error("Usage: interactive-worker.js <claudePath> <cwd> [timeout]"); process.exit(1); } benchmarkInteractive({ claudePath, cwd, timeout: timeout ? parseInt(timeout) : 3e4 }).then((result) => { console.log(JSON.stringify(result)); process.exit(0); }).catch((error) => { console.error("Benchmark error:", error); process.exit(1); }); //# sourceMappingURL=interactive-worker.js.map