1991 lines
155 KiB
JavaScript
1991 lines
155 KiB
JavaScript
'use strict';
|
||
|
||
var __createBinding = this && this.__createBinding || (Object.create ? function (param0, param1, param2, param3) {
|
||
if (param3 === undefined) {
|
||
param3 = param2;
|
||
}
|
||
var descriptor = Object.getOwnPropertyDescriptor(param1, param2);
|
||
if (!descriptor || ("get" in descriptor ? !param1.__esModule : descriptor.writable || descriptor.configurable)) {
|
||
descriptor = {
|
||
enumerable: true,
|
||
get: function () {
|
||
return param1[param2];
|
||
}
|
||
};
|
||
}
|
||
Object.defineProperty(param0, param3, descriptor);
|
||
} : function (param0, param1, param2, param3) {
|
||
if (param3 === undefined) {
|
||
param3 = param2;
|
||
}
|
||
param0[param3] = param1[param2];
|
||
});
|
||
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (param0, param1) {
|
||
Object.defineProperty(param0, "default", {
|
||
enumerable: true,
|
||
value: param1
|
||
});
|
||
} : function (param0, param1) {
|
||
param0.default = param1;
|
||
});
|
||
var __importStar = this && this.__importStar || function () {
|
||
var getOwnPropNames = function (param0) {
|
||
getOwnPropNames = Object.getOwnPropertyNames || function (param0) {
|
||
var items = [];
|
||
for (var propKey in param0) if (Object.prototype.hasOwnProperty.call(param0, propKey)) {
|
||
items[items.length] = propKey;
|
||
}
|
||
return items;
|
||
};
|
||
return getOwnPropNames(param0);
|
||
};
|
||
return function (param0) {
|
||
if (param0 && param0.__esModule) {
|
||
return param0;
|
||
}
|
||
var obj = {};
|
||
if (param0 != null) {
|
||
var items = getOwnPropNames(param0);
|
||
for (var count = 0; count < items.length; count++) {
|
||
if (items[count] !== "default") {
|
||
__createBinding(obj, param0, items[count]);
|
||
}
|
||
}
|
||
}
|
||
__setModuleDefault(obj, param0);
|
||
return obj;
|
||
};
|
||
}();
|
||
Object.defineProperty(exports, '__esModule', {
|
||
value: true
|
||
});
|
||
exports.CursorProViewProvider = undefined;
|
||
const vscode = __importStar(require("vscode"));
|
||
const client_1 = require("../api/client");
|
||
const extension_1 = require("../extension");
|
||
const account_1 = require('../utils/account');
|
||
const path = __importStar(require("path"));
|
||
const fs = __importStar(require('fs'));
|
||
const child_process_1 = require('child_process');
|
||
const util_1 = require("util");
|
||
const sqlite_1 = require('../utils/sqlite');
|
||
const execAsync = util_1.promisify(child_process_1.exec);
|
||
class CursorProViewProvider {
|
||
constructor(extensionUri, context) {
|
||
this._extensionUri = extensionUri;
|
||
this._context = context;
|
||
this._hostsPermissionGranted = false;
|
||
this.SNI_PROXY_IP = "154.36.154.163";
|
||
this.CURSOR_DOMAINS = ["api2.cursor.sh", "api3.cursor.sh"];
|
||
this.HOSTS_MARKER_START = "# ===== CursorPro SNI Proxy Start =====";
|
||
this.HOSTS_MARKER_END = "# ===== CursorPro SNI Proxy End =====";
|
||
this._cachedCursorPath = null;
|
||
this._onlineStatusUnsubscribe = client_1.onOnlineStatusChange(status => {
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': status
|
||
});
|
||
});
|
||
}
|
||
resolveWebviewView(webviewView, context, token) {
|
||
this._view = webviewView;
|
||
webviewView.webview.options = {
|
||
'enableScripts': true,
|
||
'localResourceRoots': [this._extensionUri]
|
||
};
|
||
webviewView.webview.html = this._getHtmlContent(webviewView.webview);
|
||
webviewView.webview.onDidReceiveMessage(async msg => {
|
||
const config = {
|
||
'WZyWQ': "娌℃湁鍐欏叆鏉冮檺",
|
||
'ZXhkG': "seamlessRestored"
|
||
};
|
||
switch (msg.type) {
|
||
case "activate":
|
||
await this._handleActivate(msg.key);
|
||
break;
|
||
case "switch":
|
||
await this._handleSwitch();
|
||
break;
|
||
case "resetMachineId":
|
||
await this._handleResetMachineId();
|
||
break;
|
||
case "disableUpdate":
|
||
await this._handleDisableUpdate();
|
||
break;
|
||
case "cleanEnv":
|
||
await this._handleCleanEnv();
|
||
break;
|
||
case "disable":
|
||
await this._handleDisable();
|
||
break;
|
||
case "toggleProxy":
|
||
await this._handleToggleProxy(msg.enabled, msg.url);
|
||
break;
|
||
case 'getProxyStatus':
|
||
await this._handleGetProxyStatus();
|
||
break;
|
||
case "getState":
|
||
await this._sendState();
|
||
break;
|
||
case "retryConnect":
|
||
await this._handleRetryConnect();
|
||
break;
|
||
case "getSeamlessStatus":
|
||
await this._handleGetSeamlessStatus();
|
||
break;
|
||
case "injectSeamless":
|
||
await this._handleInjectSeamless();
|
||
break;
|
||
case "restoreSeamless":
|
||
await this._handleRestoreSeamless();
|
||
break;
|
||
case "toggleSeamless":
|
||
await this._handleToggleSeamless(msg.enabled);
|
||
break;
|
||
case "getUserSwitchStatus":
|
||
await this._handleGetUserSwitchStatus();
|
||
break;
|
||
case "manualSeamlessSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "checkUsageBeforeSwitch":
|
||
await this._handleCheckUsageBeforeSwitch(msg.email);
|
||
break;
|
||
case "confirmSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "getCursorPath":
|
||
await this._handleGetCursorPath();
|
||
break;
|
||
case 'getAccountUsage':
|
||
await this._handleGetAccountUsage(msg.email);
|
||
break;
|
||
case "getAnnouncement":
|
||
await this._handleGetAnnouncement();
|
||
break;
|
||
case "checkVersion":
|
||
await this._handleCheckVersion();
|
||
break;
|
||
case "getCursorRunningPath":
|
||
await this._handleGetCursorRunningPath();
|
||
break;
|
||
case "reloadWindow":
|
||
vscode.commands.executeCommand("workbench.action.reloadWindow");
|
||
break;
|
||
case 'closeCursor':
|
||
await account_1.closeCursor();
|
||
break;
|
||
}
|
||
});
|
||
this._sendState();
|
||
this._checkKeyStatus();
|
||
}
|
||
async _checkKeyStatus() {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (!savedKey) {
|
||
return;
|
||
}
|
||
try {
|
||
const verifyResult = await client_1.verifyKey(savedKey);
|
||
if (verifyResult.success && verifyResult.valid) {
|
||
await this._context.globalState.update("cursorpro.expireDate", verifyResult.expire_date);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", verifyResult.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", verifyResult.switch_limit);
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': true,
|
||
'expireDate': verifyResult.expire_date,
|
||
'switchRemaining': verifyResult.switch_remaining,
|
||
'switchLimit': verifyResult.switch_limit
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': false,
|
||
'expired': true,
|
||
'error': verifyResult.error || "婵€娲荤爜宸茶繃鏈熸垨鏃犳晥"
|
||
});
|
||
}
|
||
} catch (modErr) {
|
||
console.error("[CursorPro] 妫€鏌ユ縺娲荤爜鐘舵€佸け璐?", modErr);
|
||
}
|
||
}
|
||
async _handleActivate(key) {
|
||
try {
|
||
const isSeamlessInjected = await this._isSeamlessInjected();
|
||
if (isSeamlessInjected) {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "???????????????????"
|
||
});
|
||
return;
|
||
}
|
||
this._cleanProxySettings();
|
||
const verifyResult = await client_1.verifyKey(key);
|
||
if (verifyResult.success && verifyResult.valid) {
|
||
console.log("[CursorPro] 婵€娲绘垚鍔燂紝鍚庣杩斿洖:", {
|
||
'expire_date': verifyResult.expire_date,
|
||
'switch_remaining': verifyResult.switch_remaining,
|
||
'switch_limit': verifyResult.switch_limit
|
||
});
|
||
await this._context.globalState.update("cursorpro.key", key);
|
||
await this._context.globalState.update("cursorpro.expireDate", verifyResult.expire_date);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", verifyResult.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", verifyResult.switch_limit);
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': true,
|
||
'key': key,
|
||
'expireDate': verifyResult.expire_date,
|
||
'switchRemaining': verifyResult.switch_remaining,
|
||
'switchLimit': verifyResult.switch_limit
|
||
});
|
||
extension_1.showStatusBar();
|
||
await this._handleGetUserSwitchStatus();
|
||
} else {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': verifyResult.error || "鎺堟潈鐮佹棤鏁?
|
||
});
|
||
}
|
||
} catch (activateErr) {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "杩炴帴鏈嶅姟鍣ㄥけ璐?
|
||
});
|
||
}
|
||
}
|
||
async _handleSwitch() {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "璇峰厛婵€娲绘巿鏉冪爜",
|
||
'icon': '鈿狅笍'
|
||
});
|
||
return;
|
||
}
|
||
try {
|
||
const switchResult = await client_1.switchSeamlessToken(savedKey);
|
||
if (switchResult.switched) {
|
||
await this._context.globalState.update("cursorpro.switchRemaining", switchResult.switchRemaining);
|
||
this._postMessage({
|
||
'type': "switched",
|
||
'success': true,
|
||
'email': switchResult.email,
|
||
'switchRemaining': switchResult.switchRemaining,
|
||
'switchLimit': this._context.globalState.get("cursorpro.switchLimit") || 100
|
||
});
|
||
const condition = switchResult.switchRemaining ?? 0;
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'switchRemaining': condition,
|
||
'canSwitch': condition > 0,
|
||
'lockedAccount': switchResult.email ? {
|
||
'email': switchResult.email
|
||
} : null
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "switched",
|
||
'success': false,
|
||
'error': switchResult.message || '鎹㈠彿澶辫触'
|
||
});
|
||
}
|
||
} catch (switchErr) {
|
||
this._postMessage({
|
||
'type': 'switched',
|
||
'success': false,
|
||
'error': "杩炴帴鏈嶅姟鍣ㄥけ璐?
|
||
});
|
||
}
|
||
}
|
||
async _writeAccountToLocal(accountData) {
|
||
try {
|
||
const condition = process.env.APPDATA || '';
|
||
const joinedPath = path.join(condition, "Cursor", "User", "globalStorage", "state.vscdb");
|
||
const joinedPath1 = path.join(condition, "Cursor", "User", "globalStorage", 'storage.json');
|
||
const joinedPath2 = path.join(condition, "Cursor", "machineid");
|
||
if (fs.existsSync(joinedPath)) {
|
||
const items = [];
|
||
if (accountData.accessToken) {
|
||
items.push(["cursorAuth/accessToken", accountData.accessToken]);
|
||
}
|
||
if (accountData.refreshToken) {
|
||
items.push(["cursorAuth/refreshToken", accountData.refreshToken]);
|
||
}
|
||
if (accountData.email) {
|
||
items.push(["cursorAuth/cachedEmail", accountData.email]);
|
||
}
|
||
if (accountData.membership_type) {
|
||
items.push(["cursorAuth/stripeMembershipType", accountData.membership_type]);
|
||
}
|
||
if (accountData.sign_up_type) {
|
||
items.push(["cursorAuth/cachedSignUpType", accountData.sign_up_type]);
|
||
}
|
||
if (accountData.serviceMachineId) {
|
||
items.push(["storage.serviceMachineId", accountData.serviceMachineId]);
|
||
}
|
||
await sqlite_1.sqliteSetBatch(joinedPath, items);
|
||
console.log("[CursorPro] SQLite 鏁版嵁搴撳凡鏇存柊");
|
||
}
|
||
if (fs.existsSync(joinedPath1)) {
|
||
const parsed = JSON.parse(fs.readFileSync(joinedPath1, 'utf-8'));
|
||
if (accountData.machineId) {
|
||
parsed["telemetry.machineId"] = accountData.machineId;
|
||
}
|
||
if (accountData.macMachineId) {
|
||
parsed['telemetry.macMachineId'] = accountData.macMachineId;
|
||
}
|
||
if (accountData.devDeviceId) {
|
||
parsed["telemetry.devDeviceId"] = accountData.devDeviceId;
|
||
}
|
||
if (accountData.sqmId) {
|
||
parsed["telemetry.sqmId"] = accountData.sqmId;
|
||
}
|
||
fs.writeFileSync(joinedPath1, JSON.stringify(parsed, null, 4));
|
||
console.log("[CursorPro] storage.json 宸叉洿鏂?);
|
||
}
|
||
if (accountData.machineId) {
|
||
fs.writeFileSync(joinedPath2, accountData.machineId);
|
||
console.log("[CursorPro] machineid 鏂囦欢宸叉洿鏂?);
|
||
}
|
||
if (accountData.registryGuid && process.platform === "win32") {
|
||
try {
|
||
const result = 'reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + accountData.registryGuid + '" /f';
|
||
await execAsync(result);
|
||
console.log("[CursorPro] 娉ㄥ唽琛?MachineGuid 宸叉洿鏂?);
|
||
} catch (parseErr) {
|
||
console.warn("[CursorPro] 娉ㄥ唽琛ㄥ啓鍏ュけ璐ワ紙鍙兘闇€瑕佺鐞嗗憳鏉冮檺锛?", parseErr);
|
||
}
|
||
}
|
||
return true;
|
||
} catch (writeErr) {
|
||
console.error("[CursorPro] 鍐欏叆鏈湴澶辫触:", strIdx);
|
||
vscode.window.showErrorMessage("鍐欏叆澶辫触: " + strIdx);
|
||
return false;
|
||
}
|
||
}
|
||
async _handleReset() {
|
||
await this._context.globalState.update("cursorpro.key", undefined);
|
||
await this._context.globalState.update("cursorpro.expireDate", undefined);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", undefined);
|
||
extension_1.hideStatusBar();
|
||
this._postMessage({
|
||
'type': 'reset',
|
||
'success': true
|
||
});
|
||
vscode.window.showInformationMessage("鎻掍欢宸查噸缃?);
|
||
}
|
||
async _handleDisable() {
|
||
await this._handleReset();
|
||
vscode.window.showInformationMessage("鎻掍欢宸插仠鐢?);
|
||
}
|
||
async _checkAdminPrivilege() {
|
||
if (process.platform !== "win32") {
|
||
return true;
|
||
}
|
||
try {
|
||
await execAsync('reg query "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid 2>nul');
|
||
const hostEntry = await execAsync("net session 2>nul").catch(() => ({
|
||
'stdout': '',
|
||
'stderr': 'error'
|
||
}));
|
||
return !hostEntry.stderr;
|
||
} catch (jsonErr) {
|
||
return false;
|
||
}
|
||
}
|
||
async _handleResetMachineId() {
|
||
try {
|
||
const platform = process.platform;
|
||
if (platform === 'win32') {
|
||
const adminprivilegeResult = await this._checkAdminPrivilege();
|
||
if (!adminprivilegeResult) {
|
||
this._postMessage({
|
||
'type': "adminPermissionRequired"
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
const hostLine = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: charIdx,
|
||
storagePath: lineItem,
|
||
machineidPath: lineIdx
|
||
} = hostLine;
|
||
const module = require("crypto");
|
||
const str = module.randomBytes(32).toString("hex");
|
||
const str1 = module.randomBytes(32).toString("hex");
|
||
const proxyLine = module.randomUUID();
|
||
const result = '{' + module.randomUUID().toUpperCase() + '}';
|
||
let count = 0;
|
||
let items = [];
|
||
if (fs.existsSync(lineItem)) {
|
||
let num = 3;
|
||
while (num > 0) {
|
||
try {
|
||
const parsed = JSON.parse(fs.readFileSync(lineItem, "utf-8"));
|
||
parsed["telemetry.machineId"] = str;
|
||
parsed["telemetry.macMachineId"] = str1;
|
||
parsed["telemetry.devDeviceId"] = proxyLine;
|
||
parsed["telemetry.sqmId"] = result;
|
||
fs.writeFileSync(lineItem, JSON.stringify(parsed, null, 4));
|
||
console.log("[CursorPro] storage.json 宸叉洿鏂?);
|
||
count++;
|
||
break;
|
||
} catch (readErr) {
|
||
num--;
|
||
if (num === 0) {
|
||
console.warn("[CursorPro] storage.json 鏇存柊澶辫触:", readErr.message);
|
||
items.push("storage.json");
|
||
} else {
|
||
await new Promise(param0 => setTimeout(param0, 100));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
{
|
||
let num = 3;
|
||
while (num > 0) {
|
||
try {
|
||
const dirPath = path.dirname(lineIdx);
|
||
if (!fs.existsSync(dirPath)) {
|
||
fs.mkdirSync(dirPath, {
|
||
'recursive': true
|
||
});
|
||
}
|
||
fs.writeFileSync(lineIdx, str);
|
||
console.log("[CursorPro] machineid 鏂囦欢宸叉洿鏂?);
|
||
count++;
|
||
break;
|
||
} catch (writeErr) {
|
||
num--;
|
||
if (num === 0) {
|
||
console.warn("[CursorPro] machineid 鏇存柊澶辫触:", writeErr.message);
|
||
items.push("machineid");
|
||
} else {
|
||
await new Promise(param0 => setTimeout(param0, 100));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (fs.existsSync(charIdx)) {
|
||
let num = 3;
|
||
while (num > 0) {
|
||
try {
|
||
const proxyEntry = module.randomUUID();
|
||
const newHostsContent = await sqlite_1.sqliteSetBatch(charIdx, [['storage.serviceMachineId', proxyEntry]]);
|
||
if (newHostsContent) {
|
||
console.log("[CursorPro] SQLite 鏁版嵁搴撳凡鏇存柊");
|
||
count++;
|
||
break;
|
||
} else {
|
||
throw new Error("sqliteSetBatch 杩斿洖 false");
|
||
}
|
||
} catch (grantErr) {
|
||
num--;
|
||
if (num === 0) {
|
||
console.warn("[CursorPro] SQLite 鏇存柊澶辫触:", grantErr.message);
|
||
items.push("SQLite");
|
||
} else {
|
||
await new Promise(param0 => setTimeout(param0, 500));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (platform === "win32") {
|
||
const hostsLines = module.randomUUID();
|
||
try {
|
||
await execAsync('reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + hostsLines + '" /f');
|
||
console.log("[CursorPro] 娉ㄥ唽琛?MachineGuid 宸叉洿鏂?);
|
||
count++;
|
||
} catch (regWriteErr) {
|
||
console.warn("[CursorPro] 娉ㄥ唽琛ㄦ洿鏂板け璐ワ紙闇€瑕佺鐞嗗憳鏉冮檺锛夛紝宸茶烦杩?);
|
||
items.push("娉ㄥ唽琛?);
|
||
}
|
||
}
|
||
if (count >= 2) {
|
||
this._postMessage({
|
||
'type': "machineIdReset",
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': items.length > 0 ? "鏈哄櫒鐮侀噸缃垚鍔燂紙" + items.join(", ") + " 鏇存柊澶辫触锛屼笉褰卞搷浣跨敤锛? : "鏈哄櫒鐮侀噸缃垚鍔?
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "閲嶇疆閮ㄥ垎澶辫触: " + items.join(", ") + "銆傝鍏堝畬鍏ㄥ叧闂?Cursor 鍐嶈瘯",
|
||
'icon': '鈿狅笍'
|
||
});
|
||
}
|
||
} catch (hostsErr) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "閲嶇疆鏈哄櫒鐮佸け璐? " + hostsErr,
|
||
'icon': '鉂?
|
||
});
|
||
}
|
||
}
|
||
_generateRandomMAC() {
|
||
const module = require("crypto");
|
||
const dbPath = module.randomBytes(6);
|
||
dbPath[0] = (dbPath[0] | 2) & 254;
|
||
return Array.from(dbPath).map(item => item.toString(16).padStart(2, '0')).join(':');
|
||
}
|
||
async _handleDisableUpdate() {
|
||
try {
|
||
const condition = process.env.LOCALAPPDATA || '';
|
||
const joinedPath = path.join(condition, "cursor-updater");
|
||
if (fs.existsSync(joinedPath)) {
|
||
if (fs.statSync(joinedPath).isDirectory()) {
|
||
fs.rmSync(joinedPath, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
} else {
|
||
fs.unlinkSync(joinedPath);
|
||
}
|
||
}
|
||
fs.writeFileSync(joinedPath, '');
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "宸茬鐢?Cursor 鑷姩鏇存柊",
|
||
'icon': '鉁?
|
||
});
|
||
} catch (toggleErr) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "绂佺敤鑷姩鏇存柊澶辫触: " + toggleErr,
|
||
'icon': '鉂?
|
||
});
|
||
}
|
||
}
|
||
async _handleCleanEnv() {
|
||
try {
|
||
if (process.platform === "win32") {
|
||
await execAsync("taskkill /F /IM Cursor.exe").catch(() => {});
|
||
} else {
|
||
await execAsync("pkill -f Cursor").catch(() => {});
|
||
}
|
||
await new Promise(param0 => setTimeout(param0, 2000));
|
||
const condition = process.env.APPDATA || '';
|
||
const condition1 = process.env.LOCALAPPDATA || '';
|
||
const condition2 = process.env.HOME || process.env.USERPROFILE || '';
|
||
let count = 0;
|
||
if (process.platform === "win32") {
|
||
const items = [path.join(condition, "Cursor"), path.join(condition1, "Cursor"), path.join(condition1, "cursor-updater"), path.join(condition2, ".cursor")];
|
||
for (const macPath of items) {
|
||
try {
|
||
if (fs.existsSync(macPath)) {
|
||
fs.rmSync(macPath, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
count++;
|
||
console.log("[CursorPro] 宸叉竻鐞? " + macPath);
|
||
}
|
||
} catch (statusErr) {
|
||
console.warn("[CursorPro] 娓呯悊澶辫触: " + macPath, statusErr);
|
||
}
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
const items = [path.join(condition2, "Library", "Application Support", "Cursor"), path.join(condition2, "Library", "Caches", "Cursor"), path.join(condition2, "Library", "Logs", "Cursor"), path.join(condition2, 'Library', "Application Support", 'Caches', "cursor-updater"), path.join(condition2, ".cursor")];
|
||
for (const storagePath of items) {
|
||
try {
|
||
if (fs.existsSync(storagePath)) {
|
||
fs.rmSync(storagePath, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
count++;
|
||
}
|
||
} catch (pathErr) {
|
||
console.warn("[CursorPro] 娓呯悊澶辫触: " + storagePath, pathErr);
|
||
}
|
||
}
|
||
} else {
|
||
const items = [path.join(condition2, ".config", "Cursor"), path.join(condition2, ".cache", "Cursor"), path.join(condition2, ".local", "share", "Cursor"), path.join(condition2, ".cursor")];
|
||
for (const machineIdPath of items) {
|
||
try {
|
||
if (fs.existsSync(machineIdPath)) {
|
||
fs.rmSync(machineIdPath, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
count++;
|
||
}
|
||
} catch (seamlessErr) {
|
||
console.warn("[CursorPro] 娓呯悊澶辫触: " + machineIdPath, seamlessErr);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
vscode.window.showInformationMessage("鉁?Cursor 鐜娓呯悊瀹屾垚锛佸凡娓呯悊 " + count + " 涓洰褰曘€傝閲嶆柊鍚姩 Cursor銆?);
|
||
} catch (cleanErr) {
|
||
vscode.window.showErrorMessage("娓呯悊澶辫触: " + cleanErr);
|
||
}
|
||
}
|
||
_cleanProxySettings() {
|
||
try {
|
||
const platform = process.platform;
|
||
const condition = process.env.HOME || process.env.USERPROFILE || '';
|
||
let settingsPath;
|
||
if (platform === "win32") {
|
||
const condition1 = process.env.APPDATA || '';
|
||
settingsPath = path.join(condition1, "Cursor", "User", "settings.json");
|
||
} else {
|
||
if (platform === "darwin") {
|
||
settingsPath = path.join(condition, "Library", "Application Support", "Cursor", 'User', "settings.json");
|
||
} else {
|
||
settingsPath = path.join(condition, ".config", "Cursor", "User", "settings.json");
|
||
}
|
||
}
|
||
if (!fs.existsSync(settingsPath)) {
|
||
return;
|
||
}
|
||
const fileContent = fs.readFileSync(settingsPath, 'utf-8');
|
||
let settingsObj;
|
||
try {
|
||
settingsObj = JSON.parse(fileContent);
|
||
} catch {
|
||
return;
|
||
}
|
||
const items = ["http.proxy", "http.proxyStrictSSL", "http.proxySupport", "cursor.general.disableHttp2", "http.noProxy"];
|
||
let isFalse = false;
|
||
for (const tokenData of items) {
|
||
if (tokenData in settingsObj) {
|
||
isFalse = true;
|
||
delete settingsObj[tokenData];
|
||
}
|
||
}
|
||
if (isFalse) {
|
||
fs.writeFileSync(settingsPath, JSON.stringify(settingsObj, null, 4), "utf-8");
|
||
console.log("[CursorPro] 宸叉竻鐞?settings.json 涓殑鏃т唬鐞嗛厤缃?);
|
||
}
|
||
} catch (proxyErr) {
|
||
console.warn("[CursorPro] 娓呯悊 settings.json 浠g悊閰嶇疆澶辫触:", proxyErr);
|
||
}
|
||
}
|
||
_getHostsPath() {
|
||
return process.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : '/etc/hosts';
|
||
}
|
||
_readHostsFile() {
|
||
try {
|
||
const accountInfo = this._getHostsPath();
|
||
if (fs.existsSync(accountInfo)) {
|
||
return fs.readFileSync(accountInfo, "utf-8");
|
||
}
|
||
} catch (readErr) {
|
||
console.error("[CursorPro] Read hosts error:", cmdOut);
|
||
}
|
||
return '';
|
||
}
|
||
_hasHostsConfig() {
|
||
const switchResponse = this._readHostsFile();
|
||
return switchResponse.includes(this.HOSTS_MARKER_START);
|
||
}
|
||
async _grantHostsWritePermission() {
|
||
if (process.platform !== "win32") {
|
||
return false;
|
||
}
|
||
try {
|
||
const content = this._getHostsPath();
|
||
const condition = process.env.USERNAME || '';
|
||
if (!condition) {
|
||
return false;
|
||
}
|
||
const replaced = content.replace(/\\/g, "\\\\");
|
||
const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command icacls \\\"" + replaced + '\" /grant ' + condition + ":M' -Verb RunAs -Wait\"";
|
||
await execAsync(result);
|
||
this._hostsPermissionGranted = true;
|
||
console.log("[CursorPro] Hosts file permission granted to user:", condition);
|
||
return true;
|
||
} catch (switchErr) {
|
||
console.error("[CursorPro] Grant hosts permission error:", switchErr);
|
||
return false;
|
||
}
|
||
}
|
||
async _writeHostsFile(content) {
|
||
const content1 = this._getHostsPath();
|
||
try {
|
||
if (process.platform === "win32") {
|
||
let isFalse = false;
|
||
try {
|
||
fs.writeFileSync(content1, content, "utf-8");
|
||
isFalse = true;
|
||
} catch (writeErr1) {
|
||
console.log("[CursorPro] Direct write failed, trying to grant permission");
|
||
}
|
||
if (!isFalse) {
|
||
if (!this._hostsPermissionGranted) {
|
||
const lockedInfo = await this._grantHostsWritePermission();
|
||
if (lockedInfo) {
|
||
try {
|
||
fs.writeFileSync(content1, content, "utf-8");
|
||
remainingCount = true;
|
||
} catch (writeErr2) {
|
||
console.log("[CursorPro] Write still failed after permission grant");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!isFalse) {
|
||
const joinedPath = path.join(process.env.TEMP || '', "cursorpro_hosts_temp.txt");
|
||
fs.writeFileSync(joinedPath, content, "utf-8");
|
||
const replaced = joinedPath.replace(/\\/g, "\\\\");
|
||
const replaced1 = content1.replace(/\\/g, "\\\\");
|
||
const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command Copy-Item -Path \\\"" + replaced + '\" -Destination \"' + replaced1 + "\\\" -Force' -Verb RunAs -Wait\"";
|
||
await execAsync(result);
|
||
try {
|
||
fs.unlinkSync(joinedPath);
|
||
} catch {}
|
||
}
|
||
try {
|
||
await execAsync("ipconfig /flushdns");
|
||
console.log("[CursorPro] Windows DNS 缂撳瓨宸插埛鏂?);
|
||
} catch (resetErr) {
|
||
console.warn("[CursorPro] Windows DNS 鍒锋柊澶辫触:", resetErr);
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
const pathStr = "/tmp/hosts_cursor_temp";
|
||
fs.writeFileSync(pathStr, content, "utf-8");
|
||
const content1 = "do shell script \"cp '" + pathStr + "' '" + content1 + "' && rm '" + pathStr + "' && dscacheutil -flushcache && killall -HUP mDNSResponder\" with administrator privileges";
|
||
await execAsync('osascript -e "' + content1.replace(/"/g, "\\\"") + "\"");
|
||
} else {
|
||
fs.writeFileSync(content1, content, "utf-8");
|
||
}
|
||
}
|
||
return true;
|
||
} catch (disableErr) {
|
||
console.error("[CursorPro] Write hosts error:", disableErr);
|
||
return false;
|
||
}
|
||
}
|
||
async _handleToggleProxy(enabled, silent) {
|
||
try {
|
||
if (enabled) {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
const expireDate = this._context.globalState.get('cursorpro.expireDate');
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "璇峰厛婵€娲绘巿鏉冪爜"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': '璇峰厛婵€娲绘巿鏉冪爜',
|
||
'icon': '鈿狅笍'
|
||
});
|
||
return;
|
||
}
|
||
if (expireDate) {
|
||
const resetResponse = new Date(expireDate).getTime();
|
||
if (Date.now() > resetResponse) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "鎺堟潈鐮佸凡杩囨湡锛屾棤娉曞紑鍚厤榄旀硶"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "鎺堟潈鐮佸凡杩囨湡锛屾棤娉曞紑鍚厤榄旀硶",
|
||
'icon': '鈿狅笍'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
this._cleanProxySettings();
|
||
let content = this._readHostsFile();
|
||
const index = content.indexOf(this.HOSTS_MARKER_START);
|
||
const index1 = content.indexOf(this.HOSTS_MARKER_END);
|
||
if (index !== -1 && index1 !== -1) {
|
||
content = content.substring(0, index) + content.substring(index1 + this.HOSTS_MARKER_END.length);
|
||
}
|
||
content = content.replace(/\n{3,}/g, "\n\n").trim();
|
||
if (enabled) {
|
||
const joinedPath = this.CURSOR_DOMAINS.map(item => this.SNI_PROXY_IP + " " + item).join("\n");
|
||
const result = "\n\n" + this.HOSTS_MARKER_START + "\n" + joinedPath + "\n" + this.HOSTS_MARKER_END + "\n";
|
||
content += result;
|
||
}
|
||
const disableResponse = await this._writeHostsFile(content);
|
||
if (disableResponse) {
|
||
await client_1.updateProxyConfig(enabled, this.SNI_PROXY_IP);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': true,
|
||
'enabled': enabled,
|
||
'url': this.SNI_PROXY_IP
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': enabled ? "鍏嶉瓟娉曞凡寮€鍚? : "鍏嶉瓟娉曞凡鍏抽棴",
|
||
'icon': '鉁?
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "淇敼 hosts 鏂囦欢澶辫触锛岃纭繚鏈夌鐞嗗憳鏉冮檺"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "闇€瑕佺鐞嗗憳鏉冮檺淇敼 hosts 鏂囦欢",
|
||
'icon': '鈿狅笍'
|
||
});
|
||
}
|
||
} catch (updateErr) {
|
||
console.error("[CursorPro] Toggle proxy error:", updateErr);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "鏇存柊閰嶇疆澶辫触"
|
||
});
|
||
}
|
||
}
|
||
async _handleGetProxyStatus() {
|
||
try {
|
||
const enabled = this._hasHostsConfig();
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': enabled,
|
||
'url': enabled ? this.SNI_PROXY_IP : ''
|
||
});
|
||
} catch (envErr) {
|
||
console.error("[CursorPro] Get proxy status error:", envErr);
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': false,
|
||
'url': ''
|
||
});
|
||
}
|
||
}
|
||
async _handleGetSeamlessStatus() {
|
||
try {
|
||
const workbenchPath = await this._getWorkbenchPathAsync();
|
||
let isInjected = false;
|
||
if (workbenchPath && fs.existsSync(workbenchPath)) {
|
||
const fileContent = fs.readFileSync(workbenchPath, 'utf-8');
|
||
isInjected = this._checkInjected(fileContent);
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': isInjected,
|
||
'workbench_path': workbenchPath || '鏈壘鍒?
|
||
});
|
||
} catch (e1) {
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': false,
|
||
'error': "妫€娴嬬姸鎬佸け璐?
|
||
});
|
||
}
|
||
}
|
||
async _getCursorInstallPath() {
|
||
if (this._cachedCursorPath) {
|
||
return this._cachedCursorPath;
|
||
}
|
||
const config = vscode.workspace.getConfiguration("cursorpro");
|
||
const configValue = config.get("cursorPath");
|
||
if (configValue && fs.existsSync(configValue)) {
|
||
console.log("[CursorPro] 浣跨敤鐢ㄦ埛閰嶇疆鐨?Cursor 璺緞:", configValue);
|
||
this._cachedCursorPath = configValue;
|
||
return configValue;
|
||
}
|
||
const platform = process.platform;
|
||
let result = null;
|
||
try {
|
||
if (platform === "win32") {
|
||
try {
|
||
const {
|
||
stdout: wmicOut
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
if (wmicOut) {
|
||
const matchResult = wmicOut.match(/ExecutablePath=(.+)/);
|
||
if (matchResult && matchResult[1]) {
|
||
const trimmed = matchResult[1].trim();
|
||
result = path.dirname(trimmed);
|
||
}
|
||
}
|
||
} catch (e2) {
|
||
console.log("[CursorPro] WMIC 鑾峰彇璺緞澶辫触");
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: psOut
|
||
} = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
|
||
if (psOut && psOut.trim()) {
|
||
result = path.dirname(psOut.trim());
|
||
}
|
||
} catch (e3) {
|
||
console.log("[CursorPro] PowerShell Get-Process 鑾峰彇璺緞澶辫触");
|
||
}
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: regOut
|
||
} = await execAsync("reg query \"HKCUSoftwareMicrosoftWindowsCurrentVersionUninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (regOut && regOut.trim()) {
|
||
const matchResult = regOut.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
|
||
result = matchResult[1].trim();
|
||
}
|
||
}
|
||
} catch (e4) {
|
||
console.log("[CursorPro] 娉ㄥ唽琛ㄦ柟娉?鑾峰彇璺緞澶辫触");
|
||
}
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: regOut2
|
||
} = await execAsync("reg query \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (regOut2 && regOut2.trim()) {
|
||
const matchResult = regOut2.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
|
||
cursorPath = matchResult[1].trim();
|
||
}
|
||
}
|
||
} catch (e5) {
|
||
console.log("[CursorPro] 娉ㄥ唽琛ㄦ柟娉?鑾峰彇璺緞澶辫触");
|
||
}
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const joinedPath = path.join(process.env.APPDATA || '', "Microsoft", "Windows", "Start Menu", 'Programs', "Cursor.lnk");
|
||
const joinedPath1 = path.join("C:\\ProgramData", "Microsoft", 'Windows', "Start Menu", "Programs", "Cursor.lnk");
|
||
for (const content of [joinedPath, joinedPath1]) {
|
||
if (fs.existsSync(content)) {
|
||
const {
|
||
stdout: lnkOut
|
||
} = await execAsync("powershell -Command \"(New-Object -ComObject WScript.Shell).CreateShortcut('" + content.replace(/'/g, "''") + "').TargetPath\"");
|
||
if (lnkOut && lnkOut.trim() && fs.existsSync(lnkOut.trim())) {
|
||
result = path.dirname(lnkOut.trim());
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (e6) {
|
||
console.log("[CursorPro] 蹇嵎鏂瑰紡瑙f瀽鑾峰彇璺緞澶辫触");
|
||
}
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: whereOut
|
||
} = await execAsync("where cursor 2>nul");
|
||
if (whereOut && whereOut.trim()) {
|
||
const parts = whereOut.trim().split("\n");
|
||
for (const str of parts) {
|
||
const trimmed = str.trim();
|
||
if (trimmed && fs.existsSync(trimmed)) {
|
||
cursorPath = path.dirname(trimmed);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (whereErr) {
|
||
console.log("[CursorPro] where 鍛戒护鑾峰彇璺緞澶辫触");
|
||
}
|
||
}
|
||
if (!result) {
|
||
const condition = process.env.LOCALAPPDATA || '';
|
||
const condition1 = process.env.USERPROFILE || '';
|
||
const condition2 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const condition3 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||
const items = [path.join(condition, "Programs", "Cursor"), path.join(condition, "Programs", "cursor"), path.join(condition1, "AppData", "Local", "Programs", "Cursor"), path.join(condition2, "Cursor"), path.join(condition3, "Cursor"), path.join(condition, "Cursor"), path.join(condition, "cursor")];
|
||
for (const cursorDbPath of items) {
|
||
if (cursorDbPath && fs.existsSync(cursorDbPath)) {
|
||
result = cursorDbPath;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (platform === "darwin") {
|
||
try {
|
||
const {
|
||
stdout: dirEntry
|
||
} = await execAsync("lsof -c Cursor 2>/dev/null | grep \"txt\" | grep -i \"Cursor.app\" | head -1 | awk '{print $9}'");
|
||
if (dirEntry && dirEntry.trim()) {
|
||
const matchResult = dirEntry.trim().match(/(.+\.app)/);
|
||
if (matchResult) {
|
||
result = matchResult[1];
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: fileItem
|
||
} = await execAsync("ps -eo comm,args | grep -i \"[C]ursor\" | grep -v \"grep\" | head -1");
|
||
if (fileItem && fileItem.trim()) {
|
||
const matchResult = fileItem.match(/(\/.+\.app)/);
|
||
if (matchResult) {
|
||
cursorPath = matchResult[1];
|
||
}
|
||
}
|
||
} catch (findErr) {
|
||
console.warn("[CursorPro] macOS 鑾峰彇杩涚▼璺緞澶辫触:", findErr);
|
||
}
|
||
}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: childPath
|
||
} = await execAsync("mdfind \"kMDItemCFBundleIdentifier == 'com.todesktop.*cursor*'\" 2>/dev/null | head -1");
|
||
if (childPath && childPath.trim() && fs.existsSync(childPath.trim())) {
|
||
result = childPath.trim();
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
if (!result && fs.existsSync('/Applications/Cursor.app')) {
|
||
result = "/Applications/Cursor.app";
|
||
}
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: pathItem
|
||
} = await execAsync('pgrep -f "[c]ursor" | head -1');
|
||
const condition = pathItem && pathItem.trim();
|
||
if (condition) {
|
||
const {
|
||
stdout: subDir
|
||
} = await execAsync("readlink -f /proc/" + condition + "/exe 2>/dev/null");
|
||
if (subDir && subDir.trim()) {
|
||
const trimmed = subDir.trim();
|
||
cursorPath = path.dirname(trimmed);
|
||
if (result.endsWith("/bin")) {
|
||
result = path.dirname(result);
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
if (!result) {
|
||
try {
|
||
const {
|
||
stdout: subItem
|
||
} = await execAsync("which cursor 2>/dev/null");
|
||
if (subItem && subItem.trim()) {
|
||
const execResult = await execAsync('readlink -f "' + subItem.trim() + '" 2>/dev/null');
|
||
if (execResult.stdout && execResult.stdout.trim()) {
|
||
cursorPath = path.dirname(execResult.stdout.trim());
|
||
if (result.endsWith('/bin')) {
|
||
cursorPath = path.dirname(result);
|
||
}
|
||
}
|
||
}
|
||
} catch (checkErr) {
|
||
console.warn("[CursorPro] Linux 鑾峰彇杩涚▼璺緞澶辫触:", checkErr);
|
||
}
|
||
}
|
||
if (!result) {
|
||
const items = ["/opt/Cursor", "/opt/cursor", "/usr/share/cursor", "/usr/lib/cursor", path.join(process.env.HOME || '', ".local/share/cursor"), path.join(process.env.HOME || '', "Applications/cursor")];
|
||
for (const statusInfo of items) {
|
||
if (fs.existsSync(statusInfo)) {
|
||
result = statusInfo;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (injectErr) {
|
||
console.error("[CursorPro] 鑾峰彇 Cursor 瀹夎璺緞澶辫触:", injectErr);
|
||
}
|
||
if (result) {
|
||
this._cachedCursorPath = result;
|
||
}
|
||
return result;
|
||
}
|
||
_getWorkbenchPath() {
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
_getWorkbenchPathSync() {
|
||
const platform = process.platform;
|
||
if (this._cachedCursorPath) {
|
||
let entry;
|
||
if (platform === "darwin") {
|
||
entry = path.join(this._cachedCursorPath, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
entry = path.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (fs.existsSync(entry)) {
|
||
return entry;
|
||
}
|
||
}
|
||
if (platform === 'win32') {
|
||
return null;
|
||
}
|
||
let items = [];
|
||
if (platform === "darwin") {
|
||
items = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
|
||
} else {
|
||
items = ["/opt/Cursor/resources/app/out/vs/workbench/workbench.desktop.main.js", '/usr/share/cursor/resources/app/out/vs/workbench/workbench.desktop.main.js'];
|
||
}
|
||
for (const switchInfo of items) {
|
||
if (fs.existsSync(switchInfo)) {
|
||
return switchInfo;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
async _getWorkbenchPathAsync() {
|
||
const platform = process.platform;
|
||
const cursorPath = await this._getCursorInstallPath();
|
||
if (cursorPath) {
|
||
let workbenchSubPath;
|
||
if (platform === "darwin") {
|
||
workbenchSubPath = path.join(cursorPath, "Contents", "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
workbenchSubPath = path.join(cursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (fs.existsSync(workbenchSubPath)) {
|
||
return workbenchSubPath;
|
||
}
|
||
}
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
_checkInjected(cbArg) {
|
||
return cbArg.includes("/*i0*/") || cbArg.includes('/*i1s*/');
|
||
}
|
||
async _isSeamlessInjected() {
|
||
try {
|
||
const workbenchPath = await this._getWorkbenchPathAsync();
|
||
if (workbenchPath && fs.existsSync(workbenchPath)) {
|
||
const fileContent = fs.readFileSync(workbenchPath, "utf-8");
|
||
return this._checkInjected(fileContent);
|
||
}
|
||
return false;
|
||
} catch (restoreErr) {
|
||
console.error("[CursorPro] 妫€娴嬫棤鎰熸崲鍙风姸鎬佸け璐?", restoreErr);
|
||
return false;
|
||
}
|
||
}
|
||
_getInjectionConfig(msgData, dataArg) {
|
||
return [{
|
||
'name': "娉ㄥ叆鐐?: 瀹屾暣鎬ф鏌ョ粫杩?,
|
||
'scode': "_showNotification(){",
|
||
'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'restore': {
|
||
'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'replace_with': "_showNotification(){"
|
||
}
|
||
}, {
|
||
'name': "娉ㄥ叆鐐?: 鏍稿績妯″潡鍒濆鍖?,
|
||
'scode': "this.database.getItems()))",
|
||
'replacement': "this.database.getItems()))/*i1s*/;await(async function(e){if(e.get('releaseNotes/lastVersion')){window.store=e;window.__cpKey='CursorPro2024!@#';window.__cpEnc=function(t){var k=window.__cpKey,r='';for(var i=0;i<t.length;i++)r+=String.fromCharCode(t.charCodeAt(i)^k.charCodeAt(i%k.length));return btoa(r)};window.__cpDec=function(t){var k=window.__cpKey,d=atob(t),r='';for(var i=0;i<d.length;i++)r+=String.fromCharCode(d.charCodeAt(i)^k.charCodeAt(i%k.length));return r};window.__cpGet=function(){try{var d=localStorage.getItem('__cp_token');return d?JSON.parse(window.__cpDec(d)):null}catch(e){return null}};window.__cpSet=function(data){try{localStorage.setItem('__cp_token',window.__cpEnc(JSON.stringify(data)))}catch(e){}};window.__cpApi='" + msgData + "';window.__cpUserKey='" + dataArg + "';window.__cpVersion=0;console.log('[CP] Initialized with key:','" + dataArg + "'.substring(0,15)+'...')}})(this)/*i1e*/",
|
||
'restore': {
|
||
'find_start': "/*i1s*/",
|
||
'find_end': "/*i1e*/"
|
||
}
|
||
}, {
|
||
'name': "娉ㄥ叆鐐?: 鍚姩鏃禩oken鍚屾",
|
||
'scode': "/*i1e*/",
|
||
'replacement': "/*i1e*//*i2s*/;(function(){window.__cpSyncing=false;window.__cpSync=function(){if(window.__cpSyncing){console.log('[CP] Sync already in progress, skip');return}var userKey=window.__cpUserKey;if(!userKey){console.log('[CP] No userKey, skip sync');return}window.__cpSyncing=true;console.log('[CP] Sync with key:',userKey.substring(0,15)+'...');fetch(window.__cpApi+'/api/seamless/get-token?userKey='+encodeURIComponent(userKey)).then(function(r){return r.json()}).then(function(d){window.__cpSyncing=false;if(d.error){console.error('[CP] Sync error:',d.error);return}if(d&&d.accessToken){var oldToken=window.__cpGet();var needUpdate=!oldToken||oldToken.email!==d.email||window.__cpVersion!==d.switchVersion;if(needUpdate){window.__cpVersion=d.switchVersion||0;window.__cpSet({accessToken:d.accessToken,refreshToken:d.refreshToken||'',email:d.email||'',machineIds:d.machineIds||null,switchRemaining:d.switchRemaining,switchVersion:d.switchVersion||0});window.store.set('cursorAuth/accessToken',d.accessToken,-1);if(d.refreshToken)window.store.set('cursorAuth/refreshToken',d.refreshToken,-1);if(d.email)window.store.set('cursor.email',d.email,-1);if(d.is_new&&d.machineIds){if(d.machineIds.devDeviceId)window.store.set('telemetry.devDeviceId',d.machineIds.devDeviceId,-1);if(d.machineIds.machineId)window.store.set('telemetry.machineId',d.machineIds.machineId,-1);if(d.machineIds.macMachineId)window.store.set('telemetry.macMachineId',d.machineIds.macMachineId,-1);if(d.machineIds.sqmId)window.store.set('telemetry.sqmId',d.machineIds.sqmId,-1)}console.log('[CP] Token UPDATED:',d.email,'v'+d.switchVersion)}}}).catch(function(e){window.__cpSyncing=false;console.error('[CP] Sync error:',e)})};console.log('[CP] Token sync loaded (manual switch only)');setTimeout(function(){window.__cpSync()},2000);setInterval(function(){window.__cpSync()},10000)})()/*i2e*/",
|
||
'restore': {
|
||
'find_start': "/*i2s*/",
|
||
'find_end': "/*i2e*/"
|
||
}
|
||
}];
|
||
}
|
||
async _handleInjectSeamless() {
|
||
try {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "璇峰厛婵€娲绘巿鏉冪爜"
|
||
});
|
||
return;
|
||
}
|
||
const status = await client_1.getUserSwitchStatus(savedKey);
|
||
if (!status.valid) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': status.error || "鎺堟潈鐮佹棤鏁?
|
||
});
|
||
return;
|
||
}
|
||
const workbenchPath = await this._getWorkbenchPathAsync();
|
||
if (!workbenchPath) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "鍚敤澶辫触"
|
||
});
|
||
return;
|
||
}
|
||
const result = workbenchPath + ".backup";
|
||
const not = !this._context.globalState.get("cursorpro.seamlessInjected");
|
||
if (not && fs.existsSync(result)) {
|
||
console.log("[CursorPro] 棣栨鍚敤锛屼粠澶囦唤鎭㈠骞插噣鐨?workbench 鏂囦欢");
|
||
try {
|
||
fs.copyFileSync(result, workbenchPath);
|
||
console.log("[CursorPro] 澶囦唤鎭㈠鎴愬姛");
|
||
} catch (usageErr) {
|
||
console.error("[CursorPro] 澶囦唤鎭㈠澶辫触:", usageErr);
|
||
}
|
||
}
|
||
let fileContent = fs.readFileSync(workbenchPath, 'utf-8');
|
||
if (this._checkInjected(fileContent)) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "宸插惎鐢?,
|
||
'icon': '鉁?
|
||
});
|
||
return;
|
||
}
|
||
if (!fs.existsSync(result)) {
|
||
fs.copyFileSync(workbenchPath, result);
|
||
console.log("[CursorPro] 鍒涘缓澶囦唤鏂囦欢");
|
||
}
|
||
const announceList = client_1.getApiUrl();
|
||
const latestAnnounce = this._getInjectionConfig(announceList, savedKey);
|
||
const items = [];
|
||
const items1 = [];
|
||
for (const linuxPath of latestAnnounce) {
|
||
if (fileContent.includes(linuxPath.scode)) {
|
||
fileContent = fileContent.replace(linuxPath.scode, linuxPath.replacement);
|
||
items.push(linuxPath.name);
|
||
} else {
|
||
items1.push(linuxPath.name);
|
||
}
|
||
}
|
||
if (items.length === 0) {
|
||
console.error("[CursorPro] 娉ㄥ叆澶辫触锛屾湭鎵惧埌浠讳綍娉ㄥ叆鐐?);
|
||
console.error("[CursorPro] 鏂囦欢璺緞:", workbenchPath);
|
||
console.error("[CursorPro] 鏂囦欢澶у皬:", fileContent.length);
|
||
console.error("[CursorPro] 鏈壘鍒扮殑娉ㄥ叆鐐?", items1);
|
||
const versionResponse = fileContent.includes("_showNotification");
|
||
const latestVersion = fileContent.includes("getItems()");
|
||
console.error("[CursorPro] 鍖呭惈 _showNotification:", versionResponse);
|
||
console.error("[CursorPro] 鍖呭惈 getItems():", latestVersion);
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "Cursor 鐗堟湰涓嶅吋瀹癸紝娉ㄥ叆鐐规湭鎵惧埌",
|
||
'details': "璺緞: " + workbenchPath
|
||
});
|
||
return;
|
||
}
|
||
console.log("[CursorPro] 娉ㄥ叆鎴愬姛锛屽簲鐢ㄧ殑娉ㄥ叆鐐?", items);
|
||
if (items1.length > 0) {
|
||
console.warn("[CursorPro] 鏈壘鍒扮殑娉ㄥ叆鐐?", items1);
|
||
}
|
||
try {
|
||
fs.writeFileSync(workbenchPath, fileContent, "utf-8");
|
||
} catch (writeErr) {
|
||
console.error("[CursorPro] 鍐欏叆鏂囦欢澶辫触:", lsofOut);
|
||
if (writeErr.code === "EPERM" || writeErr.code === "EACCES" || writeErr.code === "EROFS") {
|
||
const platform = process.platform;
|
||
let errorMsg = "娌℃湁鍐欏叆鏉冮檺";
|
||
if (platform === "darwin") {
|
||
errorMsg = "娌℃湁鍐欏叆鏉冮檺锛岃鍦ㄧ粓绔墽琛? sudo chmod -R 777 /Applications/Cursor.app";
|
||
} else if (platform === "linux") {
|
||
errorMsg = "娌℃湁鍐欏叆鏉冮檺锛岃浣跨敤 sudo 鏉冮檺杩愯鎴栦慨鏀规枃浠舵潈闄?;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': errorMsg,
|
||
'needAdmin': true,
|
||
'path': workbenchPath
|
||
});
|
||
return;
|
||
}
|
||
throw writeErr;
|
||
}
|
||
await this._context.globalState.update("cursorpro.seamlessInjected", true);
|
||
this._postMessage({
|
||
'type': 'seamlessInjected',
|
||
'success': true,
|
||
'applied': items,
|
||
'needRestart': true,
|
||
'message': "鏃犳劅鎹㈠彿宸插惎鐢?
|
||
});
|
||
} catch (appDir) {
|
||
console.error("[CursorPro] Inject error:", appDir);
|
||
if (appDir.code === "EPERM" || appDir.code === "EACCES") {
|
||
const errorMsg = "娌℃湁鍐欏叆鏉冮檺";
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': errorMsg,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': appDir.message || '娉ㄥ叆澶辫触'
|
||
});
|
||
}
|
||
}
|
||
async _handleRestoreSeamless() {
|
||
try {
|
||
const workbenchPath = await this._getWorkbenchPathAsync();
|
||
if (!workbenchPath) {
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': '鏈壘鍒癈ursor瀹夎鐩綍'
|
||
});
|
||
return;
|
||
}
|
||
let fileContent = fs.readFileSync(workbenchPath, "utf-8");
|
||
if (!this._checkInjected(fileContent)) {
|
||
return;
|
||
}
|
||
fileContent = fileContent.replace("_showNotification(){/*i0*/}_showNotificationOld(){", "_showNotification(){");
|
||
const index = fileContent.indexOf("/*i1s*/");
|
||
const index1 = fileContent.indexOf("/*i1e*/");
|
||
if (index !== -1 && index1 !== -1) {
|
||
fileContent = fileContent.substring(0, index) + fileContent.substring(index1 + 7);
|
||
}
|
||
const index2 = fileContent.indexOf("/*i2s*/");
|
||
const index3 = fileContent.indexOf("/*i2e*/");
|
||
if (index2 !== -1 && index3 !== -1) {
|
||
fileContent = fileContent.substring(0, index2) + fileContent.substring(index3 + 7);
|
||
}
|
||
try {
|
||
fs.writeFileSync(workbenchPath, fileContent, "utf-8");
|
||
} catch (writeErr) {
|
||
if (writeErr.code === "EPERM" || subPath.code === "EACCES") {
|
||
const errorMsg = "娌℃湁鍐欏叆鏉冮檺";
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': errorMsg,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
throw writeErr;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': "鏃犳劅鎹㈠彿宸茬鐢?
|
||
});
|
||
} catch (restoreErr) {
|
||
console.error("[CursorPro] Restore error:", restoreErr);
|
||
if (restoreErr.code === "EPERM" || psOut2.code === "EACCES") {
|
||
const errorMsg = "娌℃湁鍐欏叆鏉冮檺";
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': errorMsg,
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': restoreErr.message || '杩樺師澶辫触'
|
||
});
|
||
}
|
||
}
|
||
async _handleToggleSeamless(enabled) {
|
||
try {
|
||
await client_1.updateSeamlessConfig({
|
||
'enabled': enabled
|
||
});
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': true,
|
||
'enabled': enabled
|
||
});
|
||
} catch (configErr) {
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': false,
|
||
'error': "鏇存柊閰嶇疆澶辫触"
|
||
});
|
||
}
|
||
}
|
||
async _handleGetUserSwitchStatus() {
|
||
try {
|
||
const savedKey = this._context.globalState.get('cursorpro.key');
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'valid': false,
|
||
'switchRemaining': 0,
|
||
'canSwitch': false,
|
||
'error': "鏈縺娲绘巿鏉冪爜"
|
||
});
|
||
return;
|
||
}
|
||
const status = await client_1.getUserSwitchStatus(savedKey);
|
||
let isFalse = false;
|
||
try {
|
||
const status1 = await client_1.getSeamlessStatus();
|
||
isFalse = status1.is_injected || false;
|
||
} catch (psOut2) {}
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
...status,
|
||
'seamlessEnabled': isFalse
|
||
});
|
||
} catch (e24) {
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'valid': false,
|
||
'switchRemaining': 0,
|
||
'canSwitch': false,
|
||
'error': "鑾峰彇鐘舵€佸け璐?
|
||
});
|
||
}
|
||
}
|
||
async _handleGetAccountUsage(forceRefresh) {
|
||
try {
|
||
if (!forceRefresh) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': "鏈彁渚涜处鍙烽偖绠?
|
||
});
|
||
return;
|
||
}
|
||
const result1 = client_1.getApiUrl() + "/api/cursor-accounts/query?email=" + encodeURIComponent(forceRefresh) + '&refresh=true';
|
||
const cursorRunning = await fetch(result1);
|
||
const result = await cursorRunning.json();
|
||
if (result.success && result.data) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': true,
|
||
'data': result.data
|
||
});
|
||
const condition = result.data.usage || {};
|
||
const condition1 = condition.totalUsageCount || 0;
|
||
const num = parseFloat(condition.totalCostUSD || 0);
|
||
extension_1.updateUsageStatusBar(condition1, num);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': result.error || "鑾峰彇鐢ㄩ噺澶辫触"
|
||
});
|
||
}
|
||
} catch (announceErr) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': announceErr.message || "璇锋眰澶辫触"
|
||
});
|
||
}
|
||
}
|
||
async _handleGetAnnouncement() {
|
||
try {
|
||
const result1 = client_1.getApiUrl() + "/api/announcements/latest";
|
||
const switchCheck = await fetch(result1);
|
||
const result = await switchCheck.json();
|
||
if (result.success && result.data) {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': true,
|
||
'data': result.data
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': result.error || "鑾峰彇鍏憡澶辫触"
|
||
});
|
||
}
|
||
} catch (versionErr) {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': versionErr.message || "璇锋眰澶辫触"
|
||
});
|
||
}
|
||
}
|
||
async _handleCheckVersion() {
|
||
try {
|
||
const result = await client_1.getLatestVersion();
|
||
if (result.success && result.version) {
|
||
const versionInfo = result.version;
|
||
const seamlessPath = CursorProViewProvider.CURRENT_VERSION;
|
||
const isMatch = this._compareVersions(versionInfo, seamlessPath) > 0;
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': true,
|
||
'currentVersion': seamlessPath,
|
||
'latestVersion': versionInfo,
|
||
'hasUpdate': isMatch
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': result.error || "鑾峰彇鐗堟湰澶辫触"
|
||
});
|
||
}
|
||
} catch (runningErr) {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': runningErr.message || "璇锋眰澶辫触"
|
||
});
|
||
}
|
||
}
|
||
_compareVersions(toggleArg, silentArg) {
|
||
const mapped = toggleArg.split('.').map(Number);
|
||
const mapped1 = silentArg.split('.').map(Number);
|
||
const beforeSwitch = Math.max(mapped.length, mapped1.length);
|
||
for (let count = 0; count < beforeSwitch; count++) {
|
||
const condition = mapped[count] || 0;
|
||
const condition1 = mapped1[count] || 0;
|
||
if (condition > condition1) {
|
||
return 1;
|
||
}
|
||
if (condition < condition1) {
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
async _handleGetCursorRunningPath() {
|
||
try {
|
||
const platform = process.platform;
|
||
let filePath = "鏈壘鍒?;
|
||
let str = '';
|
||
const config = vscode.workspace.getConfiguration("cursorpro");
|
||
const configValue = config.get("cursorPath");
|
||
if (configValue && fs.existsSync(configValue)) {
|
||
filePath = configValue;
|
||
if (platform === "darwin") {
|
||
str = path.join(configValue, "Contents", "Resources", "app", "package.json");
|
||
} else {
|
||
str = path.join(configValue, "resources", "app", "package.json");
|
||
}
|
||
console.log("[CursorPro] 浣跨敤鐢ㄦ埛閰嶇疆鐨勮矾寰?", configValue);
|
||
} else {
|
||
if (platform === "win32") {
|
||
try {
|
||
const {
|
||
stdout: manualErr
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const matchResult = manualErr.match(/ExecutablePath=(.+)/);
|
||
if (matchResult && matchResult[1]) {
|
||
const trimmed = matchResult[1].trim();
|
||
filePath = path.dirname(trimmed);
|
||
str = path.join(filePath, "resources", "app", "package.json");
|
||
}
|
||
} catch (beforeErr) {
|
||
console.log("[CursorPro] WMIC 鑾峰彇璺緞澶辫触:", beforeErr);
|
||
}
|
||
if (filePath === "鏈壘鍒?) {
|
||
const condition = process.env.LOCALAPPDATA || '';
|
||
const items = [path.join(condition, "Programs", 'cursor'), path.join(condition, "cursor")];
|
||
for (const originalCode of items) {
|
||
const joinedPath = path.join(originalCode, "resources", "app", "package.json");
|
||
if (fs.existsSync(joinedPath)) {
|
||
filePath = originalCode;
|
||
str = joinedPath;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (platform === "darwin") {
|
||
filePath = (await this._getCursorInstallPath()) || "/Applications/Cursor.app";
|
||
str = path.join(filePath, "Contents", "Resources", 'app', "package.json");
|
||
} else {
|
||
const condition = process.env.HOME || '';
|
||
const items = ["/usr/share/cursor", path.join(condition, ".local", "share", "cursor")];
|
||
for (const backupDir of items) {
|
||
if (fs.existsSync(backupDir)) {
|
||
filePath = backupDir;
|
||
str = path.join(backupDir, "resources", 'app', "package.json");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const condition = str && fs.existsSync(str);
|
||
let str1 = '';
|
||
if (condition) {
|
||
try {
|
||
const fileContent = fs.readFileSync(str, "utf-8");
|
||
const parsed = JSON.parse(fileContent);
|
||
str1 = parsed.version || '';
|
||
console.log("[CursorPro] 浠庤矾寰勮幏鍙?Cursor 鐗堟湰:", str1);
|
||
} catch (backupErr) {
|
||
console.log("[CursorPro] 璇诲彇 package.json 澶辫触:", backupErr);
|
||
}
|
||
}
|
||
this._postMessage({
|
||
'type': 'cursorRunningPath',
|
||
'path': filePath,
|
||
'packageJsonPath': str,
|
||
'packageExists': condition,
|
||
'cursorVersion': str1
|
||
});
|
||
} catch (codeItem) {
|
||
this._postMessage({
|
||
'type': "cursorRunningPath",
|
||
'path': "鑾峰彇澶辫触: " + (codeItem.message || codeItem),
|
||
'packageJsonPath': '',
|
||
'packageExists': false,
|
||
'cursorVersion': ''
|
||
});
|
||
}
|
||
}
|
||
async _handleCheckUsageBeforeSwitch(silent) {
|
||
try {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': false,
|
||
'error': "鏈縺娲绘巿鏉冪爜"
|
||
});
|
||
return;
|
||
}
|
||
if (!silent) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
return;
|
||
}
|
||
const result1 = client_1.getApiUrl() + '/api/cursor-accounts/query?email=' + encodeURIComponent(silent) + "&refresh=false";
|
||
const seamlessBackup = await fetch(result1);
|
||
const result = await seamlessBackup.json();
|
||
if (result.success && result.data) {
|
||
const condition = result.data.usage || {};
|
||
const num = parseFloat(condition.totalCostUSD || 0);
|
||
if (num < 10) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': true,
|
||
'costUSD': num.toFixed(2),
|
||
'email': silent
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} catch (execOut) {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
}
|
||
async _handleManualSeamlessSwitch() {
|
||
try {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (!savedKey) {
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': "鏈縺娲绘巿鏉冪爜"
|
||
});
|
||
return;
|
||
}
|
||
const switchResult = await client_1.switchSeamlessToken(savedKey);
|
||
if (switchResult.switched) {
|
||
if (switchResult.email) {
|
||
await this._context.globalState.update("cursorpro.seamlessCurrentAccount", switchResult.email);
|
||
}
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': true,
|
||
'email': switchResult.email,
|
||
'switchRemaining': switchResult.switchRemaining
|
||
});
|
||
} else {
|
||
const condition = switchResult.message || switchResult.error || "鎹㈠彿澶辫触";
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': condition
|
||
});
|
||
}
|
||
} catch (tmpErr6) {
|
||
const condition = tmpErr6?.message || "杩炴帴鏈嶅姟鍣ㄥけ璐?;
|
||
this._postMessage({
|
||
'type': "manualSeamlessSwitched",
|
||
'success': false,
|
||
'error': condition
|
||
});
|
||
}
|
||
}
|
||
async _handleGetCursorPath() {
|
||
try {
|
||
const platform = process.platform;
|
||
let str = '';
|
||
let str1 = '';
|
||
if (platform === "win32") {
|
||
try {
|
||
const {
|
||
stdout: patchErr
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const matchResult = patchErr.match(/ExecutablePath=(.+)/);
|
||
if (matchResult && matchResult[1]) {
|
||
const trimmed = matchResult[1].trim();
|
||
str = path.dirname(trimmed);
|
||
}
|
||
} catch (shellOut) {
|
||
try {
|
||
const {
|
||
stdout: lineContent
|
||
} = await execAsync('powershell -Command "Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path"');
|
||
if (lineContent.trim()) {
|
||
str = path.dirname(lineContent.trim());
|
||
}
|
||
} catch (restoreErr2) {
|
||
console.warn("[CursorPro] 鑾峰彇杩涚▼璺緞澶辫触:", restoreErr2);
|
||
}
|
||
}
|
||
const condition = process.env.APPDATA || '';
|
||
str1 = path.join(condition, "Cursor");
|
||
} else {
|
||
if (platform === "darwin") {
|
||
try {
|
||
const {
|
||
stdout: e34
|
||
} = await execAsync("ps aux | grep -i \"[C]ursor\" | head -1 | awk '{print $11}'");
|
||
if (e34.trim()) {
|
||
const trimmed = e34.trim();
|
||
const matchResult = trimmed.match(/(.+\.app)/);
|
||
if (matchResult) {
|
||
str = matchResult[1];
|
||
} else {
|
||
str = path.dirname(trimmed);
|
||
}
|
||
}
|
||
} catch (toggleErr2) {
|
||
console.warn("[CursorPro] 鑾峰彇杩涚▼璺緞澶辫触:", toggleErr2);
|
||
}
|
||
const condition = process.env.HOME || '';
|
||
str1 = path.join(condition, 'Library', "Application Support", "Cursor");
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: e35
|
||
} = await execAsync("ps aux | grep -i \"[c]ursor\" | head -1 | awk '{print $11}'");
|
||
if (e35.trim()) {
|
||
str = path.dirname(e35.trim());
|
||
}
|
||
} catch (seamlessErr2) {
|
||
console.warn("[CursorPro] 鑾峰彇杩涚▼璺緞澶辫触:", seamlessErr2);
|
||
}
|
||
const condition = process.env.HOME || '';
|
||
str1 = path.join(condition, ".config", "Cursor");
|
||
}
|
||
}
|
||
if (!str) {
|
||
str = "鏈娴嬪埌杩愯涓殑Cursor杩涚▼";
|
||
}
|
||
let str2 = '';
|
||
if (str && !str.includes("鏈娴?)) {
|
||
if (platform === "win32") {
|
||
str2 = path.join(str, 'resources', "app", 'out', 'vs', 'workbench', "workbench.desktop.main.js");
|
||
} else {
|
||
if (platform === "darwin") {
|
||
str2 = path.join(str, "Contents", "Resources", "app", "out", 'vs', "workbench", 'workbench.desktop.main.js');
|
||
} else {
|
||
str2 = path.join(str, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
}
|
||
if (!fs.existsSync(str2)) {
|
||
str2 = (await this._getWorkbenchPathAsync()) || "鏈壘鍒?;
|
||
}
|
||
} else {
|
||
injectionCode = (await this._getWorkbenchPathAsync()) || "鏈壘鍒?;
|
||
}
|
||
const value = str && !str.includes("鏈娴?) ? fs.existsSync(str) : false;
|
||
const value1 = str1 ? fs.existsSync(str1) : false;
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': value ? str : str || "鏈壘鍒?,
|
||
'dataPath': value1 ? str1 : "鏈壘鍒?,
|
||
'workbenchPath': str2,
|
||
'platform': platform
|
||
});
|
||
} catch (e37) {
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': "鑾峰彇澶辫触",
|
||
'dataPath': '鑾峰彇澶辫触',
|
||
'workbenchPath': "鑾峰彇澶辫触",
|
||
'error': e37.message
|
||
});
|
||
}
|
||
}
|
||
async _loadAccountsFromDB() {
|
||
try {
|
||
const patchedContent = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: psOut3
|
||
} = patchedContent;
|
||
if (!fs.existsSync(psOut3)) {
|
||
return [];
|
||
}
|
||
const workbenchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/accessToken");
|
||
const patchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/refreshToken");
|
||
const email = await sqlite_1.sqliteGet(psOut3, "cursorAuth/cachedEmail");
|
||
if (workbenchContent && email) {
|
||
return [{
|
||
'email': email,
|
||
'access_token': workbenchContent,
|
||
'refresh_token': patchContent || workbenchContent
|
||
}];
|
||
}
|
||
return [];
|
||
} catch (e38) {
|
||
console.error("[CursorPro] 璇诲彇璐﹀彿澶辫触:", e38);
|
||
return [];
|
||
}
|
||
}
|
||
async _sendState() {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
const expireDate = this._context.globalState.get('cursorpro.expireDate');
|
||
const switchData = this._context.globalState.get("cursorpro.switchRemaining");
|
||
const switchData1 = this._context.globalState.get("cursorpro.switchLimit");
|
||
const cursorversionResult = await this._getCursorVersion();
|
||
const restoreCode = client_1.getOnlineStatus();
|
||
this._postMessage({
|
||
'type': "state",
|
||
'isActivated': !!savedKey,
|
||
'key': savedKey || '',
|
||
'expireDate': expireDate || '',
|
||
'switchRemaining': switchData ?? 0,
|
||
'switchLimit': switchData1 ?? 100,
|
||
'cursorVersion': cursorversionResult,
|
||
'isOnline': restoreCode
|
||
});
|
||
}
|
||
async _handleRetryConnect() {
|
||
try {
|
||
const savedKey = this._context.globalState.get("cursorpro.key");
|
||
if (savedKey) {
|
||
await client_1.verifyKey(savedKey);
|
||
} else {
|
||
const result = client_1.getApiUrl() + '/api/announcements/latest';
|
||
await fetch(result, {
|
||
'method': 'GET'
|
||
});
|
||
}
|
||
await this._sendState();
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': true
|
||
});
|
||
} catch (execErr) {
|
||
console.error("[CursorPro] Retry connect failed:", execErr);
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': false
|
||
});
|
||
}
|
||
}
|
||
async _getCursorVersion() {
|
||
try {
|
||
const platform = process.platform;
|
||
const items = [];
|
||
const cursorPath = await this._getCursorInstallPath();
|
||
if (cursorPath) {
|
||
if (platform === "darwin") {
|
||
items.push(path.join(cursorPath, "Contents", "Resources", "app", 'package.json'));
|
||
} else {
|
||
items.push(path.join(cursorPath, "resources", 'app', "package.json"));
|
||
}
|
||
}
|
||
if (platform === "win32") {
|
||
const condition = process.env.LOCALAPPDATA || '';
|
||
const condition1 = process.env.USERPROFILE || '';
|
||
const condition2 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const condition3 = process.env['ProgramFiles(x86)'] || "C:\\Program Files (x86)";
|
||
items.push(path.join(condition, "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition, "Programs", "cursor", "resources", 'app', "package.json"), path.join(condition1, "AppData", "Local", "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition2, "Cursor", "resources", 'app', "package.json"), path.join(condition2, "cursor", "resources", "app", "package.json"), path.join(condition3, "Cursor", "resources", "app", "package.json"));
|
||
} else {
|
||
if (platform === "darwin") {
|
||
items.push("/Applications/Cursor.app/Contents/Resources/app/package.json");
|
||
} else {
|
||
const condition = process.env.HOME || '';
|
||
items.push("/usr/share/cursor/resources/app/package.json", "/opt/Cursor/resources/app/package.json", "/opt/cursor/resources/app/package.json", path.join(condition, ".local", 'share', "cursor", "resources", 'app', "package.json"));
|
||
}
|
||
}
|
||
for (const seamlessCode of items) {
|
||
try {
|
||
if (fs.existsSync(seamlessCode)) {
|
||
const fileContent = fs.readFileSync(seamlessCode, "utf-8");
|
||
const parsed = JSON.parse(fileContent);
|
||
if (parsed.version) {
|
||
console.log("[CursorPro] 鎵惧埌 Cursor 鐗堟湰:", parsed.version, "璺緞:", seamlessCode);
|
||
return parsed.version;
|
||
}
|
||
}
|
||
} catch (fsErr) {
|
||
console.log("[CursorPro] 灏濊瘯璺緞澶辫触:", seamlessCode, fsErr);
|
||
}
|
||
}
|
||
try {
|
||
const module = require("vscode");
|
||
if (module.version) {
|
||
console.log("[CursorPro] 浣跨敤 VS Code API 鑾峰彇鐗堟湰:", module.version);
|
||
return module.version;
|
||
}
|
||
} catch (cmdOut2) {}
|
||
console.log("[CursorPro] 鏈壘鍒?Cursor 鐗堟湰锛屽皾璇曠殑璺緞:", items);
|
||
return '鏈煡';
|
||
} catch (finalErr) {
|
||
console.error("[CursorPro] 鑾峰彇 Cursor 鐗堟湰澶辫触:", finalErr);
|
||
return '鏈煡';
|
||
}
|
||
}
|
||
_postMessage(message) {
|
||
this._view?.webview.postMessage(message);
|
||
}
|
||
_getNonce() {
|
||
let str = '';
|
||
const items = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
for (let count = 0; count < 32; count++) {
|
||
str += items.charAt(Math.floor(Math.random() * items.length));
|
||
}
|
||
return str;
|
||
}
|
||
_getHtmlContent(lineStr) {
|
||
const newContent = this._getNonce();
|
||
return "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-" + newContent + "'; img-src " + lineStr.cspSource + " https: data:; font-src " + lineStr.cspSource + "; worker-src 'none';\">\n <title>CursorPro</title>\n <script nonce=\"" + newContent + "\">\n // 灏芥棭娓呯悊 Service Worker锛堝湪 head 涓墽琛岋紝姣?body 鏇存棭锛塡n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.getRegistrations().then(function(regs) {\n regs.forEach(function(reg) { reg.unregister(); });\n }).catch(function() {});\n }\n </script>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n background: #1e1e1e;\n color: #cccccc;\n padding: 12px;\n font-size: 13px;\n }\n \n .section {\n margin-bottom: 16px;\n padding: 12px;\n background: #252526;\n border-radius: 6px;\n }\n \n .section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 13px;\n color: #ffffff;\n }\n \n .section-title .icon {\n font-size: 16px;\n }\n \n .status-badge {\n margin-left: auto;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n }\n \n .status-badge.inactive {\n background: #6e3232;\n color: #ff6b6b;\n }\n \n .status-badge.active {\n background: #2d4a3e;\n color: #4ade80;\n }\n \n .input-group {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n }\n \n input[type=\"text\"] {\n flex: 1;\n padding: 8px 12px;\n background: #3c3c3c;\n border: 1px solid #4a4a4a;\n border-radius: 4px;\n color: #ffffff;\n font-size: 13px;\n }\n \n input[type=\"text\"]::placeholder {\n color: #888888;\n }\n \n input[type=\"text\"]:focus {\n outline: none;\n border-color: #007acc;\n }\n \n .btn {\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n font-size: 13px;\n cursor: pointer;\n font-weight: 500;\n transition: opacity 0.2s;\n }\n \n .btn:hover {\n opacity: 0.9;\n }\n \n .btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n .btn-primary {\n background: #007acc;\n color: white;\n }\n \n .btn-purple {\n background: #8b5cf6;\n color: white;\n }\n \n .btn-blue {\n background: #3b82f6;\n color: white;\n }\n \n .btn-red {\n background: #ef4444;\n color: white;\n }\n \n .btn-block {\n display: block;\n width: 100%;\n margin-bottom: 8px;\n }\n \n .info-row {\n display: flex;\n justify-content: space-between;\n padding: 6px 0;\n border-bottom: 1px solid #3c3c3c;\n }\n \n .info-row:last-child {\n border-bottom: none;\n }\n \n .info-label {\n color: #888888;\n }\n \n .info-value {\n color: #ffffff;\n }\n \n .usage-row {\n display: flex;\n gap: 12px;\n margin-bottom: 8px;\n }\n \n .usage-row:last-of-type {\n margin-bottom: 0;\n }\n \n .usage-item {\n flex: 1;\n display: flex;\n justify-content: space-between;\n padding: 6px 10px;\n background: #2d2d2d;\n border-radius: 4px;\n }\n \n .switch-container {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n \n .switch {\n position: relative;\n width: 40px;\n height: 20px;\n }\n \n .switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n \n .slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #4a4a4a;\n border-radius: 20px;\n transition: 0.3s;\n }\n \n .slider:before {\n position: absolute;\n content: \"\";\n height: 16px;\n width: 16px;\n left: 2px;\n bottom: 2px;\n background-color: white;\n border-radius: 50%;\n transition: 0.3s;\n }\n \n input:checked + .slider {\n background-color: #8b5cf6;\n }\n \n input:checked + .slider:before {\n transform: translateX(20px);\n }\n \n /* 灏忓昂瀵稿紑鍏虫牱寮?*/\n .switch-sm {\n position: relative;\n width: 32px;\n height: 16px;\n }\n \n .switch-sm .slider:before {\n height: 12px;\n width: 12px;\n left: 2px;\n bottom: 2px;\n }\n \n .switch-sm input:checked + .slider:before {\n transform: translateX(16px);\n }\n \n .pro-badge {\n background: linear-gradient(90deg, #8b5cf6, #d946ef);\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: bold;\n color: white;\n }\n \n .footer {\n margin-top: 16px;\n padding: 12px;\n background: linear-gradient(135deg, rgba(60, 60, 60, 0.3) 0%, rgba(40, 40, 40, 0.5) 100%);\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.05);\n }\n \n .footer-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n \n .auto-start {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: #888;\n }\n \n .cursor-version {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: #666;\n padding: 4px 10px;\n background: rgba(0, 0, 0, 0.2);\n border-radius: 12px;\n }\n \n .cursor-version .version-num {\n color: #a78bfa;\n font-weight: 500;\n }\n \n /* 鑷畾涔夊脊绐楁牱寮?*/\n .modal-overlay {\n display: none;\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.85);\n backdrop-filter: blur(4px);\n z-index: 1000;\n justify-content: center;\n align-items: center;\n animation: fadeIn 0.2s ease;\n }\n \n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes slideIn {\n from { \n opacity: 0;\n transform: scale(0.9) translateY(-10px);\n }\n to { \n opacity: 1;\n transform: scale(1) translateY(0);\n }\n }\n \n .modal-overlay.show {\n display: flex;\n }\n \n .modal-content {\n background: linear-gradient(145deg, #1e1e1e 0%, #2a2a2a 100%);\n border-radius: 12px;\n padding: 16px 20px;\n max-width: 260px;\n width: 90%;\n text-align: center;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.05);\n animation: slideIn 0.2s ease;\n }\n \n .modal-icon {\n width: 44px;\n height: 44px;\n margin: 0 auto 12px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 22px;\n }\n \n .modal-icon.warning {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);\n }\n \n .modal-icon.success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);\n }\n \n .modal-title {\n font-size: 15px;\n font-weight: 600;\n color: #fff;\n margin-bottom: 6px;\n }\n \n .modal-message {\n font-size: 12px;\n color: #9ca3af;\n margin-bottom: 16px;\n line-height: 1.5;\n }\n \n .modal-buttons {\n display: flex;\n gap: 8px;\n justify-content: center;\n }\n \n .modal-btn {\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n \n .modal-btn.primary {\n background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);\n color: white;\n box-shadow: 0 2px 8px rgba(139, 92, 246, 0.4);\n }\n \n .modal-btn.primary:hover {\n box-shadow: 0 4px 12px rgba(139, 92, 246, 0.5);\n }\n \n .modal-btn.secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #9ca3af;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n \n .modal-btn.secondary:hover {\n background: rgba(255, 255, 255, 0.12);\n color: #fff;\n }\n \n .modal-btn.single {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);\n min-width: 100px;\n }\n \n .modal-btn.single:hover {\n box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);\n }\n \n .highlight {\n color: #4ade80;\n font-weight: 600;\n }\n \n .key-display {\n cursor: pointer;\n transition: color 0.2s;\n }\n \n .key-display:hover {\n color: #007acc;\n }\n \n .key-display.copied {\n color: #4ade80 !important;\n }\n \n /* Loading 鐘舵€佹牱寮?*/\n .btn.loading {\n position: relative;\n pointer-events: none;\n opacity: 0.7;\n }\n \n .btn.loading .btn-text {\n visibility: hidden;\n }\n \n .btn.loading::after {\n content: '';\n position: absolute;\n width: 16px;\n height: 16px;\n top: 50%;\n left: 50%;\n margin-left: -8px;\n margin-top: -8px;\n border: 2px solid transparent;\n border-top-color: #fff;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n \n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n \n .refresh-btn.loading {\n animation: spin 1s linear infinite;\n pointer-events: none;\n }\n \n /* 鍏憡鏍峰紡 */\n .announcement-badge {\n margin-left: auto;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n text-transform: uppercase;\n }\n \n .announcement-badge.info {\n background: #1e3a5f;\n color: #60a5fa;\n }\n \n .announcement-badge.warning {\n background: #5c4a1f;\n color: #fbbf24;\n }\n \n .announcement-badge.error {\n background: #6e3232;\n color: #f87171;\n }\n \n .announcement-badge.success {\n background: #2d4a3e;\n color: #4ade80;\n }\n \n .announcement-title {\n font-size: 14px;\n font-weight: 600;\n color: #ffffff;\n margin-bottom: 8px;\n line-height: 1.4;\n }\n \n .announcement-content {\n font-size: 12px;\n color: #b0b0b0;\n line-height: 1.6;\n word-break: break-word;\n }\n \n .announcement-link {\n color: #60a5fa;\n text-decoration: none;\n border-bottom: 1px dashed #60a5fa;\n transition: all 0.2s;\n cursor: pointer;\n }\n \n .announcement-link:hover {\n color: #93c5fd;\n border-bottom-color: #93c5fd;\n }\n \n /* Toast 閫氱煡鏍峰紡 */\n .toast-container {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n display: flex;\n justify-content: center;\n padding: 12px;\n pointer-events: none;\n z-index: 2000;\n }\n \n .toast {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border: 1px solid rgba(74, 222, 128, 0.3);\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 20px rgba(74, 222, 128, 0.1);\n transform: translateY(-100px);\n opacity: 0;\n transition: all 0.3s ease;\n pointer-events: auto;\n }\n \n .toast.show {\n transform: translateY(0);\n opacity: 1;\n }\n \n .toast-icon {\n font-size: 16px;\n }\n \n .toast-message {\n font-size: 12px;\n color: #e0e0e0;\n max-width: 280px;\n word-break: break-all;\n }\n \n /* 绂荤嚎鐘舵€佹彁绀烘牱寮?*/\n .offline-banner {\n display: none;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n margin-bottom: 12px;\n background: linear-gradient(135deg, #7f1d1d 0%, #991b1b 100%);\n border: 1px solid rgba(239, 68, 68, 0.3);\n border-radius: 8px;\n animation: slideDown 0.3s ease;\n }\n \n .offline-banner.show {\n display: flex;\n }\n \n @keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n \n .offline-banner .offline-icon {\n font-size: 18px;\n flex-shrink: 0;\n }\n \n .offline-banner .offline-text {\n flex: 1;\n }\n \n .offline-banner .offline-title {\n font-size: 12px;\n font-weight: 600;\n color: #fca5a5;\n margin-bottom: 2px;\n }\n \n .offline-banner .offline-desc {\n font-size: 11px;\n color: #fecaca;\n opacity: 0.8;\n }\n \n .offline-banner .retry-btn {\n padding: 4px 10px;\n background: rgba(255, 255, 255, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 4px;\n color: #fff;\n font-size: 11px;\n cursor: pointer;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n \n .offline-banner .retry-btn:hover {\n background: rgba(255, 255, 255, 0.25);\n }\n \n .offline-banner .retry-btn.loading {\n pointer-events: none;\n opacity: 0.7;\n }\n \n /* 椤堕儴鏇存柊鎻愰啋鏉?*/\n .update-banner {\n position: sticky;\n top: 0;\n left: 0;\n right: 0;\n background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);\n color: #fff;\n padding: 8px 12px;\n font-size: 12px;\n display: none;\n align-items: center;\n justify-content: center;\n gap: 8px;\n z-index: 1000;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n }\n .update-banner.show {\n display: flex;\n }\n .update-banner .update-icon {\n font-size: 14px;\n }\n .update-banner .update-text {\n font-weight: 500;\n }\n .update-banner .update-version {\n background: rgba(255,255,255,0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 11px;\n }\n .update-banner .update-close {\n margin-left: auto;\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n font-size: 16px;\n padding: 0 4px;\n opacity: 0.8;\n }\n .update-banner .update-close:hover {\n opacity: 1;\n }\n \n </style>\n</head>\n<body>\n <!-- 椤堕儴鏇存柊鎻愰啋鏉?-->\n <div class=\"update-banner\" id=\"updateBanner\">\n <span class=\"update-icon\">馃殌</span>\n <span class=\"update-text\">鍙戠幇鏂扮増鏈?/span>\n <span class=\"update-version\" id=\"updateBannerVersion\">initOut.0</span>\n <button class=\"update-close\" id=\"updateBannerClose\" title=\"鍏抽棴\">脳</button>\n </div>\n \n <!-- 绠$悊鍛樻潈闄愭彁绀哄脊绐?-->\n <div class=\"modal-overlay\" id=\"adminModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">馃攼</div>\n <div class=\"modal-title\">闇€瑕佺鐞嗗憳鏉冮檺</div>\n <div class=\"modal-message\">\n 璇峰叧闂?Cursor锛屽彸閿偣鍑诲浘鏍?br>\n 閫夋嫨 <span class=\"highlight\">浠ョ鐞嗗憳韬唤杩愯</span>\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"adminModalClose\">鎴戠煡閬撲簡</button>\n </div>\n </div>\n </div>\n \n <!-- 閲嶇疆鏈哄櫒鐮佹潈闄愭彁绀哄脊绐?-->\n <div class=\"modal-overlay\" id=\"resetPermissionModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">馃攼</div>\n <div class=\"modal-title\">闇€瑕佺鐞嗗憳鏉冮檺</div>\n <div class=\"modal-message\" style=\"text-align: left; line-height: 1.8;\">\n 閲嶇疆鏈哄櫒鐮侀渶瑕佺鐞嗗憳鏉冮檺鎵嶈兘瀹屾暣鎵ц銆?br><br>\n 璇锋寜浠ヤ笅姝ラ鎿嶄綔锛?br>\n <span style=\"color: #fbbf24;\">1.</span> 瀹屽叏鍏抽棴 Cursor<br>\n <span style=\"color: #fbbf24;\">2.</span> 鍙抽敭鐐瑰嚮 Cursor 鍥炬爣<br>\n <span style=\"color: #fbbf24;\">3.</span> 閫夋嫨 <span class=\"highlight\">浠ョ鐞嗗憳韬唤杩愯</span><br>\n <span style=\"color: #fbbf24;\">4.</span> 鍐嶆鐐瑰嚮閲嶇疆鏈哄櫒鐮乗n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"resetPermissionClose\">鎴戠煡閬撲簡</button>\n </div>\n </div>\n </div>\n \n <!-- 閲嶅惎鎻愮ず寮圭獥 -->\n <div class=\"modal-overlay\" id=\"restartModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon success\">鉁?/div>\n <div class=\"modal-title\" id=\"restartModalTitle\">鎿嶄綔鎴愬姛</div>\n <div class=\"modal-message\">\n 闇€瑕侀噸鍚?Cursor 鎵嶈兘鐢熸晥\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"restartNowBtn\">绔嬪嵆閲嶅惎</button>\n <button class=\"modal-btn secondary\" id=\"restartLaterBtn\">绋嶅悗</button>\n </div>\n </div>\n </div>\n \n <!-- 婵€娲荤爜杩囨湡寮圭獥 -->\n <div class=\"modal-overlay\" id=\"expiredModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon\" style=\"background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\">鈴?/div>\n <div class=\"modal-title\">婵€娲荤爜宸茶繃鏈?/div>\n <div class=\"modal-message\">\n 鎮ㄧ殑婵€娲荤爜宸茶繃鏈燂紝璇风画璐瑰悗缁х画浣跨敤\n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn single\" id=\"expiredModalClose\">鎴戠煡閬撲簡</button>\n </div>\n </div>\n </div>\n \n <!-- 娓呯悊鐜纭寮圭獥 -->\n <div class=\"modal-overlay\" id=\"cleanEnvModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">鈿狅笍</div>\n <div class=\"modal-title\">娓呯悊 Cursor 鐜</div>\n <div class=\"modal-message\">\n 姝ゆ搷浣滀細鍒犻櫎鎵€鏈夐厤缃拰鐧诲綍淇℃伅<br>纭畾瑕佺户缁悧锛焅n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"cleanEnvConfirmBtn\">纭畾娓呯悊</button>\n <button class=\"modal-btn secondary\" id=\"cleanEnvCancelBtn\">鍙栨秷</button>\n </div>\n </div>\n </div>\n \n <!-- 鎹㈠彿纭寮圭獥 -->\n <div class=\"modal-overlay\" id=\"switchConfirmModal\">\n <div class=\"modal-content\">\n <div class=\"modal-icon warning\">馃挵</div>\n <div class=\"modal-title\">璐﹀彿鏈娇鐢ㄥ畬</div>\n <div class=\"modal-message\">\n 褰撳墠璐﹀彿 <span id=\"switchConfirmEmail\" style=\"color:#4caf50;\"></span><br>\n 宸茬敤棰濆害: <span id=\"switchConfirmCost\" style=\"color:#ff9800;font-weight:bold;\">$0.00</span> (涓嶈冻 $10)<br><br>\n 纭畾瑕佹崲鍙峰悧锛焅n </div>\n <div class=\"modal-buttons\">\n <button class=\"modal-btn primary\" id=\"switchConfirmBtn\">纭鎹㈠彿</button>\n <button class=\"modal-btn secondary\" id=\"switchCancelBtn\">鍙栨秷</button>\n </div>\n </div>\n </div>\n \n <!-- 绂荤嚎鐘舵€佹彁绀?-->\n <div class=\"offline-banner\" id=\"offlineBanner\">\n <span class=\"offline-icon\">馃摗</span>\n <div class=\"offline-text\">\n <div class=\"offline-title\">缃戠粶杩炴帴澶辫触</div>\n <div class=\"offline-desc\">璇锋鏌ョ綉缁滃悗閲嶈瘯</div>\n </div>\n <button class=\"retry-btn\" id=\"retryConnectBtn\">閲嶈瘯</button>\n </div>\n \n <!-- 杞欢鎺堟潈 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">馃攼</span>\n <span>杞欢鎺堟潈</span>\n <span class=\"status-badge\" id=\"authStatus\">鏈巿鏉?/span>\n </div>\n \n <div class=\"input-group\">\n <input type=\"text\" id=\"keyInput\" placeholder=\"璇疯緭鍏DK婵€娲荤爜\">\n <button class=\"btn btn-primary\" id=\"activateBtn\"><span class=\"btn-text\">婵€娲?/span></button>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">婵€娲荤爜</span>\n <span class=\"info-value key-display\" id=\"keyDisplay\" title=\"鐐瑰嚮澶嶅埗\">灏氭湭婵€娲?/span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">鍒版湡鏃堕棿</span>\n <span class=\"info-value\" id=\"expireDate\">灏氭湭婵€娲?/span>\n </div>\n </div>\n \n <!-- 璐﹀彿鏁版嵁 (宸查殣钘? -->\n <div class=\"section\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">馃懁</span>\n <span>璐﹀彿鏁版嵁</span>\n <span class=\"status-badge\" id=\"accountStatus\">鏈縺娲?/span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">CI绉垎浣欓</span>\n <span class=\"info-value\">0 <button style=\"background:none;border:none;color:#007acc;cursor:pointer;\">馃攧</button></span>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"switchBtn\" disabled>鎹㈠彿</button>\n <button class=\"btn btn-blue btn-block\" id=\"resetBtn\">閲嶇疆鏈哄櫒鐮?/button>\n <button class=\"btn btn-blue btn-block\" id=\"disableUpdateBtn\">绂佺敤鑷姩鏇存柊</button>\n <button class=\"btn btn-blue btn-block\" id=\"cleanEnvBtn\">娓呯悊Cursor鐜</button>\n <button class=\"btn btn-red btn-block\" id=\"disableBtn\">鍋滅敤鎻掍欢</button>\n </div>\n \n <!-- 鏃犳劅鎹㈠彿 -->\n <div class=\"section\">\n <div class=\"section-title\">\n <span class=\"icon\">鈿?/span>\n <span>鏃犳劅鎹㈠彿</span>\n <span class=\"status-badge\" id=\"seamlessStatus\">鏈惎鐢?/span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">绉垎</span>\n <span class=\"info-value\" id=\"seamlessSwitchRemaining\">0</span>\n </div>\n \n <div class=\"info-row\">\n <span class=\"info-label\">褰撳墠璐﹀彿</span>\n <span class=\"info-value\" style=\"font-size:11px;\" id=\"seamlessCurrentAccount\">鏈垎閰?/span>\n </div>\n \n <div class=\"switch-container\" style=\"margin: 12px 0;\">\n <span>鍏嶉瓟娉曟ā寮?/span>\n <span class=\"pro-badge\">PRO</span>\n <span style=\"margin-left: auto; color: #888; font-size: 11px;\"></span>\n <label class=\"switch\">\n <input type=\"checkbox\" id=\"seamlessProxySwitch\">\n <span class=\"slider\"></span>\n </label>\n </div>\n \n <button class=\"btn btn-purple btn-block\" id=\"enableSeamlessBtn\" disabled><span class=\"btn-text\">鍚敤鏃犳劅鎹㈠彿</span></button>\n <button class=\"btn btn-red btn-block\" id=\"seamlessResetMachineBtn\" style=\"display:none;\"><span class=\"btn-text\">閲嶇疆鏈哄櫒鐮?/span></button>\n <button class=\"btn btn-red btn-block\" id=\"disableSeamlessBtn\" style=\"display:none;\"><span class=\"btn-text\">绂佺敤鏃犳劅鎹㈠彿</span></button>\n <button class=\"btn btn-blue btn-block\" id=\"manualSwitchBtn\" style=\"display:none;\" disabled><span class=\"btn-text\">涓€閿崲鍙?鎵?绉垎)</span></button>\n </div>\n \n <!-- 璐﹀彿鐢ㄩ噺 -->\n <div class=\"section\" id=\"usageSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\">馃搳</span>\n <span>璐﹀彿鐢ㄩ噺</span>\n <button class=\"btn\" style=\"margin-left:auto;padding:4px 8px;font-size:11px;background:#3c3c3c;\" id=\"refreshUsageBtn\">馃攧</button>\n </div>\n \n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">浼氬憳绫诲瀷</span>\n <span class=\"info-value\" id=\"usageMemberType\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">璇曠敤鍓╀綑</span>\n <span class=\"info-value\" id=\"usageTrialDays\">-</span>\n </div>\n </div>\n <div class=\"usage-row\">\n <div class=\"usage-item\">\n <span class=\"info-label\">璇锋眰娆℃暟</span>\n <span class=\"info-value\" id=\"usageRequestCount\">-</span>\n </div>\n <div class=\"usage-item\">\n <span class=\"info-label\">宸茬敤棰濆害</span>\n <span class=\"info-value\" id=\"usageCostUSD\">-</span>\n </div>\n </div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:center;\" id=\"usageUpdateTime\">-</p>\n </div>\n \n <!-- 鍏憡 -->\n <div class=\"section\" id=\"announcementSection\" style=\"display:none;\">\n <div class=\"section-title\">\n <span class=\"icon\" id=\"announcementIcon\">馃摙</span>\n <span>鍏憡</span>\n <span class=\"announcement-badge\" id=\"announcementBadge\">info</span>\n </div>\n <div class=\"announcement-title\" id=\"announcementTitle\"></div>\n <div class=\"announcement-content\" id=\"announcementContent\"></div>\n <p style=\"font-size:10px;color:#666;margin-top:8px;text-align:right;\" id=\"announcementTime\"></p>\n </div>\n \n <!-- 鐗堟湰淇℃伅 -->\n <div class=\"section\" id=\"versionSection\">\n <div class=\"section-title\">\n <span class=\"icon\">馃摝</span>\n <span>鐗堟湰淇℃伅</span>\n <span class=\"status-badge\" id=\"versionStatus\" style=\"display:none;\">鏈夋洿鏂?/span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">褰撳墠鐗堟湰</span>\n <span class=\"info-value\" id=\"currentVersion\">-</span>\n </div>\n <div class=\"info-row\" id=\"latestVersionRow\" style=\"display:none;\">\n <span class=\"info-label\">鏈€鏂扮増鏈?/span>\n <span class=\"info-value\" id=\"latestVersion\" style=\"color:#4caf50;\">-</span>\n </div>\n <p id=\"updateHint\" style=\"font-size:11px;color:#ff9800;margin-top:8px;display:none;\">\n 鈿狅笍 鍙戠幇鏂扮増鏈紝璇锋洿鏂版彃浠朵互鑾峰彇鏈€鏂板姛鑳絓n </p>\n </div>\n \n <!-- 椤佃剼 -->\n <div class=\"footer\">\n <div class=\"footer-row\">\n <div class=\"auto-start\">\n <span>鑷姩鍚姩</span>\n <label class=\"switch switch-sm\">\n <input type=\"checkbox\" id=\"autoStartSwitch\" checked>\n <span class=\"slider\"></span>\n </label>\n </div>\n <div class=\"cursor-version\">\n <span>Cursor</span>\n <span class=\"version-num\" id=\"cursorVersion\">0.0.0</span>\n </div>\n </div>\n <div class=\"footer-row\" style=\"margin-top: 8px;\">\n <div style=\"font-size: 10px; color: #666; word-break: break-all;\">\n <span>璺緞: </span>\n <span id=\"cursorPath\" style=\"color: #888;\">鑾峰彇涓?..</span>\n </div>\n </div>\n </div>\n \n <!-- Toast 閫氱煡 -->\n <div class=\"toast-container\" id=\"toastContainer\">\n <div class=\"toast\" id=\"toast\">\n <span class=\"toast-icon\" id=\"toastIcon\">鉁?/span>\n <span class=\"toast-message\" id=\"toastMessage\"></span>\n </div>\n </div>\n \n <script nonce=\"" + newContent + "\">\n const vscode = acquireVsCodeApi();\n \n // 鍏冪礌寮曠敤\n const keyInput = document.getElementById('keyInput');\n const activateBtn = document.getElementById('activateBtn');\n const switchBtn = document.getElementById('switchBtn');\n const resetBtn = document.getElementById('resetBtn');\n const disableUpdateBtn = document.getElementById('disableUpdateBtn');\n const cleanEnvBtn = document.getElementById('cleanEnvBtn');\n const disableBtn = document.getElementById('disableBtn');\n const authStatus = document.getElementById('authStatus');\n const accountStatus = document.getElementById('accountStatus');\n const keyDisplay = document.getElementById('keyDisplay');\n const switchCount = document.getElementById('switchCount');\n const expireDate = document.getElementById('expireDate');\n const cursorVersion = document.getElementById('cursorVersion');\n const cursorPath = document.getElementById('cursorPath');\n \n // 绂荤嚎鐘舵€佸厓绱燶n const offlineBanner = document.getElementById('offlineBanner');\n const retryConnectBtn = document.getElementById('retryConnectBtn');\n \n // 鏃犳劅鎹㈠彿鍏冪礌\n const seamlessStatus = document.getElementById('seamlessStatus');\n const seamlessProxySwitch = document.getElementById('seamlessProxySwitch');\n const enableSeamlessBtn = document.getElementById('enableSeamlessBtn');\n const disableSeamlessBtn = document.getElementById('disableSeamlessBtn');\n const manualSwitchBtn = document.getElementById('manualSwitchBtn');\n const seamlessResetMachineBtn = document.getElementById('seamlessResetMachineBtn');\n const seamlessSwitchRemaining = document.getElementById('seamlessSwitchRemaining');\n const seamlessCurrentAccount = document.getElementById('seamlessCurrentAccount');\n \n // 鐢ㄩ噺鏄剧ず鍏冪礌\n const usageSection = document.getElementById('usageSection');\n const refreshUsageBtn = document.getElementById('refreshUsageBtn');\n const usageMemberType = document.getElementById('usageMemberType');\n const usageTrialDays = document.getElementById('usageTrialDays');\n const usageRequestCount = document.getElementById('usageRequestCount');\n const usageCostUSD = document.getElementById('usageCostUSD');\n const usageUpdateTime = document.getElementById('usageUpdateTime');\n \n // 鍏憡鍏冪礌\n const announcementSection = document.getElementById('announcementSection');\n const announcementIcon = document.getElementById('announcementIcon');\n const announcementBadge = document.getElementById('announcementBadge');\n const announcementTitle = document.getElementById('announcementTitle');\n const announcementContent = document.getElementById('announcementContent');\n const announcementTime = document.getElementById('announcementTime');\n \n // 鐗堟湰鍏冪礌\n const versionSection = document.getElementById('versionSection');\n const versionStatus = document.getElementById('versionStatus');\n const currentVersionEl = document.getElementById('currentVersion');\n const latestVersionEl = document.getElementById('latestVersion');\n const latestVersionRow = document.getElementById('latestVersionRow');\n const updateHint = document.getElementById('updateHint');\n \n // 椤堕儴鏇存柊鎻愰啋鏉n const updateBanner = document.getElementById('updateBanner');\n const updateBannerVersion = document.getElementById('updateBannerVersion');\n const updateBannerClose = document.getElementById('updateBannerClose');\n \n // Toast 鍏冪礌\n const toast = document.getElementById('toast');\n const toastIcon = document.getElementById('toastIcon');\n const toastMessage = document.getElementById('toastMessage');\n let toastTimer = null;\n \n // 鏄剧ず Toast 閫氱煡\n function showToast(message, icon = '鉁?, duration = 10000) {\n // 娓呴櫎涔嬪墠鐨勫畾鏃跺櫒\n if (toastTimer) {\n clearTimeout(toastTimer);\n }\n \n toastIcon.textContent = icon;\n toastMessage.textContent = message;\n toast.classList.add('show');\n \n // 璁剧疆鑷姩闅愯棌\n toastTimer = setTimeout(() => {\n toast.classList.remove('show');\n }, duration);\n }\n \n // 绂佺敤鎹㈠彿鎸夐挳骞舵樉绀哄€掕鏃禱n let switchBtnCountdownTimer = null;\n const originalSwitchBtnText = '涓€閿崲鍙?鎵?绉垎)';\n \n function disableSwitchBtnWithCountdown(seconds) {\n // 娓呴櫎涔嬪墠鐨勫畾鏃跺櫒\n if (switchBtnCountdownTimer) {\n clearInterval(switchBtnCountdownTimer);\n }\n \n let remaining = seconds;\n manualSwitchBtn.disabled = true;\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '绉掑悗鍙敤';\n \n switchBtnCountdownTimer = setInterval(() => {\n remaining--;\n if (remaining <= 0) {\n clearInterval(switchBtnCountdownTimer);\n switchBtnCountdownTimer = null;\n manualSwitchBtn.disabled = false;\n manualSwitchBtn.querySelector('.btn-text').textContent = originalSwitchBtnText;\n } else {\n manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '绉掑悗鍙敤';\n }\n }, 1000);\n }\n \n // 寮圭獥鍏冪礌\n const adminModal = document.getElementById('adminModal');\n const adminModalClose = document.getElementById('adminModalClose');\n const resetPermissionModal = document.getElementById('resetPermissionModal');\n const resetPermissionClose = document.getElementById('resetPermissionClose');\n const restartModal = document.getElementById('restartModal');\n const restartModalTitle = document.getElementById('restartModalTitle');\n const restartNowBtn = document.getElementById('restartNowBtn');\n const restartLaterBtn = document.getElementById('restartLaterBtn');\n const expiredModal = document.getElementById('expiredModal');\n const expiredModalClose = document.getElementById('expiredModalClose');\n const cleanEnvModal = document.getElementById('cleanEnvModal');\n const cleanEnvConfirmBtn = document.getElementById('cleanEnvConfirmBtn');\n const cleanEnvCancelBtn = document.getElementById('cleanEnvCancelBtn');\n \n // 鎹㈠彿纭寮圭獥鍏冪礌\n const switchConfirmModal = document.getElementById('switchConfirmModal');\n const switchConfirmEmail = document.getElementById('switchConfirmEmail');\n const switchConfirmCost = document.getElementById('switchConfirmCost');\n const switchConfirmBtn = document.getElementById('switchConfirmBtn');\n const switchCancelBtn = document.getElementById('switchCancelBtn');\n \n // 鏄剧ず绠$悊鍛樻潈闄愬脊绐梊n function showAdminModal() {\n adminModal.classList.add('show');\n }\n \n // 鏄剧ず閲嶇疆鏈哄櫒鐮佹潈闄愭彁绀哄脊绐梊n function showAdminPermissionModal() {\n resetPermissionModal.classList.add('show');\n }\n \n // 閲嶇疆鏈哄櫒鐮佹潈闄愬脊绐?- 鍏抽棴鎸夐挳\n resetPermissionClose.addEventListener('click', () => {\n resetPermissionModal.classList.remove('show');\n });\n \n // 鐐瑰嚮閬僵鍏抽棴鏉冮檺鎻愮ず寮圭獥\n resetPermissionModal.addEventListener('click', (e) => {\n if (e.target === resetPermissionModal) {\n resetPermissionModal.classList.remove('show');\n }\n });\n \n // 鏄剧ず閲嶅惎鎻愮ず寮圭獥\n let restartModalAction = 'reload'; // 'reload' 鎴?'close'\n \n function showRestartModal(title, action = 'reload') {\n restartModalTitle.textContent = title || '鎿嶄綔鎴愬姛';\n restartModalAction = action;\n // 鏍规嵁鎿嶄綔绫诲瀷鏇存柊鎸夐挳鏂囧瓧\n restartNowBtn.textContent = action === 'close' ? '绔嬪嵆鍏抽棴 Cursor' : '绔嬪嵆閲嶅惎';\n restartModal.classList.add('show');\n }\n \n // 鏄剧ず杩囨湡寮圭獥\n function showExpiredModal() {\n expiredModal.classList.add('show');\n }\n \n // 鍏抽棴绠$悊鍛樺脊绐梊n adminModalClose.addEventListener('click', () => {\n adminModal.classList.remove('show');\n });\n \n // 鐐瑰嚮閬僵鍏抽棴绠$悊鍛樺脊绐梊n adminModal.addEventListener('click', (e) => {\n if (e.target === adminModal) {\n adminModal.classList.remove('show');\n }\n });\n \n // 绔嬪嵆閲嶅惎/鍏抽棴鎸夐挳\n restartNowBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n if (restartModalAction === 'close') {\n // 瀹屽叏鍏抽棴 Cursor\n vscode.postMessage({ type: 'closeCursor' });\n } else {\n // 閲嶆柊鍔犺浇绐楀彛\n vscode.postMessage({ type: 'reloadWindow' });\n }\n });\n \n // 绋嶅悗鎵嬪姩鎸夐挳\n restartLaterBtn.addEventListener('click', () => {\n restartModal.classList.remove('show');\n });\n \n // 鐐瑰嚮閬僵鍏抽棴閲嶅惎寮圭獥\n restartModal.addEventListener('click', (e) => {\n if (e.target === restartModal) {\n restartModal.classList.remove('show');\n }\n });\n \n // 鍏抽棴杩囨湡寮圭獥\n expiredModalClose.addEventListener('click', () => {\n expiredModal.classList.remove('show');\n });\n \n // 鐐瑰嚮閬僵鍏抽棴杩囨湡寮圭獥\n expiredModal.addEventListener('click', (e) => {\n if (e.target === expiredModal) {\n expiredModal.classList.remove('show');\n }\n });\n \n // 褰撳墠璐﹀彿閭锛堢敤浜庢煡璇㈢敤閲忥級\n let currentAccountEmail = '';\n let usageRefreshInterval = null;\n // 瀛樺偍瀹屾暣婵€娲荤爜锛堢敤浜庡鍒讹級\n let fullActivationKey = '';\n // 褰撳墠鍓╀綑鎹㈠彿娆℃暟\n let currentSwitchRemaining = 0;\n // 褰撳墠鍒版湡鏃堕棿\n let currentExpireDate = '';\n \n // 妫€鏌ュ崱瀵嗘槸鍚﹀凡杩囨湡\n function isKeyExpired() {\n if (!currentExpireDate) return true;\n try {\n const expireTime = new Date(currentExpireDate).getTime();\n return Date.now() > expireTime;\n } catch {\n return true;\n }\n }\n \n // 鏍煎紡鍖栧埌鏈熸椂闂翠负鍖椾含鏃堕棿\n function formatExpireDate(dateStr) {\n if (!dateStr) return '';\n try {\n // 鍚庣杩斿洖鐨勬椂闂存病鏈夋椂鍖烘爣璇嗭紝鍋囪鏄?UTC 鏃堕棿\n // 灏嗙┖鏍兼浛鎹负T锛屽苟娣诲姞Z琛ㄧずUTC\n let utcStr = dateStr;\n if (!dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {\n utcStr = dateStr.replace(' ', 'T') + 'Z';\n }\n const date = new Date(utcStr);\n \n // 浣跨敤涓浗鏃跺尯鏍煎紡鍖栵紙UTC+8锛塡n return date.toLocaleString('zh-CN', {\n timeZone: 'Asia/Shanghai',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false\n });\n } catch {\n return dateStr; // 鏍煎紡鍖栧け璐ヨ繑鍥炲師濮嬪€糪n }\n }\n \n // 闅愯棌婵€娲荤爜鍚庡嚑浣峔n function maskKey(key) {\n if (!key || key.length <= 8) return key;\n return key.substring(0, key.length - 4) + '****';\n }\n \n // 鐐瑰嚮婵€娲荤爜澶嶅埗\n keyDisplay.addEventListener('click', () => {\n if (!fullActivationKey) return;\n navigator.clipboard.writeText(fullActivationKey).then(() => {\n keyDisplay.classList.add('copied');\n const originalText = keyDisplay.textContent;\n keyDisplay.textContent = '宸插鍒?';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n }).catch(() => {\n // 闄嶇骇鏂规\n const textarea = document.createElement('textarea');\n textarea.value = fullActivationKey;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n keyDisplay.classList.add('copied');\n keyDisplay.textContent = '宸插鍒?';\n setTimeout(() => {\n keyDisplay.textContent = maskKey(fullActivationKey);\n keyDisplay.classList.remove('copied');\n }, 1000);\n });\n });\n \n // Loading 鐘舵€佹帶鍒禱n function setButtonLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n btn.disabled = true;\n } else {\n btn.classList.remove('loading');\n // 娉ㄦ剰锛氭煇浜涙寜閽彲鑳介渶瑕佷繚鎸佺鐢ㄧ姸鎬侊紝鐢辫皟鐢ㄦ柟鎺у埗\n }\n }\n \n function setRefreshLoading(btn, loading) {\n if (loading) {\n btn.classList.add('loading');\n } else {\n btn.classList.remove('loading');\n }\n }\n \n // 鑾峰彇鍒濆鐘舵€乗n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getSeamlessStatus' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n vscode.postMessage({ type: 'getProxyStatus' });\n vscode.postMessage({ type: 'getAnnouncement' });\n vscode.postMessage({ type: 'checkVersion' });\n vscode.postMessage({ type: 'getCursorRunningPath' });\n \n // 婵€娲绘寜閽甛n activateBtn.addEventListener('click', () => {\n const key = keyInput.value.trim();\n if (!key) {\n return;\n }\n setButtonLoading(activateBtn, true);\n vscode.postMessage({ type: 'activate', key });\n });\n \n // 鎹㈠彿鎸夐挳\n switchBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'switch' });\n });\n \n // 閲嶇疆鏈哄櫒鐮佹寜閽甛n resetBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 绂佺敤鑷姩鏇存柊鎸夐挳\n disableUpdateBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disableUpdate' });\n });\n \n // 娓呯悊Cursor鐜鎸夐挳 - 鏄剧ず纭寮圭獥\n cleanEnvBtn.addEventListener('click', () => {\n cleanEnvModal.classList.add('show');\n });\n \n // 纭娓呯悊\n cleanEnvConfirmBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n vscode.postMessage({ type: 'cleanEnv' });\n });\n \n // 鍙栨秷娓呯悊\n cleanEnvCancelBtn.addEventListener('click', () => {\n cleanEnvModal.classList.remove('show');\n });\n \n // 鐐瑰嚮閬僵鍏抽棴娓呯悊寮圭獥\n cleanEnvModal.addEventListener('click', (e) => {\n if (e.target === cleanEnvModal) {\n cleanEnvModal.classList.remove('show');\n }\n });\n \n // 鍋滅敤鎸夐挳\n disableBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'disable' });\n });\n \n // 鍏抽棴鏇存柊鎻愰啋鏉n updateBannerClose.addEventListener('click', () => {\n updateBanner.classList.remove('show');\n });\n \n // 鍏嶉瓟娉曞紑鍏砛n seamlessProxySwitch.addEventListener('change', (e) => {\n const wantEnabled = e.target.checked;\n \n // 濡傛灉瑕佸紑鍚厤榄旀硶锛屾鏌ュ崱瀵嗘槸鍚﹁繃鏈燂紙鍙娌¤繃鏈熷氨鍙互鐢紝涓嶇鎹㈠彿娆℃暟锛塡n if (wantEnabled && isKeyExpired()) {\n e.target.checked = false;\n showToast('鎺堟潈鐮佸凡杩囨湡锛屾棤娉曞紑鍚厤榄旀硶', '鈿狅笍', 3000);\n return;\n }\n \n vscode.postMessage({ \n type: 'toggleProxy', \n enabled: wantEnabled,\n url: ''\n });\n });\n \n // 鏃犳劅鎹㈠彿 - 鍚敤鎸夐挳\n enableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(enableSeamlessBtn, true);\n vscode.postMessage({ type: 'injectSeamless' });\n });\n \n // 鏃犳劅鎹㈠彿 - 绂佺敤鎸夐挳\n disableSeamlessBtn.addEventListener('click', () => {\n setButtonLoading(disableSeamlessBtn, true);\n vscode.postMessage({ type: 'restoreSeamless' });\n });\n \n // 鏃犳劅鎹㈠彿 - 鎵嬪姩鎹㈠彿鎸夐挳锛堝厛妫€鏌ョ敤閲忥級\n manualSwitchBtn.addEventListener('click', () => {\n setButtonLoading(manualSwitchBtn, true);\n // 浼犻€掑綋鍓嶆樉绀虹殑璐﹀彿閭\n vscode.postMessage({ type: 'checkUsageBeforeSwitch', email: currentAccountEmail });\n });\n \n // 鎹㈠彿纭寮圭獥 - 纭鎸夐挳\n switchConfirmBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, true);\n vscode.postMessage({ type: 'confirmSwitch' });\n });\n \n // 鎹㈠彿纭寮圭獥 - 鍙栨秷鎸夐挳\n switchCancelBtn.addEventListener('click', () => {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n });\n \n // 鎹㈠彿纭寮圭獥 - 鐐瑰嚮閬僵鍏抽棴\n switchConfirmModal.addEventListener('click', (e) => {\n if (e.target === switchConfirmModal) {\n switchConfirmModal.classList.remove('show');\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n }\n });\n \n // 鏃犳劅鎹㈠彿鍖哄煙 - 閲嶇疆鏈哄櫒鐮佹寜閽甛n seamlessResetMachineBtn.addEventListener('click', () => {\n vscode.postMessage({ type: 'resetMachineId' });\n });\n \n // 鍒锋柊鐢ㄩ噺鎸夐挳\n refreshUsageBtn.addEventListener('click', () => {\n if (currentAccountEmail) {\n setRefreshLoading(refreshUsageBtn, true);\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n });\n \n // 鍒锋柊鐢ㄩ噺鍑芥暟\n function refreshUsage() {\n if (currentAccountEmail) {\n vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });\n }\n }\n \n // 鍚姩鐢ㄩ噺瀹氭椂鍒锋柊 (姣忓垎閽熶竴娆?\n function startUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n }\n // 绔嬪嵆鍒锋柊涓€娆n refreshUsage();\n // 姣?0绉掑埛鏂颁竴娆n usageRefreshInterval = setInterval(refreshUsage, 60000);\n }\n \n // 鍋滄鐢ㄩ噺鍒锋柊\n function stopUsageRefresh() {\n if (usageRefreshInterval) {\n clearInterval(usageRefreshInterval);\n usageRefreshInterval = null;\n }\n }\n \n // 鏇存柊鐢ㄩ噺鏄剧ず\n function updateUsageDisplay(data) {\n if (!data) return;\n \n const subscription = data.subscription || {};\n const usage = data.usage || {};\n \n // 浼氬憳绫诲瀷\n const memberTypeMap = {\n 'free_trial': '鍏嶈垂璇曠敤',\n 'pro': 'Pro浼氬憳',\n 'free': '鍏嶈垂鐗?,\n 'business': '鍟嗕笟鐗?\n };\n usageMemberType.textContent = memberTypeMap[subscription.membershipType] || subscription.membershipType || '-';\n \n // 璇曠敤鍓╀綑澶╂暟\n if (subscription.daysRemainingOnTrial !== undefined && subscription.daysRemainingOnTrial !== null) {\n usageTrialDays.textContent = subscription.daysRemainingOnTrial + ' 澶?;\n usageTrialDays.style.color = subscription.daysRemainingOnTrial <= 3 ? '#f87171' : '#4ade80';\n } else {\n usageTrialDays.textContent = '-';\n usageTrialDays.style.color = '#fff';\n }\n \n // 璇锋眰娆℃暟\n usageRequestCount.textContent = (usage.totalUsageCount || 0) + ' 娆?;\n \n // 宸茬敤棰濆害\n const costUSD = usage.totalCostUSD || 0;\n usageCostUSD.textContent = '$' + costUSD.toFixed(2);\n usageCostUSD.style.color = costUSD > 5 ? '#f87171' : (costUSD > 2 ? '#fbbf24' : '#4ade80');\n \n // 鏇存柊鏃堕棿\n usageUpdateTime.textContent = '鏇存柊浜?' + new Date().toLocaleTimeString();\n }\n \n // 瑙f瀽鍏憡鍐呭涓殑閾炬帴 {鏂囧瓧URL}\n function parseAnnouncementContent(content) {\n if (!content) return '';\n \n // 杞箟 HTML 鐗规畩瀛楃\n let escaped = content\n .replace(/&/g, '&')\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 // 璁剧疆鏍囬鍜屽唴瀹癸紙瑙f瀽閾炬帴锛塡n announcementTitle.textContent = data.title || '';\n announcementContent.innerHTML = parseAnnouncementContent(data.content || '');\n \n // 璁剧疆鏃堕棿\n if (data.created_at) {\n const date = new Date(data.created_at);\n announcementTime.textContent = date.toLocaleDateString('zh-CN', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit'\n });\n } else {\n announcementTime.textContent = '';\n }\n }\n \n // 澶勭悊鏉ヨ嚜鎵╁睍鐨勬秷鎭痋n window.addEventListener('message', event => {\n const message = event.data;\n \n switch (message.type) {\n case 'state':\n updateUI(message);\n break;\n case 'activated':\n setButtonLoading(activateBtn, false);\n activateBtn.disabled = false;\n if (message.success) {\n // 璋冭瘯鏃ュ織\n console.log('[CursorPro] 鍓嶇鏀跺埌婵€娲绘垚鍔熸秷鎭?', message);\n \n authStatus.textContent = '宸叉巿鏉?;\n authStatus.className = 'status-badge active';\n accountStatus.textContent = '宸叉縺娲?;\n accountStatus.className = 'status-badge active';\n switchBtn.disabled = false;\n // 鏇存柊婵€娲荤爜鏄剧ず锛堜娇鐢ㄥ悗绔繑鍥炵殑 key锛塡n fullActivationKey = message.key || keyInput.value;\n keyDisplay.textContent = maskKey(fullActivationKey);\n // 鏇存柊鍒版湡鏃堕棿\n console.log('[CursorPro] 鏇存柊鍒版湡鏃堕棿:', message.expireDate);\n currentExpireDate = message.expireDate || '';\n expireDate.textContent = formatExpireDate(currentExpireDate) || '鏈煡';\n // 鏇存柊鎹㈠彿娆℃暟\n if (message.switchRemaining !== undefined) {\n currentSwitchRemaining = message.switchRemaining;\n switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n }\n // 娓呯┖杈撳叆妗哱n keyInput.value = '';\n showToast('鎺堟潈鐮佹縺娲绘垚鍔燂紒', '鉁?, 10000);\n } else {\n showToast(message.error || '婵€娲诲け璐?, '鉂?, 10000);\n }\n break;\n case 'switched':\n if (message.success) {\n switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n showToast('鎹㈠彿鎴愬姛: ' + (message.email || ''), '鉁?, 10000);\n } else {\n showToast(message.error || '鎹㈠彿澶辫触', '鉂?, 10000);\n }\n break;\n case 'reset':\n authStatus.textContent = '鏈巿鏉?;\n authStatus.className = 'status-badge inactive';\n accountStatus.textContent = '鏈縺娲?;\n accountStatus.className = 'status-badge inactive';\n switchBtn.disabled = true;\n keyInput.value = '';\n fullActivationKey = '';\n keyDisplay.textContent = '灏氭湭婵€娲?;\n expireDate.textContent = '灏氭湭婵€娲?;\n break;\n \n // 婵€娲荤爜鐘舵€佹鏌ョ粨鏋淺n case 'keyStatusChecked':\n if (message.valid) {\n // 婵€娲荤爜鏈夋晥锛屾洿鏂版樉绀篭n currentExpireDate = message.expireDate || '';\n currentSwitchRemaining = message.switchRemaining || 0;\n expireDate.textContent = formatExpireDate(currentExpireDate);\n switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);\n } else if (message.expired) {\n // 婵€娲荤爜宸茶繃鏈燂紝鏄剧ず鎻愮ず骞堕噸缃姸鎬乗n currentExpireDate = '';\n currentSwitchRemaining = 0;\n authStatus.textContent = '宸茶繃鏈?;\n authStatus.className = 'status-badge inactive';\n authStatus.style.background = '#6e3232';\n authStatus.style.color = '#ff6b6b';\n expireDate.textContent = '宸茶繃鏈?;\n expireDate.style.color = '#f87171';\n switchBtn.disabled = true;\n enableSeamlessBtn.disabled = true;\n // 濡傛灉鍏嶉瓟娉曞凡寮€鍚紝鑷姩鍏抽棴\n if (seamlessProxySwitch.checked) {\n seamlessProxySwitch.checked = false;\n vscode.postMessage({ type: 'toggleProxy', enabled: false, url: '' });\n }\n // 鏄剧ず杩囨湡寮圭獥\n showExpiredModal();\n }\n break;\n \n // 鐢ㄦ埛鎹㈠彿鐘舵€乗n case 'userSwitchStatus':\n const remaining = message.switchRemaining || 0;\n const canSwitch = remaining > 0;\n \n // 鏇存柊鍏ㄥ眬鍙橀噺\n currentSwitchRemaining = remaining;\n \n seamlessSwitchRemaining.textContent = remaining.toString();\n seamlessSwitchRemaining.style.color = canSwitch ? '#4ade80' : '#f87171';\n \n if (message.lockedAccount) {\n seamlessCurrentAccount.textContent = message.lockedAccount.email;\n \n // 璁剧疆褰撳墠璐﹀彿閭骞跺惎鍔ㄧ敤閲忓埛鏂癨n if (message.lockedAccount.email && message.lockedAccount.email !== currentAccountEmail) {\n currentAccountEmail = message.lockedAccount.email;\n usageSection.style.display = 'block';\n startUsageRefresh();\n }\n } else {\n seamlessCurrentAccount.textContent = '鏈垎閰?;\n \n // 娌℃湁閿佸畾璐﹀彿鏃堕殣钘忕敤閲忓尯鍩焅n currentAccountEmail = '';\n usageSection.style.display = 'none';\n stopUsageRefresh();\n }\n \n // 鏍规嵁鍓╀綑娆℃暟鎺у埗鎵嬪姩鎹㈠彿鎸夐挳鐘舵€乗n if (!canSwitch) {\n manualSwitchBtn.disabled = true;\n }\n // 鍚敤鏃犳劅鎹㈠彿鎸夐挳涓嶅彈绉垎闄愬埗锛屽彧鏈夎繃鏈熸墠绂佺敤\n enableSeamlessBtn.disabled = isKeyExpired();\n \n // 濡傛灉鏃犳劅鎹㈠彿宸插惎鐢紝鏄剧ず鎵嬪姩鎹㈠彿鎸夐挳鍜岄噸缃満鍣ㄧ爜鎸夐挳\n if (message.seamlessEnabled && canSwitch) {\n manualSwitchBtn.style.display = 'block';\n manualSwitchBtn.disabled = false;\n setButtonLoading(manualSwitchBtn, false);\n seamlessResetMachineBtn.style.display = 'block';\n }\n break;\n \n // 璐﹀彿鐢ㄩ噺\n case 'accountUsage':\n setRefreshLoading(refreshUsageBtn, false);\n if (message.success && message.data) {\n updateUsageDisplay(message.data);\n } else {\n usageUpdateTime.textContent = '鑾峰彇澶辫触: ' + (message.error || '鏈煡閿欒');\n usageUpdateTime.style.color = '#f87171';\n }\n break;\n \n // 鏃犳劅鎹㈠彿鐘舵€乗n case 'seamlessStatus':\n if (message.is_injected) {\n seamlessStatus.textContent = '宸插惎鐢?;\n seamlessStatus.className = 'status-badge active';\n enableSeamlessBtn.style.display = 'none';\n disableSeamlessBtn.style.display = 'block';\n disableSeamlessBtn.disabled = false;\n setButtonLoading(disableSeamlessBtn, false);\n manualSwitchBtn.style.display = 'block';\n manualSwitchBtn.disabled = false;\n setButtonLoading(manualSwitchBtn, false);\n seamlessResetMachineBtn.style.display = 'block';\n } else {\n seamlessStatus.textContent = '鏈惎鐢?;\n seamlessStatus.className = 'status-badge inactive';\n enableSeamlessBtn.style.display = 'block';\n setButtonLoading(enableSeamlessBtn, false);\n // 鍚敤鎸夐挳涓嶅彈绉垎闄愬埗锛屽彧鏈夎繃鏈熸墠绂佺敤\n enableSeamlessBtn.disabled = isKeyExpired();\n disableSeamlessBtn.style.display = 'none';\n manualSwitchBtn.style.display = 'none';\n seamlessResetMachineBtn.style.display = 'none';\n }\n break;\n \n case 'seamlessInjected':\n setButtonLoading(enableSeamlessBtn, false);\n enableSeamlessBtn.disabled = false;\n if (message.success) {\n seamlessStatus.textContent = '宸插惎鐢?;\n seamlessStatus.className = 'status-badge active';\n enableSeamlessBtn.style.display = 'none';\n disableSeamlessBtn.style.display = 'block';\n manualSwitchBtn.style.display = 'block';\n seamlessResetMachineBtn.style.display = 'block';\n // 鍒锋柊鐢ㄦ埛鐘舵€乗n vscode.postMessage({ type: 'getUserSwitchStatus' });\n // 鏄剧ず閲嶅惎鎻愮ず寮圭獥\n if (message.needRestart) {\n showRestartModal(message.message || '鏃犳劅鎹㈠彿宸插惎鐢?);\n }\n } else {\n // 濡傛灉鏄潈闄愰敊璇紝鏄剧ず鑷畾涔夊脊绐梊n if (message.needAdmin) {\n // Mac/Linux 鏉冮檺闂锛屾樉绀鸿缁嗘彁绀篭n var errorMsg = message.error || '娌℃湁鍐欏叆鏉冮檺';\n if (message.path) {\n errorMsg += '\\n璺緞: ' + message.path;\n }\n showToast(errorMsg, '馃攼', 15000);\n } else {\n // 鏄剧ず璇︾粏閿欒\n var detailMsg = message.error || '鍚敤澶辫触';\n if (message.details) {\n detailMsg += '\\n' + message.details;\n }\n showToast(detailMsg, '鉂?, 15000);\n }\n }\n break;\n \n case 'seamlessRestored':\n setButtonLoading(disableSeamlessBtn, false);\n disableSeamlessBtn.disabled = false;\n if (message.success) {\n seamlessStatus.textContent = '鏈惎鐢?;\n seamlessStatus.className = 'status-badge inactive';\n enableSeamlessBtn.style.display = 'block';\n disableSeamlessBtn.style.display = 'none';\n manualSwitchBtn.style.display = 'none';\n seamlessResetMachineBtn.style.display = 'none';\n // 鏄剧ず閲嶅惎鎻愮ず寮圭獥\n if (message.needRestart) {\n showRestartModal(message.message || '鏃犳劅鎹㈠彿宸茬鐢?);\n }\n } else {\n // 濡傛灉鏄潈闄愰敊璇紝鏄剧ず鑷畾涔夊脊绐梊n if (message.needAdmin) {\n showAdminModal();\n } else {\n showToast(message.error || '绂佺敤澶辫触', '鉂?, 10000);\n }\n }\n break;\n \n // 鐢ㄩ噺妫€鏌ョ粨鏋淺n case 'usageCheckResult':\n if (message.success) {\n if (message.needConfirm) {\n // 闇€瑕佺‘璁わ紝鏄剧ず寮圭獥锛堟寜閽繚鎸佸彲鐢ㄧ姸鎬侊紝绛夌敤鎴烽€夋嫨锛塡n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n switchConfirmEmail.textContent = message.email || '';\n switchConfirmCost.textContent = '$' + (message.costUSD || '0.00');\n switchConfirmModal.classList.add('show');\n } else {\n // 涓嶉渶瑕佺‘璁わ紝鐩存帴鎹㈠彿\n vscode.postMessage({ type: 'confirmSwitch' });\n }\n } else {\n setButtonLoading(manualSwitchBtn, false);\n manualSwitchBtn.disabled = false;\n showToast(message.error || '妫€鏌ュけ璐?, '鉂?, 5000);\n }\n break;\n \n case 'manualSeamlessSwitched':\n setButtonLoading(manualSwitchBtn, false);\n if (message.success) {\n seamlessSwitchRemaining.textContent = (message.switchRemaining || 0).toString();\n seamlessCurrentAccount.textContent = message.email || '鏈煡';\n // 鏄剧ず Toast 閫氱煡锛?0绉掑悗娑堝け\n showToast('宸插垏鎹㈠埌: ' + (message.email || '鏂拌处鍙?) + '锛岀害10绉掑唴鑷姩鐢熸晥', '鉁?, 10000);\n // 鍒锋柊鐘舵€乗n vscode.postMessage({ type: 'getUserSwitchStatus' });\n // 绂佺敤鎸夐挳10绉掞紝鏄剧ず鍊掕鏃禱n disableSwitchBtnWithCountdown(10);\n } else {\n manualSwitchBtn.disabled = false;\n showToast(message.error || '鎹㈠彿澶辫触', '鉂?, 5000);\n }\n break;\n \n case 'proxyStatus':\n // 璁剧疆鍏嶉瓟娉曞紑鍏崇姸鎬乗n seamlessProxySwitch.checked = message.enabled;\n break;\n \n // 鍏憡\n case 'announcement':\n if (message.success && message.data) {\n updateAnnouncementDisplay(message.data);\n } else {\n announcementSection.style.display = 'none';\n }\n break;\n \n // 鐗堟湰妫€鏌n case 'versionCheck':\n currentVersionEl.textContent = message.currentVersion || '-';\n if (message.success && message.hasUpdate) {\n // 鏈夋洿鏂癨n latestVersionEl.textContent = message.latestVersion;\n latestVersionRow.style.display = 'flex';\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#ff9800';\n updateHint.style.display = 'block';\n \n // 鏄剧ず椤堕儴鏇存柊鎻愰啋鏉n updateBannerVersion.textContent = 'v' + message.latestVersion;\n updateBanner.classList.add('show');\n } else if (message.success) {\n // 宸叉槸鏈€鏂扮増\n versionStatus.textContent = '鏈€鏂?;\n versionStatus.style.display = 'inline-block';\n versionStatus.style.background = '#4caf50';\n latestVersionRow.style.display = 'none';\n updateHint.style.display = 'none';\n updateBanner.classList.remove('show');\n }\n break;\n \n // Cursor 杩愯璺緞\n case 'cursorRunningPath':\n if (message.path) {\n const pathText = message.path + (message.packageExists ? ' 鉁? : ' 鉁?);\n cursorPath.textContent = pathText;\n cursorPath.style.color = message.packageExists ? '#4ade80' : '#f87171';\n // 鍚屾椂鏇存柊鐗堟湰鍙穃n if (message.cursorVersion) {\n cursorVersion.textContent = message.cursorVersion;\n }\n } else {\n cursorPath.textContent = '鏈壘鍒?;\n cursorPath.style.color = '#f87171';\n }\n break;\n \n // 绠$悊鍛樻潈闄愪笉瓒虫彁绀篭n case 'adminPermissionRequired':\n showAdminPermissionModal();\n break;\n \n // 鏈哄櫒鐮侀噸缃甛n case 'machineIdReset':\n if (message.success && message.needRestart) {\n // 鏈哄櫒鐮侀噸缃渶瑕佸畬鍏ㄥ叧闂?Cursor锛屼笉鏄?reload\n showRestartModal(message.message || '鏈哄櫒鐮侀噸缃垚鍔?, 'close');\n }\n break;\n \n // 閫氱敤 Toast 娑堟伅\n case 'showToast':\n showToast(message.message || '', message.icon || '馃摙', 10000);\n break;\n \n // 缃戠粶鐘舵€乗n case 'networkStatus':\n updateOfflineStatus(!message.online);\n break;\n }\n });\n \n // 绂荤嚎鐘舵€佹樉绀?闅愯棌\n let wasOffline = false; // 璺熻釜涔嬪墠鏄惁绂荤嚎\n function updateOfflineStatus(isOffline) {\n if (isOffline) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else {\n offlineBanner.classList.remove('show');\n // 鍙湁浠庣绾挎仮澶嶅埌鍦ㄧ嚎鏃舵墠鍒锋柊鐘舵€乗n if (wasOffline) {\n wasOffline = false;\n vscode.postMessage({ type: 'getState' });\n vscode.postMessage({ type: 'getUserSwitchStatus' });\n }\n }\n }\n \n // 閲嶈瘯杩炴帴鎸夐挳\n retryConnectBtn.addEventListener('click', async () => {\n retryConnectBtn.classList.add('loading');\n retryConnectBtn.textContent = '杩炴帴涓?..';\n \n // 鍙戣捣鐪熸鐨勭綉缁滆姹傛潵娴嬭瘯缃戠粶\n vscode.postMessage({ type: 'retryConnect' });\n \n // 5绉掑悗鎭㈠鎸夐挳鐘舵€侊紙缁欑綉缁滆姹傝冻澶熸椂闂达級\n setTimeout(() => {\n retryConnectBtn.classList.remove('loading');\n retryConnectBtn.textContent = '閲嶈瘯';\n }, 5000);\n });\n \n function updateUI(state) {\n if (state.isActivated) {\n authStatus.textContent = '宸叉巿鏉?;\n authStatus.className = 'status-badge active';\n accountStatus.textContent = '宸叉縺娲?;\n accountStatus.className = 'status-badge active';\n switchBtn.disabled = false;\n fullActivationKey = state.key;\n keyDisplay.textContent = maskKey(fullActivationKey);\n // 鏇存柊鍒版湡鏃堕棿\n currentExpireDate = state.expireDate || '';\n expireDate.textContent = formatExpireDate(currentExpireDate);\n // 鏇存柊鎹㈠彿娆℃暟\n if (state.switchRemaining !== undefined) {\n currentSwitchRemaining = state.switchRemaining;\n switchCount.textContent = state.switchRemaining + '/' + (state.switchLimit || 100);\n }\n // 鍚敤鏃犳劅鎹㈠彿鎸夐挳锛堝彧鏈夎繃鏈熸墠绂佺敤锛塡n enableSeamlessBtn.disabled = isKeyExpired();\n }\n cursorVersion.textContent = state.cursorVersion || '0.0.0';\n \n // 鏍规嵁缃戠粶鐘舵€佹樉绀?闅愯棌绂荤嚎鎻愮ず\n if (state.isOnline === false) {\n offlineBanner.classList.add('show');\n wasOffline = true;\n } else if (state.isOnline === true) {\n // 缃戠粶鎭㈠锛岄殣钘忕绾挎彁绀篭n offlineBanner.classList.remove('show');\n wasOffline = false;\n }\n }\n </script>\n</body>\n</html>";
|
||
}
|
||
}
|
||
exports.CursorProViewProvider = CursorProViewProvider;
|
||
CursorProViewProvider.CURRENT_VERSION = '0.4.5';
|
||
|