"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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DebugClient = void 0; const vscode = __importStar(require("vscode")); const crypto = __importStar(require("crypto")); class DebugClient { constructor(formattingProvider) { this.ws = null; // WebSocket instance this.isConnecting = false; this.reconnectTimer = null; this.pingTimer = null; this.sessionId = null; this.serverKey = null; this.formattingProvider = formattingProvider; this.outputChannel = vscode.window.createOutputChannel('RSPade Debug Proxy'); this.outputChannel.show(); this.log('Debug client initialized'); } async start() { this.log('Starting debug client...'); await this.connect(); } async connect() { if (this.isConnecting || this.ws?.readyState === WebSocket.OPEN) { return; } this.isConnecting = true; try { // Get authentication from formatting provider await this.ensureAuthenticated(); const serverUrl = await this.formattingProvider.get_server_url(); if (!serverUrl) { throw new Error('No server URL configured'); } // Parse URL and construct WebSocket URL const url = new URL(serverUrl); const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${wsProtocol}//${url.host}/_ide/debug/ws`; this.log(`Connecting to WebSocket: ${wsUrl}`); // Create WebSocket (standard API doesn't support headers in constructor) // We'll send auth after connection this.ws = new WebSocket(wsUrl); this.setupEventHandlers(); } catch (error) { this.log(`Connection failed: ${error.message}`); this.isConnecting = false; this.scheduleReconnect(); } } setupEventHandlers() { if (!this.ws) return; this.ws.onopen = () => { this.isConnecting = false; this.log('WebSocket connected, sending authentication...'); // Send authentication as first message const signature = crypto .createHmac('sha256', this.serverKey) .update(this.sessionId) .digest('hex'); this.sendMessage({ type: 'auth', data: { sessionId: this.sessionId, signature: signature } }); // Send initial hello message after auth setTimeout(() => { this.sendMessage({ type: 'hello', data: { name: 'VS Code Debug Client' } }); // Start ping timer this.startPingTimer(); }, 100); }; this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data); this.handleMessage(message); } catch (error) { this.log(`Failed to parse message: ${error}`); } }; this.ws.onclose = () => { this.log('WebSocket disconnected'); this.ws = null; this.stopPingTimer(); this.scheduleReconnect(); }; this.ws.onerror = (error) => { this.log(`WebSocket error: ${error}`); }; } handleMessage(message) { this.log(`Received: ${message.type}`, message.data); switch (message.type) { case 'welcome': this.log('✅ Authentication successful! Connected to debug proxy'); this.log(`Session ID: ${message.data?.sessionId}`); break; case 'pong': this.log(`PONG received! Server responded to ping`); break; case 'hello_response': this.log(`Server says: ${message.data?.message}`); break; case 'error': this.log(`❌ Error: ${message.data?.message}`); break; default: this.log(`Unknown message type: ${message.type}`); } } sendMessage(message) { if (this.ws?.readyState === 1) { // 1 = OPEN in standard WebSocket API this.ws.send(JSON.stringify(message)); this.log(`Sent: ${message.type}`, message.data); } } startPingTimer() { this.stopPingTimer(); // Send ping every 5 seconds this.pingTimer = setInterval(() => { this.sendMessage({ type: 'ping', data: { timestamp: Date.now() } }); this.log('PING sent to server'); }, 5000); } stopPingTimer() { if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; } } scheduleReconnect() { if (this.reconnectTimer) { return; } this.log('Scheduling reconnection in 5 seconds...'); this.reconnectTimer = setTimeout(() => { this.reconnectTimer = null; this.connect(); }, 5000); } async ensureAuthenticated() { // Get auth data from formatting provider const authData = await this.formattingProvider.ensure_auth(); if (!authData) { throw new Error('Failed to authenticate'); } // Extract session ID and server key this.sessionId = authData.session_id; this.serverKey = authData.server_key; if (!this.sessionId || !this.serverKey) { throw new Error('Invalid auth data'); } } log(message, data) { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] ${message}`; if (data) { this.outputChannel.appendLine(`${logMessage}\n${JSON.stringify(data, null, 2)}`); } else { this.outputChannel.appendLine(logMessage); } } dispose() { this.stopPingTimer(); if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } if (this.ws) { this.ws.close(); this.ws = null; } this.outputChannel.dispose(); } } exports.DebugClient = DebugClient; //# sourceMappingURL=debug_client.js.map