## 当前状态 - 插件界面已完成重命名 (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>
150 lines
5.6 KiB
Plaintext
150 lines
5.6 KiB
Plaintext
---
|
|
alwaysApply: false
|
|
---
|
|
# API Authoring Guidelines (VibeviewerAPI)
|
|
|
|
## Goals
|
|
- Unify API naming, directories, abstractions, dependency injection, and decoding patterns
|
|
- Keep all APIs in a single module `VibeviewerAPI` to enforce isolation and modularity
|
|
- Standardize `DecodableTargetType` and the `HttpClient.decodableRequest(_:)` usage (async/await only) on top of Moya/Alamofire
|
|
|
|
## Hard rules
|
|
- API targets must be declared with `struct` (no `enum`/case-style targets)
|
|
- Use async/await-only decoding; callback-based styles are forbidden
|
|
- Separate API declarations from model declarations:
|
|
- API Targets/Services → `VibeviewerAPI`
|
|
- Data models/aggregations → `VibeviewerModel`
|
|
- Views/upper layers must use `Service` protocols via dependency injection, and must not call API targets or `HttpClient` directly
|
|
- The API module only exposes `Service` protocols and default implementations; API targets, networking details, and common header configuration remain internal
|
|
|
|
## Dependencies & imports
|
|
- API module imports only:
|
|
- `Foundation`
|
|
- `Moya`
|
|
- `Alamofire` (used via `HttpClient`)
|
|
- `VibeviewerModel`
|
|
- Never import UI frameworks in the API module (`SwiftUI`/`AppKit`/`UIKit`)
|
|
|
|
## Naming conventions
|
|
- Targets: Feature name + `API`, e.g., `YourFeatureAPI`
|
|
- Protocols: `YourFeatureService`
|
|
- Default implementations: `DefaultYourFeatureService`
|
|
- Models: `YourFeatureResponse`, `YourFeatureDetail`, etc.
|
|
|
|
## Directory structure (VibeviewerAPI)
|
|
```text
|
|
VibeviewerAPI/
|
|
Sources/VibeviewerAPI/
|
|
Mapping/
|
|
... DTOs & Mappers
|
|
Plugins/
|
|
RequestHeaderConfigurationPlugin.swift
|
|
RequestErrorHandlingPlugin.swift
|
|
SimpleNetworkLoggerPlugin.swift
|
|
Service/
|
|
MoyaProvider+DecodableRequest.swift
|
|
HttpClient.swift # Unified Moya provider & session wrapper
|
|
HttpClientError.swift
|
|
Targets/
|
|
CursorGetMeAPI.swift # internal target
|
|
CursorUsageAPI.swift # internal target
|
|
CursorTeamSpendAPI.swift # internal target
|
|
CursorService.swift # public protocol + default implementation (service only)
|
|
```
|
|
|
|
## Target and decoding conventions
|
|
- Targets conform to `DecodableTargetType`:
|
|
- `associatedtype ResultType: Decodable`
|
|
- `var decodeAtKeyPath: String? { get }` (default `nil`)
|
|
- Implement `baseURL`, `path`, `method`, `task`, `headers`, `sampleData`
|
|
- Avoid overriding `validationType` unless necessary
|
|
|
|
Example:
|
|
```swift
|
|
import Foundation
|
|
import Moya
|
|
import VibeviewerModel
|
|
|
|
struct UserProfileDetailAPI: DecodableTargetType {
|
|
typealias ResultType = UserProfileResponse
|
|
|
|
let userId: String
|
|
|
|
var baseURL: URL { APIConfig.baseURL }
|
|
var path: String { "/users/\(userId)" }
|
|
var method: Moya.Method { .get }
|
|
var task: Task { .requestPlain }
|
|
var headers: [String: String]? { APIHeadersBuilder.basicHeaders(cookieHeader: nil) }
|
|
var sampleData: Data { Data("{\"id\":\"1\",\"name\":\"foo\"}".utf8) }
|
|
}
|
|
```
|
|
|
|
## Service abstraction & dependency injection
|
|
- Expose protocol + default implementation (expose services only; hide networking details)
|
|
- The default `public init(decoding:)` must not leak internal protocol types; provide `internal init(network:decoding:)` for test injection
|
|
|
|
```swift
|
|
import Foundation
|
|
import Moya
|
|
import VibeviewerModel
|
|
|
|
public protocol UserProfileService {
|
|
func fetchDetail(userId: String) async throws -> UserProfileResponse
|
|
}
|
|
|
|
public struct DefaultUserProfileService: UserProfileService {
|
|
private let network: NetworkClient
|
|
private let decoding: JSONDecoder.KeyDecodingStrategy
|
|
|
|
// Business-facing: do not expose internal NetworkClient abstraction
|
|
public init(decoding: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) {
|
|
self.network = DefaultNetworkClient()
|
|
self.decoding = decoding
|
|
}
|
|
|
|
// Test injection: available within the API module (same package or @testable)
|
|
init(network: any NetworkClient, decoding: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) {
|
|
self.network = network
|
|
self.decoding = decoding
|
|
}
|
|
|
|
public func fetchDetail(userId: String) async throws -> UserProfileResponse {
|
|
try await network.decodableRequest(
|
|
UserProfileDetailAPI(userId: userId),
|
|
decodingStrategy: decoding
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
> Note: `DefaultNetworkClient`, the `NetworkClient` protocol, and the concrete `HttpClient` implementation details remain `internal` and are not exposed.
|
|
|
|
## View usage (dependency injection)
|
|
Views must not call API targets or `HttpClient` directly. Use injected services instead:
|
|
```swift
|
|
import VibeviewerAPI
|
|
import VibeviewerModel
|
|
|
|
let service: UserProfileService = DefaultUserProfileService()
|
|
let model = try await service.fetchDetail(userId: "1")
|
|
```
|
|
|
|
## Error handling & logging
|
|
- Enable `SimpleNetworkLoggerPlugin` by default to log requests/responses
|
|
- Enable `RequestErrorHandlingPlugin` by default:
|
|
- Timeouts/offline → unified handling
|
|
- Customizable via strategy protocols
|
|
|
|
## Testing & mock conventions
|
|
- Within the `VibeviewerAPI` module, inject a `FakeNetworkClient` via `internal init(network:decoding:)` to replace real networking
|
|
- Provide `sampleData` for each target; prefer minimal realistic JSON to ensure robust decoding
|
|
- Use `@testable import VibeviewerAPI` to access internal symbols when external tests are required
|
|
|
|
## Alignment with modular architecture (architecture.mdc)
|
|
- Do not import UI frameworks in the API module
|
|
- Expose only `Service` protocols and default implementations; hide targets and networking details
|
|
- Dependency direction: `VibeviewerModel` ← `VibeviewerAPI` ← `VibeviewerFeature`
|
|
- Strict “one file, one type/responsibility”; clear feature aggregation; one-way dependencies
|
|
|
|
|