Files
karuo-ai/01_卡资(金)/金仓_存储备份/脚本/cursor_code5_stabilize.py
2026-07-19 00:21:46 +08:00

133 lines
4.5 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Cursor code5 稳定化:保留最近 N 条对话气泡,其余仅保留列表元数据。
根因(社区+本机):
- 切标签时一次性加载过多 bubbleId/composerData → 渲染进程 OOM → code 5
- AgentAnalytics SQLite 嵌套事务加剧不稳定
- 全量 restore 后 1 万+ bubble 是复发主因
策略:
- 最近 keep 条:保留 bubbleId + fullConversationHeadersOnly
- 更早的:删 bubbleId清空 headers列表仍可见点开可从 Mongo 再 restore --name
- 安全清 composer.content/ofsContent/agentKv 等可再生缓存
"""
from __future__ import annotations
import argparse
import json
import sqlite3
from pathlib import Path
DB = Path.home() / "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
SAFE_DELETE_PREFIXES = (
"composer.content.%",
"ofsContent:%",
"agentKv:blob:%",
"checkpointId:%",
"codeBlockPartialInlineDiffFates:%",
"codeBlockDiff:%",
"inlineDiff:%",
"patch-graph:%",
"messageRequestContext:%",
"expectedContent-v1-%",
"composerVirtualRowHeights:%",
)
def load_composers(cur: sqlite3.Cursor) -> list[tuple[str, int, dict]]:
rows: list[tuple[str, int, dict]] = []
for key, value in cur.execute(
"SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'"
):
cid = key.replace("composerData:", "")
try:
data = json.loads(value)
except (json.JSONDecodeError, TypeError):
continue
ts = int(data.get("lastUpdatedAt") or data.get("createdAt") or 0)
rows.append((cid, ts, data))
rows.sort(key=lambda x: x[1], reverse=True)
return rows
def main() -> int:
parser = argparse.ArgumentParser(description="Cursor code5 稳定化")
parser.add_argument("--keep", type=int, default=120, help="保留完整气泡的最近 N 条对话")
args = parser.parse_args()
if not DB.is_file():
print(f"❌ 未找到 {DB}")
return 1
before_mb = DB.stat().st_size // (1024 * 1024)
conn = sqlite3.connect(DB, timeout=300)
cur = conn.cursor()
cur.execute("PRAGMA busy_timeout=300000")
composers = load_composers(cur)
keep_ids = {cid for cid, _, _ in composers[: args.keep]}
prune_ids = [cid for cid, _, _ in composers[args.keep :]]
pruned_bubbles = 0
pruned_headers = 0
for cid in prune_ids:
cur.execute("DELETE FROM cursorDiskKV WHERE key LIKE ?", (f"bubbleId:{cid}:%",))
pruned_bubbles += cur.rowcount
row = cur.execute(
"SELECT value FROM cursorDiskKV WHERE key = ?", (f"composerData:{cid}",)
).fetchone()
if not row:
continue
try:
data = json.loads(row[0])
except (json.JSONDecodeError, TypeError):
continue
if data.get("fullConversationHeadersOnly"):
data["fullConversationHeadersOnly"] = []
data["hasLoaded"] = False
cur.execute(
"UPDATE cursorDiskKV SET value = ? WHERE key = ?",
(json.dumps(data, ensure_ascii=False), f"composerData:{cid}"),
)
pruned_headers += 1
cache_deleted = 0
for pat in SAFE_DELETE_PREFIXES:
if pat.endswith("%") and ":" not in pat.replace(".%", ""):
q = f"DELETE FROM cursorDiskKV WHERE key LIKE '{pat}'"
else:
q = f"DELETE FROM cursorDiskKV WHERE key LIKE '{pat}'"
cur.execute(q)
cache_deleted += cur.rowcount
cur.execute("DELETE FROM ItemTable WHERE key = 'browserAutomation.history'")
cur.execute("DELETE FROM ItemTable WHERE key = 'aiCodeTrackingLines'")
conn.commit()
print(f"📋 composerData 总数: {len(composers)}")
print(f"✅ 保留完整气泡: {len(keep_ids)}")
print(f"🗂️ 仅保留列表元数据: {len(prune_ids)}")
print(f"🧹 删除旧 bubbleId: {pruned_bubbles}")
print(f"🧹 清空旧 headers: {pruned_headers}")
print(f"🧹 删除可再生缓存: {cache_deleted}")
print("🔧 WAL checkpoint…")
cur.execute("PRAGMA wal_checkpoint(TRUNCATE)")
print(f"{cur.fetchone()}")
print("🔧 VACUUM…")
cur.execute("VACUUM")
conn.commit()
cur.execute("PRAGMA wal_checkpoint(TRUNCATE)")
conn.close()
after_mb = DB.stat().st_size // (1024 * 1024)
print(f"📊 {before_mb} MB → {after_mb} MB")
print("💡 旧对话仍在列表;点开若空白:")
print(' python3 agent_sync_restore.py restore --name "对话关键词"')
return 0
if __name__ == "__main__":
raise SystemExit(main())