蜂鸟Pro v2.0.1 - 基础框架版本 (待完善)

## 当前状态
- 插件界面已完成重命名 (cursorpro → hummingbird)
- 双账号池 UI 已实现 (Auto/Pro 卡片)
- 后端已切换到 MySQL 数据库
- 添加了 Cursor 官方用量 API 文档

## 已知问题 (待修复)
1. 激活时检查账号导致无账号时激活失败
2. 未启用无感换号时不应获取账号
3. 账号用量模块不显示 (seamless 未启用时应隐藏)
4. 积分显示为 0 (后端未正确返回)
5. Auto/Pro 双密钥逻辑混乱,状态不同步
6. 账号添加后无自动分析功能

## 下一版本计划
- 重构数据模型,优化账号状态管理
- 实现 Cursor API 自动分析账号
- 修复激活流程,不依赖账号
- 启用无感时才分配账号
- 完善账号用量实时显示

## 文件说明
- docs/系统设计文档.md - 完整架构设计
- cursor 官方用量接口.md - Cursor API 文档
- 参考计费/ - Vibeviewer 开源项目参考

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ccdojox-crypto
2025-12-18 11:21:52 +08:00
parent f310ca7b97
commit 73a71f198f
202 changed files with 19142 additions and 252 deletions

199
test_cursor_api.js Normal file
View File

@@ -0,0 +1,199 @@
/**
* Cursor 官方用量接口测试脚本
*/
const https = require('https');
const TOKEN = 'user_01KCP4PQM80HPAZA7NY8RFR1V6::eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxS0NQNFBRTTgwSFBBWkE3Tlk4UkZSMVY2IiwidGltZSI6IjE3NjU5NzQ3MTEiLCJyYW5kb21uZXNzIjoiNzMyNGMwOWItZTk2ZS00Y2YzIiwiZXhwIjoxNzcxMTU4NzExLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20iLCJ0eXBlIjoid2ViIn0.oy_GRvz-3hIUj5BlXahE1QeTb5NuOrM-3pqemw_FEQw';
const COOKIE = `WorkosCursorSessionToken=${TOKEN}`;
function request(options, postData = null) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve({ status: res.statusCode, data: JSON.parse(data) });
} catch (e) {
resolve({ status: res.statusCode, data: data });
}
});
});
req.on('error', reject);
if (postData) req.write(postData);
req.end();
});
}
function getHeaders(isPost = false) {
const headers = {
'Cookie': COOKIE,
'accept': '*/*',
'origin': 'https://cursor.com',
'referer': 'https://cursor.com/dashboard'
};
if (isPost) {
headers['content-type'] = 'application/json';
}
return headers;
}
async function testGetMe() {
console.log('\n========== 1. 获取用户信息 (GET /api/dashboard/get-me) ==========');
const result = await request({
hostname: 'cursor.com',
path: '/api/dashboard/get-me',
method: 'GET',
headers: getHeaders()
});
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
return result.data;
}
async function testUsageSummary() {
console.log('\n========== 2. 获取用量摘要 (GET /api/usage-summary) ==========');
const result = await request({
hostname: 'cursor.com',
path: '/api/usage-summary',
method: 'GET',
headers: getHeaders()
});
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
return result.data;
}
async function testBillingCycle() {
console.log('\n========== 3. 获取计费周期 (POST /api/dashboard/get-current-billing-cycle) ==========');
const result = await request({
hostname: 'cursor.com',
path: '/api/dashboard/get-current-billing-cycle',
method: 'POST',
headers: getHeaders(true)
}, '{}');
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
return result.data;
}
async function testFilteredUsage(userId, startMs, endMs) {
console.log('\n========== 4. 获取使用事件 (POST /api/dashboard/get-filtered-usage-events) ==========');
// 尝试不同的参数组合
const body = JSON.stringify({
startDate: startMs,
endDate: endMs,
userId: userId || undefined,
page: 1,
pageSize: 5
});
console.log('Request body:', body);
const result = await request({
hostname: 'cursor.com',
path: '/api/dashboard/get-filtered-usage-events',
method: 'POST',
headers: getHeaders(true)
}, body);
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
if (result.data && result.data.totalUsageEventsCount !== undefined) {
console.log('\n>>> 总对话次数 (totalUsageEventsCount):', result.data.totalUsageEventsCount);
}
return result.data;
}
async function testFilteredUsageNoUser(startMs, endMs) {
console.log('\n========== 4b. 获取使用事件 - 不带userId ==========');
const body = JSON.stringify({
startDate: startMs,
endDate: endMs,
page: 1,
pageSize: 5
});
console.log('Request body:', body);
const result = await request({
hostname: 'cursor.com',
path: '/api/dashboard/get-filtered-usage-events',
method: 'POST',
headers: getHeaders(true)
}, body);
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
if (result.data && result.data.totalUsageEventsCount !== undefined) {
console.log('\n>>> 总对话次数 (totalUsageEventsCount):', result.data.totalUsageEventsCount);
}
return result.data;
}
async function testAggregatedUsage(startMs) {
console.log('\n========== 5. 获取聚合用量 (POST /api/dashboard/get-aggregated-usage-events) ==========');
const body = JSON.stringify({
startDate: parseInt(startMs)
});
const result = await request({
hostname: 'cursor.com',
path: '/api/dashboard/get-aggregated-usage-events',
method: 'POST',
headers: getHeaders(true)
}, body);
console.log('Status:', result.status);
console.log('Response:', JSON.stringify(result.data, null, 2));
return result.data;
}
async function main() {
console.log('====================================================');
console.log(' Cursor 官方用量接口测试');
console.log('====================================================');
console.log('Cookie:', COOKIE.substring(0, 50) + '...');
try {
// 1. 获取用户信息
const me = await testGetMe();
const userId = me.userId;
console.log('\n>>> 用户ID:', userId);
console.log('>>> 邮箱:', me.email);
console.log('>>> 团队ID:', me.teamId);
// 2. 获取用量摘要
const summary = await testUsageSummary();
console.log('\n>>> 会员类型:', summary.membershipType);
console.log('>>> 计费周期:', summary.billingCycleStart, '至', summary.billingCycleEnd);
if (summary.individualUsage) {
const plan = summary.individualUsage.plan;
console.log('>>> 套餐用量:', plan.used, '/', plan.limit, '(剩余', plan.remaining, ')');
}
// 3. 获取计费周期
const billing = await testBillingCycle();
const startMs = billing.startDateEpochMillis;
const endMs = billing.endDateEpochMillis;
console.log('\n>>> 计费开始:', new Date(parseInt(startMs)).toISOString());
console.log('>>> 计费结束:', new Date(parseInt(endMs)).toISOString());
// 4. 获取使用事件 - 不带 userId
await testFilteredUsageNoUser(startMs, endMs);
// 4b. 如果有 userId也试试带 userId 的
if (userId) {
await testFilteredUsage(userId, startMs, endMs);
}
// 5. 获取聚合用量
if (startMs) {
await testAggregatedUsage(startMs);
}
console.log('\n====================================================');
console.log(' 测试完成');
console.log('====================================================');
} catch (error) {
console.error('测试出错:', error.message);
}
}
main();