3067 lines
266 KiB
JavaScript
3067 lines
266 KiB
JavaScript
'use strict';
|
||
|
||
var __createBinding = this && this.__createBinding || (Object.create ? function (arg1, arg2, arg3, arg4) {
|
||
if (arg4 === undefined) {
|
||
arg4 = arg3;
|
||
}
|
||
var var2 = Object.getOwnPropertyDescriptor(arg2, arg3);
|
||
if (!var2 || ("get" in var2 ? !arg2.__esModule : var2.writable || var2.configurable)) {
|
||
var2 = {
|
||
'enumerable': true,
|
||
'get': function () {
|
||
return arg2[arg3];
|
||
}
|
||
};
|
||
}
|
||
Object.defineProperty(arg1, arg4, var2);
|
||
} : function (arg9, arg10, arg11, arg12) {
|
||
if (arg12 === undefined) {
|
||
arg12 = arg11;
|
||
}
|
||
arg9[arg12] = arg10[arg11];
|
||
});
|
||
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (arg15, arg16) {
|
||
Object.defineProperty(arg15, "default", {
|
||
'enumerable': true,
|
||
'value': arg16
|
||
});
|
||
} : function (arg17, arg18) {
|
||
arg17["default"] = arg18;
|
||
});
|
||
var __importStar = this && this.__importStar || function () {
|
||
var var7 = function (arg34) {
|
||
var7 = Object.getOwnPropertyNames || function (arg35) {
|
||
var var8 = [];
|
||
for (var var9 in arg35) if (Object.prototype.hasOwnProperty.call(arg35, var9)) {
|
||
var8[var8.length] = var9;
|
||
}
|
||
return var8;
|
||
};
|
||
return var7(arg34);
|
||
};
|
||
return function (arg36) {
|
||
const var10 = "1|4|0|3|2".split('|');
|
||
let var11 = 0x0;
|
||
while (true) {
|
||
switch (var10[var11++]) {
|
||
case '0':
|
||
if (arg36 != null) {
|
||
var var12 = var7(arg36);
|
||
for (var var13 = 0x0; var13 < var12.length; var13++) {
|
||
if (var12[var13] !== "default") {
|
||
__createBinding(v14, arg36, var12[var13]);
|
||
}
|
||
}
|
||
}
|
||
continue;
|
||
case '1':
|
||
if (arg36 && arg36.__esModule) {
|
||
return arg36;
|
||
}
|
||
continue;
|
||
case '2':
|
||
return v14;
|
||
case '3':
|
||
__setModuleDefault(v14, arg36);
|
||
continue;
|
||
case '4':
|
||
var v14 = {};
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
};
|
||
}();
|
||
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');
|
||
0x0;
|
||
const execAsync = util_1.promisify(child_process_1.exec);
|
||
class CursorProViewProvider {
|
||
constructor(arg37, arg38) {
|
||
this._extensionUri = arg37;
|
||
this._context = arg38;
|
||
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;
|
||
0x0;
|
||
this._onlineStatusUnsubscribe = client_1.onOnlineStatusChange(arg39 => {
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': arg39
|
||
});
|
||
});
|
||
}
|
||
resolveWebviewView(arg40, arg41, arg42) {
|
||
this._view = arg40;
|
||
arg40.webview.options = {
|
||
'enableScripts': true,
|
||
'localResourceRoots': [this._extensionUri]
|
||
};
|
||
arg40.webview.html = this._getHtmlContent(arg40.webview);
|
||
arg40.webview.onDidReceiveMessage(async arg43 => {
|
||
const var17 = {
|
||
'WZyWQ': "\u6CA1\u6709\u5199\u5165\u6743\u9650",
|
||
'ZXhkG': "seamlessRestored"
|
||
};
|
||
if ("GfeNG" !== "LNoTP") {
|
||
switch (arg43.type) {
|
||
case "activate":
|
||
await this._handleActivate(arg43.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(arg43.enabled, arg43.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(arg43.enabled);
|
||
break;
|
||
case "getUserSwitchStatus":
|
||
await this._handleGetUserSwitchStatus();
|
||
break;
|
||
case "manualSeamlessSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "checkUsageBeforeSwitch":
|
||
await this._handleCheckUsageBeforeSwitch(arg43.email);
|
||
break;
|
||
case "confirmSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "getCursorPath":
|
||
await this._handleGetCursorPath();
|
||
break;
|
||
case 'getAccountUsage':
|
||
await this._handleGetAccountUsage(arg43.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':
|
||
0x0;
|
||
await account_1.closeCursor();
|
||
break;
|
||
}
|
||
} else {
|
||
const var18 = var17.WZyWQ;
|
||
this._postMessage({
|
||
'type': var17.ZXhkG,
|
||
'success': false,
|
||
'error': var18,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
});
|
||
this._sendState();
|
||
this._checkKeyStatus();
|
||
}
|
||
async _checkKeyStatus() {
|
||
const var20 = this._context.globalState.get("cursorpro.key");
|
||
if (!var20) {
|
||
if ('NCQkd' !== "FAZar") {
|
||
return;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': "未提供账号邮箱"
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
try {
|
||
if ("QlWcq" !== 'QlWcq') {
|
||
v21.writeFileSync(v22, v23, "utf-8");
|
||
} else {
|
||
0x0;
|
||
const var24 = await client_1.verifyKey(var20);
|
||
if (var24.success && var24.valid) {
|
||
if ("EtMGw" === "DVTND") {
|
||
const var25 = v26.readFileSync(v27, "utf-8");
|
||
v28 = this._checkInjected(var25);
|
||
} else {
|
||
await this._context.globalState.update("cursorpro.expireDate", var24.expire_date);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", var24.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", var24.switch_limit);
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': true,
|
||
'expireDate': var24.expire_date,
|
||
'switchRemaining': var24.switch_remaining,
|
||
'switchLimit': var24.switch_limit
|
||
});
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': false,
|
||
'expired': true,
|
||
'error': var24.error || "\u6FC0\u6D3B\u7801\u5DF2\u8FC7\u671F\u6216\u65E0\u6548"
|
||
});
|
||
}
|
||
}
|
||
} catch (v29) {
|
||
console.error("[CursorPro] 检查激活码状态失败:", v29);
|
||
}
|
||
}
|
||
async _handleActivate(arg48) {
|
||
try {
|
||
const var31 = await this._isSeamlessInjected();
|
||
if (var31) {
|
||
if ("ZPKij" === "ZPKij") {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "\u65E0\u611F\u6362\u53F7\u5DF2\u542F\u7528\uFF0C\u8BF7\u5148\u7981\u7528\u540E\u518D\u66F4\u6362\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
} else {
|
||
const var32 = this._getHostsPath();
|
||
if (v33.existsSync(var32)) {
|
||
return v34.readFileSync(var32, "utf-8");
|
||
}
|
||
}
|
||
}
|
||
this._cleanProxySettings();
|
||
0x0;
|
||
const var35 = await client_1.verifyKey(arg48);
|
||
if (var35.success && var35.valid) {
|
||
if ('ZyMNB' === "ZyMNB") {
|
||
console.log("[CursorPro] 激活成功,后端返回:", {
|
||
'expire_date': var35.expire_date,
|
||
'switch_remaining': var35.switch_remaining,
|
||
'switch_limit': var35.switch_limit
|
||
});
|
||
await this._context.globalState.update("cursorpro.key", arg48);
|
||
await this._context.globalState.update("cursorpro.expireDate", var35.expire_date);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", var35.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", var35.switch_limit);
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': true,
|
||
'key': arg48,
|
||
'expireDate': var35.expire_date,
|
||
'switchRemaining': var35.switch_remaining,
|
||
'switchLimit': var35.switch_limit
|
||
});
|
||
0x0;
|
||
extension_1.showStatusBar();
|
||
await this._handleGetUserSwitchStatus();
|
||
} else {
|
||
v36.warn("[CursorPro] 清理失败: " + v37, v38);
|
||
}
|
||
} else {
|
||
if ("aVnan" === "SOGed") {
|
||
const var39 = v40("vscode");
|
||
if (var39.version) {
|
||
v41.log("[CursorPro] \u4F7F\u7528 VS Code API \u83B7\u53D6\u7248\u672C:", var39.version);
|
||
return var39.version;
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': var35.error || "\u6388\u6743\u7801\u65E0\u6548"
|
||
});
|
||
}
|
||
}
|
||
} catch (v42) {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "\u8FDE\u63A5\u670D\u52A1\u5668\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
async _handleSwitch() {
|
||
const var44 = this._context.globalState.get("cursorpro.key");
|
||
if (!var44) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
try {
|
||
0x0;
|
||
const var45 = await client_1.switchSeamlessToken(var44);
|
||
if (var45.switched) {
|
||
await this._context.globalState.update("cursorpro.switchRemaining", var45.switchRemaining);
|
||
this._postMessage({
|
||
'type': "switched",
|
||
'success': true,
|
||
'email': var45.email,
|
||
'switchRemaining': var45.switchRemaining,
|
||
'switchLimit': this._context.globalState.get("cursorpro.switchLimit") || 0x64
|
||
});
|
||
const var46 = var45.switchRemaining ?? 0x0;
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'switchRemaining': var46,
|
||
'canSwitch': var46 > 0x0,
|
||
'lockedAccount': var45.email ? {
|
||
'email': var45.email
|
||
} : null
|
||
});
|
||
} else if ("yFoId" !== "kqrkg") {
|
||
this._postMessage({
|
||
'type': "switched",
|
||
'success': false,
|
||
'error': var45.message || '换号失败'
|
||
});
|
||
} else {
|
||
v47 = v48[0x1];
|
||
}
|
||
} catch (v49) {
|
||
if ("CyYkO" !== "QcuMv") {
|
||
this._postMessage({
|
||
'type': 'switched',
|
||
'success': false,
|
||
'error': "连接服务器失败"
|
||
});
|
||
} else {
|
||
v50.log("[CursorPro] WMIC \u83B7\u53D6\u8DEF\u5F84\u5931\u8D25:", v51);
|
||
}
|
||
}
|
||
}
|
||
async _writeAccountToLocal(arg59) {
|
||
try {
|
||
if ("MClbP" === "hdUrt") {
|
||
let var53;
|
||
if (v54 === "darwin") {
|
||
var53 = v55.join(this._cachedCursorPath, "Contents", 'Resources', "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
var53 = v56.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (v57.existsSync(var53)) {
|
||
return var53;
|
||
}
|
||
} else {
|
||
const var58 = process.env.APPDATA || '';
|
||
const var59 = path.join(var58, "Cursor", "User", "globalStorage", "state.vscdb");
|
||
const var60 = path.join(var58, "Cursor", "User", "globalStorage", 'storage.json');
|
||
const var61 = path.join(var58, "Cursor", "machineid");
|
||
if (fs.existsSync(var59)) {
|
||
const var62 = [];
|
||
if (arg59.accessToken) {
|
||
var62.push(["cursorAuth/accessToken", arg59.accessToken]);
|
||
}
|
||
if (arg59.refreshToken) {
|
||
var62.push(["cursorAuth/refreshToken", arg59.refreshToken]);
|
||
}
|
||
if (arg59.email) {
|
||
if ("rmAmZ" === 'mIiuu') {
|
||
v63 = v64.dirname(v65.trim());
|
||
} else {
|
||
var62.push(["cursorAuth/cachedEmail", arg59.email]);
|
||
}
|
||
}
|
||
if (arg59.membership_type) {
|
||
if ('vhcuB' === "pAlKG") {
|
||
const var66 = v67.platform;
|
||
let var68 = '没有写入权限';
|
||
if (var66 === "darwin") {
|
||
var68 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo chmod -R 777 /Applications/Cursor.app";
|
||
} else if (var66 === "linux") {
|
||
var68 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u4F7F\u7528 sudo \u6743\u9650\u8FD0\u884C\u6216\u4FEE\u6539\u6587\u4EF6\u6743\u9650";
|
||
}
|
||
this._postMessage({
|
||
'type': 'seamlessInjected',
|
||
'success': false,
|
||
'error': var68,
|
||
'needAdmin': true,
|
||
'path': v69
|
||
});
|
||
return;
|
||
} else {
|
||
var62.push(["cursorAuth/stripeMembershipType", arg59.membership_type]);
|
||
}
|
||
}
|
||
if (arg59.sign_up_type) {
|
||
if ("ptOKi" !== 'yLQZc') {
|
||
var62.push(["cursorAuth/cachedSignUpType", arg59.sign_up_type]);
|
||
} else {
|
||
v70 = v71.dirname(v72);
|
||
}
|
||
}
|
||
if (arg59.serviceMachineId) {
|
||
if ('fYfXI' !== "eNCnU") {
|
||
var62.push(["storage.serviceMachineId", arg59.serviceMachineId]);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': true,
|
||
'costUSD': v73.toFixed(0x2),
|
||
'email': v74
|
||
});
|
||
}
|
||
}
|
||
0x0;
|
||
await sqlite_1.sqliteSetBatch(var59, var62);
|
||
console.log("[CursorPro] SQLite 数据库已更新");
|
||
}
|
||
if (fs.existsSync(var60)) {
|
||
if ("xouQM" === "nqqWG") {
|
||
const var75 = v76.message || v77.error || "\u6362\u53F7\u5931\u8D25";
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': var75
|
||
});
|
||
} else {
|
||
const var78 = JSON.parse(fs.readFileSync(var60, 'utf-8'));
|
||
if (arg59.machineId) {
|
||
var78["telemetry.machineId"] = arg59.machineId;
|
||
}
|
||
if (arg59.macMachineId) {
|
||
var78['telemetry.macMachineId'] = arg59.macMachineId;
|
||
}
|
||
if (arg59.devDeviceId) {
|
||
var78["telemetry.devDeviceId"] = arg59.devDeviceId;
|
||
}
|
||
if (arg59.sqmId) {
|
||
if ("eBQqu" === "hCBnO") {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "修改 hosts 文件失败,请确保有管理员权限"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "需要管理员权限修改 hosts 文件",
|
||
'icon': '⚠️'
|
||
});
|
||
} else {
|
||
var78["telemetry.sqmId"] = arg59.sqmId;
|
||
}
|
||
}
|
||
fs.writeFileSync(var60, JSON.stringify(var78, null, 0x4));
|
||
console.log("[CursorPro] storage.json \u5DF2\u66F4\u65B0");
|
||
}
|
||
}
|
||
if (arg59.machineId) {
|
||
fs.writeFileSync(var61, arg59.machineId);
|
||
console.log("[CursorPro] machineid \u6587\u4EF6\u5DF2\u66F4\u65B0");
|
||
}
|
||
if (arg59.registryGuid && process.platform === "win32") {
|
||
if ("hhZyB" === "hhZyB") {
|
||
try {
|
||
const var79 = 'reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + arg59.registryGuid + '" /f';
|
||
await execAsync(var79);
|
||
console.log("[CursorPro] 注册表 MachineGuid 已更新");
|
||
} catch (v80) {
|
||
console.warn("[CursorPro] \u6CE8\u518C\u8868\u5199\u5165\u5931\u8D25\uFF08\u53EF\u80FD\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650\uFF09:", v80);
|
||
}
|
||
} else {
|
||
v81 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u4F7F\u7528 sudo \u6743\u9650\u8FD0\u884C\u6216\u4FEE\u6539\u6587\u4EF6\u6743\u9650";
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
} catch (v82) {
|
||
console.error("[CursorPro] 写入本地失败:", v82);
|
||
vscode.window.showErrorMessage("写入失败: " + v82);
|
||
return false;
|
||
}
|
||
}
|
||
async _handleReset() {
|
||
const var84 = '4|0|3|5|2|1'.split('|');
|
||
let var85 = 0x0;
|
||
while (true) {
|
||
switch (var84[var85++]) {
|
||
case '0':
|
||
await this._context.globalState.update("cursorpro.expireDate", undefined);
|
||
continue;
|
||
case '1':
|
||
vscode.window.showInformationMessage("插件已重置");
|
||
continue;
|
||
case '2':
|
||
this._postMessage({
|
||
'type': 'reset',
|
||
'success': true
|
||
});
|
||
continue;
|
||
case '3':
|
||
await this._context.globalState.update("cursorpro.switchRemaining", undefined);
|
||
continue;
|
||
case '4':
|
||
await this._context.globalState.update("cursorpro.key", undefined);
|
||
continue;
|
||
case '5':
|
||
0x0;
|
||
extension_1.hideStatusBar();
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
async _handleDisable() {
|
||
await this._handleReset();
|
||
vscode.window.showInformationMessage("插件已停用");
|
||
}
|
||
async _checkAdminPrivilege() {
|
||
if (process.platform !== "win32") {
|
||
if ("FIUZS" === 'FIUZS') {
|
||
return true;
|
||
} else {
|
||
v87 = v88.join(v89, 'Contents', "Resources", 'app', "package.json");
|
||
}
|
||
}
|
||
try {
|
||
await execAsync('reg query "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid 2>nul');
|
||
const var90 = await execAsync("net session 2>nul").catch(() => ({
|
||
'stdout': '',
|
||
'stderr': 'error'
|
||
}));
|
||
return !var90.stderr;
|
||
} catch (v91) {
|
||
return false;
|
||
}
|
||
}
|
||
async _handleResetMachineId() {
|
||
try {
|
||
const var93 = process.platform;
|
||
if (var93 === 'win32') {
|
||
const var94 = await this._checkAdminPrivilege();
|
||
if (!var94) {
|
||
this._postMessage({
|
||
'type': "adminPermissionRequired"
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
0x0;
|
||
const var95 = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: v96,
|
||
storagePath: v97,
|
||
machineidPath: v98
|
||
} = var95;
|
||
const var99 = require("crypto");
|
||
const var100 = var99.randomBytes(0x20).toString("hex");
|
||
const var101 = var99.randomBytes(0x20).toString("hex");
|
||
const var102 = var99.randomUUID();
|
||
const var103 = '{' + var99.randomUUID().toUpperCase() + '}';
|
||
let var104 = 0x0;
|
||
let var105 = [];
|
||
if (fs.existsSync(v97)) {
|
||
if ("EIFLO" !== "EIFLO") {
|
||
let var106;
|
||
if (v107 === "darwin") {
|
||
var106 = v108.join(v109, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
var106 = v110.join(v111, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (v112.existsSync(var106)) {
|
||
return var106;
|
||
}
|
||
} else {
|
||
let var113 = 0x3;
|
||
while (var113 > 0x0) {
|
||
try {
|
||
const var114 = JSON.parse(fs.readFileSync(v97, "utf-8"));
|
||
var114["telemetry.machineId"] = var100;
|
||
var114["telemetry.macMachineId"] = var101;
|
||
var114["telemetry.devDeviceId"] = var102;
|
||
var114["telemetry.sqmId"] = var103;
|
||
fs.writeFileSync(v97, JSON.stringify(var114, null, 0x4));
|
||
console.log("[CursorPro] storage.json \u5DF2\u66F4\u65B0");
|
||
var104++;
|
||
break;
|
||
} catch (v115) {
|
||
var113--;
|
||
if (var113 === 0x0) {
|
||
if ("VbcUu" === "OKDBI") {
|
||
v116.rmSync(v117, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
v118++;
|
||
v119.log("[CursorPro] 已清理: " + v120);
|
||
} else {
|
||
console.warn("[CursorPro] storage.json \u66F4\u65B0\u5931\u8D25:", v115.message);
|
||
var105.push("storage.json");
|
||
}
|
||
} else {
|
||
await new Promise(arg110 => setTimeout(arg110, 0x64));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
{
|
||
if ("AGUOM" === "omtSK") {
|
||
var var121 = [];
|
||
for (var var122 in v123) if (v124.prototype.hasOwnProperty.call(v125, var122)) {
|
||
var121[var121.length] = var122;
|
||
}
|
||
return var121;
|
||
} else {
|
||
let var126 = 0x3;
|
||
while (var126 > 0x0) {
|
||
try {
|
||
const var127 = path.dirname(v98);
|
||
if (!fs.existsSync(var127)) {
|
||
if ('UjEXX' !== "XygLc") {
|
||
fs.mkdirSync(var127, {
|
||
'recursive': true
|
||
});
|
||
} else {
|
||
return v128;
|
||
}
|
||
}
|
||
fs.writeFileSync(v98, var100);
|
||
console.log("[CursorPro] machineid \u6587\u4EF6\u5DF2\u66F4\u65B0");
|
||
var104++;
|
||
break;
|
||
} catch (v129) {
|
||
var126--;
|
||
if (var126 === 0x0) {
|
||
console.warn("[CursorPro] machineid 更新失败:", v129.message);
|
||
var105.push("machineid");
|
||
} else {
|
||
await new Promise(arg111 => setTimeout(arg111, 0x64));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (fs.existsSync(v96)) {
|
||
let var130 = 0x3;
|
||
while (var130 > 0x0) {
|
||
if ("UbSjF" === "UbSjF") {
|
||
try {
|
||
if ("EvWMl" !== "SxHmQ") {
|
||
const var131 = var99.randomUUID();
|
||
0x0;
|
||
const var132 = await sqlite_1.sqliteSetBatch(v96, [['storage.serviceMachineId', var131]]);
|
||
if (var132) {
|
||
console.log("[CursorPro] SQLite 数据库已更新");
|
||
var104++;
|
||
break;
|
||
} else {
|
||
if ('edqsV' !== "edqsV") {
|
||
const var133 = {
|
||
'JDiGp': function (arg112, arg113) {
|
||
return arg112 != arg113;
|
||
},
|
||
'UJfLB': function (arg114, arg115) {
|
||
return arg114 !== arg115;
|
||
},
|
||
'YIhbu': "default",
|
||
'hQSHL': function (arg116, arg117, arg118, arg119) {
|
||
return arg116(arg117, arg118, arg119);
|
||
},
|
||
'iFgQw': function (arg120, arg121, arg122) {
|
||
return arg120(arg121, arg122);
|
||
}
|
||
};
|
||
var var134 = function (arg123) {
|
||
var134 = v135.getOwnPropertyNames || function (arg124) {
|
||
var var136 = [];
|
||
for (var var137 in arg124) if (v138.prototype.hasOwnProperty.call(arg124, var137)) {
|
||
var136[var136.length] = var137;
|
||
}
|
||
return var136;
|
||
};
|
||
return var134(arg123);
|
||
};
|
||
return function (arg125) {
|
||
if (arg125 && arg125.__esModule) {
|
||
return arg125;
|
||
}
|
||
var var139 = {};
|
||
if (var133.JDiGp(arg125, null)) {
|
||
var var140 = var134(arg125);
|
||
for (var var141 = 0x0; var141 < var140.length; var141++) {
|
||
if (var140[var141] !== var133.YIhbu) {
|
||
v142(var139, arg125, var140[var141]);
|
||
}
|
||
}
|
||
}
|
||
v143(var139, arg125);
|
||
return var139;
|
||
};
|
||
} else {
|
||
throw new Error("sqliteSetBatch 返回 false");
|
||
}
|
||
}
|
||
} else {
|
||
throw new v144("sqliteSetBatch 返回 false");
|
||
}
|
||
} catch (v145) {
|
||
var130--;
|
||
if (var130 === 0x0) {
|
||
if ('pUuQI' === "EvLtz") {
|
||
v146.error("[CursorPro] Retry connect failed:", v147);
|
||
this._postMessage({
|
||
'type': 'networkStatus',
|
||
'online': false
|
||
});
|
||
} else {
|
||
console.warn("[CursorPro] SQLite \u66F4\u65B0\u5931\u8D25:", v145.message);
|
||
var105.push("SQLite");
|
||
}
|
||
} else {
|
||
await new Promise(arg126 => setTimeout(arg126, 0x1f4));
|
||
}
|
||
}
|
||
} else {
|
||
v148 = v149.trim();
|
||
}
|
||
}
|
||
}
|
||
if (var93 === "win32") {
|
||
if ("uxdjv" === 'uxdjv') {
|
||
const var150 = var99.randomUUID();
|
||
try {
|
||
if ("qWEGL" !== "lGcMr") {
|
||
await execAsync('reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + var150 + '" /f');
|
||
console.log("[CursorPro] \u6CE8\u518C\u8868 MachineGuid \u5DF2\u66F4\u65B0");
|
||
var104++;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': v151.error || "\u6388\u6743\u7801\u65E0\u6548"
|
||
});
|
||
return;
|
||
}
|
||
} catch (v152) {
|
||
console.warn("[CursorPro] 注册表更新失败(需要管理员权限),已跳过");
|
||
var105.push("注册表");
|
||
}
|
||
} else {
|
||
v153 = v154.getOwnPropertyNames || function (arg127) {
|
||
var var155 = [];
|
||
for (var var156 in arg127) if (v157.prototype.hasOwnProperty.call(arg127, var156)) {
|
||
var155[var155.length] = var156;
|
||
}
|
||
return var155;
|
||
};
|
||
return v158(v159);
|
||
}
|
||
}
|
||
if (var104 >= 0x2) {
|
||
this._postMessage({
|
||
'type': "machineIdReset",
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': var105.length > 0x0 ? "机器码重置成功(" + var105.join(", ") + " 更新失败,不影响使用)" : "\u673A\u5668\u7801\u91CD\u7F6E\u6210\u529F"
|
||
});
|
||
} else if ("uIJXM" === "gGIGL") {
|
||
v160.log("[CursorPro] 尝试路径失败:", v161, v162);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "重置部分失败: " + var105.join(", ") + "。请先完全关闭 Cursor 再试",
|
||
'icon': '⚠️'
|
||
});
|
||
}
|
||
} catch (v163) {
|
||
if ("KokEE" !== "KokEE") {
|
||
v164.log("[CursorPro] where \u547D\u4EE4\u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
} else {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "重置机器码失败: " + v163,
|
||
'icon': '❌'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
_generateRandomMAC() {
|
||
const var166 = require("crypto");
|
||
const var167 = var166.randomBytes(0x6);
|
||
var167[0x0] = (var167[0x0] | 0x2) & 0xfe;
|
||
return Array.from(var167).map(arg134 => arg134.toString(0x10).padStart(0x2, '0')).join(':');
|
||
}
|
||
async _handleDisableUpdate() {
|
||
try {
|
||
const var169 = process.env.LOCALAPPDATA || '';
|
||
const var170 = path.join(var169, "cursor-updater");
|
||
if (fs.existsSync(var170)) {
|
||
if ("Fkark" !== "Fkark") {
|
||
return false;
|
||
} else if (fs.statSync(var170).isDirectory()) {
|
||
if ("IvZQp" !== "IvZQp") {
|
||
v171 = v172.substring(0x0, v173) + v174.substring(v175 + 0x7);
|
||
} else {
|
||
fs.rmSync(var170, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
}
|
||
} else {
|
||
fs.unlinkSync(var170);
|
||
}
|
||
}
|
||
fs.writeFileSync(var170, '');
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "\u5DF2\u7981\u7528 Cursor \u81EA\u52A8\u66F4\u65B0",
|
||
'icon': '✅'
|
||
});
|
||
} catch (v176) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "禁用自动更新失败: " + v176,
|
||
'icon': '❌'
|
||
});
|
||
}
|
||
}
|
||
async _handleCleanEnv() {
|
||
try {
|
||
if (process.platform === "win32") {
|
||
await execAsync("taskkill /F /IM Cursor.exe").catch(() => {});
|
||
} else if ("rhIFB" === "xYuSE") {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "禁用自动更新失败: " + v178,
|
||
'icon': '❌'
|
||
});
|
||
} else {
|
||
await execAsync("pkill -f Cursor").catch(() => {});
|
||
}
|
||
await new Promise(arg153 => setTimeout(arg153, 0x7d0));
|
||
const var179 = process.env.APPDATA || '';
|
||
const var180 = process.env.LOCALAPPDATA || '';
|
||
const var181 = process.env.HOME || process.env.USERPROFILE || '';
|
||
let var182 = 0x0;
|
||
if (process.platform === "win32") {
|
||
if ('OMLAe' !== "uKgvW") {
|
||
const var183 = [path.join(var179, "Cursor"), path.join(var180, "Cursor"), path.join(var180, "cursor-updater"), path.join(var181, ".cursor")];
|
||
for (const var184 of var183) {
|
||
if ("CTJTi" !== "CTJTi") {
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': '获取状态失败'
|
||
});
|
||
} else {
|
||
try {
|
||
if (fs.existsSync(var184)) {
|
||
if ("YZsoE" === "jAgIw") {
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': v185
|
||
});
|
||
} else {
|
||
fs.rmSync(var184, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
var182++;
|
||
console.log("[CursorPro] 已清理: " + var184);
|
||
}
|
||
}
|
||
} catch (v186) {
|
||
console.warn("[CursorPro] 清理失败: " + var184, v186);
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
v187.log("[CursorPro] WMIC \u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
const var188 = [path.join(var181, "Library", "Application Support", "Cursor"), path.join(var181, "Library", "Caches", "Cursor"), path.join(var181, "Library", "Logs", "Cursor"), path.join(var181, 'Library', "Application Support", 'Caches', "cursor-updater"), path.join(var181, ".cursor")];
|
||
for (const var189 of var188) {
|
||
if ("ZwnwK" === "juOiE") {
|
||
const var190 = v191.readFileSync(v192, 'utf-8');
|
||
const var193 = v194.parse(var190);
|
||
if (var193.version) {
|
||
v195.log("[CursorPro] \u627E\u5230 Cursor \u7248\u672C:", var193.version, "\u8DEF\u5F84:", v196);
|
||
return var193.version;
|
||
}
|
||
} else {
|
||
try {
|
||
if (fs.existsSync(var189)) {
|
||
if ("WKeGp" !== "rxZvP") {
|
||
fs.rmSync(var189, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
var182++;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': v197.error || "\u83B7\u53D6\u516C\u544A\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
} catch (v198) {
|
||
if ("pLQpn" !== "udDhy") {
|
||
console.warn("[CursorPro] 清理失败: " + var189, v198);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': v199.error || "\u6388\u6743\u7801\u65E0\u6548"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
const var200 = [path.join(var181, ".config", "Cursor"), path.join(var181, ".cache", "Cursor"), path.join(var181, ".local", "share", "Cursor"), path.join(var181, ".cursor")];
|
||
for (const var201 of var200) {
|
||
if ('lkchx' !== "lkchx") {
|
||
v202.error("[CursorPro] \u6CE8\u5165\u5931\u8D25\uFF0C\u672A\u627E\u5230\u4EFB\u4F55\u6CE8\u5165\u70B9");
|
||
v203.error("[CursorPro] \u6587\u4EF6\u8DEF\u5F84:", v204);
|
||
v205.error("[CursorPro] \u6587\u4EF6\u5927\u5C0F:", v206.length);
|
||
v207.error("[CursorPro] \u672A\u627E\u5230\u7684\u6CE8\u5165\u70B9:", v208);
|
||
const var209 = v210.includes('_showNotification');
|
||
const var211 = v212.includes("getItems()");
|
||
v213.error("[CursorPro] 包含 _showNotification:", var209);
|
||
v214.error("[CursorPro] 包含 getItems():", var211);
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "Cursor \u7248\u672C\u4E0D\u517C\u5BB9\uFF0C\u6CE8\u5165\u70B9\u672A\u627E\u5230",
|
||
'details': "路径: " + v215
|
||
});
|
||
return;
|
||
} else {
|
||
try {
|
||
if (fs.existsSync(var201)) {
|
||
fs.rmSync(var201, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
var182++;
|
||
}
|
||
} catch (v216) {
|
||
if ('SPIaC' === "SPIaC") {
|
||
console.warn("[CursorPro] 清理失败: " + var201, v216);
|
||
} else {
|
||
if (v217 === v218) {
|
||
v219 = v220;
|
||
}
|
||
var var221 = v222.getOwnPropertyDescriptor(v223, v224);
|
||
if (!var221 || ('get' in var221 ? !v225.__esModule : var221.writable || var221.configurable)) {
|
||
var221 = {
|
||
'enumerable': true,
|
||
'get': function () {
|
||
return v226[v227];
|
||
}
|
||
};
|
||
}
|
||
v228.defineProperty(v229, v230, var221);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
vscode.window.showInformationMessage("✅ Cursor 环境清理完成!已清理 " + var182 + " 个目录。请重新启动 Cursor。");
|
||
} catch (v231) {
|
||
if ("FbJEH" !== "UEqoH") {
|
||
vscode.window.showErrorMessage("清理失败: " + v231);
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
_cleanProxySettings() {
|
||
try {
|
||
if ("uJNLm" === "uJNLm") {
|
||
const var233 = process.platform;
|
||
const var234 = process.env.HOME || process.env.USERPROFILE || '';
|
||
let var235;
|
||
if (var233 === "win32") {
|
||
if ("xHyzt" !== "xHyzt") {
|
||
return "/etc/hosts";
|
||
} else {
|
||
const var236 = process.env.APPDATA || '';
|
||
var235 = path.join(var236, "Cursor", "User", "settings.json");
|
||
}
|
||
} else {
|
||
if (var233 === "darwin") {
|
||
var235 = path.join(var234, "Library", "Application Support", "Cursor", 'User', "settings.json");
|
||
} else {
|
||
if ("UAknb" === 'UAknb') {
|
||
var235 = path.join(var234, ".config", "Cursor", "User", "settings.json");
|
||
} else {
|
||
const var237 = v238.version;
|
||
const var239 = v240.CURRENT_VERSION;
|
||
const var241 = this._compareVersions(var237, var239) > 0x0;
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': true,
|
||
'currentVersion': var239,
|
||
'latestVersion': var237,
|
||
'hasUpdate': var241
|
||
});
|
||
}
|
||
}
|
||
}
|
||
if (!fs.existsSync(var235)) {
|
||
return;
|
||
}
|
||
const var242 = fs.readFileSync(var235, 'utf-8');
|
||
let var243;
|
||
try {
|
||
var243 = JSON.parse(var242);
|
||
} catch {
|
||
if ("TSToT" === "TSToT") {
|
||
return;
|
||
} else {
|
||
v244 = v245.join(v246, "resources", "app", "package.json");
|
||
}
|
||
}
|
||
const var247 = ["http.proxy", "http.proxyStrictSSL", "http.proxySupport", "cursor.general.disableHttp2", "http.noProxy"];
|
||
let var248 = false;
|
||
for (const var249 of var247) {
|
||
if (var249 in var243) {
|
||
var248 = true;
|
||
delete var243[var249];
|
||
}
|
||
}
|
||
if (var248) {
|
||
if ("tQfhG" !== 'xvUoy') {
|
||
fs.writeFileSync(var235, JSON.stringify(var243, null, 0x4), "utf-8");
|
||
console.log("[CursorPro] 已清理 settings.json 中的旧代理配置");
|
||
} else {
|
||
v250 = v251[0x1].trim();
|
||
}
|
||
}
|
||
} else {
|
||
const var252 = v253?.message || '连接服务器失败';
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': var252
|
||
});
|
||
}
|
||
} catch (v254) {
|
||
console.warn("[CursorPro] \u6E05\u7406 settings.json \u4EE3\u7406\u914D\u7F6E\u5931\u8D25:", v254);
|
||
}
|
||
}
|
||
_getHostsPath() {
|
||
return process.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : '/etc/hosts';
|
||
}
|
||
_readHostsFile() {
|
||
try {
|
||
if ('SLTdx' !== "SLTdx") {
|
||
v257.error("[CursorPro] \u5199\u5165\u672C\u5730\u5931\u8D25:", v258);
|
||
v259.window.showErrorMessage("写入失败: " + v260);
|
||
return false;
|
||
} else {
|
||
const var261 = this._getHostsPath();
|
||
if (fs.existsSync(var261)) {
|
||
return fs.readFileSync(var261, "utf-8");
|
||
}
|
||
}
|
||
} catch (v262) {
|
||
console.error("[CursorPro] Read hosts error:", v262);
|
||
}
|
||
return '';
|
||
}
|
||
_hasHostsConfig() {
|
||
const var263 = this._readHostsFile();
|
||
return var263.includes(this.HOSTS_MARKER_START);
|
||
}
|
||
async _grantHostsWritePermission() {
|
||
if (process.platform !== "win32") {
|
||
return false;
|
||
}
|
||
try {
|
||
if ("ffBKI" === "ffBKI") {
|
||
const var265 = this._getHostsPath();
|
||
const var266 = process.env.USERNAME || '';
|
||
if (!var266) {
|
||
return false;
|
||
}
|
||
const var267 = var265.replace(/\\/g, "\\\\");
|
||
const var268 = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command icacls \\\"" + var267 + '\" /grant ' + var266 + ":M' -Verb RunAs -Wait\"";
|
||
await execAsync(var268);
|
||
this._hostsPermissionGranted = true;
|
||
console.log("[CursorPro] Hosts file permission granted to user:", var266);
|
||
return true;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': false,
|
||
'error': "\u68C0\u6D4B\u72B6\u6001\u5931\u8D25"
|
||
});
|
||
}
|
||
} catch (v269) {
|
||
console.error("[CursorPro] Grant hosts permission error:", v269);
|
||
return false;
|
||
}
|
||
}
|
||
async _writeHostsFile(arg168) {
|
||
const var271 = this._getHostsPath();
|
||
try {
|
||
if (process.platform === "win32") {
|
||
let var272 = false;
|
||
try {
|
||
if ("qQhXV" !== "qQhXV") {
|
||
v273 = v274.dirname(v275.trim());
|
||
} else {
|
||
fs.writeFileSync(var271, arg168, "utf-8");
|
||
var272 = true;
|
||
}
|
||
} catch (v276) {
|
||
console.log("[CursorPro] Direct write failed, trying to grant permission");
|
||
}
|
||
if (!var272) {
|
||
if (!this._hostsPermissionGranted) {
|
||
const var277 = await this._grantHostsWritePermission();
|
||
if (var277) {
|
||
if ("gFBAJ" === "UnshK") {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': false,
|
||
'error': "\u672A\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
} else {
|
||
try {
|
||
fs.writeFileSync(var271, arg168, "utf-8");
|
||
var272 = true;
|
||
} catch (v278) {
|
||
console.log("[CursorPro] Write still failed after permission grant");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!var272) {
|
||
const var279 = path.join(process.env.TEMP || '', "cursorpro_hosts_temp.txt");
|
||
fs.writeFileSync(var279, arg168, "utf-8");
|
||
const var280 = var279.replace(/\\/g, "\\\\");
|
||
const var281 = var271.replace(/\\/g, "\\\\");
|
||
const var282 = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command Copy-Item -Path \\\"" + var280 + '\" -Destination \"' + var281 + "\\\" -Force' -Verb RunAs -Wait\"";
|
||
await execAsync(var282);
|
||
try {
|
||
if ("XVhkW" !== "kizvY") {
|
||
fs.unlinkSync(var279);
|
||
} else {
|
||
v283.push(["cursorAuth/cachedSignUpType", v284.sign_up_type]);
|
||
}
|
||
} catch {}
|
||
}
|
||
try {
|
||
await execAsync("ipconfig /flushdns");
|
||
console.log("[CursorPro] Windows DNS 缓存已刷新");
|
||
} catch (v285) {
|
||
console.warn("[CursorPro] Windows DNS 刷新失败:", v285);
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
const var286 = "/tmp/hosts_cursor_temp";
|
||
fs.writeFileSync(var286, arg168, "utf-8");
|
||
const var287 = "do shell script \"cp '" + var286 + "' '" + var271 + "' && rm '" + var286 + "' && dscacheutil -flushcache && killall -HUP mDNSResponder\" with administrator privileges";
|
||
await execAsync('osascript -e "' + var287.replace(/"/g, "\\\"") + "\"");
|
||
} else {
|
||
fs.writeFileSync(var271, arg168, "utf-8");
|
||
}
|
||
}
|
||
return true;
|
||
} catch (v288) {
|
||
console.error("[CursorPro] Write hosts error:", v288);
|
||
return false;
|
||
}
|
||
}
|
||
async _handleToggleProxy(arg179, arg180) {
|
||
try {
|
||
if ("JCMUi" === "JCMUi") {
|
||
if (arg179) {
|
||
if ("owyly" !== "JZnQC") {
|
||
const var290 = this._context.globalState.get("cursorpro.key");
|
||
const var291 = this._context.globalState.get('cursorpro.expireDate');
|
||
if (!var290) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': '请先激活授权码',
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
if (var291) {
|
||
if ("nMubd" === "nMubd") {
|
||
const var292 = new Date(var291).getTime();
|
||
if (Date.now() > var292) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u6388\u6743\u7801\u5DF2\u8FC7\u671F\uFF0C\u65E0\u6CD5\u5F00\u542F\u514D\u9B54\u6CD5"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "\u6388\u6743\u7801\u5DF2\u8FC7\u671F\uFF0C\u65E0\u6CD5\u5F00\u542F\u514D\u9B54\u6CD5",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
} else {
|
||
v293.error("[CursorPro] 检测无感换号状态失败:", v294);
|
||
return false;
|
||
}
|
||
}
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
this._cleanProxySettings();
|
||
let var295 = this._readHostsFile();
|
||
const var296 = var295.indexOf(this.HOSTS_MARKER_START);
|
||
const var297 = var295.indexOf(this.HOSTS_MARKER_END);
|
||
if (var296 !== -0x1 && var297 !== -0x1) {
|
||
if ("BHFHl" !== 'jcvMA') {
|
||
var295 = var295.substring(0x0, var296) + var295.substring(var297 + this.HOSTS_MARKER_END.length);
|
||
} else {
|
||
return v298.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
|
||
}
|
||
}
|
||
var295 = var295.replace(/\n{3,}/g, "\n\n").trim();
|
||
if (arg179) {
|
||
const var299 = this.CURSOR_DOMAINS.map(arg193 => this.SNI_PROXY_IP + " " + arg193).join("\n");
|
||
const var300 = "\n\n" + this.HOSTS_MARKER_START + "\n" + var299 + "\n" + this.HOSTS_MARKER_END + "\n";
|
||
var295 += var300;
|
||
}
|
||
const var301 = await this._writeHostsFile(var295);
|
||
if (var301) {
|
||
if ("ejFIc" !== "QrGmP") {
|
||
0x0;
|
||
await client_1.updateProxyConfig(arg179, this.SNI_PROXY_IP);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': true,
|
||
'enabled': arg179,
|
||
'url': this.SNI_PROXY_IP
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': arg179 ? "免魔法已开启" : "\u514D\u9B54\u6CD5\u5DF2\u5173\u95ED",
|
||
'icon': '✅'
|
||
});
|
||
} else {
|
||
v302.log("[CursorPro] 快捷方式解析获取路径失败");
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u4FEE\u6539 hosts \u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u786E\u4FDD\u6709\u7BA1\u7406\u5458\u6743\u9650"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "需要管理员权限修改 hosts 文件",
|
||
'icon': '⚠️'
|
||
});
|
||
}
|
||
} else {
|
||
v303.warn("[CursorPro] macOS \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v304);
|
||
}
|
||
} catch (v305) {
|
||
console.error("[CursorPro] Toggle proxy error:", v305);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u66F4\u65B0\u914D\u7F6E\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
async _handleGetProxyStatus() {
|
||
try {
|
||
const var307 = this._hasHostsConfig();
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': var307,
|
||
'url': var307 ? this.SNI_PROXY_IP : ''
|
||
});
|
||
} catch (v308) {
|
||
console.error("[CursorPro] Get proxy status error:", v308);
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': false,
|
||
'url': ''
|
||
});
|
||
}
|
||
}
|
||
async _handleGetSeamlessStatus() {
|
||
try {
|
||
const var310 = await this._getWorkbenchPathAsync();
|
||
let var311 = false;
|
||
if (var310 && fs.existsSync(var310)) {
|
||
const var312 = fs.readFileSync(var310, 'utf-8');
|
||
var311 = this._checkInjected(var312);
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': var311,
|
||
'workbench_path': var310 || '未找到'
|
||
});
|
||
} catch (v313) {
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': false,
|
||
'error': "检测状态失败"
|
||
});
|
||
}
|
||
}
|
||
async _getCursorInstallPath() {
|
||
if (this._cachedCursorPath) {
|
||
return this._cachedCursorPath;
|
||
}
|
||
const var315 = vscode.workspace.getConfiguration("cursorpro");
|
||
const var316 = var315.get("cursorPath");
|
||
if (var316 && fs.existsSync(var316)) {
|
||
console.log("[CursorPro] \u4F7F\u7528\u7528\u6237\u914D\u7F6E\u7684 Cursor \u8DEF\u5F84:", var316);
|
||
this._cachedCursorPath = var316;
|
||
return var316;
|
||
}
|
||
const var317 = process.platform;
|
||
let var318 = null;
|
||
try {
|
||
if (var317 === "win32") {
|
||
try {
|
||
const {
|
||
stdout: v319
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
if (v319) {
|
||
if ("qoucU" !== "qoucU") {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
} else {
|
||
const var320 = v319.match(/ExecutablePath=(.+)/);
|
||
if (var320 && var320[0x1]) {
|
||
if ("DFFnS" === "qzRkG") {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "重置机器码失败: " + v321,
|
||
'icon': '❌'
|
||
});
|
||
} else {
|
||
const var322 = var320[0x1].trim();
|
||
var318 = path.dirname(var322);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v323) {
|
||
console.log("[CursorPro] WMIC \u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
}
|
||
if (!var318) {
|
||
if ('YbsJH' !== "KgPWK") {
|
||
try {
|
||
const {
|
||
stdout: v324
|
||
} = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
|
||
if (v324 && v324.trim()) {
|
||
var318 = path.dirname(v324.trim());
|
||
}
|
||
} catch (v325) {
|
||
if ("PCmsj" !== "idSrD") {
|
||
console.log("[CursorPro] PowerShell Get-Process \u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
} else if (v326.includes(v327.scode)) {
|
||
v328 = v329.replace(v330.scode, v331.replacement);
|
||
v332.push(v333.name);
|
||
} else {
|
||
v334.push(v335.name);
|
||
}
|
||
}
|
||
} else {
|
||
v336 = v337.dirname(v338);
|
||
}
|
||
}
|
||
if (!var318) {
|
||
try {
|
||
const {
|
||
stdout: v339
|
||
} = await execAsync("reg query \"HKCUSoftwareMicrosoftWindowsCurrentVersionUninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (v339 && v339.trim()) {
|
||
const var340 = v339.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (var340 && var340[0x1] && fs.existsSync(var340[0x1].trim())) {
|
||
if ("xkLFN" !== "VRqTq") {
|
||
var318 = var340[0x1].trim();
|
||
} else {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
} catch (v341) {
|
||
if ("qGOpJ" !== "WEzFK") {
|
||
console.log("[CursorPro] \u6CE8\u518C\u8868\u65B9\u6CD51\u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'seamlessConfigUpdated',
|
||
'success': false,
|
||
'error': "\u66F4\u65B0\u914D\u7F6E\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
if (!var318) {
|
||
try {
|
||
const {
|
||
stdout: v342
|
||
} = await execAsync("reg query \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (v342 && v342.trim()) {
|
||
const var343 = v342.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (var343 && var343[0x1] && fs.existsSync(var343[0x1].trim())) {
|
||
if ("vnDbD" === "xFoOA") {
|
||
v344 = v345.replace(v346.scode, v347.replacement);
|
||
v348.push(v349.name);
|
||
} else {
|
||
var318 = var343[0x1].trim();
|
||
}
|
||
}
|
||
}
|
||
} catch (v350) {
|
||
console.log("[CursorPro] 注册表方法2获取路径失败");
|
||
}
|
||
}
|
||
if (!var318) {
|
||
if ("OHDPB" !== 'ASYNt') {
|
||
try {
|
||
const var351 = path.join(process.env.APPDATA || '', "Microsoft", "Windows", "Start Menu", 'Programs', "Cursor.lnk");
|
||
const var352 = path.join("C:\\ProgramData", "Microsoft", 'Windows', "Start Menu", "Programs", "Cursor.lnk");
|
||
for (const var353 of [var351, var352]) {
|
||
if (fs.existsSync(var353)) {
|
||
const {
|
||
stdout: v354
|
||
} = await execAsync("powershell -Command \"(New-Object -ComObject WScript.Shell).CreateShortcut('" + var353.replace(/'/g, "''") + "').TargetPath\"");
|
||
if (v354 && v354.trim() && fs.existsSync(v354.trim())) {
|
||
if ("EzvlI" === "EzvlI") {
|
||
var318 = path.dirname(v354.trim());
|
||
break;
|
||
} else {
|
||
v355 = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v356) {
|
||
console.log("[CursorPro] \u5FEB\u6377\u65B9\u5F0F\u89E3\u6790\u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
}
|
||
} else {
|
||
const var357 = v358.match(/ExecutablePath=(.+)/);
|
||
if (var357 && var357[0x1]) {
|
||
const var359 = var357[0x1].trim();
|
||
v360 = v361.dirname(var359);
|
||
}
|
||
}
|
||
}
|
||
if (!var318) {
|
||
if ("vhjCP" === "vhjCP") {
|
||
try {
|
||
const {
|
||
stdout: v362
|
||
} = await execAsync("where cursor 2>nul");
|
||
if (v362 && v362.trim()) {
|
||
const var363 = v362.trim().split("\n");
|
||
for (const var364 of var363) {
|
||
const var365 = var364.trim();
|
||
if (var365 && fs.existsSync(var365)) {
|
||
var318 = path.dirname(var365);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (v366) {
|
||
if ("BQAEU" !== "BQAEU") {
|
||
v367.copyFileSync(v368, v369);
|
||
v370.log("[CursorPro] \u521B\u5EFA\u5907\u4EFD\u6587\u4EF6");
|
||
} else {
|
||
console.log("[CursorPro] where \u547D\u4EE4\u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
}
|
||
}
|
||
} else {
|
||
const var371 = v372[0x1].trim();
|
||
v373 = v374.dirname(var371);
|
||
v375 = v376.join(v377, "resources", "app", "package.json");
|
||
}
|
||
}
|
||
if (!var318) {
|
||
const var378 = process.env.LOCALAPPDATA || '';
|
||
const var379 = process.env.USERPROFILE || '';
|
||
const var380 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const var381 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||
const var382 = [path.join(var378, "Programs", "Cursor"), path.join(var378, "Programs", "cursor"), path.join(var379, "AppData", "Local", "Programs", "Cursor"), path.join(var380, "Cursor"), path.join(var381, "Cursor"), path.join(var378, "Cursor"), path.join(var378, "cursor")];
|
||
for (const var383 of var382) {
|
||
if ("mDWBe" === "XrXcG") {
|
||
v384.error("[CursorPro] \u8BFB\u53D6\u8D26\u53F7\u5931\u8D25:", v385);
|
||
return [];
|
||
} else {
|
||
if (var383 && fs.existsSync(var383)) {
|
||
var318 = var383;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (var317 === "darwin") {
|
||
if ("iDOKM" !== "mPbmb") {
|
||
try {
|
||
if ("VYhDm" === "EZQju") {
|
||
v386.error("[CursorPro] \u5199\u5165\u6587\u4EF6\u5931\u8D25:", v387);
|
||
if (v388.code === "EPERM" || v389.code === "EACCES" || v390.code === "EROFS") {
|
||
const var391 = v392.platform;
|
||
let var393 = "没有写入权限";
|
||
if (var391 === 'darwin') {
|
||
var393 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo chmod -R 777 /Applications/Cursor.app";
|
||
} else if (var391 === "linux") {
|
||
var393 = "没有写入权限,请使用 sudo 权限运行或修改文件权限";
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': var393,
|
||
'needAdmin': true,
|
||
'path': v394
|
||
});
|
||
return;
|
||
}
|
||
throw v395;
|
||
} else {
|
||
const {
|
||
stdout: v396
|
||
} = await execAsync("lsof -c Cursor 2>/dev/null | grep \"txt\" | grep -i \"Cursor.app\" | head -1 | awk '{print $9}'");
|
||
if (v396 && v396.trim()) {
|
||
if ("kFqGj" !== "kFqGj") {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
} else {
|
||
const var397 = v396.trim().match(/(.+\.app)/);
|
||
if (var397) {
|
||
if ("AvPWh" === 'hsffN') {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
return;
|
||
} else {
|
||
var318 = var397[0x1];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v398) {}
|
||
if (!var318) {
|
||
try {
|
||
if ("DLzJi" === "fmKaB") {
|
||
const var399 = v400.data.usage || {};
|
||
const var401 = v402(var399.totalCostUSD || 0x0);
|
||
if (var401 < 0xa) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': true,
|
||
'costUSD': var401.toFixed(0x2),
|
||
'email': v403
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} else {
|
||
const {
|
||
stdout: v404
|
||
} = await execAsync("ps -eo comm,args | grep -i \"[C]ursor\" | grep -v \"grep\" | head -1");
|
||
if (v404 && v404.trim()) {
|
||
const var405 = v404.match(/(\/.+\.app)/);
|
||
if (var405) {
|
||
var318 = var405[0x1];
|
||
}
|
||
}
|
||
}
|
||
} catch (v406) {
|
||
console.warn("[CursorPro] macOS \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v406);
|
||
}
|
||
}
|
||
if (!var318) {
|
||
if ("LPxcr" !== "LPxcr") {
|
||
v407.error("[CursorPro] Write hosts error:", v408);
|
||
return false;
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: v409
|
||
} = await execAsync("mdfind \"kMDItemCFBundleIdentifier == 'com.todesktop.*cursor*'\" 2>/dev/null | head -1");
|
||
if (v409 && v409.trim() && fs.existsSync(v409.trim())) {
|
||
var318 = v409.trim();
|
||
}
|
||
} catch (v410) {}
|
||
}
|
||
}
|
||
if (!var318 && fs.existsSync('/Applications/Cursor.app')) {
|
||
var318 = "/Applications/Cursor.app";
|
||
}
|
||
} else {
|
||
v411.warn("[CursorPro] SQLite \u66F4\u65B0\u5931\u8D25:", v412.message);
|
||
v413.push("SQLite");
|
||
}
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: v414
|
||
} = await execAsync('pgrep -f "[c]ursor" | head -1');
|
||
const var415 = v414 && v414.trim();
|
||
if (var415) {
|
||
const {
|
||
stdout: v416
|
||
} = await execAsync("readlink -f /proc/" + var415 + "/exe 2>/dev/null");
|
||
if (v416 && v416.trim()) {
|
||
const var417 = v416.trim();
|
||
var318 = path.dirname(var417);
|
||
if (var318.endsWith("/bin")) {
|
||
var318 = path.dirname(var318);
|
||
}
|
||
}
|
||
}
|
||
} catch (v418) {}
|
||
if (!var318) {
|
||
if ("BDrEe" !== "BDrEe") {
|
||
let var419 = '';
|
||
const var420 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
for (let var421 = 0x0; var421 < 0x20; var421++) {
|
||
var419 += var420.charAt(v422.floor(v423.random() * var420.length));
|
||
}
|
||
return var419;
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: v424
|
||
} = await execAsync("which cursor 2>/dev/null");
|
||
if (v424 && v424.trim()) {
|
||
const var425 = await execAsync('readlink -f "' + v424.trim() + '" 2>/dev/null');
|
||
if (var425.stdout && var425.stdout.trim()) {
|
||
var318 = path.dirname(var425.stdout.trim());
|
||
if (var318.endsWith('/bin')) {
|
||
if ("RTVew" === "ZRSMl") {
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': "\u672A\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
} else {
|
||
var318 = path.dirname(var318);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v426) {
|
||
console.warn("[CursorPro] Linux \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v426);
|
||
}
|
||
}
|
||
}
|
||
if (!var318) {
|
||
const var427 = ["/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 var428 of var427) {
|
||
if (fs.existsSync(var428)) {
|
||
var318 = var428;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v429) {
|
||
console.error("[CursorPro] \u83B7\u53D6 Cursor \u5B89\u88C5\u8DEF\u5F84\u5931\u8D25:", v429);
|
||
}
|
||
if (var318) {
|
||
this._cachedCursorPath = var318;
|
||
}
|
||
return var318;
|
||
}
|
||
_getWorkbenchPath() {
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
_getWorkbenchPathSync() {
|
||
const var431 = process.platform;
|
||
if (this._cachedCursorPath) {
|
||
if ('zZyLK' !== "PQRiM") {
|
||
let var432;
|
||
if (var431 === "darwin") {
|
||
if ("xuLZV" === 'xuLZV') {
|
||
var432 = path.join(this._cachedCursorPath, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
v433.push(["storage.serviceMachineId", v434.serviceMachineId]);
|
||
}
|
||
} else if ("XbfIf" !== "kWVws") {
|
||
var432 = path.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
v435.warn("[CursorPro] machineid \u66F4\u65B0\u5931\u8D25:", v436.message);
|
||
v437.push("machineid");
|
||
}
|
||
if (fs.existsSync(var432)) {
|
||
return var432;
|
||
}
|
||
} else {
|
||
return [];
|
||
}
|
||
}
|
||
if (var431 === 'win32') {
|
||
return null;
|
||
}
|
||
let var438 = [];
|
||
if (var431 === "darwin") {
|
||
var438 = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
|
||
} else {
|
||
if ('DESET' !== "qRWfN") {
|
||
var438 = ["/opt/Cursor/resources/app/out/vs/workbench/workbench.desktop.main.js", '/usr/share/cursor/resources/app/out/vs/workbench/workbench.desktop.main.js'];
|
||
} else {
|
||
const var439 = v440('crypto');
|
||
const var441 = var439.randomBytes(0x6);
|
||
var441[0x0] = (var441[0x0] | 0x2) & 0xfe;
|
||
return v442.from(var441).map(arg250 => arg250.toString(0x10).padStart(0x2, '0')).join(':');
|
||
}
|
||
}
|
||
for (const var443 of var438) {
|
||
if (fs.existsSync(var443)) {
|
||
return var443;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
async _getWorkbenchPathAsync() {
|
||
const var445 = process.platform;
|
||
const var446 = await this._getCursorInstallPath();
|
||
if (var446) {
|
||
if ("EUUHA" === "eXeOK") {
|
||
const var447 = v448[0x1].trim();
|
||
v449 = v450.dirname(var447);
|
||
} else {
|
||
let var451;
|
||
if (var445 === "darwin") {
|
||
if ("vUlDT" === "vUlDT") {
|
||
var451 = path.join(var446, "Contents", "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
const var452 = v453.env.LOCALAPPDATA || '';
|
||
const var454 = v455.env.USERPROFILE || '';
|
||
const var456 = v457.env.ProgramFiles || "C:\\Program Files";
|
||
const var458 = v459.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||
v460.push(v461.join(var452, "Programs", "Cursor", "resources", "app", "package.json"), v462.join(var452, "Programs", "cursor", "resources", "app", "package.json"), v463.join(var454, "AppData", "Local", "Programs", "Cursor", "resources", "app", "package.json"), v464.join(var456, "Cursor", "resources", "app", "package.json"), v465.join(var456, "cursor", "resources", "app", "package.json"), v466.join(var458, "Cursor", "resources", "app", "package.json"));
|
||
}
|
||
} else {
|
||
var451 = path.join(var446, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (fs.existsSync(var451)) {
|
||
if ("dtGAS" === "dtGAS") {
|
||
return var451;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': v467.CURRENT_VERSION,
|
||
'error': v468.message || "请求失败"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
_checkInjected(arg257) {
|
||
return arg257.includes("/*i0*/") || arg257.includes('/*i1s*/');
|
||
}
|
||
async _isSeamlessInjected() {
|
||
try {
|
||
const var471 = await this._getWorkbenchPathAsync();
|
||
if (var471 && fs.existsSync(var471)) {
|
||
const var472 = fs.readFileSync(var471, "utf-8");
|
||
return this._checkInjected(var472);
|
||
}
|
||
return false;
|
||
} catch (v473) {
|
||
console.error("[CursorPro] \u68C0\u6D4B\u65E0\u611F\u6362\u53F7\u72B6\u6001\u5931\u8D25:", v473);
|
||
return false;
|
||
}
|
||
}
|
||
_getInjectionConfig(arg258, arg259) {
|
||
return [{
|
||
'name': "注入点0: 完整性检查绕过",
|
||
'scode': "_showNotification(){",
|
||
'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'restore': {
|
||
'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'replace_with': "_showNotification(){"
|
||
}
|
||
}, {
|
||
'name': "注入点1: 核心模块初始化",
|
||
'scode': "this.database.getItems()))",
|
||
'replacement': "this.database.getItems()))/*i1s*/;await(async function(e){if(e.get('releaseNotes/lastVersion')){window.store=e;window.__cpKey='CursorPro2024!@#';window.__cpEnc=function(t){var k=window.__cpKey,r='';for(var i=0;i<t.length;i++)r+=String.fromCharCode(t.charCodeAt(i)^k.charCodeAt(i%k.length));return btoa(r)};window.__cpDec=function(t){var k=window.__cpKey,d=atob(t),r='';for(var i=0;i<d.length;i++)r+=String.fromCharCode(d.charCodeAt(i)^k.charCodeAt(i%k.length));return r};window.__cpGet=function(){try{var d=localStorage.getItem('__cp_token');return d?JSON.parse(window.__cpDec(d)):null}catch(e){return null}};window.__cpSet=function(data){try{localStorage.setItem('__cp_token',window.__cpEnc(JSON.stringify(data)))}catch(e){}};window.__cpApi='" + arg258 + "';window.__cpUserKey='" + arg259 + "';window.__cpVersion=0;console.log('[CP] Initialized with key:','" + arg259 + "'.substring(0,15)+'...')}})(this)/*i1e*/",
|
||
'restore': {
|
||
'find_start': "/*i1s*/",
|
||
'find_end': "/*i1e*/"
|
||
}
|
||
}, {
|
||
'name': "\u6CE8\u5165\u70B92: \u542F\u52A8\u65F6Token\u540C\u6B65",
|
||
'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 var476 = this._context.globalState.get("cursorpro.key");
|
||
if (!var476) {
|
||
if ("dlteM" !== 'reoBh') {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "请先激活授权码"
|
||
});
|
||
return;
|
||
} else {
|
||
v477.push(v478.join(v479, "resources", "app", "package.json"));
|
||
}
|
||
}
|
||
0x0;
|
||
const var480 = await client_1.getUserSwitchStatus(var476);
|
||
if (!var480.valid) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': var480.error || "\u6388\u6743\u7801\u65E0\u6548"
|
||
});
|
||
return;
|
||
}
|
||
const var481 = await this._getWorkbenchPathAsync();
|
||
if (!var481) {
|
||
if ("wecNx" === "wecNx") {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "\u542F\u7528\u5931\u8D25"
|
||
});
|
||
return;
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'keyStatusChecked',
|
||
'valid': false,
|
||
'expired': true,
|
||
'error': v482.error || "\u6FC0\u6D3B\u7801\u5DF2\u8FC7\u671F\u6216\u65E0\u6548"
|
||
});
|
||
}
|
||
}
|
||
const var483 = var481 + ".backup";
|
||
const var484 = !this._context.globalState.get("cursorpro.seamlessInjected");
|
||
if (var484 && fs.existsSync(var483)) {
|
||
if ("xfseF" === "grpZy") {
|
||
v485 = v486;
|
||
if (v487 === "darwin") {
|
||
v488 = v489.join(v490, "Contents", "Resources", "app", 'package.json');
|
||
} else {
|
||
v491 = v492.join(v493, "resources", "app", 'package.json');
|
||
}
|
||
v494.log("[CursorPro] \u4F7F\u7528\u7528\u6237\u914D\u7F6E\u7684\u8DEF\u5F84:", v495);
|
||
} else {
|
||
console.log("[CursorPro] \u9996\u6B21\u542F\u7528\uFF0C\u4ECE\u5907\u4EFD\u6062\u590D\u5E72\u51C0\u7684 workbench \u6587\u4EF6");
|
||
try {
|
||
fs.copyFileSync(var483, var481);
|
||
console.log("[CursorPro] \u5907\u4EFD\u6062\u590D\u6210\u529F");
|
||
} catch (v496) {
|
||
console.error("[CursorPro] \u5907\u4EFD\u6062\u590D\u5931\u8D25:", v496);
|
||
}
|
||
}
|
||
}
|
||
let var497 = fs.readFileSync(var481, 'utf-8');
|
||
if (this._checkInjected(var497)) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "已启用",
|
||
'icon': '✅'
|
||
});
|
||
return;
|
||
}
|
||
if (!fs.existsSync(var483)) {
|
||
fs.copyFileSync(var481, var483);
|
||
console.log("[CursorPro] 创建备份文件");
|
||
}
|
||
0x0;
|
||
const var498 = client_1.getApiUrl();
|
||
const var499 = this._getInjectionConfig(var498, var476);
|
||
const var500 = [];
|
||
const var501 = [];
|
||
for (const var502 of var499) {
|
||
if ("jYehy" === "gRHmI") {
|
||
v503 = "\u672A\u68C0\u6D4B\u5230\u8FD0\u884C\u4E2D\u7684Cursor\u8FDB\u7A0B";
|
||
} else if (var497.includes(var502.scode)) {
|
||
var497 = var497.replace(var502.scode, var502.replacement);
|
||
var500.push(var502.name);
|
||
} else {
|
||
var501.push(var502.name);
|
||
}
|
||
}
|
||
if (var500.length === 0x0) {
|
||
console.error("[CursorPro] \u6CE8\u5165\u5931\u8D25\uFF0C\u672A\u627E\u5230\u4EFB\u4F55\u6CE8\u5165\u70B9");
|
||
console.error("[CursorPro] \u6587\u4EF6\u8DEF\u5F84:", var481);
|
||
console.error("[CursorPro] \u6587\u4EF6\u5927\u5C0F:", var497.length);
|
||
console.error("[CursorPro] 未找到的注入点:", var501);
|
||
const var504 = var497.includes("_showNotification");
|
||
const var505 = var497.includes("getItems()");
|
||
console.error("[CursorPro] \u5305\u542B _showNotification:", var504);
|
||
console.error("[CursorPro] \u5305\u542B getItems():", var505);
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "Cursor \u7248\u672C\u4E0D\u517C\u5BB9\uFF0C\u6CE8\u5165\u70B9\u672A\u627E\u5230",
|
||
'details': "路径: " + var481
|
||
});
|
||
return;
|
||
}
|
||
console.log("[CursorPro] 注入成功,应用的注入点:", var500);
|
||
if (var501.length > 0x0) {
|
||
console.warn("[CursorPro] \u672A\u627E\u5230\u7684\u6CE8\u5165\u70B9:", var501);
|
||
}
|
||
try {
|
||
if ("nYkuW" === "oAKop") {
|
||
v506.warn("[CursorPro] 获取进程路径失败:", v507);
|
||
} else {
|
||
fs.writeFileSync(var481, var497, "utf-8");
|
||
}
|
||
} catch (v508) {
|
||
if ("txcqo" !== "txcqo") {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "\u65E0\u611F\u6362\u53F7\u5DF2\u542F\u7528\uFF0C\u8BF7\u5148\u7981\u7528\u540E\u518D\u66F4\u6362\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
} else {
|
||
console.error("[CursorPro] \u5199\u5165\u6587\u4EF6\u5931\u8D25:", v508);
|
||
if (v508.code === "EPERM" || v508.code === "EACCES" || v508.code === "EROFS") {
|
||
const var509 = process.platform;
|
||
let var510 = "没有写入权限";
|
||
if (var509 === "darwin") {
|
||
var510 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo chmod -R 777 /Applications/Cursor.app";
|
||
} else if (var509 === "linux") {
|
||
if ("gWxzY" === "qchHx") {
|
||
v511.error("[CursorPro] Read hosts error:", v512);
|
||
} else {
|
||
var510 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u4F7F\u7528 sudo \u6743\u9650\u8FD0\u884C\u6216\u4FEE\u6539\u6587\u4EF6\u6743\u9650";
|
||
}
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': var510,
|
||
'needAdmin': true,
|
||
'path': var481
|
||
});
|
||
return;
|
||
}
|
||
throw v508;
|
||
}
|
||
}
|
||
await this._context.globalState.update("cursorpro.seamlessInjected", true);
|
||
this._postMessage({
|
||
'type': 'seamlessInjected',
|
||
'success': true,
|
||
'applied': var500,
|
||
'needRestart': true,
|
||
'message': "\u65E0\u611F\u6362\u53F7\u5DF2\u542F\u7528"
|
||
});
|
||
} catch (v513) {
|
||
if ("nUNdq" !== 'nUNdq') {
|
||
const var514 = this._readHostsFile();
|
||
return var514.includes(this.HOSTS_MARKER_START);
|
||
} else {
|
||
console.error("[CursorPro] Inject error:", v513);
|
||
if (v513.code === "EPERM" || v513.code === "EACCES") {
|
||
if ("TKgrk" === "rIkKS") {
|
||
v515.error("[CursorPro] \u5907\u4EFD\u6062\u590D\u5931\u8D25:", v516);
|
||
} else {
|
||
const var517 = "\u6CA1\u6709\u5199\u5165\u6743\u9650";
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': var517,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': v513.message || '注入失败'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
async _handleRestoreSeamless() {
|
||
try {
|
||
const var519 = await this._getWorkbenchPathAsync();
|
||
if (!var519) {
|
||
if ("JnQIK" === "gPXAj") {
|
||
return v520;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': '未找到Cursor安装目录'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
let var521 = fs.readFileSync(var519, "utf-8");
|
||
if (!this._checkInjected(var521)) {
|
||
return;
|
||
}
|
||
var521 = var521.replace("_showNotification(){/*i0*/}_showNotificationOld(){", "_showNotification(){");
|
||
const var522 = var521.indexOf("/*i1s*/");
|
||
const var523 = var521.indexOf("/*i1e*/");
|
||
if (var522 !== -0x1 && var523 !== -0x1) {
|
||
var521 = var521.substring(0x0, var522) + var521.substring(var523 + 0x7);
|
||
}
|
||
const var524 = var521.indexOf("/*i2s*/");
|
||
const var525 = var521.indexOf("/*i2e*/");
|
||
if (var524 !== -0x1 && var525 !== -0x1) {
|
||
if ('kmqvv' !== "kmqvv") {
|
||
v526.unlinkSync(v527);
|
||
} else {
|
||
var521 = var521.substring(0x0, var524) + var521.substring(var525 + 0x7);
|
||
}
|
||
}
|
||
try {
|
||
fs.writeFileSync(var519, var521, "utf-8");
|
||
} catch (v528) {
|
||
if (v528.code === "EPERM" || v528.code === "EACCES") {
|
||
if ("uoVdW" === "tzZLb") {
|
||
v529 = v530[0x1];
|
||
} else {
|
||
const var531 = "\u6CA1\u6709\u5199\u5165\u6743\u9650";
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': var531,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
throw v528;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': "无感换号已禁用"
|
||
});
|
||
} catch (v532) {
|
||
console.error("[CursorPro] Restore error:", v532);
|
||
if (v532.code === "EPERM" || v532.code === "EACCES") {
|
||
const var533 = "没有写入权限";
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': var533,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': v532.message || '还原失败'
|
||
});
|
||
}
|
||
}
|
||
async _handleToggleSeamless(arg296) {
|
||
try {
|
||
if ("dSScr" === 'OoEdP') {
|
||
try {
|
||
if (v535.existsSync(v536)) {
|
||
v537.rmSync(v538, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
v539++;
|
||
}
|
||
} catch (v540) {
|
||
v541.warn("[CursorPro] 清理失败: " + v542, v540);
|
||
}
|
||
} else {
|
||
0x0;
|
||
await client_1.updateSeamlessConfig({
|
||
'enabled': arg296
|
||
});
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': true,
|
||
'enabled': arg296
|
||
});
|
||
}
|
||
} catch (v543) {
|
||
if ("lzzwb" === "SZovO") {
|
||
return this._getWorkbenchPathSync();
|
||
} else {
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': false,
|
||
'error': "\u66F4\u65B0\u914D\u7F6E\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
async _handleGetUserSwitchStatus() {
|
||
try {
|
||
if ("tFzAd" !== "tFzAd") {
|
||
v545.push(["cursorAuth/cachedEmail", v546.email]);
|
||
} else {
|
||
const var547 = this._context.globalState.get('cursorpro.key');
|
||
if (!var547) {
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': "未激活授权码"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const var548 = await client_1.getUserSwitchStatus(var547);
|
||
let var549 = false;
|
||
try {
|
||
if ('TSpKd' === "TSpKd") {
|
||
0x0;
|
||
const var550 = await client_1.getSeamlessStatus();
|
||
var549 = var550.is_injected || false;
|
||
} else {
|
||
v551.warn("[CursorPro] 清理失败: " + v552, v553);
|
||
}
|
||
} catch (v554) {}
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
...var548,
|
||
'seamlessEnabled': var549
|
||
});
|
||
}
|
||
} catch (v555) {
|
||
if ("ObXTw" !== "NMuKY") {
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': "\u83B7\u53D6\u72B6\u6001\u5931\u8D25"
|
||
});
|
||
} else {
|
||
const var556 = 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-" + var556 + "'; img-src " + v557.cspSource + " https: data:; font-src " + v558.cspSource + "; worker-src 'none';\">\n <title>CursorPro</title>\n <script nonce=\"" + var556 + "\">\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\">v0.0</span>\n <button class=\"update-close\" id=\"updateBannerClose\" title=\"关闭\">×</button>\n </div>\n \n <!-- 管理员权限提示弹窗 -->\n <div class=\"modal-overlay\" id=\"adminModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">🔐</div>\n <div class=\"modal-title\">需要管理员权限</div>\n <div class=\"modal-message\">\n 请关闭 Cursor,右键点击图标<br>\n 选择 <span class=\"highlight\">以管理员身份运行</span>\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"adminModalClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 重置机器码权限提示弹窗 -->\n <div class=\"modal-overlay\" id=\"resetPermissionModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">🔐</div>\n <div class=\"modal-title\">需要管理员权限</div>\n <div class=\"modal-message\" style=\"text-align: left; line-height: 1.8;\">\n 重置机器码需要管理员权限才能完整执行。<br><br>\n 请按以下步骤操作:<br>\n <span style=\"color: #fbbf24;\">1.</span> 完全关闭 Cursor<br>\n <span style=\"color: #fbbf24;\">2.</span> 右键点击 Cursor 图标<br>\n <span style=\"color: #fbbf24;\">3.</span> 选择 <span class=\"highlight\">以管理员身份运行</span><br>\n <span style=\"color: #fbbf24;\">4.</span> 再次点击重置机器码\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"resetPermissionClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 重启提示弹窗 -->\n <div class=\"modal-overlay\" id=\"restartModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon success\">✓</div>\n <div class=\"modal-title\" id=\"restartModalTitle\">操作成功</div>\n <div class=\"modal-message\">\n 需要重启 Cursor 才能生效\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"restartNowBtn\">立即重启</button>\n <button class=\"modal-btn secondary\" id=\"restartLaterBtn\">稍后</button>\n </div>\n </div>\n </div>\n \n <!-- 激活码过期弹窗 -->\n <div class=\"modal-overlay\" id=\"expiredModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon\" style=\"background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\">⏰</div>\n <div class=\"modal-title\">激活码已过期</div>\n <div class=\"modal-message\">\n 您的激活码已过期,请续费后继续使用\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"expiredModalClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 清理环境确认弹窗 -->\n <div class=\"modal-overlay\" id=\"cleanEnvModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">⚠️</div>\n <div class=\"modal-title\">清理 Cursor 环境</div>\n <div class=\"modal-message\">\n 此操作会删除所有配置和登录信息<br>确定要继续吗?\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"cleanEnvConfirmBtn\">确定清理</button>\n <button class=\"modal-btn secondary\" id=\"cleanEnvCancelBtn\">取消</button>\n </div>\n </div>\n </div>\n \n <!-- 换号确认弹窗 -->\n <div class=\"modal-overlay\" id=\"switchConfirmModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">💰</div>\n <div class=\"modal-title\">账号未使用完</div>\n <div class=\"modal-message\">\n 当前账号 <span id=\"switchConfirmEmail\" style=\"color:#4caf50;\"></span><br>\n 已用额度: <span id=\"switchConfirmCost\" style=\"color:#ff9800;font-weight:bold;\">$0.00</span> (不足 $10)<br><br>\n 确定要换号吗?\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"switchConfirmBtn\">确认换号</button>\n <button class=\"modal-btn secondary\" id=\"switchCancelBtn\">取消</button>\n </div>\n </div>\n </div>\n \n <!-- 离线状态提示 -->\n <div class=\"offline-banner\" id=\"offlineBanner\">\n <span class=\"offline-icon\">📡</span>\n <div class=\"offline-text\">\n <div class=\"offline-title\">网络连接失败</div>\n <div class=\"offline-desc\">请检查网络后重试</div>\n </div>\n <button class=\"retry-btn\" id=\"retryConnectBtn\">重试</button>\n </div>\n \n <!-- 软件授权 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">🔐</span>\n <span>软件授权</span>\n <span class=\"status-badge\" id=\"authStatus\">未授权</span>\n </div>\n \n <div class=\"input-group\">\n <input type=\"text\" id=\"keyInput\" placeholder=\"请输入CDK激活码\">\n <button class=\"btn btn-primary\" id=\"activateBtn\"><span class=\"btn-text\">激活</span></button>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">激活码</span>\n <span class=\"info-value key-display\" id=\"keyDisplay\" title=\"点击复制\">尚未激活</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">到期时间</span>\n <span class=\"info-value\" id=\"expireDate\">尚未激活</span>\n </div>\n </div>\n \n <!-- 账号数据 (已隐藏) -->\n <div class=\"section\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">👤</span>\n <span>账号数据</span>\n <span class=\"status-badge\" id=\"accountStatus\">未激活</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">CI积分余额</span>\n <span class=\"info-value\">0 <button style=\"background:none;border:none;color:#007acc;cursor:pointer;\">🔄</button></span>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"switchBtn\" disabled>换号</button>\n <button class=\"btn btn-blue btn-block\" id=\"resetBtn\">重置机器码</button>\n <button class=\"btn btn-blue btn-block\" id=\"disableUpdateBtn\">禁用自动更新</button>\n <button class=\"btn btn-blue btn-block\" id=\"cleanEnvBtn\">清理Cursor环境</button>\n <button class=\"btn btn-red btn-block\" id=\"disableBtn\">停用插件</button>\n </div>\n \n <!-- 无感换号 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">⚡</span>\n <span>无感换号</span>\n <span class=\"status-badge\" id=\"seamlessStatus\">未启用</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">积分</span>\n <span class=\"info-value\" id=\"seamlessSwitchRemaining\">0</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">当前账号</span>\n <span class=\"info-value\" style=\"font-size:11px;\" id=\"seamlessCurrentAccount\">未分配</span>\n </div>\n \n <div class=\"switch-container\" style=\"margin: 12px 0;\">\n <span>免魔法模式</span>\n <span class=\"pro-badge\">PRO</span>\n <span style=\"margin-left: auto; color: #888; font-size: 11px;\"></span>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"seamlessProxySwitch\">\n <span class=\"slider\"></span>\n </label>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"enableSeamlessBtn\" disabled><span class=\"btn-text\">启用无感换号</span></button>\n <button class=\"btn btn-red btn-block\" id=\"seamlessResetMachineBtn\" style=\"display:none;\"><span class=\"btn-text\">重置机器码</span></button>\n <button class=\"btn btn-red btn-block\" id=\"disableSeamlessBtn\" style=\"display:none;\"><span class=\"btn-text\">禁用无感换号</span></button>\n <button class=\"btn btn-blue btn-block\" id=\"manualSwitchBtn\" style=\"display:none;\" disabled><span class=\"btn-text\">一键换号(扣1积分)</span></button>\n </div>\n \n <!-- 账号用量 -->\n <div class=\"section\" id=\"usageSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">📊</span>\n <span>账号用量</span>\n <button class=\"btn\" style=\"margin-left:auto;padding:4px 8px;font-size:11px;background:#3c3c3c;\" id=\"refreshUsageBtn\">🔄</button>\n </div>\n \n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">会员类型</span>\n <span class=\"info-value\" id=\"usageMemberType\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">试用剩余</span>\n <span class=\"info-value\" id=\"usageTrialDays\">-</span>\n </div>\n </div>\n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">请求次数</span>\n <span class=\"info-value\" id=\"usageRequestCount\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">已用额度</span>\n <span class=\"info-value\" id=\"usageCostUSD\">-</span>\n </div>\n </div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:center;\" id=\"usageUpdateTime\">-</p>\n </div>\n \n <!-- 公告 -->\n <div class=\"section\" id=\"announcementSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\" id=\"announcementIcon\">📢</span>\n <span>公告</span>\n <span class=\"announcement-badge\" id=\"announcementBadge\">info</span>\n </div>\n <div class=\"announcement-title\" id=\"announcementTitle\"></div>\n <div class=\"announcement-content\" id=\"announcementContent\"></div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:right;\" id=\"announcementTime\"></p>\n </div>\n \n <!-- 版本信息 -->\n <div class=\"section\" id=\"versionSection\">\n <div class=\"section-title\">\n <span class=\"icon\">📦</span>\n <span>版本信息</span>\n <span class=\"status-badge\" id=\"versionStatus\" style=\"display:none;\">有更新</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">当前版本</span>\n <span class=\"info-value\" id=\"currentVersion\">-</span>\n </div>\n <div class=\"info-row\" id=\"latestVersionRow\" style=\"display:none;\">\n <span class=\"info-label\">最新版本</span>\n <span class=\"info-value\" id=\"latestVersion\" style=\"color:#4caf50;\">-</span>\n </div>\n <p id=\"updateHint\" style=\"font-size:11px;color:#ff9800;margin-top:8px;display:none;\">\n ⚠️ 发现新版本,请更新插件以获取最新功能\n </p>\n </div>\n \n <!-- 页脚 -->\n <div class=\"footer\">\n <div class=\"footer-row\">\n <div class=\"auto-start\">\n <span>自动启动</span>\n <label class=\"switch switch-sm\">\n <input type=\"checkbox\" id=\"autoStartSwitch\" checked>\n <span class=\"slider\"></span>\n </label>\n </div>\n <div class=\"cursor-version\">\n <span>Cursor</span>\n <span class=\"version-num\" id=\"cursorVersion\">0.0.0</span>\n </div>\n </div>\n <div class=\"footer-row\" style=\"margin-top: 8px;\">\n <div style=\"font-size: 10px; color: #666; word-break: break-all;\">\n <span>路径: </span>\n <span id=\"cursorPath\" style=\"color: #888;\">获取中...</span>\n </div>\n </div>\n </div>\n \n <!-- Toast 通知 -->\n <div class=\"toast-container\" id=\"toastContainer\">\n <div class=\"toast\" id=\"toast\">\n <span class=\"toast-icon\" id=\"toastIcon\">✅</span>\n <span class=\"toast-message\" id=\"toastMessage\"></span>\n </div>\n </div>\n \n <script nonce=\"" + var556 + "\">\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 = '一键换号(扣1积分)';\n \n function disableSwitchBtnWithCountdown(seconds) {\n // 清除之前的定时器\n if (switchBtnCountdownTimer) {\n clearInterval(switchBtnCountdownTimer);\n }\n \n let remaining = seconds;\n manualSwitchBtn.disabled = true;\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n \n switchBtnCountdownTimer = setInterval(() => {\n remaining--;\n if (remaining <= 0) {\n clearInterval(switchBtnCountdownTimer);\n switchBtnCountdownTimer = null;\n manualSwitchBtn.disabled = false;\n manualSwitchBtn.querySelector('.btn-text').textContent = originalSwitchBtnText;\n } else {\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n }\n }, 1000);\n }\n \n // 弹窗元素\n const adminModal = document.getElementById('adminModal');\n const adminModalClose = document.getElementById('adminModalClose');\n const resetPermissionModal = document.getElementById('resetPermissionModal');\n const resetPermissionClose = document.getElementById('resetPermissionClose');\n const restartModal = document.getElementById('restartModal');\n const restartModalTitle = document.getElementById('restartModalTitle');\n const restartNowBtn = document.getElementById('restartNowBtn');\n const restartLaterBtn = document.getElementById('restartLaterBtn');\n const expiredModal = document.getElementById('expiredModal');\n const expiredModalClose = document.getElementById('expiredModalClose');\n const cleanEnvModal = document.getElementById('cleanEnvModal');\n const cleanEnvConfirmBtn = document.getElementById('cleanEnvConfirmBtn');\n const cleanEnvCancelBtn = document.getElementById('cleanEnvCancelBtn');\n \n // 换号确认弹窗元素\n const switchConfirmModal = document.getElementById('switchConfirmModal');\n const switchConfirmEmail = document.getElementById('switchConfirmEmail');\n const switchConfirmCost = document.getElementById('switchConfirmCost');\n const switchConfirmBtn = document.getElementById('switchConfirmBtn');\n const switchCancelBtn = document.getElementById('switchCancelBtn');\n \n // 显示管理员权限弹窗\n function showAdminModal() {\n adminModal.classList.add('show');\n }\n \n // 显示重置机器码权限提示弹窗\n function showAdminPermissionModal() {\n resetPermissionModal.classList.add('show');\n }\n \n // 重置机器码权限弹窗 - 关闭按钮\n resetPermissionClose.addEventListener('click', () => {\n resetPermissionModal.classList.remove('show');\n });\n \n // 点击遮罩关闭权限提示弹窗\n resetPermissionModal.addEventListener('click', (e) => {\n if (e.target === resetPermissionModal) {\n resetPermissionModal.classList.remove('show');\n }\n });\n \n // 显示重启提示弹窗\n let restartModalAction = 'reload'; // 'reload' 或 'close'\n \n function showRestartModal(title, action = 'reload') {\n restartModalTitle.textContent = title || '操作成功';\n restartModalAction = action;\n // 根据操作类型更新按钮文字\n restartNowBtn.textContent = action === 'close' ? '立即关闭 Cursor' : '立即重启';\n restartModal.classList.add('show');\n }\n \n // 显示过期弹窗\n function showExpiredModal() {\n expiredModal.classList.add('show');\n }\n \n // 关闭管理员弹窗\n adminModalClose.addEventListener('click', () => {\n adminModal.classList.remove('show');\n });\n \n // 点击遮罩关闭管理员弹窗\n adminModal.addEventListener('click', (e) => {\n if (e.target === adminModal) {\n adminModal.classList.remove('show');\n }\n });\n \n // 立即重启/关闭按钮\n restartNowBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n if (restartModalAction === 'close') {\n // 完全关闭 Cursor\n vscode.postMessage({ type: 'closeCursor' });\n } else {\n // 重新加载窗口\n vscode.postMessage({ type: 'reloadWindow' });\n }\n });\n \n // 稍后手动按钮\n restartLaterBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n });\n \n // 点击遮罩关闭重启弹窗\n restartModal.addEventListener('click', (e) => {\n if (e.target === restartModal) {\n restartModal.classList.remove('show');\n }\n });\n \n // 关闭过期弹窗\n expiredModalClose.addEventListener('click', () => {\n expiredModal.classList.remove('show');\n });\n \n // 点击遮罩关闭过期弹窗\n expiredModal.addEventListener('click', (e) => {\n if (e.target === expiredModal) {\n expiredModal.classList.remove('show');\n }\n });\n \n // 当前账号邮箱(用于查询用量)\n let currentAccountEmail = '';\n let usageRefreshInterval = null;\n // 存储完整激活码(用于复制)\n let fullActivationKey = '';\n // 当前剩余换号次数\n let currentSwitchRemaining = 0;\n // 当前到期时间\n let currentExpireDate = '';\n \n // 检查卡密是否已过期\n function isKeyExpired() {\n if (!currentExpireDate) return true;\n try {\n const expireTime = new Date(currentExpireDate).getTime();\n return Date.now() > expireTime;\n } catch {\n return true;\n }\n }\n \n // 格式化到期时间为北京时间\n function formatExpireDate(dateStr) {\n if (!dateStr) return '';\n try {\n // 后端返回的时间没有时区标识,假设是 UTC 时间\n // 将空格替换为T,并添加Z表示UTC\n let utcStr = dateStr;\n if (!dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {\n utcStr = dateStr.replace(' ', 'T') + 'Z';\n }\n const date = new Date(utcStr);\n \n // 使用中国时区格式化(UTC+8)\n return date.toLocaleString('zh-CN', {\n timeZone: 'Asia/Shanghai',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false\n });\n } catch {\n return dateStr; // 格式化失败返回原始值\n }\n }\n \n // 隐藏激活码后几位\n function maskKey(key) {\n if (!key || key.length <= 8) return key;\n return key.substring(0, key.length - 4) + '****';\n }\n \n // 点击激活码复制\n keyDisplay.addEventListener('click', () => {\n if (!fullActivationKey) return;\n navigator.clipboard.writeText(fullActivationKey).then(() => {\n keyDisplay.classList.add('copied');\n const originalText = keyDisplay.textContent;\n keyDisplay.textContent = '已复制!';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n }).catch(() => {\n // 降级方案\n const textarea = document.createElement('textarea');\n textarea.value = fullActivationKey;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n keyDisplay.classList.add('copied');\n keyDisplay.textContent = '已复制!';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n });\n });\n \n // Loading 状态控制\n function setButtonLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n btn.disabled = true;\n } else {\n btn.classList.remove('loading');\n // 注意:某些按钮可能需要保持禁用状态,由调用方控制\n }\n }\n \n function setRefreshLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n } else {\n btn.classList.remove('loading');\n }\n }\n \n // 获取初始状态\n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getSeamlessStatus' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n vscode.postMessage({ type: 'getProxyStatus' });\n vscode.postMessage({ type: 'getAnnouncement' });\n vscode.postMessage({ type: 'checkVersion' });\n vscode.postMessage({ type: 'getCursorRunningPath' });\n \n // 激活按钮\n activateBtn.addEventListener('click', () => {\n const key = keyInput.value.trim();\n if (!key) {\n return;\n }\n setButtonLoading(activateBtn, true);\n vscode.postMessage({ type: 'activate', key });\n });\n \n // 换号按钮\n switchBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'switch' });\n });\n \n // 重置机器码按钮\n resetBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 禁用自动更新按钮\n disableUpdateBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disableUpdate' });\n });\n \n // 清理Cursor环境按钮 - 显示确认弹窗\n cleanEnvBtn.addEventListener('click', () => {\n cleanEnvModal.classList.add('show');\n });\n \n // 确认清理\n cleanEnvConfirmBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n vscode.postMessage({ type: 'cleanEnv' });\n });\n \n // 取消清理\n cleanEnvCancelBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n });\n \n // 点击遮罩关闭清理弹窗\n cleanEnvModal.addEventListener('click', (e) => {\n if (e.target === cleanEnvModal) {\n cleanEnvModal.classList.remove('show');\n }\n });\n \n // 停用按钮\n disableBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disable' });\n });\n \n // 关闭更新提醒条\n updateBannerClose.addEventListener('click', () => {\n updateBanner.classList.remove('show');\n });\n \n // 免魔法开关\n seamlessProxySwitch.addEventListener('change', (e) => {\n const wantEnabled = e.target.checked;\n \n // 如果要开启免魔法,检查卡密是否过期(只要没过期就可以用,不管换号次数)\n if (wantEnabled && isKeyExpired()) {\n e.target.checked = false;\n showToast('授权码已过期,无法开启免魔法', '⚠️', 3000);\n return;\n }\n \n vscode.postMessage({ \n type: 'toggleProxy', \n enabled: wantEnabled,\n url: ''\n });\n });\n \n // 无感换号 - 启用按钮\n enableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(enableSeamlessBtn, true);\n vscode.postMessage({ type: 'injectSeamless' });\n });\n \n // 无感换号 - 禁用按钮\n disableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(disableSeamlessBtn, true);\n vscode.postMessage({ type: 'restoreSeamless' });\n });\n \n // 无感换号 - 手动换号按钮(先检查用量)\n manualSwitchBtn.addEventListener('click', () => {\n setButtonLoading(manualSwitchBtn, true);\n // 传递当前显示的账号邮箱\n vscode.postMessage({ type: 'checkUsageBeforeSwitch', email: currentAccountEmail });\n });\n \n // 换号确认弹窗 - 确认按钮\n switchConfirmBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, true);\n vscode.postMessage({ type: 'confirmSwitch' });\n });\n \n // 换号确认弹窗 - 取消按钮\n switchCancelBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n });\n \n // 换号确认弹窗 - 点击遮罩关闭\n switchConfirmModal.addEventListener('click', (e) => {\n if (e.target === switchConfirmModal) {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n }\n });\n \n // 无感换号区域 - 重置机器码按钮\n seamlessResetMachineBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 刷新用量按钮\n refreshUsageBtn.addEventListener('click', () => {\n if (currentAccountEmail) {\n setRefreshLoading(refreshUsageBtn, true);\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n });\n \n // 刷新用量函数\n function refreshUsage() {\n if (currentAccountEmail) {\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n }\n \n // 启动用量定时刷新 (每分钟一次)\n function startUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n }\n // 立即刷新一次\n refreshUsage();\n // 每60秒刷新一次\n usageRefreshInterval = setInterval(refreshUsage, 60000);\n }\n \n // 停止用量刷新\n function stopUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n usageRefreshInterval = null;\n }\n }\n \n // 更新用量显示\n function updateUsageDisplay(data) {\n if (!data) return;\n \n const subscription = data.subscription || {};\n const usage = data.usage || {};\n \n // 会员类型\n const memberTypeMap = {\n 'free_trial': '免费试用',\n 'pro': 'Pro会员',\n 'free': '免费版',\n 'business': '商业版'\n };\n usageMemberType.textContent = memberTypeMap[subscription.membershipType] || subscription.membershipType || '-';\n \n // 试用剩余天数\n if (subscription.daysRemainingOnTrial !== undefined && subscription.daysRemainingOnTrial !== null) {\n usageTrialDays.textContent = subscription.daysRemainingOnTrial + ' 天';\n usageTrialDays.style.color = subscription.daysRemainingOnTrial <= 3 ? '#f87171' : '#4ade80';\n } else {\n usageTrialDays.textContent = '-';\n usageTrialDays.style.color = '#fff';\n }\n \n // 请求次数\n usageRequestCount.textContent = (usage.totalUsageCount || 0) + ' 次';\n \n // 已用额度\n const costUSD = usage.totalCostUSD || 0;\n usageCostUSD.textContent = '$' + costUSD.toFixed(2);\n usageCostUSD.style.color = costUSD > 5 ? '#f87171' : (costUSD > 2 ? '#fbbf24' : '#4ade80');\n \n // 更新时间\n usageUpdateTime.textContent = '更新于 ' + new Date().toLocaleTimeString();\n }\n \n // 解析公告内容中的链接 {文字URL}\n function parseAnnouncementContent(content) {\n if (!content) return '';\n \n // 转义 HTML 特殊字符\n let escaped = content\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\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 通知,10秒后消失\n showToast('已切换到: ' + (message.email || '新账号') + ',约10秒内自动生效', '✅', 10000);\n // 刷新状态\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n // 禁用按钮10秒,显示倒计时\n disableSwitchBtnWithCountdown(10);\n } else {\n manualSwitchBtn.disabled = false;\n showToast(message.error || '换号失败', '❌', 5000);\n }\n break;\n \n case 'proxyStatus':\n // 设置免魔法开关状态\n seamlessProxySwitch.checked = message.enabled;\n break;\n \n // 公告\n case 'announcement':\n if (message.success && message.data) {\n updateAnnouncementDisplay(message.data);\n } else {\n announcementSection.style.display = 'none';\n }\n break;\n \n // 版本检查\n case 'versionCheck':\n currentVersionEl.textContent = message.currentVersion || '-';\n if (message.success && message.hasUpdate) {\n // 有更新\n latestVersionEl.textContent = message.latestVersion;\n latestVersionRow.style.display = 'flex';\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#ff9800';\n updateHint.style.display = 'block';\n \n // 显示顶部更新提醒条\n updateBannerVersion.textContent = 'v' + message.latestVersion;\n updateBanner.classList.add('show');\n } else if (message.success) {\n // 已是最新版\n versionStatus.textContent = '最新';\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#4caf50';\n latestVersionRow.style.display = 'none';\n updateHint.style.display = 'none';\n updateBanner.classList.remove('show');\n }\n break;\n \n // Cursor 运行路径\n case 'cursorRunningPath':\n if (message.path) {\n const pathText = message.path + (message.packageExists ? ' ✓' : ' ✗');\n cursorPath.textContent = pathText;\n cursorPath.style.color = message.packageExists ? '#4ade80' : '#f87171';\n // 同时更新版本号\n if (message.cursorVersion) {\n cursorVersion.textContent = message.cursorVersion;\n }\n } else {\n cursorPath.textContent = '未找到';\n cursorPath.style.color = '#f87171';\n }\n break;\n \n // 管理员权限不足提示\n case 'adminPermissionRequired':\n showAdminPermissionModal();\n break;\n \n // 机器码重置\n case 'machineIdReset':\n if (message.success && message.needRestart) {\n // 机器码重置需要完全关闭 Cursor,不是 reload\n showRestartModal(message.message || '机器码重置成功', 'close');\n }\n break;\n \n // 通用 Toast 消息\n case 'showToast':\n showToast(message.message || '', message.icon || '📢', 10000);\n break;\n \n // 网络状态\n case 'networkStatus':\n updateOfflineStatus(!message.online);\n break;\n }\n });\n \n // 离线状态显示/隐藏\n let wasOffline = false; // 跟踪之前是否离线\n function updateOfflineStatus(isOffline) {\n if (isOffline) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else {\n offlineBanner.classList.remove('show');\n // 只有从离线恢复到在线时才刷新状态\n if (wasOffline) {\n wasOffline = false;\n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n }\n }\n }\n \n // 重试连接按钮\n retryConnectBtn.addEventListener('click', async () => {\n retryConnectBtn.classList.add('loading');\n retryConnectBtn.textContent = '连接中...';\n \n // 发起真正的网络请求来测试网络\n vscode.postMessage({ type: 'retryConnect' });\n \n // 5秒后恢复按钮状态(给网络请求足够时间)\n setTimeout(() => {\n retryConnectBtn.classList.remove('loading');\n retryConnectBtn.textContent = '重试';\n }, 5000);\n });\n \n function updateUI(state) {\n if (state.isActivated) {\n authStatus.textContent = '已授权';\n authStatus.className = 'status-badge active';\n accountStatus.textContent = '已激活';\n accountStatus.className = 'status-badge active';\n switchBtn.disabled = false;\n fullActivationKey = state.key;\n keyDisplay.textContent = maskKey(fullActivationKey);\n // 更新到期时间\n currentExpireDate = state.expireDate || '';\n expireDate.textContent = formatExpireDate(currentExpireDate);\n // 更新换号次数\n if (state.switchRemaining !== undefined) {\n currentSwitchRemaining = state.switchRemaining;\n switchCount.textContent = state.switchRemaining + '/' + (state.switchLimit || 100);\n }\n // 启用无感换号按钮(只有过期才禁用)\n enableSeamlessBtn.disabled = isKeyExpired();\n }\n cursorVersion.textContent = state.cursorVersion || '0.0.0';\n \n // 根据网络状态显示/隐藏离线提示\n if (state.isOnline === false) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else if (state.isOnline === true) {\n // 网络恢复,隐藏离线提示\n offlineBanner.classList.remove('show');\n wasOffline = false;\n }\n }\n </script>\n</body>\n</html>";
|
||
}
|
||
}
|
||
}
|
||
async _handleGetAccountUsage(arg301) {
|
||
try {
|
||
if (!arg301) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': "\u672A\u63D0\u4F9B\u8D26\u53F7\u90AE\u7BB1"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const var560 = client_1.getApiUrl() + "/api/cursor-accounts/query?email=" + encodeURIComponent(arg301) + '&refresh=true';
|
||
const var561 = await fetch(var560);
|
||
const var562 = await var561.json();
|
||
if (var562.success && var562.data) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': true,
|
||
'data': var562.data
|
||
});
|
||
const var563 = var562.data.usage || {};
|
||
const var564 = var563.totalUsageCount || 0x0;
|
||
const var565 = parseFloat(var563.totalCostUSD || 0x0);
|
||
0x0;
|
||
extension_1.updateUsageStatusBar(var564, var565);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': var562.error || "\u83B7\u53D6\u7528\u91CF\u5931\u8D25"
|
||
});
|
||
}
|
||
} catch (v566) {
|
||
if ("NYkng" !== 'NYkng') {
|
||
v567.error("[CursorPro] Toggle proxy error:", v568);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u66F4\u65B0\u914D\u7F6E\u5931\u8D25"
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': v566.message || "\u8BF7\u6C42\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
async _handleGetAnnouncement() {
|
||
try {
|
||
0x0;
|
||
const var570 = client_1.getApiUrl() + "/api/announcements/latest";
|
||
const var571 = await fetch(var570);
|
||
const var572 = await var571.json();
|
||
if (var572.success && var572.data) {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': true,
|
||
'data': var572.data
|
||
});
|
||
} else {
|
||
if ('ysOwe' === "ysOwe") {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': var572.error || "\u83B7\u53D6\u516C\u544A\u5931\u8D25"
|
||
});
|
||
} else {
|
||
const var573 = this._context.globalState.get("cursorpro.key");
|
||
const var574 = this._context.globalState.get("cursorpro.expireDate");
|
||
if (!var573) {
|
||
this._postMessage({
|
||
'type': 'proxyUpdated',
|
||
'success': false,
|
||
'error': '请先激活授权码'
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
if (var574) {
|
||
const var575 = new v576(var574).getTime();
|
||
if (v577.now() > var575) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "\u6388\u6743\u7801\u5DF2\u8FC7\u671F\uFF0C\u65E0\u6CD5\u5F00\u542F\u514D\u9B54\u6CD5"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "授权码已过期,无法开启免魔法",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (v578) {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': v578.message || "\u8BF7\u6C42\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
async _handleCheckVersion() {
|
||
try {
|
||
0x0;
|
||
const var580 = await client_1.getLatestVersion();
|
||
if (var580.success && var580.version) {
|
||
const var581 = var580.version;
|
||
const var582 = CursorProViewProvider.CURRENT_VERSION;
|
||
const var583 = this._compareVersions(var581, var582) > 0x0;
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': true,
|
||
'currentVersion': var582,
|
||
'latestVersion': var581,
|
||
'hasUpdate': var583
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': var580.error || "获取版本失败"
|
||
});
|
||
}
|
||
} catch (v584) {
|
||
if ("vUHCr" === "LOWQh") {
|
||
v585.log("[CursorPro] \u4F7F\u7528\u7528\u6237\u914D\u7F6E\u7684 Cursor \u8DEF\u5F84:", v586);
|
||
this._cachedCursorPath = v587;
|
||
return v588;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': v584.message || "\u8BF7\u6C42\u5931\u8D25"
|
||
});
|
||
}
|
||
}
|
||
}
|
||
_compareVersions(arg314, arg315) {
|
||
const var590 = arg314.split('.').map(Number);
|
||
const var591 = arg315.split('.').map(Number);
|
||
const var592 = Math.max(var590.length, var591.length);
|
||
for (let var593 = 0x0; var593 < var592; var593++) {
|
||
const var594 = var590[var593] || 0x0;
|
||
const var595 = var591[var593] || 0x0;
|
||
if (var594 > var595) {
|
||
return 0x1;
|
||
}
|
||
if (var594 < var595) {
|
||
return -0x1;
|
||
}
|
||
}
|
||
return 0x0;
|
||
}
|
||
async _handleGetCursorRunningPath() {
|
||
try {
|
||
if ("UBmxB" !== "UBmxB") {
|
||
return [{
|
||
'email': v597,
|
||
'access_token': v598,
|
||
'refresh_token': v599 || v600
|
||
}];
|
||
} else {
|
||
const var601 = process.platform;
|
||
let var602 = "\u672A\u627E\u5230";
|
||
let var603 = '';
|
||
const var604 = vscode.workspace.getConfiguration("cursorpro");
|
||
const var605 = var604.get("cursorPath");
|
||
if (var605 && fs.existsSync(var605)) {
|
||
var602 = var605;
|
||
if (var601 === "darwin") {
|
||
var603 = path.join(var605, "Contents", "Resources", "app", "package.json");
|
||
} else {
|
||
var603 = path.join(var605, "resources", "app", "package.json");
|
||
}
|
||
console.log("[CursorPro] \u4F7F\u7528\u7528\u6237\u914D\u7F6E\u7684\u8DEF\u5F84:", var605);
|
||
} else {
|
||
if (var601 === "win32") {
|
||
try {
|
||
if ("BbiYe" === "eKgQY") {
|
||
v606 = true;
|
||
delete v607[v608];
|
||
} else {
|
||
const {
|
||
stdout: v609
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const var610 = v609.match(/ExecutablePath=(.+)/);
|
||
if (var610 && var610[0x1]) {
|
||
const var611 = var610[0x1].trim();
|
||
var602 = path.dirname(var611);
|
||
var603 = path.join(var602, "resources", "app", "package.json");
|
||
}
|
||
}
|
||
} catch (v612) {
|
||
if ("snkgX" === "BoVlO") {
|
||
v613.log("[CursorPro] \u4F7F\u7528 VS Code API \u83B7\u53D6\u7248\u672C:", v614.version);
|
||
return v615.version;
|
||
} else {
|
||
console.log("[CursorPro] WMIC 获取路径失败:", v612);
|
||
}
|
||
}
|
||
if (var602 === "\u672A\u627E\u5230") {
|
||
if ("DZGqD" !== "DZGqD") {
|
||
try {
|
||
const var616 = this._getHostsPath();
|
||
if (v617.existsSync(var616)) {
|
||
return v618.readFileSync(var616, "utf-8");
|
||
}
|
||
} catch (v619) {
|
||
v620.error("[CursorPro] Read hosts error:", v619);
|
||
}
|
||
return '';
|
||
} else {
|
||
const var621 = process.env.LOCALAPPDATA || '';
|
||
const var622 = [path.join(var621, "Programs", 'cursor'), path.join(var621, "cursor")];
|
||
for (const var623 of var622) {
|
||
const var624 = path.join(var623, "resources", "app", "package.json");
|
||
if (fs.existsSync(var624)) {
|
||
var602 = var623;
|
||
var603 = var624;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (var601 === "darwin") {
|
||
if ("ZhNbs" === "WrYgR") {
|
||
return null;
|
||
} else {
|
||
var602 = (await this._getCursorInstallPath()) || "/Applications/Cursor.app";
|
||
var603 = path.join(var602, "Contents", "Resources", 'app', "package.json");
|
||
}
|
||
} else {
|
||
const var625 = process.env.HOME || '';
|
||
const var626 = ["/usr/share/cursor", path.join(var625, ".local", "share", "cursor")];
|
||
for (const var627 of var626) {
|
||
if (fs.existsSync(var627)) {
|
||
var602 = var627;
|
||
var603 = path.join(var627, "resources", 'app', "package.json");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const var628 = var603 && fs.existsSync(var603);
|
||
let var629 = '';
|
||
if (var628) {
|
||
try {
|
||
if ("mnjXn" === "WRpnS") {
|
||
if (v630.existsSync(v631)) {
|
||
return v632;
|
||
}
|
||
} else {
|
||
const var633 = fs.readFileSync(var603, "utf-8");
|
||
const var634 = JSON.parse(var633);
|
||
var629 = var634.version || '';
|
||
console.log("[CursorPro] \u4ECE\u8DEF\u5F84\u83B7\u53D6 Cursor \u7248\u672C:", var629);
|
||
}
|
||
} catch (v635) {
|
||
console.log("[CursorPro] 读取 package.json 失败:", v635);
|
||
}
|
||
}
|
||
this._postMessage({
|
||
'type': 'cursorRunningPath',
|
||
'path': var602,
|
||
'packageJsonPath': var603,
|
||
'packageExists': var628,
|
||
'cursorVersion': var629
|
||
});
|
||
}
|
||
} catch (v636) {
|
||
this._postMessage({
|
||
'type': "cursorRunningPath",
|
||
'path': "获取失败: " + (v636.message || v636),
|
||
'packageJsonPath': '',
|
||
'packageExists': false,
|
||
'cursorVersion': ''
|
||
});
|
||
}
|
||
}
|
||
async _handleCheckUsageBeforeSwitch(arg330) {
|
||
try {
|
||
if ('pydTE' !== "klkmy") {
|
||
const var638 = this._context.globalState.get("cursorpro.key");
|
||
if (!var638) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': false,
|
||
'error': "\u672A\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
}
|
||
if (!arg330) {
|
||
if ("taehn" === "taehn") {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
return;
|
||
} else {
|
||
v639.window.showErrorMessage("清理失败: " + v640);
|
||
}
|
||
}
|
||
0x0;
|
||
const var641 = client_1.getApiUrl() + '/api/cursor-accounts/query?email=' + encodeURIComponent(arg330) + "&refresh=false";
|
||
const var642 = await fetch(var641);
|
||
const var643 = await var642.json();
|
||
if (var643.success && var643.data) {
|
||
if ("Drfmf" !== "lZGGL") {
|
||
const var644 = var643.data.usage || {};
|
||
const var645 = parseFloat(var644.totalCostUSD || 0x0);
|
||
if (var645 < 0xa) {
|
||
if ("aCFwf" === "MhURV") {
|
||
v646.log("[CursorPro] PowerShell Get-Process \u83B7\u53D6\u8DEF\u5F84\u5931\u8D25");
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': true,
|
||
'costUSD': var645.toFixed(0x2),
|
||
'email': arg330
|
||
});
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} else {
|
||
v647.rmSync(v648, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
v649++;
|
||
}
|
||
} else if ('GbpRm' !== "PgKQl") {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
} else {
|
||
v650.log("[CursorPro] Direct write failed, trying to grant permission");
|
||
}
|
||
} else {
|
||
if (v651 === v652) {
|
||
v653 = v654;
|
||
}
|
||
v655[v656] = v657[v658];
|
||
}
|
||
} catch (v659) {
|
||
if ("hlMgj" !== "zdYFk") {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
} else {
|
||
v660.push(v661.name);
|
||
}
|
||
}
|
||
}
|
||
async _handleManualSeamlessSwitch() {
|
||
try {
|
||
if ("exYVj" === 'exYVj') {
|
||
const var663 = this._context.globalState.get("cursorpro.key");
|
||
if (!var663) {
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': "\u672A\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const var664 = await client_1.switchSeamlessToken(var663);
|
||
if (var664.switched) {
|
||
if (var664.email) {
|
||
await this._context.globalState.update("cursorpro.seamlessCurrentAccount", var664.email);
|
||
}
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': true,
|
||
'email': var664.email,
|
||
'switchRemaining': var664.switchRemaining
|
||
});
|
||
} else {
|
||
if ("JYbEY" !== "FYJZv") {
|
||
const var665 = var664.message || var664.error || "\u6362\u53F7\u5931\u8D25";
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': var665
|
||
});
|
||
} else {
|
||
v666.warn("[CursorPro] Linux \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v667);
|
||
}
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'cursorRunningPath',
|
||
'path': "\u83B7\u53D6\u5931\u8D25: " + (v668.message || v669),
|
||
'packageJsonPath': '',
|
||
'packageExists': false,
|
||
'cursorVersion': ''
|
||
});
|
||
}
|
||
} catch (v670) {
|
||
const var671 = v670?.message || "\u8FDE\u63A5\u670D\u52A1\u5668\u5931\u8D25";
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': var671
|
||
});
|
||
}
|
||
}
|
||
async _handleGetCursorPath() {
|
||
try {
|
||
const var673 = process.platform;
|
||
let var674 = '';
|
||
let var675 = '';
|
||
if (var673 === "win32") {
|
||
try {
|
||
const {
|
||
stdout: v676
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const var677 = v676.match(/ExecutablePath=(.+)/);
|
||
if (var677 && var677[0x1]) {
|
||
const var678 = var677[0x1].trim();
|
||
var674 = path.dirname(var678);
|
||
}
|
||
} catch (v679) {
|
||
if ("hKLIf" !== "hKLIf") {
|
||
if (v680.code === "EPERM" || v681.code === "EACCES") {
|
||
const var682 = "\u6CA1\u6709\u5199\u5165\u6743\u9650";
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': var682,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
throw v683;
|
||
} else {
|
||
try {
|
||
if ("BKPzM" === "BKPzM") {
|
||
const {
|
||
stdout: v684
|
||
} = await execAsync('powershell -Command "Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path"');
|
||
if (v684.trim()) {
|
||
if ("ATCVo" !== "lshQE") {
|
||
var674 = path.dirname(v684.trim());
|
||
} else {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': '连接服务器失败'
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
v685 = v686.substring(0x0, v687) + v688.substring(v689 + this.HOSTS_MARKER_END.length);
|
||
}
|
||
} catch (v690) {
|
||
console.warn("[CursorPro] \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v690);
|
||
}
|
||
}
|
||
}
|
||
const var691 = process.env.APPDATA || '';
|
||
var675 = path.join(var691, "Cursor");
|
||
} else {
|
||
if (var673 === "darwin") {
|
||
try {
|
||
const {
|
||
stdout: v692
|
||
} = await execAsync("ps aux | grep -i \"[C]ursor\" | head -1 | awk '{print $11}'");
|
||
if (v692.trim()) {
|
||
const var693 = v692.trim();
|
||
const var694 = var693.match(/(.+\.app)/);
|
||
if (var694) {
|
||
var674 = var694[0x1];
|
||
} else {
|
||
var674 = path.dirname(var693);
|
||
}
|
||
}
|
||
} catch (v695) {
|
||
console.warn("[CursorPro] 获取进程路径失败:", v695);
|
||
}
|
||
const var696 = process.env.HOME || '';
|
||
var675 = path.join(var696, 'Library', "Application Support", "Cursor");
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: v697
|
||
} = await execAsync("ps aux | grep -i \"[c]ursor\" | head -1 | awk '{print $11}'");
|
||
if (v697.trim()) {
|
||
if ("TJpGd" === "TJpGd") {
|
||
var674 = path.dirname(v697.trim());
|
||
} else {
|
||
v698 = "\u6CA1\u6709\u5199\u5165\u6743\u9650\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C: sudo chmod -R 777 /Applications/Cursor.app";
|
||
}
|
||
}
|
||
} catch (v699) {
|
||
console.warn("[CursorPro] \u83B7\u53D6\u8FDB\u7A0B\u8DEF\u5F84\u5931\u8D25:", v699);
|
||
}
|
||
const var700 = process.env.HOME || '';
|
||
var675 = path.join(var700, ".config", "Cursor");
|
||
}
|
||
}
|
||
if (!var674) {
|
||
if ("dWdoR" === "rnnax") {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "\u8BF7\u5148\u6FC0\u6D3B\u6388\u6743\u7801"
|
||
});
|
||
return;
|
||
} else {
|
||
var674 = "\u672A\u68C0\u6D4B\u5230\u8FD0\u884C\u4E2D\u7684Cursor\u8FDB\u7A0B";
|
||
}
|
||
}
|
||
let var701 = '';
|
||
if (var674 && !var674.includes("\u672A\u68C0\u6D4B")) {
|
||
if (var673 === "win32") {
|
||
if ('lPhmJ' !== "lPhmJ") {
|
||
v702["telemetry.devDeviceId"] = v703.devDeviceId;
|
||
} else {
|
||
var701 = path.join(var674, 'resources', "app", 'out', 'vs', 'workbench', "workbench.desktop.main.js");
|
||
}
|
||
} else {
|
||
if (var673 === "darwin") {
|
||
if ("RgvPD" === "RgvPD") {
|
||
var701 = path.join(var674, "Contents", "Resources", "app", "out", 'vs', "workbench", 'workbench.desktop.main.js');
|
||
} else {
|
||
v704.writeFileSync(v705, v706, 'utf-8');
|
||
v707 = true;
|
||
}
|
||
} else {
|
||
if ("PYNDj" === "fFSTL") {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "\u542F\u7528\u5931\u8D25"
|
||
});
|
||
return;
|
||
} else {
|
||
var701 = path.join(var674, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
}
|
||
}
|
||
if (!fs.existsSync(var701)) {
|
||
if ("LwcoD" !== "bwKxQ") {
|
||
var701 = (await this._getWorkbenchPathAsync()) || "\u672A\u627E\u5230";
|
||
} else {
|
||
const var708 = v709.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (var708 && var708[0x1] && v710.existsSync(var708[0x1].trim())) {
|
||
v711 = var708[0x1].trim();
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if ("EvRNl" !== "npeOU") {
|
||
var701 = (await this._getWorkbenchPathAsync()) || "未找到";
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
const var712 = var674 && !var674.includes("\u672A\u68C0\u6D4B") ? fs.existsSync(var674) : false;
|
||
const var713 = var675 ? fs.existsSync(var675) : false;
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': var712 ? var674 : var674 || "未找到",
|
||
'dataPath': var713 ? var675 : "\u672A\u627E\u5230",
|
||
'workbenchPath': var701,
|
||
'platform': var673
|
||
});
|
||
} catch (v714) {
|
||
if ("ZzJmG" !== "GnlEK") {
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': "\u83B7\u53D6\u5931\u8D25",
|
||
'dataPath': '获取失败',
|
||
'workbenchPath': "\u83B7\u53D6\u5931\u8D25",
|
||
'error': v714.message
|
||
});
|
||
} else {
|
||
this._view?.webview.postMessage(v715);
|
||
}
|
||
}
|
||
}
|
||
async _loadAccountsFromDB() {
|
||
try {
|
||
0x0;
|
||
const var717 = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: v718
|
||
} = var717;
|
||
if (!fs.existsSync(v718)) {
|
||
return 'OIoiO' === "YEFIo" ? (v719.log("[CursorPro] \u627E\u5230 Cursor \u7248\u672C:", v720.version, "\u8DEF\u5F84:", v721), v722.version) : [];
|
||
}
|
||
0x0;
|
||
const var723 = await sqlite_1.sqliteGet(v718, "cursorAuth/accessToken");
|
||
0x0;
|
||
const var724 = await sqlite_1.sqliteGet(v718, "cursorAuth/refreshToken");
|
||
0x0;
|
||
const var725 = await sqlite_1.sqliteGet(v718, "cursorAuth/cachedEmail");
|
||
if (var723 && var725) {
|
||
return [{
|
||
'email': var725,
|
||
'access_token': var723,
|
||
'refresh_token': var724 || var723
|
||
}];
|
||
}
|
||
return [];
|
||
} catch (v726) {
|
||
console.error("[CursorPro] 读取账号失败:", v726);
|
||
return [];
|
||
}
|
||
}
|
||
async _sendState() {
|
||
const var728 = this._context.globalState.get("cursorpro.key");
|
||
const var729 = this._context.globalState.get('cursorpro.expireDate');
|
||
const var730 = this._context.globalState.get("cursorpro.switchRemaining");
|
||
const var731 = this._context.globalState.get("cursorpro.switchLimit");
|
||
const var732 = await this._getCursorVersion();
|
||
0x0;
|
||
const var733 = client_1.getOnlineStatus();
|
||
this._postMessage({
|
||
'type': "state",
|
||
'isActivated': !!var728,
|
||
'key': var728 || '',
|
||
'expireDate': var729 || '',
|
||
'switchRemaining': var730 ?? 0x0,
|
||
'switchLimit': var731 ?? 0x64,
|
||
'cursorVersion': var732,
|
||
'isOnline': var733
|
||
});
|
||
}
|
||
async _handleRetryConnect() {
|
||
try {
|
||
const var735 = this._context.globalState.get("cursorpro.key");
|
||
if (var735) {
|
||
if ("NZAQL" === "NZAQL") {
|
||
0x0;
|
||
await client_1.verifyKey(var735);
|
||
} else {
|
||
const var736 = v737.split('.').map(v738);
|
||
const var739 = v740.split('.').map(v741);
|
||
const var742 = v743.max(var736.length, var739.length);
|
||
for (let var744 = 0x0; var744 < var742; var744++) {
|
||
const var745 = var736[var744] || 0x0;
|
||
const var746 = var739[var744] || 0x0;
|
||
if (var745 > var746) {
|
||
return 0x1;
|
||
}
|
||
if (var745 < var746) {
|
||
return -0x1;
|
||
}
|
||
}
|
||
return 0x0;
|
||
}
|
||
} else {
|
||
if ("ANSiR" !== "PPUYG") {
|
||
0x0;
|
||
const var747 = client_1.getApiUrl() + '/api/announcements/latest';
|
||
await fetch(var747, {
|
||
'method': 'GET'
|
||
});
|
||
} else {
|
||
v748 = v749[0x1].trim();
|
||
}
|
||
}
|
||
await this._sendState();
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': true
|
||
});
|
||
} catch (v750) {
|
||
console.error("[CursorPro] Retry connect failed:", v750);
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': false
|
||
});
|
||
}
|
||
}
|
||
async _getCursorVersion() {
|
||
try {
|
||
const var752 = process.platform;
|
||
const var753 = [];
|
||
const var754 = await this._getCursorInstallPath();
|
||
if (var754) {
|
||
if ("LtYMm" !== "FXgaA") {
|
||
if (var752 === "darwin") {
|
||
var753.push(path.join(var754, "Contents", "Resources", "app", 'package.json'));
|
||
} else {
|
||
var753.push(path.join(var754, "resources", 'app', "package.json"));
|
||
}
|
||
} else {
|
||
v755.rmSync(v756, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
}
|
||
}
|
||
if (var752 === "win32") {
|
||
const var757 = process.env.LOCALAPPDATA || '';
|
||
const var758 = process.env.USERPROFILE || '';
|
||
const var759 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const var760 = process.env['ProgramFiles(x86)'] || "C:\\Program Files (x86)";
|
||
var753.push(path.join(var757, "Programs", "Cursor", "resources", "app", "package.json"), path.join(var757, "Programs", "cursor", "resources", 'app', "package.json"), path.join(var758, "AppData", "Local", "Programs", "Cursor", "resources", "app", "package.json"), path.join(var759, "Cursor", "resources", 'app', "package.json"), path.join(var759, "cursor", "resources", "app", "package.json"), path.join(var760, "Cursor", "resources", "app", "package.json"));
|
||
} else {
|
||
if (var752 === "darwin") {
|
||
var753.push("/Applications/Cursor.app/Contents/Resources/app/package.json");
|
||
} else {
|
||
const var761 = process.env.HOME || '';
|
||
var753.push("/usr/share/cursor/resources/app/package.json", "/opt/Cursor/resources/app/package.json", "/opt/cursor/resources/app/package.json", path.join(var761, ".local", 'share', "cursor", "resources", 'app', "package.json"));
|
||
}
|
||
}
|
||
for (const var762 of var753) {
|
||
if ("olTfK" !== "olTfK") {
|
||
return;
|
||
} else {
|
||
try {
|
||
if ("gPJxP" !== "eTsxR") {
|
||
if (fs.existsSync(var762)) {
|
||
if ("dqmkC" !== "dqmkC") {
|
||
v763.log("[CursorPro] \u9996\u6B21\u542F\u7528\uFF0C\u4ECE\u5907\u4EFD\u6062\u590D\u5E72\u51C0\u7684 workbench \u6587\u4EF6");
|
||
try {
|
||
v764.copyFileSync(v765, v766);
|
||
v767.log("[CursorPro] \u5907\u4EFD\u6062\u590D\u6210\u529F");
|
||
} catch (v768) {
|
||
v769.error("[CursorPro] \u5907\u4EFD\u6062\u590D\u5931\u8D25:", v768);
|
||
}
|
||
} else {
|
||
const var770 = fs.readFileSync(var762, "utf-8");
|
||
const var771 = JSON.parse(var770);
|
||
if (var771.version) {
|
||
if ("Eoizq" !== "YcIFS") {
|
||
console.log("[CursorPro] 找到 Cursor 版本:", var771.version, "\u8DEF\u5F84:", var762);
|
||
return var771.version;
|
||
} else {
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': "获取失败",
|
||
'dataPath': "\u83B7\u53D6\u5931\u8D25",
|
||
'workbenchPath': "\u83B7\u53D6\u5931\u8D25",
|
||
'error': v772.message
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else if (v773.statSync(v774).isDirectory()) {
|
||
v775.rmSync(v776, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
} else {
|
||
v777.unlinkSync(v778);
|
||
}
|
||
} catch (v779) {
|
||
console.log("[CursorPro] \u5C1D\u8BD5\u8DEF\u5F84\u5931\u8D25:", var762, v779);
|
||
}
|
||
}
|
||
}
|
||
try {
|
||
const var780 = require("vscode");
|
||
if (var780.version) {
|
||
console.log("[CursorPro] 使用 VS Code API 获取版本:", var780.version);
|
||
return var780.version;
|
||
}
|
||
} catch (v781) {}
|
||
console.log("[CursorPro] \u672A\u627E\u5230 Cursor \u7248\u672C\uFF0C\u5C1D\u8BD5\u7684\u8DEF\u5F84:", var753);
|
||
return '未知';
|
||
} catch (v782) {
|
||
console.error("[CursorPro] \u83B7\u53D6 Cursor \u7248\u672C\u5931\u8D25:", v782);
|
||
return '未知';
|
||
}
|
||
}
|
||
_postMessage(arg388) {
|
||
this._view?.webview.postMessage(arg388);
|
||
}
|
||
_getNonce() {
|
||
let var784 = '';
|
||
const var785 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
for (let var786 = 0x0; var786 < 0x20; var786++) {
|
||
if ("UMZcN" === "UMZcN") {
|
||
var784 += var785.charAt(Math.floor(Math.random() * var785.length));
|
||
} else if (v787.existsSync(v788)) {
|
||
v789.rmSync(v790, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
v791++;
|
||
v792.log("[CursorPro] 已清理: " + v793);
|
||
}
|
||
}
|
||
return var784;
|
||
}
|
||
_getHtmlContent(arg395) {
|
||
const var794 = 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-" + var794 + "'; img-src " + arg395.cspSource + " https: data:; font-src " + arg395.cspSource + "; worker-src 'none';\">\n <title>CursorPro</title>\n <script nonce=\"" + var794 + "\">\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\">v0.0</span>\n <button class=\"update-close\" id=\"updateBannerClose\" title=\"关闭\">×</button>\n </div>\n \n <!-- 管理员权限提示弹窗 -->\n <div class=\"modal-overlay\" id=\"adminModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">🔐</div>\n <div class=\"modal-title\">需要管理员权限</div>\n <div class=\"modal-message\">\n 请关闭 Cursor,右键点击图标<br>\n 选择 <span class=\"highlight\">以管理员身份运行</span>\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"adminModalClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 重置机器码权限提示弹窗 -->\n <div class=\"modal-overlay\" id=\"resetPermissionModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">🔐</div>\n <div class=\"modal-title\">需要管理员权限</div>\n <div class=\"modal-message\" style=\"text-align: left; line-height: 1.8;\">\n 重置机器码需要管理员权限才能完整执行。<br><br>\n 请按以下步骤操作:<br>\n <span style=\"color: #fbbf24;\">1.</span> 完全关闭 Cursor<br>\n <span style=\"color: #fbbf24;\">2.</span> 右键点击 Cursor 图标<br>\n <span style=\"color: #fbbf24;\">3.</span> 选择 <span class=\"highlight\">以管理员身份运行</span><br>\n <span style=\"color: #fbbf24;\">4.</span> 再次点击重置机器码\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"resetPermissionClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 重启提示弹窗 -->\n <div class=\"modal-overlay\" id=\"restartModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon success\">✓</div>\n <div class=\"modal-title\" id=\"restartModalTitle\">操作成功</div>\n <div class=\"modal-message\">\n 需要重启 Cursor 才能生效\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"restartNowBtn\">立即重启</button>\n <button class=\"modal-btn secondary\" id=\"restartLaterBtn\">稍后</button>\n </div>\n </div>\n </div>\n \n <!-- 激活码过期弹窗 -->\n <div class=\"modal-overlay\" id=\"expiredModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon\" style=\"background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\">⏰</div>\n <div class=\"modal-title\">激活码已过期</div>\n <div class=\"modal-message\">\n 您的激活码已过期,请续费后继续使用\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"expiredModalClose\">我知道了</button>\n </div>\n </div>\n </div>\n \n <!-- 清理环境确认弹窗 -->\n <div class=\"modal-overlay\" id=\"cleanEnvModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">⚠️</div>\n <div class=\"modal-title\">清理 Cursor 环境</div>\n <div class=\"modal-message\">\n 此操作会删除所有配置和登录信息<br>确定要继续吗?\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"cleanEnvConfirmBtn\">确定清理</button>\n <button class=\"modal-btn secondary\" id=\"cleanEnvCancelBtn\">取消</button>\n </div>\n </div>\n </div>\n \n <!-- 换号确认弹窗 -->\n <div class=\"modal-overlay\" id=\"switchConfirmModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">💰</div>\n <div class=\"modal-title\">账号未使用完</div>\n <div class=\"modal-message\">\n 当前账号 <span id=\"switchConfirmEmail\" style=\"color:#4caf50;\"></span><br>\n 已用额度: <span id=\"switchConfirmCost\" style=\"color:#ff9800;font-weight:bold;\">$0.00</span> (不足 $10)<br><br>\n 确定要换号吗?\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"switchConfirmBtn\">确认换号</button>\n <button class=\"modal-btn secondary\" id=\"switchCancelBtn\">取消</button>\n </div>\n </div>\n </div>\n \n <!-- 离线状态提示 -->\n <div class=\"offline-banner\" id=\"offlineBanner\">\n <span class=\"offline-icon\">📡</span>\n <div class=\"offline-text\">\n <div class=\"offline-title\">网络连接失败</div>\n <div class=\"offline-desc\">请检查网络后重试</div>\n </div>\n <button class=\"retry-btn\" id=\"retryConnectBtn\">重试</button>\n </div>\n \n <!-- 软件授权 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">🔐</span>\n <span>软件授权</span>\n <span class=\"status-badge\" id=\"authStatus\">未授权</span>\n </div>\n \n <div class=\"input-group\">\n <input type=\"text\" id=\"keyInput\" placeholder=\"请输入CDK激活码\">\n <button class=\"btn btn-primary\" id=\"activateBtn\"><span class=\"btn-text\">激活</span></button>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">激活码</span>\n <span class=\"info-value key-display\" id=\"keyDisplay\" title=\"点击复制\">尚未激活</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">到期时间</span>\n <span class=\"info-value\" id=\"expireDate\">尚未激活</span>\n </div>\n </div>\n \n <!-- 账号数据 (已隐藏) -->\n <div class=\"section\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">👤</span>\n <span>账号数据</span>\n <span class=\"status-badge\" id=\"accountStatus\">未激活</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">CI积分余额</span>\n <span class=\"info-value\">0 <button style=\"background:none;border:none;color:#007acc;cursor:pointer;\">🔄</button></span>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"switchBtn\" disabled>换号</button>\n <button class=\"btn btn-blue btn-block\" id=\"resetBtn\">重置机器码</button>\n <button class=\"btn btn-blue btn-block\" id=\"disableUpdateBtn\">禁用自动更新</button>\n <button class=\"btn btn-blue btn-block\" id=\"cleanEnvBtn\">清理Cursor环境</button>\n <button class=\"btn btn-red btn-block\" id=\"disableBtn\">停用插件</button>\n </div>\n \n <!-- 无感换号 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">⚡</span>\n <span>无感换号</span>\n <span class=\"status-badge\" id=\"seamlessStatus\">未启用</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">积分</span>\n <span class=\"info-value\" id=\"seamlessSwitchRemaining\">0</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">当前账号</span>\n <span class=\"info-value\" style=\"font-size:11px;\" id=\"seamlessCurrentAccount\">未分配</span>\n </div>\n \n <div class=\"switch-container\" style=\"margin: 12px 0;\">\n <span>免魔法模式</span>\n <span class=\"pro-badge\">PRO</span>\n <span style=\"margin-left: auto; color: #888; font-size: 11px;\"></span>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"seamlessProxySwitch\">\n <span class=\"slider\"></span>\n </label>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"enableSeamlessBtn\" disabled><span class=\"btn-text\">启用无感换号</span></button>\n <button class=\"btn btn-red btn-block\" id=\"seamlessResetMachineBtn\" style=\"display:none;\"><span class=\"btn-text\">重置机器码</span></button>\n <button class=\"btn btn-red btn-block\" id=\"disableSeamlessBtn\" style=\"display:none;\"><span class=\"btn-text\">禁用无感换号</span></button>\n <button class=\"btn btn-blue btn-block\" id=\"manualSwitchBtn\" style=\"display:none;\" disabled><span class=\"btn-text\">一键换号(扣1积分)</span></button>\n </div>\n \n <!-- 账号用量 -->\n <div class=\"section\" id=\"usageSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">📊</span>\n <span>账号用量</span>\n <button class=\"btn\" style=\"margin-left:auto;padding:4px 8px;font-size:11px;background:#3c3c3c;\" id=\"refreshUsageBtn\">🔄</button>\n </div>\n \n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">会员类型</span>\n <span class=\"info-value\" id=\"usageMemberType\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">试用剩余</span>\n <span class=\"info-value\" id=\"usageTrialDays\">-</span>\n </div>\n </div>\n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">请求次数</span>\n <span class=\"info-value\" id=\"usageRequestCount\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">已用额度</span>\n <span class=\"info-value\" id=\"usageCostUSD\">-</span>\n </div>\n </div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:center;\" id=\"usageUpdateTime\">-</p>\n </div>\n \n <!-- 公告 -->\n <div class=\"section\" id=\"announcementSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\" id=\"announcementIcon\">📢</span>\n <span>公告</span>\n <span class=\"announcement-badge\" id=\"announcementBadge\">info</span>\n </div>\n <div class=\"announcement-title\" id=\"announcementTitle\"></div>\n <div class=\"announcement-content\" id=\"announcementContent\"></div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:right;\" id=\"announcementTime\"></p>\n </div>\n \n <!-- 版本信息 -->\n <div class=\"section\" id=\"versionSection\">\n <div class=\"section-title\">\n <span class=\"icon\">📦</span>\n <span>版本信息</span>\n <span class=\"status-badge\" id=\"versionStatus\" style=\"display:none;\">有更新</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">当前版本</span>\n <span class=\"info-value\" id=\"currentVersion\">-</span>\n </div>\n <div class=\"info-row\" id=\"latestVersionRow\" style=\"display:none;\">\n <span class=\"info-label\">最新版本</span>\n <span class=\"info-value\" id=\"latestVersion\" style=\"color:#4caf50;\">-</span>\n </div>\n <p id=\"updateHint\" style=\"font-size:11px;color:#ff9800;margin-top:8px;display:none;\">\n ⚠️ 发现新版本,请更新插件以获取最新功能\n </p>\n </div>\n \n <!-- 页脚 -->\n <div class=\"footer\">\n <div class=\"footer-row\">\n <div class=\"auto-start\">\n <span>自动启动</span>\n <label class=\"switch switch-sm\">\n <input type=\"checkbox\" id=\"autoStartSwitch\" checked>\n <span class=\"slider\"></span>\n </label>\n </div>\n <div class=\"cursor-version\">\n <span>Cursor</span>\n <span class=\"version-num\" id=\"cursorVersion\">0.0.0</span>\n </div>\n </div>\n <div class=\"footer-row\" style=\"margin-top: 8px;\">\n <div style=\"font-size: 10px; color: #666; word-break: break-all;\">\n <span>路径: </span>\n <span id=\"cursorPath\" style=\"color: #888;\">获取中...</span>\n </div>\n </div>\n </div>\n \n <!-- Toast 通知 -->\n <div class=\"toast-container\" id=\"toastContainer\">\n <div class=\"toast\" id=\"toast\">\n <span class=\"toast-icon\" id=\"toastIcon\">✅</span>\n <span class=\"toast-message\" id=\"toastMessage\"></span>\n </div>\n </div>\n \n <script nonce=\"" + var794 + "\">\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 = '一键换号(扣1积分)';\n \n function disableSwitchBtnWithCountdown(seconds) {\n // 清除之前的定时器\n if (switchBtnCountdownTimer) {\n clearInterval(switchBtnCountdownTimer);\n }\n \n let remaining = seconds;\n manualSwitchBtn.disabled = true;\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n \n switchBtnCountdownTimer = setInterval(() => {\n remaining--;\n if (remaining <= 0) {\n clearInterval(switchBtnCountdownTimer);\n switchBtnCountdownTimer = null;\n manualSwitchBtn.disabled = false;\n manualSwitchBtn.querySelector('.btn-text').textContent = originalSwitchBtnText;\n } else {\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';\n }\n }, 1000);\n }\n \n // 弹窗元素\n const adminModal = document.getElementById('adminModal');\n const adminModalClose = document.getElementById('adminModalClose');\n const resetPermissionModal = document.getElementById('resetPermissionModal');\n const resetPermissionClose = document.getElementById('resetPermissionClose');\n const restartModal = document.getElementById('restartModal');\n const restartModalTitle = document.getElementById('restartModalTitle');\n const restartNowBtn = document.getElementById('restartNowBtn');\n const restartLaterBtn = document.getElementById('restartLaterBtn');\n const expiredModal = document.getElementById('expiredModal');\n const expiredModalClose = document.getElementById('expiredModalClose');\n const cleanEnvModal = document.getElementById('cleanEnvModal');\n const cleanEnvConfirmBtn = document.getElementById('cleanEnvConfirmBtn');\n const cleanEnvCancelBtn = document.getElementById('cleanEnvCancelBtn');\n \n // 换号确认弹窗元素\n const switchConfirmModal = document.getElementById('switchConfirmModal');\n const switchConfirmEmail = document.getElementById('switchConfirmEmail');\n const switchConfirmCost = document.getElementById('switchConfirmCost');\n const switchConfirmBtn = document.getElementById('switchConfirmBtn');\n const switchCancelBtn = document.getElementById('switchCancelBtn');\n \n // 显示管理员权限弹窗\n function showAdminModal() {\n adminModal.classList.add('show');\n }\n \n // 显示重置机器码权限提示弹窗\n function showAdminPermissionModal() {\n resetPermissionModal.classList.add('show');\n }\n \n // 重置机器码权限弹窗 - 关闭按钮\n resetPermissionClose.addEventListener('click', () => {\n resetPermissionModal.classList.remove('show');\n });\n \n // 点击遮罩关闭权限提示弹窗\n resetPermissionModal.addEventListener('click', (e) => {\n if (e.target === resetPermissionModal) {\n resetPermissionModal.classList.remove('show');\n }\n });\n \n // 显示重启提示弹窗\n let restartModalAction = 'reload'; // 'reload' 或 'close'\n \n function showRestartModal(title, action = 'reload') {\n restartModalTitle.textContent = title || '操作成功';\n restartModalAction = action;\n // 根据操作类型更新按钮文字\n restartNowBtn.textContent = action === 'close' ? '立即关闭 Cursor' : '立即重启';\n restartModal.classList.add('show');\n }\n \n // 显示过期弹窗\n function showExpiredModal() {\n expiredModal.classList.add('show');\n }\n \n // 关闭管理员弹窗\n adminModalClose.addEventListener('click', () => {\n adminModal.classList.remove('show');\n });\n \n // 点击遮罩关闭管理员弹窗\n adminModal.addEventListener('click', (e) => {\n if (e.target === adminModal) {\n adminModal.classList.remove('show');\n }\n });\n \n // 立即重启/关闭按钮\n restartNowBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n if (restartModalAction === 'close') {\n // 完全关闭 Cursor\n vscode.postMessage({ type: 'closeCursor' });\n } else {\n // 重新加载窗口\n vscode.postMessage({ type: 'reloadWindow' });\n }\n });\n \n // 稍后手动按钮\n restartLaterBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n });\n \n // 点击遮罩关闭重启弹窗\n restartModal.addEventListener('click', (e) => {\n if (e.target === restartModal) {\n restartModal.classList.remove('show');\n }\n });\n \n // 关闭过期弹窗\n expiredModalClose.addEventListener('click', () => {\n expiredModal.classList.remove('show');\n });\n \n // 点击遮罩关闭过期弹窗\n expiredModal.addEventListener('click', (e) => {\n if (e.target === expiredModal) {\n expiredModal.classList.remove('show');\n }\n });\n \n // 当前账号邮箱(用于查询用量)\n let currentAccountEmail = '';\n let usageRefreshInterval = null;\n // 存储完整激活码(用于复制)\n let fullActivationKey = '';\n // 当前剩余换号次数\n let currentSwitchRemaining = 0;\n // 当前到期时间\n let currentExpireDate = '';\n \n // 检查卡密是否已过期\n function isKeyExpired() {\n if (!currentExpireDate) return true;\n try {\n const expireTime = new Date(currentExpireDate).getTime();\n return Date.now() > expireTime;\n } catch {\n return true;\n }\n }\n \n // 格式化到期时间为北京时间\n function formatExpireDate(dateStr) {\n if (!dateStr) return '';\n try {\n // 后端返回的时间没有时区标识,假设是 UTC 时间\n // 将空格替换为T,并添加Z表示UTC\n let utcStr = dateStr;\n if (!dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {\n utcStr = dateStr.replace(' ', 'T') + 'Z';\n }\n const date = new Date(utcStr);\n \n // 使用中国时区格式化(UTC+8)\n return date.toLocaleString('zh-CN', {\n timeZone: 'Asia/Shanghai',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false\n });\n } catch {\n return dateStr; // 格式化失败返回原始值\n }\n }\n \n // 隐藏激活码后几位\n function maskKey(key) {\n if (!key || key.length <= 8) return key;\n return key.substring(0, key.length - 4) + '****';\n }\n \n // 点击激活码复制\n keyDisplay.addEventListener('click', () => {\n if (!fullActivationKey) return;\n navigator.clipboard.writeText(fullActivationKey).then(() => {\n keyDisplay.classList.add('copied');\n const originalText = keyDisplay.textContent;\n keyDisplay.textContent = '已复制!';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n }).catch(() => {\n // 降级方案\n const textarea = document.createElement('textarea');\n textarea.value = fullActivationKey;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n keyDisplay.classList.add('copied');\n keyDisplay.textContent = '已复制!';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n });\n });\n \n // Loading 状态控制\n function setButtonLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n btn.disabled = true;\n } else {\n btn.classList.remove('loading');\n // 注意:某些按钮可能需要保持禁用状态,由调用方控制\n }\n }\n \n function setRefreshLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n } else {\n btn.classList.remove('loading');\n }\n }\n \n // 获取初始状态\n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getSeamlessStatus' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n vscode.postMessage({ type: 'getProxyStatus' });\n vscode.postMessage({ type: 'getAnnouncement' });\n vscode.postMessage({ type: 'checkVersion' });\n vscode.postMessage({ type: 'getCursorRunningPath' });\n \n // 激活按钮\n activateBtn.addEventListener('click', () => {\n const key = keyInput.value.trim();\n if (!key) {\n return;\n }\n setButtonLoading(activateBtn, true);\n vscode.postMessage({ type: 'activate', key });\n });\n \n // 换号按钮\n switchBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'switch' });\n });\n \n // 重置机器码按钮\n resetBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 禁用自动更新按钮\n disableUpdateBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disableUpdate' });\n });\n \n // 清理Cursor环境按钮 - 显示确认弹窗\n cleanEnvBtn.addEventListener('click', () => {\n cleanEnvModal.classList.add('show');\n });\n \n // 确认清理\n cleanEnvConfirmBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n vscode.postMessage({ type: 'cleanEnv' });\n });\n \n // 取消清理\n cleanEnvCancelBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n });\n \n // 点击遮罩关闭清理弹窗\n cleanEnvModal.addEventListener('click', (e) => {\n if (e.target === cleanEnvModal) {\n cleanEnvModal.classList.remove('show');\n }\n });\n \n // 停用按钮\n disableBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disable' });\n });\n \n // 关闭更新提醒条\n updateBannerClose.addEventListener('click', () => {\n updateBanner.classList.remove('show');\n });\n \n // 免魔法开关\n seamlessProxySwitch.addEventListener('change', (e) => {\n const wantEnabled = e.target.checked;\n \n // 如果要开启免魔法,检查卡密是否过期(只要没过期就可以用,不管换号次数)\n if (wantEnabled && isKeyExpired()) {\n e.target.checked = false;\n showToast('授权码已过期,无法开启免魔法', '⚠️', 3000);\n return;\n }\n \n vscode.postMessage({ \n type: 'toggleProxy', \n enabled: wantEnabled,\n url: ''\n });\n });\n \n // 无感换号 - 启用按钮\n enableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(enableSeamlessBtn, true);\n vscode.postMessage({ type: 'injectSeamless' });\n });\n \n // 无感换号 - 禁用按钮\n disableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(disableSeamlessBtn, true);\n vscode.postMessage({ type: 'restoreSeamless' });\n });\n \n // 无感换号 - 手动换号按钮(先检查用量)\n manualSwitchBtn.addEventListener('click', () => {\n setButtonLoading(manualSwitchBtn, true);\n // 传递当前显示的账号邮箱\n vscode.postMessage({ type: 'checkUsageBeforeSwitch', email: currentAccountEmail });\n });\n \n // 换号确认弹窗 - 确认按钮\n switchConfirmBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, true);\n vscode.postMessage({ type: 'confirmSwitch' });\n });\n \n // 换号确认弹窗 - 取消按钮\n switchCancelBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n });\n \n // 换号确认弹窗 - 点击遮罩关闭\n switchConfirmModal.addEventListener('click', (e) => {\n if (e.target === switchConfirmModal) {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n }\n });\n \n // 无感换号区域 - 重置机器码按钮\n seamlessResetMachineBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 刷新用量按钮\n refreshUsageBtn.addEventListener('click', () => {\n if (currentAccountEmail) {\n setRefreshLoading(refreshUsageBtn, true);\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n });\n \n // 刷新用量函数\n function refreshUsage() {\n if (currentAccountEmail) {\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n }\n \n // 启动用量定时刷新 (每分钟一次)\n function startUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n }\n // 立即刷新一次\n refreshUsage();\n // 每60秒刷新一次\n usageRefreshInterval = setInterval(refreshUsage, 60000);\n }\n \n // 停止用量刷新\n function stopUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n usageRefreshInterval = null;\n }\n }\n \n // 更新用量显示\n function updateUsageDisplay(data) {\n if (!data) return;\n \n const subscription = data.subscription || {};\n const usage = data.usage || {};\n \n // 会员类型\n const memberTypeMap = {\n 'free_trial': '免费试用',\n 'pro': 'Pro会员',\n 'free': '免费版',\n 'business': '商业版'\n };\n usageMemberType.textContent = memberTypeMap[subscription.membershipType] || subscription.membershipType || '-';\n \n // 试用剩余天数\n if (subscription.daysRemainingOnTrial !== undefined && subscription.daysRemainingOnTrial !== null) {\n usageTrialDays.textContent = subscription.daysRemainingOnTrial + ' 天';\n usageTrialDays.style.color = subscription.daysRemainingOnTrial <= 3 ? '#f87171' : '#4ade80';\n } else {\n usageTrialDays.textContent = '-';\n usageTrialDays.style.color = '#fff';\n }\n \n // 请求次数\n usageRequestCount.textContent = (usage.totalUsageCount || 0) + ' 次';\n \n // 已用额度\n const costUSD = usage.totalCostUSD || 0;\n usageCostUSD.textContent = '$' + costUSD.toFixed(2);\n usageCostUSD.style.color = costUSD > 5 ? '#f87171' : (costUSD > 2 ? '#fbbf24' : '#4ade80');\n \n // 更新时间\n usageUpdateTime.textContent = '更新于 ' + new Date().toLocaleTimeString();\n }\n \n // 解析公告内容中的链接 {文字URL}\n function parseAnnouncementContent(content) {\n if (!content) return '';\n \n // 转义 HTML 特殊字符\n let escaped = content\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\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 通知,10秒后消失\n showToast('已切换到: ' + (message.email || '新账号') + ',约10秒内自动生效', '✅', 10000);\n // 刷新状态\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n // 禁用按钮10秒,显示倒计时\n disableSwitchBtnWithCountdown(10);\n } else {\n manualSwitchBtn.disabled = false;\n showToast(message.error || '换号失败', '❌', 5000);\n }\n break;\n \n case 'proxyStatus':\n // 设置免魔法开关状态\n seamlessProxySwitch.checked = message.enabled;\n break;\n \n // 公告\n case 'announcement':\n if (message.success && message.data) {\n updateAnnouncementDisplay(message.data);\n } else {\n announcementSection.style.display = 'none';\n }\n break;\n \n // 版本检查\n case 'versionCheck':\n currentVersionEl.textContent = message.currentVersion || '-';\n if (message.success && message.hasUpdate) {\n // 有更新\n latestVersionEl.textContent = message.latestVersion;\n latestVersionRow.style.display = 'flex';\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#ff9800';\n updateHint.style.display = 'block';\n \n // 显示顶部更新提醒条\n updateBannerVersion.textContent = 'v' + message.latestVersion;\n updateBanner.classList.add('show');\n } else if (message.success) {\n // 已是最新版\n versionStatus.textContent = '最新';\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#4caf50';\n latestVersionRow.style.display = 'none';\n updateHint.style.display = 'none';\n updateBanner.classList.remove('show');\n }\n break;\n \n // Cursor 运行路径\n case 'cursorRunningPath':\n if (message.path) {\n const pathText = message.path + (message.packageExists ? ' ✓' : ' ✗');\n cursorPath.textContent = pathText;\n cursorPath.style.color = message.packageExists ? '#4ade80' : '#f87171';\n // 同时更新版本号\n if (message.cursorVersion) {\n cursorVersion.textContent = message.cursorVersion;\n }\n } else {\n cursorPath.textContent = '未找到';\n cursorPath.style.color = '#f87171';\n }\n break;\n \n // 管理员权限不足提示\n case 'adminPermissionRequired':\n showAdminPermissionModal();\n break;\n \n // 机器码重置\n case 'machineIdReset':\n if (message.success && message.needRestart) {\n // 机器码重置需要完全关闭 Cursor,不是 reload\n showRestartModal(message.message || '机器码重置成功', 'close');\n }\n break;\n \n // 通用 Toast 消息\n case 'showToast':\n showToast(message.message || '', message.icon || '📢', 10000);\n break;\n \n // 网络状态\n case 'networkStatus':\n updateOfflineStatus(!message.online);\n break;\n }\n });\n \n // 离线状态显示/隐藏\n let wasOffline = false; // 跟踪之前是否离线\n function updateOfflineStatus(isOffline) {\n if (isOffline) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else {\n offlineBanner.classList.remove('show');\n // 只有从离线恢复到在线时才刷新状态\n if (wasOffline) {\n wasOffline = false;\n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n }\n }\n }\n \n // 重试连接按钮\n retryConnectBtn.addEventListener('click', async () => {\n retryConnectBtn.classList.add('loading');\n retryConnectBtn.textContent = '连接中...';\n \n // 发起真正的网络请求来测试网络\n vscode.postMessage({ type: 'retryConnect' });\n \n // 5秒后恢复按钮状态(给网络请求足够时间)\n setTimeout(() => {\n retryConnectBtn.classList.remove('loading');\n retryConnectBtn.textContent = '重试';\n }, 5000);\n });\n \n function updateUI(state) {\n if (state.isActivated) {\n authStatus.textContent = '已授权';\n authStatus.className = 'status-badge active';\n accountStatus.textContent = '已激活';\n accountStatus.className = 'status-badge active';\n switchBtn.disabled = false;\n fullActivationKey = state.key;\n keyDisplay.textContent = maskKey(fullActivationKey);\n // 更新到期时间\n currentExpireDate = state.expireDate || '';\n expireDate.textContent = formatExpireDate(currentExpireDate);\n // 更新换号次数\n if (state.switchRemaining !== undefined) {\n currentSwitchRemaining = state.switchRemaining;\n switchCount.textContent = state.switchRemaining + '/' + (state.switchLimit || 100);\n }\n // 启用无感换号按钮(只有过期才禁用)\n enableSeamlessBtn.disabled = isKeyExpired();\n }\n cursorVersion.textContent = state.cursorVersion || '0.0.0';\n \n // 根据网络状态显示/隐藏离线提示\n if (state.isOnline === false) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else if (state.isOnline === true) {\n // 网络恢复,隐藏离线提示\n offlineBanner.classList.remove('show');\n wasOffline = false;\n }\n }\n </script>\n</body>\n</html>";
|
||
}
|
||
}
|
||
exports.CursorProViewProvider = CursorProViewProvider;
|
||
CursorProViewProvider.CURRENT_VERSION = '0.4.5'; |