蜂鸟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

View File

@@ -0,0 +1,17 @@
// swift-tools-version:5.10
import PackageDescription
let package = Package(
name: "VibeviewerCore",
platforms: [
.macOS(.v14)
],
products: [
.library(name: "VibeviewerCore", targets: ["VibeviewerCore"]),
],
dependencies: [],
targets: [
.target(name: "VibeviewerCore", dependencies: []),
.testTarget(name: "VibeviewerCoreTests", dependencies: ["VibeviewerCore"])
]
)

View File

@@ -0,0 +1,22 @@
//
// Data+E.swift
// HttpClient
//
// Created by Groot chen on 2024/9/6.
//
import Foundation
public extension Data {
func toPrettyPrintedJSONString() -> String? {
if let json = try? JSONSerialization.jsonObject(with: self),
let data = try? JSONSerialization.data(
withJSONObject: json,
options: [.prettyPrinted, .withoutEscapingSlashes]
)
{
return String(data: data, encoding: .utf8)
}
return nil
}
}

View File

@@ -0,0 +1,33 @@
import Foundation
public extension Date {
///
public var millisecondsSince1970String: String {
String(Int(self.timeIntervalSince1970 * 1000))
}
/// Date
public static func fromMillisecondsString(_ msString: String) -> Date? {
guard let ms = Double(msString) else { return nil }
return Date(timeIntervalSince1970: ms / 1000.0)
}
}
public extension Calendar {
/// [start, end]
public func dayRange(for date: Date) -> (start: Date, end: Date) {
let startOfDay = self.startOfDay(for: date)
let nextDay = self.date(byAdding: .day, value: 1, to: startOfDay) ?? date
let endOfDay = Date(timeInterval: -0.001, since: nextDay)
return (startOfDay, endOfDay)
}
/// 00:00 [yesterdayStart, now]
public func yesterdayToNowRange(from now: Date = Date()) -> (start: Date, end: Date) {
let startOfToday = self.startOfDay(for: now)
let startOfYesterday = self.date(byAdding: .day, value: -1, to: startOfToday) ?? now
return (startOfYesterday, now)
}
}

View File

