'use strict';

var __createBinding = this && this.__createBinding || (Object.create ? function (param0, param1, param2, param3) {
  if (param3 === undefined) {
    param3 = param2;
  }
  var descriptor = Object.getOwnPropertyDescriptor(param1, param2);
  if (!descriptor || ("get" in descriptor ? !param1.__esModule : descriptor.writable || descriptor.configurable)) {
    descriptor = {
      enumerable: true,
      get: function () {
        return param1[param2];
      }
    };
  }
  Object.defineProperty(param0, param3, descriptor);
} : function (param0, param1, param2, param3) {
  if (param3 === undefined) {
    param3 = param2;
  }
  param0[param3] = param1[param2];
});
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (param0, param1) {
  Object.defineProperty(param0, "default", {
    enumerable: true,
    value: param1
  });
} : function (param0, param1) {
  param0.default = param1;
});
var __importStar = this && this.__importStar || function () {
  var getOwnPropNames = function (param0) {
    getOwnPropNames = Object.getOwnPropertyNames || function (param0) {
      var items = [];
      for (var propKey in param0) if (Object.prototype.hasOwnProperty.call(param0, propKey)) {
        items[items.length] = propKey;
      }
      return items;
    };
    return getOwnPropNames(param0);
  };
  return function (param0) {
    if (param0 && param0.__esModule) {
      return param0;
    }
    var obj = {};
    if (param0 != null) {
      var items = getOwnPropNames(param0);
      for (var count = 0; count < items.length; count++) {
        if (items[count] !== "default") {
          __createBinding(obj, param0, items[count]);
        }
      }
    }
    __setModuleDefault(obj, param0);
    return obj;
  };
}();
Object.defineProperty(exports, '__esModule', {
  value: true
});
exports.CursorProViewProvider = undefined;
const vscode = __importStar(require("vscode"));
const client_1 = require("../api/client");
const extension_1 = require("../extension");
const account_1 = require('../utils/account');
const path = __importStar(require("path"));
const fs = __importStar(require('fs'));
const child_process_1 = require('child_process');
const util_1 = require("util");
const sqlite_1 = require('../utils/sqlite');
const execAsync = util_1.promisify(child_process_1.exec);
class CursorProViewProvider {
  constructor(extensionUri, context) {
    this._extensionUri = extensionUri;
    this._context = context;
    this._hostsPermissionGranted = false;
    this.SNI_PROXY_IP = "154.36.154.163";
    this.CURSOR_DOMAINS = ["api2.cursor.sh", "api3.cursor.sh"];
    this.HOSTS_MARKER_START = "# ===== CursorPro SNI Proxy Start =====";
    this.HOSTS_MARKER_END = "# ===== CursorPro SNI Proxy End =====";
    this._cachedCursorPath = null;
    this._onlineStatusUnsubscribe = client_1.onOnlineStatusChange(status => {
      this._postMessage({
        'type': "networkStatus",
        'online': status
      });
    });
  }
  resolveWebviewView(webviewView, context, token) {
    this._view = webviewView;
    webviewView.webview.options = {
      'enableScripts': true,
      'localResourceRoots': [this._extensionUri]
    };
    webviewView.webview.html = this._getHtmlContent(webviewView.webview);
    webviewView.webview.onDidReceiveMessage(async msg => {
      const config = {
        'WZyWQ': "没有写入权限",
        'ZXhkG': "seamlessRestored"
      };
      switch (msg.type) {
        case "activate":
          await this._handleActivate(msg.key);
          break;
        case "switch":
          await this._handleSwitch();
          break;
        case "resetMachineId":
          await this._handleResetMachineId();
          break;
        case "disableUpdate":
          await this._handleDisableUpdate();
          break;
        case "cleanEnv":
          await this._handleCleanEnv();
          break;
        case "disable":
          await this._handleDisable();
          break;
        case "toggleProxy":
          await this._handleToggleProxy(msg.enabled, msg.url);
          break;
        case 'getProxyStatus':
          await this._handleGetProxyStatus();
          break;
        case "getState":
          await this._sendState();
          break;
        case "retryConnect":
          await this._handleRetryConnect();
          break;
        case "getSeamlessStatus":
          await this._handleGetSeamlessStatus();
          break;
        case "injectSeamless":
          await this._handleInjectSeamless();
          break;
        case "restoreSeamless":
          await this._handleRestoreSeamless();
          break;
        case "toggleSeamless":
          await this._handleToggleSeamless(msg.enabled);
          break;
        case "getUserSwitchStatus":
          await this._handleGetUserSwitchStatus();
          break;
        case "manualSeamlessSwitch":
          await this._handleManualSeamlessSwitch();
          break;
        case "checkUsageBeforeSwitch":
          await this._handleCheckUsageBeforeSwitch(msg.email);
          break;
        case "confirmSwitch":
          await this._handleManualSeamlessSwitch();
          break;
        case "getCursorPath":
          await this._handleGetCursorPath();
          break;
        case 'getAccountUsage':
          await this._handleGetAccountUsage(msg.email);
          break;
        case "getAnnouncement":
          await this._handleGetAnnouncement();
          break;
        case "checkVersion":
          await this._handleCheckVersion();
          break;
        case "getCursorRunningPath":
          await this._handleGetCursorRunningPath();
          break;
        case "reloadWindow":
          vscode.commands.executeCommand("workbench.action.reloadWindow");
          break;
        case 'closeCursor':
          await account_1.closeCursor();
          break;
      }
    });
    this._sendState();
    this._checkKeyStatus();
  }
  async _checkKeyStatus() {
    const savedKey = this._context.globalState.get("cursorpro.key");
    if (!savedKey) {
      return;
    }
    try {
      const verifyResult = await client_1.verifyKey(savedKey);
      if (verifyResult.success && verifyResult.valid) {
        await this._context.globalState.update("cursorpro.expireDate", verifyResult.expire_date);
        await this._context.globalState.update("cursorpro.switchRemaining", verifyResult.switch_remaining);
        await this._context.globalState.update("cursorpro.switchLimit", verifyResult.switch_limit);
        this._postMessage({
          'type': "keyStatusChecked",
          'valid': true,
          'expireDate': verifyResult.expire_date,
          'switchRemaining': verifyResult.switch_remaining,
          'switchLimit': verifyResult.switch_limit
        });
      } else {
        this._postMessage({
          'type': "keyStatusChecked",
          'valid': false,
          'expired': true,
          'error': verifyResult.error || "激活码已过期或无效"
        });
      }
    } catch (modErr) {
      console.error("[CursorPro] 检查激活码状态失败:", modErr);
    }
  }
  async _handleActivate(key) {
    try {
      const isSeamlessInjected = await this._isSeamlessInjected();
      if (isSeamlessInjected) {
        this._postMessage({
          'type': "activated",
          'success': false,
          'error': "无感换号已启用，请先禁用后再更换授权码"
        });
        return;
      }
      this._cleanProxySettings();
      const verifyResult = await client_1.verifyKey(key);
      if (verifyResult.success && verifyResult.valid) {
        console.log("[CursorPro] 激活成功，后端返回:", {
          'expire_date': verifyResult.expire_date,
          'switch_remaining': verifyResult.switch_remaining,
          'switch_limit': verifyResult.switch_limit
        });
        await this._context.globalState.update("cursorpro.key", key);
        await this._context.globalState.update("cursorpro.expireDate", verifyResult.expire_date);
        await this._context.globalState.update("cursorpro.switchRemaining", verifyResult.switch_remaining);
        await this._context.globalState.update("cursorpro.switchLimit", verifyResult.switch_limit);
        this._postMessage({
          'type': "activated",
          'success': true,
          'key': key,
          'expireDate': verifyResult.expire_date,
          'switchRemaining': verifyResult.switch_remaining,
          'switchLimit': verifyResult.switch_limit
        });
        extension_1.showStatusBar();
        await this._handleGetUserSwitchStatus();
      } else {
        this._postMessage({
          'type': "activated",
          'success': false,
          'error': verifyResult.error || "授权码无效"
        });
      }
    } catch (activateErr) {
      this._postMessage({
        'type': "activated",
        'success': false,
        'error': "连接服务器失败"
      });
    }
  }
  async _handleSwitch() {
    const savedKey = this._context.globalState.get("cursorpro.key");
    if (!savedKey) {
      this._postMessage({
        'type': "showToast",
        'message': "请先激活授权码",
        'icon': '⚠️'
      });
      return;
    }
    try {
      const switchResult = await client_1.switchSeamlessToken(savedKey);
      if (switchResult.switched) {
        await this._context.globalState.update("cursorpro.switchRemaining", switchResult.switchRemaining);
        this._postMessage({
          'type': "switched",
          'success': true,
          'email': switchResult.email,
          'switchRemaining': switchResult.switchRemaining,
          'switchLimit': this._context.globalState.get("cursorpro.switchLimit") || 100
        });
        const condition = switchResult.switchRemaining ?? 0;
        this._postMessage({
          'type': "userSwitchStatus",
          'switchRemaining': condition,
          'canSwitch': condition > 0,
          'lockedAccount': switchResult.email ? {
            'email': switchResult.email
          } : null
        });
      } else {
        this._postMessage({
          'type': "switched",
          'success': false,
          'error': switchResult.message || '换号失败'
        });
      }
    } catch (switchErr) {
      this._postMessage({
        'type': 'switched',
        'success': false,
        'error': "连接服务器失败"
      });
    }
  }
  async _writeAccountToLocal(accountData) {
    try {
      const condition = process.env.APPDATA || '';
      const joinedPath = path.join(condition, "Cursor", "User", "globalStorage", "state.vscdb");
      const joinedPath1 = path.join(condition, "Cursor", "User", "globalStorage", 'storage.json');
      const joinedPath2 = path.join(condition, "Cursor", "machineid");
      if (fs.existsSync(joinedPath)) {
        const items = [];
        if (accountData.accessToken) {
          items.push(["cursorAuth/accessToken", accountData.accessToken]);
        }
        if (accountData.refreshToken) {
          items.push(["cursorAuth/refreshToken", accountData.refreshToken]);
        }
        if (accountData.email) {
          items.push(["cursorAuth/cachedEmail", accountData.email]);
        }
        if (accountData.membership_type) {
          items.push(["cursorAuth/stripeMembershipType", accountData.membership_type]);
        }
        if (accountData.sign_up_type) {
          items.push(["cursorAuth/cachedSignUpType", accountData.sign_up_type]);
        }
        if (accountData.serviceMachineId) {
          items.push(["storage.serviceMachineId", accountData.serviceMachineId]);
        }
        await sqlite_1.sqliteSetBatch(joinedPath, items);
        console.log("[CursorPro] SQLite 数据库已更新");
      }
      if (fs.existsSync(joinedPath1)) {
        const parsed = JSON.parse(fs.readFileSync(joinedPath1, 'utf-8'));
        if (accountData.machineId) {
          parsed["telemetry.machineId"] = accountData.machineId;
        }
        if (accountData.macMachineId) {
          parsed['telemetry.macMachineId'] = accountData.macMachineId;
        }
        if (accountData.devDeviceId) {
          parsed["telemetry.devDeviceId"] = accountData.devDeviceId;
        }
        if (accountData.sqmId) {
          parsed["telemetry.sqmId"] = accountData.sqmId;
        }
        fs.writeFileSync(joinedPath1, JSON.stringify(parsed, null, 4));
        console.log("[CursorPro] storage.json 已更新");
      }
      if (accountData.machineId) {
        fs.writeFileSync(joinedPath2, accountData.machineId);
        console.log("[CursorPro] machineid 文件已更新");
      }
      if (accountData.registryGuid && process.platform === "win32") {
        try {
          const result = 'reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + accountData.registryGuid + '" /f';
          await execAsync(result);
          console.log("[CursorPro] 注册表 MachineGuid 已更新");
        } catch (parseErr) {
          console.warn("[CursorPro] 注册表写入失败（可能需要管理员权限）:", parseErr);
        }
      }
      return true;
    } catch (writeErr) {
      console.error("[CursorPro] 写入本地失败:", writeErr);
      vscode.window.showErrorMessage("写入失败: " + writeErr);
      return false;
    }
  }
  async _handleReset() {
    await this._context.globalState.update("cursorpro.key", undefined);
    await this._context.globalState.update("cursorpro.expireDate", undefined);
    await this._context.globalState.update("cursorpro.switchRemaining", undefined);
    extension_1.hideStatusBar();
    this._postMessage({
      'type': 'reset',
      'success': true
    });
    vscode.window.showInformationMessage("插件已重置");
  }
  async _handleDisable() {
    await this._handleReset();
    vscode.window.showInformationMessage("插件已停用");
  }
  async _checkAdminPrivilege() {
    if (process.platform !== "win32") {
      return true;
    }
    try {
      await execAsync('reg query "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid 2>nul');
      const hostEntry = await execAsync("net session 2>nul").catch(() => ({
        'stdout': '',
        'stderr': 'error'
      }));
      return !hostEntry.stderr;
    } catch (jsonErr) {
      return false;
    }
  }
  async _handleResetMachineId() {
    try {
      const platform = process.platform;
      if (platform === 'win32') {
        const adminprivilegeResult = await this._checkAdminPrivilege();
        if (!adminprivilegeResult) {
          this._postMessage({
            'type': "adminPermissionRequired"
          });
          return;
        }
      }
      const hostLine = account_1.getCursorPaths();
      const {
        dbPath: charIdx,
        storagePath: lineItem,
        machineidPath: lineIdx
      } = hostLine;
      const module = require("crypto");
      const str = module.randomBytes(32).toString("hex");
      const str1 = module.randomBytes(32).toString("hex");
      const proxyLine = module.randomUUID();
      const result = '{' + module.randomUUID().toUpperCase() + '}';
      let count = 0;
      let items = [];
      if (fs.existsSync(lineItem)) {
        let num = 3;
        while (num > 0) {
          try {
            const parsed = JSON.parse(fs.readFileSync(lineItem, "utf-8"));
            parsed["telemetry.machineId"] = str;
            parsed["telemetry.macMachineId"] = str1;
            parsed["telemetry.devDeviceId"] = proxyLine;
            parsed["telemetry.sqmId"] = result;
            fs.writeFileSync(lineItem, JSON.stringify(parsed, null, 4));
            console.log("[CursorPro] storage.json 已更新");
            count++;
            break;
          } catch (readErr) {
            num--;
            if (num === 0) {
              console.warn("[CursorPro] storage.json 更新失败:", readErr.message);
              items.push("storage.json");
            } else {
              await new Promise(param0 => setTimeout(param0, 100));
            }
          }
        }
      }
      {
        let num = 3;
        while (num > 0) {
          try {
            const dirPath = path.dirname(lineIdx);
            if (!fs.existsSync(dirPath)) {
              fs.mkdirSync(dirPath, {
                'recursive': true
              });
            }
            fs.writeFileSync(lineIdx, str);
            console.log("[CursorPro] machineid 文件已更新");
            count++;
            break;
          } catch (writeErr) {
            num--;
            if (num === 0) {
              console.warn("[CursorPro] machineid 更新失败:", writeErr.message);
              items.push("machineid");
            } else {
              await new Promise(param0 => setTimeout(param0, 100));
            }
          }
        }
      }
      if (fs.existsSync(charIdx)) {
        let num = 3;
        while (num > 0) {
          try {
            const proxyEntry = module.randomUUID();
            const newHostsContent = await sqlite_1.sqliteSetBatch(charIdx, [['storage.serviceMachineId', proxyEntry]]);
            if (newHostsContent) {
              console.log("[CursorPro] SQLite 数据库已更新");
              count++;
              break;
            } else {
              throw new Error("sqliteSetBatch 返回 false");
            }
          } catch (grantErr) {
            num--;
            if (num === 0) {
              console.warn("[CursorPro] SQLite 更新失败:", grantErr.message);
              items.push("SQLite");
            } else {
              await new Promise(param0 => setTimeout(param0, 500));
            }
          }
        }
      }
      if (platform === "win32") {
        const hostsLines = module.randomUUID();
        try {
          await execAsync('reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + hostsLines + '" /f');
          console.log("[CursorPro] 注册表 MachineGuid 已更新");
          count++;
        } catch (regWriteErr) {
          console.warn("[CursorPro] 注册表更新失败（需要管理员权限），已跳过");
          items.push("注册表");
        }
      }
      if (count >= 2) {
        this._postMessage({
          'type': "machineIdReset",
          'success': true,
          'needRestart': true,
          'message': items.length > 0 ? "机器码重置成功（" + items.join(", ") + " 更新失败，不影响使用）" : "机器码重置成功"
        });
      } else {
        this._postMessage({
          'type': "showToast",
          'message': "重置部分失败: " + items.join(", ") + "。请先完全关闭 Cursor 再试",
          'icon': '⚠️'
        });
      }
    } catch (hostsErr) {
      this._postMessage({
        'type': "showToast",
        'message': "重置机器码失败: " + hostsErr,
        'icon': '❌'
      });
    }
  }
  _generateRandomMAC() {
    const module = require("crypto");
    const dbPath = module.randomBytes(6);
    dbPath[0] = (dbPath[0] | 2) & 254;
    return Array.from(dbPath).map(item => item.toString(16).padStart(2, '0')).join(':');
  }
  async _handleDisableUpdate() {
    try {
      const condition = process.env.LOCALAPPDATA || '';
      const joinedPath = path.join(condition, "cursor-updater");
      if (fs.existsSync(joinedPath)) {
        if (fs.statSync(joinedPath).isDirectory()) {
          fs.rmSync(joinedPath, {
            'recursive': true,
            'force': true
          });
        } else {
          fs.unlinkSync(joinedPath);
        }
      }
      fs.writeFileSync(joinedPath, '');
      this._postMessage({
        'type': 'showToast',
        'message': "已禁用 Cursor 自动更新",
        'icon': '✅'
      });
    } catch (toggleErr) {
      this._postMessage({
        'type': "showToast",
        'message': "禁用自动更新失败: " + toggleErr,
        'icon': '❌'
      });
    }
  }
  async _handleCleanEnv() {
    try {
      if (process.platform === "win32") {
        await execAsync("taskkill /F /IM Cursor.exe").catch(() => {});
      } else {
        await execAsync("pkill -f Cursor").catch(() => {});
      }
      await new Promise(param0 => setTimeout(param0, 2000));
      const condition = process.env.APPDATA || '';
      const condition1 = process.env.LOCALAPPDATA || '';
      const condition2 = process.env.HOME || process.env.USERPROFILE || '';
      let count = 0;
      if (process.platform === "win32") {
        const items = [path.join(condition, "Cursor"), path.join(condition1, "Cursor"), path.join(condition1, "cursor-updater"), path.join(condition2, ".cursor")];
        for (const macPath of items) {
          try {
            if (fs.existsSync(macPath)) {
              fs.rmSync(macPath, {
                'recursive': true,
                'force': true
              });
              count++;
              console.log("[CursorPro] 已清理: " + macPath);
            }
          } catch (statusErr) {
            console.warn("[CursorPro] 清理失败: " + macPath, statusErr);
          }
        }
      } else {
        if (process.platform === "darwin") {
          const items = [path.join(condition2, "Library", "Application Support", "Cursor"), path.join(condition2, "Library", "Caches", "Cursor"), path.join(condition2, "Library", "Logs", "Cursor"), path.join(condition2, 'Library', "Application Support", 'Caches', "cursor-updater"), path.join(condition2, ".cursor")];
          for (const storagePath of items) {
            try {
              if (fs.existsSync(storagePath)) {
                fs.rmSync(storagePath, {
                  'recursive': true,
                  'force': true
                });
                count++;
              }
            } catch (pathErr) {
              console.warn("[CursorPro] 清理失败: " + storagePath, pathErr);
            }
          }
        } else {
          const items = [path.join(condition2, ".config", "Cursor"), path.join(condition2, ".cache", "Cursor"), path.join(condition2, ".local", "share", "Cursor"), path.join(condition2, ".cursor")];
          for (const machineIdPath of items) {
            try {
              if (fs.existsSync(machineIdPath)) {
                fs.rmSync(machineIdPath, {
                  'recursive': true,
                  'force': true
                });
                count++;
              }
            } catch (seamlessErr) {
              console.warn("[CursorPro] 清理失败: " + machineIdPath, seamlessErr);
            }
          }
        }
      }
      vscode.window.showInformationMessage("✅ Cursor 环境清理完成！已清理 " + count + " 个目录。请重新启动 Cursor。");
    } catch (cleanErr) {
      vscode.window.showErrorMessage("清理失败: " + cleanErr);
    }
  }
  _cleanProxySettings() {
    try {
      const platform = process.platform;
      const condition = process.env.HOME || process.env.USERPROFILE || '';
      let settingsPath;
      if (platform === "win32") {
        const condition1 = process.env.APPDATA || '';
        settingsPath = path.join(condition1, "Cursor", "User", "settings.json");
      } else {
        if (platform === "darwin") {
          settingsPath = path.join(condition, "Library", "Application Support", "Cursor", 'User', "settings.json");
        } else {
          settingsPath = path.join(condition, ".config", "Cursor", "User", "settings.json");
        }
      }
      if (!fs.existsSync(settingsPath)) {
        return;
      }
      const fileContent = fs.readFileSync(settingsPath, 'utf-8');
      let settingsObj;
      try {
        settingsObj = JSON.parse(fileContent);
      } catch {
        return;
      }
      const items = ["http.proxy", "http.proxyStrictSSL", "http.proxySupport", "cursor.general.disableHttp2", "http.noProxy"];
      let isFalse = false;
      for (const tokenData of items) {
        if (tokenData in settingsObj) {
          isFalse = true;
          delete settingsObj[tokenData];
        }
      }
      if (isFalse) {
        fs.writeFileSync(settingsPath, JSON.stringify(settingsObj, null, 4), "utf-8");
        console.log("[CursorPro] 已清理 settings.json 中的旧代理配置");
      }
    } catch (proxyErr) {
      console.warn("[CursorPro] 清理 settings.json 代理配置失败:", proxyErr);
    }
  }
  _getHostsPath() {
    return process.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : '/etc/hosts';
  }
  _readHostsFile() {
    try {
      const accountInfo = this._getHostsPath();
      if (fs.existsSync(accountInfo)) {
        return fs.readFileSync(accountInfo, "utf-8");
      }
    } catch (readErr) {
      console.error("[CursorPro] Read hosts error:", readErr);
    }
    return '';
  }
  _hasHostsConfig() {
    const switchResponse = this._readHostsFile();
    return switchResponse.includes(this.HOSTS_MARKER_START);
  }
  async _grantHostsWritePermission() {
    if (process.platform !== "win32") {
      return false;
    }
    try {
      const content = this._getHostsPath();
      const condition = process.env.USERNAME || '';
      if (!condition) {
        return false;
      }
      const replaced = content.replace(/\\/g, "\\\\");
      const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command icacls \\\"" + replaced + '\" /grant ' + condition + ":M' -Verb RunAs -Wait\"";
      await execAsync(result);
      this._hostsPermissionGranted = true;
      console.log("[CursorPro] Hosts file permission granted to user:", condition);
      return true;
    } catch (switchErr) {
      console.error("[CursorPro] Grant hosts permission error:", switchErr);
      return false;
    }
  }
  async _writeHostsFile(content) {
    const content1 = this._getHostsPath();
    try {
      if (process.platform === "win32") {
        let isFalse = false;
        try {
          fs.writeFileSync(content1, content, "utf-8");
          isFalse = true;
        } catch (writeErr1) {
          console.log("[CursorPro] Direct write failed, trying to grant permission");
        }
        if (!isFalse) {
          if (!this._hostsPermissionGranted) {
            const lockedInfo = await this._grantHostsWritePermission();
            if (lockedInfo) {
              try {
                fs.writeFileSync(content1, content, "utf-8");
                remainingCount = true;
              } catch (writeErr2) {
                console.log("[CursorPro] Write still failed after permission grant");
              }
            }
          }
        }
        if (!isFalse) {
          const joinedPath = path.join(process.env.TEMP || '', "cursorpro_hosts_temp.txt");
          fs.writeFileSync(joinedPath, content, "utf-8");
          const replaced = joinedPath.replace(/\\/g, "\\\\");
          const replaced1 = content1.replace(/\\/g, "\\\\");
          const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command Copy-Item -Path \\\"" + replaced + '\" -Destination \"' + replaced1 + "\\\" -Force' -Verb RunAs -Wait\"";
          await execAsync(result);
          try {
            fs.unlinkSync(joinedPath);
          } catch {}
        }
        try {
          await execAsync("ipconfig /flushdns");
          console.log("[CursorPro] Windows DNS 缓存已刷新");
        } catch (resetErr) {
          console.warn("[CursorPro] Windows DNS 刷新失败:", resetErr);
        }
      } else {
        if (process.platform === "darwin") {
          const pathStr = "/tmp/hosts_cursor_temp";
          fs.writeFileSync(pathStr, content, "utf-8");
          const content1 = "do shell script \"cp '" + pathStr + "' '" + content1 + "' && rm '" + pathStr + "' && dscacheutil -flushcache && killall -HUP mDNSResponder\" with administrator privileges";
          await execAsync('osascript -e "' + content1.replace(/"/g, "\\\"") + "\"");
        } else {
          fs.writeFileSync(content1, content, "utf-8");
        }
      }
      return true;
    } catch (disableErr) {
      console.error("[CursorPro] Write hosts error:", disableErr);
      return false;
    }
  }
  async _handleToggleProxy(enabled, silent) {
    try {
      if (enabled) {
        const savedKey = this._context.globalState.get("cursorpro.key");
        const expireDate = this._context.globalState.get('cursorpro.expireDate');
        if (!savedKey) {
          this._postMessage({
            'type': "proxyUpdated",
            'success': false,
            'error': "请先激活授权码"
          });
          this._postMessage({
            'type': "showToast",
            'message': '请先激活授权码',
            'icon': '⚠️'
          });
          return;
        }
        if (expireDate) {
          const resetResponse = new Date(expireDate).getTime();
          if (Date.now() > resetResponse) {
            this._postMessage({
              'type': "proxyUpdated",
              'success': false,
              'error': "授权码已过期，无法开启免魔法"
            });
            this._postMessage({
              'type': "showToast",
              'message': "授权码已过期，无法开启免魔法",
              'icon': '⚠️'
            });
            return;
          }
        }
      }
      this._cleanProxySettings();
      let content = this._readHostsFile();
      const index = content.indexOf(this.HOSTS_MARKER_START);
      const index1 = content.indexOf(this.HOSTS_MARKER_END);
      if (index !== -1 && index1 !== -1) {
        content = content.substring(0, index) + content.substring(index1 + this.HOSTS_MARKER_END.length);
      }
      content = content.replace(/\n{3,}/g, "\n\n").trim();
      if (enabled) {
        const joinedPath = this.CURSOR_DOMAINS.map(item => this.SNI_PROXY_IP + " " + item).join("\n");
        const result = "\n\n" + this.HOSTS_MARKER_START + "\n" + joinedPath + "\n" + this.HOSTS_MARKER_END + "\n";
        content += result;
      }
      const disableResponse = await this._writeHostsFile(content);
      if (disableResponse) {
        await client_1.updateProxyConfig(enabled, this.SNI_PROXY_IP);
        this._postMessage({
          'type': "proxyUpdated",
          'success': true,
          'enabled': enabled,
          'url': this.SNI_PROXY_IP
        });
        this._postMessage({
          'type': "showToast",
          'message': enabled ? "免魔法已开启" : "免魔法已关闭",
          'icon': '✅'
        });
      } else {
        this._postMessage({
          'type': "proxyUpdated",
          'success': false,
          'error': "修改 hosts 文件失败，请确保有管理员权限"
        });
        this._postMessage({
          'type': "showToast",
          'message': "需要管理员权限修改 hosts 文件",
          'icon': '⚠️'
        });
      }
    } catch (updateErr) {
      console.error("[CursorPro] Toggle proxy error:", updateErr);
      this._postMessage({
        'type': "proxyUpdated",
        'success': false,
        'error': "更新配置失败"
      });
    }
  }
  async _handleGetProxyStatus() {
    try {
      const enabled = this._hasHostsConfig();
      this._postMessage({
        'type': "proxyStatus",
        'enabled': enabled,
        'url': enabled ? this.SNI_PROXY_IP : ''
      });
    } catch (envErr) {
      console.error("[CursorPro] Get proxy status error:", envErr);
      this._postMessage({
        'type': "proxyStatus",
        'enabled': false,
        'url': ''
      });
    }
  }
  async _handleGetSeamlessStatus() {
    try {
      const workbenchPath = await this._getWorkbenchPathAsync();
      let isInjected = false;
      if (workbenchPath && fs.existsSync(workbenchPath)) {
        const fileContent = fs.readFileSync(workbenchPath, 'utf-8');
        isInjected = this._checkInjected(fileContent);
      }
      this._postMessage({
        'type': "seamlessStatus",
        'is_injected': isInjected,
        'workbench_path': workbenchPath || '未找到'
      });
    } catch (e1) {
      this._postMessage({
        'type': "seamlessStatus",
        'is_injected': false,
        'error': "检测状态失败"
      });
    }
  }
  async _getCursorInstallPath() {
    if (this._cachedCursorPath) {
      return this._cachedCursorPath;
    }
    const config = vscode.workspace.getConfiguration("cursorpro");
    const configValue = config.get("cursorPath");
    if (configValue && fs.existsSync(configValue)) {
      console.log("[CursorPro] 使用用户配置的 Cursor 路径:", configValue);
      this._cachedCursorPath = configValue;
      return configValue;
    }
    const platform = process.platform;
    let result = null;
    try {
      if (platform === "win32") {
        try {
          const {
            stdout: wmicOut
          } = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
          if (wmicOut) {
            const matchResult = wmicOut.match(/ExecutablePath=(.+)/);
            if (matchResult && matchResult[1]) {
              const trimmed = matchResult[1].trim();
              result = path.dirname(trimmed);
            }
          }
        } catch (e2) {
          console.log("[CursorPro] WMIC 获取路径失败");
        }
        if (!result) {
          try {
            const {
              stdout: psOut
            } = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
            if (psOut && psOut.trim()) {
              result = path.dirname(psOut.trim());
            }
          } catch (e3) {
            console.log("[CursorPro] PowerShell Get-Process 获取路径失败");
          }
        }
        if (!result) {
          try {
            const {
              stdout: regOut
            } = await execAsync("reg query \"HKCUSoftwareMicrosoftWindowsCurrentVersionUninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
            if (regOut && regOut.trim()) {
              const matchResult = regOut.match(/InstallLocation\s+REG_SZ\s+(.+)/);
              if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
                result = matchResult[1].trim();
              }
            }
          } catch (e4) {
            console.log("[CursorPro] 注册表方法1获取路径失败");
          }
        }
        if (!result) {
          try {
            const {
              stdout: regOut2
            } = await execAsync("reg query \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
            if (regOut2 && regOut2.trim()) {
              const matchResult = regOut2.match(/InstallLocation\s+REG_SZ\s+(.+)/);
              if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
                cursorPath = matchResult[1].trim();
              }
            }
          } catch (e5) {
            console.log("[CursorPro] 注册表方法2获取路径失败");
          }
        }
        if (!result) {
          try {
            const joinedPath = path.join(process.env.APPDATA || '', "Microsoft", "Windows", "Start Menu", 'Programs', "Cursor.lnk");
            const joinedPath1 = path.join("C:\\ProgramData", "Microsoft", 'Windows', "Start Menu", "Programs", "Cursor.lnk");
            for (const content of [joinedPath, joinedPath1]) {
              if (fs.existsSync(content)) {
                const {
                  stdout: lnkOut
                } = await execAsync("powershell -Command \"(New-Object -ComObject WScript.Shell).CreateShortcut('" + content.replace(/'/g, "''") + "').TargetPath\"");
                if (lnkOut && lnkOut.trim() && fs.existsSync(lnkOut.trim())) {
                  result = path.dirname(lnkOut.trim());
                  break;
                }
              }
            }
          } catch (e6) {
            console.log("[CursorPro] 快捷方式解析获取路径失败");
          }
        }
        if (!result) {
          try {
            const {
              stdout: whereOut
            } = await execAsync("where cursor 2>nul");
            if (whereOut && whereOut.trim()) {
              const parts = whereOut.trim().split("\n");
              for (const str of parts) {
                const trimmed = str.trim();
                if (trimmed && fs.existsSync(trimmed)) {
                  cursorPath = path.dirname(trimmed);
                  break;
                }
              }
            }
          } catch (whereErr) {
            console.log("[CursorPro] where 命令获取路径失败");
          }
        }
        if (!result) {
          const condition = process.env.LOCALAPPDATA || '';
          const condition1 = process.env.USERPROFILE || '';
          const condition2 = process.env.ProgramFiles || "C:\\Program Files";
          const condition3 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
          const items = [path.join(condition, "Programs", "Cursor"), path.join(condition, "Programs", "cursor"), path.join(condition1, "AppData", "Local", "Programs", "Cursor"), path.join(condition2, "Cursor"), path.join(condition3, "Cursor"), path.join(condition, "Cursor"), path.join(condition, "cursor")];
          for (const cursorDbPath of items) {
            if (cursorDbPath && fs.existsSync(cursorDbPath)) {
              result = cursorDbPath;
              break;
            }
          }
        }
      } else {
        if (platform === "darwin") {
          try {
            const {
              stdout: dirEntry
            } = await execAsync("lsof -c Cursor 2>/dev/null | grep \"txt\" | grep -i \"Cursor.app\" | head -1 | awk '{print $9}'");
            if (dirEntry && dirEntry.trim()) {
              const matchResult = dirEntry.trim().match(/(.+\.app)/);
              if (matchResult) {
                result = matchResult[1];
              }
            }
          } catch (e) {}
          if (!result) {
            try {
              const {
                stdout: fileItem
              } = await execAsync("ps -eo comm,args | grep -i \"[C]ursor\" | grep -v \"grep\" | head -1");
              if (fileItem && fileItem.trim()) {
                const matchResult = fileItem.match(/(\/.+\.app)/);
                if (matchResult) {
                  cursorPath = matchResult[1];
                }
              }
            } catch (findErr) {
              console.warn("[CursorPro] macOS 获取进程路径失败:", findErr);
            }
          }
          if (!result) {
            try {
              const {
                stdout: childPath
              } = await execAsync("mdfind \"kMDItemCFBundleIdentifier == 'com.todesktop.*cursor*'\" 2>/dev/null | head -1");
              if (childPath && childPath.trim() && fs.existsSync(childPath.trim())) {
                result = childPath.trim();
              }
            } catch (e) {}
          }
          if (!result && fs.existsSync('/Applications/Cursor.app')) {
            result = "/Applications/Cursor.app";
          }
        } else {
          try {
            const {
              stdout: pathItem
            } = await execAsync('pgrep -f "[c]ursor" | head -1');
            const condition = pathItem && pathItem.trim();
            if (condition) {
              const {
                stdout: subDir
              } = await execAsync("readlink -f /proc/" + condition + "/exe 2>/dev/null");
              if (subDir && subDir.trim()) {
                const trimmed = subDir.trim();
                cursorPath = path.dirname(trimmed);
                if (result.endsWith("/bin")) {
                  result = path.dirname(result);
                }
              }
            }
          } catch (e) {}
          if (!result) {
            try {
              const {
                stdout: subItem
              } = await execAsync("which cursor 2>/dev/null");
              if (subItem && subItem.trim()) {
                const execResult = await execAsync('readlink -f "' + subItem.trim() + '" 2>/dev/null');
                if (execResult.stdout && execResult.stdout.trim()) {
                  cursorPath = path.dirname(execResult.stdout.trim());
                  if (result.endsWith('/bin')) {
                    cursorPath = path.dirname(result);
                  }
                }
              }
            } catch (checkErr) {
              console.warn("[CursorPro] Linux 获取进程路径失败:", checkErr);
            }
          }
          if (!result) {
            const items = ["/opt/Cursor", "/opt/cursor", "/usr/share/cursor", "/usr/lib/cursor", path.join(process.env.HOME || '', ".local/share/cursor"), path.join(process.env.HOME || '', "Applications/cursor")];
            for (const statusInfo of items) {
              if (fs.existsSync(statusInfo)) {
                result = statusInfo;
                break;
              }
            }
          }
        }
      }
    } catch (injectErr) {
      console.error("[CursorPro] 获取 Cursor 安装路径失败:", injectErr);
    }
    if (result) {
      this._cachedCursorPath = result;
    }
    return result;
  }
  _getWorkbenchPath() {
    return this._getWorkbenchPathSync();
  }
  _getWorkbenchPathSync() {
    const platform = process.platform;
    if (this._cachedCursorPath) {
      let entry;
      if (platform === "darwin") {
        entry = path.join(this._cachedCursorPath, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
      } else {
        entry = path.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
      }
      if (fs.existsSync(entry)) {
        return entry;
      }
    }
    if (platform === 'win32') {
      return null;
    }
    let items = [];
    if (platform === "darwin") {
      items = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
    } else {
      items = ["/opt/Cursor/resources/app/out/vs/workbench/workbench.desktop.main.js", '/usr/share/cursor/resources/app/out/vs/workbench/workbench.desktop.main.js'];
    }
    for (const switchInfo of items) {
      if (fs.existsSync(switchInfo)) {
        return switchInfo;
      }
    }
    return null;
  }
  async _getWorkbenchPathAsync() {
    const platform = process.platform;
    const cursorPath = await this._getCursorInstallPath();
    if (cursorPath) {
      let workbenchSubPath;
      if (platform === "darwin") {
        workbenchSubPath = path.join(cursorPath, "Contents", "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
      } else {
        workbenchSubPath = path.join(cursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
      }
      if (fs.existsSync(workbenchSubPath)) {
        return workbenchSubPath;
      }
    }
    return this._getWorkbenchPathSync();
  }
  _checkInjected(cbArg) {
    return cbArg.includes("/*i0*/") || cbArg.includes('/*i1s*/');
  }
  async _isSeamlessInjected() {
    try {
      const workbenchPath = await this._getWorkbenchPathAsync();
      if (workbenchPath && fs.existsSync(workbenchPath)) {
        const fileContent = fs.readFileSync(workbenchPath, "utf-8");
        return this._checkInjected(fileContent);
      }
      return false;
    } catch (restoreErr) {
      console.error("[CursorPro] 检测无感换号状态失败:", restoreErr);
      return false;
    }
  }
  _getInjectionConfig(msgData, dataArg) {
    return [{
      'name': "注入点0: 完整性检查绕过",
      'scode': "_showNotification(){",
      'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
      'restore': {
        'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
        'replace_with': "_showNotification(){"
      }
    }, {
      'name': "注入点1: 核心模块初始化",
      'scode': "this.database.getItems()))",
      'replacement': "this.database.getItems()))/*i1s*/;await(async function(e){if(e.get('releaseNotes/lastVersion')){window.store=e;window.__cpKey='CursorPro2024!@#';window.__cpEnc=function(t){var k=window.__cpKey,r='';for(var i=0;i<t.length;i++)r+=String.fromCharCode(t.charCodeAt(i)^k.charCodeAt(i%k.length));return btoa(r)};window.__cpDec=function(t){var k=window.__cpKey,d=atob(t),r='';for(var i=0;i<d.length;i++)r+=String.fromCharCode(d.charCodeAt(i)^k.charCodeAt(i%k.length));return r};window.__cpGet=function(){try{var d=localStorage.getItem('__cp_token');return d?JSON.parse(window.__cpDec(d)):null}catch(e){return null}};window.__cpSet=function(data){try{localStorage.setItem('__cp_token',window.__cpEnc(JSON.stringify(data)))}catch(e){}};window.__cpApi='" + msgData + "';window.__cpUserKey='" + dataArg + "';window.__cpVersion=0;console.log('[CP] Initialized with key:','" + dataArg + "'.substring(0,15)+'...')}})(this)/*i1e*/",
      'restore': {
        'find_start': "/*i1s*/",
        'find_end': "/*i1e*/"
      }
    }, {
      'name': "注入点2: 启动时Token同步",
      'scode': "/*i1e*/",
      'replacement': "/*i1e*//*i2s*/;(function(){window.__cpSyncing=false;window.__cpSync=function(){if(window.__cpSyncing){console.log('[CP] Sync already in progress, skip');return}var userKey=window.__cpUserKey;if(!userKey){console.log('[CP] No userKey, skip sync');return}window.__cpSyncing=true;console.log('[CP] Sync with key:',userKey.substring(0,15)+'...');fetch(window.__cpApi+'/api/seamless/get-token?userKey='+encodeURIComponent(userKey)).then(function(r){return r.json()}).then(function(d){window.__cpSyncing=false;if(d.error){console.error('[CP] Sync error:',d.error);return}if(d&&d.accessToken){var oldToken=window.__cpGet();var needUpdate=!oldToken||oldToken.email!==d.email||window.__cpVersion!==d.switchVersion;if(needUpdate){window.__cpVersion=d.switchVersion||0;window.__cpSet({accessToken:d.accessToken,refreshToken:d.refreshToken||'',email:d.email||'',machineIds:d.machineIds||null,switchRemaining:d.switchRemaining,switchVersion:d.switchVersion||0});window.store.set('cursorAuth/accessToken',d.accessToken,-1);if(d.refreshToken)window.store.set('cursorAuth/refreshToken',d.refreshToken,-1);if(d.email)window.store.set('cursor.email',d.email,-1);if(d.is_new&&d.machineIds){if(d.machineIds.devDeviceId)window.store.set('telemetry.devDeviceId',d.machineIds.devDeviceId,-1);if(d.machineIds.machineId)window.store.set('telemetry.machineId',d.machineIds.machineId,-1);if(d.machineIds.macMachineId)window.store.set('telemetry.macMachineId',d.machineIds.macMachineId,-1);if(d.machineIds.sqmId)window.store.set('telemetry.sqmId',d.machineIds.sqmId,-1)}console.log('[CP] Token UPDATED:',d.email,'v'+d.switchVersion)}}}).catch(function(e){window.__cpSyncing=false;console.error('[CP] Sync error:',e)})};console.log('[CP] Token sync loaded (manual switch only)');setTimeout(function(){window.__cpSync()},2000);setInterval(function(){window.__cpSync()},10000)})()/*i2e*/",
      'restore': {
        'find_start': "/*i2s*/",
        'find_end': "/*i2e*/"
      }
    }];
  }
  async _handleInjectSeamless() {
    try {
      const savedKey = this._context.globalState.get("cursorpro.key");
      if (!savedKey) {
        this._postMessage({
          'type': "seamlessInjected",
          'success': false,
          'error': "请先激活授权码"
        });
        return;
      }
      const status = await client_1.getUserSwitchStatus(savedKey);
      if (!status.valid) {
        this._postMessage({
          'type': "seamlessInjected",
          'success': false,
          'error': status.error || "授权码无效"
        });
        return;
      }
      const workbenchPath = await this._getWorkbenchPathAsync();
      if (!workbenchPath) {
        this._postMessage({
          'type': "seamlessInjected",
          'success': false,
          'error': "启用失败"
        });
        return;
      }
      const result = workbenchPath + ".backup";
      const not = !this._context.globalState.get("cursorpro.seamlessInjected");
      if (not && fs.existsSync(result)) {
        console.log("[CursorPro] 首次启用，从备份恢复干净的 workbench 文件");
        try {
          fs.copyFileSync(result, workbenchPath);
          console.log("[CursorPro] 备份恢复成功");
        } catch (usageErr) {
          console.error("[CursorPro] 备份恢复失败:", usageErr);
        }
      }
      let fileContent = fs.readFileSync(workbenchPath, 'utf-8');
      if (this._checkInjected(fileContent)) {
        this._postMessage({
          'type': "showToast",
          'message': "已启用",
          'icon': '✅'
        });
        return;
      }
      if (!fs.existsSync(result)) {
        fs.copyFileSync(workbenchPath, result);
        console.log("[CursorPro] 创建备份文件");
      }
      const announceList = client_1.getApiUrl();
      const latestAnnounce = this._getInjectionConfig(announceList, savedKey);
      const items = [];
      const items1 = [];
      for (const linuxPath of latestAnnounce) {
        if (fileContent.includes(linuxPath.scode)) {
          fileContent = fileContent.replace(linuxPath.scode, linuxPath.replacement);
          items.push(linuxPath.name);
        } else {
          items1.push(linuxPath.name);
        }
      }
      if (items.length === 0) {
        console.error("[CursorPro] 注入失败，未找到任何注入点");
        console.error("[CursorPro] 文件路径:", workbenchPath);
        console.error("[CursorPro] 文件大小:", fileContent.length);
        console.error("[CursorPro] 未找到的注入点:", items1);
        const versionResponse = fileContent.includes("_showNotification");
        const latestVersion = fileContent.includes("getItems()");
        console.error("[CursorPro] 包含 _showNotification:", versionResponse);
        console.error("[CursorPro] 包含 getItems():", latestVersion);
        this._postMessage({
          'type': "seamlessInjected",
          'success': false,
          'error': "Cursor 版本不兼容，注入点未找到",
          'details': "路径: " + workbenchPath
        });
        return;
      }
      console.log("[CursorPro] 注入成功，应用的注入点:", items);
      if (items1.length > 0) {
        console.warn("[CursorPro] 未找到的注入点:", items1);
      }
      try {
        fs.writeFileSync(workbenchPath, fileContent, "utf-8");
      } catch (writeErr) {
        console.error("[CursorPro] 写入文件失败:", writeErr);
        if (writeErr.code === "EPERM" || writeErr.code === "EACCES" || writeErr.code === "EROFS") {
          const platform = process.platform;
          let errorMsg = "没有写入权限";
          if (platform === "darwin") {
            errorMsg = "没有写入权限，请在终端执行: sudo chmod -R 777 /Applications/Cursor.app";
          } else if (platform === "linux") {
            errorMsg = "没有写入权限，请使用 sudo 权限运行或修改文件权限";
          }
          this._postMessage({
            'type': "seamlessInjected",
            'success': false,
            'error': errorMsg,
            'needAdmin': true,
            'path': workbenchPath
          });
          return;
        }
        throw writeErr;
      }
      await this._context.globalState.update("cursorpro.seamlessInjected", true);
      this._postMessage({
        'type': 'seamlessInjected',
        'success': true,
        'applied': items,
        'needRestart': true,
        'message': "无感换号已启用"
      });
    } catch (appDir) {
      console.error("[CursorPro] Inject error:", appDir);
      if (appDir.code === "EPERM" || appDir.code === "EACCES") {
        const errorMsg = "没有写入权限";
        this._postMessage({
          'type': "seamlessInjected",
          'success': false,
          'error': errorMsg,
          'needAdmin': true
        });
        return;
      }
      this._postMessage({
        'type': "seamlessInjected",
        'success': false,
        'error': appDir.message || '注入失败'
      });
    }
  }
  async _handleRestoreSeamless() {
    try {
      const workbenchPath = await this._getWorkbenchPathAsync();
      if (!workbenchPath) {
        this._postMessage({
          'type': "seamlessRestored",
          'success': false,
          'error': '未找到Cursor安装目录'
        });
        return;
      }
      let fileContent = fs.readFileSync(workbenchPath, "utf-8");
      if (!this._checkInjected(fileContent)) {
        return;
      }
      fileContent = fileContent.replace("_showNotification(){/*i0*/}_showNotificationOld(){", "_showNotification(){");
      const index = fileContent.indexOf("/*i1s*/");
      const index1 = fileContent.indexOf("/*i1e*/");
      if (index !== -1 && index1 !== -1) {
        fileContent = fileContent.substring(0, index) + fileContent.substring(index1 + 7);
      }
      const index2 = fileContent.indexOf("/*i2s*/");
      const index3 = fileContent.indexOf("/*i2e*/");
      if (index2 !== -1 && index3 !== -1) {
        fileContent = fileContent.substring(0, index2) + fileContent.substring(index3 + 7);
      }
      try {
        fs.writeFileSync(workbenchPath, fileContent, "utf-8");
      } catch (writeErr) {
        if (writeErr.code === "EPERM" || writeErr.code === "EACCES") {
          const errorMsg = "没有写入权限";
          this._postMessage({
            'type': "seamlessRestored",
            'success': false,
            'error': errorMsg,
            'needAdmin': true
          });
          return;
        }
        throw writeErr;
      }
      this._postMessage({
        'type': "seamlessRestored",
        'success': true,
        'needRestart': true,
        'message': "无感换号已禁用"
      });
    } catch (restoreErr) {
      console.error("[CursorPro] Restore error:", restoreErr);
      if (restoreErr.code === "EPERM" || restoreErr.code === "EACCES") {
        const errorMsg = "没有写入权限";
        this._postMessage({
          'type': "seamlessRestored",
          'success': false,
          'error': errorMsg,
          'needAdmin': true
        });
        return;
      }
      this._postMessage({
        'type': "seamlessRestored",
        'success': false,
        'error': restoreErr.message || '还原失败'
      });
    }
  }
  async _handleToggleSeamless(enabled) {
    try {
      await client_1.updateSeamlessConfig({
        'enabled': enabled
      });
      this._postMessage({
        'type': "seamlessConfigUpdated",
        'success': true,
        'enabled': enabled
      });
    } catch (configErr) {
      this._postMessage({
        'type': "seamlessConfigUpdated",
        'success': false,
        'error': "更新配置失败"
      });
    }
  }
  async _handleGetUserSwitchStatus() {
    try {
      const savedKey = this._context.globalState.get('cursorpro.key');
      if (!savedKey) {
        this._postMessage({
          'type': "userSwitchStatus",
          'valid': false,
          'switchRemaining': 0,
          'canSwitch': false,
          'error': "未激活授权码"
        });
        return;
      }
      const status = await client_1.getUserSwitchStatus(savedKey);
      let isFalse = false;
      try {
        const status1 = await client_1.getSeamlessStatus();
        isFalse = status1.is_injected || false;
      } catch (psOut2) {}
      this._postMessage({
        'type': 'userSwitchStatus',
        ...status,
        'seamlessEnabled': isFalse
      });
    } catch (e24) {
      this._postMessage({
        'type': "userSwitchStatus",
        'valid': false,
        'switchRemaining': 0,
        'canSwitch': false,
        'error': "获取状态失败"
      });
    }
  }
  async _handleGetAccountUsage(forceRefresh) {
    try {
      if (!forceRefresh) {
        this._postMessage({
          'type': "accountUsage",
          'success': false,
          'error': "未提供账号邮箱"
        });
        return;
      }
      const result1 = client_1.getApiUrl() + "/api/cursor-accounts/query?email=" + encodeURIComponent(forceRefresh) + '&refresh=true';
      const cursorRunning = await fetch(result1);
      const result = await cursorRunning.json();
      if (result.success && result.data) {
        this._postMessage({
          'type': "accountUsage",
          'success': true,
          'data': result.data
        });
        const condition = result.data.usage || {};
        const condition1 = condition.totalUsageCount || 0;
        const num = parseFloat(condition.totalCostUSD || 0);
        extension_1.updateUsageStatusBar(condition1, num);
      } else {
        this._postMessage({
          'type': "accountUsage",
          'success': false,
          'error': result.error || "获取用量失败"
        });
      }
    } catch (announceErr) {
      this._postMessage({
        'type': "accountUsage",
        'success': false,
        'error': announceErr.message || "请求失败"
      });
    }
  }
  async _handleGetAnnouncement() {
    try {
      const result1 = client_1.getApiUrl() + "/api/announcements/latest";
      const switchCheck = await fetch(result1);
      const result = await switchCheck.json();
      if (result.success && result.data) {
        this._postMessage({
          'type': "announcement",
          'success': true,
          'data': result.data
        });
      } else {
        this._postMessage({
          'type': "announcement",
          'success': false,
          'error': result.error || "获取公告失败"
        });
      }
    } catch (versionErr) {
      this._postMessage({
        'type': "announcement",
        'success': false,
        'error': versionErr.message || "请求失败"
      });
    }
  }
  async _handleCheckVersion() {
    try {
      const result = await client_1.getLatestVersion();
      if (result.success && result.version) {
        const versionInfo = result.version;
        const seamlessPath = CursorProViewProvider.CURRENT_VERSION;
        const isMatch = this._compareVersions(versionInfo, seamlessPath) > 0;
        this._postMessage({
          'type': "versionCheck",
          'success': true,
          'currentVersion': seamlessPath,
          'latestVersion': versionInfo,
          'hasUpdate': isMatch
        });
      } else {
        this._postMessage({
          'type': "versionCheck",
          'success': false,
          'currentVersion': CursorProViewProvider.CURRENT_VERSION,
          'error': result.error || "获取版本失败"
        });
      }
    } catch (runningErr) {
      this._postMessage({
        'type': "versionCheck",
        'success': false,
        'currentVersion': CursorProViewProvider.CURRENT_VERSION,
        'error': runningErr.message || "请求失败"
      });
    }
  }
  _compareVersions(toggleArg, silentArg) {
    const mapped = toggleArg.split('.').map(Number);
    const mapped1 = silentArg.split('.').map(Number);
    const beforeSwitch = Math.max(mapped.length, mapped1.length);
    for (let count = 0; count < beforeSwitch; count++) {
      const condition = mapped[count] || 0;
      const condition1 = mapped1[count] || 0;
      if (condition > condition1) {
        return 1;
      }
      if (condition < condition1) {
        return -1;
      }
    }
    return 0;
  }
  async _handleGetCursorRunningPath() {
    try {
      const platform = process.platform;
      let filePath = "未找到";
      let str = '';
      const config = vscode.workspace.getConfiguration("cursorpro");
      const configValue = config.get("cursorPath");
      if (configValue && fs.existsSync(configValue)) {
        filePath = configValue;
        if (platform === "darwin") {
          str = path.join(configValue, "Contents", "Resources", "app", "package.json");
        } else {
          str = path.join(configValue, "resources", "app", "package.json");
        }
        console.log("[CursorPro] 使用用户配置的路径:", configValue);
      } else {
        if (platform === "win32") {
          try {
            const {
              stdout: manualErr
            } = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
            const matchResult = manualErr.match(/ExecutablePath=(.+)/);
            if (matchResult && matchResult[1]) {
              const trimmed = matchResult[1].trim();
              filePath = path.dirname(trimmed);
              str = path.join(filePath, "resources", "app", "package.json");
            }
          } catch (beforeErr) {
            console.log("[CursorPro] WMIC 获取路径失败:", beforeErr);
          }
          if (filePath === "未找到") {
            const condition = process.env.LOCALAPPDATA || '';
            const items = [path.join(condition, "Programs", 'cursor'), path.join(condition, "cursor")];
            for (const originalCode of items) {
              const joinedPath = path.join(originalCode, "resources", "app", "package.json");
              if (fs.existsSync(joinedPath)) {
                filePath = originalCode;
                str = joinedPath;
                break;
              }
            }
          }
        } else {
          if (platform === "darwin") {
            filePath = (await this._getCursorInstallPath()) || "/Applications/Cursor.app";
            str = path.join(filePath, "Contents", "Resources", 'app', "package.json");
          } else {
            const condition = process.env.HOME || '';
            const items = ["/usr/share/cursor", path.join(condition, ".local", "share", "cursor")];
            for (const backupDir of items) {
              if (fs.existsSync(backupDir)) {
                filePath = backupDir;
                str = path.join(backupDir, "resources", 'app', "package.json");
                break;
              }
            }
          }
        }
      }
      const condition = str && fs.existsSync(str);
      let str1 = '';
      if (condition) {
        try {
          const fileContent = fs.readFileSync(str, "utf-8");
          const parsed = JSON.parse(fileContent);
          str1 = parsed.version || '';
          console.log("[CursorPro] 从路径获取 Cursor 版本:", str1);
        } catch (backupErr) {
          console.log("[CursorPro] 读取 package.json 失败:", backupErr);
        }
      }
      this._postMessage({
        'type': 'cursorRunningPath',
        'path': filePath,
        'packageJsonPath': str,
        'packageExists': condition,
        'cursorVersion': str1
      });
    } catch (codeItem) {
      this._postMessage({
        'type': "cursorRunningPath",
        'path': "获取失败: " + (codeItem.message || codeItem),
        'packageJsonPath': '',
        'packageExists': false,
        'cursorVersion': ''
      });
    }
  }
  async _handleCheckUsageBeforeSwitch(silent) {
    try {
      const savedKey = this._context.globalState.get("cursorpro.key");
      if (!savedKey) {
        this._postMessage({
          'type': "usageCheckResult",
          'success': false,
          'error': "未激活授权码"
        });
        return;
      }
      if (!silent) {
        this._postMessage({
          'type': "usageCheckResult",
          'success': true,
          'needConfirm': false
        });
        return;
      }
      const result1 = client_1.getApiUrl() + '/api/cursor-accounts/query?email=' + encodeURIComponent(silent) + "&refresh=false";
      const seamlessBackup = await fetch(result1);
      const result = await seamlessBackup.json();
      if (result.success && result.data) {
        const condition = result.data.usage || {};
        const num = parseFloat(condition.totalCostUSD || 0);
        if (num < 10) {
          this._postMessage({
            'type': "usageCheckResult",
            'success': true,
            'needConfirm': true,
            'costUSD': num.toFixed(2),
            'email': silent
          });
        } else {
          this._postMessage({
            'type': "usageCheckResult",
            'success': true,
            'needConfirm': false
          });
        }
      } else {
        this._postMessage({
          'type': "usageCheckResult",
          'success': true,
          'needConfirm': false
        });
      }
    } catch (execOut) {
      this._postMessage({
        'type': 'usageCheckResult',
        'success': true,
        'needConfirm': false
      });
    }
  }
  async _handleManualSeamlessSwitch() {
    try {
      const savedKey = this._context.globalState.get("cursorpro.key");
      if (!savedKey) {
        this._postMessage({
          'type': "manualSeamlessSwitched",
          'success': false,
          'error': "未激活授权码"
        });
        return;
      }
      const switchResult = await client_1.switchSeamlessToken(savedKey);
      if (switchResult.switched) {
        if (switchResult.email) {
          await this._context.globalState.update("cursorpro.seamlessCurrentAccount", switchResult.email);
        }
        // 写入 token 到本地 Cursor 存储
        if (switchResult.data) {
          try {
            await this._writeAccountToLocal(switchResult.data);
            console.log("[CursorPro] 换号成功，已写入新 token");
          } catch (writeErr) {
            console.error("[CursorPro] 写入 token 失败:", writeErr);
          }
        }
        this._postMessage({
          'type': "manualSeamlessSwitched",
          'success': true,
          'email': switchResult.email,
          'switchRemaining': switchResult.switchRemaining
        });
      } else {
        const condition = switchResult.message || switchResult.error || "换号失败";
        this._postMessage({
          'type': "manualSeamlessSwitched",
          'success': false,
          'error': condition
        });
      }
    } catch (tmpErr6) {
      const condition = tmpErr6?.message || "连接服务器失败";
      this._postMessage({
        'type': "manualSeamlessSwitched",
        'success': false,
        'error': condition
      });
    }
  }
  async _handleGetCursorPath() {
    try {
      const platform = process.platform;
      let str = '';
      let str1 = '';
      if (platform === "win32") {
        try {
          const {
            stdout: patchErr
          } = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
          const matchResult = patchErr.match(/ExecutablePath=(.+)/);
          if (matchResult && matchResult[1]) {
            const trimmed = matchResult[1].trim();
            str = path.dirname(trimmed);
          }
        } catch (shellOut) {
          try {
            const {
              stdout: lineContent
            } = await execAsync('powershell -Command "Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path"');
            if (lineContent.trim()) {
              str = path.dirname(lineContent.trim());
            }
          } catch (restoreErr2) {
            console.warn("[CursorPro] 获取进程路径失败:", restoreErr2);
          }
        }
        const condition = process.env.APPDATA || '';
        str1 = path.join(condition, "Cursor");
      } else {
        if (platform === "darwin") {
          try {
            const {
              stdout: e34
            } = await execAsync("ps aux | grep -i \"[C]ursor\" | head -1 | awk '{print $11}'");
            if (e34.trim()) {
              const trimmed = e34.trim();
              const matchResult = trimmed.match(/(.+\.app)/);
              if (matchResult) {
                str = matchResult[1];
              } else {
                str = path.dirname(trimmed);
              }
            }
          } catch (toggleErr2) {
            console.warn("[CursorPro] 获取进程路径失败:", toggleErr2);
          }
          const condition = process.env.HOME || '';
          str1 = path.join(condition, 'Library', "Application Support", "Cursor");
        } else {
          try {
            const {
              stdout: e35
            } = await execAsync("ps aux | grep -i \"[c]ursor\" | head -1 | awk '{print $11}'");
            if (e35.trim()) {
              str = path.dirname(e35.trim());
            }
          } catch (seamlessErr2) {
            console.warn("[CursorPro] 获取进程路径失败:", seamlessErr2);
          }
          const condition = process.env.HOME || '';
          str1 = path.join(condition, ".config", "Cursor");
        }
      }
      if (!str) {
        str = "未检测到运行中的Cursor进程";
      }
      let str2 = '';
      if (str && !str.includes("未检测")) {
        if (platform === "win32") {
          str2 = path.join(str, 'resources', "app", 'out', 'vs', 'workbench', "workbench.desktop.main.js");
        } else {
          if (platform === "darwin") {
            str2 = path.join(str, "Contents", "Resources", "app", "out", 'vs', "workbench", 'workbench.desktop.main.js');
          } else {
            str2 = path.join(str, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
          }
        }
        if (!fs.existsSync(str2)) {
          str2 = (await this._getWorkbenchPathAsync()) || "未找到";
        }
      } else {
        str2 = (await this._getWorkbenchPathAsync()) || "未找到";
      }
      const value = str && !str.includes("未检测") ? fs.existsSync(str) : false;
      const value1 = str1 ? fs.existsSync(str1) : false;
      this._postMessage({
        'type': "cursorPath",
        'cursorPath': value ? str : str || "未找到",
        'dataPath': value1 ? str1 : "未找到",
        'workbenchPath': str2,
        'platform': platform
      });
    } catch (e37) {
      this._postMessage({
        'type': "cursorPath",
        'cursorPath': "获取失败",
        'dataPath': '获取失败',
        'workbenchPath': "获取失败",
        'error': e37.message
      });
    }
  }
  async _loadAccountsFromDB() {
    try {
      const patchedContent = account_1.getCursorPaths();
      const {
        dbPath: psOut3
      } = patchedContent;
      if (!fs.existsSync(psOut3)) {
        return [];
      }
      const workbenchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/accessToken");
      const patchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/refreshToken");
      const email = await sqlite_1.sqliteGet(psOut3, "cursorAuth/cachedEmail");
      if (workbenchContent && email) {
        return [{
          'email': email,
          'access_token': workbenchContent,
          'refresh_token': patchContent || workbenchContent
        }];
      }
      return [];
    } catch (e38) {
      console.error("[CursorPro] 读取账号失败:", e38);
      return [];
    }
  }
  async _sendState() {
    const savedKey = this._context.globalState.get("cursorpro.key");
    const expireDate = this._context.globalState.get('cursorpro.expireDate');
    const switchData = this._context.globalState.get("cursorpro.switchRemaining");
    const switchData1 = this._context.globalState.get("cursorpro.switchLimit");
    const cursorversionResult = await this._getCursorVersion();
    const restoreCode = client_1.getOnlineStatus();
    this._postMessage({
      'type': "state",
      'isActivated': !!savedKey,
      'key': savedKey || '',
      'expireDate': expireDate || '',
      'switchRemaining': switchData ?? 0,
      'switchLimit': switchData1 ?? 100,
      'cursorVersion': cursorversionResult,
      'isOnline': restoreCode
    });
  }
  async _handleRetryConnect() {
    try {
      const savedKey = this._context.globalState.get("cursorpro.key");
      if (savedKey) {
        await client_1.verifyKey(savedKey);
      } else {
        const result = client_1.getApiUrl() + '/api/announcements/latest';
        await fetch(result, {
          'method': 'GET'
        });
      }
      await this._sendState();
      this._postMessage({
        'type': "networkStatus",
        'online': true
      });
    } catch (execErr) {
      console.error("[CursorPro] Retry connect failed:", execErr);
      this._postMessage({
        'type': "networkStatus",
        'online': false
      });
    }
  }
  async _getCursorVersion() {
    try {
      const platform = process.platform;
      const items = [];
      const cursorPath = await this._getCursorInstallPath();
      if (cursorPath) {
        if (platform === "darwin") {
          items.push(path.join(cursorPath, "Contents", "Resources", "app", 'package.json'));
        } else {
          items.push(path.join(cursorPath, "resources", 'app', "package.json"));
        }
      }
      if (platform === "win32") {
        const condition = process.env.LOCALAPPDATA || '';
        const condition1 = process.env.USERPROFILE || '';
        const condition2 = process.env.ProgramFiles || "C:\\Program Files";
        const condition3 = process.env['ProgramFiles(x86)'] || "C:\\Program Files (x86)";
        items.push(path.join(condition, "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition, "Programs", "cursor", "resources", 'app', "package.json"), path.join(condition1, "AppData", "Local", "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition2, "Cursor", "resources", 'app', "package.json"), path.join(condition2, "cursor", "resources", "app", "package.json"), path.join(condition3, "Cursor", "resources", "app", "package.json"));
      } else {
        if (platform === "darwin") {
          items.push("/Applications/Cursor.app/Contents/Resources/app/package.json");
        } else {
          const condition = process.env.HOME || '';
          items.push("/usr/share/cursor/resources/app/package.json", "/opt/Cursor/resources/app/package.json", "/opt/cursor/resources/app/package.json", path.join(condition, ".local", 'share', "cursor", "resources", 'app', "package.json"));
        }
      }
      for (const seamlessCode of items) {
        try {
          if (fs.existsSync(seamlessCode)) {
            const fileContent = fs.readFileSync(seamlessCode, "utf-8");
            const parsed = JSON.parse(fileContent);
            if (parsed.version) {
              console.log("[CursorPro] 找到 Cursor 版本:", parsed.version, "路径:", seamlessCode);
              return parsed.version;
            }
          }
        } catch (fsErr) {
          console.log("[CursorPro] 尝试路径失败:", seamlessCode, fsErr);
        }
      }
      try {
        const module = require("vscode");
        if (module.version) {
          console.log("[CursorPro] 使用 VS Code API 获取版本:", module.version);
          return module.version;
        }
      } catch (cmdOut2) {}
      console.log("[CursorPro] 未找到 Cursor 版本，尝试的路径:", items);
      return '未知';
    } catch (finalErr) {
      console.error("[CursorPro] 获取 Cursor 版本失败:", finalErr);
      return '未知';
    }
  }
  _postMessage(message) {
    this._view?.webview.postMessage(message);
  }
  _getNonce() {
    let str = '';
    const items = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for (let count = 0; count < 32; count++) {
      str += items.charAt(Math.floor(Math.random() * items.length));
    }
    return str;
  }
  _getHtmlContent(lineStr) {
    const newContent = this._getNonce();
    return "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-" + newContent + "'; img-src " + lineStr.cspSource + " https: data:; font-src " + lineStr.cspSource + "; worker-src 'none';\">\n    <title>CursorPro</title>\n    <script nonce=\"" + newContent + "\">\n        // 尽早清理 Service Worker（在 head 中执行，比 body 更早）\n        if ('serviceWorker' in navigator) {\n            navigator.serviceWorker.getRegistrations().then(function(regs) {\n                regs.forEach(function(reg) { reg.unregister(); });\n            }).catch(function() {});\n        }\n    </script>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        \n        body {\n            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n            background: #1e1e1e;\n            color: #cccccc;\n            padding: 12px;\n            font-size: 13px;\n        }\n        \n        .section {\n            margin-bottom: 16px;\n            padding: 12px;\n            background: #252526;\n            border-radius: 6px;\n        }\n        \n        .section-title {\n            display: flex;\n            align-items: center;\n            gap: 8px;\n            margin-bottom: 12px;\n            font-size: 13px;\n            color: #ffffff;\n        }\n        \n        .section-title .icon {\n            font-size: 16px;\n        }\n        \n        .status-badge {\n            margin-left: auto;\n            padding: 2px 8px;\n            border-radius: 4px;\n            font-size: 11px;\n        }\n        \n        .status-badge.inactive {\n            background: #6e3232;\n            color: #ff6b6b;\n        }\n        \n        .status-badge.active {\n            background: #2d4a3e;\n            color: #4ade80;\n        }\n        \n        .input-group {\n            display: flex;\n            gap: 8px;\n            margin-bottom: 12px;\n        }\n        \n        input[type=\"text\"] {\n            flex: 1;\n            padding: 8px 12px;\n            background: #3c3c3c;\n            border: 1px solid #4a4a4a;\n            border-radius: 4px;\n            color: #ffffff;\n            font-size: 13px;\n        }\n        \n        input[type=\"text\"]::placeholder {\n            color: #888888;\n        }\n        \n        input[type=\"text\"]:focus {\n            outline: none;\n            border-color: #007acc;\n        }\n        \n        .btn {\n            padding: 8px 16px;\n            border: none;\n            border-radius: 4px;\n            font-size: 13px;\n            cursor: pointer;\n            font-weight: 500;\n            transition: opacity 0.2s;\n        }\n        \n        .btn:hover {\n            opacity: 0.9;\n        }\n        \n        .btn:disabled {\n            opacity: 0.5;\n            cursor: not-allowed;\n        }\n        \n        .btn-primary {\n            background: #007acc;\n            color: white;\n        }\n        \n        .btn-purple {\n            background: #8b5cf6;\n            color: white;\n        }\n        \n        .btn-blue {\n            background: #3b82f6;\n            color: white;\n        }\n        \n        .btn-red {\n            background: #ef4444;\n            color: white;\n        }\n        \n        .btn-block {\n            display: block;\n            width: 100%;\n            margin-bottom: 8px;\n        }\n        \n        .info-row {\n            display: flex;\n            justify-content: space-between;\n            padding: 6px 0;\n            border-bottom: 1px solid #3c3c3c;\n        }\n        \n        .info-row:last-child {\n            border-bottom: none;\n        }\n        \n        .info-label {\n            color: #888888;\n        }\n        \n        .info-value {\n            color: #ffffff;\n        }\n        \n        .usage-row {\n            display: flex;\n            gap: 12px;\n            margin-bottom: 8px;\n        }\n        \n        .usage-row:last-of-type {\n            margin-bottom: 0;\n        }\n        \n        .usage-item {\n            flex: 1;\n            display: flex;\n            justify-content: space-between;\n            padding: 6px 10px;\n            background: #2d2d2d;\n            border-radius: 4px;\n        }\n        \n        .switch-container {\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n        \n        .switch {\n            position: relative;\n            width: 40px;\n            height: 20px;\n        }\n        \n        .switch input {\n            opacity: 0;\n            width: 0;\n            height: 0;\n        }\n        \n        .slider {\n            position: absolute;\n            cursor: pointer;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            background-color: #4a4a4a;\n            border-radius: 20px;\n            transition: 0.3s;\n        }\n        \n        .slider:before {\n            position: absolute;\n            content: \"\";\n            height: 16px;\n            width: 16px;\n            left: 2px;\n            bottom: 2px;\n            background-color: white;\n            border-radius: 50%;\n            transition: 0.3s;\n        }\n        \n        input:checked + .slider {\n            background-color: #8b5cf6;\n        }\n        \n        input:checked + .slider:before {\n            transform: translateX(20px);\n        }\n        \n        /* 小尺寸开关样式 */\n        .switch-sm {\n            position: relative;\n            width: 32px;\n            height: 16px;\n        }\n        \n        .switch-sm .slider:before {\n            height: 12px;\n            width: 12px;\n            left: 2px;\n            bottom: 2px;\n        }\n        \n        .switch-sm input:checked + .slider:before {\n            transform: translateX(16px);\n        }\n        \n        .pro-badge {\n            background: linear-gradient(90deg, #8b5cf6, #d946ef);\n            padding: 2px 6px;\n            border-radius: 4px;\n            font-size: 10px;\n            font-weight: bold;\n            color: white;\n        }\n        \n        .footer {\n            margin-top: 16px;\n            padding: 12px;\n            background: linear-gradient(135deg, rgba(60, 60, 60, 0.3) 0%, rgba(40, 40, 40, 0.5) 100%);\n            border-radius: 8px;\n            border: 1px solid rgba(255, 255, 255, 0.05);\n        }\n        \n        .footer-row {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n        }\n        \n        .auto-start {\n            display: flex;\n            align-items: center;\n            gap: 8px;\n            font-size: 11px;\n            color: #888;\n        }\n        \n        .cursor-version {\n            display: flex;\n            align-items: center;\n            gap: 6px;\n            font-size: 11px;\n            color: #666;\n            padding: 4px 10px;\n            background: rgba(0, 0, 0, 0.2);\n            border-radius: 12px;\n        }\n        \n        .cursor-version .version-num {\n            color: #a78bfa;\n            font-weight: 500;\n        }\n        \n        /* 自定义弹窗样式 */\n        .modal-overlay {\n            display: none;\n            position: fixed;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.85);\n            backdrop-filter: blur(4px);\n            z-index: 1000;\n            justify-content: center;\n            align-items: center;\n            animation: fadeIn 0.2s ease;\n        }\n        \n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n        \n        @keyframes slideIn {\n            from { \n                opacity: 0;\n                transform: scale(0.9) translateY(-10px);\n            }\n            to { \n                opacity: 1;\n                transform: scale(1) translateY(0);\n            }\n        }\n        \n        .modal-overlay.show {\n            display: flex;\n        }\n        \n        .modal-content {\n            background: linear-gradient(145deg, #1e1e1e 0%, #2a2a2a 100%);\n            border-radius: 12px;\n            padding: 16px 20px;\n            max-width: 260px;\n            width: 90%;\n            text-align: center;\n            box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.05);\n            animation: slideIn 0.2s ease;\n        }\n        \n        .modal-icon {\n            width: 44px;\n            height: 44px;\n            margin: 0 auto 12px;\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 22px;\n        }\n        \n        .modal-icon.warning {\n            background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n            box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);\n        }\n        \n        .modal-icon.success {\n            background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n            box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);\n        }\n        \n        .modal-title {\n            font-size: 15px;\n            font-weight: 600;\n            color: #fff;\n            margin-bottom: 6px;\n        }\n        \n        .modal-message {\n            font-size: 12px;\n            color: #9ca3af;\n            margin-bottom: 16px;\n            line-height: 1.5;\n        }\n        \n        .modal-buttons {\n            display: flex;\n            gap: 8px;\n            justify-content: center;\n        }\n        \n        .modal-btn {\n            padding: 8px 16px;\n            border: none;\n            border-radius: 8px;\n            font-size: 12px;\n            font-weight: 500;\n            cursor: pointer;\n            transition: all 0.15s ease;\n        }\n        \n        .modal-btn.primary {\n            background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);\n            color: white;\n            box-shadow: 0 2px 8px rgba(139, 92, 246, 0.4);\n        }\n        \n        .modal-btn.primary:hover {\n            box-shadow: 0 4px 12px rgba(139, 92, 246, 0.5);\n        }\n        \n        .modal-btn.secondary {\n            background: rgba(255, 255, 255, 0.08);\n            color: #9ca3af;\n            border: 1px solid rgba(255, 255, 255, 0.1);\n        }\n        \n        .modal-btn.secondary:hover {\n            background: rgba(255, 255, 255, 0.12);\n            color: #fff;\n        }\n        \n        .modal-btn.single {\n            background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n            color: white;\n            box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);\n            min-width: 100px;\n        }\n        \n        .modal-btn.single:hover {\n            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);\n        }\n        \n        .highlight {\n            color: #4ade80;\n            font-weight: 600;\n        }\n        \n        .key-display {\n            cursor: pointer;\n            transition: color 0.2s;\n        }\n        \n        .key-display:hover {\n            color: #007acc;\n        }\n        \n        .key-display.copied {\n            color: #4ade80 !important;\n        }\n        \n        /* Loading 状态样式 */\n        .btn.loading {\n            position: relative;\n            pointer-events: none;\n            opacity: 0.7;\n        }\n        \n        .btn.loading .btn-text {\n            visibility: hidden;\n        }\n        \n        .btn.loading::after {\n            content: '';\n            position: absolute;\n            width: 16px;\n            height: 16px;\n            top: 50%;\n            left: 50%;\n            margin-left: -8px;\n            margin-top: -8px;\n            border: 2px solid transparent;\n            border-top-color: #fff;\n            border-radius: 50%;\n            animation: spin 0.8s linear infinite;\n        }\n        \n        @keyframes spin {\n            to { transform: rotate(360deg); }\n        }\n        \n        .refresh-btn.loading {\n            animation: spin 1s linear infinite;\n            pointer-events: none;\n        }\n        \n        /* 公告样式 */\n        .announcement-badge {\n            margin-left: auto;\n            padding: 2px 8px;\n            border-radius: 4px;\n            font-size: 11px;\n            text-transform: uppercase;\n        }\n        \n        .announcement-badge.info {\n            background: #1e3a5f;\n            color: #60a5fa;\n        }\n        \n        .announcement-badge.warning {\n            background: #5c4a1f;\n            color: #fbbf24;\n        }\n        \n        .announcement-badge.error {\n            background: #6e3232;\n            color: #f87171;\n        }\n        \n        .announcement-badge.success {\n            background: #2d4a3e;\n            color: #4ade80;\n        }\n        \n        .announcement-title {\n            font-size: 14px;\n            font-weight: 600;\n            color: #ffffff;\n            margin-bottom: 8px;\n            line-height: 1.4;\n        }\n        \n        .announcement-content {\n            font-size: 12px;\n            color: #b0b0b0;\n            line-height: 1.6;\n            word-break: break-word;\n        }\n        \n        .announcement-link {\n            color: #60a5fa;\n            text-decoration: none;\n            border-bottom: 1px dashed #60a5fa;\n            transition: all 0.2s;\n            cursor: pointer;\n        }\n        \n        .announcement-link:hover {\n            color: #93c5fd;\n            border-bottom-color: #93c5fd;\n        }\n        \n        /* Toast 通知样式 */\n        .toast-container {\n            position: fixed;\n            top: 0;\n            left: 0;\n            right: 0;\n            display: flex;\n            justify-content: center;\n            padding: 12px;\n            pointer-events: none;\n            z-index: 2000;\n        }\n        \n        .toast {\n            display: flex;\n            align-items: center;\n            gap: 8px;\n            padding: 10px 16px;\n            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n            border: 1px solid rgba(74, 222, 128, 0.3);\n            border-radius: 8px;\n            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 20px rgba(74, 222, 128, 0.1);\n            transform: translateY(-100px);\n            opacity: 0;\n            transition: all 0.3s ease;\n            pointer-events: auto;\n        }\n        \n        .toast.show {\n            transform: translateY(0);\n            opacity: 1;\n        }\n        \n        .toast-icon {\n            font-size: 16px;\n        }\n        \n        .toast-message {\n            font-size: 12px;\n            color: #e0e0e0;\n            max-width: 280px;\n            word-break: break-all;\n        }\n        \n        /* 离线状态提示样式 */\n        .offline-banner {\n            display: none;\n            align-items: center;\n            gap: 8px;\n            padding: 10px 14px;\n            margin-bottom: 12px;\n            background: linear-gradient(135deg, #7f1d1d 0%, #991b1b 100%);\n            border: 1px solid rgba(239, 68, 68, 0.3);\n            border-radius: 8px;\n            animation: slideDown 0.3s ease;\n        }\n        \n        .offline-banner.show {\n            display: flex;\n        }\n        \n        @keyframes slideDown {\n            from {\n                opacity: 0;\n                transform: translateY(-10px);\n            }\n            to {\n                opacity: 1;\n                transform: translateY(0);\n            }\n        }\n        \n        .offline-banner .offline-icon {\n            font-size: 18px;\n            flex-shrink: 0;\n        }\n        \n        .offline-banner .offline-text {\n            flex: 1;\n        }\n        \n        .offline-banner .offline-title {\n            font-size: 12px;\n            font-weight: 600;\n            color: #fca5a5;\n            margin-bottom: 2px;\n        }\n        \n        .offline-banner .offline-desc {\n            font-size: 11px;\n            color: #fecaca;\n            opacity: 0.8;\n        }\n        \n        .offline-banner .retry-btn {\n            padding: 4px 10px;\n            background: rgba(255, 255, 255, 0.15);\n            border: 1px solid rgba(255, 255, 255, 0.2);\n            border-radius: 4px;\n            color: #fff;\n            font-size: 11px;\n            cursor: pointer;\n            transition: all 0.2s;\n            flex-shrink: 0;\n        }\n        \n        .offline-banner .retry-btn:hover {\n            background: rgba(255, 255, 255, 0.25);\n        }\n        \n        .offline-banner .retry-btn.loading {\n            pointer-events: none;\n            opacity: 0.7;\n        }\n        \n        /* 顶部更新提醒条 */\n        .update-banner {\n            position: sticky;\n            top: 0;\n            left: 0;\n            right: 0;\n            background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);\n            color: #fff;\n            padding: 8px 12px;\n            font-size: 12px;\n            display: none;\n            align-items: center;\n            justify-content: center;\n            gap: 8px;\n            z-index: 1000;\n            box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n        }\n        .update-banner.show {\n            display: flex;\n        }\n        .update-banner .update-icon {\n            font-size: 14px;\n        }\n        .update-banner .update-text {\n            font-weight: 500;\n        }\n        .update-banner .update-version {\n            background: rgba(255,255,255,0.2);\n            padding: 2px 6px;\n            border-radius: 4px;\n            font-size: 11px;\n        }\n        .update-banner .update-close {\n            margin-left: auto;\n            background: none;\n            border: none;\n            color: #fff;\n            cursor: pointer;\n            font-size: 16px;\n            padding: 0 4px;\n            opacity: 0.8;\n        }\n        .update-banner .update-close:hover {\n            opacity: 1;\n        }\n        \n    </style>\n</head>\n<body>\n    <!-- 顶部更新提醒条 -->\n    <div class=\"update-banner\" id=\"updateBanner\">\n        <span class=\"update-icon\">🚀</span>\n        <span class=\"update-text\">发现新版本</span>\n        <span class=\"update-version\" id=\"updateBannerVersion\">initOut.0</span>\n        <button class=\"update-close\" id=\"updateBannerClose\" title=\"关闭\">×</button>\n    </div>\n    \n    <!-- 管理员权限提示弹窗 -->\n    <div class=\"modal-overlay\" id=\"adminModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon warning\">🔐</div>\n            <div class=\"modal-title\">需要管理员权限</div>\n            <div class=\"modal-message\">\n                请关闭 Cursor，右键点击图标<br>\n                选择 <span class=\"highlight\">以管理员身份运行</span>\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn single\" id=\"adminModalClose\">我知道了</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 重置机器码权限提示弹窗 -->\n    <div class=\"modal-overlay\" id=\"resetPermissionModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon warning\">🔐</div>\n            <div class=\"modal-title\">需要管理员权限</div>\n            <div class=\"modal-message\" style=\"text-align: left; line-height: 1.8;\">\n                重置机器码需要管理员权限才能完整执行。<br><br>\n                请按以下步骤操作：<br>\n                <span style=\"color: #fbbf24;\">1.</span> 完全关闭 Cursor<br>\n                <span style=\"color: #fbbf24;\">2.</span> 右键点击 Cursor 图标<br>\n                <span style=\"color: #fbbf24;\">3.</span> 选择 <span class=\"highlight\">以管理员身份运行</span><br>\n                <span style=\"color: #fbbf24;\">4.</span> 再次点击重置机器码\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn single\" id=\"resetPermissionClose\">我知道了</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 重启提示弹窗 -->\n    <div class=\"modal-overlay\" id=\"restartModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon success\">✓</div>\n            <div class=\"modal-title\" id=\"restartModalTitle\">操作成功</div>\n            <div class=\"modal-message\">\n                需要重启 Cursor 才能生效\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn primary\" id=\"restartNowBtn\">立即重启</button>\n                <button class=\"modal-btn secondary\" id=\"restartLaterBtn\">稍后</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 激活码过期弹窗 -->\n    <div class=\"modal-overlay\" id=\"expiredModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon\" style=\"background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\">⏰</div>\n            <div class=\"modal-title\">激活码已过期</div>\n            <div class=\"modal-message\">\n                您的激活码已过期，请续费后继续使用\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn single\" id=\"expiredModalClose\">我知道了</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 清理环境确认弹窗 -->\n    <div class=\"modal-overlay\" id=\"cleanEnvModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon warning\">⚠️</div>\n            <div class=\"modal-title\">清理 Cursor 环境</div>\n            <div class=\"modal-message\">\n                此操作会删除所有配置和登录信息<br>确定要继续吗？\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn primary\" id=\"cleanEnvConfirmBtn\">确定清理</button>\n                <button class=\"modal-btn secondary\" id=\"cleanEnvCancelBtn\">取消</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 换号确认弹窗 -->\n    <div class=\"modal-overlay\" id=\"switchConfirmModal\">\n        <div class=\"modal-content\">\n            <div class=\"modal-icon warning\">💰</div>\n            <div class=\"modal-title\">账号未使用完</div>\n            <div class=\"modal-message\">\n                当前账号 <span id=\"switchConfirmEmail\" style=\"color:#4caf50;\"></span><br>\n                已用额度: <span id=\"switchConfirmCost\" style=\"color:#ff9800;font-weight:bold;\">$0.00</span> (不足 $10)<br><br>\n                确定要换号吗？\n            </div>\n            <div class=\"modal-buttons\">\n                <button class=\"modal-btn primary\" id=\"switchConfirmBtn\">确认换号</button>\n                <button class=\"modal-btn secondary\" id=\"switchCancelBtn\">取消</button>\n            </div>\n        </div>\n    </div>\n    \n    <!-- 离线状态提示 -->\n    <div class=\"offline-banner\" id=\"offlineBanner\">\n        <span class=\"offline-icon\">📡</span>\n        <div class=\"offline-text\">\n            <div class=\"offline-title\">网络连接失败</div>\n            <div class=\"offline-desc\">请检查网络后重试</div>\n        </div>\n        <button class=\"retry-btn\" id=\"retryConnectBtn\">重试</button>\n    </div>\n    \n    <!-- 软件授权 -->\n    <div class=\"section\">\n        <div class=\"section-title\">\n            <span class=\"icon\">🔐</span>\n            <span>软件授权</span>\n            <span class=\"status-badge\" id=\"authStatus\">未授权</span>\n        </div>\n        \n        <div class=\"input-group\">\n            <input type=\"text\" id=\"keyInput\" placeholder=\"请输入CDK激活码\">\n            <button class=\"btn btn-primary\" id=\"activateBtn\"><span class=\"btn-text\">激活</span></button>\n        </div>\n        \n        <div class=\"info-row\">\n            <span class=\"info-label\">激活码</span>\n            <span class=\"info-value key-display\" id=\"keyDisplay\" title=\"点击复制\">尚未激活</span>\n        </div>\n        <div class=\"info-row\">\n            <span class=\"info-label\">到期时间</span>\n            <span class=\"info-value\" id=\"expireDate\">尚未激活</span>\n        </div>\n    </div>\n    \n    <!-- 账号数据 (已隐藏) -->\n    <div class=\"section\" style=\"display:none;\">\n        <div class=\"section-title\">\n            <span class=\"icon\">👤</span>\n            <span>账号数据</span>\n            <span class=\"status-badge\" id=\"accountStatus\">未激活</span>\n        </div>\n        \n        <div class=\"info-row\">\n            <span class=\"info-label\">CI积分余额</span>\n            <span class=\"info-value\">0 <button style=\"background:none;border:none;color:#007acc;cursor:pointer;\">🔄</button></span>\n        </div>\n        \n        <button class=\"btn btn-purple btn-block\" id=\"switchBtn\" disabled>换号</button>\n        <button class=\"btn btn-blue btn-block\" id=\"resetBtn\">重置机器码</button>\n        <button class=\"btn btn-blue btn-block\" id=\"disableUpdateBtn\">禁用自动更新</button>\n        <button class=\"btn btn-blue btn-block\" id=\"cleanEnvBtn\">清理Cursor环境</button>\n        <button class=\"btn btn-red btn-block\" id=\"disableBtn\">停用插件</button>\n    </div>\n    \n    <!-- 无感换号 -->\n    <div class=\"section\">\n        <div class=\"section-title\">\n            <span class=\"icon\">⚡</span>\n            <span>无感换号</span>\n            <span class=\"status-badge\" id=\"seamlessStatus\">未启用</span>\n        </div>\n        \n        <div class=\"info-row\">\n            <span class=\"info-label\">积分</span>\n            <span class=\"info-value\" id=\"seamlessSwitchRemaining\">0</span>\n        </div>\n        \n        <div class=\"info-row\">\n            <span class=\"info-label\">当前账号</span>\n            <span class=\"info-value\" style=\"font-size:11px;\" id=\"seamlessCurrentAccount\">未分配</span>\n        </div>\n        \n        <div class=\"switch-container\" style=\"margin: 12px 0;\">\n            <span>免魔法模式</span>\n            <span class=\"pro-badge\">PRO</span>\n            <span style=\"margin-left: auto; color: #888; font-size: 11px;\"></span>\n            <label class=\"switch\">\n                <input type=\"checkbox\" id=\"seamlessProxySwitch\">\n                <span class=\"slider\"></span>\n            </label>\n        </div>\n        \n        <button class=\"btn btn-purple btn-block\" id=\"enableSeamlessBtn\" disabled><span class=\"btn-text\">启用无感换号</span></button>\n        <button class=\"btn btn-red btn-block\" id=\"seamlessResetMachineBtn\" style=\"display:none;\"><span class=\"btn-text\">重置机器码</span></button>\n        <button class=\"btn btn-red btn-block\" id=\"disableSeamlessBtn\" style=\"display:none;\"><span class=\"btn-text\">禁用无感换号</span></button>\n        <button class=\"btn btn-blue btn-block\" id=\"manualSwitchBtn\" style=\"display:none;\" disabled><span class=\"btn-text\">一键换号(扣1积分)</span></button>\n    </div>\n    \n    <!-- 账号用量 -->\n    <div class=\"section\" id=\"usageSection\" style=\"display:none;\">\n        <div class=\"section-title\">\n            <span class=\"icon\">📊</span>\n            <span>账号用量</span>\n            <button class=\"btn\" style=\"margin-left:auto;padding:4px 8px;font-size:11px;background:#3c3c3c;\" id=\"refreshUsageBtn\">🔄</button>\n        </div>\n        \n        <div class=\"usage-row\">\n            <div class=\"usage-item\">\n                <span class=\"info-label\">会员类型</span>\n                <span class=\"info-value\" id=\"usageMemberType\">-</span>\n            </div>\n            <div class=\"usage-item\">\n                <span class=\"info-label\">试用剩余</span>\n                <span class=\"info-value\" id=\"usageTrialDays\">-</span>\n            </div>\n        </div>\n        <div class=\"usage-row\">\n            <div class=\"usage-item\">\n                <span class=\"info-label\">请求次数</span>\n                <span class=\"info-value\" id=\"usageRequestCount\">-</span>\n            </div>\n            <div class=\"usage-item\">\n                <span class=\"info-label\">已用额度</span>\n                <span class=\"info-value\" id=\"usageCostUSD\">-</span>\n            </div>\n        </div>\n        <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:center;\" id=\"usageUpdateTime\">-</p>\n    </div>\n    \n    <!-- 公告 -->\n    <div class=\"section\" id=\"announcementSection\" style=\"display:none;\">\n        <div class=\"section-title\">\n            <span class=\"icon\" id=\"announcementIcon\">📢</span>\n            <span>公告</span>\n            <span class=\"announcement-badge\" id=\"announcementBadge\">info</span>\n        </div>\n        <div class=\"announcement-title\" id=\"announcementTitle\"></div>\n        <div class=\"announcement-content\" id=\"announcementContent\"></div>\n        <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:right;\" id=\"announcementTime\"></p>\n    </div>\n    \n    <!-- 版本信息 -->\n    <div class=\"section\" id=\"versionSection\">\n        <div class=\"section-title\">\n            <span class=\"icon\">📦</span>\n            <span>版本信息</span>\n            <span class=\"status-badge\" id=\"versionStatus\" style=\"display:none;\">有更新</span>\n        </div>\n        <div class=\"info-row\">\n            <span class=\"info-label\">当前版本</span>\n            <span class=\"info-value\" id=\"currentVersion\">-</span>\n        </div>\n        <div class=\"info-row\" id=\"latestVersionRow\" style=\"display:none;\">\n            <span class=\"info-label\">最新版本</span>\n            <span class=\"info-value\" id=\"latestVersion\" style=\"color:#4caf50;\">-</span>\n        </div>\n        <p id=\"updateHint\" style=\"font-size:11px;color:#ff9800;margin-top:8px;display:none;\">\n            ⚠️ 发现新版本，请更新插件以获取最新功能\n        </p>\n    </div>\n    \n    <!-- 页脚 -->\n    <div class=\"footer\">\n        <div class=\"footer-row\">\n            <div class=\"auto-start\">\n                <span>自动启动</span>\n                <label class=\"switch switch-sm\">\n                    <input type=\"checkbox\" id=\"autoStartSwitch\" checked>\n                    <span class=\"slider\"></span>\n                </label>\n            </div>\n            <div class=\"cursor-version\">\n                <span>Cursor</span>\n                <span class=\"version-num\" id=\"cursorVersion\">0.0.0</span>\n            </div>\n        </div>\n        <div class=\"footer-row\" style=\"margin-top: 8px;\">\n            <div style=\"font-size: 10px; color: #666; word-break: break-all;\">\n                <span>路径: </span>\n                <span id=\"cursorPath\" style=\"color: #888;\">获取中...</span>\n            </div>\n        </div>\n    </div>\n    \n    <!-- Toast 通知 -->\n    <div class=\"toast-container\" id=\"toastContainer\">\n        <div class=\"toast\" id=\"toast\">\n            <span class=\"toast-icon\" id=\"toastIcon\">✅</span>\n            <span class=\"toast-message\" id=\"toastMessage\"></span>\n        </div>\n    </div>\n    \n    <script nonce=\"" + newContent + "\">\n        const vscode = acquireVsCodeApi();\n        \n        // Trusted Types policy for innerHTML\n        var htmlPolicy = null;\n        if (typeof trustedTypes !== 'undefined' && trustedTypes.createPolicy) {\n            try { htmlPolicy = trustedTypes.createPolicy('cursorpro', { createHTML: function(html) { return html; } }); } catch(e) {}\n        }\n        function setInnerHTML(el, html) { if (htmlPolicy) { el.innerHTML = htmlPolicy.createHTML(html); } else { el.innerHTML = html; } }\n        \n        // 元素引用\n        const keyInput = document.getElementById('keyInput');\n        const activateBtn = document.getElementById('activateBtn');\n        const switchBtn = document.getElementById('switchBtn');\n        const resetBtn = document.getElementById('resetBtn');\n        const disableUpdateBtn = document.getElementById('disableUpdateBtn');\n        const cleanEnvBtn = document.getElementById('cleanEnvBtn');\n        const disableBtn = document.getElementById('disableBtn');\n        const authStatus = document.getElementById('authStatus');\n        const accountStatus = document.getElementById('accountStatus');\n        const keyDisplay = document.getElementById('keyDisplay');\n        const switchCount = document.getElementById('switchCount');\n        const expireDate = document.getElementById('expireDate');\n        const cursorVersion = document.getElementById('cursorVersion');\n        const cursorPath = document.getElementById('cursorPath');\n        \n        // 离线状态元素\n        const offlineBanner = document.getElementById('offlineBanner');\n        const retryConnectBtn = document.getElementById('retryConnectBtn');\n        \n        // 无感换号元素\n        const seamlessStatus = document.getElementById('seamlessStatus');\n        const seamlessProxySwitch = document.getElementById('seamlessProxySwitch');\n        const enableSeamlessBtn = document.getElementById('enableSeamlessBtn');\n        const disableSeamlessBtn = document.getElementById('disableSeamlessBtn');\n        const manualSwitchBtn = document.getElementById('manualSwitchBtn');\n        const seamlessResetMachineBtn = document.getElementById('seamlessResetMachineBtn');\n        const seamlessSwitchRemaining = document.getElementById('seamlessSwitchRemaining');\n        const seamlessCurrentAccount = document.getElementById('seamlessCurrentAccount');\n        \n        // 用量显示元素\n        const usageSection = document.getElementById('usageSection');\n        const refreshUsageBtn = document.getElementById('refreshUsageBtn');\n        const usageMemberType = document.getElementById('usageMemberType');\n        const usageTrialDays = document.getElementById('usageTrialDays');\n        const usageRequestCount = document.getElementById('usageRequestCount');\n        const usageCostUSD = document.getElementById('usageCostUSD');\n        const usageUpdateTime = document.getElementById('usageUpdateTime');\n        \n        // 公告元素\n        const announcementSection = document.getElementById('announcementSection');\n        const announcementIcon = document.getElementById('announcementIcon');\n        const announcementBadge = document.getElementById('announcementBadge');\n        const announcementTitle = document.getElementById('announcementTitle');\n        const announcementContent = document.getElementById('announcementContent');\n        const announcementTime = document.getElementById('announcementTime');\n        \n        // 版本元素\n        const versionSection = document.getElementById('versionSection');\n        const versionStatus = document.getElementById('versionStatus');\n        const currentVersionEl = document.getElementById('currentVersion');\n        const latestVersionEl = document.getElementById('latestVersion');\n        const latestVersionRow = document.getElementById('latestVersionRow');\n        const updateHint = document.getElementById('updateHint');\n        \n        // 顶部更新提醒条\n        const updateBanner = document.getElementById('updateBanner');\n        const updateBannerVersion = document.getElementById('updateBannerVersion');\n        const updateBannerClose = document.getElementById('updateBannerClose');\n        \n        // Toast 元素\n        const toast = document.getElementById('toast');\n        const toastIcon = document.getElementById('toastIcon');\n        const toastMessage = document.getElementById('toastMessage');\n        let toastTimer = null;\n        \n        // 显示 Toast 通知\n        function showToast(message, icon = '✅', duration = 10000) {\n            // 清除之前的定时器\n            if (toastTimer) {\n                clearTimeout(toastTimer);\n            }\n            \n            toastIcon.textContent = icon;\n            toastMessage.textContent = message;\n            toast.classList.add('show');\n            \n            // 设置自动隐藏\n            toastTimer = setTimeout(() => {\n                toast.classList.remove('show');\n            }, duration);\n        }\n        \n        // 禁用换号按钮并显示倒计时\n        let switchBtnCountdownTimer = null;\n        const originalSwitchBtnText = '一键换号(扣1积分)';\n        \n        function disableSwitchBtnWithCountdown(seconds) {\n            // 清除之前的定时器\n            if (switchBtnCountdownTimer) {\n                clearInterval(switchBtnCountdownTimer);\n            }\n            \n            let remaining = seconds;\n            manualSwitchBtn.disabled = true;\n            manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n            \n            switchBtnCountdownTimer = setInterval(() => {\n                remaining--;\n                if (remaining <= 0) {\n                    clearInterval(switchBtnCountdownTimer);\n                    switchBtnCountdownTimer = null;\n                    manualSwitchBtn.disabled = false;\n                    manualSwitchBtn.querySelector('.btn-text').textContent = originalSwitchBtnText;\n                } else {\n                    manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n                }\n            }, 1000);\n        }\n        \n        // 弹窗元素\n        const adminModal = document.getElementById('adminModal');\n        const adminModalClose = document.getElementById('adminModalClose');\n        const resetPermissionModal = document.getElementById('resetPermissionModal');\n        const resetPermissionClose = document.getElementById('resetPermissionClose');\n        const restartModal = document.getElementById('restartModal');\n        const restartModalTitle = document.getElementById('restartModalTitle');\n        const restartNowBtn = document.getElementById('restartNowBtn');\n        const restartLaterBtn = document.getElementById('restartLaterBtn');\n        const expiredModal = document.getElementById('expiredModal');\n        const expiredModalClose = document.getElementById('expiredModalClose');\n        const cleanEnvModal = document.getElementById('cleanEnvModal');\n        const cleanEnvConfirmBtn = document.getElementById('cleanEnvConfirmBtn');\n        const cleanEnvCancelBtn = document.getElementById('cleanEnvCancelBtn');\n        \n        // 换号确认弹窗元素\n        const switchConfirmModal = document.getElementById('switchConfirmModal');\n        const switchConfirmEmail = document.getElementById('switchConfirmEmail');\n        const switchConfirmCost = document.getElementById('switchConfirmCost');\n        const switchConfirmBtn = document.getElementById('switchConfirmBtn');\n        const switchCancelBtn = document.getElementById('switchCancelBtn');\n        \n        // 显示管理员权限弹窗\n        function showAdminModal() {\n            adminModal.classList.add('show');\n        }\n        \n        // 显示重置机器码权限提示弹窗\n        function showAdminPermissionModal() {\n            resetPermissionModal.classList.add('show');\n        }\n        \n        // 重置机器码权限弹窗 - 关闭按钮\n        resetPermissionClose.addEventListener('click', () => {\n            resetPermissionModal.classList.remove('show');\n        });\n        \n        // 点击遮罩关闭权限提示弹窗\n        resetPermissionModal.addEventListener('click', (e) => {\n            if (e.target === resetPermissionModal) {\n                resetPermissionModal.classList.remove('show');\n            }\n        });\n        \n        // 显示重启提示弹窗\n        let restartModalAction = 'reload'; // 'reload' 或 'close'\n        \n        function showRestartModal(title, action = 'reload') {\n            restartModalTitle.textContent = title || '操作成功';\n            restartModalAction = action;\n            // 根据操作类型更新按钮文字\n            restartNowBtn.textContent = action === 'close' ? '立即关闭 Cursor' : '立即重启';\n            restartModal.classList.add('show');\n        }\n        \n        // 显示过期弹窗\n        function showExpiredModal() {\n            expiredModal.classList.add('show');\n        }\n        \n        // 关闭管理员弹窗\n        adminModalClose.addEventListener('click', () => {\n            adminModal.classList.remove('show');\n        });\n        \n        // 点击遮罩关闭管理员弹窗\n        adminModal.addEventListener('click', (e) => {\n            if (e.target === adminModal) {\n                adminModal.classList.remove('show');\n            }\n        });\n        \n        // 立即重启/关闭按钮\n        restartNowBtn.addEventListener('click', () => {\n            restartModal.classList.remove('show');\n            if (restartModalAction === 'close') {\n                // 完全关闭 Cursor\n                vscode.postMessage({ type: 'closeCursor' });\n            } else {\n                // 重新加载窗口\n            vscode.postMessage({ type: 'reloadWindow' });\n            }\n        });\n        \n        // 稍后手动按钮\n        restartLaterBtn.addEventListener('click', () => {\n            restartModal.classList.remove('show');\n        });\n        \n        // 点击遮罩关闭重启弹窗\n        restartModal.addEventListener('click', (e) => {\n            if (e.target === restartModal) {\n                restartModal.classList.remove('show');\n            }\n        });\n        \n        // 关闭过期弹窗\n        expiredModalClose.addEventListener('click', () => {\n            expiredModal.classList.remove('show');\n        });\n        \n        // 点击遮罩关闭过期弹窗\n        expiredModal.addEventListener('click', (e) => {\n            if (e.target === expiredModal) {\n                expiredModal.classList.remove('show');\n            }\n        });\n        \n        // 当前账号邮箱（用于查询用量）\n        let currentAccountEmail = '';\n        let usageRefreshInterval = null;\n        // 存储完整激活码（用于复制）\n        let fullActivationKey = '';\n        // 当前剩余换号次数\n        let currentSwitchRemaining = 0;\n        // 当前到期时间\n        let currentExpireDate = '';\n        \n        // 检查卡密是否已过期\n        function isKeyExpired() {\n            if (!currentExpireDate) return true;\n            try {\n                const expireTime = new Date(currentExpireDate).getTime();\n                return Date.now() > expireTime;\n            } catch {\n                return true;\n            }\n        }\n        \n        // 格式化到期时间为北京时间\n        function formatExpireDate(dateStr) {\n            if (!dateStr) return '';\n            try {\n                // 后端返回的时间没有时区标识，假设是 UTC 时间\n                // 将空格替换为T，并添加Z表示UTC\n                let utcStr = dateStr;\n                if (!dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {\n                    utcStr = dateStr.replace(' ', 'T') + 'Z';\n                }\n                const date = new Date(utcStr);\n                \n                // 使用中国时区格式化（UTC+8）\n                return date.toLocaleString('zh-CN', {\n                    timeZone: 'Asia/Shanghai',\n                    year: 'numeric',\n                    month: '2-digit',\n                    day: '2-digit',\n                    hour: '2-digit',\n                    minute: '2-digit',\n                    second: '2-digit',\n                    hour12: false\n                });\n            } catch {\n                return dateStr;  // 格式化失败返回原始值\n            }\n        }\n        \n        // 隐藏激活码后几位\n        function maskKey(key) {\n            if (!key || key.length <= 8) return key;\n            return key.substring(0, key.length - 4) + '****';\n        }\n        \n        // 点击激活码复制\n        keyDisplay.addEventListener('click', () => {\n            if (!fullActivationKey) return;\n            navigator.clipboard.writeText(fullActivationKey).then(() => {\n                keyDisplay.classList.add('copied');\n                const originalText = keyDisplay.textContent;\n                keyDisplay.textContent = '已复制!';\n                setTimeout(() => {\n                    keyDisplay.textContent = maskKey(fullActivationKey);\n                    keyDisplay.classList.remove('copied');\n                }, 1000);\n            }).catch(() => {\n                // 降级方案\n                const textarea = document.createElement('textarea');\n                textarea.value = fullActivationKey;\n                document.body.appendChild(textarea);\n                textarea.select();\n                document.execCommand('copy');\n                document.body.removeChild(textarea);\n                keyDisplay.classList.add('copied');\n                keyDisplay.textContent = '已复制!';\n                setTimeout(() => {\n                    keyDisplay.textContent = maskKey(fullActivationKey);\n                    keyDisplay.classList.remove('copied');\n                }, 1000);\n            });\n        });\n        \n        // Loading 状态控制\n        function setButtonLoading(btn, loading) {\n            if (loading) {\n                btn.classList.add('loading');\n                btn.disabled = true;\n            } else {\n                btn.classList.remove('loading');\n                // 注意：某些按钮可能需要保持禁用状态，由调用方控制\n            }\n        }\n        \n        function setRefreshLoading(btn, loading) {\n            if (loading) {\n                btn.classList.add('loading');\n            } else {\n                btn.classList.remove('loading');\n            }\n        }\n        \n        // 获取初始状态\n        vscode.postMessage({ type: 'getState' });\n        vscode.postMessage({ type: 'getSeamlessStatus' });\n        vscode.postMessage({ type: 'getUserSwitchStatus' });\n        vscode.postMessage({ type: 'getProxyStatus' });\n        vscode.postMessage({ type: 'getAnnouncement' });\n        vscode.postMessage({ type: 'checkVersion' });\n        vscode.postMessage({ type: 'getCursorRunningPath' });\n        \n        // 激活按钮\n        activateBtn.addEventListener('click', () => {\n            const key = keyInput.value.trim();\n            if (!key) {\n                return;\n            }\n            setButtonLoading(activateBtn, true);\n            vscode.postMessage({ type: 'activate', key });\n        });\n        \n        // 换号按钮\n        switchBtn.addEventListener('click', () => {\n            vscode.postMessage({ type: 'switch' });\n        });\n        \n        // 重置机器码按钮\n        resetBtn.addEventListener('click', () => {\n            vscode.postMessage({ type: 'resetMachineId' });\n        });\n        \n        // 禁用自动更新按钮\n        disableUpdateBtn.addEventListener('click', () => {\n            vscode.postMessage({ type: 'disableUpdate' });\n        });\n        \n        // 清理Cursor环境按钮 - 显示确认弹窗\n        cleanEnvBtn.addEventListener('click', () => {\n            cleanEnvModal.classList.add('show');\n        });\n        \n        // 确认清理\n        cleanEnvConfirmBtn.addEventListener('click', () => {\n            cleanEnvModal.classList.remove('show');\n            vscode.postMessage({ type: 'cleanEnv' });\n        });\n        \n        // 取消清理\n        cleanEnvCancelBtn.addEventListener('click', () => {\n            cleanEnvModal.classList.remove('show');\n        });\n        \n        // 点击遮罩关闭清理弹窗\n        cleanEnvModal.addEventListener('click', (e) => {\n            if (e.target === cleanEnvModal) {\n                cleanEnvModal.classList.remove('show');\n            }\n        });\n        \n        // 停用按钮\n        disableBtn.addEventListener('click', () => {\n            vscode.postMessage({ type: 'disable' });\n        });\n        \n        // 关闭更新提醒条\n        updateBannerClose.addEventListener('click', () => {\n            updateBanner.classList.remove('show');\n        });\n        \n        // 免魔法开关\n        seamlessProxySwitch.addEventListener('change', (e) => {\n            const wantEnabled = e.target.checked;\n            \n            // 如果要开启免魔法，检查卡密是否过期（只要没过期就可以用，不管换号次数）\n            if (wantEnabled && isKeyExpired()) {\n                e.target.checked = false;\n                showToast('授权码已过期，无法开启免魔法', '⚠️', 3000);\n                return;\n            }\n            \n            vscode.postMessage({ \n                type: 'toggleProxy', \n                enabled: wantEnabled,\n                url: ''\n            });\n        });\n        \n        // 无感换号 - 启用按钮\n        enableSeamlessBtn.addEventListener('click', () => {\n            setButtonLoading(enableSeamlessBtn, true);\n            vscode.postMessage({ type: 'injectSeamless' });\n        });\n        \n        // 无感换号 - 禁用按钮\n        disableSeamlessBtn.addEventListener('click', () => {\n            setButtonLoading(disableSeamlessBtn, true);\n            vscode.postMessage({ type: 'restoreSeamless' });\n        });\n        \n        // 无感换号 - 手动换号按钮（先检查用量）\n        manualSwitchBtn.addEventListener('click', () => {\n            setButtonLoading(manualSwitchBtn, true);\n            // 传递当前显示的账号邮箱\n            vscode.postMessage({ type: 'checkUsageBeforeSwitch', email: currentAccountEmail });\n        });\n        \n        // 换号确认弹窗 - 确认按钮\n        switchConfirmBtn.addEventListener('click', () => {\n            switchConfirmModal.classList.remove('show');\n            setButtonLoading(manualSwitchBtn, true);\n            vscode.postMessage({ type: 'confirmSwitch' });\n        });\n        \n        // 换号确认弹窗 - 取消按钮\n        switchCancelBtn.addEventListener('click', () => {\n            switchConfirmModal.classList.remove('show');\n            setButtonLoading(manualSwitchBtn, false);\n            manualSwitchBtn.disabled = false;\n        });\n        \n        // 换号确认弹窗 - 点击遮罩关闭\n        switchConfirmModal.addEventListener('click', (e) => {\n            if (e.target === switchConfirmModal) {\n                switchConfirmModal.classList.remove('show');\n                setButtonLoading(manualSwitchBtn, false);\n                manualSwitchBtn.disabled = false;\n            }\n        });\n        \n        // 无感换号区域 - 重置机器码按钮\n        seamlessResetMachineBtn.addEventListener('click', () => {\n            vscode.postMessage({ type: 'resetMachineId' });\n        });\n        \n        // 刷新用量按钮\n        refreshUsageBtn.addEventListener('click', () => {\n            if (currentAccountEmail) {\n                setRefreshLoading(refreshUsageBtn, true);\n                vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n            }\n        });\n        \n        // 刷新用量函数\n        function refreshUsage() {\n            if (currentAccountEmail) {\n                vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n            }\n        }\n        \n        // 启动用量定时刷新 (每分钟一次)\n        function startUsageRefresh() {\n            if (usageRefreshInterval) {\n                clearInterval(usageRefreshInterval);\n            }\n            // 立即刷新一次\n            refreshUsage();\n            // 每60秒刷新一次\n            usageRefreshInterval = setInterval(refreshUsage, 60000);\n        }\n        \n        // 停止用量刷新\n        function stopUsageRefresh() {\n            if (usageRefreshInterval) {\n                clearInterval(usageRefreshInterval);\n                usageRefreshInterval = null;\n            }\n        }\n        \n        // 更新用量显示\n        function updateUsageDisplay(data) {\n            if (!data) return;\n            \n            const subscription = data.subscription || {};\n            const usage = data.usage || {};\n            \n            // 会员类型\n            const memberTypeMap = {\n                'free_trial': '免费试用',\n                'pro': 'Pro会员',\n                'free': '免费版',\n                'business': '商业版'\n            };\n            usageMemberType.textContent = memberTypeMap[subscription.membershipType] || subscription.membershipType || '-';\n            \n            // 试用剩余天数\n            if (subscription.daysRemainingOnTrial !== undefined && subscription.daysRemainingOnTrial !== null) {\n                usageTrialDays.textContent = subscription.daysRemainingOnTrial + ' 天';\n                usageTrialDays.style.color = subscription.daysRemainingOnTrial <= 3 ? '#f87171' : '#4ade80';\n            } else {\n                usageTrialDays.textContent = '-';\n                usageTrialDays.style.color = '#fff';\n            }\n            \n            // 请求次数\n            usageRequestCount.textContent = (usage.totalUsageCount || 0) + ' 次';\n            \n            // 已用额度\n            const costUSD = usage.totalCostUSD || 0;\n            usageCostUSD.textContent = '$' + costUSD.toFixed(2);\n            usageCostUSD.style.color = costUSD > 5 ? '#f87171' : (costUSD > 2 ? '#fbbf24' : '#4ade80');\n            \n            // 更新时间\n            usageUpdateTime.textContent = '更新于 ' + new Date().toLocaleTimeString();\n        }\n        \n        // 解析公告内容中的链接 {文字URL}\n        function parseAnnouncementContent(content) {\n            if (!content) return '';\n            \n            // 转义 HTML 特殊字符\n            let escaped = content\n                .replace(/&/g, '&amp;')\n                .replace(/</g, '&lt;')\n                .replace(/>/g, '&gt;')\n                .replace(/\"/g, '&quot;');\n            \n            // 匹配 {文字https://...} 或 {文字http://...} 格式\n            const linkRegex = /\\{([^}]+?)(https?:\\/\\/[^}]+)\\}/g;\n            \n            escaped = escaped.replace(linkRegex, function(match, text, url) {\n                return '<a href=\"' + url + '\" class=\"announcement-link\" target=\"_blank\">' + text + '</a>';\n            });\n            \n            // 将换行符转换为 <br>\n            escaped = escaped.replace(/\\n/g, '<br>');\n            \n            return escaped;\n        }\n        \n        // 更新公告显示\n        function updateAnnouncementDisplay(data) {\n            if (!data || !data.is_active) {\n                announcementSection.style.display = 'none';\n                return;\n            }\n            \n            // 显示公告区域\n            announcementSection.style.display = 'block';\n            \n            // 设置图标和类型徽章\n            const typeConfig = {\n                'info': { icon: '📢', text: '通知', class: 'info' },\n                'warning': { icon: '⚠️', text: '警告', class: 'warning' },\n                'error': { icon: '🚨', text: '重要', class: 'error' },\n                'success': { icon: '✅', text: '好消息', class: 'success' }\n            };\n            \n            const config = typeConfig[data.type] || typeConfig.info;\n            announcementIcon.textContent = config.icon;\n            announcementBadge.textContent = config.text;\n            announcementBadge.className = 'announcement-badge ' + config.class;\n            \n            // 设置标题和内容（解析链接）\n            announcementTitle.textContent = data.title || '';\n            setInnerHTML(announcementContent, parseAnnouncementContent(data.content || ''));\n            \n            // 设置时间\n            if (data.created_at) {\n                const date = new Date(data.created_at);\n                announcementTime.textContent = date.toLocaleDateString('zh-CN', {\n                    year: 'numeric',\n                    month: '2-digit',\n                    day: '2-digit',\n                    hour: '2-digit',\n                    minute: '2-digit'\n                });\n            } else {\n                announcementTime.textContent = '';\n            }\n        }\n        \n        // 处理来自扩展的消息\n        window.addEventListener('message', event => {\n            const message = event.data;\n            \n            switch (message.type) {\n                case 'state':\n                    updateUI(message);\n                    break;\n                case 'activated':\n                    setButtonLoading(activateBtn, false);\n                    activateBtn.disabled = false;\n                    if (message.success) {\n                        // 调试日志\n                        console.log('[CursorPro] 前端收到激活成功消息:', message);\n                        \n                        authStatus.textContent = '已授权';\n                        authStatus.className = 'status-badge active';\n                        accountStatus.textContent = '已激活';\n                        accountStatus.className = 'status-badge active';\n                        switchBtn.disabled = false;\n                        // 更新激活码显示（使用后端返回的 key）\n                        fullActivationKey = message.key || keyInput.value;\n                        keyDisplay.textContent = maskKey(fullActivationKey);\n                        // 更新到期时间\n                        console.log('[CursorPro] 更新到期时间:', message.expireDate);\n                        currentExpireDate = message.expireDate || '';\n                        expireDate.textContent = formatExpireDate(currentExpireDate) || '未知';\n                        // 更新换号次数\n                        if (message.switchRemaining !== undefined) {\n                            currentSwitchRemaining = message.switchRemaining;\n                            switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n                        }\n                        // 清空输入框\n                        keyInput.value = '';\n                        showToast('授权码激活成功！', '✅', 10000);\n                    } else {\n                        showToast(message.error || '激活失败', '❌', 10000);\n                    }\n                    break;\n                case 'switched':\n                    if (message.success) {\n                        switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n                        showToast('换号成功: ' + (message.email || ''), '✅', 10000);\n                    } else {\n                        showToast(message.error || '换号失败', '❌', 10000);\n                    }\n                    break;\n                case 'reset':\n                    authStatus.textContent = '未授权';\n                    authStatus.className = 'status-badge inactive';\n                    accountStatus.textContent = '未激活';\n                    accountStatus.className = 'status-badge inactive';\n                    switchBtn.disabled = true;\n                    keyInput.value = '';\n                    fullActivationKey = '';\n                    keyDisplay.textContent = '尚未激活';\n                    expireDate.textContent = '尚未激活';\n                    break;\n                \n                // 激活码状态检查结果\n                case 'keyStatusChecked':\n                    if (message.valid) {\n                        // 激活码有效，更新显示\n                        currentExpireDate = message.expireDate || '';\n                        currentSwitchRemaining = message.switchRemaining || 0;\n                        expireDate.textContent = formatExpireDate(currentExpireDate);\n                        switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n                    } else if (message.expired) {\n                        // 激活码已过期，显示提示并重置状态\n                        currentExpireDate = '';\n                        currentSwitchRemaining = 0;\n                        authStatus.textContent = '已过期';\n                        authStatus.className = 'status-badge inactive';\n                        authStatus.style.background = '#6e3232';\n                        authStatus.style.color = '#ff6b6b';\n                        expireDate.textContent = '已过期';\n                        expireDate.style.color = '#f87171';\n                        switchBtn.disabled = true;\n                        enableSeamlessBtn.disabled = true;\n                        // 如果免魔法已开启，自动关闭\n                        if (seamlessProxySwitch.checked) {\n                            seamlessProxySwitch.checked = false;\n                            vscode.postMessage({ type: 'toggleProxy', enabled: false, url: '' });\n                        }\n                        // 显示过期弹窗\n                        showExpiredModal();\n                    }\n                    break;\n                \n                // 用户换号状态\n                case 'userSwitchStatus':\n                    const remaining = message.switchRemaining || 0;\n                    const canSwitch = remaining > 0;\n                    \n                    // 更新全局变量\n                    currentSwitchRemaining = remaining;\n                    \n                    seamlessSwitchRemaining.textContent = remaining.toString();\n                    seamlessSwitchRemaining.style.color = canSwitch ? '#4ade80' : '#f87171';\n                    \n                    if (message.lockedAccount) {\n                        seamlessCurrentAccount.textContent = message.lockedAccount.email;\n                        \n                        // 设置当前账号邮箱并启动用量刷新\n                        if (message.lockedAccount.email && message.lockedAccount.email !== currentAccountEmail) {\n                            currentAccountEmail = message.lockedAccount.email;\n                            usageSection.style.display = 'block';\n                            startUsageRefresh();\n                        }\n                    } else {\n                        seamlessCurrentAccount.textContent = '未分配';\n                        \n                        // 没有锁定账号时隐藏用量区域\n                        currentAccountEmail = '';\n                        usageSection.style.display = 'none';\n                        stopUsageRefresh();\n                    }\n                    \n                    // 根据剩余次数控制手动换号按钮状态\n                    if (!canSwitch) {\n                        manualSwitchBtn.disabled = true;\n                    }\n                    // 启用无感换号按钮不受积分限制，只有过期才禁用\n                    enableSeamlessBtn.disabled = isKeyExpired();\n                    \n                    // 如果无感换号已启用，显示手动换号按钮和重置机器码按钮\n                    if (message.seamlessEnabled && canSwitch) {\n                        manualSwitchBtn.style.display = 'block';\n                        manualSwitchBtn.disabled = false;\n                        setButtonLoading(manualSwitchBtn, false);\n                        seamlessResetMachineBtn.style.display = 'block';\n                    }\n                    break;\n                \n                // 账号用量\n                case 'accountUsage':\n                    setRefreshLoading(refreshUsageBtn, false);\n                    if (message.success && message.data) {\n                        updateUsageDisplay(message.data);\n                    } else {\n                        usageUpdateTime.textContent = '获取失败: ' + (message.error || '未知错误');\n                        usageUpdateTime.style.color = '#f87171';\n                    }\n                    break;\n                \n                // 无感换号状态\n                case 'seamlessStatus':\n                    if (message.is_injected) {\n                        seamlessStatus.textContent = '已启用';\n                        seamlessStatus.className = 'status-badge active';\n                        enableSeamlessBtn.style.display = 'none';\n                        disableSeamlessBtn.style.display = 'block';\n                        disableSeamlessBtn.disabled = false;\n                        setButtonLoading(disableSeamlessBtn, false);\n                        manualSwitchBtn.style.display = 'block';\n                        manualSwitchBtn.disabled = false;\n                        setButtonLoading(manualSwitchBtn, false);\n                        seamlessResetMachineBtn.style.display = 'block';\n                    } else {\n                        seamlessStatus.textContent = '未启用';\n                        seamlessStatus.className = 'status-badge inactive';\n                        enableSeamlessBtn.style.display = 'block';\n                        setButtonLoading(enableSeamlessBtn, false);\n                        // 启用按钮不受积分限制，只有过期才禁用\n                        enableSeamlessBtn.disabled = isKeyExpired();\n                        disableSeamlessBtn.style.display = 'none';\n                        manualSwitchBtn.style.display = 'none';\n                        seamlessResetMachineBtn.style.display = 'none';\n                    }\n                    break;\n                \n                case 'seamlessInjected':\n                    setButtonLoading(enableSeamlessBtn, false);\n                    enableSeamlessBtn.disabled = false;\n                    if (message.success) {\n                        seamlessStatus.textContent = '已启用';\n                        seamlessStatus.className = 'status-badge active';\n                        enableSeamlessBtn.style.display = 'none';\n                        disableSeamlessBtn.style.display = 'block';\n                        manualSwitchBtn.style.display = 'block';\n                        seamlessResetMachineBtn.style.display = 'block';\n                        // 刷新用户状态\n                        vscode.postMessage({ type: 'getUserSwitchStatus' });\n                        // 显示重启提示弹窗\n                        if (message.needRestart) {\n                            showRestartModal(message.message || '无感换号已启用');\n                        }\n                    } else {\n                        // 如果是权限错误，显示自定义弹窗\n                        if (message.needAdmin) {\n                            // Mac/Linux 权限问题，显示详细提示\n                            var errorMsg = message.error || '没有写入权限';\n                            if (message.path) {\n                                errorMsg += '\\n路径: ' + message.path;\n                            }\n                            showToast(errorMsg, '🔐', 15000);\n                        } else {\n                            // 显示详细错误\n                            var detailMsg = message.error || '启用失败';\n                            if (message.details) {\n                                detailMsg += '\\n' + message.details;\n                            }\n                            showToast(detailMsg, '❌', 15000);\n                        }\n                    }\n                    break;\n                \n                case 'seamlessRestored':\n                    setButtonLoading(disableSeamlessBtn, false);\n                    disableSeamlessBtn.disabled = false;\n                    if (message.success) {\n                        seamlessStatus.textContent = '未启用';\n                        seamlessStatus.className = 'status-badge inactive';\n                        enableSeamlessBtn.style.display = 'block';\n                        disableSeamlessBtn.style.display = 'none';\n                        manualSwitchBtn.style.display = 'none';\n                        seamlessResetMachineBtn.style.display = 'none';\n                        // 显示重启提示弹窗\n                        if (message.needRestart) {\n                            showRestartModal(message.message || '无感换号已禁用');\n                        }\n                    } else {\n                        // 如果是权限错误，显示自定义弹窗\n                        if (message.needAdmin) {\n                            showAdminModal();\n                        } else {\n                            showToast(message.error || '禁用失败', '❌', 10000);\n                        }\n                    }\n                    break;\n                \n                // 用量检查结果\n                case 'usageCheckResult':\n                    if (message.success) {\n                        if (message.needConfirm) {\n                            // 需要确认，显示弹窗（按钮保持可用状态，等用户选择）\n                            setButtonLoading(manualSwitchBtn, false);\n                            manualSwitchBtn.disabled = false;\n                            switchConfirmEmail.textContent = message.email || '';\n                            switchConfirmCost.textContent = '$' + (message.costUSD || '0.00');\n                            switchConfirmModal.classList.add('show');\n                        } else {\n                            // 不需要确认，直接换号\n                            vscode.postMessage({ type: 'confirmSwitch' });\n                        }\n                    } else {\n                        setButtonLoading(manualSwitchBtn, false);\n                        manualSwitchBtn.disabled = false;\n                        showToast(message.error || '检查失败', '❌', 5000);\n                    }\n                    break;\n                \n                case 'manualSeamlessSwitched':\n                    setButtonLoading(manualSwitchBtn, false);\n                    if (message.success) {\n                        seamlessSwitchRemaining.textContent = (message.switchRemaining || 0).toString();\n                        seamlessCurrentAccount.textContent = message.email || '未知';\n                        // 显示 Toast 通知，10秒后消失\n                        showToast('已切换到: ' + (message.email || '新账号') + '，约10秒内自动生效', '✅', 10000);\n                        // 刷新状态\n                        vscode.postMessage({ type: 'getUserSwitchStatus' });\n                        // 禁用按钮10秒，显示倒计时\n                        disableSwitchBtnWithCountdown(10);\n                    } else {\n                        manualSwitchBtn.disabled = false;\n                        showToast(message.error || '换号失败', '❌', 5000);\n                    }\n                    break;\n                \n                case 'proxyStatus':\n                    // 设置免魔法开关状态\n                    seamlessProxySwitch.checked = message.enabled;\n                    break;\n                \n                // 公告\n                case 'announcement':\n                    if (message.success && message.data) {\n                        updateAnnouncementDisplay(message.data);\n                    } else {\n                        announcementSection.style.display = 'none';\n                    }\n                    break;\n                \n                // 版本检查\n                case 'versionCheck':\n                    currentVersionEl.textContent = message.currentVersion || '-';\n                    if (message.success && message.hasUpdate) {\n                        // 有更新\n                        latestVersionEl.textContent = message.latestVersion;\n                        latestVersionRow.style.display = 'flex';\n                        versionStatus.style.display = 'inline-block';\n                        versionStatus.style.background = '#ff9800';\n                        updateHint.style.display = 'block';\n                        \n                        // 显示顶部更新提醒条\n                        updateBannerVersion.textContent = 'v' + message.latestVersion;\n                        updateBanner.classList.add('show');\n                    } else if (message.success) {\n                        // 已是最新版\n                        versionStatus.textContent = '最新';\n                        versionStatus.style.display = 'inline-block';\n                        versionStatus.style.background = '#4caf50';\n                        latestVersionRow.style.display = 'none';\n                        updateHint.style.display = 'none';\n                        updateBanner.classList.remove('show');\n                    }\n                    break;\n                \n                // Cursor 运行路径\n                case 'cursorRunningPath':\n                    if (message.path) {\n                        const pathText = message.path + (message.packageExists ? ' ✓' : ' ✗');\n                        cursorPath.textContent = pathText;\n                        cursorPath.style.color = message.packageExists ? '#4ade80' : '#f87171';\n                        // 同时更新版本号\n                        if (message.cursorVersion) {\n                            cursorVersion.textContent = message.cursorVersion;\n                        }\n                    } else {\n                        cursorPath.textContent = '未找到';\n                        cursorPath.style.color = '#f87171';\n                    }\n                    break;\n                \n                // 管理员权限不足提示\n                case 'adminPermissionRequired':\n                    showAdminPermissionModal();\n                    break;\n                \n                // 机器码重置\n                case 'machineIdReset':\n                    if (message.success && message.needRestart) {\n                        // 机器码重置需要完全关闭 Cursor，不是 reload\n                        showRestartModal(message.message || '机器码重置成功', 'close');\n                    }\n                    break;\n                \n                // 通用 Toast 消息\n                case 'showToast':\n                    showToast(message.message || '', message.icon || '📢', 10000);\n                    break;\n                \n                // 网络状态\n                case 'networkStatus':\n                    updateOfflineStatus(!message.online);\n                    break;\n            }\n        });\n        \n        // 离线状态显示/隐藏\n        let wasOffline = false; // 跟踪之前是否离线\n        function updateOfflineStatus(isOffline) {\n            if (isOffline) {\n                offlineBanner.classList.add('show');\n                wasOffline = true;\n            } else {\n                offlineBanner.classList.remove('show');\n                // 只有从离线恢复到在线时才刷新状态\n                if (wasOffline) {\n                    wasOffline = false;\n                    vscode.postMessage({ type: 'getState' });\n                    vscode.postMessage({ type: 'getUserSwitchStatus' });\n                }\n            }\n        }\n        \n        // 重试连接按钮\n        retryConnectBtn.addEventListener('click', async () => {\n            retryConnectBtn.classList.add('loading');\n            retryConnectBtn.textContent = '连接中...';\n            \n            // 发起真正的网络请求来测试网络\n            vscode.postMessage({ type: 'retryConnect' });\n            \n            // 5秒后恢复按钮状态（给网络请求足够时间）\n            setTimeout(() => {\n                retryConnectBtn.classList.remove('loading');\n                retryConnectBtn.textContent = '重试';\n            }, 5000);\n        });\n        \n        function updateUI(state) {\n            if (state.isActivated) {\n                authStatus.textContent = '已授权';\n                authStatus.className = 'status-badge active';\n                accountStatus.textContent = '已激活';\n                accountStatus.className = 'status-badge active';\n                switchBtn.disabled = false;\n                fullActivationKey = state.key;\n                keyDisplay.textContent = maskKey(fullActivationKey);\n                // 更新到期时间\n                currentExpireDate = state.expireDate || '';\n                expireDate.textContent = formatExpireDate(currentExpireDate);\n                // 更新换号次数\n                if (state.switchRemaining !== undefined) {\n                    currentSwitchRemaining = state.switchRemaining;\n                    switchCount.textContent = state.switchRemaining + '/' + (state.switchLimit || 100);\n                }\n                // 启用无感换号按钮（只有过期才禁用）\n                enableSeamlessBtn.disabled = isKeyExpired();\n            }\n            cursorVersion.textContent = state.cursorVersion || '0.0.0';\n            \n            // 根据网络状态显示/隐藏离线提示\n            if (state.isOnline === false) {\n                offlineBanner.classList.add('show');\n                wasOffline = true;\n            } else if (state.isOnline === true) {\n                // 网络恢复，隐藏离线提示\n                offlineBanner.classList.remove('show');\n                wasOffline = false;\n            }\n        }\n    </script>\n</body>\n</html>";
  }
}
exports.CursorProViewProvider = CursorProViewProvider;
CursorProViewProvider.CURRENT_VERSION = '2.0.0';