Files
cursornew2026/extension_clean/out/webview/provider.js
huangzhenpc bc883df6e8 v2.1.5: 密钥状态显示优化 + 版本号统一
- 密钥无到期时间/积分数据时显示"待验证"而非"已激活"
- 后端验证完成后自动更新为真实状态
- 版本号统一更新到2.1.5(provider/client/main)
- 添加.vscodeignore排除大文件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:20:06 +08:00

2281 lines
87 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

'use strict';
var __createBinding = this && this.__createBinding || (Object.create ? function (param0, param1, param2, param3) {
if (param3 === undefined) {
param3 = param2;
}
var descriptor = Object.getOwnPropertyDescriptor(param1, param2);
if (!descriptor || ("get" in descriptor ? !param1.__esModule : descriptor.writable || descriptor.configurable)) {
descriptor = {
enumerable: true,
get: function () {
return param1[param2];
}
};
}
Object.defineProperty(param0, param3, descriptor);
} : function (param0, param1, param2, param3) {
if (param3 === undefined) {
param3 = param2;
}
param0[param3] = param1[param2];
});
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (param0, param1) {
Object.defineProperty(param0, "default", {
enumerable: true,
value: param1
});
} : function (param0, param1) {
param0.default = param1;
});
var __importStar = this && this.__importStar || function () {
var getOwnPropNames = function (param0) {
getOwnPropNames = Object.getOwnPropertyNames || function (param0) {
var items = [];
for (var propKey in param0) if (Object.prototype.hasOwnProperty.call(param0, propKey)) {
items[items.length] = propKey;
}
return items;
};
return getOwnPropNames(param0);
};
return function (param0) {
if (param0 && param0.__esModule) {
return param0;
}
var obj = {};
if (param0 != null) {
var items = getOwnPropNames(param0);
for (var count = 0; count < items.length; count++) {
if (items[count] !== "default") {
__createBinding(obj, param0, items[count]);
}
}
}
__setModuleDefault(obj, param0);
return obj;
};
}();
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.HummingbirdProViewProvider = undefined;
const vscode = __importStar(require("vscode"));
const client_1 = require("../api/client");
const extension_1 = require("../extension");
const account_1 = require('../utils/account');
const path = __importStar(require("path"));
const fs = __importStar(require('fs'));
const child_process_1 = require('child_process');
const util_1 = require("util");
const sqlite_1 = require('../utils/sqlite');
const execAsync = util_1.promisify(child_process_1.exec);
class HummingbirdProViewProvider {
constructor(extensionUri, context) {
this._extensionUri = extensionUri;
this._context = context;
this._hostsPermissionGranted = false;
this.SNI_PROXY_IP = "154.36.154.163";
this.CURSOR_DOMAINS = ["api2.cursor.sh", "api3.cursor.sh"];
this.HOSTS_MARKER_START = "# ===== HummingbirdPro SNI Proxy Start =====";
this.HOSTS_MARKER_END = "# ===== HummingbirdPro SNI Proxy End =====";
this._cachedCursorPath = null;
this._onlineStatusUnsubscribe = client_1.onOnlineStatusChange(status => {
this._postMessage({
'type': "networkStatus",
'online': status
});
});
}
resolveWebviewView(webviewView, context, token) {
this._view = webviewView;
webviewView.webview.options = {
'enableScripts': true,
'localResourceRoots': [this._extensionUri]
};
webviewView.webview.html = this._getHtmlContent(webviewView.webview);
webviewView.webview.onDidReceiveMessage(async msg => {
const config = {
'WZyWQ': "没有写入权限",
'ZXhkG': "seamlessRestored"
};
switch (msg.type) {
case "activate":
await this._handleActivate(msg.key);
break;
case "switch":
await this._handleSwitch();
break;
case "resetMachineId":
await this._handleResetMachineId();
break;
case "disableUpdate":
await this._handleDisableUpdate();
break;
case "cleanEnv":
await this._handleCleanEnv();
break;
case "disable":
await this._handleDisable();
break;
case "toggleProxy":
await this._handleToggleProxy(msg.enabled, msg.url);
break;
case 'getProxyStatus':
await this._handleGetProxyStatus();
break;
case "getState":
await this._sendState();
break;
case "retryConnect":
await this._handleRetryConnect();
break;
case "getSeamlessStatus":
await this._handleGetSeamlessStatus();
break;
case "injectSeamless":
await this._handleInjectSeamless();
break;
case "restoreSeamless":
await this._handleRestoreSeamless();
break;
case "toggleSeamless":
await this._handleToggleSeamless(msg.enabled);
break;
case "getUserSwitchStatus":
await this._handleGetUserSwitchStatus();
break;
case "manualSeamlessSwitch":
await this._handleManualSeamlessSwitch();
break;
case "checkUsageBeforeSwitch":
await this._handleCheckUsageBeforeSwitch(msg.email);
break;
case "confirmSwitch":
await this._handleManualSeamlessSwitch();
break;
case "getCursorPath":
await this._handleGetCursorPath();
break;
case 'getAccountUsage':
await this._handleGetAccountUsage(msg.email);
break;
case "getAnnouncement":
await this._handleGetAnnouncement();
break;
case "checkVersion":
await this._handleCheckVersion();
break;
case "getCursorRunningPath":
await this._handleGetCursorRunningPath();
break;
case "reloadWindow":
vscode.commands.executeCommand("workbench.action.reloadWindow");
break;
case 'closeCursor':
await account_1.closeCursor();
break;
case 'selectPool':
await this._handleSelectPool(msg.pool);
break;
case 'clearKey':
await this._handleClearKey(msg.keyType);
break;
}
});
this._sendState();
this._checkKeyStatus();
}
/**
* 获取当前激活的密钥(统一入口)
* 优先返回当前选中号池的密钥,兼容旧版本的 hummingbird.key
*/
_getActiveKey() {
const selectedPool = this._context.globalState.get("hummingbird.selectedPool") || "auto";
const key = selectedPool === "pro"
? this._context.globalState.get("hummingbird.proKey")
: this._context.globalState.get("hummingbird.autoKey");
// 兼容旧版本:如果新字段没有值,回退到旧字段
if (!key) {
return this._context.globalState.get("hummingbird.key");
}
return key;
}
async _checkKeyStatus() {
// 检查 Auto 和 Pro 两个密钥的状态
const autoKey = this._context.globalState.get("hummingbird.autoKey");
const proKey = this._context.globalState.get("hummingbird.proKey");
// 兼容旧版本:如果有旧的 hummingbird.key迁移到 autoKey
const oldKey = this._context.globalState.get("hummingbird.key");
if (oldKey && !autoKey && !proKey) {
await this._context.globalState.update("hummingbird.autoKey", oldKey);
await this._context.globalState.update("hummingbird.key", undefined);
}
const keyStatus = { auto: null, pro: null };
// 检查 Auto 密钥
if (autoKey) {
try {
const verifyResult = await client_1.verifyKey(autoKey);
if (verifyResult.success && verifyResult.valid) {
if (verifyResult.key && verifyResult.key !== autoKey) {
await this._context.globalState.update("hummingbird.autoKey", verifyResult.key);
}
keyStatus.auto = {
valid: true,
expireDate: verifyResult.expire_date,
switchRemaining: verifyResult.switch_remaining,
mergedCount: verifyResult.merged_count || 0
};
await this._context.globalState.update("hummingbird.autoExpireDate", verifyResult.expire_date);
} else if (!verifyResult.isOffline) {
// 后端明确返回无效(非网络错误),自动清除本地 Auto 密钥
console.warn("[蜂鸟Pro] Auto 密钥已失效,自动清除:", verifyResult.error);
await this._context.globalState.update("hummingbird.autoKey", undefined);
await this._context.globalState.update("hummingbird.autoExpireDate", undefined);
await this._context.globalState.update("hummingbird.autoMergedCount", undefined);
await this._context.globalState.update("hummingbird.key", undefined);
keyStatus.auto = { valid: false, error: verifyResult.error, cleared: true };
}
} catch (err) {
console.error("[蜂鸟Pro] 检查 Auto 密钥状态失败:", err);
}
}
// 检查 Pro 密钥
if (proKey) {
try {
const verifyResult = await client_1.verifyKey(proKey);
if (verifyResult.success && verifyResult.valid) {
if (verifyResult.key && verifyResult.key !== proKey) {
await this._context.globalState.update("hummingbird.proKey", verifyResult.key);
}
keyStatus.pro = {
valid: true,
quota: verifyResult.quota,
quotaUsed: verifyResult.quota_used,
quotaRemaining: verifyResult.switch_remaining,
mergedCount: verifyResult.merged_count || 0
};
await this._context.globalState.update("hummingbird.proQuota", verifyResult.quota);
await this._context.globalState.update("hummingbird.proQuotaUsed", verifyResult.quota_used);
} else if (!verifyResult.isOffline) {
// 后端明确返回无效(非网络错误),自动清除本地 Pro 密钥
console.warn("[蜂鸟Pro] Pro 密钥已失效,自动清除:", verifyResult.error);
await this._context.globalState.update("hummingbird.proKey", undefined);
await this._context.globalState.update("hummingbird.proQuota", undefined);
await this._context.globalState.update("hummingbird.proQuotaUsed", undefined);
await this._context.globalState.update("hummingbird.proMergedCount", undefined);
keyStatus.pro = { valid: false, error: verifyResult.error, cleared: true };
}
} catch (err) {
console.error("[蜂鸟Pro] 检查 Pro 密钥状态失败:", err);
}
}
// 如果有密钥被清除,同时清理无感换号状态
if (keyStatus.auto?.cleared || keyStatus.pro?.cleared) {
const remainingAuto = this._context.globalState.get("hummingbird.autoKey");
const remainingPro = this._context.globalState.get("hummingbird.proKey");
if (!remainingAuto && !remainingPro) {
// 所有密钥都失效了,清理全部状态
await this._context.globalState.update("hummingbird.selectedPool", undefined);
await this._context.globalState.update("hummingbird.seamlessInjected", undefined);
await this._context.globalState.update("hummingbird.seamlessCurrentAccount", undefined);
await this._context.globalState.update("hummingbird.expireDate", undefined);
await this._context.globalState.update("hummingbird.switchRemaining", undefined);
extension_1.hideStatusBar();
}
}
this._postMessage({
'type': "keyStatusChecked",
'auto': keyStatus.auto,
'pro': keyStatus.pro
});
// 如果有密钥被清除重新发送状态给面板刷新UI
if (keyStatus.auto?.cleared || keyStatus.pro?.cleared) {
await this._sendState();
}
}
async _handleActivate(key) {
try {
const isSeamlessInjected = await this._isSeamlessInjected();
if (isSeamlessInjected) {
this._postMessage({
'type': "activated",
'success': false,
'error': "无感换号已启用,请先禁用后再更换授权码"
});
return;
}
this._cleanProxySettings();
const verifyResult = await client_1.verifyKey(key);
if (verifyResult.success && verifyResult.valid) {
const membershipType = verifyResult.membership_type || 'auto';
const isAuto = membershipType === 'auto' || membershipType === 'free';
const activatedKey = verifyResult.key || key;
console.log("[蜂鸟Pro] 激活成功,类型:", membershipType, "后端返回:", {
'expire_date': verifyResult.expire_date,
'switch_remaining': verifyResult.switch_remaining,
'quota': verifyResult.quota,
'merged_count': verifyResult.merged_count
});
// 根据类型存储到不同字段
if (isAuto) {
await this._context.globalState.update("hummingbird.autoKey", activatedKey);
await this._context.globalState.update("hummingbird.autoExpireDate", verifyResult.expire_date);
await this._context.globalState.update("hummingbird.autoMergedCount", verifyResult.merged_count || 0);
// 同步写回旧字段(兼容旧逻辑)
await this._context.globalState.update("hummingbird.key", activatedKey);
await this._context.globalState.update("hummingbird.expireDate", verifyResult.expire_date);
} else {
await this._context.globalState.update("hummingbird.proKey", activatedKey);
await this._context.globalState.update("hummingbird.proQuota", verifyResult.quota);
await this._context.globalState.update("hummingbird.proQuotaUsed", verifyResult.quota_used || 0);
await this._context.globalState.update("hummingbird.proMergedCount", verifyResult.merged_count || 0);
// 同步写回旧字段(兼容旧逻辑)
await this._context.globalState.update("hummingbird.key", activatedKey);
}
this._postMessage({
'type': "activated",
'success': true,
'key': activatedKey,
'membershipType': membershipType,
'expireDate': verifyResult.expire_date,
'quota': verifyResult.quota,
'quotaUsed': verifyResult.quota_used,
'switchRemaining': verifyResult.switch_remaining,
'mergedCount': verifyResult.merged_count || 0,
'masterKey': verifyResult.master_key
});
extension_1.showStatusBar();
await this._handleGetUserSwitchStatus();
} else {
this._postMessage({
'type': "activated",
'success': false,
'error': verifyResult.error || "授权码无效"
});
}
} catch (activateErr) {
this._postMessage({
'type': "activated",
'success': false,
'error': "连接服务器失败"
});
}
}
async _handleSwitch() {
// 获取当前选择的号池
const selectedPool = this._context.globalState.get("hummingbird.selectedPool") || "auto";
const savedKey = selectedPool === "pro"
? this._context.globalState.get("hummingbird.proKey")
: this._context.globalState.get("hummingbird.autoKey");
if (!savedKey) {
this._postMessage({
'type': "showToast",
'message': selectedPool === "pro" ? "请先激活Pro授权码" : "请先激活Auto授权码",
'icon': '⚠️'
});
return;
}
try {
const switchResult = await client_1.switchSeamlessToken(savedKey);
if (switchResult.switched) {
await this._context.globalState.update("hummingbird.switchRemaining", switchResult.switchRemaining);
// 写入新 token 到本地 Cursor 存储
if (switchResult.data) {
try {
await this._writeAccountToLocal(switchResult.data);
console.log("[蜂鸟Pro] 换号成功,已写入新 token");
} catch (writeErr) {
console.error("[蜂鸟Pro] 写入 token 失败:", writeErr);
}
}
this._postMessage({
'type': "switched",
'success': true,
'email': switchResult.email,
'switchRemaining': switchResult.switchRemaining,
'switchLimit': this._context.globalState.get("hummingbird.switchLimit") || 100
});
const condition = switchResult.switchRemaining ?? 0;
this._postMessage({
'type': "userSwitchStatus",
'switchRemaining': condition,
'canSwitch': condition > 0,
'lockedAccount': switchResult.email ? {
'email': switchResult.email
} : null
});
} else {
this._postMessage({
'type': "switched",
'success': false,
'error': switchResult.message || '换号失败'
});
}
} catch (switchErr) {
this._postMessage({
'type': 'switched',
'success': false,
'error': "连接服务器失败"
});
}
}
async _writeAccountToLocal(accountData) {
try {
const cursorPaths = account_1.getCursorPaths();
const joinedPath = cursorPaths.dbPath;
const joinedPath1 = cursorPaths.storagePath;
const joinedPath2 = cursorPaths.machineidPath;
if (fs.existsSync(joinedPath)) {
const items = [];
if (accountData.accessToken) {
items.push(["cursorAuth/accessToken", accountData.accessToken]);
}
if (accountData.refreshToken) {
items.push(["cursorAuth/refreshToken", accountData.refreshToken]);
}
if (accountData.workosSessionToken) {
items.push(["cursorAuth/WorkosCursorSessionToken", accountData.workosSessionToken]);
}
if (accountData.email) {
items.push(["cursorAuth/cachedEmail", accountData.email]);
}
if (accountData.membership_type) {
items.push(["cursorAuth/stripeMembershipType", accountData.membership_type]);
}
if (accountData.sign_up_type) {
items.push(["cursorAuth/cachedSignUpType", accountData.sign_up_type]);
}
if (accountData.serviceMachineId) {
items.push(["storage.serviceMachineId", accountData.serviceMachineId]);
}
await sqlite_1.sqliteSetBatch(joinedPath, items);
console.log("[蜂鸟Pro] SQLite 数据库已更新");
}
if (fs.existsSync(joinedPath1)) {
const parsed = JSON.parse(fs.readFileSync(joinedPath1, 'utf-8'));
if (accountData.machineId) {
parsed["telemetry.machineId"] = accountData.machineId;
}
if (accountData.macMachineId) {
parsed['telemetry.macMachineId'] = accountData.macMachineId;
}
if (accountData.devDeviceId) {
parsed["telemetry.devDeviceId"] = accountData.devDeviceId;
}
if (accountData.sqmId) {
parsed["telemetry.sqmId"] = accountData.sqmId;
}
fs.writeFileSync(joinedPath1, JSON.stringify(parsed, null, 4));
console.log("[蜂鸟Pro] storage.json 已更新");
}
if (accountData.machineId) {
fs.writeFileSync(joinedPath2, accountData.machineId);
console.log("[蜂鸟Pro] machineid 文件已更新");
}
if (accountData.registryGuid && process.platform === "win32") {
try {
const result = 'reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + accountData.registryGuid + '" /f';
await execAsync(result);
console.log("[蜂鸟Pro] 注册表 MachineGuid 已更新");
} catch (parseErr) {
console.warn("[蜂鸟Pro] 注册表写入失败(可能需要管理员权限):", parseErr);
}
}
return true;
} catch (writeErr) {
console.error("[蜂鸟Pro] 写入本地失败:", writeErr);
vscode.window.showErrorMessage("写入失败: " + writeErr);
return false;
}
}
async _handleReset() {
await this._context.globalState.update("hummingbird.key", undefined);
await this._context.globalState.update("hummingbird.expireDate", undefined);
await this._context.globalState.update("hummingbird.switchRemaining", undefined);
// 清理双池相关状态
await this._context.globalState.update("hummingbird.autoKey", undefined);
await this._context.globalState.update("hummingbird.proKey", undefined);
await this._context.globalState.update("hummingbird.autoExpireDate", undefined);
await this._context.globalState.update("hummingbird.proQuota", undefined);
await this._context.globalState.update("hummingbird.proQuotaUsed", undefined);
await this._context.globalState.update("hummingbird.proMergedCount", undefined);
await this._context.globalState.update("hummingbird.autoMergedCount", undefined);
await this._context.globalState.update("hummingbird.selectedPool", undefined);
await this._context.globalState.update("hummingbird.seamlessInjected", undefined);
await this._context.globalState.update("hummingbird.seamlessCurrentAccount", undefined);
extension_1.hideStatusBar();
this._postMessage({
'type': 'reset',
'success': true
});
vscode.window.showInformationMessage("插件已重置");
}
async _handleDisable() {
await this._handleReset();
vscode.window.showInformationMessage("插件已停用");
}
async _checkAdminPrivilege() {
if (process.platform !== "win32") {
return true;
}
try {
await execAsync('reg query "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid 2>nul');
const hostEntry = await execAsync("net session 2>nul").catch(() => ({
'stdout': '',
'stderr': 'error'
}));
return !hostEntry.stderr;
} catch (jsonErr) {
return false;
}
}
async _handleResetMachineId() {
try {
const platform = process.platform;
if (platform === 'win32') {
const adminprivilegeResult = await this._checkAdminPrivilege();
if (!adminprivilegeResult) {
this._postMessage({
'type': "adminPermissionRequired"
});
return;
}
}
const hostLine = account_1.getCursorPaths();
const {
dbPath: dbPath,
storagePath: storagePath,
machineidPath: machineidPath
} = hostLine;
const module = require("crypto");
const str = module.randomBytes(32).toString("hex");
const str1 = module.randomBytes(32).toString("hex");
const proxyLine = module.randomUUID();
const result = '{' + module.randomUUID().toUpperCase() + '}';
let count = 0;
let items = [];
if (fs.existsSync(storagePath)) {
let num = 3;
while (num > 0) {
try {
const parsed = JSON.parse(fs.readFileSync(storagePath, "utf-8"));
parsed["telemetry.machineId"] = str;
parsed["telemetry.macMachineId"] = str1;
parsed["telemetry.devDeviceId"] = proxyLine;
parsed["telemetry.sqmId"] = result;
fs.writeFileSync(storagePath, JSON.stringify(parsed, null, 4));
console.log("[蜂鸟Pro] storage.json 已更新");
count++;
break;
} catch (readErr) {
num--;
if (num === 0) {
console.warn("[蜂鸟Pro] storage.json 更新失败:", readErr.message);
items.push("storage.json");
} else {
await new Promise(param0 => setTimeout(param0, 100));
}
}
}
}
{
let num = 3;
while (num > 0) {
try {
const dirPath = path.dirname(machineidPath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, {
'recursive': true
});
}
fs.writeFileSync(machineidPath, str);
console.log("[蜂鸟Pro] machineid 文件已更新");
count++;
break;
} catch (writeErr) {
num--;
if (num === 0) {
console.warn("[蜂鸟Pro] machineid 更新失败:", writeErr.message);
items.push("machineid");
} else {
await new Promise(param0 => setTimeout(param0, 100));
}
}
}
}
if (fs.existsSync(dbPath)) {
let num = 3;
while (num > 0) {
try {
const proxyEntry = module.randomUUID();
const newHostsContent = await sqlite_1.sqliteSetBatch(dbPath, [['storage.serviceMachineId', proxyEntry]]);
if (newHostsContent) {
console.log("[蜂鸟Pro] SQLite 数据库已更新");
count++;
break;
} else {
throw new Error("sqliteSetBatch 返回 false");
}
} catch (grantErr) {
num--;
if (num === 0) {
console.warn("[蜂鸟Pro] SQLite 更新失败:", grantErr.message);
items.push("SQLite");
} else {
await new Promise(param0 => setTimeout(param0, 500));
}
}
}
}
if (platform === "win32") {
const hostsLines = module.randomUUID();
try {
await execAsync('reg add "HKLM\SOFTWARE\Microsoft\Cryptography" /v MachineGuid /t REG_SZ /d "' + hostsLines + '" /f');
console.log("[蜂鸟Pro] 注册表 MachineGuid 已更新");
count++;
} catch (regWriteErr) {
console.warn("[蜂鸟Pro] 注册表更新失败(需要管理员权限),已跳过");
items.push("注册表");
}
}
if (count >= 2) {
this._postMessage({
'type': "machineIdReset",
'success': true,
'needRestart': true,
'message': items.length > 0 ? "机器码重置成功(" + items.join(", ") + " 更新失败,不影响使用)" : "机器码重置成功"
});
} else {
this._postMessage({
'type': "showToast",
'message': "重置部分失败: " + items.join(", ") + "。请先完全关闭 Cursor 再试",
'icon': '⚠️'
});
}
} catch (hostsErr) {
this._postMessage({
'type': "showToast",
'message': "重置机器码失败: " + hostsErr,
'icon': '❌'
});
}
}
_generateRandomMAC() {
const module = require("crypto");
const dbPath = module.randomBytes(6);
dbPath[0] = (dbPath[0] | 2) & 254;
return Array.from(dbPath).map(item => item.toString(16).padStart(2, '0')).join(':');
}
async _handleDisableUpdate() {
try {
const condition = process.env.LOCALAPPDATA || '';
const joinedPath = path.join(condition, "cursor-updater");
if (fs.existsSync(joinedPath)) {
if (fs.statSync(joinedPath).isDirectory()) {
fs.rmSync(joinedPath, {
'recursive': true,
'force': true
});
} else {
fs.unlinkSync(joinedPath);
}
}
fs.writeFileSync(joinedPath, '');
this._postMessage({
'type': 'showToast',
'message': "已禁用 Cursor 自动更新",
'icon': '✅'
});
} catch (toggleErr) {
this._postMessage({
'type': "showToast",
'message': "禁用自动更新失败: " + toggleErr,
'icon': '❌'
});
}
}
async _handleCleanEnv() {
try {
if (process.platform === "win32") {
await execAsync("taskkill /F /IM Cursor.exe").catch(() => {});
} else {
await execAsync("pkill -f Cursor").catch(() => {});
}
await new Promise(param0 => setTimeout(param0, 2000));
const condition = process.env.APPDATA || '';
const condition1 = process.env.LOCALAPPDATA || '';
const condition2 = process.env.HOME || process.env.USERPROFILE || '';
let count = 0;
if (process.platform === "win32") {
const items = [path.join(condition, "Cursor"), path.join(condition1, "Cursor"), path.join(condition1, "cursor-updater"), path.join(condition2, ".cursor")];
for (const macPath of items) {
try {
if (fs.existsSync(macPath)) {
fs.rmSync(macPath, {
'recursive': true,
'force': true
});
count++;
console.log("[蜂鸟Pro] 已清理: " + macPath);
}
} catch (statusErr) {
console.warn("[蜂鸟Pro] 清理失败: " + macPath, statusErr);
}
}
} else {
if (process.platform === "darwin") {
const items = [path.join(condition2, "Library", "Application Support", "Cursor"), path.join(condition2, "Library", "Caches", "Cursor"), path.join(condition2, "Library", "Logs", "Cursor"), path.join(condition2, 'Library', "Application Support", 'Caches', "cursor-updater"), path.join(condition2, ".cursor")];
for (const storagePath of items) {
try {
if (fs.existsSync(storagePath)) {
fs.rmSync(storagePath, {
'recursive': true,
'force': true
});
count++;
}
} catch (pathErr) {
console.warn("[蜂鸟Pro] 清理失败: " + storagePath, pathErr);
}
}
} else {
const items = [path.join(condition2, ".config", "Cursor"), path.join(condition2, ".cache", "Cursor"), path.join(condition2, ".local", "share", "Cursor"), path.join(condition2, ".cursor")];
for (const machineIdPath of items) {
try {
if (fs.existsSync(machineIdPath)) {
fs.rmSync(machineIdPath, {
'recursive': true,
'force': true
});
count++;
}
} catch (seamlessErr) {
console.warn("[蜂鸟Pro] 清理失败: " + machineIdPath, seamlessErr);
}
}
}
}
vscode.window.showInformationMessage("✅ Cursor 环境清理完成!已清理 " + count + " 个目录。请重新启动 Cursor。");
} catch (cleanErr) {
vscode.window.showErrorMessage("清理失败: " + cleanErr);
}
}
_cleanProxySettings() {
try {
const platform = process.platform;
const condition = process.env.HOME || process.env.USERPROFILE || '';
let settingsPath;
if (platform === "win32") {
const condition1 = process.env.APPDATA || '';
settingsPath = path.join(condition1, "Cursor", "User", "settings.json");
} else {
if (platform === "darwin") {
settingsPath = path.join(condition, "Library", "Application Support", "Cursor", 'User', "settings.json");
} else {
settingsPath = path.join(condition, ".config", "Cursor", "User", "settings.json");
}
}
if (!fs.existsSync(settingsPath)) {
return;
}
const fileContent = fs.readFileSync(settingsPath, 'utf-8');
let settingsObj;
try {
settingsObj = JSON.parse(fileContent);
} catch {
return;
}
const items = ["http.proxy", "http.proxyStrictSSL", "http.proxySupport", "cursor.general.disableHttp2", "http.noProxy"];
let isFalse = false;
for (const tokenData of items) {
if (tokenData in settingsObj) {
isFalse = true;
delete settingsObj[tokenData];
}
}
if (isFalse) {
fs.writeFileSync(settingsPath, JSON.stringify(settingsObj, null, 4), "utf-8");
console.log("[蜂鸟Pro] 已清理 settings.json 中的旧代理配置");
}
} catch (proxyErr) {
console.warn("[蜂鸟Pro] 清理 settings.json 代理配置失败:", proxyErr);
}
}
_getHostsPath() {
return process.platform === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : '/etc/hosts';
}
_readHostsFile() {
try {
const accountInfo = this._getHostsPath();
if (fs.existsSync(accountInfo)) {
return fs.readFileSync(accountInfo, "utf-8");
}
} catch (readErr) {
console.error("[蜂鸟Pro] Read hosts error:", readErr);
}
return '';
}
_hasHostsConfig() {
const switchResponse = this._readHostsFile();
return switchResponse.includes(this.HOSTS_MARKER_START);
}
async _grantHostsWritePermission() {
if (process.platform !== "win32") {
return false;
}
try {
const content = this._getHostsPath();
const condition = process.env.USERNAME || '';
if (!condition) {
return false;
}
const replaced = content.replace(/\\/g, "\\\\");
const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command icacls \\\"" + replaced + '\" /grant ' + condition + ":M' -Verb RunAs -Wait\"";
await execAsync(result);
this._hostsPermissionGranted = true;
console.log("[蜂鸟Pro] Hosts file permission granted to user:", condition);
return true;
} catch (switchErr) {
console.error("[蜂鸟Pro] Grant hosts permission error:", switchErr);
return false;
}
}
async _writeHostsFile(content) {
const content1 = this._getHostsPath();
try {
if (process.platform === "win32") {
let isFalse = false;
try {
fs.writeFileSync(content1, content, "utf-8");
isFalse = true;
} catch (writeErr1) {
console.log("[蜂鸟Pro] Direct write failed, trying to grant permission");
}
if (!isFalse) {
if (!this._hostsPermissionGranted) {
const lockedInfo = await this._grantHostsWritePermission();
if (lockedInfo) {
try {
fs.writeFileSync(content1, content, "utf-8");
isFalse = true;
} catch (writeErr2) {
console.log("[蜂鸟Pro] Write still failed after permission grant");
}
}
}
}
if (!isFalse) {
const joinedPath = path.join(process.env.TEMP || '', "hummingbird_hosts_temp.txt");
fs.writeFileSync(joinedPath, content, "utf-8");
const replaced = joinedPath.replace(/\\/g, "\\\\");
const replaced1 = content1.replace(/\\/g, "\\\\");
const result = "powershell -WindowStyle Hidden -Command \"Start-Process powershell -ArgumentList '-WindowStyle Hidden -Command Copy-Item -Path \\\"" + replaced + '\" -Destination \"' + replaced1 + "\\\" -Force' -Verb RunAs -Wait\"";
await execAsync(result);
try {
fs.unlinkSync(joinedPath);
} catch {}
}
try {
await execAsync("ipconfig /flushdns");
console.log("[蜂鸟Pro] Windows DNS 缓存已刷新");
} catch (resetErr) {
console.warn("[蜂鸟Pro] Windows DNS 刷新失败:", resetErr);
}
} else {
if (process.platform === "darwin") {
const pathStr = "/tmp/hosts_cursor_temp";
fs.writeFileSync(pathStr, content, "utf-8");
const osaCmd = "do shell script \"cp '" + pathStr + "' '" + content1 + "' && rm '" + pathStr + "' && dscacheutil -flushcache && killall -HUP mDNSResponder\" with administrator privileges";
await execAsync('osascript -e "' + osaCmd.replace(/"/g, "\\\"") + "\"");
} else {
fs.writeFileSync(content1, content, "utf-8");
}
}
return true;
} catch (disableErr) {
console.error("[蜂鸟Pro] Write hosts error:", disableErr);
return false;
}
}
async _handleToggleProxy(enabled, silent) {
try {
if (enabled) {
const savedKey = this._getActiveKey();
const expireDate = this._context.globalState.get('hummingbird.expireDate');
if (!savedKey) {
this._postMessage({
'type': "proxyUpdated",
'success': false,
'error': "请先激活授权码"
});
this._postMessage({
'type': "showToast",
'message': '请先激活授权码',
'icon': '⚠️'
});
return;
}
if (expireDate) {
const resetResponse = new Date(expireDate).getTime();
if (Date.now() > resetResponse) {
this._postMessage({
'type': "proxyUpdated",
'success': false,
'error': "授权码已过期,无法开启免魔法"
});
this._postMessage({
'type': "showToast",
'message': "授权码已过期,无法开启免魔法",
'icon': '⚠️'
});
return;
}
}
}
this._cleanProxySettings();
let content = this._readHostsFile();
const index = content.indexOf(this.HOSTS_MARKER_START);
const index1 = content.indexOf(this.HOSTS_MARKER_END);
if (index !== -1 && index1 !== -1) {
content = content.substring(0, index) + content.substring(index1 + this.HOSTS_MARKER_END.length);
}
content = content.replace(/\n{3,}/g, "\n\n").trim();
if (enabled) {
const joinedPath = this.CURSOR_DOMAINS.map(item => this.SNI_PROXY_IP + " " + item).join("\n");
const result = "\n\n" + this.HOSTS_MARKER_START + "\n" + joinedPath + "\n" + this.HOSTS_MARKER_END + "\n";
content += result;
}
const disableResponse = await this._writeHostsFile(content);
if (disableResponse) {
await client_1.updateProxyConfig(enabled, this.SNI_PROXY_IP);
this._postMessage({
'type': "proxyUpdated",
'success': true,
'enabled': enabled,
'url': this.SNI_PROXY_IP
});
this._postMessage({
'type': "showToast",
'message': enabled ? "免魔法已开启" : "免魔法已关闭",
'icon': '✅'
});
} else {
this._postMessage({
'type': "proxyUpdated",
'success': false,
'error': "修改 hosts 文件失败,请确保有管理员权限"
});
this._postMessage({
'type': "showToast",
'message': "需要管理员权限修改 hosts 文件",
'icon': '⚠️'
});
}
} catch (updateErr) {
console.error("[蜂鸟Pro] Toggle proxy error:", updateErr);
this._postMessage({
'type': "proxyUpdated",
'success': false,
'error': "更新配置失败"
});
}
}
async _handleGetProxyStatus() {
try {
const enabled = this._hasHostsConfig();
this._postMessage({
'type': "proxyStatus",
'enabled': enabled,
'url': enabled ? this.SNI_PROXY_IP : ''
});
} catch (envErr) {
console.error("[蜂鸟Pro] Get proxy status error:", envErr);
this._postMessage({
'type': "proxyStatus",
'enabled': false,
'url': ''
});
}
}
async _handleGetSeamlessStatus() {
try {
const workbenchPath = await this._getWorkbenchPathAsync();
let isInjected = false;
if (workbenchPath && fs.existsSync(workbenchPath)) {
const fileContent = fs.readFileSync(workbenchPath, 'utf-8');
isInjected = this._checkInjected(fileContent);
}
this._postMessage({
'type': "seamlessStatus",
'is_injected': isInjected,
'workbench_path': workbenchPath || '未找到'
});
} catch (e1) {
this._postMessage({
'type': "seamlessStatus",
'is_injected': false,
'error': "检测状态失败"
});
}
}
async _getCursorInstallPath() {
if (this._cachedCursorPath) {
return this._cachedCursorPath;
}
const config = vscode.workspace.getConfiguration("hummingbird");
const configValue = config.get("cursorPath");
if (configValue && fs.existsSync(configValue)) {
console.log("[蜂鸟Pro] 使用用户配置的 Cursor 路径:", configValue);
this._cachedCursorPath = configValue;
return configValue;
}
const platform = process.platform;
let result = null;
try {
if (platform === "win32") {
try {
const {
stdout: wmicOut
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
if (wmicOut) {
const matchResult = wmicOut.match(/ExecutablePath=(.+)/);
if (matchResult && matchResult[1]) {
const trimmed = matchResult[1].trim();
result = path.dirname(trimmed);
}
}
} catch (e2) {
console.log("[蜂鸟Pro] WMIC 获取路径失败");
}
if (!result) {
try {
const {
stdout: psOut
} = await execAsync("powershell -Command \"Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path\"");
if (psOut && psOut.trim()) {
result = path.dirname(psOut.trim());
}
} catch (e3) {
console.log("[蜂鸟Pro] PowerShell Get-Process 获取路径失败");
}
}
if (!result) {
try {
const {
stdout: regOut
} = await execAsync("reg query \"HKCUSoftwareMicrosoftWindowsCurrentVersionUninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
if (regOut && regOut.trim()) {
const matchResult = regOut.match(/InstallLocation\s+REG_SZ\s+(.+)/);
if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
result = matchResult[1].trim();
}
}
} catch (e4) {
console.log("[蜂鸟Pro] 注册表方法1获取路径失败");
}
}
if (!result) {
try {
const {
stdout: regOut2
} = await execAsync("reg query \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\" /s /f \"Cursor\" 2>nul | findstr \"InstallLocation\"");
if (regOut2 && regOut2.trim()) {
const matchResult = regOut2.match(/InstallLocation\s+REG_SZ\s+(.+)/);
if (matchResult && matchResult[1] && fs.existsSync(matchResult[1].trim())) {
result = matchResult[1].trim();
}
}
} catch (e5) {
console.log("[蜂鸟Pro] 注册表方法2获取路径失败");
}
}
if (!result) {
try {
const joinedPath = path.join(process.env.APPDATA || '', "Microsoft", "Windows", "Start Menu", 'Programs', "Cursor.lnk");
const joinedPath1 = path.join("C:\\ProgramData", "Microsoft", 'Windows', "Start Menu", "Programs", "Cursor.lnk");
for (const content of [joinedPath, joinedPath1]) {
if (fs.existsSync(content)) {
const {
stdout: lnkOut
} = await execAsync("powershell -Command \"(New-Object -ComObject WScript.Shell).CreateShortcut('" + content.replace(/'/g, "''") + "').TargetPath\"");
if (lnkOut && lnkOut.trim() && fs.existsSync(lnkOut.trim())) {
result = path.dirname(lnkOut.trim());
break;
}
}
}
} catch (e6) {
console.log("[蜂鸟Pro] 快捷方式解析获取路径失败");
}
}
if (!result) {
try {
const {
stdout: whereOut
} = await execAsync("where cursor 2>nul");
if (whereOut && whereOut.trim()) {
const parts = whereOut.trim().split("\n");
for (const str of parts) {
const trimmed = str.trim();
if (trimmed && fs.existsSync(trimmed)) {
result = path.dirname(trimmed);
break;
}
}
}
} catch (whereErr) {
console.log("[蜂鸟Pro] where 命令获取路径失败");
}
}
if (!result) {
const condition = process.env.LOCALAPPDATA || '';
const condition1 = process.env.USERPROFILE || '';
const condition2 = process.env.ProgramFiles || "C:\\Program Files";
const condition3 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
const items = [path.join(condition, "Programs", "Cursor"), path.join(condition, "Programs", "cursor"), path.join(condition1, "AppData", "Local", "Programs", "Cursor"), path.join(condition2, "Cursor"), path.join(condition3, "Cursor"), path.join(condition, "Cursor"), path.join(condition, "cursor")];
for (const cursorDbPath of items) {
if (cursorDbPath && fs.existsSync(cursorDbPath)) {
result = cursorDbPath;
break;
}
}
}
} else {
if (platform === "darwin") {
try {
const {
stdout: dirEntry
} = await execAsync("lsof -c Cursor 2>/dev/null | grep \"txt\" | grep -i \"Cursor.app\" | head -1 | awk '{print $9}'");
if (dirEntry && dirEntry.trim()) {
const matchResult = dirEntry.trim().match(/(.+\.app)/);
if (matchResult) {
result = matchResult[1];
}
}
} catch (e) {}
if (!result) {
try {
const {
stdout: fileItem
} = await execAsync("ps -eo comm,args | grep -i \"[C]ursor\" | grep -v \"grep\" | head -1");
if (fileItem && fileItem.trim()) {
const matchResult = fileItem.match(/(\/.+\.app)/);
if (matchResult) {
result = matchResult[1];
}
}
} catch (findErr) {
console.warn("[蜂鸟Pro] macOS 获取进程路径失败:", findErr);
}
}
if (!result) {
try {
const {
stdout: childPath
} = await execAsync("mdfind \"kMDItemCFBundleIdentifier == 'com.todesktop.*cursor*'\" 2>/dev/null | head -1");
if (childPath && childPath.trim() && fs.existsSync(childPath.trim())) {
result = childPath.trim();
}
} catch (e) {}
}
if (!result && fs.existsSync('/Applications/Cursor.app')) {
result = "/Applications/Cursor.app";
}
} else {
try {
const {
stdout: pathItem
} = await execAsync('pgrep -f "[c]ursor" | head -1');
const condition = pathItem && pathItem.trim();
if (condition) {
const {
stdout: subDir
} = await execAsync("readlink -f /proc/" + condition + "/exe 2>/dev/null");
if (subDir && subDir.trim()) {
const trimmed = subDir.trim();
result = path.dirname(trimmed);
if (result.endsWith("/bin")) {
result = path.dirname(result);
}
}
}
} catch (e) {}
if (!result) {
try {
const {
stdout: subItem
} = await execAsync("which cursor 2>/dev/null");
if (subItem && subItem.trim()) {
const execResult = await execAsync('readlink -f "' + subItem.trim() + '" 2>/dev/null');
if (execResult.stdout && execResult.stdout.trim()) {
result = path.dirname(execResult.stdout.trim());
if (result.endsWith('/bin')) {
result = path.dirname(result);
}
}
}
} catch (checkErr) {
console.warn("[蜂鸟Pro] Linux 获取进程路径失败:", checkErr);
}
}
if (!result) {
const items = ["/opt/Cursor", "/opt/cursor", "/usr/share/cursor", "/usr/lib/cursor", path.join(process.env.HOME || '', ".local/share/cursor"), path.join(process.env.HOME || '', "Applications/cursor")];
for (const statusInfo of items) {
if (fs.existsSync(statusInfo)) {
result = statusInfo;
break;
}
}
}
}
}
} catch (injectErr) {
console.error("[蜂鸟Pro] 获取 Cursor 安装路径失败:", injectErr);
}
if (result) {
this._cachedCursorPath = result;
}
return result;
}
_getWorkbenchPath() {
return this._getWorkbenchPathSync();
}
_getWorkbenchPathSync() {
const platform = process.platform;
if (this._cachedCursorPath) {
let entry;
if (platform === "darwin") {
entry = path.join(this._cachedCursorPath, 'Contents', "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
} else {
entry = path.join(this._cachedCursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
}
if (fs.existsSync(entry)) {
return entry;
}
}
if (platform === 'win32') {
return null;
}
let items = [];
if (platform === "darwin") {
items = ["/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"];
} else {
items = ["/opt/Cursor/resources/app/out/vs/workbench/workbench.desktop.main.js", '/usr/share/cursor/resources/app/out/vs/workbench/workbench.desktop.main.js'];
}
for (const switchInfo of items) {
if (fs.existsSync(switchInfo)) {
return switchInfo;
}
}
return null;
}
async _getWorkbenchPathAsync() {
const platform = process.platform;
const cursorPath = await this._getCursorInstallPath();
if (cursorPath) {
let workbenchSubPath;
if (platform === "darwin") {
workbenchSubPath = path.join(cursorPath, "Contents", "Resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
} else {
workbenchSubPath = path.join(cursorPath, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
}
if (fs.existsSync(workbenchSubPath)) {
return workbenchSubPath;
}
}
return this._getWorkbenchPathSync();
}
_checkInjected(cbArg) {
return cbArg.includes("/*i0*/") || cbArg.includes('/*i1s*/');
}
async _isSeamlessInjected() {
try {
const workbenchPath = await this._getWorkbenchPathAsync();
if (workbenchPath && fs.existsSync(workbenchPath)) {
const fileContent = fs.readFileSync(workbenchPath, "utf-8");
return this._checkInjected(fileContent);
}
return false;
} catch (restoreErr) {
console.error("[蜂鸟Pro] 检测无感换号状态失败:", restoreErr);
return false;
}
}
_getInjectionConfig(msgData, dataArg) {
return [{
'name': "注入点0: 完整性检查绕过",
'scode': "_showNotification(){",
'replacement': "_showNotification(){/*i0*/}_showNotificationOld(){",
'restore': {
'find': "_showNotification(){/*i0*/}_showNotificationOld(){",
'replace_with': "_showNotification(){"
}
}, {
'name': "注入点1: 核心模块初始化",
'scode': "this.database.getItems()))",
'replacement': "this.database.getItems()))/*i1s*/;await(async function(e){if(e.get('releaseNotes/lastVersion')){window.store=e;window.__cpKey='HummingbirdPro2024!@#';window.__cpEnc=function(t){var k=window.__cpKey,r='';for(var i=0;i<t.length;i++)r+=String.fromCharCode(t.charCodeAt(i)^k.charCodeAt(i%k.length));return btoa(r)};window.__cpDec=function(t){var k=window.__cpKey,d=atob(t),r='';for(var i=0;i<d.length;i++)r+=String.fromCharCode(d.charCodeAt(i)^k.charCodeAt(i%k.length));return r};window.__cpGet=function(){try{var d=localStorage.getItem('__cp_token');return d?JSON.parse(window.__cpDec(d)):null}catch(e){return null}};window.__cpSet=function(data){try{localStorage.setItem('__cp_token',window.__cpEnc(JSON.stringify(data)))}catch(e){}};window.__cpApi='" + msgData + "';window.__cpUserKey='" + dataArg + "';window.__cpVersion=0;console.log('[CP] Initialized with key:','" + dataArg + "'.substring(0,15)+'...')}})(this)/*i1e*/",
'restore': {
'find_start': "/*i1s*/",
'find_end': "/*i1e*/"
}
}, {
'name': "注入点2: 启动时Token同步",
'scode': "/*i1e*/",
'replacement': "/*i1e*//*i2s*/;(function(){window.__cpSyncing=false;window.__cpSync=function(){if(window.__cpSyncing){console.log('[CP] Sync already in progress, skip');return}var userKey=window.__cpUserKey;if(!userKey){console.log('[CP] No userKey, skip sync');return}window.__cpSyncing=true;console.log('[CP] Sync with key:',userKey.substring(0,15)+'...');fetch(window.__cpApi+'/api/seamless/get-token?userKey='+encodeURIComponent(userKey)).then(function(r){return r.json()}).then(function(d){window.__cpSyncing=false;if(d.error){console.error('[CP] Sync error:',d.error);return}if(d&&d.accessToken){var oldToken=window.__cpGet();var needUpdate=!oldToken||oldToken.email!==d.email||window.__cpVersion!==d.switchVersion;if(needUpdate){window.__cpVersion=d.switchVersion||0;window.__cpSet({accessToken:d.accessToken,refreshToken:d.refreshToken||'',email:d.email||'',machineIds:d.machineIds||null,switchRemaining:d.switchRemaining,switchVersion:d.switchVersion||0});window.store.set('cursorAuth/accessToken',d.accessToken,-1);if(d.refreshToken)window.store.set('cursorAuth/refreshToken',d.refreshToken,-1);if(d.email)window.store.set('cursor.email',d.email,-1);if(d.is_new&&d.machineIds){if(d.machineIds.devDeviceId)window.store.set('telemetry.devDeviceId',d.machineIds.devDeviceId,-1);if(d.machineIds.machineId)window.store.set('telemetry.machineId',d.machineIds.machineId,-1);if(d.machineIds.macMachineId)window.store.set('telemetry.macMachineId',d.machineIds.macMachineId,-1);if(d.machineIds.sqmId)window.store.set('telemetry.sqmId',d.machineIds.sqmId,-1)}console.log('[CP] Token UPDATED:',d.email,'v'+d.switchVersion)}}}).catch(function(e){window.__cpSyncing=false;console.error('[CP] Sync error:',e)})};console.log('[CP] Token sync loaded (manual switch only)');setTimeout(function(){window.__cpSync()},2000);setInterval(function(){window.__cpSync()},10000)})()/*i2e*/",
'restore': {
'find_start': "/*i2s*/",
'find_end': "/*i2e*/"
}
}];
}
async _handleInjectSeamless() {
try {
const savedKey = this._getActiveKey();
if (!savedKey) {
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': "请先激活授权码"
});
return;
}
// 1. 先调用后端启用无感换号(分配账号)
const deviceId = client_1.getDeviceId();
console.log("[蜂鸟Pro] 正在调用后端启用无感换号...");
const enableResult = await client_1.enableSeamless(savedKey, deviceId);
if (!enableResult.success) {
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': enableResult.error || "启用无感换号失败"
});
return;
}
// 2. 验证账号数据
const accountData = enableResult.data?.account;
if (!accountData || !(accountData.token || accountData.access_token)) {
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': "后端未返回账号信息"
});
return;
}
console.log("[蜂鸟Pro] 后端分配账号成功:", accountData.email);
// 3. 获取 workbench 路径并进行本地文件注入
const workbenchPath = await this._getWorkbenchPathAsync();
if (!workbenchPath) {
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': "启用失败"
});
return;
}
const result = workbenchPath + ".backup";
const not = !this._context.globalState.get("hummingbird.seamlessInjected");
if (not && fs.existsSync(result)) {
console.log("[蜂鸟Pro] 首次启用,从备份恢复干净的 workbench 文件");
try {
fs.copyFileSync(result, workbenchPath);
console.log("[蜂鸟Pro] 备份恢复成功");
} catch (usageErr) {
console.error("[蜂鸟Pro] 备份恢复失败:", usageErr);
}
}
let fileContent = fs.readFileSync(workbenchPath, 'utf-8');
if (this._checkInjected(fileContent)) {
this._postMessage({
'type': "showToast",
'message': "已启用",
'icon': '✅'
});
return;
}
if (!fs.existsSync(result)) {
fs.copyFileSync(workbenchPath, result);
console.log("[蜂鸟Pro] 创建备份文件");
}
const announceList = client_1.getApiUrl();
const latestAnnounce = this._getInjectionConfig(announceList, savedKey);
const items = [];
const items1 = [];
for (const linuxPath of latestAnnounce) {
if (fileContent.includes(linuxPath.scode)) {
fileContent = fileContent.replace(linuxPath.scode, linuxPath.replacement);
items.push(linuxPath.name);
} else {
items1.push(linuxPath.name);
}
}
if (items.length === 0) {
console.error("[蜂鸟Pro] 注入失败,未找到任何注入点");
console.error("[蜂鸟Pro] 文件路径:", workbenchPath);
console.error("[蜂鸟Pro] 文件大小:", fileContent.length);
console.error("[蜂鸟Pro] 未找到的注入点:", items1);
const versionResponse = fileContent.includes("_showNotification");
const latestVersion = fileContent.includes("getItems()");
console.error("[蜂鸟Pro] 包含 _showNotification:", versionResponse);
console.error("[蜂鸟Pro] 包含 getItems():", latestVersion);
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': "Cursor 版本不兼容,注入点未找到",
'details': "路径: " + workbenchPath
});
return;
}
console.log("[蜂鸟Pro] 注入成功,应用的注入点:", items);
if (items1.length > 0) {
console.warn("[蜂鸟Pro] 未找到的注入点:", items1);
}
try {
fs.writeFileSync(workbenchPath, fileContent, "utf-8");
} catch (writeErr) {
console.error("[蜂鸟Pro] 写入文件失败:", writeErr);
if (writeErr.code === "EPERM" || writeErr.code === "EACCES" || writeErr.code === "EROFS") {
const platform = process.platform;
let errorMsg = "没有写入权限";
if (platform === "darwin") {
errorMsg = "没有写入权限,请在终端执行: sudo chmod -R 777 /Applications/Cursor.app";
} else if (platform === "linux") {
errorMsg = "没有写入权限,请使用 sudo 权限运行或修改文件权限";
}
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': errorMsg,
'needAdmin': true,
'path': workbenchPath
});
return;
}
throw writeErr;
}
await this._context.globalState.update("hummingbird.seamlessInjected", true);
// 4. 注入成功后,将后端分配的账号信息写入 Cursor
console.log("[蜂鸟Pro] 正在写入账号信息到 Cursor...");
try {
await this._writeAccountToLocal({
accessToken: accountData.access_token || accountData.token,
refreshToken: accountData.refresh_token,
workosSessionToken: accountData.workos_session_token,
email: accountData.email,
membership_type: accountData.membership_type
});
console.log("[蜂鸟Pro] 账号信息写入成功");
} catch (writeAccountErr) {
console.error("[蜂鸟Pro] 写入账号信息失败:", writeAccountErr);
// 写入失败不阻塞整个流程,因为注入已经成功
}
this._postMessage({
'type': 'seamlessInjected',
'success': true,
'applied': items,
'needRestart': true,
'message': "无感换号已启用",
'account': {
email: accountData.email,
membership_type: accountData.membership_type
}
});
} catch (appDir) {
console.error("[蜂鸟Pro] Inject error:", appDir);
if (appDir.code === "EPERM" || appDir.code === "EACCES") {
const errorMsg = "没有写入权限";
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': errorMsg,
'needAdmin': true
});
return;
}
this._postMessage({
'type': "seamlessInjected",
'success': false,
'error': appDir.message || '注入失败'
});
}
}
async _handleRestoreSeamless() {
try {
const workbenchPath = await this._getWorkbenchPathAsync();
if (!workbenchPath) {
this._postMessage({
'type': "seamlessRestored",
'success': false,
'error': '未找到Cursor安装目录'
});
return;
}
let fileContent = fs.readFileSync(workbenchPath, "utf-8");
if (!this._checkInjected(fileContent)) {
return;
}
fileContent = fileContent.replace("_showNotification(){/*i0*/}_showNotificationOld(){", "_showNotification(){");
const index = fileContent.indexOf("/*i1s*/");
const index1 = fileContent.indexOf("/*i1e*/");
if (index !== -1 && index1 !== -1) {
fileContent = fileContent.substring(0, index) + fileContent.substring(index1 + 7);
}
const index2 = fileContent.indexOf("/*i2s*/");
const index3 = fileContent.indexOf("/*i2e*/");
if (index2 !== -1 && index3 !== -1) {
fileContent = fileContent.substring(0, index2) + fileContent.substring(index3 + 7);
}
try {
fs.writeFileSync(workbenchPath, fileContent, "utf-8");
} catch (writeErr) {
if (writeErr.code === "EPERM" || writeErr.code === "EACCES") {
const errorMsg = "没有写入权限";
this._postMessage({
'type': "seamlessRestored",
'success': false,
'error': errorMsg,
'needAdmin': true
});
return;
}
throw writeErr;
}
// 通知后端释放锁定的账号
try {
const savedKey = this._context.globalState.get("hummingbird.key");
if (savedKey) {
await client_1.disableSeamless(savedKey);
}
} catch (disableErr) {
console.warn("[蜂鸟Pro] 通知后端禁用无感换号失败:", disableErr);
}
this._postMessage({
'type': "seamlessRestored",
'success': true,
'needRestart': true,
'message': "无感换号已禁用"
});
} catch (restoreErr) {
console.error("[蜂鸟Pro] Restore error:", restoreErr);
if (restoreErr.code === "EPERM" || restoreErr.code === "EACCES") {
const errorMsg = "没有写入权限";
this._postMessage({
'type': "seamlessRestored",
'success': false,
'error': errorMsg,
'needAdmin': true
});
return;
}
this._postMessage({
'type': "seamlessRestored",
'success': false,
'error': restoreErr.message || '还原失败'
});
}
}
async _handleToggleSeamless(enabled) {
try {
await client_1.updateSeamlessConfig({
'enabled': enabled
});
this._postMessage({
'type': "seamlessConfigUpdated",
'success': true,
'enabled': enabled
});
} catch (configErr) {
this._postMessage({
'type': "seamlessConfigUpdated",
'success': false,
'error': "更新配置失败"
});
}
}
async _handleGetUserSwitchStatus() {
try {
const savedKey = this._getActiveKey();
if (!savedKey) {
this._postMessage({
'type': "userSwitchStatus",
'valid': false,
'switchRemaining': 0,
'canSwitch': false,
'error': "未激活授权码"
});
return;
}
const status = await client_1.getUserSwitchStatus(savedKey);
let isFalse = false;
try {
const status1 = await client_1.getSeamlessStatus();
isFalse = status1.is_injected || false;
} catch (psOut2) {}
this._postMessage({
'type': 'userSwitchStatus',
...status,
'seamlessEnabled': isFalse
});
} catch (e24) {
this._postMessage({
'type': "userSwitchStatus",
'valid': false,
'switchRemaining': 0,
'canSwitch': false,
'error': "获取状态失败"
});
}
}
async _handleGetAccountUsage(forceRefresh) {
try {
if (!forceRefresh) {
this._postMessage({
'type': "accountUsage",
'success': false,
'error': "未提供账号邮箱"
});
return;
}
const result1 = client_1.getApiUrl() + "/api/cursor-accounts/query?email=" + encodeURIComponent(forceRefresh) + '&refresh=true';
const cursorRunning = await fetch(result1);
const result = await cursorRunning.json();
if (result.success && result.data) {
this._postMessage({
'type': "accountUsage",
'success': true,
'data': result.data
});
const condition = result.data.usage || {};
const condition1 = condition.totalUsageCount || 0;
const num = parseFloat(condition.totalCostUSD || 0);
extension_1.updateUsageStatusBar(condition1, num);
} else {
this._postMessage({
'type': "accountUsage",
'success': false,
'error': result.error || "获取用量失败"
});
}
} catch (announceErr) {
this._postMessage({
'type': "accountUsage",
'success': false,
'error': announceErr.message || "请求失败"
});
}
}
async _handleGetAnnouncement() {
try {
const result1 = client_1.getApiUrl() + "/api/announcements/latest";
const switchCheck = await fetch(result1);
const result = await switchCheck.json();
if (result.success && result.data) {
this._postMessage({
'type': "announcement",
'success': true,
'data': result.data
});
} else {
this._postMessage({
'type': "announcement",
'success': false,
'error': result.error || "获取公告失败"
});
}
} catch (versionErr) {
this._postMessage({
'type': "announcement",
'success': false,
'error': versionErr.message || "请求失败"
});
}
}
async _handleCheckVersion() {
try {
const result = await client_1.getLatestVersion();
if (result.success && result.version) {
const versionInfo = result.version;
const seamlessPath = HummingbirdProViewProvider.CURRENT_VERSION;
const isMatch = this._compareVersions(versionInfo, seamlessPath) > 0;
this._postMessage({
'type': "versionCheck",
'success': true,
'currentVersion': seamlessPath,
'latestVersion': versionInfo,
'hasUpdate': isMatch
});
} else {
this._postMessage({
'type': "versionCheck",
'success': false,
'currentVersion': HummingbirdProViewProvider.CURRENT_VERSION,
'error': result.error || "获取版本失败"
});
}
} catch (runningErr) {
this._postMessage({
'type': "versionCheck",
'success': false,
'currentVersion': HummingbirdProViewProvider.CURRENT_VERSION,
'error': runningErr.message || "请求失败"
});
}
}
_compareVersions(toggleArg, silentArg) {
const mapped = toggleArg.split('.').map(Number);
const mapped1 = silentArg.split('.').map(Number);
const beforeSwitch = Math.max(mapped.length, mapped1.length);
for (let count = 0; count < beforeSwitch; count++) {
const condition = mapped[count] || 0;
const condition1 = mapped1[count] || 0;
if (condition > condition1) {
return 1;
}
if (condition < condition1) {
return -1;
}
}
return 0;
}
async _handleGetCursorRunningPath() {
try {
const platform = process.platform;
let filePath = "未找到";
let str = '';
const config = vscode.workspace.getConfiguration("hummingbird");
const configValue = config.get("cursorPath");
if (configValue && fs.existsSync(configValue)) {
filePath = configValue;
if (platform === "darwin") {
str = path.join(configValue, "Contents", "Resources", "app", "package.json");
} else {
str = path.join(configValue, "resources", "app", "package.json");
}
console.log("[蜂鸟Pro] 使用用户配置的路径:", configValue);
} else {
if (platform === "win32") {
try {
const {
stdout: manualErr
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
const matchResult = manualErr.match(/ExecutablePath=(.+)/);
if (matchResult && matchResult[1]) {
const trimmed = matchResult[1].trim();
filePath = path.dirname(trimmed);
str = path.join(filePath, "resources", "app", "package.json");
}
} catch (beforeErr) {
console.log("[蜂鸟Pro] WMIC 获取路径失败:", beforeErr);
}
if (filePath === "未找到") {
const condition = process.env.LOCALAPPDATA || '';
const items = [path.join(condition, "Programs", 'cursor'), path.join(condition, "cursor")];
for (const originalCode of items) {
const joinedPath = path.join(originalCode, "resources", "app", "package.json");
if (fs.existsSync(joinedPath)) {
filePath = originalCode;
str = joinedPath;
break;
}
}
}
} else {
if (platform === "darwin") {
filePath = (await this._getCursorInstallPath()) || "/Applications/Cursor.app";
str = path.join(filePath, "Contents", "Resources", 'app', "package.json");
} else {
const condition = process.env.HOME || '';
const items = ["/usr/share/cursor", path.join(condition, ".local", "share", "cursor")];
for (const backupDir of items) {
if (fs.existsSync(backupDir)) {
filePath = backupDir;
str = path.join(backupDir, "resources", 'app', "package.json");
break;
}
}
}
}
}
const condition = str && fs.existsSync(str);
let str1 = '';
if (condition) {
try {
const fileContent = fs.readFileSync(str, "utf-8");
const parsed = JSON.parse(fileContent);
str1 = parsed.version || '';
console.log("[蜂鸟Pro] 从路径获取 Cursor 版本:", str1);
} catch (backupErr) {
console.log("[蜂鸟Pro] 读取 package.json 失败:", backupErr);
}
}
this._postMessage({
'type': 'cursorRunningPath',
'path': filePath,
'packageJsonPath': str,
'packageExists': condition,
'cursorVersion': str1
});
} catch (codeItem) {
this._postMessage({
'type': "cursorRunningPath",
'path': "获取失败: " + (codeItem.message || codeItem),
'packageJsonPath': '',
'packageExists': false,
'cursorVersion': ''
});
}
}
async _handleCheckUsageBeforeSwitch(silent) {
try {
const savedKey = this._getActiveKey();
if (!savedKey) {
this._postMessage({
'type': "usageCheckResult",
'success': false,
'error': "未激活授权码"
});
return;
}
if (!silent) {
this._postMessage({
'type': "usageCheckResult",
'success': true,
'needConfirm': false
});
return;
}
const result1 = client_1.getApiUrl() + '/api/cursor-accounts/query?email=' + encodeURIComponent(silent) + "&refresh=false";
const seamlessBackup = await fetch(result1);
const result = await seamlessBackup.json();
if (result.success && result.data) {
const condition = result.data.usage || {};
const num = parseFloat(condition.totalCostUSD || 0);
if (num < 10) {
this._postMessage({
'type': "usageCheckResult",
'success': true,
'needConfirm': true,
'costUSD': num.toFixed(2),
'email': silent
});
} else {
this._postMessage({
'type': "usageCheckResult",
'success': true,
'needConfirm': false
});
}
} else {
this._postMessage({
'type': "usageCheckResult",
'success': true,
'needConfirm': false
});
}
} catch (execOut) {
this._postMessage({
'type': 'usageCheckResult',
'success': true,
'needConfirm': false
});
}
}
async _handleManualSeamlessSwitch() {
try {
// 获取当前选择的号池
const selectedPool = this._context.globalState.get("hummingbird.selectedPool") || "auto";
const savedKey = selectedPool === "pro"
? this._context.globalState.get("hummingbird.proKey")
: this._context.globalState.get("hummingbird.autoKey");
if (!savedKey) {
this._postMessage({
'type': "manualSeamlessSwitched",
'success': false,
'error': selectedPool === "pro" ? "未激活Pro授权码" : "未激活Auto授权码"
});
return;
}
const switchResult = await client_1.switchSeamlessToken(savedKey);
if (switchResult.switched) {
if (switchResult.email) {
await this._context.globalState.update("hummingbird.seamlessCurrentAccount", switchResult.email);
}
// 写入 token 到本地 Cursor 存储
if (switchResult.data) {
try {
await this._writeAccountToLocal(switchResult.data);
console.log("[蜂鸟Pro] 换号成功,已写入新 token");
} catch (writeErr) {
console.error("[蜂鸟Pro] 写入 token 失败:", writeErr);
}
}
this._postMessage({
'type': "manualSeamlessSwitched",
'success': true,
'email': switchResult.email,
'switchRemaining': switchResult.switchRemaining
});
} else {
const condition = switchResult.message || switchResult.error || "换号失败";
this._postMessage({
'type': "manualSeamlessSwitched",
'success': false,
'error': condition
});
}
} catch (tmpErr6) {
const condition = tmpErr6?.message || "连接服务器失败";
this._postMessage({
'type': "manualSeamlessSwitched",
'success': false,
'error': condition
});
}
}
async _handleSelectPool(pool) {
// 切换号池选择 (auto / pro)
if (pool === "auto" || pool === "pro") {
await this._context.globalState.update("hummingbird.selectedPool", pool);
this._postMessage({
'type': "poolSelected",
'pool': pool
});
console.log("[蜂鸟Pro] 切换号池:", pool);
}
}
async _handleClearKey(keyType) {
// 清除指定类型的密钥
if (keyType === "auto") {
await this._context.globalState.update("hummingbird.autoKey", undefined);
await this._context.globalState.update("hummingbird.autoExpireDate", undefined);
await this._context.globalState.update("hummingbird.autoMergedCount", undefined);
await this._context.globalState.update("hummingbird.key", undefined);
await this._context.globalState.update("hummingbird.expireDate", undefined);
} else if (keyType === "pro") {
await this._context.globalState.update("hummingbird.proKey", undefined);
await this._context.globalState.update("hummingbird.proQuota", undefined);
await this._context.globalState.update("hummingbird.proQuotaUsed", undefined);
await this._context.globalState.update("hummingbird.proMergedCount", undefined);
}
// 如果两个池都空了,清理公共状态
const remainingAuto = this._context.globalState.get("hummingbird.autoKey");
const remainingPro = this._context.globalState.get("hummingbird.proKey");
if (!remainingAuto && !remainingPro) {
await this._context.globalState.update("hummingbird.selectedPool", undefined);
await this._context.globalState.update("hummingbird.seamlessInjected", undefined);
await this._context.globalState.update("hummingbird.seamlessCurrentAccount", undefined);
await this._context.globalState.update("hummingbird.switchRemaining", undefined);
extension_1.hideStatusBar();
}
this._postMessage({
'type': "keyCleared",
'success': true,
'keyType': keyType
});
// 刷新状态
this._sendState();
this._checkKeyStatus();
}
async _handleGetCursorPath() {
try {
const platform = process.platform;
let str = '';
let str1 = '';
if (platform === "win32") {
try {
const {
stdout: patchErr
} = await execAsync("wmic process where \"name='Cursor.exe'\" get ExecutablePath /format:list 2>nul");
const matchResult = patchErr.match(/ExecutablePath=(.+)/);
if (matchResult && matchResult[1]) {
const trimmed = matchResult[1].trim();
str = path.dirname(trimmed);
}
} catch (shellOut) {
try {
const {
stdout: lineContent
} = await execAsync('powershell -Command "Get-Process Cursor -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path"');
if (lineContent.trim()) {
str = path.dirname(lineContent.trim());
}
} catch (restoreErr2) {
console.warn("[蜂鸟Pro] 获取进程路径失败:", restoreErr2);
}
}
const condition = process.env.APPDATA || '';
str1 = path.join(condition, "Cursor");
} else {
if (platform === "darwin") {
try {
const {
stdout: e34
} = await execAsync("ps aux | grep -i \"[C]ursor\" | head -1 | awk '{print $11}'");
if (e34.trim()) {
const trimmed = e34.trim();
const matchResult = trimmed.match(/(.+\.app)/);
if (matchResult) {
str = matchResult[1];
} else {
str = path.dirname(trimmed);
}
}
} catch (toggleErr2) {
console.warn("[蜂鸟Pro] 获取进程路径失败:", toggleErr2);
}
const condition = process.env.HOME || '';
str1 = path.join(condition, 'Library', "Application Support", "Cursor");
} else {
try {
const {
stdout: e35
} = await execAsync("ps aux | grep -i \"[c]ursor\" | head -1 | awk '{print $11}'");
if (e35.trim()) {
str = path.dirname(e35.trim());
}
} catch (seamlessErr2) {
console.warn("[蜂鸟Pro] 获取进程路径失败:", seamlessErr2);
}
const condition = process.env.HOME || '';
str1 = path.join(condition, ".config", "Cursor");
}
}
if (!str) {
str = "未检测到运行中的Cursor进程";
}
let str2 = '';
if (str && !str.includes("未检测")) {
if (platform === "win32") {
str2 = path.join(str, 'resources', "app", 'out', 'vs', 'workbench', "workbench.desktop.main.js");
} else {
if (platform === "darwin") {
str2 = path.join(str, "Contents", "Resources", "app", "out", 'vs', "workbench", 'workbench.desktop.main.js');
} else {
str2 = path.join(str, "resources", "app", "out", 'vs', "workbench", "workbench.desktop.main.js");
}
}
if (!fs.existsSync(str2)) {
str2 = (await this._getWorkbenchPathAsync()) || "未找到";
}
} else {
str2 = (await this._getWorkbenchPathAsync()) || "未找到";
}
const value = str && !str.includes("未检测") ? fs.existsSync(str) : false;
const value1 = str1 ? fs.existsSync(str1) : false;
this._postMessage({
'type': "cursorPath",
'cursorPath': value ? str : str || "未找到",
'dataPath': value1 ? str1 : "未找到",
'workbenchPath': str2,
'platform': platform
});
} catch (e37) {
this._postMessage({
'type': "cursorPath",
'cursorPath': "获取失败",
'dataPath': '获取失败',
'workbenchPath': "获取失败",
'error': e37.message
});
}
}
async _loadAccountsFromDB() {
try {
const patchedContent = account_1.getCursorPaths();
const {
dbPath: psOut3
} = patchedContent;
if (!fs.existsSync(psOut3)) {
return [];
}
const workbenchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/accessToken");
const patchContent = await sqlite_1.sqliteGet(psOut3, "cursorAuth/refreshToken");
const email = await sqlite_1.sqliteGet(psOut3, "cursorAuth/cachedEmail");
if (workbenchContent && email) {
return [{
'email': email,
'access_token': workbenchContent,
'refresh_token': patchContent || workbenchContent
}];
}
return [];
} catch (e38) {
console.error("[蜂鸟Pro] 读取账号失败:", e38);
return [];
}
}
async _sendState() {
// 获取双密钥状态
const autoKey = this._context.globalState.get("hummingbird.autoKey");
const proKey = this._context.globalState.get("hummingbird.proKey");
const selectedPool = this._context.globalState.get("hummingbird.selectedPool") || "auto";
// Auto 密钥信息
const autoExpireDate = this._context.globalState.get('hummingbird.autoExpireDate');
const autoMergedCount = this._context.globalState.get("hummingbird.autoMergedCount") || 0;
// Pro 密钥信息
const proQuota = this._context.globalState.get("hummingbird.proQuota");
const proQuotaUsed = this._context.globalState.get("hummingbird.proQuotaUsed") || 0;
const proMergedCount = this._context.globalState.get("hummingbird.proMergedCount") || 0;
// 兼容旧字段
const savedKey = this._context.globalState.get("hummingbird.key");
const expireDate = this._context.globalState.get('hummingbird.expireDate');
const switchData = this._context.globalState.get("hummingbird.switchRemaining");
const switchData1 = this._context.globalState.get("hummingbird.switchLimit");
const cursorversionResult = await this._getCursorVersion();
const restoreCode = client_1.getOnlineStatus();
this._postMessage({
'type': "state",
// 双密钥状态
'autoKey': autoKey || '',
'proKey': proKey || '',
'selectedPool': selectedPool,
'autoExpireDate': autoExpireDate || '',
'autoMergedCount': autoMergedCount,
'proQuota': proQuota || 0,
'proQuotaUsed': proQuotaUsed,
'proQuotaRemaining': (proQuota || 0) - proQuotaUsed,
'proMergedCount': proMergedCount,
// 激活状态:任一密钥有效即为已激活
'isActivated': !!(autoKey || proKey || savedKey),
// 兼容旧字段
'key': savedKey || autoKey || '',
'expireDate': expireDate || autoExpireDate || '',
'switchRemaining': switchData ?? 0,
'switchLimit': switchData1 ?? 100,
'cursorVersion': cursorversionResult,
'isOnline': restoreCode
});
}
async _handleRetryConnect() {
try {
const savedKey = this._context.globalState.get("hummingbird.key");
if (savedKey) {
await client_1.verifyKey(savedKey);
} else {
const result = client_1.getApiUrl() + '/api/announcements/latest';
await fetch(result, {
'method': 'GET'
});
}
await this._sendState();
this._postMessage({
'type': "networkStatus",
'online': true
});
} catch (execErr) {
console.error("[蜂鸟Pro] Retry connect failed:", execErr);
this._postMessage({
'type': "networkStatus",
'online': false
});
}
}
async _getCursorVersion() {
try {
const platform = process.platform;
const items = [];
const cursorPath = await this._getCursorInstallPath();
if (cursorPath) {
if (platform === "darwin") {
items.push(path.join(cursorPath, "Contents", "Resources", "app", 'package.json'));
} else {
items.push(path.join(cursorPath, "resources", 'app', "package.json"));
}
}
if (platform === "win32") {
const condition = process.env.LOCALAPPDATA || '';
const condition1 = process.env.USERPROFILE || '';
const condition2 = process.env.ProgramFiles || "C:\\Program Files";
const condition3 = process.env['ProgramFiles(x86)'] || "C:\\Program Files (x86)";
items.push(path.join(condition, "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition, "Programs", "cursor", "resources", 'app', "package.json"), path.join(condition1, "AppData", "Local", "Programs", "Cursor", "resources", "app", "package.json"), path.join(condition2, "Cursor", "resources", 'app', "package.json"), path.join(condition2, "cursor", "resources", "app", "package.json"), path.join(condition3, "Cursor", "resources", "app", "package.json"));
} else {
if (platform === "darwin") {
items.push("/Applications/Cursor.app/Contents/Resources/app/package.json");
} else {
const condition = process.env.HOME || '';
items.push("/usr/share/cursor/resources/app/package.json", "/opt/Cursor/resources/app/package.json", "/opt/cursor/resources/app/package.json", path.join(condition, ".local", 'share', "cursor", "resources", 'app', "package.json"));
}
}
for (const seamlessCode of items) {
try {
if (fs.existsSync(seamlessCode)) {
const fileContent = fs.readFileSync(seamlessCode, "utf-8");
const parsed = JSON.parse(fileContent);
if (parsed.version) {
console.log("[蜂鸟Pro] 找到 Cursor 版本:", parsed.version, "路径:", seamlessCode);
return parsed.version;
}
}
} catch (fsErr) {
console.log("[蜂鸟Pro] 尝试路径失败:", seamlessCode, fsErr);
}
}
try {
const module = require("vscode");
if (module.version) {
console.log("[蜂鸟Pro] 使用 VS Code API 获取版本:", module.version);
return module.version;
}
} catch (cmdOut2) {}
console.log("[蜂鸟Pro] 未找到 Cursor 版本,尝试的路径:", items);
return '未知';
} catch (finalErr) {
console.error("[蜂鸟Pro] 获取 Cursor 版本失败:", finalErr);
return '未知';
}
}
_postMessage(message) {
this._view?.webview.postMessage(message);
}
_getNonce() {
let str = '';
const items = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let count = 0; count < 32; count++) {
str += items.charAt(Math.floor(Math.random() * items.length));
}
return str;
}
_getHtmlContent(lineStr) {
const nonce = this._getNonce();
// 从外部文件读取 HTML
const htmlPath = path.join(__dirname, 'panel.html');
let html = fs.readFileSync(htmlPath, 'utf8');
// 替换占位符
html = html.replace(/\{\{NONCE\}\}/g, nonce);
html = html.replace(/\{\{CSP_SOURCE\}\}/g, lineStr.cspSource);
return html;
}
}
// ========== 原始内嵌HTML代码已移至 panel.html ==========
// 以下为占位注释,保持文件结构不变
exports.HummingbirdProViewProvider = HummingbirdProViewProvider;
HummingbirdProViewProvider.CURRENT_VERSION = '2.1.5';