"use strict"; /** * Gravity Bridge — VS Code Extension (SDK Edition) * * Uses antigravity-sdk for: * - Real-time step/conversation monitoring via EventMonitor * - Full conversation content via LSBridge.getConversation() * - Message sending via CascadeManager.sendPrompt() * - Accept/Reject via CascadeManager.acceptStep()/rejectStep() * * Communication with Discord via file-based bridge protocol. */ 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.fixLSConnection = fixLSConnection; exports.activate = activate; exports.deactivate = deactivate; const vscode = __importStar(require("vscode")); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const cp = __importStar(require("child_process")); const ws_client_1 = require("./ws-client"); const step_probe_1 = require("./step-probe"); const http_bridge_1 = require("./http-bridge"); const html_patcher_1 = require("./html-patcher"); const command_handler_1 = require("./command-handler"); // ─── File-based logging (AI can read directly) ─── function logToFile(msg) { const ts = new Date().toISOString().replace('T', ' ').substring(0, 19); // Include projectName prefix so shared log can distinguish which extension instance logged const prefix = projectName ? `[${projectName}]` : ''; const line = `${ts} ${prefix} ${msg}`; console.log(`Gravity Bridge: ${prefix} ${msg}`); try { if (!bridgePath) return; const logFile = path.join(bridgePath, 'extension.log'); // Log rotation: truncate when >10MB, keep last 2MB try { const stat = fs.statSync(logFile); if (stat.size > 10 * 1024 * 1024) { const content = fs.readFileSync(logFile, 'utf-8'); fs.writeFileSync(logFile, content.slice(-2 * 1024 * 1024), 'utf-8'); } } catch { /* file doesn't exist yet */ } fs.appendFileSync(logFile, line + '\n', 'utf-8'); } catch (e) { console.error(`Gravity Bridge LOG WRITE FAIL: ${e.message}`); } } // antigravity-sdk embedded locally (src/sdk/) let AntigravitySDK; let sdk; let statusBar; let bridgePath; let projectName; let workspaceUri = ''; // filesystem path of the workspace folder (for session filtering) let isActive = false; let autoApproveEnabled = false; // toggled via !auto from Discord let watcher = null; let wsBridge = null; // WebSocket Hub connection // ─── Project Detection ─── function detectProjectName() { const config = vscode.workspace.getConfiguration('gravityBridge'); const configName = config.get('projectName'); if (configName) { return configName; } const folders = vscode.workspace.workspaceFolders; if (folders && folders.length > 0) { const cwd = folders[0].uri.fsPath; try { const gitConfigPath = path.join(cwd, '.git', 'config'); if (fs.existsSync(gitConfigPath)) { const configContent = fs.readFileSync(gitConfigPath, 'utf8'); const match = configContent.match(/url\s*=\s*.*\/([^\/]+?)(?:\.git)?$/m); if (match && match[1]) { return match[1].toLowerCase().replace(/[\s\-]+/g, '_'); } } } catch { } return path.basename(cwd).toLowerCase().replace(/[\s\-]+/g, '_'); } return 'default'; } // ─── Bridge File I/O ─── function ensureBridgeDir() { const dirs = ['', 'response', 'commands', 'chat_snapshots']; for (const d of dirs) { const p = path.join(bridgePath, d); if (!fs.existsSync(p)) { fs.mkdirSync(p, { recursive: true }); } } } // Module-level activeSessionId so writeChatSnapshot can register sessions lazily let activeSessionId = ''; let activeTrajectoryId = ''; // Track recently sent Discord→AG texts to avoid echo relay const recentDiscordSentTexts = new Map(); function writeChatSnapshot(text) { try { // WS route (preferred) — skip file write to prevent duplicate Discord delivery if (wsBridge && wsBridge.isConnected()) { wsBridge.sendChat({ content: text, conversation_id: activeSessionId, project_name: projectName, }); logToFile(`[SNAPSHOT-WS] sent (${text.length} chars)`); if (activeSessionId) { (0, step_probe_1.writeRegistration)(activeSessionId); } return; } // File route (fallback — only when WS is NOT connected) const snapshotDir = path.join(bridgePath, 'chat_snapshots'); if (!fs.existsSync(snapshotDir)) { fs.mkdirSync(snapshotDir, { recursive: true }); } const id = Date.now().toString(); const data = { id, project_name: projectName, content: text, timestamp: Date.now() / 1000, }; const filePath = path.join(snapshotDir, `${id}.json`); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); console.log(`Gravity Bridge: chat snapshot written (${text.length} chars) → ${id}.json`); logToFile(`[SNAPSHOT] written ${id}.json (${text.length} chars)`); logToFile(`[SNAPSHOT] content: ${text.substring(0, 200)}`); // Lazily register session → project mapping (correct because projectName is per-window) if (activeSessionId) { (0, step_probe_1.writeRegistration)(activeSessionId); } } catch (e) { console.log(`Gravity Bridge: snapshot write error: ${e.message}`); } } function writeChatSnapshotWithFiles(text, files) { try { // WS route (preferred) — skip file write to prevent duplicate Discord delivery if (wsBridge && wsBridge.isConnected()) { wsBridge.sendChat({ content: text, attached_files: files, conversation_id: activeSessionId, project_name: projectName, }); logToFile(`[SNAPSHOT-WS] sent with ${files.length} files (${text.length} chars)`); if (activeSessionId) { (0, step_probe_1.writeRegistration)(activeSessionId); } return; } // File route (fallback — only when WS is NOT connected) const snapshotDir = path.join(bridgePath, 'chat_snapshots'); if (!fs.existsSync(snapshotDir)) { fs.mkdirSync(snapshotDir, { recursive: true }); } const id = Date.now().toString(); const data = { id, project_name: projectName, content: text, attached_files: files, timestamp: Date.now() / 1000, }; const filePath = path.join(snapshotDir, `${id}.json`); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); logToFile(`[SNAPSHOT] written ${id}.json (${text.length} chars, ${files.length} files)`); if (activeSessionId) { (0, step_probe_1.writeRegistration)(activeSessionId); } } catch (e) { console.log(`Gravity Bridge: snapshot+files write error: ${e.message}`); } } // ─── Command handling extracted to ./command-handler.ts ─── // ─── SDK Integration ─── async function initSDK(context) { try { const sdkModule = require('./sdk/index'); AntigravitySDK = sdkModule.AntigravitySDK; } catch (err) { console.log(`Gravity Bridge: antigravity-sdk load failed: ${err.message}`); return false; } try { sdk = new AntigravitySDK(context); await sdk.initialize(); console.log('Gravity Bridge: ✅ SDK initialized'); // ── FIX: SDK's _findLSProcess() uses case-sensitive String.includes() ── // workspace_id in LS process has 'Desktop' (capital D), but SDK hint // generates 'desktop' (lowercase) → match fails → connects to WRONG LS. // Re-discover the correct LS using case-insensitive workspace_id matching. await fixLSConnection(); return true; } catch (err) { console.log(`Gravity Bridge: SDK init failed: ${err.message}`); return false; } } /** * Fix SDK's LS connection by finding the correct language_server process * for this workspace using case-insensitive matching. * * SDK bug: _findLSProcess() compares workspaceHint via JS String.includes() * which is case-sensitive. workspace_id in process args has original casing * (e.g., file_c_3A_Users_Certes_Desktop_variet_agent) but SDK hint is * lowercased (desktop_variet_agent) → no match → falls back to first LS * found (wrong workspace). */ async function fixLSConnection() { if (!sdk?.ls) { logToFile('[LS-FIX] skipped: sdk.ls not available'); return false; } try { const folders = vscode.workspace.workspaceFolders; if (!folders || folders.length === 0) { logToFile('[LS-FIX] skipped: no workspace folders'); return false; } // Generate the workspace hint the same way SDK does, but we'll match case-insensitively const folder = folders[0].uri.fsPath; const parts = folder.replace(/\\/g, '/').split('/'); const hint = parts.slice(-2).join('_').replace(/[-.\s]/g, '_').toLowerCase(); if (!hint) { logToFile('[LS-FIX] skipped: empty hint'); return false; } // Find all language_server processes with csrf_token const { exec } = cp; const { promisify } = require('util'); const execAsync = promisify(exec); let output; try { const psScript = `Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'language_server' -and $_.CommandLine -match 'csrf_token' } | ForEach-Object { $_.ProcessId.ToString() + '|' + $_.CommandLine }`; const encoded = Buffer.from(psScript, 'utf16le').toString('base64'); const result = await execAsync(`powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand ${encoded}`, { encoding: 'utf8', timeout: 5000, windowsHide: true }); output = result.stdout; } catch (psErr) { logToFile(`[LS-FIX] skipped: PowerShell failed — ${psErr.message?.substring(0, 100)}`); return false; } const lines = output.split('\n').filter((l) => l.trim().length > 0); if (lines.length === 0) { logToFile('[LS-FIX] skipped: no LS processes found'); return false; } // NOTE: Do NOT skip on single LS — SDK may have fallen back to wrong LS // due to case-sensitive hint mismatch, even when only one process exists. logToFile(`[LS-FIX] found ${lines.length} LS process(es), hint="${hint}"`); // Find the line whose workspace_id matches our workspace (case-insensitive) let matchedLine = null; for (const line of lines) { const lower = line.toLowerCase(); // Match workspace_id arg against our hint const wsMatch = line.match(/--workspace_id[= ](\S+)/i); if (wsMatch) { const wsid = wsMatch[1].toLowerCase(); if (wsid.includes(hint)) { matchedLine = line; break; } } } if (!matchedLine) { logToFile(`[LS-FIX] No LS process matched hint="${hint}" (${lines.length} processes)`); return false; } // Extract port and csrf_token from matched line const csrfMatch = matchedLine.match(/--csrf_token[= ](\S+)/); const extPortMatch = matchedLine.match(/--extension_server_port[= ](\d+)/); const pidMatch = matchedLine.split('|')[0]?.trim(); if (!csrfMatch || !extPortMatch) { logToFile(`[LS-FIX] Matched LS but missing csrf/port args`); return false; } const csrfToken = csrfMatch[1]; const extPort = parseInt(extPortMatch[1], 10); const pid = parseInt(pidMatch || '0', 10); // Check if SDK already connected to this LS if (sdk.ls.port === extPort) { logToFile(`[LS-FIX] SDK already on correct LS port=${extPort}`); return false; } // Find ConnectRPC port via netstat (same as SDK logic) let netstatOutput; try { const result = await execAsync(`netstat -aon`, { encoding: 'utf8', timeout: 4000, windowsHide: true }); netstatOutput = result.stdout.split('\n') .filter((l) => l.includes('LISTENING') && l.includes(pid.toString())) .join('\n'); } catch (err) { // Netstat failed — try extension_server_port as fallback logToFile(`[LS-FIX] netstat failed: ${err.message.substring(0, 80)}, using ext_port=${extPort} for PID=${pid}`); sdk.ls.setConnection(extPort, csrfToken, false); logToFile(`[LS-FIX] ✅ Reconnected to correct LS: port=${extPort} hint="${hint}" PID=${pid}`); return true; } const portMatches = netstatOutput.matchAll(/127\.0\.0\.1:(\d+)/g); const ports = []; for (const m of portMatches) { const p = parseInt(m[1], 10); if (p !== extPort && !ports.includes(p)) { ports.push(p); } } // Try each port — prefer HTTPS, fall back to HTTP const httpModule = require('http'); const httpsModule = require('https'); for (const useTls of [true, false]) { const mod = useTls ? httpsModule : httpModule; const proto = useTls ? 'https' : 'http'; for (const port of ports) { try { const ok = await new Promise((resolve) => { const req = mod.request(`${proto}://127.0.0.1:${port}/exa.language_server_pb.LanguageServerService/GetUserStatus`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': 2 }, rejectUnauthorized: false, timeout: 2000, }, (res) => resolve(res.statusCode === 200 || res.statusCode === 401)); req.on('error', () => resolve(false)); req.on('timeout', () => { req.destroy(); resolve(false); }); req.write('{}'); req.end(); }); if (ok) { sdk.ls.setConnection(port, csrfToken, useTls); logToFile(`[LS-FIX] ✅ Reconnected to correct LS: port=${port} ${proto} hint="${hint}" PID=${pid}`); return true; } } catch { /* try next */ } } } // Last resort: use extension_server_port sdk.ls.setConnection(extPort, csrfToken, false); logToFile(`[LS-FIX] ✅ Reconnected via ext_port=${extPort} hint="${hint}" PID=${pid}`); return true; } catch (err) { logToFile(`[LS-FIX] error: ${err.message}`); return false; } } // ─── Approval Observer + Product.json Checksums extracted to ./html-patcher.ts ─── // ─── HTTP Bridge Server extracted to ./http-bridge.ts ─── // ─── Step Probe, Response Watcher, Approval Strategies → extracted to ./step-probe.ts ─── async function activate(context) { console.log('Gravity Bridge: activating...'); // Project detection projectName = detectProjectName(); // Store workspace folder path for session filtering (prevents cross-window session grabbing) const folders = vscode.workspace.workspaceFolders; workspaceUri = folders && folders.length > 0 ? folders[0].uri.fsPath : ''; console.log(`Gravity Bridge: project "${projectName}" workspace="${workspaceUri}"`); // Bridge path const config = vscode.workspace.getConfiguration('gravityBridge'); const configPath = config.get('bridgePath'); bridgePath = configPath || path.join(os.homedir(), '.gemini', 'antigravity', 'bridge'); ensureBridgeDir(); console.log(`Gravity Bridge: bridge path: ${bridgePath}`); // ── WebSocket Hub Connection ── const hubUrl = process.env.GRAVITY_HUB_URL || config.get('hubUrl') || ''; const regCode = process.env.GRAVITY_REGISTRATION_CODE || config.get('registrationCode') || ''; const pcName = os.hostname(); if (hubUrl) { wsBridge = new ws_client_1.WSBridgeClient(hubUrl, regCode, projectName, pcName, { onResponse: (data) => { logToFile(`[WS-RESPONSE] ${data.request_id?.substring(0, 12)} approved=${data.approved} step_type=${data.step_type || '(none)'}`); const approved = data.approved ?? true; const stepType = data.step_type || ''; // ── diff_review: Accept all / Reject all (REGRESSION FIX) ── // Previously only handled in processResponseFile (file-bridge path). // WS path was missing this logic entirely, causing Accept All to fail. if (stepType === 'diff_review') { logToFile(`[WS-RESPONSE] diff_review detected — routing to handleDiffReviewResponse`); (0, step_probe_1.handleDiffReviewResponse)({ request_id: data.request_id, approved, button_index: data.button_index, step_type: stepType, }) .then(result => { logToFile(`[WS-RESPONSE] diff_review result: ${result}`); (0, step_probe_1.resetPendingState)(); }) .catch(err => logToFile(`[WS-RESPONSE] diff_review error: ${err.message}`)); return; } // Normal approval — tryApprovalStrategies const approvalCtx = (0, step_probe_1.getApprovalContext)(); logToFile(`[WS-RESPONSE] Triggering approval: approved=${approved} session=${approvalCtx.sessionId.substring(0, 8)} stepType=${stepType} stepIndex=${approvalCtx.stepIndex}`); (0, step_probe_1.tryApprovalStrategies)(approved, approvalCtx.sessionId, stepType, approvalCtx.stepIndex) .then(result => { logToFile(`[WS-RESPONSE] Approval result: ${result}`); (0, step_probe_1.resetPendingState)(); }) .catch(err => logToFile(`[WS-RESPONSE] Approval error: ${err.message}`)); }, onCommand: (data) => { logToFile(`[WS-CMD] ${data.text?.substring(0, 50)}`); (0, command_handler_1.handleWSCommand)({ bridgePath, projectName, sdk, ls: sdk?.ls, autoApproveEnabled, logToFile, onAutoApproveChanged: (enabled) => { autoApproveEnabled = enabled; }, recentDiscordSentTexts, getActiveSessionId: () => (0, step_probe_1.getActiveSessionId)(), }, data); }, onInstanceUpdate: (count, instances) => { logToFile(`[WS-INSTANCE] ${count} active instances`); }, onConnected: (connId, instanceNum, token) => { logToFile(`[WS] Connected: ${connId} instance=#${instanceNum}`); statusBar.text = '$(check) Bridge WS'; statusBar.tooltip = `Gravity Bridge: ${projectName} (WS #${instanceNum})`; // Reset step-probe state so WAITING steps are re-detected after reconnect (0, step_probe_1.resetPendingStateForReconnect)(); }, onDisconnected: () => { logToFile('[WS] Disconnected — using file fallback'); statusBar.text = '$(warning) Bridge (WS ↓)'; }, onError: (err) => { logToFile(`[WS-ERR] ${err}`); }, }, logToFile); wsBridge.connect(); logToFile(`[WS] Hub connection initiated: ${hubUrl}`); } else { logToFile('[WS] No GRAVITY_HUB_URL — WebSocket disabled, using file bridge only'); } // ── Multi-project: no lock file, each project uses project_name-based filtering ── // (active_project.lock removed — was blocking concurrent multi-project usage) logToFile(`[INIT] project="${projectName}" pid=${process.pid} — multi-project mode (no lock)`); // Status bar statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); statusBar.text = '$(sync~spin) Bridge'; statusBar.tooltip = `Gravity Bridge: ${projectName}`; statusBar.show(); context.subscriptions.push(statusBar); // Initialize SDK const sdkReady = await initSDK(context); if (sdkReady) { // ── Command Discovery Diagnostic ── // Enumerate ALL antigravity.* commands to find correct approval command names try { const allCmds = await vscode.commands.getCommands(true); const agCmds = allCmds.filter((c) => c.startsWith('antigravity.')); logToFile(`[CMD-DISCOVERY] Total antigravity.* commands: ${agCmds.length}`); // Log approval-related commands specifically const approvalKeywords = ['accept', 'reject', 'approve', 'terminal', 'agent', 'cascade', 'step', 'run', 'command.']; const relevantCmds = agCmds.filter((c) => approvalKeywords.some(kw => c.toLowerCase().includes(kw))); logToFile(`[CMD-DISCOVERY] Approval-related commands (${relevantCmds.length}):`); for (const cmd of relevantCmds) { logToFile(`[CMD-DISCOVERY] → ${cmd}`); } // Also dump ALL commands for full reference logToFile(`[CMD-DISCOVERY] ALL antigravity.* commands:`); for (const cmd of agCmds) { logToFile(`[CMD-DISCOVERY] ${cmd}`); } } catch (e) { logToFile(`[CMD-DISCOVERY] error: ${e.message}`); } // Initialize step probe (polling + response watcher) (0, step_probe_1.initStepProbe)({ bridgePath, projectName, sdk, wsBridge, activeSessionId, sessionStalled: false, lastPendingStepIndex: -1, stallProbed: false, sawRunningAfterPending: true, setClickTrigger: (action) => { const { setClickTrigger: setTrigger } = require('./http-bridge'); setTrigger(action); }, logToFile, workspaceUri, diffReviewMetadata: new Map(), recentDiscordSentTexts, writeChatSnapshot, writeChatSnapshotWithFiles, fixLSConnection, }); // Start HTTP bridge with live step-probe state (prevents stale primitive bug) const httpBridgeCtx = { bridgePath, projectName, wsBridge, logToFile, get activeSessionId() { return (0, step_probe_1.getStepProbeContext)().activeSessionId; }, get sessionStalled() { return (0, step_probe_1.getStepProbeContext)().sessionStalled; }, get lastPendingStepIndex() { return (0, step_probe_1.getStepProbeContext)().lastPendingStepIndex; }, }; const bridgePort = await (0, http_bridge_1.startHttpBridge)(httpBridgeCtx, sdk); let localPort = bridgePort; if (bridgePort) { try { const externalUri = await vscode.env.asExternalUri(vscode.Uri.parse(`http://127.0.0.1:${bridgePort}`)); const match = externalUri.authority.match(/:(\d+)$/); if (match) { localPort = parseInt(match[1], 10); } } catch (e) { logToFile(`[OBSERVER] asExternalUri failed: ${e.message}`); } await (0, html_patcher_1.setupApprovalObserver)(sdk, localPort, logToFile); } else { logToFile('[OBSERVER] HTTP bridge failed — skipping observer setup'); } statusBar.text = '$(check) Bridge'; statusBar.tooltip = `Gravity Bridge Control | port:${localPort} | project:${projectName}`; // Register SDK-powered commands context.subscriptions.push(vscode.commands.registerCommand('gravityBridge.approve', async () => { try { await sdk.cascade.acceptStep(); vscode.window.showInformationMessage('Gravity Bridge: Step approved'); } catch (e) { vscode.window.showErrorMessage(`Approve failed: ${e.message}`); } }), vscode.commands.registerCommand('gravityBridge.reject', async () => { try { await sdk.cascade.rejectStep(); vscode.window.showInformationMessage('Gravity Bridge: Step rejected'); } catch (e) { vscode.window.showErrorMessage(`Reject failed: ${e.message}`); } })); } else { statusBar.text = '$(warning) Bridge (no SDK)'; console.log('Gravity Bridge: SDK not available, file-based mode only'); } // Watch commands directory (0, command_handler_1.watchCommandsDir)({ bridgePath, projectName, sdk, ls: sdk?.ls, autoApproveEnabled, logToFile, onAutoApproveChanged: (enabled) => { autoApproveEnabled = enabled; }, recentDiscordSentTexts, getActiveSessionId: () => (0, step_probe_1.getActiveSessionId)(), }); // Response watcher is now initialized by initStepProbe() above // Register basic commands context.subscriptions.push(vscode.commands.registerCommand('gravityBridge.start', () => { isActive = true; statusBar.text = sdkReady ? '$(check) Bridge SDK' : '$(sync~spin) Bridge'; vscode.window.showInformationMessage(`Gravity Bridge started for "${projectName}"`); }), vscode.commands.registerCommand('gravityBridge.stop', () => { isActive = false; // SDK monitor is disabled, no need to stop statusBar.text = '$(circle-slash) Bridge OFF'; vscode.window.showInformationMessage('Gravity Bridge stopped'); }), vscode.commands.registerCommand('gravityBridge.connect', async () => { if (!sdk) { vscode.window.showErrorMessage('SDK not initialized'); return; } try { const sessions = await sdk.cascade.getSessions(); const items = sessions.map((s) => ({ label: s.title || 'Untitled', description: `step ${s.stepCount} • ${s.id?.substring(0, 8)}`, sessionId: s.id, })); const pick = await vscode.window.showQuickPick(items, { placeHolder: 'Select a conversation to connect' }); if (pick) { await sdk.cascade.focusSession(pick.sessionId); vscode.window.showInformationMessage(`Connected to: ${pick.label}`); } } catch (e) { vscode.window.showErrorMessage(`Connect failed: ${e.message}`); } })); // Cleanup context.subscriptions.push({ dispose: () => { if (sdk) { try { sdk.dispose(); } catch { } } if (watcher) { watcher.close(); } (0, command_handler_1.disposeCommandsWatcher)(); } }); console.log('Gravity Bridge: ✅ activated'); isActive = true; } function deactivate() { // Disconnect WebSocket if (wsBridge) { wsBridge.disconnect(); wsBridge = null; } // Clean up stale lock file if it exists (legacy cleanup) try { const lockFile = path.join(bridgePath, 'active_project.lock'); if (fs.existsSync(lockFile)) { const lockData = JSON.parse(fs.readFileSync(lockFile, 'utf-8')); if (lockData.pid === process.pid) { fs.unlinkSync(lockFile); } } } catch { } if (sdk) { try { sdk.dispose(); } catch { } } } //# sourceMappingURL=extension.js.map