feat(wiki): 監控目錄頁改走專用存取庫,比對鍵改用裸 HASH

What:MONITOR_CONTENTS 改由 wiki-repo CONTENTS 解析並透過 wiki-contents.sh
upsert 寫入,MONITOR_{HASH} 仍走 wiki-repo MONITOR。目錄頁新增一欄裸 HASH 當
比對鍵,第一欄的絕對網址連結留給人看。主機名一律取短名。

Why:目錄頁集中之後兩頁不再同庫,[[...]] 只在同一個 wiki 內解析,連結必須改絕對
網址。比對鍵原本是含網址的儲存格,換主機、換存取庫或 URL 編碼有差就比對不到,
巡檢每 15 分鐘寫一次,重複列會累積得很快。

How:目錄頁解不出存取庫時不再中止巡檢——那一頁只是索引,內容頁寫成就照樣寫心跳。
內容頁寫不成仍然中止且不寫心跳,自咬環的前提沒有鬆動。

Who:jsc-assist
This commit is contained in:
2026-09-02 11:01:51 +08:00
parent d349a76b9c
commit 4bf99dde76
10 changed files with 134 additions and 61 deletions
+37 -5
View File
@@ -74,6 +74,22 @@
# 軌跡留在摘要表:一輪一列,看得出是從哪一輪開始壞的。完整內容只留最新一輪,因為頁面要能
# 讀完才有人讀。
#
# --- 目錄頁與內容頁分屬兩個存取庫 ---
#
# 內容頁 MONITOR_{HASH} 住 MONITOR 那個存取庫,目錄頁 MONITOR_CONTENTS 住 CONTENTS 那個
# 專用存取庫,兩條解析鏈不互相退讓。所以目錄頁那一列指向內容頁的連結不能用 [[...]]:那種
# 連結只在同一個 wiki 裡解得開,跨庫是死連結,畫面上還看不出壞掉,要用絕對網址。
# 絕對網址要等內容頁真的寫進去才查得到(gitea.sh wiki-url 讀的是 API 回的 html_url),
# 而 collect 跑在寫入之前,這裡查不到。所以這支只把列組好、網址留佔位,換字交給呼叫端。
#
# --- 目錄頁的比對鍵是裸 HASH,不是那個連結 ---
#
# 目錄頁那一列另外留一欄裸 HASH(純文字、不帶連結),upsert 就拿那一欄當鍵。wiki-contents.sh
# 比對的是整格文字,拿含網址的連結當鍵太脆:GITEA_HOST 換掉、JSC_WIKI_REPO_MONITOR 換過存取
# 庫、Gitea 對頁名的網址編碼有差,整格文字就變了,鍵對不上就走附加那一支,同一台機器多出第二
# 列,舊列從此不再更新。這一頁每 15 分鐘寫一次,重複列累積得很快。裸 HASH 只由
# {主機名}/{登入帳號} 決定,上面三件事都動不到它。
#
# --- collect 的輸出 ---
#
# stdout 是 key=value,一行一個鍵,供呼叫端逐行取值。監控頁要用的 markdown 不印在
@@ -95,7 +111,9 @@
# summary_file= 「近 24 輪摘要」那一塊,表格裡先放本輪這一列,舊頁的資料列接在下面
# summary_row_file= 只有本輪那一列,方便直接插到既有表格最上面
# newpage_file= MONITOR_{HASH} 不存在時要建的整頁內容,三塊都已經排好
# contents_file= MONITOR_CONTENTS 那一列的欄位值
# contents_file= MONITOR_CONTENTS 那一列的欄位值。row= 就是整列 markdown,第一欄是連結,
# 網址的位置留 {監控頁絕對網址} 佔位,由呼叫端換掉,理由見下一段;第二欄是
# 裸 HASH,upsert 拿那一欄當鍵,理由見再下一段
#
# 環境變數:
# JSC_HOME 助理狀態檔的根目錄,預設 ~/.jsc
@@ -665,14 +683,15 @@ compose() {
printf '> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。\n'
printf '> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。\n'
printf '> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。\n'
printf '> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,別台機器的列一個字都不動。\n\n'
printf '> 目錄頁 `MONITOR_CONTENTS` 在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和這頁不同庫。\n'
printf '> 那一頁只更新自己那一列,別台機器的列一個字都不動,寫入交給 `jsc-gitea/tools/wiki-contents.sh upsert`。\n\n'
printf '```mermaid\nflowchart LR\n'
printf ' A[巡檢一輪] --> B[收攏四項結果]\n'
printf ' B --> C[讀回舊頁]\n'
printf ' C --> D[換掉最新一輪那一塊]\n'
printf ' D --> E[本輪摘要列插到表格最上面,截到 24 列]\n'
printf ' E --> F[整頁寫回]\n'
printf ' F --> G[更新 MONITOR_CONTENTS 自己那一列]\n'
printf ' F --> G[wiki-contents.sh upsert 更新目錄頁自己那一列]\n'
printf ' G --> H[最後才寫心跳]\n'
printf '```\n\n'
printf '## 本頁基本資料\n\n'
@@ -695,8 +714,15 @@ compose() {
printf 'last_patrol=%s\n' "$AT"
printf 'tasks_total=%s\n' "$TASKS_TOTAL"
printf 'tasks_failing=%s\n' "$TASKS_FAILING"
printf 'row=| [[%s]] | %s | %s | %s | %s | %s | %s |\n' \
"$PAGE" "$HOST" "$USER_NAME" "$HEARTBEAT_STATE" "$AT" "$TASKS_TOTAL" "$TASKS_FAILING"
printf 'hash=%s\n' "$HASH"
# 第一欄是連結,網址留佔位由呼叫端換掉,理由見檔頭「目錄頁與內容頁分屬兩個存取庫」。
# 連結文字先寫死成頁名:頁名這裡就知道,只有網址要等內容頁寫成才查得到。
# 第二欄是裸 HASH,upsert 拿它當鍵。鍵不能用第一欄那個連結:那一格含 GITEA_HOST 與頁名的
# 網址編碼,主機位址、存取庫或編碼一變,整格文字就變了,鍵對不上就每輪多附一列。裸 HASH
# 只跟 {主機名}/{登入帳號} 有關,那三件事都動不到它。
printf 'row=| [%s](%s) | %s | %s | %s | %s | %s | %s | %s |\n' \
"$PAGE" '{監控頁絕對網址}' "$HASH" "$HOST" "$USER_NAME" "$HEARTBEAT_STATE" "$AT" \
"$TASKS_TOTAL" "$TASKS_FAILING"
} >"$RD/contents.tsv"
return 0
}
@@ -725,7 +751,13 @@ case "$CMD" in
[ -n "$TRIGGER" ] || { if [ "${JSC_CLI:-}" = cron ]; then TRIGGER='排程'; else TRIGGER='手動'; fi; }
case "$TRIGGER" in 排程|事件|手動) ;; *) usage ;; esac
ROUND="$(date +%s)-$$"
# 主機名一律取短的:第一個小數點之前那一段。CHECK_{HASH} 的雜湊來源同樣是
# {主機}/{帳號},但那一邊由模型自己填,填進去的多半是短主機名;這一邊如果拿到 FQDN,
# 同一台機器就會算出兩個雜湊、開出兩張頁,而且兩張都看起來是對的。
# 不用 hostname -s:BusyBox 與部分系統沒有這個旗標,切字串到處都成立。
HOST=$(hostname 2>/dev/null || uname -n 2>/dev/null || printf 'unknown')
HOST="${HOST%%.*}"
[ -n "$HOST" ] || HOST='unknown'
USER_NAME="${USER:-$(id -un 2>/dev/null || printf 'unknown')}"
AT=$(date '+%Y-%m-%d %H:%M')
+10 -3
View File
@@ -104,7 +104,9 @@
# JSC_ASSISTANT_HEARTBEAT_TTL 心跳過期門檻,單位秒。巡檢週期由它算出來,也會快照進條目
# GITEA_HOST GITEA_TOKEN wiki 連線用。install 當下快照進條目
# JSC_WIKI_REPO wiki 存取庫預設值。install 當下快照進條目
# JSC_WIKI_REPO_{TYPE} 各頁型的 wiki 存取庫。已設定的全部快照進條目
# JSC_WIKI_REPO_{TYPE} 各頁型的 wiki 存取庫。已設定的全部快照進條目。名單是當下從
# 環境撈出來的,不寫死,所以新增頁型自動涵蓋,這支不必跟著改。
# 目錄頁那一支專用變數也在裡面
set -u
MARK_PREFIX='# jsc-assist:assistant'
@@ -290,6 +292,8 @@ patrol_command() {
# 要快照進條目的環境變數名稱。前四支是巡檢那一輪一定要用到的;JSC_WIKI_REPO 系列逐台機器
# 不同,直接從現在的環境撈出所有已設定的,不寫死清單。
# 不寫死是刻意的:頁型會增加,目錄頁後來也分了自己的存取庫變數,寫死清單就要跟著改一次,
# 而漏掉的那一個在排程那一輪是空值,wiki 寫不成、心跳不寫,外面只看得到心跳過期。
snapshot_names() {
printf '%s\n' GITEA_HOST GITEA_TOKEN JSC_HOME JSC_ASSISTANT_HEARTBEAT_TTL
env 2>/dev/null \
@@ -424,7 +428,7 @@ esac
# --- 裝完要開的權限 ---
# 排程那一輪跑在沒有人的工作階段:跳出一次權限詢問就是整輪卡住,卡到鎖逾時才有下一輪。
# 這一輪會用到的三支腳本,各印裸路徑、`sh 路徑`、`bash 路徑` 三種呼叫形式——同一支腳本換
# 這一輪會用到的每一支腳本,各印裸路徑、`sh 路徑`、`bash 路徑` 三種呼叫形式——同一支腳本換
# 一種叫法就是另一條規則,少印一種就會在那一種叫法上卡住。
# 路徑一律是 $JSC_HOME/current/{外掛名} 那一組,不是這支腳本現在被放在哪裡:規則放行的是
# 那一組路徑,巡檢那一輪也只能用那一組路徑去叫工具,兩邊對得起來才不會被靜靜擋掉。
@@ -432,10 +436,13 @@ print_allow_rules() {
# gitea.sh 一定要有自己這一條。`Skill(jsc-gitea:wiki)` 只放行「叫用那支技能」,技能裡的
# 每一個 Bash 呼叫仍然各自受檢,少了這一條,那一輪會在寫監控頁時靜靜被擋——頁寫不成就
# 不寫心跳,外面只看得到心跳過期,看不出是權限擋的。
# 目錄頁那一列改由 wiki-contents.sh 寫,所以它也要有自己這一條:巡檢那一輪會直接叫它,
# 少了規則就會停在權限詢問,而那一輪沒有人可以按同意。
for _s in "$CURRENT/jsc-assist/tools/schedule.sh" \
"$CURRENT/jsc-assist/tools/patrol.sh" \
"$CURRENT/jsc-hooks/hooks/heartbeat.sh" \
"$CURRENT/jsc-gitea/tools/gitea.sh"; do
"$CURRENT/jsc-gitea/tools/gitea.sh" \
"$CURRENT/jsc-gitea/tools/wiki-contents.sh"; do
printf 'allow_rule=Bash(%s:*)\n' "$_s"
printf 'allow_rule=Bash(sh %s:*)\n' "$_s"
printf 'allow_rule=Bash(bash %s:*)\n' "$_s"