备份: 完整开发状态(含反混淆脚本和临时文件)
This commit is contained in:
228
extension_clean/LICENSE.txt
Normal file
228
extension_clean/LICENSE.txt
Normal file
@@ -0,0 +1,228 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 CursorPro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
extension_clean/cursorpro-0.4.5.vsix
Normal file
BIN
extension_clean/cursorpro-0.4.5.vsix
Normal file
Binary file not shown.
BIN
extension_clean/hummingbird-cursorpro-2.0.0.vsix
Normal file
BIN
extension_clean/hummingbird-cursorpro-2.0.0.vsix
Normal file
Binary file not shown.
BIN
extension_clean/hummingbird-cursorpro-2.0.1.vsix
Normal file
BIN
extension_clean/hummingbird-cursorpro-2.0.1.vsix
Normal file
Binary file not shown.
5
extension_clean/media/icon.svg
Normal file
5
extension_clean/media/icon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
||||
<path d="M2 17l10 5 10-5"/>
|
||||
<path d="M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
258
extension_clean/out/api/client.js
Normal file
258
extension_clean/out/api/client.js
Normal file
@@ -0,0 +1,258 @@
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// CursorPro API Client - 反混淆版本
|
||||
// ============================================
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const vscode = require('vscode');
|
||||
|
||||
// 默认 API 地址
|
||||
const DEFAULT_API_URL = 'https://api.aicode.edu.pl';
|
||||
const REQUEST_TIMEOUT = 15000; // 15秒超时
|
||||
|
||||
let isOnline = true;
|
||||
let onlineStatusCallbacks = [];
|
||||
|
||||
/**
|
||||
* 获取 API URL (从配置或使用默认值)
|
||||
*/
|
||||
function getApiUrl() {
|
||||
const config = vscode.workspace.getConfiguration('cursorpro');
|
||||
return config.get('apiUrl') || DEFAULT_API_URL;
|
||||
}
|
||||
exports.getApiUrl = getApiUrl;
|
||||
|
||||
/**
|
||||
* 获取在线状态
|
||||
*/
|
||||
function getOnlineStatus() {
|
||||
return isOnline;
|
||||
}
|
||||
exports.getOnlineStatus = getOnlineStatus;
|
||||
|
||||
/**
|
||||
* 监听在线状态变化
|
||||
*/
|
||||
function onOnlineStatusChange(callback) {
|
||||
onlineStatusCallbacks.push(callback);
|
||||
return () => {
|
||||
onlineStatusCallbacks = onlineStatusCallbacks.filter(cb => cb !== callback);
|
||||
};
|
||||
}
|
||||
exports.onOnlineStatusChange = onOnlineStatusChange;
|
||||
|
||||
/**
|
||||
* 设置在线状态
|
||||
*/
|
||||
function setOnlineStatus(status) {
|
||||
if (isOnline !== status) {
|
||||
isOnline = status;
|
||||
onlineStatusCallbacks.forEach(callback => callback(status));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带超时的 fetch
|
||||
*/
|
||||
async function fetchWithTimeout(url, options, timeout) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal
|
||||
});
|
||||
clearTimeout(timeoutId);
|
||||
return response;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求函数
|
||||
*/
|
||||
async function request(endpoint, method = 'GET', body) {
|
||||
const url = `${getApiUrl()}${endpoint}`;
|
||||
const options = {
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
if (body) {
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetchWithTimeout(url, options, REQUEST_TIMEOUT);
|
||||
const data = await response.json();
|
||||
|
||||
setOnlineStatus(true);
|
||||
|
||||
if (!response.ok && data.error) {
|
||||
data.success = false;
|
||||
data.message = data.error;
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
// 检查是否是网络错误
|
||||
const isNetworkError = error.name === 'AbortError' ||
|
||||
error.name === 'TypeError' ||
|
||||
error.message?.includes('fetch') ||
|
||||
error.message?.includes('network') ||
|
||||
error.message?.includes('ECONNREFUSED') ||
|
||||
error.message?.includes('ENOTFOUND') ||
|
||||
error.message?.includes('ETIMEDOUT');
|
||||
|
||||
if (isNetworkError) {
|
||||
setOnlineStatus(false);
|
||||
return {
|
||||
success: false,
|
||||
error: '网络连接失败,请检查网络',
|
||||
isOffline: true
|
||||
};
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Key
|
||||
*/
|
||||
async function verifyKey(key) {
|
||||
return request('/api/verify-key', 'POST', { key });
|
||||
}
|
||||
exports.verifyKey = verifyKey;
|
||||
|
||||
/**
|
||||
* 切换账号
|
||||
*/
|
||||
async function switchAccount(key) {
|
||||
return request('/api/switch-account', 'POST', { key });
|
||||
}
|
||||
exports.switchAccount = switchAccount;
|
||||
|
||||
/**
|
||||
* 获取代理配置
|
||||
*/
|
||||
async function getProxyConfig() {
|
||||
return request('/api/proxy-config', 'GET');
|
||||
}
|
||||
exports.getProxyConfig = getProxyConfig;
|
||||
|
||||
/**
|
||||
* 更新代理配置
|
||||
*/
|
||||
async function updateProxyConfig(isEnabled, proxyUrl) {
|
||||
return request('/api/proxy-config', 'PUT', {
|
||||
is_enabled: isEnabled,
|
||||
proxy_url: proxyUrl
|
||||
});
|
||||
}
|
||||
exports.updateProxyConfig = updateProxyConfig;
|
||||
|
||||
// ============================================
|
||||
// 无感换号 (Seamless Mode) API
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 获取无缝模式状态
|
||||
*/
|
||||
async function getSeamlessStatus() {
|
||||
return request('/api/seamless/status');
|
||||
}
|
||||
exports.getSeamlessStatus = getSeamlessStatus;
|
||||
|
||||
/**
|
||||
* 获取用户切换状态
|
||||
*/
|
||||
async function getUserSwitchStatus(userKey) {
|
||||
return request('/api/seamless/user-status?userKey=' + encodeURIComponent(userKey));
|
||||
}
|
||||
exports.getUserSwitchStatus = getUserSwitchStatus;
|
||||
|
||||
/**
|
||||
* 获取无缝配置
|
||||
*/
|
||||
async function getSeamlessConfig() {
|
||||
return request('/api/seamless/config');
|
||||
}
|
||||
exports.getSeamlessConfig = getSeamlessConfig;
|
||||
|
||||
/**
|
||||
* 更新无缝配置
|
||||
*/
|
||||
async function updateSeamlessConfig(config) {
|
||||
return request('/api/seamless/config', 'POST', config);
|
||||
}
|
||||
exports.updateSeamlessConfig = updateSeamlessConfig;
|
||||
|
||||
/**
|
||||
* 注入无缝模式
|
||||
*/
|
||||
async function injectSeamless(apiUrl, userKey) {
|
||||
return request('/api/seamless/inject', 'POST', {
|
||||
api_url: apiUrl,
|
||||
user_key: userKey
|
||||
});
|
||||
}
|
||||
exports.injectSeamless = injectSeamless;
|
||||
|
||||
/**
|
||||
* 恢复无缝模式
|
||||
*/
|
||||
async function restoreSeamless() {
|
||||
return request('/api/seamless/restore', 'POST');
|
||||
}
|
||||
exports.restoreSeamless = restoreSeamless;
|
||||
|
||||
/**
|
||||
* 获取无缝账号列表
|
||||
*/
|
||||
async function getSeamlessAccounts() {
|
||||
return request('/api/seamless/accounts');
|
||||
}
|
||||
exports.getSeamlessAccounts = getSeamlessAccounts;
|
||||
|
||||
/**
|
||||
* 同步无缝账号
|
||||
*/
|
||||
async function syncSeamlessAccounts(accounts) {
|
||||
return request('/api/seamless/sync-accounts', 'POST', { accounts });
|
||||
}
|
||||
exports.syncSeamlessAccounts = syncSeamlessAccounts;
|
||||
|
||||
/**
|
||||
* 获取无缝 Token
|
||||
*/
|
||||
async function getSeamlessToken(userKey) {
|
||||
return request('/api/seamless/get-token?userKey=' + encodeURIComponent(userKey));
|
||||
}
|
||||
exports.getSeamlessToken = getSeamlessToken;
|
||||
|
||||
/**
|
||||
* 切换无缝 Token
|
||||
*/
|
||||
async function switchSeamlessToken(userKey) {
|
||||
return request('/api/seamless/switch-token', 'POST', {
|
||||
mode: 'manual',
|
||||
userKey: userKey
|
||||
});
|
||||
}
|
||||
exports.switchSeamlessToken = switchSeamlessToken;
|
||||
|
||||
/**
|
||||
* 获取最新版本
|
||||
*/
|
||||
async function getLatestVersion() {
|
||||
return request('/api/version');
|
||||
}
|
||||
exports.getLatestVersion = getLatestVersion;
|
||||
221
extension_clean/out/extension.js
Normal file
221
extension_clean/out/extension.js
Normal file
@@ -0,0 +1,221 @@
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// CursorPro Extension - 反混淆版本
|
||||
// ============================================
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const vscode = require('vscode');
|
||||
const provider_1 = require('./webview/provider');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let usageStatusBarItem;
|
||||
|
||||
// 创建输出通道
|
||||
exports.outputChannel = vscode.window.createOutputChannel('CursorPro');
|
||||
|
||||
/**
|
||||
* 日志函数
|
||||
*/
|
||||
function log(message) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
exports.outputChannel.appendLine('[' + timestamp + '] ' + message);
|
||||
console.log('[CursorPro] ' + message);
|
||||
}
|
||||
exports.log = log;
|
||||
|
||||
/**
|
||||
* 清理 Service Worker 缓存
|
||||
*/
|
||||
function cleanServiceWorkerCache() {
|
||||
try {
|
||||
const platform = process.platform;
|
||||
const cachePaths = [];
|
||||
|
||||
if (platform === 'win32') {
|
||||
const appData = process.env.APPDATA || '';
|
||||
const localAppData = process.env.LOCALAPPDATA || '';
|
||||
cachePaths.push(
|
||||
path.join(appData, 'Cursor', 'Service Worker'),
|
||||
path.join(localAppData, 'Cursor', 'Service Worker'),
|
||||
path.join(appData, 'Cursor', 'GPUCache'),
|
||||
path.join(localAppData, 'Cursor', 'GPUCache')
|
||||
);
|
||||
} else if (platform === 'darwin') {
|
||||
const home = process.env.HOME || '';
|
||||
cachePaths.push(
|
||||
path.join(home, 'Library', 'Application Support', 'Cursor', 'Service Worker'),
|
||||
path.join(home, 'Library', 'Caches', 'Cursor', 'Service Worker')
|
||||
);
|
||||
} else {
|
||||
const home = process.env.HOME || '';
|
||||
cachePaths.push(
|
||||
path.join(home, '.config', 'Cursor', 'Service Worker'),
|
||||
path.join(home, '.cache', 'Cursor', 'Service Worker')
|
||||
);
|
||||
}
|
||||
|
||||
for (const cachePath of cachePaths) {
|
||||
if (!fs.existsSync(cachePath)) continue;
|
||||
|
||||
// 清理 ScriptCache
|
||||
const scriptCachePath = path.join(cachePath, 'ScriptCache');
|
||||
if (fs.existsSync(scriptCachePath)) {
|
||||
try {
|
||||
const files = fs.readdirSync(scriptCachePath);
|
||||
for (const file of files) {
|
||||
try {
|
||||
fs.unlinkSync(path.join(scriptCachePath, file));
|
||||
} catch (e) {}
|
||||
}
|
||||
console.log('[CursorPro] Service Worker ScriptCache 已清理:', scriptCachePath);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 清理 CacheStorage
|
||||
const cacheStoragePath = path.join(cachePath, 'CacheStorage');
|
||||
if (fs.existsSync(cacheStoragePath)) {
|
||||
try {
|
||||
deleteFolderRecursive(cacheStoragePath);
|
||||
console.log('[CursorPro] Service Worker CacheStorage 已清理:', cacheStoragePath);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 清理 Database
|
||||
const databasePath = path.join(cachePath, 'Database');
|
||||
if (fs.existsSync(databasePath)) {
|
||||
try {
|
||||
deleteFolderRecursive(databasePath);
|
||||
console.log('[CursorPro] Service Worker Database 已清理:', databasePath);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[CursorPro] 清理 Service Worker 缓存时出错:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除文件夹
|
||||
*/
|
||||
function deleteFolderRecursive(folderPath) {
|
||||
if (fs.existsSync(folderPath)) {
|
||||
fs.readdirSync(folderPath).forEach(file => {
|
||||
const curPath = path.join(folderPath, file);
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteFolderRecursive(curPath);
|
||||
} else {
|
||||
try {
|
||||
fs.unlinkSync(curPath);
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
try {
|
||||
fs.rmdirSync(folderPath);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活扩展
|
||||
*/
|
||||
function activate(context) {
|
||||
// 清理 Service Worker 缓存
|
||||
cleanServiceWorkerCache();
|
||||
|
||||
// 创建 WebView Provider
|
||||
const viewProvider = new provider_1.CursorProViewProvider(context.extensionUri, context);
|
||||
|
||||
// 注册 WebView
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerWebviewViewProvider('cursorpro.mainView', viewProvider)
|
||||
);
|
||||
|
||||
// 创建状态栏项
|
||||
usageStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
||||
usageStatusBarItem.text = '$(dashboard) 用量: --';
|
||||
usageStatusBarItem.tooltip = '点击查看账号用量详情';
|
||||
usageStatusBarItem.command = 'cursorpro.showPanel';
|
||||
usageStatusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.prominentBackground');
|
||||
|
||||
// 如果有保存的 key,显示状态栏
|
||||
const savedKey = context.globalState.get('cursorpro.key');
|
||||
if (savedKey) {
|
||||
usageStatusBarItem.show();
|
||||
}
|
||||
|
||||
context.subscriptions.push(usageStatusBarItem);
|
||||
|
||||
// 设置同步的键
|
||||
context.globalState.setKeysForSync(['cursorpro.key']);
|
||||
|
||||
// 注册显示面板命令
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('cursorpro.showPanel', () => {
|
||||
vscode.commands.executeCommand('cursorpro.mainView.focus');
|
||||
})
|
||||
);
|
||||
}
|
||||
exports.activate = activate;
|
||||
|
||||
/**
|
||||
* 停用扩展
|
||||
*/
|
||||
function deactivate() {
|
||||
console.log('CursorPro 插件已停用');
|
||||
}
|
||||
exports.deactivate = deactivate;
|
||||
|
||||
/**
|
||||
* 显示状态栏
|
||||
*/
|
||||
function showStatusBar() {
|
||||
if (usageStatusBarItem) {
|
||||
usageStatusBarItem.show();
|
||||
}
|
||||
}
|
||||
exports.showStatusBar = showStatusBar;
|
||||
|
||||
/**
|
||||
* 隐藏状态栏
|
||||
*/
|
||||
function hideStatusBar() {
|
||||
if (usageStatusBarItem) {
|
||||
usageStatusBarItem.hide();
|
||||
}
|
||||
}
|
||||
exports.hideStatusBar = hideStatusBar;
|
||||
|
||||
/**
|
||||
* 更新用量状态栏
|
||||
* @param {number} requestCount - 请求次数
|
||||
* @param {number|string} usageCost - 已用额度
|
||||
*/
|
||||
function updateUsageStatusBar(requestCount, usageCost) {
|
||||
if (usageStatusBarItem) {
|
||||
const count = typeof requestCount === 'number' ? requestCount : requestCount;
|
||||
const cost = typeof usageCost === 'number' ? usageCost : parseFloat(usageCost.toString().replace('$', '')) || 0;
|
||||
const costDisplay = typeof usageCost === 'number' ? '$' + usageCost.toFixed(2) : usageCost;
|
||||
|
||||
usageStatusBarItem.text = '$(dashboard) ' + count + '次 | ' + costDisplay;
|
||||
usageStatusBarItem.tooltip = '请求次数: ' + count + '\n已用额度: ' + costDisplay + '\n点击查看详情';
|
||||
|
||||
// 根据用量设置颜色
|
||||
if (cost >= 10) {
|
||||
// 高用量 - 红色警告
|
||||
usageStatusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground');
|
||||
usageStatusBarItem.color = undefined;
|
||||
} else if (cost >= 5) {
|
||||
// 中用量 - 黄色警告
|
||||
usageStatusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
|
||||
usageStatusBarItem.color = undefined;
|
||||
} else {
|
||||
// 低用量 - 绿色
|
||||
usageStatusBarItem.backgroundColor = undefined;
|
||||
usageStatusBarItem.color = '#4ade80';
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.updateUsageStatusBar = updateUsageStatusBar;
|
||||
213
extension_clean/out/utils/account.js
Normal file
213
extension_clean/out/utils/account.js
Normal file
@@ -0,0 +1,213 @@
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// CursorPro Account Utils - 反混淆版本
|
||||
// ============================================
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const vscode = require('vscode');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const child_process = require('child_process');
|
||||
const util = require('util');
|
||||
const sqlite_1 = require('./sqlite');
|
||||
|
||||
const execAsync = util.promisify(child_process.exec);
|
||||
|
||||
/**
|
||||
* 获取 Cursor 相关路径
|
||||
* @returns {{dbPath: string, storagePath: string, machineidPath: string}}
|
||||
*/
|
||||
function getCursorPaths() {
|
||||
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const appData = process.env.APPDATA || '';
|
||||
return {
|
||||
dbPath: path.join(appData, 'Cursor', 'User', 'globalStorage', 'state.vscdb'),
|
||||
storagePath: path.join(appData, 'Cursor', 'User', 'globalStorage', 'storage.json'),
|
||||
machineidPath: path.join(appData, 'Cursor', 'machineid')
|
||||
};
|
||||
} else if (process.platform === 'darwin') {
|
||||
return {
|
||||
dbPath: path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'state.vscdb'),
|
||||
storagePath: path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'storage.json'),
|
||||
machineidPath: path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'machineid')
|
||||
};
|
||||
} else {
|
||||
// Linux
|
||||
return {
|
||||
dbPath: path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'state.vscdb'),
|
||||
storagePath: path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'storage.json'),
|
||||
machineidPath: path.join(homeDir, '.config', 'Cursor', 'machineid')
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.getCursorPaths = getCursorPaths;
|
||||
|
||||
/**
|
||||
* 将账号数据写入本地存储
|
||||
* @param {Object} accountData - 账号数据
|
||||
* @param {string} accountData.accessToken - 访问令牌
|
||||
* @param {string} accountData.refreshToken - 刷新令牌
|
||||
* @param {string} accountData.workosSessionToken - WorkOS 会话令牌
|
||||
* @param {string} accountData.email - 邮箱
|
||||
* @param {string} accountData.membership_type - 会员类型
|
||||
* @param {string} accountData.machineId - 机器 ID
|
||||
* @param {string} accountData.macMachineId - Mac 机器 ID
|
||||
* @param {string} accountData.devDeviceId - 开发设备 ID
|
||||
* @param {string} accountData.serviceMachineId - 服务机器 ID
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function writeAccountToLocal(accountData) {
|
||||
try {
|
||||
const cursorPaths = getCursorPaths();
|
||||
const { dbPath, storagePath, machineidPath } = cursorPaths;
|
||||
|
||||
console.log('[CursorPro] 数据库路径:', dbPath);
|
||||
console.log('[CursorPro] 数据库存在:', fs.existsSync(dbPath));
|
||||
console.log('[CursorPro] 账号数据:', JSON.stringify({
|
||||
hasAccessToken: !!accountData.accessToken,
|
||||
hasRefreshToken: !!accountData.refreshToken,
|
||||
hasWorkosToken: !!accountData.workosSessionToken,
|
||||
email: accountData.email
|
||||
}));
|
||||
|
||||
// 写入数据库
|
||||
if (fs.existsSync(dbPath)) {
|
||||
try {
|
||||
const entries = [];
|
||||
|
||||
if (accountData.accessToken) {
|
||||
entries.push(['cursorAuth/accessToken', accountData.accessToken]);
|
||||
}
|
||||
|
||||
if (accountData.refreshToken) {
|
||||
entries.push(['cursorAuth/refreshToken', accountData.refreshToken]);
|
||||
}
|
||||
|
||||
if (accountData.workosSessionToken) {
|
||||
entries.push(['cursorAuth/WorkosCursorSessionToken', accountData.workosSessionToken]);
|
||||
}
|
||||
|
||||
if (accountData.email) {
|
||||
entries.push(['cursorAuth/cachedEmail', accountData.email]);
|
||||
}
|
||||
|
||||
if (accountData.membership_type) {
|
||||
entries.push(['cursorAuth/stripeMembershipType', accountData.membership_type]);
|
||||
}
|
||||
|
||||
if (accountData.devDeviceId) {
|
||||
entries.push(['telemetry.devDeviceId', accountData.devDeviceId || 'default']);
|
||||
}
|
||||
|
||||
if (accountData.serviceMachineId) {
|
||||
entries.push(['serviceMachineId', accountData.serviceMachineId]);
|
||||
}
|
||||
|
||||
console.log('[CursorPro] 准备写入', entries.length, '个字段');
|
||||
|
||||
const success = await sqlite_1.sqliteSetBatch(dbPath, entries);
|
||||
if (!success) {
|
||||
throw new Error('数据库写入失败');
|
||||
}
|
||||
|
||||
console.log('[CursorPro] 已写入', entries.length, '个字段');
|
||||
} catch (error) {
|
||||
console.error('[CursorPro] 数据库写入错误:', error);
|
||||
vscode.window.showErrorMessage('数据库写入失败: ' + error);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.error('[CursorPro] 数据库文件不存在:', dbPath);
|
||||
vscode.window.showErrorMessage('[CursorPro] 数据库文件不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新 storage.json
|
||||
if (fs.existsSync(storagePath)) {
|
||||
const storageData = JSON.parse(fs.readFileSync(storagePath, 'utf-8'));
|
||||
|
||||
if (accountData.machineId) {
|
||||
storageData['telemetry.machineId'] = accountData.machineId;
|
||||
}
|
||||
|
||||
if (accountData.macMachineId) {
|
||||
storageData['telemetry.macMachineId'] = accountData.macMachineId;
|
||||
}
|
||||
|
||||
if (accountData.devDeviceId) {
|
||||
storageData['telemetry.devDeviceId'] = accountData.devDeviceId;
|
||||
}
|
||||
|
||||
if (accountData.serviceMachineId) {
|
||||
storageData['serviceMachineId'] = accountData.serviceMachineId;
|
||||
}
|
||||
|
||||
fs.writeFileSync(storagePath, JSON.stringify(storageData, null, 4));
|
||||
console.log('[CursorPro] storage.json 已更新');
|
||||
}
|
||||
|
||||
// 更新 machineid 文件
|
||||
if (accountData.machineId && machineidPath) {
|
||||
const machineIdDir = path.dirname(machineidPath);
|
||||
if (!fs.existsSync(machineIdDir)) {
|
||||
fs.mkdirSync(machineIdDir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(machineidPath, accountData.machineId);
|
||||
console.log('[CursorPro] machineid 文件已更新');
|
||||
}
|
||||
|
||||
// Windows: 更新注册表 (如果提供了 devDeviceId)
|
||||
if (accountData.devDeviceId && process.platform === 'win32') {
|
||||
try {
|
||||
const regCommand = 'reg add "HKCU\\Software\\Cursor" /v devDeviceId /t REG_SZ /d "' + accountData.devDeviceId + '" /f';
|
||||
await execAsync(regCommand);
|
||||
console.log('[CursorPro] 注册表已更新');
|
||||
} catch (error) {
|
||||
console.warn('[CursorPro] 注册表写入失败(可能需要管理员权限):', error);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[CursorPro] writeAccountToLocal 错误:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.writeAccountToLocal = writeAccountToLocal;
|
||||
|
||||
/**
|
||||
* 关闭 Cursor 进程
|
||||
*/
|
||||
async function closeCursor() {
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
await execAsync('taskkill /F /IM Cursor.exe').catch(() => {});
|
||||
} else {
|
||||
await execAsync('pkill -9 -f Cursor').catch(() => {});
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[CursorPro] 关闭 Cursor 失败:', error);
|
||||
}
|
||||
}
|
||||
exports.closeCursor = closeCursor;
|
||||
|
||||
/**
|
||||
* 提示重启 Cursor
|
||||
* @param {string} message - 提示消息
|
||||
*/
|
||||
async function promptRestartCursor(message) {
|
||||
const selection = await vscode.window.showInformationMessage(
|
||||
message,
|
||||
'立即重启',
|
||||
'稍后'
|
||||
);
|
||||
|
||||
if (selection === '立即重启') {
|
||||
await closeCursor();
|
||||
}
|
||||
}
|
||||
exports.promptRestartCursor = promptRestartCursor;
|
||||
176
extension_clean/out/utils/sqlite.js
Normal file
176
extension_clean/out/utils/sqlite.js
Normal file
@@ -0,0 +1,176 @@
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// CursorPro SQLite Utils - 反混淆版本
|
||||
// ============================================
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const child_process = require('child_process');
|
||||
const util = require('util');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const execAsync = util.promisify(child_process.exec);
|
||||
|
||||
/**
|
||||
* 转义 SQL 字符串中的单引号
|
||||
*/
|
||||
function escapeSqlString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
return String(value).replace(/'/g, "''");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 SQLite 命令
|
||||
*/
|
||||
async function execSqlite(dbPath, sql) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
try {
|
||||
if (isWindows) {
|
||||
// Windows: 直接使用 sqlite3 命令
|
||||
const escapedSql = sql.replace(/"/g, '\\"');
|
||||
const command = `sqlite3 "${dbPath}" "${escapedSql}"`;
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
encoding: 'utf-8',
|
||||
maxBuffer: 10 * 1024 * 1024
|
||||
});
|
||||
|
||||
if (stderr && !stderr.includes('-- Loading')) {
|
||||
console.warn('[SQLite] stderr:', stderr);
|
||||
}
|
||||
|
||||
return stdout.trim();
|
||||
} else {
|
||||
// Unix/Mac: 使用临时文件避免命令行转义问题
|
||||
const tmpFile = path.join(os.tmpdir(), 'cursor_sql_' + Date.now() + '.sql');
|
||||
fs.writeFileSync(tmpFile, sql, 'utf-8');
|
||||
|
||||
try {
|
||||
const command = `sqlite3 "${dbPath}" < "${tmpFile}"`;
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
encoding: 'utf-8',
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
shell: '/bin/bash'
|
||||
});
|
||||
|
||||
if (stderr && !stderr.includes('-- Loading')) {
|
||||
console.warn('[SQLite] stderr:', stderr);
|
||||
}
|
||||
|
||||
return stdout.trim();
|
||||
} finally {
|
||||
try {
|
||||
fs.unlinkSync(tmpFile);
|
||||
} catch (e) {
|
||||
// 忽略删除临时文件失败
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 检查是否是 sqlite3 不存在的错误
|
||||
if (error.code === 'ENOENT' ||
|
||||
error.message?.includes('not found') ||
|
||||
error.message?.includes('not recognized')) {
|
||||
throw new Error('sqlite3 命令未找到,请确保已安装 sqlite3');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 SQLite 数据库读取值
|
||||
*/
|
||||
async function sqliteGet(dbPath, key) {
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const sql = `SELECT value FROM ItemTable WHERE key = '${escapeSqlString(key)}';`;
|
||||
const result = await execSqlite(dbPath, sql);
|
||||
return result || null;
|
||||
} catch (error) {
|
||||
console.error('[SQLite] 读取失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.sqliteGet = sqliteGet;
|
||||
|
||||
/**
|
||||
* 向 SQLite 数据库写入值
|
||||
*/
|
||||
async function sqliteSet(dbPath, key, value) {
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const sql = `INSERT OR REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');`;
|
||||
await execSqlite(dbPath, sql);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[SQLite] 写入失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.sqliteSet = sqliteSet;
|
||||
|
||||
/**
|
||||
* 批量写入 SQLite 数据库
|
||||
*/
|
||||
async function sqliteSetBatch(dbPath, entries) {
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entries.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const statements = entries.map(([key, value]) =>
|
||||
`INSERT OR REPLACE INTO ItemTable (key, value) VALUES ('${escapeSqlString(key)}', '${escapeSqlString(value)}');`
|
||||
);
|
||||
const sql = 'BEGIN; ' + statements.join(' ') + ' COMMIT;';
|
||||
await execSqlite(dbPath, sql);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[SQLite] 批量写入失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.sqliteSetBatch = sqliteSetBatch;
|
||||
|
||||
/**
|
||||
* 批量读取 SQLite 数据库
|
||||
*/
|
||||
async function sqliteGetBatch(dbPath, keys) {
|
||||
const result = new Map();
|
||||
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.warn('[SQLite] 数据库文件不存在:', dbPath);
|
||||
keys.forEach(key => result.set(key, null));
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
for (const key of keys) {
|
||||
const value = await sqliteGet(dbPath, key);
|
||||
result.set(key, value);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[SQLite] 批量读取失败:', error);
|
||||
keys.forEach(key => result.set(key, null));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.sqliteGetBatch = sqliteGetBatch;
|
||||
1998
extension_clean/out/webview/provider.js
Normal file
1998
extension_clean/out/webview/provider.js
Normal file
File diff suppressed because one or more lines are too long
76
extension_clean/package.json
Normal file
76
extension_clean/package.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "hummingbird-cursorpro",
|
||||
"displayName": "蜂鸟Pro",
|
||||
"description": "蜂鸟Pro - Cursor 账号管理与智能换号工具",
|
||||
"version": "2.0.0",
|
||||
"publisher": "hummingbird",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fnpro/fnpro-extension"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.80.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"extensionKind": [
|
||||
"ui"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onStartupFinished"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "cursorpro.showPanel",
|
||||
"title": "蜂鸟Pro: 打开控制面板"
|
||||
},
|
||||
{
|
||||
"command": "cursorpro.switchAccount",
|
||||
"title": "蜂鸟Pro: 立即换号"
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "cursorpro-sidebar",
|
||||
"title": "蜂鸟Pro",
|
||||
"icon": "media/icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"cursorpro-sidebar": [
|
||||
{
|
||||
"type": "webview",
|
||||
"id": "cursorpro.mainView",
|
||||
"name": "控制面板"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "蜂鸟Pro",
|
||||
"properties": {
|
||||
"cursorpro.cursorPath": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "手动设置 Cursor 安装路径(如果自动检测失败)。例如:C:\\Program Files\\cursor 或 /Applications/Cursor.app"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "echo skip",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"lint": "eslint src --ext ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"esbuild": "^0.27.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
280
extension_clean/scripts/reset_cursor_macos.sh
Normal file
280
extension_clean/scripts/reset_cursor_macos.sh
Normal file
@@ -0,0 +1,280 @@
|
||||
#!/bin/bash
|
||||
# ==============================================
|
||||
# CursorPro - macOS 机器码重置脚本
|
||||
# 一次授权,永久免密
|
||||
# 纯 Shell 实现,不依赖 Python
|
||||
# ==============================================
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 路径定义
|
||||
CURSOR_APP="/Applications/Cursor.app"
|
||||
CURSOR_OUT="$CURSOR_APP/Contents/Resources/app/out"
|
||||
MAIN_JS="$CURSOR_OUT/main.js"
|
||||
UUID_PLIST="/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||
|
||||
# 用户数据路径
|
||||
USER_HOME="$HOME"
|
||||
CURSOR_DATA="$USER_HOME/Library/Application Support/Cursor"
|
||||
STORAGE_JSON="$CURSOR_DATA/User/globalStorage/storage.json"
|
||||
STATE_VSCDB="$CURSOR_DATA/User/globalStorage/state.vscdb"
|
||||
MACHINEID_FILE="$CURSOR_DATA/machineid"
|
||||
|
||||
# 备份目录
|
||||
BACKUP_DIR="$USER_HOME/CursorPro_backups"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}======================================${NC}"
|
||||
echo -e "${BLUE} CursorPro macOS 机器码重置工具${NC}"
|
||||
echo -e "${BLUE}======================================${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查 Cursor 是否安装
|
||||
if [ ! -d "$CURSOR_APP" ]; then
|
||||
echo -e "${RED}错误: 未找到 Cursor 应用${NC}"
|
||||
echo "请确保 Cursor 安装在 /Applications/Cursor.app"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建备份目录
|
||||
mkdir -p "$BACKUP_DIR" 2>/dev/null
|
||||
|
||||
# ============================================
|
||||
# 第一步:检测并设置权限(一次性)
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 1/7] 检查权限...${NC}"
|
||||
|
||||
NEED_SUDO=false
|
||||
|
||||
# 检查 main.js 权限
|
||||
if [ ! -w "$MAIN_JS" ] 2>/dev/null; then
|
||||
NEED_SUDO=true
|
||||
echo " - main.js: 需要授权"
|
||||
else
|
||||
echo -e " - main.js: ${GREEN}已有权限${NC}"
|
||||
fi
|
||||
|
||||
# 检查 UUID plist 权限(文件可能不存在)
|
||||
if [ -f "$UUID_PLIST" ]; then
|
||||
if [ ! -w "$UUID_PLIST" ] 2>/dev/null; then
|
||||
NEED_SUDO=true
|
||||
echo " - UUID plist: 需要授权"
|
||||
else
|
||||
echo -e " - UUID plist: ${GREEN}已有权限${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e " - UUID plist: ${YELLOW}文件不存在,跳过${NC}"
|
||||
fi
|
||||
|
||||
# 如果需要授权
|
||||
if [ "$NEED_SUDO" = true ]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}首次运行,需要管理员权限${NC}"
|
||||
echo -e "${YELLOW}授权后以后重置不再需要输入密码${NC}"
|
||||
echo ""
|
||||
|
||||
# 请求 sudo 权限
|
||||
sudo -v || { echo -e "${RED}授权失败${NC}"; exit 1; }
|
||||
|
||||
# 修改 Cursor 目录权限
|
||||
if [ ! -w "$MAIN_JS" ] 2>/dev/null; then
|
||||
echo " 正在修改 Cursor 目录权限..."
|
||||
sudo chown -R $(whoami) "$CURSOR_OUT" 2>/dev/null || true
|
||||
sudo chmod -R u+rw "$CURSOR_OUT" 2>/dev/null || true
|
||||
echo -e " ${GREEN}✓ Cursor 目录权限已修改${NC}"
|
||||
fi
|
||||
|
||||
# 修改 UUID plist 权限(如果文件存在)
|
||||
if [ -f "$UUID_PLIST" ] && [ ! -w "$UUID_PLIST" ] 2>/dev/null; then
|
||||
echo " 正在修改 UUID plist 权限..."
|
||||
sudo chown $(whoami) "$UUID_PLIST" 2>/dev/null || true
|
||||
echo -e " ${GREEN}✓ UUID plist 权限已修改${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ 权限设置完成!以后重置不再需要密码${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第二步:关闭 Cursor
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 2/7] 关闭 Cursor...${NC}"
|
||||
|
||||
if pgrep -x "Cursor" > /dev/null; then
|
||||
killall Cursor 2>/dev/null || true
|
||||
echo " 等待 Cursor 完全退出..."
|
||||
sleep 3
|
||||
echo -e " ${GREEN}✓ Cursor 已关闭${NC}"
|
||||
else
|
||||
echo -e " ${GREEN}✓ Cursor 未运行${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第三步:Patch main.js
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 3/7] Patch main.js...${NC}"
|
||||
|
||||
if [ ! -f "$MAIN_JS" ]; then
|
||||
echo -e " ${RED}警告: 未找到 main.js${NC}"
|
||||
else
|
||||
# 检查是否已经 patch 过
|
||||
if grep -q 'uuidgen' "$MAIN_JS" 2>/dev/null; then
|
||||
echo -e " ${GREEN}✓ main.js 已经 Patch 过,跳过${NC}"
|
||||
else
|
||||
# 检查目标字符串
|
||||
if grep -q 'ioreg -rd1 -c IOPlatformExpertDevice' "$MAIN_JS"; then
|
||||
# 备份到用户目录
|
||||
BACKUP_FILE="$BACKUP_DIR/main.js.backup_$(date +%s)"
|
||||
cp "$MAIN_JS" "$BACKUP_FILE" 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " 备份已创建: $BACKUP_FILE"
|
||||
fi
|
||||
|
||||
# 使用 perl 替换(macOS 自带,比 sed 更可靠处理特殊字符)
|
||||
perl -i -pe 's/ioreg -rd1 -c IOPlatformExpertDevice/UUID=\$(uuidgen | tr '"'"'[:upper:]'"'"' '"'"'[:lower:]'"'"');echo "IOPlatformUUID = "\$UUID";/g' "$MAIN_JS" 2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e " ${GREEN}✓ main.js Patch 成功${NC}"
|
||||
else
|
||||
echo -e " ${RED}✗ main.js Patch 失败${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e " ${YELLOW}警告: 未找到目标字符串,可能已 patch 或版本不兼容${NC}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第四步:重置系统 UUID
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 4/7] 重置系统 UUID...${NC}"
|
||||
|
||||
if [ ! -f "$UUID_PLIST" ]; then
|
||||
echo -e " ${YELLOW}提示: UUID plist 文件不存在,跳过${NC}"
|
||||
else
|
||||
NEW_SYS_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
/usr/libexec/PlistBuddy -c "Set :IOPlatformUUID $NEW_SYS_UUID" "$UUID_PLIST" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c "Add :IOPlatformUUID string $NEW_SYS_UUID" "$UUID_PLIST" 2>/dev/null || true
|
||||
echo -e " ${GREEN}✓ 系统 UUID 已重置: $NEW_SYS_UUID${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第五步:重置 storage.json
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 5/7] 重置 storage.json...${NC}"
|
||||
|
||||
# 生成新的机器码(64位十六进制)
|
||||
NEW_MACHINE_ID=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
|
||||
NEW_MAC_MACHINE_ID=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
|
||||
NEW_DEV_DEVICE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
NEW_SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}"
|
||||
|
||||
if [ -f "$STORAGE_JSON" ]; then
|
||||
# 备份
|
||||
cp "$STORAGE_JSON" "$BACKUP_DIR/storage.json.backup_$(date +%s)" 2>/dev/null
|
||||
|
||||
# 使用 sed 替换 JSON 中的值(macOS sed 语法)
|
||||
# machineId
|
||||
sed -i '' "s/\"telemetry\.machineId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.machineId\": \"$NEW_MACHINE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||||
|
||||
# macMachineId
|
||||
sed -i '' "s/\"telemetry\.macMachineId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.macMachineId\": \"$NEW_MAC_MACHINE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||||
|
||||
# devDeviceId
|
||||
sed -i '' "s/\"telemetry\.devDeviceId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.devDeviceId\": \"$NEW_DEV_DEVICE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||||
|
||||
# sqmId
|
||||
sed -i '' "s/\"telemetry\.sqmId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.sqmId\": \"$NEW_SQM_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||||
|
||||
echo -e " ${GREEN}✓ storage.json 已更新 (4个ID)${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}警告: 未找到 storage.json${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第六步:重置 SQLite 数据库
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 6/7] 重置 SQLite 数据库...${NC}"
|
||||
|
||||
NEW_SERVICE_MACHINE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
if [ -f "$STATE_VSCDB" ]; then
|
||||
# 备份
|
||||
cp "$STATE_VSCDB" "$BACKUP_DIR/state.vscdb.backup_$(date +%s)" 2>/dev/null
|
||||
|
||||
# 使用 sqlite3 命令(macOS 自带)
|
||||
sqlite3 "$STATE_VSCDB" "UPDATE ItemTable SET value = '$NEW_SERVICE_MACHINE_ID' WHERE key = 'storage.serviceMachineId';" 2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e " ${GREEN}✓ state.vscdb 已更新 (serviceMachineId)${NC}"
|
||||
else
|
||||
# 如果更新失败,尝试插入
|
||||
sqlite3 "$STATE_VSCDB" "INSERT OR REPLACE INTO ItemTable (key, value) VALUES ('storage.serviceMachineId', '$NEW_SERVICE_MACHINE_ID');" 2>/dev/null
|
||||
echo -e " ${GREEN}✓ state.vscdb 已更新${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e " ${YELLOW}警告: 未找到 state.vscdb${NC}"
|
||||
fi
|
||||
|
||||
# 更新 machineid 文件
|
||||
echo ""
|
||||
if [ -d "$(dirname "$MACHINEID_FILE")" ]; then
|
||||
echo "${NEW_MACHINE_ID:0:64}" > "$MACHINEID_FILE"
|
||||
echo -e " ${GREEN}✓ machineid 文件已更新${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}警告: 未找到 machineid 目录${NC}"
|
||||
fi
|
||||
|
||||
# 清理缓存
|
||||
rm -rf "$CURSOR_DATA/Cache" 2>/dev/null || true
|
||||
rm -rf "$CURSOR_DATA/CachedData" 2>/dev/null || true
|
||||
rm -rf "$CURSOR_DATA/GPUCache" 2>/dev/null || true
|
||||
echo -e " ${GREEN}✓ 缓存已清理${NC}"
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 第七步:重新打开 Cursor
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[步骤 7/7] 重新打开 Cursor...${NC}"
|
||||
|
||||
sleep 1
|
||||
open "$CURSOR_APP"
|
||||
echo -e " ${GREEN}✓ Cursor 已启动${NC}"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN} ✅ 机器码重置完成!${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo ""
|
||||
echo "已重置的内容:"
|
||||
echo " ✓ main.js (ioreg patch)"
|
||||
echo " ✓ storage.json (4个ID)"
|
||||
echo " ✓ state.vscdb (serviceMachineId)"
|
||||
echo " ✓ machineid 文件"
|
||||
echo " ✓ 缓存已清理"
|
||||
echo ""
|
||||
echo "新机器码信息:"
|
||||
echo " machineId: ${NEW_MACHINE_ID:0:32}..."
|
||||
echo " devDeviceId: $NEW_DEV_DEVICE_ID"
|
||||
echo " serviceMachineId: $NEW_SERVICE_MACHINE_ID"
|
||||
echo ""
|
||||
echo "备份位置: $BACKUP_DIR"
|
||||
echo ""
|
||||
echo -e "${BLUE}此窗口可以关闭${NC}"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user