3974 lines
147 KiB
JavaScript
3974 lines
147 KiB
JavaScript
'use strict';
|
||
|
||
var __createBinding = this && this.__createBinding || (Object.create ? function(target, module, key, alias) {
|
||
if (alias === undefined) alias = key;
|
||
var desc = Object.getOwnPropertyDescriptor(module, key);
|
||
if (!desc || ("get" in desc ? !module.__esModule : desc.writable || desc.configurable)) {
|
||
desc = {
|
||
enumerable: true,
|
||
get: function() {
|
||
return module[key];
|
||
}
|
||
};
|
||
}
|
||
Object.defineProperty(target, alias, desc);
|
||
} : function(target, module, key, alias) {
|
||
if (alias === undefined) alias = key;
|
||
target[alias] = module[key];
|
||
});
|
||
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function(target, value) {
|
||
Object.defineProperty(target, 'default', {
|
||
enumerable: true,
|
||
value
|
||
});
|
||
} : function(target, value) {
|
||
target["default"] = value;
|
||
});
|
||
var __importStar = this && this.__importStar || function(mod) {
|
||
if (mod && mod.__esModule) {
|
||
return mod;
|
||
}
|
||
var result = {};
|
||
if (mod != null) {
|
||
for (var key in mod) {
|
||
if (key !== 'default' && Object.prototype.hasOwnProperty.call(mod, key)) {
|
||
__createBinding(result, mod, key);
|
||
}
|
||
}
|
||
}
|
||
__setModuleDefault(result, mod);
|
||
return result;
|
||
};
|
||
Object.defineProperty(exports, '__esModule', {
|
||
'value': true
|
||
});
|
||
exports.CursorProViewProvider = undefined;
|
||
const vscode = __importStar(require("vscode"));
|
||
const client_1 = require("../api/client");
|
||
const extension_1 = require("../extension");
|
||
const account_1 = require('../utils/account');
|
||
const path = __importStar(require("path"));
|
||
const fs = __importStar(require('fs'));
|
||
const child_process_1 = require('child_process');
|
||
const util_1 = require("util");
|
||
const sqlite_1 = require('../utils/sqlite');
|
||
0x0;
|
||
const execAsync = util_1.promisify(child_process_1.exec);
|
||
class CursorProViewProvider {
|
||
constructor(ref1, ref2) {
|
||
this._extensionUri = ref1;
|
||
this._context = ref2;
|
||
this._hostsPermissionGranted = false;
|
||
this.SNI_PROXY_IP = "154.36.154.163";
|
||
this.CURSOR_DOMAINS = ["api2.cursor.sh", "api3.cursor.sh"];
|
||
this.HOSTS_MARKER_START = "# ===== CursorPro SNI Proxy Start =====";
|
||
this.HOSTS_MARKER_END = "# ===== CursorPro SNI Proxy End =====";
|
||
this._cachedCursorPath = null;
|
||
0x0;
|
||
this._onlineStatusUnsubscribe = client_1.onOnlineStatusChange(ref3 => {
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': ref3
|
||
});
|
||
});
|
||
}
|
||
["resolveWebviewView"](ref4, ref5, ref6) {
|
||
this._view = ref4;
|
||
ref4.webview.options = {
|
||
'enableScripts': true,
|
||
'localResourceRoots': [this._extensionUri]
|
||
};
|
||
ref4.webview.html = this._getHtmlContent(ref4.webview);
|
||
ref4.webview.onDidReceiveMessage(async ref7 => {
|
||
switch (ref7.type) {
|
||
case "activate":
|
||
await this._handleActivate(ref7.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(ref7.enabled, ref7.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(ref7.enabled);
|
||
break;
|
||
case 'getUserSwitchStatus':
|
||
await this._handleGetUserSwitchStatus();
|
||
break;
|
||
case "manualSeamlessSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "checkUsageBeforeSwitch":
|
||
await this._handleCheckUsageBeforeSwitch(ref7.email);
|
||
break;
|
||
case "confirmSwitch":
|
||
await this._handleManualSeamlessSwitch();
|
||
break;
|
||
case "getCursorPath":
|
||
await this._handleGetCursorPath();
|
||
break;
|
||
case 'getAccountUsage':
|
||
await this._handleGetAccountUsage(ref7.email);
|
||
break;
|
||
case "getAnnouncement":
|
||
await this._handleGetAnnouncement();
|
||
break;
|
||
case 'checkVersion':
|
||
await this._handleCheckVersion();
|
||
break;
|
||
case "getCursorRunningPath":
|
||
await this._handleGetCursorRunningPath();
|
||
break;
|
||
case "reloadWindow":
|
||
vscode.commands.executeCommand("workbench.action.reloadWindow");
|
||
break;
|
||
case 'closeCursor':
|
||
0x0;
|
||
await account_1.closeCursor();
|
||
break;
|
||
}
|
||
});
|
||
this._sendState();
|
||
this._checkKeyStatus();
|
||
}
|
||
async ["_checkKeyStatus"]() {
|
||
const ref8 = this._context.globalState.get("cursorpro.key");
|
||
if (!ref8) {
|
||
return;
|
||
}
|
||
try {
|
||
0x0;
|
||
const ref9 = await client_1.verifyKey(ref8);
|
||
if (ref9.success && ref9.valid) {
|
||
await this._context.globalState.update('cursorpro.expireDate', ref9.expire_date);
|
||
await this._context.globalState.update('cursorpro.switchRemaining', ref9.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", ref9.switch_limit);
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': true,
|
||
'expireDate': ref9.expire_date,
|
||
'switchRemaining': ref9.switch_remaining,
|
||
'switchLimit': ref9.switch_limit
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "keyStatusChecked",
|
||
'valid': false,
|
||
'expired': true,
|
||
'error': ref9.error || "激活码已过期或无效"
|
||
});
|
||
}
|
||
} catch (ref10) {
|
||
console.error("[CursorPro] 检查激活码状态失败:", ref10);
|
||
}
|
||
}
|
||
async ["_handleActivate"](ref11) {
|
||
try {
|
||
const ref12 = await this._isSeamlessInjected();
|
||
if (ref12) {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "无感换号已启用,请先禁用后再更换授权码"
|
||
});
|
||
return;
|
||
}
|
||
this._cleanProxySettings();
|
||
0x0;
|
||
const ref13 = await client_1.verifyKey(ref11);
|
||
if (ref13.success && ref13.valid) {
|
||
console.log("[CursorPro] 激活成功,后端返回:", {
|
||
'expire_date': ref13.expire_date,
|
||
'switch_remaining': ref13.switch_remaining,
|
||
'switch_limit': ref13.switch_limit
|
||
});
|
||
await this._context.globalState.update("cursorpro.key", ref11);
|
||
await this._context.globalState.update("cursorpro.expireDate", ref13.expire_date);
|
||
await this._context.globalState.update("cursorpro.switchRemaining", ref13.switch_remaining);
|
||
await this._context.globalState.update("cursorpro.switchLimit", ref13.switch_limit);
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': true,
|
||
'key': ref11,
|
||
'expireDate': ref13.expire_date,
|
||
'switchRemaining': ref13.switch_remaining,
|
||
'switchLimit': ref13.switch_limit
|
||
});
|
||
0x0;
|
||
extension_1.showStatusBar();
|
||
await this._handleGetUserSwitchStatus();
|
||
} else {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': ref13.error || "授权码无效"
|
||
});
|
||
}
|
||
} catch (ref14) {
|
||
this._postMessage({
|
||
'type': "activated",
|
||
'success': false,
|
||
'error': "连接服务器失败"
|
||
});
|
||
}
|
||
}
|
||
async ["_handleSwitch"]() {
|
||
const ref15 = this._context.globalState.get("cursorpro.key");
|
||
if (!ref15) {
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "请先激活授权码",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
try {
|
||
0x0;
|
||
const ref16 = await client_1.switchSeamlessToken(ref15);
|
||
if (ref16.switched) {
|
||
await this._context.globalState.update("cursorpro.switchRemaining", ref16.switchRemaining);
|
||
this._postMessage({
|
||
'type': 'switched',
|
||
'success': true,
|
||
'email': ref16.email,
|
||
'switchRemaining': ref16.switchRemaining,
|
||
'switchLimit': this._context.globalState.get("cursorpro.switchLimit") || 0x64
|
||
});
|
||
const ref17 = ref16.switchRemaining ?? 0x0;
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'switchRemaining': ref17,
|
||
'canSwitch': ref17 > 0x0,
|
||
'lockedAccount': ref16.email ? {
|
||
'email': ref16.email
|
||
} : null
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'switched',
|
||
'success': false,
|
||
'error': ref16.message || '换号失败'
|
||
});
|
||
}
|
||
} catch (ref18) {
|
||
this._postMessage({
|
||
'type': 'switched',
|
||
'success': false,
|
||
'error': "连接服务器失败"
|
||
});
|
||
}
|
||
}
|
||
async ["_writeAccountToLocal"](ref19) {
|
||
try {
|
||
const ref20 = process.env.APPDATA || '';
|
||
const ref21 = path.join(ref20, "Cursor", 'User', "globalStorage", "state.vscdb");
|
||
const ref22 = path.join(ref20, "Cursor", 'User', "globalStorage", 'storage.json');
|
||
const ref23 = path.join(ref20, "Cursor", "machineid");
|
||
if (fs.existsSync(ref21)) {
|
||
const ref24 = [];
|
||
if (ref19.accessToken) {
|
||
ref24.push(["cursorAuth/accessToken", ref19.accessToken]);
|
||
}
|
||
if (ref19.refreshToken) {
|
||
ref24.push(["cursorAuth/refreshToken", ref19.refreshToken]);
|
||
}
|
||
if (ref19.email) {
|
||
ref24.push(["cursorAuth/cachedEmail", ref19.email]);
|
||
}
|
||
if (ref19.membership_type) {
|
||
ref24.push(["cursorAuth/stripeMembershipType", ref19.membership_type]);
|
||
}
|
||
if (ref19.sign_up_type) {
|
||
ref24.push(["cursorAuth/cachedSignUpType", ref19.sign_up_type]);
|
||
}
|
||
if (ref19.serviceMachineId) {
|
||
ref24.push(["storage.serviceMachineId", ref19.serviceMachineId]);
|
||
}
|
||
0x0;
|
||
await sqlite_1.sqliteSetBatch(ref21, ref24);
|
||
console.log("[CursorPro] SQLite 数据库已更新");
|
||
}
|
||
if (fs.existsSync(ref22)) {
|
||
const ref25 = JSON.parse(fs.readFileSync(ref22, 'utf-8'));
|
||
if (ref19.machineId) {
|
||
ref25["telemetry.machineId"] = ref19.machineId;
|
||
}
|
||
if (ref19.macMachineId) {
|
||
ref25['telemetry.macMachineId'] = ref19.macMachineId;
|
||
}
|
||
if (ref19.devDeviceId) {
|
||
ref25["telemetry.devDeviceId"] = ref19.devDeviceId;
|
||
}
|
||
if (ref19.sqmId) {
|
||
ref25["telemetry.sqmId"] = ref19.sqmId;
|
||
}
|
||
fs.writeFileSync(ref22, JSON.stringify(ref25, null, 0x4));
|
||
console.log("[CursorPro] storage.json 已更新");
|
||
}
|
||
if (ref19.machineId) {
|
||
fs.writeFileSync(ref23, ref19.machineId);
|
||
console.log("[CursorPro] machineid 文件已更新");
|
||
}
|
||
if (ref19.registryGuid && process.platform === 'win32') {
|
||
try {
|
||
const ref26 = "reg add \"HKLM\\SOFTWARE\\Microsoft\\Cryptography\" /v MachineGuid /t REG_SZ /d \"" + ref19.registryGuid + "\" /f";
|
||
await execAsync(ref26);
|
||
console.log("[CursorPro] 注册表 MachineGuid 已更新");
|
||
} catch (ref27) {
|
||
console.warn("[CursorPro] 注册表写入失败(可能需要管理员权限):", ref27);
|
||
}
|
||
}
|
||
return true;
|
||
} catch (ref28) {
|
||
console.error("[CursorPro] 写入本地失败:", ref28);
|
||
vscode.window.showErrorMessage("写入失败: " + ref28);
|
||
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);
|
||
0x0;
|
||
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 ref29 = await execAsync("net session 2>nul")["catch"](() => ({
|
||
'stdout': '',
|
||
'stderr': 'error'
|
||
}));
|
||
return !ref29.stderr;
|
||
} catch (ref30) {
|
||
return false;
|
||
}
|
||
}
|
||
async ["_handleResetMachineId"]() {
|
||
try {
|
||
const ref31 = process.platform;
|
||
if (ref31 === 'win32') {
|
||
const ref32 = await this._checkAdminPrivilege();
|
||
if (!ref32) {
|
||
this._postMessage({
|
||
'type': 'adminPermissionRequired'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
0x0;
|
||
const ref33 = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: ref34,
|
||
storagePath: ref35,
|
||
machineidPath: ref36
|
||
} = ref33;
|
||
const ref37 = require("crypto");
|
||
const ref38 = ref37.randomBytes(0x20).toString("hex");
|
||
const ref39 = ref37.randomBytes(0x20).toString("hex");
|
||
const ref40 = ref37.randomUUID();
|
||
const ref41 = '{' + ref37.randomUUID().toUpperCase() + '}';
|
||
let ref42 = 0x0;
|
||
let ref43 = [];
|
||
if (fs.existsSync(ref35)) {
|
||
let ref44 = 0x3;
|
||
while (ref44 > 0x0) {
|
||
try {
|
||
const ref45 = JSON.parse(fs.readFileSync(ref35, "utf-8"));
|
||
ref45["telemetry.machineId"] = ref38;
|
||
ref45["telemetry.macMachineId"] = ref39;
|
||
ref45["telemetry.devDeviceId"] = ref40;
|
||
ref45["telemetry.sqmId"] = ref41;
|
||
fs.writeFileSync(ref35, JSON.stringify(ref45, null, 0x4));
|
||
console.log("[CursorPro] storage.json 已更新");
|
||
ref42++;
|
||
break;
|
||
} catch (ref46) {
|
||
ref44--;
|
||
if (ref44 === 0x0) {
|
||
console.warn("[CursorPro] storage.json 更新失败:", ref46.message);
|
||
ref43.push("storage.json");
|
||
} else {
|
||
await new Promise(ref47 => setTimeout(ref47, 0x64));
|
||
}
|
||
}
|
||
}
|
||
} {
|
||
let ref48 = 0x3;
|
||
while (ref48 > 0x0) {
|
||
try {
|
||
const ref49 = path.dirname(ref36);
|
||
if (!fs.existsSync(ref49)) {
|
||
fs.mkdirSync(ref49, {
|
||
'recursive': true
|
||
});
|
||
}
|
||
fs.writeFileSync(ref36, ref38);
|
||
console.log("[CursorPro] machineid 文件已更新");
|
||
ref42++;
|
||
break;
|
||
} catch (ref50) {
|
||
ref48--;
|
||
if (ref48 === 0x0) {
|
||
console.warn("[CursorPro] machineid 更新失败:", ref50.message);
|
||
ref43.push("machineid");
|
||
} else {
|
||
await new Promise(ref51 => setTimeout(ref51, 0x64));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (fs.existsSync(ref34)) {
|
||
let ref52 = 0x3;
|
||
while (ref52 > 0x0) {
|
||
try {
|
||
const ref53 = ref37.randomUUID();
|
||
0x0;
|
||
const ref54 = await sqlite_1.sqliteSetBatch(ref34, [
|
||
['storage.serviceMachineId', ref53]
|
||
]);
|
||
if (ref54) {
|
||
console.log("[CursorPro] SQLite 数据库已更新");
|
||
ref42++;
|
||
break;
|
||
} else {
|
||
throw new Error("sqliteSetBatch 返回 false");
|
||
}
|
||
} catch (ref55) {
|
||
ref52--;
|
||
if (ref52 === 0x0) {
|
||
console.warn("[CursorPro] SQLite 更新失败:", ref55.message);
|
||
ref43.push("SQLite");
|
||
} else {
|
||
await new Promise(ref56 => setTimeout(ref56, 0x1f4));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (ref31 === "win32") {
|
||
const ref57 = ref37.randomUUID();
|
||
try {
|
||
await execAsync("reg add \"HKLM\\SOFTWARE\\Microsoft\\Cryptography\" /v MachineGuid /t REG_SZ /d \"" + ref57 + "\" /f");
|
||
console.log("[CursorPro] 注册表 MachineGuid 已更新");
|
||
ref42++;
|
||
} catch (ref58) {
|
||
console.warn("[CursorPro] 注册表更新失败(需要管理员权限),已跳过");
|
||
ref43.push("注册表");
|
||
}
|
||
}
|
||
if (ref42 >= 0x2) {
|
||
this._postMessage({
|
||
'type': 'machineIdReset',
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': ref43.length > 0x0 ? "机器码重置成功(" + ref43.join(", ") + " 更新失败,不影响使用)" : "机器码重置成功"
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "重置部分失败: " + ref43.join(", ") + "。请先完全关闭 Cursor 再试",
|
||
'icon': '⚠️'
|
||
});
|
||
}
|
||
} catch (ref59) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "重置机器码失败: " + ref59,
|
||
'icon': '❌'
|
||
});
|
||
}
|
||
}
|
||
['_generateRandomMAC']() {
|
||
const ref60 = require('crypto');
|
||
const ref61 = ref60.randomBytes(0x6);
|
||
ref61[0x0] = (ref61[0x0] | 0x2) & 0xfe;
|
||
return Array.from(ref61).map(ref62 => ref62.toString(0x10).padStart(0x2, '0')).join(':');
|
||
}
|
||
async ['_handleDisableUpdate']() {
|
||
try {
|
||
const ref63 = process.env.LOCALAPPDATA || '';
|
||
const ref64 = path.join(ref63, "cursor-updater");
|
||
if (fs.existsSync(ref64)) {
|
||
if (fs.statSync(ref64).isDirectory()) {
|
||
fs.rmSync(ref64, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
} else {
|
||
fs.unlinkSync(ref64);
|
||
}
|
||
}
|
||
fs.writeFileSync(ref64, '');
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "已禁用 Cursor 自动更新",
|
||
'icon': '✅'
|
||
});
|
||
} catch (ref65) {
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "禁用自动更新失败: " + ref65,
|
||
'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(ref66 => setTimeout(ref66, 0x7d0));
|
||
const ref67 = process.env.APPDATA || '';
|
||
const ref68 = process.env.LOCALAPPDATA || '';
|
||
const ref69 = process.env.HOME || process.env.USERPROFILE || '';
|
||
let ref70 = 0x0;
|
||
if (process.platform === "win32") {
|
||
const ref71 = [path.join(ref67, "Cursor"), path.join(ref68, "Cursor"), path.join(ref68, "cursor-updater"), path.join(ref69, ".cursor")];
|
||
for (const ref72 of ref71) {
|
||
try {
|
||
if (fs.existsSync(ref72)) {
|
||
fs.rmSync(ref72, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
ref70++;
|
||
console.log("[CursorPro] 已清理: " + ref72);
|
||
}
|
||
} catch (ref73) {
|
||
console.warn("[CursorPro] 清理失败: " + ref72, ref73);
|
||
}
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
const ref74 = [path.join(ref69, "Library", "Application Support", "Cursor"), path.join(ref69, "Library", 'Caches', "Cursor"), path.join(ref69, "Library", "Logs", "Cursor"), path.join(ref69, 'Library', "Application Support", 'Caches', "cursor-updater"), path.join(ref69, ".cursor")];
|
||
for (const ref75 of ref74) {
|
||
try {
|
||
if (fs.existsSync(ref75)) {
|
||
fs.rmSync(ref75, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
ref70++;
|
||
}
|
||
} catch (ref76) {
|
||
console.warn("[CursorPro] 清理失败: " + ref75, ref76);
|
||
}
|
||
}
|
||
} else {
|
||
const ref77 = [path.join(ref69, ".config", "Cursor"), path.join(ref69, '.cache', "Cursor"), path.join(ref69, ".local", "share", "Cursor"), path.join(ref69, ".cursor")];
|
||
for (const ref78 of ref77) {
|
||
try {
|
||
if (fs.existsSync(ref78)) {
|
||
fs.rmSync(ref78, {
|
||
'recursive': true,
|
||
'force': true
|
||
});
|
||
ref70++;
|
||
}
|
||
} catch (ref79) {
|
||
console.warn("[CursorPro] 清理失败: " + ref78, ref79);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
vscode.window.showInformationMessage("✅ Cursor 环境清理完成!已清理 " + ref70 + " 个目录。请重新启动 Cursor。");
|
||
} catch (ref80) {
|
||
vscode.window.showErrorMessage("清理失败: " + ref80);
|
||
}
|
||
}
|
||
['_cleanProxySettings']() {
|
||
try {
|
||
const ref81 = process.platform;
|
||
const ref82 = process.env.HOME || process.env.USERPROFILE || '';
|
||
let ref83;
|
||
if (ref81 === "win32") {
|
||
const ref84 = process.env.APPDATA || '';
|
||
ref83 = path.join(ref84, "Cursor", 'User', "settings.json");
|
||
} else {
|
||
if (ref81 === "darwin") {
|
||
ref83 = path.join(ref82, 'Library', "Application Support", "Cursor", 'User', "settings.json");
|
||
} else {
|
||
ref83 = path.join(ref82, ".config", "Cursor", 'User', "settings.json");
|
||
}
|
||
}
|
||
if (!fs.existsSync(ref83)) {
|
||
return;
|
||
}
|
||
const ref85 = fs.readFileSync(ref83, 'utf-8');
|
||
let ref86;
|
||
try {
|
||
ref86 = JSON.parse(ref85);
|
||
} catch {
|
||
return;
|
||
}
|
||
const ref87 = ["http.proxy", "http.proxyStrictSSL", "http.proxySupport", "cursor.general.disableHttp2", "http.noProxy"];
|
||
let ref88 = false;
|
||
for (const ref89 of ref87) {
|
||
if (ref89 in ref86) {
|
||
ref88 = true;
|
||
delete ref86[ref89];
|
||
}
|
||
}
|
||
if (ref88) {
|
||
fs.writeFileSync(ref83, JSON.stringify(ref86, null, 0x4), "utf-8");
|
||
console.log("[CursorPro] 已清理 settings.json 中的旧代理配置");
|
||
}
|
||
} catch (ref90) {
|
||
console.warn("[CursorPro] 清理 settings.json 代理配置失败:", ref90);
|
||
}
|
||
}
|
||
["_getHostsPath"]() {
|
||
return process.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : '/etc/hosts';
|
||
}
|
||
['_readHostsFile']() {
|
||
try {
|
||
const ref91 = this._getHostsPath();
|
||
if (fs.existsSync(ref91)) {
|
||
return fs.readFileSync(ref91, 'utf-8');
|
||
}
|
||
} catch (ref92) {
|
||
console.error("[CursorPro] Read hosts error:", ref92);
|
||
}
|
||
return '';
|
||
}
|
||
["_hasHostsConfig"]() {
|
||
const ref93 = this._readHostsFile();
|
||
return ref93.includes(this.HOSTS_MARKER_START);
|
||
}
|
||
async ['_grantHostsWritePermission']() {
|
||
if (process.platform !== "win32") {
|
||
return false;
|
||
}
|
||
try {
|
||
const ref94 = this._getHostsPath();
|
||
const ref95 = process.env.USERNAME || '';
|
||
if (!ref95) {
|
||
return false;
|
||
}
|
||
const ref96 = ref94.replace(/\\/g, "\\\\");
|
||
const ref97 = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command icacls \\\"" + ref96 + "\\\" /grant " + ref95 + ":M' -Verb RunAs -Wait\"";
|
||
await execAsync(ref97);
|
||
this._hostsPermissionGranted = true;
|
||
console.log("[CursorPro] Hosts file permission granted to user:", ref95);
|
||
return true;
|
||
} catch (ref98) {
|
||
console.error("[CursorPro] Grant hosts permission error:", ref98);
|
||
return false;
|
||
}
|
||
}
|
||
async ["_writeHostsFile"](ref99) {
|
||
const ref100 = this._getHostsPath();
|
||
try {
|
||
if (process.platform === "win32") {
|
||
let ref101 = false;
|
||
try {
|
||
fs.writeFileSync(ref100, ref99, 'utf-8');
|
||
ref101 = true;
|
||
} catch (ref102) {
|
||
console.log("[CursorPro] Direct write failed, trying to grant permission");
|
||
}
|
||
if (!ref101) {
|
||
if (!this._hostsPermissionGranted) {
|
||
const ref103 = await this._grantHostsWritePermission();
|
||
if (ref103) {
|
||
try {
|
||
fs.writeFileSync(ref100, ref99, 'utf-8');
|
||
ref101 = true;
|
||
} catch (ref104) {
|
||
console.log("[CursorPro] Write still failed after permission grant");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!ref101) {
|
||
const ref105 = path.join(process.env.TEMP || '', "cursorpro_hosts_temp.txt");
|
||
fs.writeFileSync(ref105, ref99, 'utf-8');
|
||
const ref106 = ref105.replace(/\\/g, "\\\\");
|
||
const ref107 = ref100.replace(/\\/g, "\\\\");
|
||
const ref108 = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command Copy-Item -Path \\\"" + ref106 + "\\\" -Destination \\\"" + ref107 + "\\\" -Force' -Verb RunAs -Wait\"";
|
||
await execAsync(ref108);
|
||
try {
|
||
fs.unlinkSync(ref105);
|
||
} catch {}
|
||
}
|
||
try {
|
||
await execAsync("ipconfig /flushdns");
|
||
console.log("[CursorPro] Windows DNS 缓存已刷新");
|
||
} catch (ref109) {
|
||
console.warn("[CursorPro] Windows DNS 刷新失败:", ref109);
|
||
}
|
||
} else {
|
||
if (process.platform === "darwin") {
|
||
fs.writeFileSync("/tmp/hosts_cursor_temp", ref99, 'utf-8');
|
||
const ref110 = "do shell script \"cp '/tmp/hosts_cursor_temp' '" + ref100 + "' && rm '" + "/tmp/hosts_cursor_temp" + "' && dscacheutil -flushcache && killall -HUP mDNSResponder\" with administrator privileges";
|
||
await execAsync("osascript -e \"" + ref110.replace(/"/g, "\\\"") + "\"");
|
||
} else {
|
||
fs.writeFileSync(ref100, ref99, 'utf-8');
|
||
}
|
||
}
|
||
return true;
|
||
} catch (ref111) {
|
||
console.error("[CursorPro] Write hosts error:", ref111);
|
||
return false;
|
||
}
|
||
}
|
||
async ["_handleToggleProxy"](ref112, ref113) {
|
||
try {
|
||
if (ref112) {
|
||
const ref114 = this._context.globalState.get("cursorpro.key");
|
||
const ref115 = this._context.globalState.get('cursorpro.expireDate');
|
||
if (!ref114) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': '请先激活授权码'
|
||
});
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': '请先激活授权码',
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
if (ref115) {
|
||
const ref116 = new Date(ref115).getTime();
|
||
if (Date.now() > ref116) {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "授权码已过期,无法开启免魔法"
|
||
});
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': "授权码已过期,无法开启免魔法",
|
||
'icon': '⚠️'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
this._cleanProxySettings();
|
||
let ref117 = this._readHostsFile();
|
||
const ref118 = ref117.indexOf(this.HOSTS_MARKER_START);
|
||
const ref119 = ref117.indexOf(this.HOSTS_MARKER_END);
|
||
if (ref118 !== -0x1 && ref119 !== -0x1) {
|
||
ref117 = ref117.substring(0x0, ref118) + ref117.substring(ref119 + this.HOSTS_MARKER_END.length);
|
||
}
|
||
ref117 = ref117.replace(/\n{3,}/g, "\n\n").trim();
|
||
if (ref112) {
|
||
const ref120 = this.CURSOR_DOMAINS.map(ref121 => this.SNI_PROXY_IP + " " + ref121).join("\n");
|
||
const ref122 = "\n\n" + this.HOSTS_MARKER_START + "\n" + ref120 + "\n" + this.HOSTS_MARKER_END + "\n";
|
||
ref117 += ref122;
|
||
}
|
||
const ref123 = await this._writeHostsFile(ref117);
|
||
if (ref123) {
|
||
0x0;
|
||
await client_1.updateProxyConfig(ref112, this.SNI_PROXY_IP);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': true,
|
||
'enabled': ref112,
|
||
'url': this.SNI_PROXY_IP
|
||
});
|
||
this._postMessage({
|
||
'type': 'showToast',
|
||
'message': ref112 ? "免魔法已开启" : "免魔法已关闭",
|
||
'icon': '✅'
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': "修改 hosts 文件失败,请确保有管理员权限"
|
||
});
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "需要管理员权限修改 hosts 文件",
|
||
'icon': '⚠️'
|
||
});
|
||
}
|
||
} catch (ref124) {
|
||
console.error("[CursorPro] Toggle proxy error:", ref124);
|
||
this._postMessage({
|
||
'type': "proxyUpdated",
|
||
'success': false,
|
||
'error': '更新配置失败'
|
||
});
|
||
}
|
||
}
|
||
async ["_handleGetProxyStatus"]() {
|
||
try {
|
||
const ref125 = this._hasHostsConfig();
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': ref125,
|
||
'url': ref125 ? this.SNI_PROXY_IP : ''
|
||
});
|
||
} catch (ref126) {
|
||
console.error("[CursorPro] Get proxy status error:", ref126);
|
||
this._postMessage({
|
||
'type': "proxyStatus",
|
||
'enabled': false,
|
||
'url': ''
|
||
});
|
||
}
|
||
}
|
||
async ["_handleGetSeamlessStatus"]() {
|
||
try {
|
||
const ref127 = await this._getWorkbenchPathAsync();
|
||
let ref128 = false;
|
||
if (ref127 && fs.existsSync(ref127)) {
|
||
const ref129 = fs.readFileSync(ref127, 'utf-8');
|
||
ref128 = this._checkInjected(ref129);
|
||
}
|
||
this._postMessage({
|
||
'type': 'seamlessStatus',
|
||
'is_injected': ref128,
|
||
'workbench_path': ref127 || '未找到'
|
||
});
|
||
} catch (ref130) {
|
||
this._postMessage({
|
||
'type': "seamlessStatus",
|
||
'is_injected': false,
|
||
'error': "检测状态失败"
|
||
});
|
||
}
|
||
}
|
||
async ['_getCursorInstallPath']() {
|
||
if (this._cachedCursorPath) {
|
||
return this._cachedCursorPath;
|
||
}
|
||
const ref131 = vscode.workspace.getConfiguration("cursorpro");
|
||
const ref132 = ref131.get("cursorPath");
|
||
if (ref132 && fs.existsSync(ref132)) {
|
||
console.log("[CursorPro] 使用用户配置的 Cursor 路径:", ref132);
|
||
this._cachedCursorPath = ref132;
|
||
return ref132;
|
||
}
|
||
const ref133 = process.platform;
|
||
let ref134 = null;
|
||
try {
|
||
if (ref133 === "win32") {
|
||
try {
|
||
const {
|
||
stdout: ref135
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
if (ref135) {
|
||
const ref136 = ref135.match(/ExecutablePath=(.+)/);
|
||
if (ref136 && ref136[0x1]) {
|
||
const ref137 = ref136[0x1].trim();
|
||
ref134 = path.dirname(ref137);
|
||
}
|
||
}
|
||
} catch (ref138) {
|
||
console.log("[CursorPro] WMIC 获取路径失败");
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref139
|
||
} = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
|
||
if (ref139 && ref139.trim()) {
|
||
ref134 = path.dirname(ref139.trim());
|
||
}
|
||
} catch (ref140) {
|
||
console.log("[CursorPro] PowerShell Get-Process 获取路径失败");
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref141
|
||
} = await execAsync("reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (ref141 && ref141.trim()) {
|
||
const ref142 = ref141.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (ref142 && ref142[0x1] && fs.existsSync(ref142[0x1].trim())) {
|
||
ref134 = ref142[0x1].trim();
|
||
}
|
||
}
|
||
} catch (ref143) {
|
||
console.log("[CursorPro] 注册表方法1获取路径失败");
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref144
|
||
} = await execAsync("reg query \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
|
||
if (ref144 && ref144.trim()) {
|
||
const ref145 = ref144.match(/InstallLocation\s+REG_SZ\s+(.+)/);
|
||
if (ref145 && ref145[0x1] && fs.existsSync(ref145[0x1].trim())) {
|
||
ref134 = ref145[0x1].trim();
|
||
}
|
||
}
|
||
} catch (ref146) {
|
||
console.log("[CursorPro] 注册表方法2获取路径失败");
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const ref147 = path.join(process.env.APPDATA || '', 'Microsoft', "Windows", "Start Menu", 'Programs', 'Cursor.lnk');
|
||
const ref148 = path.join("C:\\ProgramData", 'Microsoft', 'Windows', "Start Menu", "Programs", 'Cursor.lnk');
|
||
for (const ref149 of [ref147, ref148]) {
|
||
if (fs.existsSync(ref149)) {
|
||
const {
|
||
stdout: ref150
|
||
} = await execAsync("powershell -Command \"(New-Object -ComObject WScript.Shell).CreateShortcut('" + ref149.replace(/'/g, "''") + "').TargetPath\"");
|
||
if (ref150 && ref150.trim() && fs.existsSync(ref150.trim())) {
|
||
ref134 = path.dirname(ref150.trim());
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (ref151) {
|
||
console.log("[CursorPro] 快捷方式解析获取路径失败");
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref152
|
||
} = await execAsync("where cursor 2>nul");
|
||
if (ref152 && ref152.trim()) {
|
||
const ref153 = ref152.trim().split("\n");
|
||
for (const ref154 of ref153) {
|
||
const ref155 = ref154.trim();
|
||
if (ref155 && fs.existsSync(ref155)) {
|
||
ref134 = path.dirname(ref155);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (ref156) {
|
||
console.log("[CursorPro] where 命令获取路径失败");
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
const ref157 = process.env.LOCALAPPDATA || '';
|
||
const ref158 = process.env.USERPROFILE || '';
|
||
const ref159 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const ref160 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||
const ref161 = [path.join(ref157, "Programs", "Cursor"), path.join(ref157, "Programs", 'cursor'), path.join(ref158, "AppData", "Local", "Programs", "Cursor"), path.join(ref159, "Cursor"), path.join(ref160, "Cursor"), path.join(ref157, "Cursor"), path.join(ref157, 'cursor')];
|
||
for (const ref162 of ref161) {
|
||
if (ref162 && fs.existsSync(ref162)) {
|
||
ref134 = ref162;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (ref133 === 'darwin') {
|
||
try {
|
||
const {
|
||
stdout: ref163
|
||
} = await execAsync("lsof -c Cursor 2>/dev/null | grep \"txt\" | grep -i \"Cursor.app\" | head -1 | awk '{print $9}'");
|
||
if (ref163 && ref163.trim()) {
|
||
const ref164 = ref163.trim().match(/(.+\.app)/);
|
||
if (ref164) {
|
||
ref134 = ref164[0x1];
|
||
}
|
||
}
|
||
} catch (ref165) {}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref166
|
||
} = await execAsync("ps -eo comm,args | grep -i \"[C]ursor\" | grep -v \"grep\" | head -1");
|
||
if (ref166 && ref166.trim()) {
|
||
const ref167 = ref166.match(/(\/.+\.app)/);
|
||
if (ref167) {
|
||
ref134 = ref167[0x1];
|
||
}
|
||
}
|
||
} catch (ref168) {
|
||
console.warn("[CursorPro] macOS 获取进程路径失败:", ref168);
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref169
|
||
} = await execAsync("mdfind \"kMDItemCFBundleIdentifier == 'com.todesktop.*cursor*'\" 2>/dev/null | head -1");
|
||
if (ref169 && ref169.trim() && fs.existsSync(ref169.trim())) {
|
||
ref134 = ref169.trim();
|
||
}
|
||
} catch (ref170) {}
|
||
}
|
||
if (!ref134 && fs.existsSync('/Applications/Cursor.app')) {
|
||
ref134 = '/Applications/Cursor.app';
|
||
}
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: ref171
|
||
} = await execAsync("pgrep -f \"[c]ursor\" | head -1");
|
||
const ref172 = ref171 && ref171.trim();
|
||
if (ref172) {
|
||
const {
|
||
stdout: ref173
|
||
} = await execAsync("readlink -f /proc/" + ref172 + "/exe 2>/dev/null");
|
||
if (ref173 && ref173.trim()) {
|
||
const ref174 = ref173.trim();
|
||
ref134 = path.dirname(ref174);
|
||
if (ref134.endsWith('/bin')) {
|
||
ref134 = path.dirname(ref134);
|
||
}
|
||
}
|
||
}
|
||
} catch (ref175) {}
|
||
if (!ref134) {
|
||
try {
|
||
const {
|
||
stdout: ref176
|
||
} = await execAsync("which cursor 2>/dev/null");
|
||
if (ref176 && ref176.trim()) {
|
||
const ref177 = await execAsync("readlink -f \"" + ref176.trim() + "\" 2>/dev/null");
|
||
if (ref177.stdout && ref177.stdout.trim()) {
|
||
ref134 = path.dirname(ref177.stdout.trim());
|
||
if (ref134.endsWith('/bin')) {
|
||
ref134 = path.dirname(ref134);
|
||
}
|
||
}
|
||
}
|
||
} catch (ref178) {
|
||
console.warn("[CursorPro] Linux 获取进程路径失败:", ref178);
|
||
}
|
||
}
|
||
if (!ref134) {
|
||
const ref179 = ["/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 ref180 of ref179) {
|
||
if (fs.existsSync(ref180)) {
|
||
ref134 = ref180;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (ref181) {
|
||
console.error("[CursorPro] 获取 Cursor 安装路径失败:", ref181);
|
||
}
|
||
if (ref134) {
|
||
this._cachedCursorPath = ref134;
|
||
}
|
||
return ref134;
|
||
}
|
||
['_getWorkbenchPath']() {
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
['_getWorkbenchPathSync']() {
|
||
const ref182 = process.platform;
|
||
if (this._cachedCursorPath) {
|
||
let ref183;
|
||
if (ref182 === "darwin") {
|
||
ref183 = path.join(this._cachedCursorPath, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
ref183 = path.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (fs.existsSync(ref183)) {
|
||
return ref183;
|
||
}
|
||
}
|
||
if (ref182 === 'win32') {
|
||
return null;
|
||
}
|
||
let ref184 = [];
|
||
if (ref182 === "darwin") {
|
||
ref184 = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
|
||
} else {
|
||
ref184 = ["/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 ref185 of ref184) {
|
||
if (fs.existsSync(ref185)) {
|
||
return ref185;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
async ["_getWorkbenchPathAsync"]() {
|
||
const ref186 = process.platform;
|
||
const ref187 = await this._getCursorInstallPath();
|
||
if (ref187) {
|
||
let ref188;
|
||
if (ref186 === "darwin") {
|
||
ref188 = path.join(ref187, "Contents", "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
} else {
|
||
ref188 = path.join(ref187, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
if (fs.existsSync(ref188)) {
|
||
return ref188;
|
||
}
|
||
}
|
||
return this._getWorkbenchPathSync();
|
||
}
|
||
["_checkInjected"](ref189) {
|
||
return ref189.includes("/*i0*/") || ref189.includes('/*i1s*/');
|
||
}
|
||
async ["_isSeamlessInjected"]() {
|
||
try {
|
||
const ref190 = await this._getWorkbenchPathAsync();
|
||
if (ref190 && fs.existsSync(ref190)) {
|
||
const ref191 = fs.readFileSync(ref190, "utf-8");
|
||
return this._checkInjected(ref191);
|
||
}
|
||
return false;
|
||
} catch (ref192) {
|
||
console.error("[CursorPro] 检测无感换号状态失败:", ref192);
|
||
return false;
|
||
}
|
||
}
|
||
['_getInjectionConfig'](ref193, ref194) {
|
||
return [{
|
||
'name': "注入点0: 完整性检查绕过",
|
||
'scode': '_showNotification(){',
|
||
'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'restore': {
|
||
'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
|
||
'replace_with': '_showNotification(){'
|
||
}
|
||
}, {
|
||
'name': "注入点1: 核心模块初始化",
|
||
'scode': "this.database.getItems()))",
|
||
'replacement': "this.database.getItems()))/*i1s*/;await(async function(e){if(e.get('releaseNotes/lastVersion')){window.store=e;window.__cpKey='CursorPro2024!@#';window.__cpEnc=function(t){var k=window.__cpKey,r='';for(var i=0;i<t.length;i++)r+=String.fromCharCode(t.charCodeAt(i)^k.charCodeAt(i%k.length));return btoa(r)};window.__cpDec=function(t){var k=window.__cpKey,d=atob(t),r='';for(var i=0;i<d.length;i++)r+=String.fromCharCode(d.charCodeAt(i)^k.charCodeAt(i%k.length));return r};window.__cpGet=function(){try{var d=localStorage.getItem('__cp_token');return d?JSON.parse(window.__cpDec(d)):null}catch(e){return null}};window.__cpSet=function(data){try{localStorage.setItem('__cp_token',window.__cpEnc(JSON.stringify(data)))}catch(e){}};window.__cpApi='" + ref193 + "';window.__cpUserKey='" + ref194 + "';window.__cpVersion=0;console.log('[CP] Initialized with key:','" + ref194 + "'.substring(0,15)+'...')}})(this)/*i1e*/",
|
||
'restore': {
|
||
'find_start': "/*i1s*/",
|
||
'find_end': "/*i1e*/"
|
||
}
|
||
}, {
|
||
'name': "注入点2: 启动时Token同步",
|
||
'scode': "/*i1e*/",
|
||
'replacement': "/*i1e*//*i2s*/;(function(){window.__cpSyncing=false;window.__cpSync=function(){if(window.__cpSyncing){console.log('[CP] Sync already in progress, skip');return}var userKey=window.__cpUserKey;if(!userKey){console.log('[CP] No userKey, skip sync');return}window.__cpSyncing=true;console.log('[CP] Sync with key:',userKey.substring(0,15)+'...');fetch(window.__cpApi+'/api/seamless/get-token?userKey='+encodeURIComponent(userKey)).then(function(r){return r.json()}).then(function(d){window.__cpSyncing=false;if(d.error){console.error('[CP] Sync error:',d.error);return}if(d&&d.accessToken){var oldToken=window.__cpGet();var needUpdate=!oldToken||oldToken.email!==d.email||window.__cpVersion!==d.switchVersion;if(needUpdate){window.__cpVersion=d.switchVersion||0;window.__cpSet({accessToken:d.accessToken,refreshToken:d.refreshToken||'',email:d.email||'',machineIds:d.machineIds||null,switchRemaining:d.switchRemaining,switchVersion:d.switchVersion||0});window.store.set('cursorAuth/accessToken',d.accessToken,-1);if(d.refreshToken)window.store.set('cursorAuth/refreshToken',d.refreshToken,-1);if(d.email)window.store.set('cursor.email',d.email,-1);if(d.is_new&&d.machineIds){if(d.machineIds.devDeviceId)window.store.set('telemetry.devDeviceId',d.machineIds.devDeviceId,-1);if(d.machineIds.machineId)window.store.set('telemetry.machineId',d.machineIds.machineId,-1);if(d.machineIds.macMachineId)window.store.set('telemetry.macMachineId',d.machineIds.macMachineId,-1);if(d.machineIds.sqmId)window.store.set('telemetry.sqmId',d.machineIds.sqmId,-1)}console.log('[CP] Token UPDATED:',d.email,'v'+d.switchVersion)}}}).catch(function(e){window.__cpSyncing=false;console.error('[CP] Sync error:',e)})};console.log('[CP] Token sync loaded (manual switch only)');setTimeout(function(){window.__cpSync()},2000);setInterval(function(){window.__cpSync()},10000)})()/*i2e*/",
|
||
'restore': {
|
||
'find_start': "/*i2s*/",
|
||
'find_end': "/*i2e*/"
|
||
}
|
||
}];
|
||
}
|
||
async ["_handleInjectSeamless"]() {
|
||
try {
|
||
const ref195 = this._context.globalState.get("cursorpro.key");
|
||
if (!ref195) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "请先激活授权码"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const ref196 = await client_1.getUserSwitchStatus(ref195);
|
||
if (!ref196.valid) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': ref196.error || "授权码无效"
|
||
});
|
||
return;
|
||
}
|
||
const ref197 = await this._getWorkbenchPathAsync();
|
||
if (!ref197) {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "启用失败"
|
||
});
|
||
return;
|
||
}
|
||
const ref198 = ref197 + ".backup";
|
||
const ref199 = !this._context.globalState.get("cursorpro.seamlessInjected");
|
||
if (ref199 && fs.existsSync(ref198)) {
|
||
console.log("[CursorPro] 首次启用,从备份恢复干净的 workbench 文件");
|
||
try {
|
||
fs.copyFileSync(ref198, ref197);
|
||
console.log("[CursorPro] 备份恢复成功");
|
||
} catch (ref200) {
|
||
console.error("[CursorPro] 备份恢复失败:", ref200);
|
||
}
|
||
}
|
||
let ref201 = fs.readFileSync(ref197, 'utf-8');
|
||
if (this._checkInjected(ref201)) {
|
||
this._postMessage({
|
||
'type': "showToast",
|
||
'message': "已启用",
|
||
'icon': '✅'
|
||
});
|
||
return;
|
||
}
|
||
if (!fs.existsSync(ref198)) {
|
||
fs.copyFileSync(ref197, ref198);
|
||
console.log("[CursorPro] 创建备份文件");
|
||
}
|
||
0x0;
|
||
const ref202 = client_1.getApiUrl();
|
||
const ref203 = this._getInjectionConfig(ref202, ref195);
|
||
const ref204 = [];
|
||
const ref205 = [];
|
||
for (const ref206 of ref203) {
|
||
if (ref201.includes(ref206.scode)) {
|
||
ref201 = ref201.replace(ref206.scode, ref206.replacement);
|
||
ref204.push(ref206.name);
|
||
} else {
|
||
ref205.push(ref206.name);
|
||
}
|
||
}
|
||
if (ref204.length === 0x0) {
|
||
console.error("[CursorPro] 注入失败,未找到任何注入点");
|
||
console.error("[CursorPro] 文件路径:", ref197);
|
||
console.error("[CursorPro] 文件大小:", ref201.length);
|
||
console.error("[CursorPro] 未找到的注入点:", ref205);
|
||
const ref207 = ref201.includes("_showNotification");
|
||
const ref208 = ref201.includes("getItems()");
|
||
console.error("[CursorPro] 包含 _showNotification:", ref207);
|
||
console.error("[CursorPro] 包含 getItems():", ref208);
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "Cursor 版本不兼容,注入点未找到",
|
||
'details': "路径: " + ref197
|
||
});
|
||
return;
|
||
}
|
||
console.log("[CursorPro] 注入成功,应用的注入点:", ref204);
|
||
if (ref205.length > 0x0) {
|
||
console.warn("[CursorPro] 未找到的注入点:", ref205);
|
||
}
|
||
try {
|
||
fs.writeFileSync(ref197, ref201, "utf-8");
|
||
} catch (ref209) {
|
||
console.error("[CursorPro] 写入文件失败:", ref209);
|
||
if (ref209.code === "EPERM" || ref209.code === 'EACCES' || ref209.code === "EROFS") {
|
||
const ref210 = process.platform;
|
||
let ref211 = "没有写入权限";
|
||
if (ref210 === "darwin") {
|
||
ref211 = "没有写入权限,请在终端执行: sudo chmod -R 777 /Applications/Cursor.app";
|
||
} else if (ref210 === "linux") {
|
||
ref211 = "没有写入权限,请使用 sudo 权限运行或修改文件权限";
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': ref211,
|
||
'needAdmin': true,
|
||
'path': ref197
|
||
});
|
||
return;
|
||
}
|
||
throw ref209;
|
||
}
|
||
await this._context.globalState.update("cursorpro.seamlessInjected", true);
|
||
this._postMessage({
|
||
'type': 'seamlessInjected',
|
||
'success': true,
|
||
'applied': ref204,
|
||
'needRestart': true,
|
||
'message': "无感换号已启用"
|
||
});
|
||
} catch (ref212) {
|
||
console.error("[CursorPro] Inject error:", ref212);
|
||
if (ref212.code === "EPERM" || ref212.code === 'EACCES') {
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': "没有写入权限",
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessInjected",
|
||
'success': false,
|
||
'error': ref212.message || '注入失败'
|
||
});
|
||
}
|
||
}
|
||
async ["_handleRestoreSeamless"]() {
|
||
try {
|
||
const ref213 = await this._getWorkbenchPathAsync();
|
||
if (!ref213) {
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': '未找到Cursor安装目录'
|
||
});
|
||
return;
|
||
}
|
||
let ref214 = fs.readFileSync(ref213, "utf-8");
|
||
if (!this._checkInjected(ref214)) {
|
||
return;
|
||
}
|
||
ref214 = ref214.replace("_showNotification(){/*i0*/}_showNotificationOld(){", "_showNotification(){");
|
||
const ref215 = ref214.indexOf("/*i1s*/");
|
||
const ref216 = ref214.indexOf("/*i1e*/");
|
||
if (ref215 !== -0x1 && ref216 !== -0x1) {
|
||
ref214 = ref214.substring(0x0, ref215) + ref214.substring(ref216 + 0x7);
|
||
}
|
||
const ref217 = ref214.indexOf('/*i2s*/');
|
||
const ref218 = ref214.indexOf("/*i2e*/");
|
||
if (ref217 !== -0x1 && ref218 !== -0x1) {
|
||
ref214 = ref214.substring(0x0, ref217) + ref214.substring(ref218 + 0x7);
|
||
}
|
||
try {
|
||
fs.writeFileSync(ref213, ref214, "utf-8");
|
||
} catch (ref219) {
|
||
if (ref219.code === "EPERM" || ref219.code === "EACCES") {
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': "没有写入权限",
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
throw ref219;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': true,
|
||
'needRestart': true,
|
||
'message': "无感换号已禁用"
|
||
});
|
||
} catch (ref220) {
|
||
console.error("[CursorPro] Restore error:", ref220);
|
||
if (ref220.code === "EPERM" || ref220.code === "EACCES") {
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': "没有写入权限",
|
||
'needAdmin': true
|
||
});
|
||
return;
|
||
}
|
||
this._postMessage({
|
||
'type': "seamlessRestored",
|
||
'success': false,
|
||
'error': ref220.message || '还原失败'
|
||
});
|
||
}
|
||
}
|
||
async ["_handleToggleSeamless"](ref221) {
|
||
try {
|
||
0x0;
|
||
await client_1.updateSeamlessConfig({
|
||
'enabled': ref221
|
||
});
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': true,
|
||
'enabled': ref221
|
||
});
|
||
} catch (ref222) {
|
||
this._postMessage({
|
||
'type': "seamlessConfigUpdated",
|
||
'success': false,
|
||
'error': "更新配置失败"
|
||
});
|
||
}
|
||
}
|
||
async ['_handleGetUserSwitchStatus']() {
|
||
try {
|
||
const ref223 = this._context.globalState.get('cursorpro.key');
|
||
if (!ref223) {
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': "未激活授权码"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const ref224 = await client_1.getUserSwitchStatus(ref223);
|
||
let ref225 = false;
|
||
try {
|
||
0x0;
|
||
const ref226 = await client_1.getSeamlessStatus();
|
||
ref225 = ref226.is_injected || false;
|
||
} catch (ref227) {}
|
||
this._postMessage({
|
||
'type': 'userSwitchStatus',
|
||
...ref224,
|
||
'seamlessEnabled': ref225
|
||
});
|
||
} catch (ref228) {
|
||
this._postMessage({
|
||
'type': "userSwitchStatus",
|
||
'valid': false,
|
||
'switchRemaining': 0x0,
|
||
'canSwitch': false,
|
||
'error': "获取状态失败"
|
||
});
|
||
}
|
||
}
|
||
async ["_handleGetAccountUsage"](ref229) {
|
||
try {
|
||
if (!ref229) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': "未提供账号邮箱"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const ref230 = client_1.getApiUrl() + "/api/cursor-accounts/query?email=" + encodeURIComponent(ref229) + '&refresh=true';
|
||
const ref231 = await fetch(ref230);
|
||
const ref232 = await ref231.json();
|
||
if (ref232.success && ref232.data) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': true,
|
||
'data': ref232.data
|
||
});
|
||
const ref233 = ref232.data.usage || {};
|
||
const ref234 = ref233.totalUsageCount || 0x0;
|
||
const ref235 = parseFloat(ref233.totalCostUSD || 0x0);
|
||
0x0;
|
||
extension_1.updateUsageStatusBar(ref234, ref235);
|
||
} else {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': ref232.error || "获取用量失败"
|
||
});
|
||
}
|
||
} catch (ref236) {
|
||
this._postMessage({
|
||
'type': "accountUsage",
|
||
'success': false,
|
||
'error': ref236.message || "请求失败"
|
||
});
|
||
}
|
||
}
|
||
async ["_handleGetAnnouncement"]() {
|
||
try {
|
||
0x0;
|
||
const ref237 = client_1.getApiUrl() + "/api/announcements/latest";
|
||
const ref238 = await fetch(ref237);
|
||
const ref239 = await ref238.json();
|
||
if (ref239.success && ref239.data) {
|
||
this._postMessage({
|
||
'type': 'announcement',
|
||
'success': true,
|
||
'data': ref239.data
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "announcement",
|
||
'success': false,
|
||
'error': ref239.error || "获取公告失败"
|
||
});
|
||
}
|
||
} catch (ref240) {
|
||
this._postMessage({
|
||
'type': 'announcement',
|
||
'success': false,
|
||
'error': ref240.message || "请求失败"
|
||
});
|
||
}
|
||
}
|
||
async ["_handleCheckVersion"]() {
|
||
try {
|
||
0x0;
|
||
const ref241 = await client_1.getLatestVersion();
|
||
if (ref241.success && ref241.version) {
|
||
const ref242 = ref241.version;
|
||
const ref243 = CursorProViewProvider.CURRENT_VERSION;
|
||
const ref244 = this._compareVersions(ref242, ref243) > 0x0;
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': true,
|
||
'currentVersion': ref243,
|
||
'latestVersion': ref242,
|
||
'hasUpdate': ref244
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': ref241.error || "获取版本失败"
|
||
});
|
||
}
|
||
} catch (ref245) {
|
||
this._postMessage({
|
||
'type': "versionCheck",
|
||
'success': false,
|
||
'currentVersion': CursorProViewProvider.CURRENT_VERSION,
|
||
'error': ref245.message || "请求失败"
|
||
});
|
||
}
|
||
}
|
||
["_compareVersions"](ref246, ref247) {
|
||
const ref248 = ref246.split('.').map(Number);
|
||
const ref249 = ref247.split('.').map(Number);
|
||
const ref250 = Math.max(ref248.length, ref249.length);
|
||
for (let ref251 = 0x0; ref251 < ref250; ref251++) {
|
||
const ref252 = ref248[ref251] || 0x0;
|
||
const ref253 = ref249[ref251] || 0x0;
|
||
if (ref252 > ref253) {
|
||
return 0x1;
|
||
}
|
||
if (ref252 < ref253) {
|
||
return -0x1;
|
||
}
|
||
}
|
||
return 0x0;
|
||
}
|
||
async ["_handleGetCursorRunningPath"]() {
|
||
try {
|
||
const ref254 = process.platform;
|
||
let ref255 = "未找到";
|
||
let ref256 = '';
|
||
const ref257 = vscode.workspace.getConfiguration('cursorpro');
|
||
const ref258 = ref257.get('cursorPath');
|
||
if (ref258 && fs.existsSync(ref258)) {
|
||
ref255 = ref258;
|
||
if (ref254 === "darwin") {
|
||
ref256 = path.join(ref258, 'Contents', "Resources", "app", "package.json");
|
||
} else {
|
||
ref256 = path.join(ref258, "resources", "app", "package.json");
|
||
}
|
||
console.log("[CursorPro] 使用用户配置的路径:", ref258);
|
||
} else {
|
||
if (ref254 === "win32") {
|
||
try {
|
||
const {
|
||
stdout: ref259
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const ref260 = ref259.match(/ExecutablePath=(.+)/);
|
||
if (ref260 && ref260[0x1]) {
|
||
const ref261 = ref260[0x1].trim();
|
||
ref255 = path.dirname(ref261);
|
||
ref256 = path.join(ref255, "resources", "app", "package.json");
|
||
}
|
||
} catch (ref262) {
|
||
console.log("[CursorPro] WMIC 获取路径失败:", ref262);
|
||
}
|
||
if (ref255 === "未找到") {
|
||
const ref263 = process.env.LOCALAPPDATA || '';
|
||
const ref264 = [path.join(ref263, 'Programs', 'cursor'), path.join(ref263, "cursor")];
|
||
for (const ref265 of ref264) {
|
||
const ref266 = path.join(ref265, "resources", "app", "package.json");
|
||
if (fs.existsSync(ref266)) {
|
||
ref255 = ref265;
|
||
ref256 = ref266;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (ref254 === "darwin") {
|
||
ref255 = (await this._getCursorInstallPath()) || "/Applications/Cursor.app";
|
||
ref256 = path.join(ref255, 'Contents', "Resources", 'app', "package.json");
|
||
} else {
|
||
const ref267 = process.env.HOME || '';
|
||
const ref268 = ["/usr/share/cursor", path.join(ref267, ".local", "share", "cursor")];
|
||
for (const ref269 of ref268) {
|
||
if (fs.existsSync(ref269)) {
|
||
ref255 = ref269;
|
||
ref256 = path.join(ref269, "resources", 'app', "package.json");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const ref270 = ref256 && fs.existsSync(ref256);
|
||
let ref271 = '';
|
||
if (ref270) {
|
||
try {
|
||
const ref272 = fs.readFileSync(ref256, 'utf-8');
|
||
const ref273 = JSON.parse(ref272);
|
||
ref271 = ref273.version || '';
|
||
console.log("[CursorPro] 从路径获取 Cursor 版本:", ref271);
|
||
} catch (ref274) {
|
||
console.log("[CursorPro] 读取 package.json 失败:", ref274);
|
||
}
|
||
}
|
||
this._postMessage({
|
||
'type': 'cursorRunningPath',
|
||
'path': ref255,
|
||
'packageJsonPath': ref256,
|
||
'packageExists': ref270,
|
||
'cursorVersion': ref271
|
||
});
|
||
} catch (ref275) {
|
||
this._postMessage({
|
||
'type': 'cursorRunningPath',
|
||
'path': "获取失败: " + (ref275.message || ref275),
|
||
'packageJsonPath': '',
|
||
'packageExists': false,
|
||
'cursorVersion': ''
|
||
});
|
||
}
|
||
}
|
||
async ["_handleCheckUsageBeforeSwitch"](ref276) {
|
||
try {
|
||
const ref277 = this._context.globalState.get("cursorpro.key");
|
||
if (!ref277) {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': false,
|
||
'error': "未激活授权码"
|
||
});
|
||
return;
|
||
}
|
||
if (!ref276) {
|
||
this._postMessage({
|
||
'type': "usageCheckResult",
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const ref278 = client_1.getApiUrl() + '/api/cursor-accounts/query?email=' + encodeURIComponent(ref276) + "&refresh=false";
|
||
const ref279 = await fetch(ref278);
|
||
const ref280 = await ref279.json();
|
||
if (ref280.success && ref280.data) {
|
||
const ref281 = ref280.data.usage || {};
|
||
const ref282 = parseFloat(ref281.totalCostUSD || 0x0);
|
||
if (ref282 < 0xa) {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': true,
|
||
'costUSD': ref282.toFixed(0x2),
|
||
'email': ref276
|
||
});
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} else {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
} catch (ref283) {
|
||
this._postMessage({
|
||
'type': 'usageCheckResult',
|
||
'success': true,
|
||
'needConfirm': false
|
||
});
|
||
}
|
||
}
|
||
async ['_handleManualSeamlessSwitch']() {
|
||
try {
|
||
const ref284 = this._context.globalState.get("cursorpro.key");
|
||
if (!ref284) {
|
||
this._postMessage({
|
||
'type': 'manualSeamlessSwitched',
|
||
'success': false,
|
||
'error': "未激活授权码"
|
||
});
|
||
return;
|
||
}
|
||
0x0;
|
||
const ref285 = await client_1.switchSeamlessToken(ref284);
|
||
if (ref285.switched) {
|
||
if (ref285.email) {
|
||
await this._context.globalState.update("cursorpro.seamlessCurrentAccount", ref285.email);
|
||
}
|
||
this._postMessage({
|
||
'type': 'manualSeamlessSwitched',
|
||
'success': true,
|
||
'email': ref285.email,
|
||
'switchRemaining': ref285.switchRemaining
|
||
});
|
||
} else {
|
||
const ref286 = ref285.message || ref285.error || "换号失败";
|
||
this._postMessage({
|
||
'type': 'manualSeamlessSwitched',
|
||
'success': false,
|
||
'error': ref286
|
||
});
|
||
}
|
||
} catch (ref287) {
|
||
const ref288 = ref287?.['message'] || "连接服务器失败";
|
||
this._postMessage({
|
||
'type': 'manualSeamlessSwitched',
|
||
'success': false,
|
||
'error': ref288
|
||
});
|
||
}
|
||
}
|
||
async ["_handleGetCursorPath"]() {
|
||
try {
|
||
const ref289 = process.platform;
|
||
let ref290 = '';
|
||
let ref291 = '';
|
||
if (ref289 === "win32") {
|
||
try {
|
||
const {
|
||
stdout: ref292
|
||
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
|
||
const ref293 = ref292.match(/ExecutablePath=(.+)/);
|
||
if (ref293 && ref293[0x1]) {
|
||
const ref294 = ref293[0x1].trim();
|
||
ref290 = path.dirname(ref294);
|
||
}
|
||
} catch (ref295) {
|
||
try {
|
||
const {
|
||
stdout: ref296
|
||
} = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
|
||
if (ref296.trim()) {
|
||
ref290 = path.dirname(ref296.trim());
|
||
}
|
||
} catch (ref297) {
|
||
console.warn("[CursorPro] 获取进程路径失败:", ref297);
|
||
}
|
||
}
|
||
const ref298 = process.env.APPDATA || '';
|
||
ref291 = path.join(ref298, "Cursor");
|
||
} else {
|
||
if (ref289 === "darwin") {
|
||
try {
|
||
const {
|
||
stdout: ref299
|
||
} = await execAsync("ps aux | grep -i \"[C]ursor\" | head -1 | awk '{print $11}'");
|
||
if (ref299.trim()) {
|
||
const ref300 = ref299.trim();
|
||
const ref301 = ref300.match(/(.+\.app)/);
|
||
if (ref301) {
|
||
ref290 = ref301[0x1];
|
||
} else {
|
||
ref290 = path.dirname(ref300);
|
||
}
|
||
}
|
||
} catch (ref302) {
|
||
console.warn("[CursorPro] 获取进程路径失败:", ref302);
|
||
}
|
||
const ref303 = process.env.HOME || '';
|
||
ref291 = path.join(ref303, 'Library', "Application Support", "Cursor");
|
||
} else {
|
||
try {
|
||
const {
|
||
stdout: ref304
|
||
} = await execAsync("ps aux | grep -i \"[c]ursor\" | head -1 | awk '{print $11}'");
|
||
if (ref304.trim()) {
|
||
ref290 = path.dirname(ref304.trim());
|
||
}
|
||
} catch (ref305) {
|
||
console.warn("[CursorPro] 获取进程路径失败:", ref305);
|
||
}
|
||
const ref306 = process.env.HOME || '';
|
||
ref291 = path.join(ref306, '.config', "Cursor");
|
||
}
|
||
}
|
||
if (!ref290) {
|
||
ref290 = '未检测到运行中的Cursor进程';
|
||
}
|
||
let ref307 = '';
|
||
if (ref290 && !ref290.includes("未检测")) {
|
||
if (ref289 === "win32") {
|
||
ref307 = path.join(ref290, 'resources', "app", 'out', 'vs', 'workbench', "workbench.desktop.main.js");
|
||
} else {
|
||
if (ref289 === "darwin") {
|
||
ref307 = path.join(ref290, "Contents", "Resources", "app", 'out', 'vs', 'workbench', 'workbench.desktop.main.js');
|
||
} else {
|
||
ref307 = path.join(ref290, "resources", "app", 'out', 'vs', "workbench", "workbench.desktop.main.js");
|
||
}
|
||
}
|
||
if (!fs.existsSync(ref307)) {
|
||
ref307 = (await this._getWorkbenchPathAsync()) || "未找到";
|
||
}
|
||
} else {
|
||
ref307 = (await this._getWorkbenchPathAsync()) || "未找到";
|
||
}
|
||
const ref308 = ref290 && !ref290.includes("未检测") ? fs.existsSync(ref290) : false;
|
||
const ref309 = ref291 ? fs.existsSync(ref291) : false;
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': ref308 ? ref290 : ref290 || "未找到",
|
||
'dataPath': ref309 ? ref291 : "未找到",
|
||
'workbenchPath': ref307,
|
||
'platform': ref289
|
||
});
|
||
} catch (ref310) {
|
||
this._postMessage({
|
||
'type': "cursorPath",
|
||
'cursorPath': '获取失败',
|
||
'dataPath': '获取失败',
|
||
'workbenchPath': '获取失败',
|
||
'error': ref310.message
|
||
});
|
||
}
|
||
}
|
||
async ['_loadAccountsFromDB']() {
|
||
try {
|
||
0x0;
|
||
const ref311 = account_1.getCursorPaths();
|
||
const {
|
||
dbPath: ref312
|
||
} = ref311;
|
||
if (!fs.existsSync(ref312)) {
|
||
return [];
|
||
}
|
||
0x0;
|
||
const ref313 = await sqlite_1.sqliteGet(ref312, "cursorAuth/accessToken");
|
||
0x0;
|
||
const ref314 = await sqlite_1.sqliteGet(ref312, "cursorAuth/refreshToken");
|
||
0x0;
|
||
const ref315 = await sqlite_1.sqliteGet(ref312, "cursorAuth/cachedEmail");
|
||
if (ref313 && ref315) {
|
||
return [{
|
||
'email': ref315,
|
||
'access_token': ref313,
|
||
'refresh_token': ref314 || ref313
|
||
}];
|
||
}
|
||
return [];
|
||
} catch (ref316) {
|
||
console.error("[CursorPro] 读取账号失败:", ref316);
|
||
return [];
|
||
}
|
||
}
|
||
async ["_sendState"]() {
|
||
const ref317 = this._context.globalState.get("cursorpro.key");
|
||
const ref318 = this._context.globalState.get('cursorpro.expireDate');
|
||
const ref319 = this._context.globalState.get('cursorpro.switchRemaining');
|
||
const ref320 = this._context.globalState.get("cursorpro.switchLimit");
|
||
const ref321 = await this._getCursorVersion();
|
||
0x0;
|
||
const ref322 = client_1.getOnlineStatus();
|
||
this._postMessage({
|
||
'type': "state",
|
||
'isActivated': !!ref317,
|
||
'key': ref317 || '',
|
||
'expireDate': ref318 || '',
|
||
'switchRemaining': ref319 ?? 0x0,
|
||
'switchLimit': ref320 ?? 0x64,
|
||
'cursorVersion': ref321,
|
||
'isOnline': ref322
|
||
});
|
||
}
|
||
async ["_handleRetryConnect"]() {
|
||
try {
|
||
const ref323 = this._context.globalState.get('cursorpro.key');
|
||
if (ref323) {
|
||
0x0;
|
||
await client_1.verifyKey(ref323);
|
||
} else {
|
||
0x0;
|
||
const ref324 = client_1.getApiUrl() + '/api/announcements/latest';
|
||
await fetch(ref324, {
|
||
'method': 'GET'
|
||
});
|
||
}
|
||
await this._sendState();
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': true
|
||
});
|
||
} catch (ref325) {
|
||
console.error("[CursorPro] Retry connect failed:", ref325);
|
||
this._postMessage({
|
||
'type': "networkStatus",
|
||
'online': false
|
||
});
|
||
}
|
||
}
|
||
async ["_getCursorVersion"]() {
|
||
try {
|
||
const ref326 = process.platform;
|
||
const ref327 = [];
|
||
const ref328 = await this._getCursorInstallPath();
|
||
if (ref328) {
|
||
if (ref326 === "darwin") {
|
||
ref327.push(path.join(ref328, "Contents", "Resources", 'app', 'package.json'));
|
||
} else {
|
||
ref327.push(path.join(ref328, "resources", 'app', "package.json"));
|
||
}
|
||
}
|
||
if (ref326 === "win32") {
|
||
const ref329 = process.env.LOCALAPPDATA || '';
|
||
const ref330 = process.env.USERPROFILE || '';
|
||
const ref331 = process.env.ProgramFiles || "C:\\Program Files";
|
||
const ref332 = process.env['ProgramFiles(x86)'] || "C:\\Program Files (x86)";
|
||
ref327.push(path.join(ref329, "Programs", "Cursor", "resources", "app", "package.json"), path.join(ref329, "Programs", "cursor", "resources", 'app', "package.json"), path.join(ref330, "AppData", "Local", "Programs", "Cursor", "resources", 'app', "package.json"), path.join(ref331, "Cursor", "resources", 'app', "package.json"), path.join(ref331, "cursor", "resources", 'app', "package.json"), path.join(ref332, "Cursor", "resources", 'app', "package.json"));
|
||
} else {
|
||
if (ref326 === "darwin") {
|
||
ref327.push('/Applications/Cursor.app/Contents/Resources/app/package.json');
|
||
} else {
|
||
const ref333 = process.env.HOME || '';
|
||
ref327.push("/usr/share/cursor/resources/app/package.json", "/opt/Cursor/resources/app/package.json", "/opt/cursor/resources/app/package.json", path.join(ref333, ".local", 'share', "cursor", "resources", 'app', "package.json"));
|
||
}
|
||
}
|
||
for (const ref334 of ref327) {
|
||
try {
|
||
if (fs.existsSync(ref334)) {
|
||
const ref335 = fs.readFileSync(ref334, "utf-8");
|
||
const ref336 = JSON.parse(ref335);
|
||
if (ref336.version) {
|
||
console.log("[CursorPro] 找到 Cursor 版本:", ref336.version, "路径:", ref334);
|
||
return ref336.version;
|
||
}
|
||
}
|
||
} catch (ref337) {
|
||
console.log("[CursorPro] 尝试路径失败:", ref334, ref337);
|
||
}
|
||
}
|
||
try {
|
||
const ref338 = require("vscode");
|
||
if (ref338.version) {
|
||
console.log("[CursorPro] 使用 VS Code API 获取版本:", ref338.version);
|
||
return ref338.version;
|
||
}
|
||
} catch (ref339) {}
|
||
console.log("[CursorPro] 未找到 Cursor 版本,尝试的路径:", ref327);
|
||
return '未知';
|
||
} catch (ref340) {
|
||
console.error("[CursorPro] 获取 Cursor 版本失败:", ref340);
|
||
return '未知';
|
||
}
|
||
}
|
||
['_postMessage'](ref341) {
|
||
this._view?.["webview"]["postMessage"](ref341);
|
||
}
|
||
["_getNonce"]() {
|
||
let ref342 = '';
|
||
for (let ref343 = 0x0; ref343 < 0x20; ref343++) {
|
||
ref342 += 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.charAt(Math.floor(Math.random() * 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.length));
|
||
}
|
||
return ref342;
|
||
}
|
||
["_getHtmlContent"](ref344) {
|
||
const ref345 = 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-" + ref345 + "'; img-src " + ref344.cspSource + " https: data:; font-src " + ref344.cspSource + `; worker-src 'none';">
|
||
<title>CursorPro</title>
|
||
<script nonce="` + ref345 + `">
|
||
// 尽早清理 Service Worker(在 head 中执行,比 body 更早)
|
||
if ('serviceWorker' in navigator) {
|
||
navigator.serviceWorker.getRegistrations().then(function(regs) {
|
||
regs.forEach(function(reg) { reg.unregister(); });
|
||
}).catch(function() {});
|
||
}
|
||
</script>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||
background: #1e1e1e;
|
||
color: #cccccc;
|
||
padding: 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.section {
|
||
margin-bottom: 16px;
|
||
padding: 12px;
|
||
background: #252526;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
font-size: 13px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.section-title .icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.status-badge {
|
||
margin-left: auto;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.status-badge.inactive {
|
||
background: #6e3232;
|
||
color: #ff6b6b;
|
||
}
|
||
|
||
.status-badge.active {
|
||
background: #2d4a3e;
|
||
color: #4ade80;
|
||
}
|
||
|
||
.input-group {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
input[type="text"] {
|
||
flex: 1;
|
||
padding: 8px 12px;
|
||
background: #3c3c3c;
|
||
border: 1px solid #4a4a4a;
|
||
border-radius: 4px;
|
||
color: #ffffff;
|
||
font-size: 13px;
|
||
}
|
||
|
||
input[type="text"]::placeholder {
|
||
color: #888888;
|
||
}
|
||
|
||
input[type="text"]:focus {
|
||
outline: none;
|
||
border-color: #007acc;
|
||
}
|
||
|
||
.btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.btn:hover {
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #007acc;
|
||
color: white;
|
||
}
|
||
|
||
.btn-purple {
|
||
background: #8b5cf6;
|
||
color: white;
|
||
}
|
||
|
||
.btn-blue {
|
||
background: #3b82f6;
|
||
color: white;
|
||
}
|
||
|
||
.btn-red {
|
||
background: #ef4444;
|
||
color: white;
|
||
}
|
||
|
||
.btn-block {
|
||
display: block;
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid #3c3c3c;
|
||
}
|
||
|
||
.info-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.info-label {
|
||
color: #888888;
|
||
}
|
||
|
||
.info-value {
|
||
color: #ffffff;
|
||
}
|
||
|
||
.usage-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.usage-row:last-of-type {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.usage-item {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 6px 10px;
|
||
background: #2d2d2d;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.switch-container {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.switch {
|
||
position: relative;
|
||
width: 40px;
|
||
height: 20px;
|
||
}
|
||
|
||
.switch input {
|
||
opacity: 0;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.slider {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: #4a4a4a;
|
||
border-radius: 20px;
|
||
transition: 0.3s;
|
||
}
|
||
|
||
.slider:before {
|
||
position: absolute;
|
||
content: "";
|
||
height: 16px;
|
||
width: 16px;
|
||
left: 2px;
|
||
bottom: 2px;
|
||
background-color: white;
|
||
border-radius: 50%;
|
||
transition: 0.3s;
|
||
}
|
||
|
||
input:checked + .slider {
|
||
background-color: #8b5cf6;
|
||
}
|
||
|
||
input:checked + .slider:before {
|
||
transform: translateX(20px);
|
||
}
|
||
|
||
/* 小尺寸开关样式 */
|
||
.switch-sm {
|
||
position: relative;
|
||
width: 32px;
|
||
height: 16px;
|
||
}
|
||
|
||
.switch-sm .slider:before {
|
||
height: 12px;
|
||
width: 12px;
|
||
left: 2px;
|
||
bottom: 2px;
|
||
}
|
||
|
||
.switch-sm input:checked + .slider:before {
|
||
transform: translateX(16px);
|
||
}
|
||
|
||
.pro-badge {
|
||
background: linear-gradient(90deg, #8b5cf6, #d946ef);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-size: 10px;
|
||
font-weight: bold;
|
||
color: white;
|
||
}
|
||
|
||
.footer {
|
||
margin-top: 16px;
|
||
padding: 12px;
|
||
background: linear-gradient(135deg, rgba(60, 60, 60, 0.3) 0%, rgba(40, 40, 40, 0.5) 100%);
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.footer-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.auto-start {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 11px;
|
||
color: #888;
|
||
}
|
||
|
||
.cursor-version {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 11px;
|
||
color: #666;
|
||
padding: 4px 10px;
|
||
background: rgba(0, 0, 0, 0.2);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.cursor-version .version-num {
|
||
color: #a78bfa;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 自定义弹窗样式 */
|
||
.modal-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.85);
|
||
backdrop-filter: blur(4px);
|
||
z-index: 1000;
|
||
justify-content: center;
|
||
align-items: center;
|
||
animation: fadeIn 0.2s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: scale(0.9) translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: scale(1) translateY(0);
|
||
}
|
||
}
|
||
|
||
.modal-overlay.show {
|
||
display: flex;
|
||
}
|
||
|
||
.modal-content {
|
||
background: linear-gradient(145deg, #1e1e1e 0%, #2a2a2a 100%);
|
||
border-radius: 12px;
|
||
padding: 16px 20px;
|
||
max-width: 260px;
|
||
width: 90%;
|
||
text-align: center;
|
||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.05);
|
||
animation: slideIn 0.2s ease;
|
||
}
|
||
|
||
.modal-icon {
|
||
width: 44px;
|
||
height: 44px;
|
||
margin: 0 auto 12px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22px;
|
||
}
|
||
|
||
.modal-icon.warning {
|
||
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
|
||
}
|
||
|
||
.modal-icon.success {
|
||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.modal-message {
|
||
font-size: 12px;
|
||
color: #9ca3af;
|
||
margin-bottom: 16px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.modal-buttons {
|
||
display: flex;
|
||
gap: 8px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
|
||
.modal-btn.primary {
|
||
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
|
||
color: white;
|
||
box-shadow: 0 2px 8px rgba(139, 92, 246, 0.4);
|
||
}
|
||
|
||
.modal-btn.primary:hover {
|
||
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.5);
|
||
}
|
||
|
||
.modal-btn.secondary {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
color: #9ca3af;
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.modal-btn.secondary:hover {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
color: #fff;
|
||
}
|
||
|
||
.modal-btn.single {
|
||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||
color: white;
|
||
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
|
||
min-width: 100px;
|
||
}
|
||
|
||
.modal-btn.single:hover {
|
||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);
|
||
}
|
||
|
||
.highlight {
|
||
color: #4ade80;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.key-display {
|
||
cursor: pointer;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.key-display:hover {
|
||
color: #007acc;
|
||
}
|
||
|
||
.key-display.copied {
|
||
color: #4ade80 !important;
|
||
}
|
||
|
||
/* Loading 状态样式 */
|
||
.btn.loading {
|
||
position: relative;
|
||
pointer-events: none;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.btn.loading .btn-text {
|
||
visibility: hidden;
|
||
}
|
||
|
||
.btn.loading::after {
|
||
content: '';
|
||
position: absolute;
|
||
width: 16px;
|
||
height: 16px;
|
||
top: 50%;
|
||
left: 50%;
|
||
margin-left: -8px;
|
||
margin-top: -8px;
|
||
border: 2px solid transparent;
|
||
border-top-color: #fff;
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.refresh-btn.loading {
|
||
animation: spin 1s linear infinite;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* 公告样式 */
|
||
.announcement-badge {
|
||
margin-left: auto;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.announcement-badge.info {
|
||
background: #1e3a5f;
|
||
color: #60a5fa;
|
||
}
|
||
|
||
.announcement-badge.warning {
|
||
background: #5c4a1f;
|
||
color: #fbbf24;
|
||
}
|
||
|
||
.announcement-badge.error {
|
||
background: #6e3232;
|
||
color: #f87171;
|
||
}
|
||
|
||
.announcement-badge.success {
|
||
background: #2d4a3e;
|
||
color: #4ade80;
|
||
}
|
||
|
||
.announcement-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
margin-bottom: 8px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.announcement-content {
|
||
font-size: 12px;
|
||
color: #b0b0b0;
|
||
line-height: 1.6;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.announcement-link {
|
||
color: #60a5fa;
|
||
text-decoration: none;
|
||
border-bottom: 1px dashed #60a5fa;
|
||
transition: all 0.2s;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.announcement-link:hover {
|
||
color: #93c5fd;
|
||
border-bottom-color: #93c5fd;
|
||
}
|
||
|
||
/* Toast 通知样式 */
|
||
.toast-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 12px;
|
||
pointer-events: none;
|
||
z-index: 2000;
|
||
}
|
||
|
||
.toast {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 16px;
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||
border: 1px solid rgba(74, 222, 128, 0.3);
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 20px rgba(74, 222, 128, 0.1);
|
||
transform: translateY(-100px);
|
||
opacity: 0;
|
||
transition: all 0.3s ease;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.toast.show {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.toast-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.toast-message {
|
||
font-size: 12px;
|
||
color: #e0e0e0;
|
||
max-width: 280px;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* 离线状态提示样式 */
|
||
.offline-banner {
|
||
display: none;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 14px;
|
||
margin-bottom: 12px;
|
||
background: linear-gradient(135deg, #7f1d1d 0%, #991b1b 100%);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
border-radius: 8px;
|
||
animation: slideDown 0.3s ease;
|
||
}
|
||
|
||
.offline-banner.show {
|
||
display: flex;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.offline-banner .offline-icon {
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.offline-banner .offline-text {
|
||
flex: 1;
|
||
}
|
||
|
||
.offline-banner .offline-title {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #fca5a5;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.offline-banner .offline-desc {
|
||
font-size: 11px;
|
||
color: #fecaca;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.offline-banner .retry-btn {
|
||
padding: 4px 10px;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 4px;
|
||
color: #fff;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.offline-banner .retry-btn:hover {
|
||
background: rgba(255, 255, 255, 0.25);
|
||
}
|
||
|
||
.offline-banner .retry-btn.loading {
|
||
pointer-events: none;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* 顶部更新提醒条 */
|
||
.update-banner {
|
||
position: sticky;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
|
||
color: #fff;
|
||
padding: 8px 12px;
|
||
font-size: 12px;
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
z-index: 1000;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||
}
|
||
.update-banner.show {
|
||
display: flex;
|
||
}
|
||
.update-banner .update-icon {
|
||
font-size: 14px;
|
||
}
|
||
.update-banner .update-text {
|
||
font-weight: 500;
|
||
}
|
||
.update-banner .update-version {
|
||
background: rgba(255,255,255,0.2);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
}
|
||
.update-banner .update-close {
|
||
margin-left: auto;
|
||
background: none;
|
||
border: none;
|
||
color: #fff;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
padding: 0 4px;
|
||
opacity: 0.8;
|
||
}
|
||
.update-banner .update-close:hover {
|
||
opacity: 1;
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 顶部更新提醒条 -->
|
||
<div class="update-banner" id="updateBanner">
|
||
<span class="update-icon">🚀</span>
|
||
<span class="update-text">发现新版本</span>
|
||
<span class="update-version" id="updateBannerVersion">v0.0</span>
|
||
<button class="update-close" id="updateBannerClose" title="关闭">×</button>
|
||
</div>
|
||
|
||
<!-- 管理员权限提示弹窗 -->
|
||
<div class="modal-overlay" id="adminModal">
|
||
<div class="modal-content">
|
||
<div class="modal-icon warning">🔐</div>
|
||
<div class="modal-title">需要管理员权限</div>
|
||
<div class="modal-message">
|
||
请关闭 Cursor,右键点击图标<br>
|
||
选择 <span class="highlight">以管理员身份运行</span>
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn single" id="adminModalClose">我知道了</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 重置机器码权限提示弹窗 -->
|
||
<div class="modal-overlay" id="resetPermissionModal">
|
||
<div class="modal-content">
|
||
<div class="modal-icon warning">🔐</div>
|
||
<div class="modal-title">需要管理员权限</div>
|
||
<div class="modal-message" style="text-align: left; line-height: 1.8;">
|
||
重置机器码需要管理员权限才能完整执行。<br><br>
|
||
请按以下步骤操作:<br>
|
||
<span style="color: #fbbf24;">1.</span> 完全关闭 Cursor<br>
|
||
<span style="color: #fbbf24;">2.</span> 右键点击 Cursor 图标<br>
|
||
<span style="color: #fbbf24;">3.</span> 选择 <span class="highlight">以管理员身份运行</span><br>
|
||
<span style="color: #fbbf24;">4.</span> 再次点击重置机器码
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn single" id="resetPermissionClose">我知道了</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 重启提示弹窗 -->
|
||
<div class="modal-overlay" id="restartModal">
|
||
<div class="modal-content">
|
||
<div class="modal-icon success">✓</div>
|
||
<div class="modal-title" id="restartModalTitle">操作成功</div>
|
||
<div class="modal-message">
|
||
需要重启 Cursor 才能生效
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn primary" id="restartNowBtn">立即重启</button>
|
||
<button class="modal-btn secondary" id="restartLaterBtn">稍后</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 激活码过期弹窗 -->
|
||
<div class="modal-overlay" id="expiredModal">
|
||
<div class="modal-content">
|
||
<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>
|
||
<div class="modal-title">激活码已过期</div>
|
||
<div class="modal-message">
|
||
您的激活码已过期,请续费后继续使用
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn single" id="expiredModalClose">我知道了</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 清理环境确认弹窗 -->
|
||
<div class="modal-overlay" id="cleanEnvModal">
|
||
<div class="modal-content">
|
||
<div class="modal-icon warning">⚠️</div>
|
||
<div class="modal-title">清理 Cursor 环境</div>
|
||
<div class="modal-message">
|
||
此操作会删除所有配置和登录信息<br>确定要继续吗?
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn primary" id="cleanEnvConfirmBtn">确定清理</button>
|
||
<button class="modal-btn secondary" id="cleanEnvCancelBtn">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 换号确认弹窗 -->
|
||
<div class="modal-overlay" id="switchConfirmModal">
|
||
<div class="modal-content">
|
||
<div class="modal-icon warning">💰</div>
|
||
<div class="modal-title">账号未使用完</div>
|
||
<div class="modal-message">
|
||
当前账号 <span id="switchConfirmEmail" style="color:#4caf50;"></span><br>
|
||
已用额度: <span id="switchConfirmCost" style="color:#ff9800;font-weight:bold;">$0.00</span> (不足 $10)<br><br>
|
||
确定要换号吗?
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button class="modal-btn primary" id="switchConfirmBtn">确认换号</button>
|
||
<button class="modal-btn secondary" id="switchCancelBtn">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 离线状态提示 -->
|
||
<div class="offline-banner" id="offlineBanner">
|
||
<span class="offline-icon">📡</span>
|
||
<div class="offline-text">
|
||
<div class="offline-title">网络连接失败</div>
|
||
<div class="offline-desc">请检查网络后重试</div>
|
||
</div>
|
||
<button class="retry-btn" id="retryConnectBtn">重试</button>
|
||
</div>
|
||
|
||
<!-- 软件授权 -->
|
||
<div class="section">
|
||
<div class="section-title">
|
||
<span class="icon">🔐</span>
|
||
<span>软件授权</span>
|
||
<span class="status-badge" id="authStatus">未授权</span>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<input type="text" id="keyInput" placeholder="请输入CDK激活码">
|
||
<button class="btn btn-primary" id="activateBtn"><span class="btn-text">激活</span></button>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<span class="info-label">激活码</span>
|
||
<span class="info-value key-display" id="keyDisplay" title="点击复制">尚未激活</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">到期时间</span>
|
||
<span class="info-value" id="expireDate">尚未激活</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 账号数据 (已隐藏) -->
|
||
<div class="section" style="display:none;">
|
||
<div class="section-title">
|
||
<span class="icon">👤</span>
|
||
<span>账号数据</span>
|
||
<span class="status-badge" id="accountStatus">未激活</span>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<span class="info-label">CI积分余额</span>
|
||
<span class="info-value">0 <button style="background:none;border:none;color:#007acc;cursor:pointer;">🔄</button></span>
|
||
</div>
|
||
|
||
<button class="btn btn-purple btn-block" id="switchBtn" disabled>换号</button>
|
||
<button class="btn btn-blue btn-block" id="resetBtn">重置机器码</button>
|
||
<button class="btn btn-blue btn-block" id="disableUpdateBtn">禁用自动更新</button>
|
||
<button class="btn btn-blue btn-block" id="cleanEnvBtn">清理Cursor环境</button>
|
||
<button class="btn btn-red btn-block" id="disableBtn">停用插件</button>
|
||
</div>
|
||
|
||
<!-- 无感换号 -->
|
||
<div class="section">
|
||
<div class="section-title">
|
||
<span class="icon">⚡</span>
|
||
<span>无感换号</span>
|
||
<span class="status-badge" id="seamlessStatus">未启用</span>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<span class="info-label">积分</span>
|
||
<span class="info-value" id="seamlessSwitchRemaining">0</span>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<span class="info-label">当前账号</span>
|
||
<span class="info-value" style="font-size:11px;" id="seamlessCurrentAccount">未分配</span>
|
||
</div>
|
||
|
||
<div class="switch-container" style="margin: 12px 0;">
|
||
<span>免魔法模式</span>
|
||
<span class="pro-badge">PRO</span>
|
||
<span style="margin-left: auto; color: #888; font-size: 11px;"></span>
|
||
<label class="switch">
|
||
<input type="checkbox" id="seamlessProxySwitch">
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<button class="btn btn-purple btn-block" id="enableSeamlessBtn" disabled><span class="btn-text">启用无感换号</span></button>
|
||
<button class="btn btn-red btn-block" id="seamlessResetMachineBtn" style="display:none;"><span class="btn-text">重置机器码</span></button>
|
||
<button class="btn btn-red btn-block" id="disableSeamlessBtn" style="display:none;"><span class="btn-text">禁用无感换号</span></button>
|
||
<button class="btn btn-blue btn-block" id="manualSwitchBtn" style="display:none;" disabled><span class="btn-text">一键换号(扣1积分)</span></button>
|
||
</div>
|
||
|
||
<!-- 账号用量 -->
|
||
<div class="section" id="usageSection" style="display:none;">
|
||
<div class="section-title">
|
||
<span class="icon">📊</span>
|
||
<span>账号用量</span>
|
||
<button class="btn" style="margin-left:auto;padding:4px 8px;font-size:11px;background:#3c3c3c;" id="refreshUsageBtn">🔄</button>
|
||
</div>
|
||
|
||
<div class="usage-row">
|
||
<div class="usage-item">
|
||
<span class="info-label">会员类型</span>
|
||
<span class="info-value" id="usageMemberType">-</span>
|
||
</div>
|
||
<div class="usage-item">
|
||
<span class="info-label">试用剩余</span>
|
||
<span class="info-value" id="usageTrialDays">-</span>
|
||
</div>
|
||
</div>
|
||
<div class="usage-row">
|
||
<div class="usage-item">
|
||
<span class="info-label">请求次数</span>
|
||
<span class="info-value" id="usageRequestCount">-</span>
|
||
</div>
|
||
<div class="usage-item">
|
||
<span class="info-label">已用额度</span>
|
||
<span class="info-value" id="usageCostUSD">-</span>
|
||
</div>
|
||
</div>
|
||
<p style="font-size:10px;color:#666;margin-top:8px;text-align:center;" id="usageUpdateTime">-</p>
|
||
</div>
|
||
|
||
<!-- 公告 -->
|
||
<div class="section" id="announcementSection" style="display:none;">
|
||
<div class="section-title">
|
||
<span class="icon" id="announcementIcon">📢</span>
|
||
<span>公告</span>
|
||
<span class="announcement-badge" id="announcementBadge">info</span>
|
||
</div>
|
||
<div class="announcement-title" id="announcementTitle"></div>
|
||
<div class="announcement-content" id="announcementContent"></div>
|
||
<p style="font-size:10px;color:#666;margin-top:8px;text-align:right;" id="announcementTime"></p>
|
||
</div>
|
||
|
||
<!-- 版本信息 -->
|
||
<div class="section" id="versionSection">
|
||
<div class="section-title">
|
||
<span class="icon">📦</span>
|
||
<span>版本信息</span>
|
||
<span class="status-badge" id="versionStatus" style="display:none;">有更新</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">当前版本</span>
|
||
<span class="info-value" id="currentVersion">-</span>
|
||
</div>
|
||
<div class="info-row" id="latestVersionRow" style="display:none;">
|
||
<span class="info-label">最新版本</span>
|
||
<span class="info-value" id="latestVersion" style="color:#4caf50;">-</span>
|
||
</div>
|
||
<p id="updateHint" style="font-size:11px;color:#ff9800;margin-top:8px;display:none;">
|
||
⚠️ 发现新版本,请更新插件以获取最新功能
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 页脚 -->
|
||
<div class="footer">
|
||
<div class="footer-row">
|
||
<div class="auto-start">
|
||
<span>自动启动</span>
|
||
<label class="switch switch-sm">
|
||
<input type="checkbox" id="autoStartSwitch" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="cursor-version">
|
||
<span>Cursor</span>
|
||
<span class="version-num" id="cursorVersion">0.0.0</span>
|
||
</div>
|
||
</div>
|
||
<div class="footer-row" style="margin-top: 8px;">
|
||
<div style="font-size: 10px; color: #666; word-break: break-all;">
|
||
<span>路径: </span>
|
||
<span id="cursorPath" style="color: #888;">获取中...</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toast 通知 -->
|
||
<div class="toast-container" id="toastContainer">
|
||
<div class="toast" id="toast">
|
||
<span class="toast-icon" id="toastIcon">✅</span>
|
||
<span class="toast-message" id="toastMessage"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<script nonce="` + ref345 + `">
|
||
const vscode = acquireVsCodeApi();
|
||
|
||
// 元素引用
|
||
const keyInput = document.getElementById('keyInput');
|
||
const activateBtn = document.getElementById('activateBtn');
|
||
const switchBtn = document.getElementById('switchBtn');
|
||
const resetBtn = document.getElementById('resetBtn');
|
||
const disableUpdateBtn = document.getElementById('disableUpdateBtn');
|
||
const cleanEnvBtn = document.getElementById('cleanEnvBtn');
|
||
const disableBtn = document.getElementById('disableBtn');
|
||
const authStatus = document.getElementById('authStatus');
|
||
const accountStatus = document.getElementById('accountStatus');
|
||
const keyDisplay = document.getElementById('keyDisplay');
|
||
const switchCount = document.getElementById('switchCount');
|
||
const expireDate = document.getElementById('expireDate');
|
||
const cursorVersion = document.getElementById('cursorVersion');
|
||
const cursorPath = document.getElementById('cursorPath');
|
||
|
||
// 离线状态元素
|
||
const offlineBanner = document.getElementById('offlineBanner');
|
||
const retryConnectBtn = document.getElementById('retryConnectBtn');
|
||
|
||
// 无感换号元素
|
||
const seamlessStatus = document.getElementById('seamlessStatus');
|
||
const seamlessProxySwitch = document.getElementById('seamlessProxySwitch');
|
||
const enableSeamlessBtn = document.getElementById('enableSeamlessBtn');
|
||
const disableSeamlessBtn = document.getElementById('disableSeamlessBtn');
|
||
const manualSwitchBtn = document.getElementById('manualSwitchBtn');
|
||
const seamlessResetMachineBtn = document.getElementById('seamlessResetMachineBtn');
|
||
const seamlessSwitchRemaining = document.getElementById('seamlessSwitchRemaining');
|
||
const seamlessCurrentAccount = document.getElementById('seamlessCurrentAccount');
|
||
|
||
// 用量显示元素
|
||
const usageSection = document.getElementById('usageSection');
|
||
const refreshUsageBtn = document.getElementById('refreshUsageBtn');
|
||
const usageMemberType = document.getElementById('usageMemberType');
|
||
const usageTrialDays = document.getElementById('usageTrialDays');
|
||
const usageRequestCount = document.getElementById('usageRequestCount');
|
||
const usageCostUSD = document.getElementById('usageCostUSD');
|
||
const usageUpdateTime = document.getElementById('usageUpdateTime');
|
||
|
||
// 公告元素
|
||
const announcementSection = document.getElementById('announcementSection');
|
||
const announcementIcon = document.getElementById('announcementIcon');
|
||
const announcementBadge = document.getElementById('announcementBadge');
|
||
const announcementTitle = document.getElementById('announcementTitle');
|
||
const announcementContent = document.getElementById('announcementContent');
|
||
const announcementTime = document.getElementById('announcementTime');
|
||
|
||
// 版本元素
|
||
const versionSection = document.getElementById('versionSection');
|
||
const versionStatus = document.getElementById('versionStatus');
|
||
const currentVersionEl = document.getElementById('currentVersion');
|
||
const latestVersionEl = document.getElementById('latestVersion');
|
||
const latestVersionRow = document.getElementById('latestVersionRow');
|
||
const updateHint = document.getElementById('updateHint');
|
||
|
||
// 顶部更新提醒条
|
||
const updateBanner = document.getElementById('updateBanner');
|
||
const updateBannerVersion = document.getElementById('updateBannerVersion');
|
||
const updateBannerClose = document.getElementById('updateBannerClose');
|
||
|
||
// Toast 元素
|
||
const toast = document.getElementById('toast');
|
||
const toastIcon = document.getElementById('toastIcon');
|
||
const toastMessage = document.getElementById('toastMessage');
|
||
let toastTimer = null;
|
||
|
||
// 显示 Toast 通知
|
||
function showToast(message, icon = '✅', duration = 10000) {
|
||
// 清除之前的定时器
|
||
if (toastTimer) {
|
||
clearTimeout(toastTimer);
|
||
}
|
||
|
||
toastIcon.textContent = icon;
|
||
toastMessage.textContent = message;
|
||
toast.classList.add('show');
|
||
|
||
// 设置自动隐藏
|
||
toastTimer = setTimeout(() => {
|
||
toast.classList.remove('show');
|
||
}, duration);
|
||
}
|
||
|
||
// 禁用换号按钮并显示倒计时
|
||
let switchBtnCountdownTimer = null;
|
||
const originalSwitchBtnText = '一键换号(扣1积分)';
|
||
|
||
function disableSwitchBtnWithCountdown(seconds) {
|
||
// 清除之前的定时器
|
||
if (switchBtnCountdownTimer) {
|
||
clearInterval(switchBtnCountdownTimer);
|
||
}
|
||
|
||
let remaining = seconds;
|
||
manualSwitchBtn.disabled = true;
|
||
manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';
|
||
|
||
switchBtnCountdownTimer = setInterval(() => {
|
||
remaining--;
|
||
if (remaining <= 0) {
|
||
clearInterval(switchBtnCountdownTimer);
|
||
switchBtnCountdownTimer = null;
|
||
manualSwitchBtn.disabled = false;
|
||
manualSwitchBtn.querySelector('.btn-text').textContent = originalSwitchBtnText;
|
||
} else {
|
||
manualSwitchBtn.querySelector('.btn-text').textContent = remaining + '秒后可用';
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
// 弹窗元素
|
||
const adminModal = document.getElementById('adminModal');
|
||
const adminModalClose = document.getElementById('adminModalClose');
|
||
const resetPermissionModal = document.getElementById('resetPermissionModal');
|
||
const resetPermissionClose = document.getElementById('resetPermissionClose');
|
||
const restartModal = document.getElementById('restartModal');
|
||
const restartModalTitle = document.getElementById('restartModalTitle');
|
||
const restartNowBtn = document.getElementById('restartNowBtn');
|
||
const restartLaterBtn = document.getElementById('restartLaterBtn');
|
||
const expiredModal = document.getElementById('expiredModal');
|
||
const expiredModalClose = document.getElementById('expiredModalClose');
|
||
const cleanEnvModal = document.getElementById('cleanEnvModal');
|
||
const cleanEnvConfirmBtn = document.getElementById('cleanEnvConfirmBtn');
|
||
const cleanEnvCancelBtn = document.getElementById('cleanEnvCancelBtn');
|
||
|
||
// 换号确认弹窗元素
|
||
const switchConfirmModal = document.getElementById('switchConfirmModal');
|
||
const switchConfirmEmail = document.getElementById('switchConfirmEmail');
|
||
const switchConfirmCost = document.getElementById('switchConfirmCost');
|
||
const switchConfirmBtn = document.getElementById('switchConfirmBtn');
|
||
const switchCancelBtn = document.getElementById('switchCancelBtn');
|
||
|
||
// 显示管理员权限弹窗
|
||
function showAdminModal() {
|
||
adminModal.classList.add('show');
|
||
}
|
||
|
||
// 显示重置机器码权限提示弹窗
|
||
function showAdminPermissionModal() {
|
||
resetPermissionModal.classList.add('show');
|
||
}
|
||
|
||
// 重置机器码权限弹窗 - 关闭按钮
|
||
resetPermissionClose.addEventListener('click', () => {
|
||
resetPermissionModal.classList.remove('show');
|
||
});
|
||
|
||
// 点击遮罩关闭权限提示弹窗
|
||
resetPermissionModal.addEventListener('click', (e) => {
|
||
if (e.target === resetPermissionModal) {
|
||
resetPermissionModal.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// 显示重启提示弹窗
|
||
let restartModalAction = 'reload'; // 'reload' 或 'close'
|
||
|
||
function showRestartModal(title, action = 'reload') {
|
||
restartModalTitle.textContent = title || '操作成功';
|
||
restartModalAction = action;
|
||
// 根据操作类型更新按钮文字
|
||
restartNowBtn.textContent = action === 'close' ? '立即关闭 Cursor' : '立即重启';
|
||
restartModal.classList.add('show');
|
||
}
|
||
|
||
// 显示过期弹窗
|
||
function showExpiredModal() {
|
||
expiredModal.classList.add('show');
|
||
}
|
||
|
||
// 关闭管理员弹窗
|
||
adminModalClose.addEventListener('click', () => {
|
||
adminModal.classList.remove('show');
|
||
});
|
||
|
||
// 点击遮罩关闭管理员弹窗
|
||
adminModal.addEventListener('click', (e) => {
|
||
if (e.target === adminModal) {
|
||
adminModal.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// 立即重启/关闭按钮
|
||
restartNowBtn.addEventListener('click', () => {
|
||
restartModal.classList.remove('show');
|
||
if (restartModalAction === 'close') {
|
||
// 完全关闭 Cursor
|
||
vscode.postMessage({ type: 'closeCursor' });
|
||
} else {
|
||
// 重新加载窗口
|
||
vscode.postMessage({ type: 'reloadWindow' });
|
||
}
|
||
});
|
||
|
||
// 稍后手动按钮
|
||
restartLaterBtn.addEventListener('click', () => {
|
||
restartModal.classList.remove('show');
|
||
});
|
||
|
||
// 点击遮罩关闭重启弹窗
|
||
restartModal.addEventListener('click', (e) => {
|
||
if (e.target === restartModal) {
|
||
restartModal.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// 关闭过期弹窗
|
||
expiredModalClose.addEventListener('click', () => {
|
||
expiredModal.classList.remove('show');
|
||
});
|
||
|
||
// 点击遮罩关闭过期弹窗
|
||
expiredModal.addEventListener('click', (e) => {
|
||
if (e.target === expiredModal) {
|
||
expiredModal.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// 当前账号邮箱(用于查询用量)
|
||
let currentAccountEmail = '';
|
||
let usageRefreshInterval = null;
|
||
// 存储完整激活码(用于复制)
|
||
let fullActivationKey = '';
|
||
// 当前剩余换号次数
|
||
let currentSwitchRemaining = 0;
|
||
// 当前到期时间
|
||
let currentExpireDate = '';
|
||
|
||
// 检查卡密是否已过期
|
||
function isKeyExpired() {
|
||
if (!currentExpireDate) return true;
|
||
try {
|
||
const expireTime = new Date(currentExpireDate).getTime();
|
||
return Date.now() > expireTime;
|
||
} catch {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 格式化到期时间为北京时间
|
||
function formatExpireDate(dateStr) {
|
||
if (!dateStr) return '';
|
||
try {
|
||
// 后端返回的时间没有时区标识,假设是 UTC 时间
|
||
// 将空格替换为T,并添加Z表示UTC
|
||
let utcStr = dateStr;
|
||
if (!dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {
|
||
utcStr = dateStr.replace(' ', 'T') + 'Z';
|
||
}
|
||
const date = new Date(utcStr);
|
||
|
||
// 使用中国时区格式化(UTC+8)
|
||
return date.toLocaleString('zh-CN', {
|
||
timeZone: 'Asia/Shanghai',
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit',
|
||
hour12: false
|
||
});
|
||
} catch {
|
||
return dateStr; // 格式化失败返回原始值
|
||
}
|
||
}
|
||
|
||
// 隐藏激活码后几位
|
||
function maskKey(key) {
|
||
if (!key || key.length <= 8) return key;
|
||
return key.substring(0, key.length - 4) + '****';
|
||
}
|
||
|
||
// 点击激活码复制
|
||
keyDisplay.addEventListener('click', () => {
|
||
if (!fullActivationKey) return;
|
||
navigator.clipboard.writeText(fullActivationKey).then(() => {
|
||
keyDisplay.classList.add('copied');
|
||
const originalText = keyDisplay.textContent;
|
||
keyDisplay.textContent = '已复制!';
|
||
setTimeout(() => {
|
||
keyDisplay.textContent = maskKey(fullActivationKey);
|
||
keyDisplay.classList.remove('copied');
|
||
}, 1000);
|
||
}).catch(() => {
|
||
// 降级方案
|
||
const textarea = document.createElement('textarea');
|
||
textarea.value = fullActivationKey;
|
||
document.body.appendChild(textarea);
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textarea);
|
||
keyDisplay.classList.add('copied');
|
||
keyDisplay.textContent = '已复制!';
|
||
setTimeout(() => {
|
||
keyDisplay.textContent = maskKey(fullActivationKey);
|
||
keyDisplay.classList.remove('copied');
|
||
}, 1000);
|
||
});
|
||
});
|
||
|
||
// Loading 状态控制
|
||
function setButtonLoading(btn, loading) {
|
||
if (loading) {
|
||
btn.classList.add('loading');
|
||
btn.disabled = true;
|
||
} else {
|
||
btn.classList.remove('loading');
|
||
// 注意:某些按钮可能需要保持禁用状态,由调用方控制
|
||
}
|
||
}
|
||
|
||
function setRefreshLoading(btn, loading) {
|
||
if (loading) {
|
||
btn.classList.add('loading');
|
||
} else {
|
||
btn.classList.remove('loading');
|
||
}
|
||
}
|
||
|
||
// 获取初始状态
|
||
vscode.postMessage({ type: 'getState' });
|
||
vscode.postMessage({ type: 'getSeamlessStatus' });
|
||
vscode.postMessage({ type: 'getUserSwitchStatus' });
|
||
vscode.postMessage({ type: 'getProxyStatus' });
|
||
vscode.postMessage({ type: 'getAnnouncement' });
|
||
vscode.postMessage({ type: 'checkVersion' });
|
||
vscode.postMessage({ type: 'getCursorRunningPath' });
|
||
|
||
// 激活按钮
|
||
activateBtn.addEventListener('click', () => {
|
||
const key = keyInput.value.trim();
|
||
if (!key) {
|
||
return;
|
||
}
|
||
setButtonLoading(activateBtn, true);
|
||
vscode.postMessage({ type: 'activate', key });
|
||
});
|
||
|
||
// 换号按钮
|
||
switchBtn.addEventListener('click', () => {
|
||
vscode.postMessage({ type: 'switch' });
|
||
});
|
||
|
||
// 重置机器码按钮
|
||
resetBtn.addEventListener('click', () => {
|
||
vscode.postMessage({ type: 'resetMachineId' });
|
||
});
|
||
|
||
// 禁用自动更新按钮
|
||
disableUpdateBtn.addEventListener('click', () => {
|
||
vscode.postMessage({ type: 'disableUpdate' });
|
||
});
|
||
|
||
// 清理Cursor环境按钮 - 显示确认弹窗
|
||
cleanEnvBtn.addEventListener('click', () => {
|
||
cleanEnvModal.classList.add('show');
|
||
});
|
||
|
||
// 确认清理
|
||
cleanEnvConfirmBtn.addEventListener('click', () => {
|
||
cleanEnvModal.classList.remove('show');
|
||
vscode.postMessage({ type: 'cleanEnv' });
|
||
});
|
||
|
||
// 取消清理
|
||
cleanEnvCancelBtn.addEventListener('click', () => {
|
||
cleanEnvModal.classList.remove('show');
|
||
});
|
||
|
||
// 点击遮罩关闭清理弹窗
|
||
cleanEnvModal.addEventListener('click', (e) => {
|
||
if (e.target === cleanEnvModal) {
|
||
cleanEnvModal.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
// 停用按钮
|
||
disableBtn.addEventListener('click', () => {
|
||
vscode.postMessage({ type: 'disable' });
|
||
});
|
||
|
||
// 关闭更新提醒条
|
||
updateBannerClose.addEventListener('click', () => {
|
||
updateBanner.classList.remove('show');
|
||
});
|
||
|
||
// 免魔法开关
|
||
seamlessProxySwitch.addEventListener('change', (e) => {
|
||
const wantEnabled = e.target.checked;
|
||
|
||
// 如果要开启免魔法,检查卡密是否过期(只要没过期就可以用,不管换号次数)
|
||
if (wantEnabled && isKeyExpired()) {
|
||
e.target.checked = false;
|
||
showToast('授权码已过期,无法开启免魔法', '⚠️', 3000);
|
||
return;
|
||
}
|
||
|
||
vscode.postMessage({
|
||
type: 'toggleProxy',
|
||
enabled: wantEnabled,
|
||
url: ''
|
||
});
|
||
});
|
||
|
||
// 无感换号 - 启用按钮
|
||
enableSeamlessBtn.addEventListener('click', () => {
|
||
setButtonLoading(enableSeamlessBtn, true);
|
||
vscode.postMessage({ type: 'injectSeamless' });
|
||
});
|
||
|
||
// 无感换号 - 禁用按钮
|
||
disableSeamlessBtn.addEventListener('click', () => {
|
||
setButtonLoading(disableSeamlessBtn, true);
|
||
vscode.postMessage({ type: 'restoreSeamless' });
|
||
});
|
||
|
||
// 无感换号 - 手动换号按钮(先检查用量)
|
||
manualSwitchBtn.addEventListener('click', () => {
|
||
setButtonLoading(manualSwitchBtn, true);
|
||
// 传递当前显示的账号邮箱
|
||
vscode.postMessage({ type: 'checkUsageBeforeSwitch', email: currentAccountEmail });
|
||
});
|
||
|
||
// 换号确认弹窗 - 确认按钮
|
||
switchConfirmBtn.addEventListener('click', () => {
|
||
switchConfirmModal.classList.remove('show');
|
||
setButtonLoading(manualSwitchBtn, true);
|
||
vscode.postMessage({ type: 'confirmSwitch' });
|
||
});
|
||
|
||
// 换号确认弹窗 - 取消按钮
|
||
switchCancelBtn.addEventListener('click', () => {
|
||
switchConfirmModal.classList.remove('show');
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
manualSwitchBtn.disabled = false;
|
||
});
|
||
|
||
// 换号确认弹窗 - 点击遮罩关闭
|
||
switchConfirmModal.addEventListener('click', (e) => {
|
||
if (e.target === switchConfirmModal) {
|
||
switchConfirmModal.classList.remove('show');
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
manualSwitchBtn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// 无感换号区域 - 重置机器码按钮
|
||
seamlessResetMachineBtn.addEventListener('click', () => {
|
||
vscode.postMessage({ type: 'resetMachineId' });
|
||
});
|
||
|
||
// 刷新用量按钮
|
||
refreshUsageBtn.addEventListener('click', () => {
|
||
if (currentAccountEmail) {
|
||
setRefreshLoading(refreshUsageBtn, true);
|
||
vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });
|
||
}
|
||
});
|
||
|
||
// 刷新用量函数
|
||
function refreshUsage() {
|
||
if (currentAccountEmail) {
|
||
vscode.postMessage({ type: 'getAccountUsage', email: currentAccountEmail });
|
||
}
|
||
}
|
||
|
||
// 启动用量定时刷新 (每分钟一次)
|
||
function startUsageRefresh() {
|
||
if (usageRefreshInterval) {
|
||
clearInterval(usageRefreshInterval);
|
||
}
|
||
// 立即刷新一次
|
||
refreshUsage();
|
||
// 每60秒刷新一次
|
||
usageRefreshInterval = setInterval(refreshUsage, 60000);
|
||
}
|
||
|
||
// 停止用量刷新
|
||
function stopUsageRefresh() {
|
||
if (usageRefreshInterval) {
|
||
clearInterval(usageRefreshInterval);
|
||
usageRefreshInterval = null;
|
||
}
|
||
}
|
||
|
||
// 更新用量显示
|
||
function updateUsageDisplay(data) {
|
||
if (!data) return;
|
||
|
||
const subscription = data.subscription || {};
|
||
const usage = data.usage || {};
|
||
|
||
// 会员类型
|
||
const memberTypeMap = {
|
||
'free_trial': '免费试用',
|
||
'pro': 'Pro会员',
|
||
'free': '免费版',
|
||
'business': '商业版'
|
||
};
|
||
usageMemberType.textContent = memberTypeMap[subscription.membershipType] || subscription.membershipType || '-';
|
||
|
||
// 试用剩余天数
|
||
if (subscription.daysRemainingOnTrial !== undefined && subscription.daysRemainingOnTrial !== null) {
|
||
usageTrialDays.textContent = subscription.daysRemainingOnTrial + ' 天';
|
||
usageTrialDays.style.color = subscription.daysRemainingOnTrial <= 3 ? '#f87171' : '#4ade80';
|
||
} else {
|
||
usageTrialDays.textContent = '-';
|
||
usageTrialDays.style.color = '#fff';
|
||
}
|
||
|
||
// 请求次数
|
||
usageRequestCount.textContent = (usage.totalUsageCount || 0) + ' 次';
|
||
|
||
// 已用额度
|
||
const costUSD = usage.totalCostUSD || 0;
|
||
usageCostUSD.textContent = '$' + costUSD.toFixed(2);
|
||
usageCostUSD.style.color = costUSD > 5 ? '#f87171' : (costUSD > 2 ? '#fbbf24' : '#4ade80');
|
||
|
||
// 更新时间
|
||
usageUpdateTime.textContent = '更新于 ' + new Date().toLocaleTimeString();
|
||
}
|
||
|
||
// 解析公告内容中的链接 {文字URL}
|
||
function parseAnnouncementContent(content) {
|
||
if (!content) return '';
|
||
|
||
// 转义 HTML 特殊字符
|
||
let escaped = content
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"');
|
||
|
||
// 匹配 {文字https://...} 或 {文字http://...} 格式
|
||
const linkRegex = /\{([^}]+?)(https?:\/\/[^}]+)\}/g;
|
||
|
||
escaped = escaped.replace(linkRegex, function(match, text, url) {
|
||
return '<a href="' + url + '" class="announcement-link" target="_blank">' + text + '</a>';
|
||
});
|
||
|
||
// 将换行符转换为 <br>
|
||
escaped = escaped.replace(/\n/g, '<br>');
|
||
|
||
return escaped;
|
||
}
|
||
|
||
// 更新公告显示
|
||
function updateAnnouncementDisplay(data) {
|
||
if (!data || !data.is_active) {
|
||
announcementSection.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
// 显示公告区域
|
||
announcementSection.style.display = 'block';
|
||
|
||
// 设置图标和类型徽章
|
||
const typeConfig = {
|
||
'info': { icon: '📢', text: '通知', class: 'info' },
|
||
'warning': { icon: '⚠️', text: '警告', class: 'warning' },
|
||
'error': { icon: '🚨', text: '重要', class: 'error' },
|
||
'success': { icon: '✅', text: '好消息', class: 'success' }
|
||
};
|
||
|
||
const config = typeConfig[data.type] || typeConfig['info'];
|
||
announcementIcon.textContent = config.icon;
|
||
announcementBadge.textContent = config.text;
|
||
announcementBadge.className = 'announcement-badge ' + config.class;
|
||
|
||
// 设置标题和内容(解析链接)
|
||
announcementTitle.textContent = data.title || '';
|
||
announcementContent.innerHTML = parseAnnouncementContent(data.content || '');
|
||
|
||
// 设置时间
|
||
if (data.created_at) {
|
||
const date = new Date(data.created_at);
|
||
announcementTime.textContent = date.toLocaleDateString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
} else {
|
||
announcementTime.textContent = '';
|
||
}
|
||
}
|
||
|
||
// 处理来自扩展的消息
|
||
window.addEventListener('message', event => {
|
||
const message = event.data;
|
||
|
||
switch (message.type) {
|
||
case 'state':
|
||
updateUI(message);
|
||
break;
|
||
case 'activated':
|
||
setButtonLoading(activateBtn, false);
|
||
activateBtn.disabled = false;
|
||
if (message.success) {
|
||
// 调试日志
|
||
console.log('[CursorPro] 前端收到激活成功消息:', message);
|
||
|
||
authStatus.textContent = '已授权';
|
||
authStatus.className = 'status-badge active';
|
||
accountStatus.textContent = '已激活';
|
||
accountStatus.className = 'status-badge active';
|
||
switchBtn.disabled = false;
|
||
// 更新激活码显示(使用后端返回的 key)
|
||
fullActivationKey = message.key || keyInput.value;
|
||
keyDisplay.textContent = maskKey(fullActivationKey);
|
||
// 更新到期时间
|
||
console.log('[CursorPro] 更新到期时间:', message.expireDate);
|
||
currentExpireDate = message.expireDate || '';
|
||
expireDate.textContent = formatExpireDate(currentExpireDate) || '未知';
|
||
// 更新换号次数
|
||
if (message.switchRemaining !== undefined) {
|
||
currentSwitchRemaining = message.switchRemaining;
|
||
switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);
|
||
}
|
||
// 清空输入框
|
||
keyInput.value = '';
|
||
showToast('授权码激活成功!', '✅', 10000);
|
||
} else {
|
||
showToast(message.error || '激活失败', '❌', 10000);
|
||
}
|
||
break;
|
||
case 'switched':
|
||
if (message.success) {
|
||
switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);
|
||
showToast('换号成功: ' + (message.email || ''), '✅', 10000);
|
||
} else {
|
||
showToast(message.error || '换号失败', '❌', 10000);
|
||
}
|
||
break;
|
||
case 'reset':
|
||
authStatus.textContent = '未授权';
|
||
authStatus.className = 'status-badge inactive';
|
||
accountStatus.textContent = '未激活';
|
||
accountStatus.className = 'status-badge inactive';
|
||
switchBtn.disabled = true;
|
||
keyInput.value = '';
|
||
fullActivationKey = '';
|
||
keyDisplay.textContent = '尚未激活';
|
||
expireDate.textContent = '尚未激活';
|
||
break;
|
||
|
||
// 激活码状态检查结果
|
||
case 'keyStatusChecked':
|
||
if (message.valid) {
|
||
// 激活码有效,更新显示
|
||
currentExpireDate = message.expireDate || '';
|
||
currentSwitchRemaining = message.switchRemaining || 0;
|
||
expireDate.textContent = formatExpireDate(currentExpireDate);
|
||
switchCount.textContent = message.switchRemaining + '/' + (message.switchLimit || 100);
|
||
} else if (message.expired) {
|
||
// 激活码已过期,显示提示并重置状态
|
||
currentExpireDate = '';
|
||
currentSwitchRemaining = 0;
|
||
authStatus.textContent = '已过期';
|
||
authStatus.className = 'status-badge inactive';
|
||
authStatus.style.background = '#6e3232';
|
||
authStatus.style.color = '#ff6b6b';
|
||
expireDate.textContent = '已过期';
|
||
expireDate.style.color = '#f87171';
|
||
switchBtn.disabled = true;
|
||
enableSeamlessBtn.disabled = true;
|
||
// 如果免魔法已开启,自动关闭
|
||
if (seamlessProxySwitch.checked) {
|
||
seamlessProxySwitch.checked = false;
|
||
vscode.postMessage({ type: 'toggleProxy', enabled: false, url: '' });
|
||
}
|
||
// 显示过期弹窗
|
||
showExpiredModal();
|
||
}
|
||
break;
|
||
|
||
// 用户换号状态
|
||
case 'userSwitchStatus':
|
||
const remaining = message.switchRemaining || 0;
|
||
const canSwitch = remaining > 0;
|
||
|
||
// 更新全局变量
|
||
currentSwitchRemaining = remaining;
|
||
|
||
seamlessSwitchRemaining.textContent = remaining.toString();
|
||
seamlessSwitchRemaining.style.color = canSwitch ? '#4ade80' : '#f87171';
|
||
|
||
if (message.lockedAccount) {
|
||
seamlessCurrentAccount.textContent = message.lockedAccount.email;
|
||
|
||
// 设置当前账号邮箱并启动用量刷新
|
||
if (message.lockedAccount.email && message.lockedAccount.email !== currentAccountEmail) {
|
||
currentAccountEmail = message.lockedAccount.email;
|
||
usageSection.style.display = 'block';
|
||
startUsageRefresh();
|
||
}
|
||
} else {
|
||
seamlessCurrentAccount.textContent = '未分配';
|
||
|
||
// 没有锁定账号时隐藏用量区域
|
||
currentAccountEmail = '';
|
||
usageSection.style.display = 'none';
|
||
stopUsageRefresh();
|
||
}
|
||
|
||
// 根据剩余次数控制手动换号按钮状态
|
||
if (!canSwitch) {
|
||
manualSwitchBtn.disabled = true;
|
||
}
|
||
// 启用无感换号按钮不受积分限制,只有过期才禁用
|
||
enableSeamlessBtn.disabled = isKeyExpired();
|
||
|
||
// 如果无感换号已启用,显示手动换号按钮和重置机器码按钮
|
||
if (message.seamlessEnabled && canSwitch) {
|
||
manualSwitchBtn.style.display = 'block';
|
||
manualSwitchBtn.disabled = false;
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
seamlessResetMachineBtn.style.display = 'block';
|
||
}
|
||
break;
|
||
|
||
// 账号用量
|
||
case 'accountUsage':
|
||
setRefreshLoading(refreshUsageBtn, false);
|
||
if (message.success && message.data) {
|
||
updateUsageDisplay(message.data);
|
||
} else {
|
||
usageUpdateTime.textContent = '获取失败: ' + (message.error || '未知错误');
|
||
usageUpdateTime.style.color = '#f87171';
|
||
}
|
||
break;
|
||
|
||
// 无感换号状态
|
||
case 'seamlessStatus':
|
||
if (message.is_injected) {
|
||
seamlessStatus.textContent = '已启用';
|
||
seamlessStatus.className = 'status-badge active';
|
||
enableSeamlessBtn.style.display = 'none';
|
||
disableSeamlessBtn.style.display = 'block';
|
||
disableSeamlessBtn.disabled = false;
|
||
setButtonLoading(disableSeamlessBtn, false);
|
||
manualSwitchBtn.style.display = 'block';
|
||
manualSwitchBtn.disabled = false;
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
seamlessResetMachineBtn.style.display = 'block';
|
||
} else {
|
||
seamlessStatus.textContent = '未启用';
|
||
seamlessStatus.className = 'status-badge inactive';
|
||
enableSeamlessBtn.style.display = 'block';
|
||
setButtonLoading(enableSeamlessBtn, false);
|
||
// 启用按钮不受积分限制,只有过期才禁用
|
||
enableSeamlessBtn.disabled = isKeyExpired();
|
||
disableSeamlessBtn.style.display = 'none';
|
||
manualSwitchBtn.style.display = 'none';
|
||
seamlessResetMachineBtn.style.display = 'none';
|
||
}
|
||
break;
|
||
|
||
case 'seamlessInjected':
|
||
setButtonLoading(enableSeamlessBtn, false);
|
||
enableSeamlessBtn.disabled = false;
|
||
if (message.success) {
|
||
seamlessStatus.textContent = '已启用';
|
||
seamlessStatus.className = 'status-badge active';
|
||
enableSeamlessBtn.style.display = 'none';
|
||
disableSeamlessBtn.style.display = 'block';
|
||
manualSwitchBtn.style.display = 'block';
|
||
seamlessResetMachineBtn.style.display = 'block';
|
||
// 刷新用户状态
|
||
vscode.postMessage({ type: 'getUserSwitchStatus' });
|
||
// 显示重启提示弹窗
|
||
if (message.needRestart) {
|
||
showRestartModal(message.message || '无感换号已启用');
|
||
}
|
||
} else {
|
||
// 如果是权限错误,显示自定义弹窗
|
||
if (message.needAdmin) {
|
||
// Mac/Linux 权限问题,显示详细提示
|
||
var errorMsg = message.error || '没有写入权限';
|
||
if (message.path) {
|
||
errorMsg += '\n路径: ' + message.path;
|
||
}
|
||
showToast(errorMsg, '🔐', 15000);
|
||
} else {
|
||
// 显示详细错误
|
||
var detailMsg = message.error || '启用失败';
|
||
if (message.details) {
|
||
detailMsg += '\n' + message.details;
|
||
}
|
||
showToast(detailMsg, '❌', 15000);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'seamlessRestored':
|
||
setButtonLoading(disableSeamlessBtn, false);
|
||
disableSeamlessBtn.disabled = false;
|
||
if (message.success) {
|
||
seamlessStatus.textContent = '未启用';
|
||
seamlessStatus.className = 'status-badge inactive';
|
||
enableSeamlessBtn.style.display = 'block';
|
||
disableSeamlessBtn.style.display = 'none';
|
||
manualSwitchBtn.style.display = 'none';
|
||
seamlessResetMachineBtn.style.display = 'none';
|
||
// 显示重启提示弹窗
|
||
if (message.needRestart) {
|
||
showRestartModal(message.message || '无感换号已禁用');
|
||
}
|
||
} else {
|
||
// 如果是权限错误,显示自定义弹窗
|
||
if (message.needAdmin) {
|
||
showAdminModal();
|
||
} else {
|
||
showToast(message.error || '禁用失败', '❌', 10000);
|
||
}
|
||
}
|
||
break;
|
||
|
||
// 用量检查结果
|
||
case 'usageCheckResult':
|
||
if (message.success) {
|
||
if (message.needConfirm) {
|
||
// 需要确认,显示弹窗(按钮保持可用状态,等用户选择)
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
manualSwitchBtn.disabled = false;
|
||
switchConfirmEmail.textContent = message.email || '';
|
||
switchConfirmCost.textContent = '$' + (message.costUSD || '0.00');
|
||
switchConfirmModal.classList.add('show');
|
||
} else {
|
||
// 不需要确认,直接换号
|
||
vscode.postMessage({ type: 'confirmSwitch' });
|
||
}
|
||
} else {
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
manualSwitchBtn.disabled = false;
|
||
showToast(message.error || '检查失败', '❌', 5000);
|
||
}
|
||
break;
|
||
|
||
case 'manualSeamlessSwitched':
|
||
setButtonLoading(manualSwitchBtn, false);
|
||
if (message.success) {
|
||
seamlessSwitchRemaining.textContent = (message.switchRemaining || 0).toString();
|
||
seamlessCurrentAccount.textContent = message.email || '未知';
|
||
// 显示 Toast 通知,10秒后消失
|
||
showToast('已切换到: ' + (message.email || '新账号') + ',约10秒内自动生效', '✅', 10000);
|
||
// 刷新状态
|
||
vscode.postMessage({ type: 'getUserSwitchStatus' });
|
||
// 禁用按钮10秒,显示倒计时
|
||
disableSwitchBtnWithCountdown(10);
|
||
} else {
|
||
manualSwitchBtn.disabled = false;
|
||
showToast(message.error || '换号失败', '❌', 5000);
|
||
}
|
||
break;
|
||
|
||
case 'proxyStatus':
|
||
// 设置免魔法开关状态
|
||
seamlessProxySwitch.checked = message.enabled;
|
||
break;
|
||
|
||
// 公告
|
||
case 'announcement':
|
||
if (message.success && message.data) {
|
||
updateAnnouncementDisplay(message.data);
|
||
} else {
|
||
announcementSection.style.display = 'none';
|
||
}
|
||
break;
|
||
|
||
// 版本检查
|
||
case 'versionCheck':
|
||
currentVersionEl.textContent = message.currentVersion || '-';
|
||
if (message.success && message.hasUpdate) {
|
||
// 有更新
|
||
latestVersionEl.textContent = message.latestVersion;
|
||
latestVersionRow.style.display = 'flex';
|
||
versionStatus.style.display = 'inline-block';
|
||
versionStatus.style.background = '#ff9800';
|
||
updateHint.style.display = 'block';
|
||
|
||
// 显示顶部更新提醒条
|
||
updateBannerVersion.textContent = 'v' + message.latestVersion;
|
||
updateBanner.classList.add('show');
|
||
} else if (message.success) {
|
||
// 已是最新版
|
||
versionStatus.textContent = '最新';
|
||
versionStatus.style.display = 'inline-block';
|
||
versionStatus.style.background = '#4caf50';
|
||
latestVersionRow.style.display = 'none';
|
||
updateHint.style.display = 'none';
|
||
updateBanner.classList.remove('show');
|
||
}
|
||
break;
|
||
|
||
// Cursor 运行路径
|
||
case 'cursorRunningPath':
|
||
if (message.path) {
|
||
const pathText = message.path + (message.packageExists ? ' ✓' : ' ✗');
|
||
cursorPath.textContent = pathText;
|
||
cursorPath.style.color = message.packageExists ? '#4ade80' : '#f87171';
|
||
// 同时更新版本号
|
||
if (message.cursorVersion) {
|
||
cursorVersion.textContent = message.cursorVersion;
|
||
}
|
||
} else {
|
||
cursorPath.textContent = '未找到';
|
||
cursorPath.style.color = '#f87171';
|
||
}
|
||
break;
|
||
|
||
// 管理员权限不足提示
|
||
case 'adminPermissionRequired':
|
||
showAdminPermissionModal();
|
||
break;
|
||
|
||
// 机器码重置
|
||
case 'machineIdReset':
|
||
if (message.success && message.needRestart) {
|
||
// 机器码重置需要完全关闭 Cursor,不是 reload
|
||
showRestartModal(message.message || '机器码重置成功', 'close');
|
||
}
|
||
break;
|
||
|
||
// 通用 Toast 消息
|
||
case 'showToast':
|
||
showToast(message.message || '', message.icon || '📢', 10000);
|
||
break;
|
||
|
||
// 网络状态
|
||
case 'networkStatus':
|
||
updateOfflineStatus(!message.online);
|
||
break;
|
||
}
|
||
});
|
||
|
||
// 离线状态显示/隐藏
|
||
let wasOffline = false; // 跟踪之前是否离线
|
||
function updateOfflineStatus(isOffline) {
|
||
if (isOffline) {
|
||
offlineBanner.classList.add('show');
|
||
wasOffline = true;
|
||
} else {
|
||
offlineBanner.classList.remove('show');
|
||
// 只有从离线恢复到在线时才刷新状态
|
||
if (wasOffline) {
|
||
wasOffline = false;
|
||
vscode.postMessage({ type: 'getState' });
|
||
vscode.postMessage({ type: 'getUserSwitchStatus' });
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重试连接按钮
|
||
retryConnectBtn.addEventListener('click', async () => {
|
||
retryConnectBtn.classList.add('loading');
|
||
retryConnectBtn.textContent = '连接中...';
|
||
|
||
// 发起真正的网络请求来测试网络
|
||
vscode.postMessage({ type: 'retryConnect' });
|
||
|
||
// 5秒后恢复按钮状态(给网络请求足够时间)
|
||
setTimeout(() => {
|
||
retryConnectBtn.classList.remove('loading');
|
||
retryConnectBtn.textContent = '重试';
|
||
}, 5000);
|
||
});
|
||
|
||
function updateUI(state) {
|
||
if (state.isActivated) {
|
||
authStatus.textContent = '已授权';
|
||
authStatus.className = 'status-badge active';
|
||
accountStatus.textContent = '已激活';
|
||
accountStatus.className = 'status-badge active';
|
||
switchBtn.disabled = false;
|
||
fullActivationKey = state.key;
|
||
keyDisplay.textContent = maskKey(fullActivationKey);
|
||
// 更新到期时间
|
||
currentExpireDate = state.expireDate || '';
|
||
expireDate.textContent = formatExpireDate(currentExpireDate);
|
||
// 更新换号次数
|
||
if (state.switchRemaining !== undefined) {
|
||
currentSwitchRemaining = state.switchRemaining;
|
||
switchCount.textContent = state.switchRemaining + '/' + (state.switchLimit || 100);
|
||
}
|
||
// 启用无感换号按钮(只有过期才禁用)
|
||
enableSeamlessBtn.disabled = isKeyExpired();
|
||
}
|
||
cursorVersion.textContent = state.cursorVersion || '0.0.0';
|
||
|
||
// 根据网络状态显示/隐藏离线提示
|
||
if (state.isOnline === false) {
|
||
offlineBanner.classList.add('show');
|
||
wasOffline = true;
|
||
} else if (state.isOnline === true) {
|
||
// 网络恢复,隐藏离线提示
|
||
offlineBanner.classList.remove('show');
|
||
wasOffline = false;
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>`;
|
||
}
|
||
}
|
||
exports.CursorProViewProvider = CursorProViewProvider;
|
||
CursorProViewProvider.CURRENT_VERSION = '0.4.5'; |