'use strict'; // ============================================ // CursorPro 无感换号模块 - 详细分析 // ============================================ const vscode = require('vscode'); const client = require('./api/client'); const account = require('./utils/account'); /** * ============================================ * 无感换号 (Seamless Mode) 工作原理 * ============================================ * * 核心思路: * 1. 用户配置一个"账号池",包含多个 Cursor 账号的 token * 2. 当检测到当前账号额度用尽或即将用尽时 * 3. 自动从账号池中选择下一个可用账号 * 4. 无缝切换到新账号,用户无感知 * * 关键 API 端点: * - /api/seamless/status 获取无缝模式状态 * - /api/seamless/config 获取/更新无缝配置 * - /api/seamless/inject 注入无缝模式到本地 * - /api/seamless/restore 恢复原始设置 * - /api/seamless/accounts 获取账号池列表 * - /api/seamless/token 获取指定账号的 token * - /api/seamless/switch 切换到指定账号 */ // ============================================ // 无缝模式配置结构 // ============================================ /** * @typedef {Object} SeamlessConfig * @property {boolean} enabled - 是否启用无缝模式 * @property {string} mode - 切换模式: 'auto' | 'manual' * @property {number} switchThreshold - 切换阈值 (剩余额度百分比) * @property {string[]} accountPool - 账号池 (userKey 列表) * @property {number} currentIndex - 当前使用的账号索引 */ const defaultSeamlessConfig = { enabled: false, mode: 'auto', // 自动切换 switchThreshold: 10, // 当剩余额度低于 10% 时切换 accountPool: [], currentIndex: 0 }; // ============================================ // 无缝模式核心函数 // ============================================ /** * 获取无缝模式状态 * 检查服务端是否支持无缝模式,以及当前用户是否有权使用 */ async function getSeamlessStatus() { return client.request('/api/seamless/status'); } /** * 获取无缝模式配置 * 从服务端获取用户的无缝模式配置 */ async function getSeamlessConfig() { return client.request('/api/seamless/config'); } /** * 更新无缝模式配置 * @param {SeamlessConfig} config - 新的配置 */ async function updateSeamlessConfig(config) { return client.request('/api/seamless/config', 'POST', config); } /** * 获取用户切换状态 * 检查指定用户当前的使用状态,判断是否需要切换 * @param {string} userKey - 用户标识 */ async function getUserSwitchStatus(userKey) { return client.request('/api/seamless/user-status?key=' + encodeURIComponent(userKey)); } /** * 注入无缝模式 * 将无缝模式的配置写入本地 Cursor * * 这是无感换号的核心! * 它会修改 Cursor 的认证配置,使其指向一个代理服务器 * 代理服务器会自动处理账号切换 * * @param {string} apiUrl - 无缝模式的 API 代理地址 * @param {string} userKey - 用户标识 */ async function injectSeamless(apiUrl, userKey) { const result = await client.request('/api/seamless/inject', 'POST', { api_url: apiUrl, user_key: userKey }); if (result.success && result.data) { // 将返回的账号数据写入本地 // 这里的关键是:写入的 token 是代理服务器的 token // 代理服务器会根据使用情况自动切换真实账号 await account.writeAccountToLocal(result.data); } return result; } /** * 恢复原始设置 * 移除无缝模式,恢复到单账号模式 */ async function restoreSeamless() { return client.request('/api/seamless/restore', 'POST'); } /** * 获取账号池列表 * 返回用户配置的所有账号 */ async function getSeamlessAccounts() { return client.request('/api/seamless/accounts'); } /** * 同步账号池 * 将本地账号列表同步到服务端 * @param {Array} accounts - 账号列表 */ async function syncSeamlessAccounts(accounts) { return client.request('/api/seamless/accounts', 'POST', { accounts }); } /** * 获取指定账号的 Token * @param {string} userKey - 用户标识 */ async function getSeamlessToken(userKey) { return client.request('/api/seamless/token?key=' + encodeURIComponent(userKey)); } /** * 手动切换到指定账号 * @param {string} userKey - 要切换到的账号标识 */ async function switchSeamlessToken(userKey) { const result = await client.request('/api/seamless/switch', 'POST', { mode: 'seamless', userKey: userKey }); if (result.success && result.data) { await account.writeAccountToLocal(result.data); } return result; } // ============================================ // 无感换号流程图 // ============================================ /** * * ┌─────────────────────────────────────────────────────────────────┐ * │ 无感换号工作流程 │ * ├─────────────────────────────────────────────────────────────────┤ * │ │ * │ ┌──────────────┐ │ * │ │ 用户请求 │ │ * │ │ (使用 Cursor) │ │ * │ └──────┬───────┘ │ * │ │ │ * │ ▼ │ * │ ┌──────────────┐ ┌──────────────┐ │ * │ │ Cursor 客户端 │────▶│ 代理服务器 │ (CursorPro API) │ * │ │ (本地修改后) │ │ │ │ * │ └──────────────┘ └──────┬───────┘ │ * │ │ │ * │ ▼ │ * │ ┌──────────────┐ │ * │ │ 检查当前账号 │ │ * │ │ 额度是否充足 │ │ * │ └──────┬───────┘ │ * │ │ │ * │ ┌───────────────┼───────────────┐ │ * │ │ │ │ │ * │ ▼ ▼ ▼ │ * │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ * │ │ 账号 A │ │ 账号 B │ │ 账号 C │ (账号池) │ * │ │ 额度:5% │ │ 额度:80% │ │ 额度:60% │ │ * │ └─────────┘ └────┬────┘ └─────────┘ │ * │ │ │ * │ ▼ │ * │ ┌──────────────┐ │ * │ │ 使用账号 B │ (额度最充足) │ * │ │ 转发请求 │ │ * │ └──────┬───────┘ │ * │ │ │ * │ ▼ │ * │ ┌──────────────┐ │ * │ │ Cursor API │ │ * │ │ (官方服务器) │ │ * │ └──────┬───────┘ │ * │ │ │ * │ ▼ │ * │ ┌──────────────┐ │ * │ │ 返回结果给 │ │ * │ │ 用户 │ │ * │ └──────────────┘ │ * │ │ * │ 用户全程无感知,只要账号池中有任一账号有额度,就能继续使用 │ * │ │ * └─────────────────────────────────────────────────────────────────┘ * */ // ============================================ // 无感换号的技术实现细节 // ============================================ /** * 关键技术点: * * 1. 代理注入 * - 修改本地 Cursor 的 API 端点指向代理服务器 * - 所有请求先经过代理,代理决定使用哪个真实账号 * * 2. Token 管理 * - 代理服务器维护账号池的所有 token * - 根据各账号的额度情况动态选择 * * 3. 切换策略 * - 自动模式:当前账号额度 < 阈值时自动切换 * - 手动模式:用户手动选择要使用的账号 * * 4. 本地写入的数据 * - accessToken: 代理服务器生成的特殊 token * - refreshToken: 用于刷新代理 token * - 设备 ID: 统一使用代理分配的 ID,避免被检测 */ const seamlessModule = { getSeamlessStatus, getSeamlessConfig, updateSeamlessConfig, getUserSwitchStatus, injectSeamless, restoreSeamless, getSeamlessAccounts, syncSeamlessAccounts, getSeamlessToken, switchSeamlessToken }; module.exports = seamlessModule;