Files
cursornew2026/codexfanbianyi/extension/out/webview/provider.js

1991 lines
155 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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] 鍐欏叆鏈湴澶辫触:", strIdx);
vscode.window.showErrorMessage("鍐欏叆澶辫触: " + strIdx);
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 浠g悊閰嶇疆澶辫触:", 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:", cmdOut);
}
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] 娉ㄥ唽琛ㄦ柟娉?鑾峰彇璺緞澶辫触");
}
}
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] 娉ㄥ唽琛ㄦ柟娉?鑾峰彇璺緞澶辫触");
}
}
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] 蹇嵎鏂瑰紡瑙f瀽鑾峰彇璺緞澶辫触");
}
}
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': "娉ㄥ叆鐐?: 瀹屾暣鎬ф鏌ョ粫杩?,
'scode': "_showNotification(){",
'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
'restore': {
'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
'replace_with': "_showNotification(){"
}
}, {
'name': "娉ㄥ叆鐐?: 鏍稿績妯潡鍒濆?,
'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': "娉ㄥ叆鐐?: 鍚姩鏃禩oken鍚屾",
'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] 鍐欏叆鏂囦欢澶辫触:", lsofOut);
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': '壘鍒癈ursor瀹夎'
});
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" || subPath.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" || psOut2.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);
}
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 {
injectionCode = (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=\"璇疯緭鍏DK婵€娲荤爜\">\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\">涓€閿崲鍙?鎵?绉垎)</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 // 鍏冪礌寮曠敤\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 = '涓€閿崲鍙?鎵?绉垎)';\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 // 姣?0绉掑埛鏂颁竴娆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 // 瑙f瀽鍏憡鍐呭涓殑閾炬帴 {鏂囧瓧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 announcementContent.innerHTML = 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 閫氱煡锛?0绉掑悗娑堝け\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 = '0.4.5';