@@ -0,0 +1,121 @@
import Foundation
public enum DateUtils {
public enum TimeFormat {
case hm // HH:mm
case hms // HH:mm:ss
fileprivate var dateFormat: String {
switch self {
case .hm: return "HH:mm"
case .hms: return "HH:mm:ss"
}
}
}
/// [start, end]
public static func dayRange(for date: Date, calendar: Calendar = .current) -> (start: Date, end: Date) {
let startOfDay = calendar.startOfDay(for: date)
let nextDay = calendar.date(byAdding: .day, value: 1, to: startOfDay) ?? date
let endOfDay = Date(timeInterval: -0.001, since: nextDay)
return (startOfDay, endOfDay)
}
/// 00:00 [yesterdayStart, now]
public static func yesterdayToNowRange(from now: Date = Date(), calendar: Calendar = .current) -> (start: Date, end: Date) {
let startOfToday = calendar.startOfDay(for: now)
let startOfYesterday = calendar.date(byAdding: .day, value: -1, to: startOfToday) ?? now
return (startOfYesterday, now)
}
/// 7 00:00 00:00 [sevenDaysAgoStart, tomorrowStart]
public static func sevenDaysAgoToNowRange(from now: Date = Date(), calendar: Calendar = .current) -> (start: Date, end: Date) {
let startOfToday = calendar.startOfDay(for: now)
let startOfSevenDaysAgo = calendar.date(byAdding: .day, value: -7, to: startOfToday) ?? now
let startOfTomorrow = calendar.date(byAdding: .day, value: 1, to: startOfToday) ?? now
return (startOfSevenDaysAgo, startOfTomorrow)
}
/// 00:00 00:00 [nDaysAgoStart, tomorrowStart]
public static func daysAgoToNowRange(days: Int, from now: Date = Date(), calendar: Calendar = .current) -> (start: Date, end: Date) {
let startOfToday = calendar.startOfDay(for: now)
let startOfNDaysAgo = calendar.date(byAdding: .day, value: -days, to: startOfToday) ?? now
let startOfTomorrow = calendar.date(byAdding: .day, value: 1, to: startOfToday) ?? now
return (startOfNDaysAgo, startOfTomorrow)
}
/// Date
public static func millisecondsString(from date: Date) -> String {
String(Int(date.timeIntervalSince1970 * 1000))
}
/// Date
public static func date(fromMillisecondsString msString: String) -> Date? {
guard let ms = Double(msString) else { return nil }
return Date(timeIntervalSince1970: ms / 1000.0)
}
/// Date HH:mm:ss
public static func timeString(from date: Date,
format: TimeFormat = .hms,
timeZone: TimeZone = .current,
locale: Locale = Locale(identifier: "en_US_POSIX")) -> String {
let formatter = DateFormatter()
formatter.locale = locale
formatter.timeZone = timeZone
formatter.dateFormat = format.dateFormat
return formatter.string(from: date)
}
///
public static func timeString(fromMilliseconds ms: Int64,
format: TimeFormat = .hms,
timeZone: TimeZone = .current,
locale: Locale = .current) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(ms) / 1000.0)
return timeString(from: date, format: format, timeZone: timeZone, locale: locale)
}
///
public static func timeString(fromSeconds s: Int64,
format: TimeFormat = .hms,
timeZone: TimeZone = .current,
locale: Locale = .current) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(s))
return timeString(from: date, format: format, timeZone: timeZone, locale: locale)
}
///
public static func timeString(fromMillisecondsString msString: String,
format: TimeFormat = .hms,
timeZone: TimeZone = .current,
locale: Locale = .current) -> String {
guard let ms = Int64(msString) else { return "" }
return timeString(fromMilliseconds: ms, format: format, timeZone: timeZone, locale: locale)
}
/// Date YYYY-MM-DD
public static func dateString(from date: Date, calendar: Calendar = .current) -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: date)
}
/// API
/// 使 UTC
/// n
public static func daysAgoToTodayRange(days: Int, from now: Date = Date(), calendar: Calendar = .current) -> (start: String, end: String) {
// 使 UTC dateString
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(secondsFromGMT: 0)!
let startOfToday = utcCalendar.startOfDay(for: now)
// (days-1) days
let startOfNDaysAgo = utcCalendar.date(byAdding: .day, value: -(days - 1), to: startOfToday) ?? now
return (dateString(from: startOfNDaysAgo, calendar: utcCalendar), dateString(from: startOfToday, calendar: utcCalendar))
}
}

View File

@@ -0,0 +1,7 @@
import Foundation
public extension Int {
var dollarStringFromCents: String {
"$" + String(format: "%.2f", Double(self) / 100.0)
}
}

View File

@@ -0,0 +1,36 @@
import Foundation
import ServiceManagement
public protocol LaunchAtLoginService {
var isEnabled: Bool { get }
func setEnabled(_ enabled: Bool) -> Bool
}
public final class DefaultLaunchAtLoginService: LaunchAtLoginService {
public init() {}
public var isEnabled: Bool {
SMAppService.mainApp.status == .enabled
}
public func setEnabled(_ enabled: Bool) -> Bool {
do {
if enabled {
if SMAppService.mainApp.status == .enabled {
return true
}
try SMAppService.mainApp.register()
return true
} else {
if SMAppService.mainApp.status != .enabled {
return true
}
try SMAppService.mainApp.unregister()
return true
}
} catch {
print("Failed to \(enabled ? "enable" : "disable") launch at login: \(error)")
return false
}
}
}

View File

@@ -0,0 +1,8 @@
@testable import VibeviewerCore
import XCTest
final class VibeviewerCoreTests: XCTestCase {
func testExample() {
XCTAssertTrue(true)
}
}