ISEP 0.1.0:環境設定收成一個 plugin,本機與雲端共用一份

leo 2026-08-20:「同一個 plugin 你用,薄殼也用,保證兩邊同步」
              「我要你幫雲端做薄殼,永遠都有問題,你要做的就是這組設定
                你自己可以 dogfooding」

搬進來:41 支 hook(51 條註冊)/7 支 command/2 支 skill/23 支腳本。
不搬 .env、wiki、docs——那些是知識不是環境。

51 條 hook 路徑全部從 $CLAUDE_PROJECT_DIR/.claude/hooks/ 改成 ${CLAUDE_PLUGIN_ROOT}/hooks/,
零漏網。那正是薄殼一直壞掉的根:雲端 cwd 不是真身,寫死路徑就斷。

尚未驗證:Claude Code 能不能從私有 Gitea repo 裝 marketplace(要憑證)。
下一步就是在本機實際裝一次,通了才動雲端 bootstrap.sh。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 11:41:46 +08:00
commit c2638668e3
85 changed files with 11879 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""daemon-selfcheck.py — 桌面小幫手(daemon)本機狀態自檢,唯讀。
為什麼有這支(leo 2026-08-19):
「現在本機的資料夾跟雲端已經不相連了。」
這句話有好幾種可能的形狀,而它們的處置完全不同:
① 帳號指的那台實例被重裝過(網址沒變、鑰匙換了) ← arcrun-rag#103
② 監看資料夾在本機被搬走/改名/刪掉
③ 資料夾是空的,雲端因此「什麼都沒有」 ← arcrun-rag#106
④ 真的連不上網路
這支不猜,只把**分得出這四種**的事實印出來。
🔴 唯讀:不寫任何檔、不連網、不印任何金鑰(api_keygemini_api_key 一律過濾)。
用法:
python3 scripts/daemon-selfcheck.py # 看 ~/.arcrun-rag/
python3 scripts/daemon-selfcheck.py /some/home # 指定家目錄(測試用)
"""
import json,glob,os,sys
H=os.path.expanduser(sys.argv[1] if len(sys.argv)>1 else "~")
D=os.path.join(H,".arcrun-rag")
SECRET={"api_key","gemini_api_key","password","token"}
def acc(a):
return {k:v for k,v in a.items() if k not in SECRET and not isinstance(v,(dict,list))}
print("狀態目錄:",D, "存在" if os.path.isdir(D) else "❌ 不存在")
try:
c=json.load(open(os.path.join(D,"config.json")))
except Exception as e:
print("config.json 讀不到:",e); c={}
accs=c.get("accounts") or ([{k:c.get(k) for k in ("cypher_url","namespace","email","instance_name")}] if c.get("cypher_url") else [])
print("\n帳號 %d 個:"%len(accs))
for a in accs: print(" -",acc(a))
print("\n監看資料夾:")
for f in (c.get("watch_folders") or ([c["watch_folder"]] if c.get("watch_folder") else [])): print(" -",f, "(本機存在)" if os.path.isdir(os.path.expanduser(f)) else "(❌ 本機找不到)")
for a in accs:
for f in (a.get("watch_folders") or []): print(" -",f,"[帳號 %s]"%a.get("instance_name",""), "(本機存在)" if os.path.isdir(os.path.expanduser(f)) else "(❌ 本機找不到)")
print("\n機器身分:")
try: print(" ",json.load(open(os.path.join(D,"machine.json"))))
except Exception as e: print(" machine.json 沒有或讀不到:",e,"(⇒ 這台還沒鑄過機器 ID,或 daemon 還沒跑過 0.18.33")
print("\nmanifest 的最後錯誤(每份只印一種):")
for p in sorted(glob.glob(os.path.join(D,"manifest*.json"))):
try: m=json.load(open(p))
except Exception as e: print(" ",os.path.basename(p),"讀不到",e); continue
errs={}
for k,v in (m.get("entries") or {}).items():
e=(v or {}).get("last_error") or ""
if e: errs[e]=errs.get(e,0)+1
print(" ",os.path.basename(p),"root=",m.get("root"),"檔數=",len(m.get("entries") or {}))
if not errs: print(" (無錯誤)")
for e,n in sorted(errs.items(),key=lambda x:-x[1])[:3]: print(" x%d %s"%(n,e[:160]))