Skip to content

Instantly share code, notes, and snippets.

@lixiangwuxian
Created April 14, 2026 09:25
Show Gist options
  • Select an option

  • Save lixiangwuxian/70af6eb0a7f4855d58333e39914b8bd5 to your computer and use it in GitHub Desktop.

Select an option

Save lixiangwuxian/70af6eb0a7f4855d58333e39914b8bd5 to your computer and use it in GitHub Desktop.
Unlock claude code 1h cache ttl
#!/usr/bin/env python3
"""
Claude Code 1h Cache Patch Tool
跨平台:macOS / Linux / Windows
安装类型:native binary / npm cli.js 自动识别
用法:
python3 claude-1h-cache.py # 打补丁 + 安装自动监听
python3 claude-1h-cache.py patch # 仅打补丁
python3 claude-1h-cache.py watch # 仅安装自动监听
python3 claude-1h-cache.py status # 查看状态
python3 claude-1h-cache.py restore # 还原备份
python3 claude-1h-cache.py unwatch # 卸载自动监听
python3 claude-1h-cache.py daemon # 内部用:守护进程模式(Windows/Linux 后台监听)
"""
import sys
import os
import shutil
import subprocess
import platform
import time
import json
# ─── 版本 ─────────────────────────────────────────────────────────────────────
VERSION = "2.0.0"
# ─── 补丁锚点(版本无关,依赖语义字符串)────────────────────────────────────
# ─── 语义锚点(不依赖混淆变量名,跨版本稳定)─────────────────────────────────
# 这两个字符串是 env var 名和配置 key,不会被混淆器重命名
ANCHOR_ENV = b'ENABLE_PROMPT_CACHING_1H_BEDROCK'
ANCHOR_KEY = b'tengu_prompt_cache_1h_config'
PATCH_MARKER = b'/*__1h_patched__*/'
def _find_func_bounds(data: bytes, start: int = 0):
"""
从 start 开始,通过语义锚点定位下一个 1h 缓存判断函数的 [func_start, func_end)。
向锚点前最多 400 字节内找最近的 'function ',再用括号计数找闭合 '}'。
验证第二个锚点也在函数体内才返回结果,防止误命中。
找不到返回 None。
"""
pos0 = start
while True:
idx = data.find(ANCHOR_ENV, pos0)
if idx == -1:
return None
search_start = max(0, idx - 400)
rel = data[search_start:idx].rfind(b'function ')
if rel == -1:
pos0 = idx + 1
continue
func_start = search_start + rel
try:
brace_open = data.index(b'{', func_start)
except ValueError:
pos0 = idx + 1
continue
depth, p = 0, brace_open
func_end = None
while p < len(data):
b = data[p]
if b == ord('{'):
depth += 1
elif b == ord('}'):
depth -= 1
if depth == 0:
func_end = p + 1
break
p += 1
if func_end is None:
pos0 = idx + 1
continue
if ANCHOR_KEY in data[func_start:func_end]:
return func_start, func_end
# 锚点不在函数体内,从该锚点之后继续找下一个
pos0 = idx + 1
def _find_all_func_bounds(data: bytes):
"""定位所有未 patch 的目标函数。patched 函数体里没有锚点字符串,自动被跳过。"""
bounds = []
start = 0
while True:
b = _find_func_bounds(data, start)
if b is None:
break
bounds.append(b)
start = b[1]
return bounds
def _apply_patch_bytes(data: bytes):
"""
返回 (patched_data, count) 或 (None, 0) 表示没有可 patch 的函数。
所有未 patch 的目标函数都会被等长替换。
"""
all_bounds = _find_all_func_bounds(data)
if not all_bounds:
return None, 0
# 等长替换,偏移不会变化,但按倒序写更安全
out = data
for func_start, func_end in sorted(all_bounds, reverse=True):
orig = out[func_start:func_end]
repl = _make_replacement(orig)
out = out[:func_start] + repl + out[func_end:]
return out, len(all_bounds)
def _make_replacement(orig: bytes) -> bytes:
"""构造等长替换:保留函数名,函数体改为 return!0,尾部空格填充。"""
import re
m = re.match(rb'function (\w+)\(', orig)
if not m:
raise ValueError("无法从原函数提取函数名")
func_name = m.group(1)
core = PATCH_MARKER + b'function ' + func_name + b'(H){return!0}'
if len(core) > len(orig):
raise ValueError(f"补丁长度超出原函数:{len(core)} > {len(orig)}")
return core + b' ' * (len(orig) - len(core))
# ─── 平台 ─────────────────────────────────────────────────────────────────────
SYSTEM = platform.system() # Darwin / Linux / Windows
IS_MAC = SYSTEM == "Darwin"
IS_WIN = SYSTEM == "Windows"
IS_LINUX = SYSTEM == "Linux"
HOME = os.path.expanduser("~")
SCRIPT = os.path.abspath(sys.argv[0])
# ─── 颜色(Windows CMD 不一定支持,做了兼容)──────────────────────────────────
_use_color = sys.stdout.isatty() and not IS_WIN or (IS_WIN and os.environ.get("WT_SESSION"))
def _c(code, s): return f"\033[{code}m{s}\033[0m" if _use_color else s
def green(s): return _c("32", s)
def yellow(s): return _c("33", s)
def red(s): return _c("31", s)
def cyan(s): return _c("36", s)
def bold(s): return _c("1", s)
def ok(msg): print(f" {green('v')} {msg}")
def warn(msg): print(f" {yellow('!')} {msg}")
def err(msg): print(f" {red('x')} {msg}", file=sys.stderr)
def info(msg): print(f" {cyan('i')} {msg}")
def section(msg): print(f"\n{bold(cyan(msg))}")
# ─── 定位安装 ─────────────────────────────────────────────────────────────────
def find_target():
"""
返回 (path, mode, versions_dir_or_None)
mode: "binary" | "npm"
versions_dir: native binary 模式下的版本目录(用于监听)
"""
# ── native binary:~/.local/share/claude/versions/(macOS / Linux)
versions_dir = os.path.join(HOME, ".local", "share", "claude", "versions")
if os.path.isdir(versions_dir):
entries = [
v for v in os.listdir(versions_dir)
if not v.endswith(".bak") and not v.endswith(".1h-cache-bak")
and os.path.isfile(os.path.join(versions_dir, v))
]
if entries:
def ver_key(v):
parts = v.split(".")
return [int(x) if x.isdigit() else 0 for x in parts]
latest = sorted(entries, key=ver_key, reverse=True)[0]
return os.path.join(versions_dir, latest), "binary", versions_dir
# ── native binary:~\.local\bin\claude.exe(Unix 风格安装在 Windows)
if IS_WIN:
local_bin_exe = os.path.join(HOME, ".local", "bin", "claude.exe")
if os.path.isfile(local_bin_exe):
return local_bin_exe, "binary", os.path.join(HOME, ".local", "bin")
# ── native binary:Windows %LOCALAPPDATA%\Programs\claude\
if IS_WIN:
local_app = os.environ.get("LOCALAPPDATA", "")
for base in [
os.path.join(local_app, "Programs", "claude"),
os.path.join(local_app, "claude"),
]:
if os.path.isdir(base):
# versions 子目录
vdir = os.path.join(base, "versions")
if os.path.isdir(vdir):
entries = [
v for v in os.listdir(vdir)
if os.path.isfile(os.path.join(vdir, v))
and not v.endswith(".bak")
]
if entries:
def ver_key2(v):
stem = os.path.splitext(v)[0]
return [int(x) if x.isdigit() else 0 for x in stem.split(".")]
latest = sorted(entries, key=ver_key2, reverse=True)[0]
return os.path.join(vdir, latest), "binary", vdir
# 直接的 .exe
exe = os.path.join(base, "claude.exe")
if os.path.isfile(exe):
return exe, "binary", base
# ── npm 全局安装
npm_candidates = _find_npm_cli()
if npm_candidates:
return npm_candidates, "npm", None
return None, None, None
def _same_binary(a: str, b: str, head_bytes: int = 8192) -> bool:
"""两个文件是否是"同一份二进制":大小一致且文件头若干字节一致。"""
try:
if os.path.getsize(a) != os.path.getsize(b):
return False
with open(a, "rb") as fa, open(b, "rb") as fb:
return fa.read(head_bytes) == fb.read(head_bytes)
except Exception:
return False
def find_secondary_copies(primary: str) -> list:
"""
找到与 primary 内容一致但独立存在的副本(非硬链接)。
Windows 场景:Unix 风格安装时,`~/.local/bin/claude.exe` 和
`~/.local/share/claude/versions/<latest>` 是两份独立文件。
find_target() 只返回 versions 下那份,导致 .local/bin/claude.exe
残留未 patch 的原始副本,而它恰好是 PATH 上被优先执行的那个。
"""
copies = []
if not primary:
return copies
candidates = []
if IS_WIN:
candidates.append(os.path.join(HOME, ".local", "bin", "claude.exe"))
for c in candidates:
if not os.path.isfile(c):
continue
try:
if os.path.abspath(c) == os.path.abspath(primary):
continue
if os.path.samefile(c, primary):
continue # 硬链接指向同一 inode
except Exception:
pass
if _same_binary(c, primary):
copies.append(c)
return copies
def _find_npm_cli():
# npm root -g
try:
npm_root = subprocess.check_output(
["npm", "root", "-g"], text=True, stderr=subprocess.DEVNULL, shell=IS_WIN
).strip().splitlines()[0].strip()
p = os.path.join(npm_root, "@anthropic-ai", "claude-code", "cli.js")
if os.path.isfile(p): return p
except Exception:
pass
# 固定路径
candidates = []
if IS_WIN:
appdata = os.environ.get("APPDATA", "")
candidates += [
os.path.join(appdata, "npm", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
]
nvm_home = os.environ.get("NVM_HOME", "")
if nvm_home and os.path.isdir(nvm_home):
for v in os.listdir(nvm_home):
candidates.append(os.path.join(nvm_home, v, "node_modules", "@anthropic-ai", "claude-code", "cli.js"))
else:
candidates += [
"/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
"/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js",
os.path.join(HOME, ".npm-global", "lib", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
]
# nvm
nvm_dir = os.environ.get("NVM_DIR", os.path.join(HOME, ".nvm"))
if os.path.isdir(nvm_dir):
vdir = os.path.join(nvm_dir, "versions", "node")
if os.path.isdir(vdir):
for v in os.listdir(vdir):
candidates.append(os.path.join(vdir, v, "lib", "node_modules", "@anthropic-ai", "claude-code", "cli.js"))
# Homebrew
for root in ["/opt/homebrew/lib/node_modules", "/usr/local/lib/node_modules"]:
p = os.path.join(root, "@anthropic-ai", "claude-code", "cli.js")
candidates.append(p)
for p in candidates:
if os.path.isfile(p): return p
return None
# ─── 补丁状态 ─────────────────────────────────────────────────────────────────
def detect_state(data: bytes) -> str:
# 先检查是否还有未 patch 的目标函数(可能存在 marker 的同时另一份副本仍是原始的)
if _find_func_bounds(data): return "original"
if PATCH_MARKER in data: return "patched"
return "unknown"
# ─── macOS 重签名 ─────────────────────────────────────────────────────────────
def resign_macos(path: str) -> bool:
try:
subprocess.run(["codesign", "--remove-signature", path],
check=True, capture_output=True)
subprocess.run(["codesign", "-s", "-", path],
check=True, capture_output=True)
return True
except Exception:
return False
# ─── 核心:打补丁 ─────────────────────────────────────────────────────────────
def do_patch(target: str, mode: str, secondaries=None) -> bool:
try:
data = open(target, "rb").read()
except PermissionError:
err(f"读取失败,权限不足。")
_hint_sudo()
return False
state = detect_state(data)
info(f"当前状态:{green('已打补丁') if state=='patched' else yellow('未打补丁') if state=='original' else red('未知')}")
if state == "patched":
ok("已是补丁状态,无需操作。")
return True
if state == "unknown":
err("未找到目标函数锚点,版本结构可能已变更。")
return False
all_bounds = _find_all_func_bounds(data)
if not all_bounds:
err("定位失败。")
return False
for fs, fe in all_bounds:
info(f"未 patch 函数偏移:{fs},长度:{fe - fs} 字节")
try:
patched, count = _apply_patch_bytes(data)
except ValueError as e:
err(str(e))
return False
if patched is None:
err("无可 patch 的函数。")
return False
info(f"本次将 patch {count} 处副本")
# 备份
bak = target + ".1h-cache-bak"
if not os.path.isfile(bak):
try:
shutil.copy2(target, bak)
ok(f"备份已创建:{bak}")
except Exception as e:
err(f"备份失败:{e}")
return False
else:
info("备份已存在,跳过。")
try:
with open(target, "wb") as f:
f.write(patched)
except PermissionError:
err("写入失败,权限不足。")
_hint_sudo()
return False
# macOS:重签名
if mode == "binary" and IS_MAC:
if resign_macos(target):
ok("代码签名已更新(ad-hoc)。")
else:
warn("codesign 不可用,跳过签名。若程序崩溃请手动运行:")
warn(f" codesign -s - {target}")
# 验证
verify = open(target, "rb").read()
if PATCH_MARKER not in verify:
err("验证失败,自动还原...")
shutil.copy2(bak, target)
if mode == "binary" and IS_MAC:
resign_macos(target)
return False
ok("补丁写入成功,已验证。")
# 同步到其它独立副本(如 Windows 的 ~/.local/bin/claude.exe)
if secondaries:
for sec in secondaries:
sec_bak = sec + ".1h-cache-bak"
if not os.path.isfile(sec_bak):
try:
shutil.copy2(sec, sec_bak)
ok(f"副本备份已创建:{sec_bak}")
except Exception as e:
warn(f"副本备份失败,跳过同步:{sec}: {e}")
continue
try:
shutil.copy2(target, sec)
if mode == "binary" and IS_MAC:
resign_macos(sec)
ok(f"副本已同步:{sec}")
except PermissionError:
warn(f"副本写入失败(权限不足):{sec}")
except Exception as e:
warn(f"副本同步失败:{sec}: {e}")
return True
def do_restore(target: str, mode: str, secondaries=None) -> bool:
bak = target + ".1h-cache-bak"
if not os.path.isfile(bak):
err(f"未找到备份:{bak}")
return False
try:
shutil.copy2(bak, target)
if mode == "binary" and IS_MAC:
resign_macos(target)
ok("已还原至原始版本。")
except PermissionError:
err("写入失败,权限不足。")
_hint_sudo()
return False
if secondaries:
for sec in secondaries:
sec_bak = sec + ".1h-cache-bak"
if not os.path.isfile(sec_bak):
warn(f"副本无备份,跳过:{sec}")
continue
try:
shutil.copy2(sec_bak, sec)
if mode == "binary" and IS_MAC:
resign_macos(sec)
ok(f"副本已还原:{sec}")
except Exception as e:
warn(f"副本还原失败:{sec}: {e}")
return True
# ─── 自动监听:macOS launchd ──────────────────────────────────────────────────
_LAUNCHD_LABEL = "com.claude.1h-cache-autopatch"
_LAUNCHD_PLIST = os.path.join(HOME, "Library", "LaunchAgents", f"{_LAUNCHD_LABEL}.plist")
def watch_install_macos(versions_dir: str):
plist = f"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>{_LAUNCHD_LABEL}</string>
<key>WatchPaths</key>
<array>
<string>{versions_dir}</string>
</array>
<key>ProgramArguments</key>
<array>
<string>{sys.executable}</string>
<string>{SCRIPT}</string>
<string>patch</string>
</array>
<key>StandardOutPath</key>
<string>{os.path.join(HOME, ".local", "share", "claude", "1h-cache-patch.log")}</string>
<key>StandardErrorPath</key>
<string>{os.path.join(HOME, ".local", "share", "claude", "1h-cache-patch.log")}</string>
<key>ThrottleInterval</key>
<integer>5</integer>
</dict>
</plist>
"""
os.makedirs(os.path.dirname(_LAUNCHD_PLIST), exist_ok=True)
with open(_LAUNCHD_PLIST, "w") as f:
f.write(plist)
# 卸载旧的再装
subprocess.run(["launchctl", "unload", _LAUNCHD_PLIST],
capture_output=True)
result = subprocess.run(["launchctl", "load", _LAUNCHD_PLIST],
capture_output=True)
if result.returncode == 0:
ok(f"launchd 监听已安装(WatchPaths: {versions_dir})")
return True
else:
warn(f"launchctl load 失败:{result.stderr.decode().strip()}")
return False
def watch_uninstall_macos():
if os.path.isfile(_LAUNCHD_PLIST):
subprocess.run(["launchctl", "unload", _LAUNCHD_PLIST], capture_output=True)
os.remove(_LAUNCHD_PLIST)
ok("launchd 监听已卸载。")
else:
info("launchd 监听未安装。")
# ─── 自动监听:Linux systemd ──────────────────────────────────────────────────
_SYSTEMD_DIR = os.path.join(HOME, ".config", "systemd", "user")
_SYSTEMD_SERVICE = "claude-1h-cache-autopatch.service"
_SYSTEMD_PATH = "claude-1h-cache-autopatch.path"
def watch_install_linux(versions_dir: str):
os.makedirs(_SYSTEMD_DIR, exist_ok=True)
service = f"""[Unit]
Description=Claude Code 1h Cache Auto Patch
[Service]
Type=oneshot
ExecStart={sys.executable} {SCRIPT} patch
StandardOutput=append:{HOME}/.local/share/claude/1h-cache-patch.log
StandardError=append:{HOME}/.local/share/claude/1h-cache-patch.log
"""
path_unit = f"""[Unit]
Description=Watch Claude Code versions directory
[Path]
PathChanged={versions_dir}
Unit={_SYSTEMD_SERVICE}
[Install]
WantedBy=default.target
"""
with open(os.path.join(_SYSTEMD_DIR, _SYSTEMD_SERVICE), "w") as f:
f.write(service)
with open(os.path.join(_SYSTEMD_DIR, _SYSTEMD_PATH), "w") as f:
f.write(path_unit)
try:
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True, capture_output=True)
subprocess.run(["systemctl", "--user", "enable", "--now", _SYSTEMD_PATH],
check=True, capture_output=True)
ok(f"systemd path 监听已安装并启动。")
return True
except subprocess.CalledProcessError as e:
warn(f"systemctl 失败:{e.stderr.decode().strip() if e.stderr else e}")
info("如 systemd 不可用,改用守护进程模式:")
return _watch_install_daemon_linux(versions_dir)
def _watch_install_daemon_linux(versions_dir: str):
"""systemd 不可用时,改写 autostart 桌面条目或 .bashrc 启动守护"""
autostart_dir = os.path.join(HOME, ".config", "autostart")
os.makedirs(autostart_dir, exist_ok=True)
desktop = f"""[Desktop Entry]
Type=Application
Name=Claude 1h Cache Autopatch
Exec={sys.executable} {SCRIPT} daemon {versions_dir}
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
"""
desktop_file = os.path.join(autostart_dir, "claude-1h-cache-autopatch.desktop")
with open(desktop_file, "w") as f:
f.write(desktop)
ok(f"已写入 autostart 条目:{desktop_file}")
info("登录后自动启动守护进程。立即启动:")
info(f" nohup {sys.executable} {SCRIPT} daemon {versions_dir} &")
return True
def watch_uninstall_linux():
try:
subprocess.run(["systemctl", "--user", "disable", "--now", _SYSTEMD_PATH],
capture_output=True)
except Exception:
pass
for f in [_SYSTEMD_SERVICE, _SYSTEMD_PATH]:
p = os.path.join(_SYSTEMD_DIR, f)
if os.path.isfile(p): os.remove(p)
try:
subprocess.run(["systemctl", "--user", "daemon-reload"], capture_output=True)
except Exception:
pass
ok("systemd 监听已卸载。")
# ─── 自动监听:Windows 任务计划 ───────────────────────────────────────────────
_TASK_NAME = "ClaudeCode1hCacheAutopatch"
def watch_install_windows(versions_dir: str):
"""
创建一个在用户登录时启动的任务,以守护进程方式持续监听。
使用 Windows Task Scheduler + pythonw 静默运行。
"""
pythonw = sys.executable.replace("python.exe", "pythonw.exe")
if not os.path.isfile(pythonw):
pythonw = sys.executable # 降级使用 python.exe
xml = f"""<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
</LogonTrigger>
</Triggers>
<Actions Context="Author">
<Exec>
<Command>{pythonw}</Command>
<Arguments>"{SCRIPT}" daemon "{versions_dir}"</Arguments>
</Exec>
</Actions>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
</Settings>
</Task>
"""
xml_path = os.path.join(os.environ.get("TEMP", ""), "claude_patch_task.xml")
with open(xml_path, "w", encoding="utf-16") as f:
f.write(xml)
result = subprocess.run(
["schtasks", "/Create", "/F", "/TN", _TASK_NAME, "/XML", xml_path],
capture_output=True, text=True
)
os.remove(xml_path)
if result.returncode == 0:
ok(f"Windows 任务计划已创建:{_TASK_NAME}")
# 立即启动守护
subprocess.Popen(
[pythonw, SCRIPT, "daemon", versions_dir],
creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NO_WINDOW
)
ok("守护进程已在后台启动。")
return True
else:
warn(f"任务计划创建失败:{result.stderr.strip()}")
info("请以管理员身份运行本脚本。")
return False
def watch_uninstall_windows():
result = subprocess.run(
["schtasks", "/Delete", "/F", "/TN", _TASK_NAME],
capture_output=True, text=True
)
if result.returncode == 0:
ok("Windows 任务计划已删除。")
else:
info("任务计划不存在或删除失败。")
# ─── 守护进程:跨平台文件监听(用于 Windows / Linux 无 systemd 场景)──────────
def daemon_watch(versions_dir: str):
"""
轮询 versions_dir,发现新文件时自动打补丁。
日志写入同目录的 1h-cache-patch.log。
"""
log_path = os.path.join(HOME, ".local", "share", "claude", "1h-cache-patch.log")
os.makedirs(os.path.dirname(log_path), exist_ok=True)
def log(msg):
ts = time.strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {msg}\n"
with open(log_path, "a", encoding="utf-8") as f:
f.write(line)
log(f"守护进程启动,监听:{versions_dir}")
def get_files():
if not os.path.isdir(versions_dir): return set()
return {
f for f in os.listdir(versions_dir)
if not f.endswith(".bak") and not f.endswith(".1h-cache-bak")
and os.path.isfile(os.path.join(versions_dir, f))
}
known = get_files()
while True:
time.sleep(10)
try:
current = get_files()
new_files = current - known
if new_files:
log(f"发现新文件:{new_files},触发补丁...")
target, mode, _ = find_target()
if target:
data = open(target, "rb").read()
state = detect_state(data)
if state == "original":
bak = target + ".1h-cache-bak"
if not os.path.isfile(bak):
shutil.copy2(target, bak)
patched, pcount = _apply_patch_bytes(data)
if patched is None:
log("锚点定位失败,跳过。")
known = current
continue
log(f"patch {pcount} 处副本")
with open(target, "wb") as f:
f.write(patched)
if mode == "binary" and IS_MAC:
resign_macos(target)
log(f"补丁成功:{target}")
for sec in find_secondary_copies(target):
try:
sec_bak = sec + ".1h-cache-bak"
if not os.path.isfile(sec_bak):
shutil.copy2(sec, sec_bak)
shutil.copy2(target, sec)
log(f"副本已同步:{sec}")
except Exception as e:
log(f"副本同步失败 {sec}: {e}")
elif state == "patched":
log("已是补丁状态,跳过。")
else:
log("锚点未找到,版本可能已变更。")
known = current
except Exception as e:
log(f"错误:{e}")
# ─── 监听安装分发 ─────────────────────────────────────────────────────────────
def do_watch_install(versions_dir: str) -> bool:
if versions_dir is None:
warn("npm 安装模式不支持目录监听(npm 更新不会产生新文件)。")
info("建议更新后手动运行:python3 claude-1h-cache.py patch")
return False
if IS_MAC:
return watch_install_macos(versions_dir)
elif IS_LINUX:
return watch_install_linux(versions_dir)
elif IS_WIN:
return watch_install_windows(versions_dir)
else:
warn(f"未知平台 {SYSTEM},跳过监听安装。")
return False
def do_watch_uninstall():
if IS_MAC:
watch_uninstall_macos()
elif IS_LINUX:
watch_uninstall_linux()
elif IS_WIN:
watch_uninstall_windows()
else:
warn(f"未知平台 {SYSTEM}。")
# ─── 权限提示 ─────────────────────────────────────────────────────────────────
def _hint_sudo():
if IS_WIN:
info("请以管理员身份运行 PowerShell / CMD,再重试。")
else:
info(f"请用:sudo python3 {SCRIPT}")
# ─── 主入口 ───────────────────────────────────────────────────────────────────
def main():
cmd = sys.argv[1] if len(sys.argv) > 1 else "all"
# 守护进程模式(内部调用,不打印 banner)
if cmd == "daemon":
watch_dir = sys.argv[2] if len(sys.argv) > 2 else None
if not watch_dir:
_, _, watch_dir = find_target()
if not watch_dir:
sys.exit(1)
daemon_watch(watch_dir)
return
print()
print(bold(cyan(" ╔═══════════════════════════════════════════════════╗")))
print(bold(cyan(f" ║ Claude Code 1h Cache Patch Tool v{VERSION} ║")))
print(bold(cyan(f" ║ 平台:{SYSTEM:<10} Python {sys.version.split()[0]:<10} ║")))
print(bold(cyan(" ╚═══════════════════════════════════════════════════╝")))
print()
# ── 定位
section("[ 1 ] 定位 Claude Code 安装...")
target, mode, versions_dir = find_target()
if not target:
err("未找到 Claude Code。请确认已安装(native 或 npm)。")
sys.exit(1)
secondaries = find_secondary_copies(target)
ok(f"安装类型:{bold(mode)}")
ok(f"目标文件:{cyan(target)}")
if versions_dir:
info(f"版本目录:{cyan(versions_dir)}")
for sec in secondaries:
ok(f"独立副本:{cyan(sec)}")
# ── status
if cmd == "status":
section("[ 状态 ]")
data = open(target, "rb").read()
state = detect_state(data)
if state == "patched":
ok("已打补丁,1h 缓存已启用。")
elif state == "original":
warn("未打补丁。")
else:
warn("状态未知,锚点字符串未找到。")
bak = target + ".1h-cache-bak"
if os.path.isfile(bak):
info(f"备份存在:{cyan(bak)}")
for sec in secondaries:
try:
sdata = open(sec, "rb").read()
except Exception as e:
warn(f"副本读取失败 {sec}: {e}")
continue
sstate = detect_state(sdata)
label = green("已打补丁") if sstate == "patched" else yellow("未打补丁") if sstate == "original" else red("未知")
info(f"副本 {cyan(sec)}: {label}")
sec_bak = sec + ".1h-cache-bak"
if os.path.isfile(sec_bak):
info(f" 副本备份:{cyan(sec_bak)}")
# 监听状态
if IS_MAC and os.path.isfile(_LAUNCHD_PLIST):
ok("launchd 自动监听已安装。")
elif IS_LINUX:
sp = os.path.join(_SYSTEMD_DIR, _SYSTEMD_PATH)
if os.path.isfile(sp):
ok("systemd 自动监听已安装。")
sys.exit(0)
# ── restore
if cmd == "restore":
section("[ 还原 ]")
do_restore(target, mode, secondaries)
sys.exit(0)
# ── unwatch
if cmd == "unwatch":
section("[ 卸载监听 ]")
do_watch_uninstall()
sys.exit(0)
# ── patch only
if cmd == "patch":
section("[ 打补丁 ]")
ok_patch = do_patch(target, mode, secondaries)
sys.exit(0 if ok_patch else 1)
# ── watch only
if cmd == "watch":
section("[ 安装自动监听 ]")
do_watch_install(versions_dir)
sys.exit(0)
# ── all(默认):patch + watch
section("[ 2 ] 打补丁...")
ok_patch = do_patch(target, mode, secondaries)
section("[ 3 ] 安装自动监听...")
ok_watch = do_watch_install(versions_dir)
print()
print(bold(green(" ┌────────────────────────────────────────────────┐")))
if ok_patch:
print(bold(green(" │ v 补丁已生效 │")))
if ok_watch:
print(bold(green(" │ v 自动监听已安装(更新后自动重打) │")))
print(bold(green(" └────────────────────────────────────────────────┘")))
print()
info("重启 Claude Code 后生效。")
info(f"查看日志:{cyan(os.path.join(HOME, '.local', 'share', 'claude', '1h-cache-patch.log'))}")
info(f"还原命令:python3 {os.path.basename(SCRIPT)} restore")
info(f"卸载监听:python3 {os.path.basename(SCRIPT)} unwatch")
print()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment