初始化项目版本

This commit is contained in:
Axhub Make
2026-07-29 16:04:39 +08:00
commit 4305a1082b
2629 changed files with 760590 additions and 0 deletions

118
scripts/cursor-usage-snapshot.py Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env python3
"""本地用量快照:不消耗 Cursor Agent token直接读官方 API。"""
from __future__ import annotations
import datetime as dt
import json
import sqlite3
import urllib.request
from collections import defaultdict
STATE_DB = "/Users/sylvawong/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
TZ = dt.timezone(dt.timedelta(hours=8))
def token() -> str:
conn = sqlite3.connect(STATE_DB)
try:
row = conn.execute(
"SELECT value FROM ItemTable WHERE key='cursorAuth/accessToken'"
).fetchone()
finally:
conn.close()
if not row:
raise SystemExit("未找到 Cursor accessToken请先在 Cursor 登录。")
return row[0]
def post(path: str, body: dict | None = None) -> dict:
req = urllib.request.Request(
f"https://api2.cursor.sh/{path}",
data=json.dumps(body or {}).encode(),
headers={
"Authorization": f"Bearer {token()}",
"Content-Type": "application/json",
"Connect-Protocol-Version": "1",
"User-Agent": "oneos-token-budget/1.0",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=60) as resp:
return json.loads(resp.read().decode() or "{}")
def ms_to_local(ms: str | int) -> str:
return dt.datetime.fromtimestamp(int(ms) / 1000, TZ).strftime("%Y-%m-%d %H:%M")
def main() -> None:
usage = post("aiserver.v1.DashboardService/GetCurrentPeriodUsage")
plan = post("aiserver.v1.DashboardService/GetPlanInfo")
pu = usage.get("planUsage") or {}
info = plan.get("planInfo") or {}
print("=== Cursor 用量快照 ===")
print(f"套餐: {info.get('planName')} ({info.get('price')})")
print(f"账期: {ms_to_local(usage.get('billingCycleStart'))}{ms_to_local(usage.get('billingCycleEnd'))}")
print(f"Auto+Composer: {float(pu.get('autoPercentUsed') or 0):.2f}%")
print(f"API: {float(pu.get('apiPercentUsed') or 0):.2f}%")
print(f"综合 total: {float(pu.get('totalPercentUsed') or 0):.2f}%")
print(
f"等价花费: ${float(pu.get('totalSpend') or 0)/100:.2f} / ${float(pu.get('limit') or 0)/100:.2f}"
)
# Today since 00:00
now = dt.datetime.now(TZ)
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
data = post(
"aiserver.v1.DashboardService/GetFilteredUsageEvents",
{
"startDate": str(int(start.timestamp() * 1000)),
"endDate": str(int(now.timestamp() * 1000)),
"page": 1,
"pageSize": 200,
},
)
events = list(data.get("usageEventsDisplay") or [])
total = int(data.get("totalUsageEventsCount") or 0)
pages = max(1, (total + 199) // 200)
for page in range(2, pages + 1):
more = post(
"aiserver.v1.DashboardService/GetFilteredUsageEvents",
{
"startDate": str(int(start.timestamp() * 1000)),
"endDate": str(int(now.timestamp() * 1000)),
"page": page,
"pageSize": 200,
},
)
events.extend(more.get("usageEventsDisplay") or [])
tot = defaultdict(int)
by_model = defaultdict(lambda: defaultdict(int))
for e in events:
tu = e.get("tokenUsage") or {}
inp = int(tu.get("inputTokens") or 0)
out = int(tu.get("outputTokens") or 0)
cache = int(tu.get("cacheReadTokens") or 0)
model = e.get("model") or "unknown"
tot["events"] += 1
tot["input"] += inp
tot["output"] += out
tot["cache"] += cache
tot["all"] += inp + out + cache
tot["io"] += inp + out
by_model[model]["all"] += inp + out + cache
by_model[model]["events"] += 1
print(f"\n=== 今日(自 {start.strftime('%H:%M')}===")
print(f"请求: {tot['events']:,}")
print(f"输入+输出: {tot['io']:,}")
print(f"含缓存合计: {tot['all']:,}")
print("按模型:")
for model, b in sorted(by_model.items(), key=lambda kv: -kv[1]["all"]):
print(f" - {model}: {b['events']} 次, 含缓存 {b['all']:,}")
if __name__ == "__main__":
main()