Merge pull request 'release: wiki 目錄頁專用存取庫、HASH 完整 40 碼、閘門依 CLI 分流' (#10) from develop into master
Reviewed-on: #10 Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #10.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
### `assistant`
|
### `assistant`
|
||||||
|
|
||||||
助理主體,四個操作:`start` 啟動、`status` 查現況、`patrol` 跑一輪巡檢、`stop` 停止。心跳的寫入、判定與清除一律交給 `jsc-hooks` 的 `hooks/heartbeat.sh`,判定只有那一份;系統排程一律交給 `tools/schedule.sh`;一輪巡檢的流程交給 `tools/patrol.sh`。工具一律用 `$JSC_HOME/current/{外掛名}` 那一組不帶版本的路徑叫,不用技能提示給的快取基底目錄——權限只放行 current 那一組。**心跳由巡檢寫,而且只由巡檢寫**:一輪跑完、結果寫上監控頁了,才寫那一次心跳,所以心跳新鮮等於「上一輪巡檢真的做完了」。`start` 先跑一輪巡檢,再裝上巡檢那一筆排程;巡檢週期由心跳的過期門檻算出來,兩個數字綁在一起。`patrol` 讀四項來源(使用統計、版本與重啟閘門、SDLC 階段鎖與工作包鎖、心跳自述),四項各自獨立,一項掛掉其餘三項照跑、照記,結果寫上 `MONITOR_{HASH}`:那頁固定三塊,基本資料不動、最新一輪整塊換掉、摘要表保留近 24 輪,一輪一列。`status` 全程唯讀,讀心跳、排程與待辦簿,印成三塊;助理沒在跑就印「助理未運行」,不當成錯誤。`stop` 先移除排程再清掉心跳,順序不能反。這支不參與閘門判定、不做決策、巡檢那一路全程不問人。
|
助理主體,四個操作:`start` 啟動、`status` 查現況、`patrol` 跑一輪巡檢、`stop` 停止。心跳的寫入、判定與清除一律交給 `jsc-hooks` 的 `hooks/heartbeat.sh`,判定只有那一份;系統排程一律交給 `tools/schedule.sh`;一輪巡檢的流程交給 `tools/patrol.sh`。工具一律用 `$JSC_HOME/current/{外掛名}` 那一組不帶版本的路徑叫,不用技能提示給的快取基底目錄——權限只放行 current 那一組。**心跳由巡檢寫,而且只由巡檢寫**:一輪跑完、結果寫上監控頁了,才寫那一次心跳,所以心跳新鮮等於「上一輪巡檢真的做完了」。`start` 先跑一輪巡檢,再裝上巡檢那一筆排程;巡檢週期由心跳的過期門檻算出來,兩個數字綁在一起。`patrol` 讀四項來源(使用統計、版本與重啟閘門、SDLC 階段鎖與工作包鎖、心跳自述),四項各自獨立,一項掛掉其餘三項照跑、照記,結果寫上 `MONITOR_{HASH}`:那頁固定三塊,基本資料不動、最新一輪整塊換掉、摘要表保留近 24 輪,一輪一列。目錄頁 `MONITOR_CONTENTS` 在另一個存取庫(`JSC_WIKI_REPO_CONTENTS`),只更新自己那一列,交給 `jsc-gitea/tools/wiki-contents.sh upsert` 寫,連結用絕對網址;那個存取庫沒設定時只少一列索引,這一輪照樣算跑完、照樣寫心跳。`status` 全程唯讀,讀心跳、排程與待辦簿,印成三塊;助理沒在跑就印「助理未運行」,不當成錯誤。`stop` 先移除排程再清掉心跳,順序不能反。這支不參與閘門判定、不做決策、巡檢那一路全程不問人。
|
||||||
|
|
||||||
<!-- JSC-SKILLS:END -->
|
<!-- JSC-SKILLS:END -->
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
| Plugin | 最低版本 | 用途 |
|
| Plugin | 最低版本 | 用途 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `jsc-cli` | `>=0.2.7` | CLI 偵測與委派 |
|
| `jsc-cli` | `>=0.2.7` | CLI 偵測與委派 |
|
||||||
| `jsc-gitea` | `>=0.2.0` | 監控頁的所有 wiki 讀寫,一律經 `tools/gitea.sh` |
|
| `jsc-gitea` | `>=0.2.0` | 監控頁的所有 wiki 讀寫,一律經 `tools/gitea.sh`;目錄頁那一列走 `tools/wiki-contents.sh upsert`,頁名雜湊走 `tools/hash-id` |
|
||||||
| `jsc-hooks` | `>=0.3.7` | 心跳、閘門與事件來源(`$JSC_HOME` 底下的狀態檔)。心跳的寫入、判定與清除一律走 `hooks/heartbeat.sh`,那支腳本是 `0.3.7` 才有的 |
|
| `jsc-hooks` | `>=0.3.7` | 心跳、閘門與事件來源(`$JSC_HOME` 底下的狀態檔)。心跳的寫入、判定與清除一律走 `hooks/heartbeat.sh`,那支腳本是 `0.3.7` 才有的 |
|
||||||
| `jsc-log` | `>=0.1.4` | 使用統計與工作日誌的資料來源 |
|
| `jsc-log` | `>=0.1.4` | 使用統計與工作日誌的資料來源 |
|
||||||
|
|
||||||
@@ -43,10 +43,10 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
| 檔案 | 用途 |
|
| 檔案 | 用途 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `tools/schedule.sh` | 助理系統排程的安裝、移除與查現況。三個子命令 `install`、`remove`、`status`,只裝 `patrol` 這一筆——心跳由巡檢自己寫,`install heartbeat` 一律回 6,舊版遺留的心跳條目由 `install patrol` 順手清掉。巡檢週期由心跳的過期門檻算出來(`2 × 週期 × 60 < 門檻`,再取能整除一小時的分鐘數):門檻 300 秒是每 2 分鐘一輪,門檻 1800 秒是每 12 分鐘一輪。Linux、WSL 與 macOS 走 crontab,Windows 走 schtasks。條目行尾帶固定標記 `# jsc-assist:assistant {工作}`,只動自己那一筆,別人的排程一行都不碰。條目自己把環境帶齊:CLI 用 `command -v` 解成絕對路徑、安裝當下把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL` 與已設定的 `JSC_WIKI_REPO` 系列快照進條目、自帶 `JSC_GITEA_CONFIRM=yes`——cron 的 PATH 很短、不讀設定檔、也沒有 tty。印出條目時金鑰一律遮掉,條目本身含金鑰快照,crontab 檔案要保持只有本人讀得到,變數改過要重跑一次 install。裝完會檢查排程服務在不在跑,沒跑就回 1——WSL 預設不啟動 cron;也會檢查 `$JSC_HOME/current` 那組連結在不在、印出這一輪要開的 allow 規則,連結不在只警告、不代建。`--dry-run` 只印組出來的條目與寫回後的內容,什麼都不動 |
|
| `tools/schedule.sh` | 助理系統排程的安裝、移除與查現況。三個子命令 `install`、`remove`、`status`,只裝 `patrol` 這一筆——心跳由巡檢自己寫,`install heartbeat` 一律回 6,舊版遺留的心跳條目由 `install patrol` 順手清掉。巡檢週期由心跳的過期門檻算出來(`2 × 週期 × 60 < 門檻`,再取能整除一小時的分鐘數):門檻 300 秒是每 2 分鐘一輪,門檻 1800 秒是每 12 分鐘一輪。Linux、WSL 與 macOS 走 crontab,Windows 走 schtasks。條目行尾帶固定標記 `# jsc-assist:assistant {工作}`,只動自己那一筆,別人的排程一行都不碰。條目自己把環境帶齊:CLI 用 `command -v` 解成絕對路徑、安裝當下把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL` 與已設定的 `JSC_WIKI_REPO` 系列快照進條目、自帶 `JSC_GITEA_CONFIRM=yes`。`JSC_WIKI_REPO` 系列含內容頁的 `JSC_WIKI_REPO_MONITOR` 與目錄頁的 `JSC_WIKI_REPO_CONTENTS`:監控頁 `MONITOR_{HASH}` 與目錄頁 `MONITOR_CONTENTS` 分屬不同存取庫,兩支變數都要帶。名單是安裝當下從環境撈出所有已設定的,不寫死,所以新增的頁型變數自動涵蓋,這支不必跟著改——cron 的 PATH 很短、不讀設定檔、也沒有 tty。印出條目時金鑰一律遮掉,條目本身含金鑰快照,crontab 檔案要保持只有本人讀得到,變數改過要重跑一次 install。裝完會檢查排程服務在不在跑,沒跑就回 1——WSL 預設不啟動 cron;也會檢查 `$JSC_HOME/current` 那組連結在不在、印出這一輪要開的 allow 規則,連結不在只警告、不代建。`--dry-run` 只印組出來的條目與寫回後的內容,什麼都不動 |
|
||||||
| `tools/patrol.sh` | 一輪巡檢的收攏與收口。三個子命令:`collect` 取鎖、讀四項來源、組出監控頁的「最新一輪」與「近 24 輪摘要」兩塊、本輪的摘要列與目錄頁那一列;`finish` 在監控頁寫成之後才寫心跳、換上用量快照、放掉鎖;`abort` 只放掉鎖,不寫心跳。四項來源各自獨立,一項失敗其餘三項照跑,失敗那一項在頁上寫明是「這一項失敗」而不是沒資料。整輪拿一把目錄鎖,上一輪還在跑就回 4 讓開;鎖逾時(門檻取心跳門檻)會被下一輪搶回來,並在頁上記一筆。`version-guard.sh report` 回「查詢失敗」時照原字抄,不補查、不美化 |
|
| `tools/patrol.sh` | 一輪巡檢的收攏與收口。三個子命令:`collect` 取鎖、讀四項來源、組出監控頁的「最新一輪」與「近 24 輪摘要」兩塊、本輪的摘要列與目錄頁那一列(那一列的第一欄是連結,網址留佔位,等監控頁寫成之後由呼叫端用 `gitea.sh wiki-url` 的絕對網址換掉;第 2 欄是裸 HASH,upsert 拿那一欄當鍵);`finish` 在監控頁寫成之後才寫心跳、換上用量快照、放掉鎖;`abort` 只放掉鎖,不寫心跳。四項來源各自獨立,一項失敗其餘三項照跑,失敗那一項在頁上寫明是「這一項失敗」而不是沒資料。整輪拿一把目錄鎖,上一輪還在跑就回 4 讓開;鎖逾時(門檻取心跳門檻)會被下一輪搶回來,並在頁上記一筆。`version-guard.sh report` 回「查詢失敗」時照原字抄,不補查、不美化 |
|
||||||
| `references/behaviors.md` | 本 domain 的技能行為清單:一支技能一節,五列記下觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象,供稽核與驗證比對。格式合約見 `plugins/meta` 的 `references/guidelines.md`「技能行為清單」 |
|
| `references/behaviors.md` | 本 domain 的技能行為清單:一支技能一節,五列記下觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象,供稽核與驗證比對。格式合約見 `plugins/meta` 的 `references/guidelines.md`「技能行為清單」 |
|
||||||
| `templates/monitor-contents.md` | 目錄頁 `MONITOR_CONTENTS` 的範本。一列代表一台機器,雜湊來源是 `{主機名}/{登入帳號}`。寫入語意是**只更新自己那一列**:比對主機與帳號兩欄,別台機器的列原樣保留,禁止整頁覆蓋 |
|
| `templates/monitor-contents.md` | 目錄頁 `MONITOR_CONTENTS` 的範本,這一頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和監控頁不同庫。一列代表一台機器,雜湊來源是 `{主機名}/{登入帳號}`,主機名取短的那一段。寫入一律走 `jsc-gitea/tools/wiki-contents.sh upsert`,比對鍵是第 2 欄的裸 HASH:**只更新自己那一列**,別台機器的列原樣保留,禁止整頁覆蓋。第一欄的連結用 `gitea.sh wiki-url` 的絕對網址(跨存取庫 `[[...]]` 連不過去),但那一格含主機位址與網址編碼,會變,所以不當鍵 |
|
||||||
| `templates/monitor-page.md` | 內容頁 `MONITOR_{HASH}` 的範本。記的是這台機器的巡檢軌跡。頁面固定三塊:本頁基本資料建頁時寫一次就不動、最新一輪每輪整塊換掉、近 24 輪摘要一輪一列且最新的在最上面。軌跡留在摘要表,完整內容只留最新一輪,頁面才讀得完 |
|
| `templates/monitor-page.md` | 內容頁 `MONITOR_{HASH}` 的範本。記的是這台機器的巡檢軌跡。頁面固定三塊:本頁基本資料建頁時寫一次就不動、最新一輪每輪整塊換掉、近 24 輪摘要一輪一列且最新的在最上面。軌跡留在摘要表,完整內容只留最新一輪,頁面才讀得完 |
|
||||||
|
|
||||||
## 助理的狀態檔
|
## 助理的狀態檔
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-assist",
|
"name": "jsc-assist",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
"description": "助理:事件收攏、健康巡檢與待辦簿(MONITOR_{HASH} wiki 頁)",
|
||||||
"skills": "./skills/",
|
"skills": "./skills/",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
| 項目 | 內容 |
|
| 項目 | 內容 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 觸發時機 | 要啟動助理、要停止助理、要跑一輪巡檢,或要問助理現在還在不在跑、待辦簿剩下哪幾筆時用。四個操作 `start`、`status`、`patrol`、`stop` 都走這一支。排程每一輪叫起來的也是這一支的 `patrol`。執行環境健檢不走這支,走 `jsc-cli:doctor`。技能使用次數不走這支,走 `jsc-log:stats` |
|
| 觸發時機 | 要啟動助理、要停止助理、要跑一輪巡檢,或要問助理現在還在不在跑、待辦簿剩下哪幾筆時用。四個操作 `start`、`status`、`patrol`、`stop` 都走這一支。排程每一輪叫起來的也是這一支的 `patrol`。執行環境健檢不走這支,走 `jsc-cli:doctor`。技能使用次數不走這支,走 `jsc-log:stats` |
|
||||||
| 關鍵步驟 | 先認出使用者要的是哪一個操作,`patrol` 那一路全程不問人。`start`:先照 `patrol` 的每一步跑完一輪巡檢,第一次心跳由那一輪寫、不另外寫、跑不完就不算啟動、跑 `heartbeat.sh report` 確認 `state=fresh`、跑 `tools/schedule.sh install patrol` 裝巡檢那一筆排程、把它印的 `allow_rule=` 每一行、環境快照提醒與 `current` 連結缺漏的警告原樣轉給人、依結束碼選一段收尾訊息印出——排程接上、排程寫進去了但 cron 沒在跑、排程沒接上三種各一段。心跳那一筆不裝了,`install heartbeat` 一律回 6。`patrol`:跑 `tools/patrol.sh collect` 取鎖並讀四項來源、結束碼 4 就讓開不寫任何東西、結束碼 1 與 3 照樣把這一輪寫上監控頁、`hash` 是空的就 `abort`、經 `jsc-gitea:wiki` 讀回 `MONITOR_{HASH}` 舊頁、基本資料原樣留著、最新一輪那一塊整塊換成 `latest_file`、`summary_file` 的本輪那一列擺最上面(五欄:巡檢時間、本輪判定、四項成敗、待人處理、警示來源)、舊的資料列接在下面並截到 24 列、三塊重組成整頁寫回、頁不存在(唯有結束碼 4)才用 `newpage_file` 建頁、讀不回舊頁就不寫、把 `contents_file` 的 `row` 更新到 `MONITOR_CONTENTS` 自己那一列、兩次寫入任一失敗就 `abort` 且不寫心跳、全部寫成才跑 `tools/patrol.sh finish` 寫心跳、最後印出四項結果、判成警示時的警示來源與待人處理列。`status`:跑 `heartbeat.sh report` 取心跳現況、把 `state` 對映成新鮮、過期、心跳檔損壞、不存在、不自己解析心跳檔也不自己判定、從 `file=` 解出助理目錄後列出 `tasks/` 底下每一個檔案並解析 `state`、`title`、`next_run`、`fail_count`、跑 `tools/schedule.sh status` 取排程現況與週期、印成心跳、排程、待辦三塊、`fail_count` 大於 0 的列標上「已連續失敗 N 次」、心跳與排程兜起來會誤讀的四種組合各補一句話。`stop`:先跑 `heartbeat.sh report` 留下原本的狀態、再跑 `tools/schedule.sh remove all` 移除排程與舊版遺留的心跳條目、最後才跑 `heartbeat.sh clear` 清掉心跳、印出停止訊息並說明心跳清掉之後閘門會擋人、同時說明閘門還沒接線所以現在擋不到人 |
|
| 關鍵步驟 | 先認出使用者要的是哪一個操作,`patrol` 那一路全程不問人。`start`:先照 `patrol` 的每一步跑完一輪巡檢,第一次心跳由那一輪寫、不另外寫、跑不完就不算啟動、跑 `heartbeat.sh report` 確認 `state=fresh`、跑 `tools/schedule.sh install patrol` 裝巡檢那一筆排程、把它印的 `allow_rule=` 每一行、環境快照提醒與 `current` 連結缺漏的警告原樣轉給人、依結束碼選一段收尾訊息印出——排程接上、排程寫進去了但 cron 沒在跑、排程沒接上三種各一段。心跳那一筆不裝了,`install heartbeat` 一律回 6。`patrol`:跑 `tools/patrol.sh collect` 取鎖並讀四項來源、結束碼 4 就讓開不寫任何東西、結束碼 1 與 3 照樣把這一輪寫上監控頁、`hash` 是空的就 `abort`、經 `jsc-gitea:wiki` 讀回 `MONITOR_{HASH}` 舊頁、基本資料原樣留著、最新一輪那一塊整塊換成 `latest_file`、`summary_file` 的本輪那一列擺最上面(五欄:巡檢時間、本輪判定、四項成敗、待人處理、警示來源)、舊的資料列接在下面並截到 24 列、三塊重組成整頁寫回、頁不存在(唯有結束碼 4)才用 `newpage_file` 建頁、讀不回舊頁就不寫、監控頁寫成之後跑 `gitea.sh wiki-url` 取那一頁的絕對網址並依結束碼分流(4 回步驟三重寫、5 沒有 `html_url`、7 與 8 走 `abort`,其餘非 0 也走 `abort`,網址取不到就不寫那一列)、換掉 `contents_file` 的 `row` 裡 `{監控頁絕對網址}` 那個佔位、用 `jsc-gitea/tools/wiki-contents.sh upsert MONITOR 2` 以第 2 欄的裸 HASH 當鍵更新 `MONITOR_CONTENTS` 自己那一列並一律帶上 `templates/monitor-contents.md` 當範本、目錄頁回 3(`CONTENTS` 存取庫沒設定)不中止這一輪,照樣往下寫心跳,並把「設 `JSC_WIKI_REPO_CONTENTS` 或 `JSC_WIKI_REPO`」列進待人處理、監控頁任一失敗或目錄頁其餘非 0 才 `abort` 且不寫心跳、跑 `tools/patrol.sh finish` 寫心跳、最後印出四項結果、判成警示時的警示來源與待人處理列。`status`:跑 `heartbeat.sh report` 取心跳現況、把 `state` 對映成新鮮、過期、心跳檔損壞、不存在、不自己解析心跳檔也不自己判定、從 `file=` 解出助理目錄後列出 `tasks/` 底下每一個檔案並解析 `state`、`title`、`next_run`、`fail_count`、跑 `tools/schedule.sh status` 取排程現況與週期、印成心跳、排程、待辦三塊、`fail_count` 大於 0 的列標上「已連續失敗 N 次」、心跳與排程兜起來會誤讀的四種組合各補一句話。`stop`:先跑 `heartbeat.sh report` 留下原本的狀態、再跑 `tools/schedule.sh remove all` 移除排程與舊版遺留的心跳條目、最後才跑 `heartbeat.sh clear` 清掉心跳、印出停止訊息並說明心跳清掉之後閘門會擋人、同時說明閘門還沒接線所以現在擋不到人 |
|
||||||
| 外部呼叫 | 工具一律走 `$JSC_HOME/current/{外掛名}` 那一組不帶版本的路徑:`current/jsc-assist/tools/patrol.sh`、`current/jsc-assist/tools/schedule.sh`、`current/jsc-hooks/hooks/heartbeat.sh`,wiki 那一支是 `current/jsc-gitea/tools/gitea.sh`,`$JSC_HOME` 沒設就退回 `~/.jsc`;不拿技能提示給的快取基底目錄組工具路徑——權限只放行 current 那一組,用錯路徑會被靜靜擋掉。`jsc-hooks/hooks/heartbeat.sh` 的 `write`、`report`、`clear` 三個子命令,六個結束碼各有處置:0 往下走、1 與 3 印「助理未運行」、2 回報判不出狀態並停下、4 當成不新鮮並回報心跳檔損壞、5 是嚴重狀況要吵出來且不得回報成功、6 是呼叫寫錯要更正後重跑。`write` 只由 `tools/patrol.sh finish` 呼叫,技能自己不呼叫。本 domain 的 `tools/schedule.sh` 的 `install`、`remove`、`status` 三個子命令:`install` 會查 `$JSC_HOME/current/jsc-assist` 與 `$JSC_HOME/current/jsc-gitea` 兩個連結在不在、不在就警告且不代建,會把巡檢的 CLI 用 `command -v` 解成絕對路徑、把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL` 與所有已設定的 `JSC_WIKI_REPO` 系列快照進條目、條目自帶 `JSC_GITEA_CONFIRM=yes`、並印出這一輪要開的 `allow_rule=` 規則(四支腳本各三種呼叫形式,含 `gitea.sh`——`Skill(jsc-gitea:wiki)` 只放行叫用技能,技能內部的 Bash 呼叫仍各自受檢;路徑是 `current` 那一組確切路徑,不用萬用字元);七個結束碼各有處置:0 往下走、1 是條目裝了但 cron 沒在跑要照實講不會執行、2 是缺 jsc-hooks 導致門檻讀不到、3 是這台機器沒有排程機制、4 是排程操作失敗要原樣引用 stderr、5 是回讀驗證失敗要叫人自己去看 `crontab -l`、6 是呼叫寫錯,含 `install heartbeat`、週期塞不進門檻、判不出 CLI,以及那一支 CLI 的執行檔不在 `PATH` 上。本 domain 的 `tools/patrol.sh` 的 `collect`、`finish`、`abort` 三個子命令,七個結束碼各有處置:0 往下走、1 部分失敗照樣寫頁、2 是 finish 找不到 heartbeat.sh 要回報「記下來了但沒有心跳」、3 是四項全失敗照樣寫頁且判定異常、4 是讓開或鎖被搶走一律不寫心跳、5 是檔案系統失敗要吵出來、6 是呼叫寫錯。巡檢那四項讀 `jsc-log/tools/usage-stats.sh`、`jsc-hooks/hooks/version-guard.sh report`、`jsc-hooks/hooks/restart-gate.sh report`、`$JSC_HOME/sessions/*.stage`、`$JSC_HOME/wp/*.pr`、`heartbeat.sh report`,全部只讀,任一項失敗不影響其餘三項。wiki 讀寫一律經 `jsc-gitea:wiki`,技能自己不拼 API 呼叫。crontab 與 schtasks 一律經 `tools/schedule.sh`。另外唯讀 `$JSC_HOME/assistant/tasks/` 底下的檔案。呼叫端沒講清楚要哪一個操作時走 `jsc-ask:ask` 的決策樹問,但 `patrol` 那一路一律不問。不參與閘門判定 |
|
| 外部呼叫 | 工具一律走 `$JSC_HOME/current/{外掛名}` 那一組不帶版本的路徑:`current/jsc-assist/tools/patrol.sh`、`current/jsc-assist/tools/schedule.sh`、`current/jsc-hooks/hooks/heartbeat.sh`,wiki 那一支是 `current/jsc-gitea/tools/gitea.sh`,`$JSC_HOME` 沒設就退回 `~/.jsc`;不拿技能提示給的快取基底目錄組工具路徑——權限只放行 current 那一組,用錯路徑會被靜靜擋掉。`jsc-hooks/hooks/heartbeat.sh` 的 `write`、`report`、`clear` 三個子命令,六個結束碼各有處置:0 往下走、1 與 3 印「助理未運行」、2 回報判不出狀態並停下、4 當成不新鮮並回報心跳檔損壞、5 是嚴重狀況要吵出來且不得回報成功、6 是呼叫寫錯要更正後重跑。`write` 只由 `tools/patrol.sh finish` 呼叫,技能自己不呼叫。本 domain 的 `tools/schedule.sh` 的 `install`、`remove`、`status` 三個子命令:`install` 會查 `$JSC_HOME/current/jsc-assist` 與 `$JSC_HOME/current/jsc-gitea` 兩個連結在不在、不在就警告且不代建,會把巡檢的 CLI 用 `command -v` 解成絕對路徑、把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL` 與所有已設定的 `JSC_WIKI_REPO` 系列快照進條目(含內容頁的 `JSC_WIKI_REPO_MONITOR` 與目錄頁的 `JSC_WIKI_REPO_CONTENTS`,名單當下從環境撈、不寫死,新頁型自動涵蓋)、條目自帶 `JSC_GITEA_CONFIRM=yes`、並印出這一輪要開的 `allow_rule=` 規則(五支腳本各三種呼叫形式,含 `gitea.sh` 與 `wiki-contents.sh`——`Skill(jsc-gitea:wiki)` 只放行叫用技能,技能內部的 Bash 呼叫仍各自受檢;路徑是 `current` 那一組確切路徑,不用萬用字元);七個結束碼各有處置:0 往下走、1 是條目裝了但 cron 沒在跑要照實講不會執行、2 是缺 jsc-hooks 導致門檻讀不到、3 是這台機器沒有排程機制、4 是排程操作失敗要原樣引用 stderr、5 是回讀驗證失敗要叫人自己去看 `crontab -l`、6 是呼叫寫錯,含 `install heartbeat`、週期塞不進門檻、判不出 CLI,以及那一支 CLI 的執行檔不在 `PATH` 上。本 domain 的 `tools/patrol.sh` 的 `collect`、`finish`、`abort` 三個子命令,七個結束碼各有處置:0 往下走、1 部分失敗照樣寫頁、2 是 finish 找不到 heartbeat.sh 要回報「記下來了但沒有心跳」、3 是四項全失敗照樣寫頁且判定異常、4 是讓開或鎖被搶走一律不寫心跳、5 是檔案系統失敗要吵出來、6 是呼叫寫錯。巡檢那四項讀 `jsc-log/tools/usage-stats.sh`、`jsc-hooks/hooks/version-guard.sh report`、`jsc-hooks/hooks/restart-gate.sh report`、`$JSC_HOME/sessions/*.stage`、`$JSC_HOME/wp/*.pr`、`heartbeat.sh report`,全部只讀,任一項失敗不影響其餘三項。wiki 讀寫一律經 `jsc-gitea:wiki`,技能自己不拼 API 呼叫;只有目錄頁那一列例外,走 `jsc-gitea/tools/wiki-contents.sh upsert`,它自己解 `CONTENTS` 存取庫、自己讀回整頁比對鍵,七個結束碼各有處置:0 已更新或已新增、1 寫入失敗要 `abort`、2 參數錯就改正重跑(範本路徑不存在也回這一碼,代表 plugin 沒裝齊)、3 是 `CONTENTS` 存取庫未設定且**不中止這一輪**、4 是頁不存在又沒給範本,本技能一律帶第五個參數所以不會出現、7 金鑰失效要 `abort`、8 其他 API 失敗要 `abort`。比對鍵取那一列第 2 欄的裸 HASH,不取第一欄那個連結:連結含 `GITEA_HOST` 與頁名的網址編碼,那三樣一變鍵就對不上,同一台機器每輪多附一列。跨存取庫的連結一律取 `gitea.sh wiki-url` 印的絕對網址,不用 `[[...]]`,那一支的結束碼 4、5、7、8 與其餘非 0 各有處置;頁名雜湊一律取 `gitea.sh hash-id`/`tools/hash-id` 印的完整 40 碼大寫十六進位,不截短、不加前綴、不手算,空輸入回 2。crontab 與 schtasks 一律經 `tools/schedule.sh`。另外唯讀 `$JSC_HOME/assistant/tasks/` 底下的檔案。呼叫端沒講清楚要哪一個操作時走 `jsc-ask:ask` 的決策樹問,但 `patrol` 那一路一律不問。不參與閘門判定 |
|
||||||
| 完成條件 | `start` 要那一輪巡檢的 `finish` 回 0 且 `report` 回 `state=fresh`,才算啟動成功;巡檢沒寫成心跳一律回報失敗並停下,不得宣稱啟動;`schedule.sh install patrol` 回 1 要講明條目不會被執行與 `sudo service cron start`,不得宣稱排程會定時執行;回 0 或 1 都要把 `allow_rule=` 各行、「條目含金鑰快照、變數改了要重裝」這句提醒,以及 `current` 連結缺漏的警告轉出去。`patrol` 要四項各自有 `status`、監控頁三塊重組寫成、目錄頁那一列更新成功、`finish` 回 0,才算一輪跑完;`collect` 回 4 是讓開,不算失敗也不寫任何東西;舊頁讀不回來就不寫,回報「這一輪沒有結果」;監控頁或目錄頁任一沒寫成就 `abort`,心跳一定不寫。`status` 要印出現況表,或印出「助理未運行」並說明原因;心跳不存在、待辦簿目錄不存在、待辦簿零筆、排程沒裝,四種都算正常結束。`stop` 要 `schedule.sh remove all` 先回 0、`clear` 再回 0,並印出帶三段話的停止訊息;`remove` 非 0 就回報排程還在、助理停不掉,不清心跳也不印停止訊息;`clear` 回 5 就回報心跳檔還在、助理沒有確實停掉,不印停止訊息 |
|
| 完成條件 | `start` 要那一輪巡檢的 `finish` 回 0 且 `report` 回 `state=fresh`,才算啟動成功;巡檢沒寫成心跳一律回報失敗並停下,不得宣稱啟動;`schedule.sh install patrol` 回 1 要講明條目不會被執行與 `sudo service cron start`,不得宣稱排程會定時執行;回 0 或 1 都要把 `allow_rule=` 各行、「條目含金鑰快照、變數改了要重裝」這句提醒,以及 `current` 連結缺漏的警告轉出去。`patrol` 要四項各自有 `status`、監控頁三塊重組寫成、目錄頁那一列更新成功或以結束碼 3 回報成沒更新、`finish` 回 0,才算一輪跑完;`collect` 回 4 是讓開,不算失敗也不寫任何東西;舊頁讀不回來就不寫,回報「這一輪沒有結果」;監控頁沒寫成就 `abort`,心跳一定不寫;目錄頁除了結束碼 3 之外的非 0 也一樣 `abort`,結束碼 3 只少一列索引,那一輪的結果已經在監控頁上,照樣寫心跳並把缺的變數列進待人處理。`status` 要印出現況表,或印出「助理未運行」並說明原因;心跳不存在、待辦簿目錄不存在、待辦簿零筆、排程沒裝,四種都算正常結束。`stop` 要 `schedule.sh remove all` 先回 0、`clear` 再回 0,並印出帶三段話的停止訊息;`remove` 非 0 就回報排程還在、助理停不掉,不清心跳也不印停止訊息;`clear` 回 5 就回報心跳檔還在、助理沒有確實停掉,不印停止訊息 |
|
||||||
| 可驗證跡象 | `start` 之後 `$JSC_HOME/assistant/heartbeat` 存在,`ts` 是剛才那一輪的時間,`crontab -l` 找得到一筆帶 `# jsc-assist:assistant patrol` 的條目,而且只有一筆,帶 `# jsc-assist:assistant heartbeat` 的舊條目一筆都不剩;那一筆條目裡的 CLI 是絕對路徑,前面帶著 `JSC_GITEA_CONFIRM=yes` 與環境變數快照;install 印出的 `allow_rule=` 都是 `$JSC_HOME/current` 那一組確切路徑,沒有萬用字元,也沒有 `Write(...)`。`patrol` 跑完之後 wiki 的 `MONITOR_{HASH}` 只有三塊:基本資料一字未改、最新一輪換成本輪、摘要表最上面一列是本輪且總列數不超過 24,`MONITOR_CONTENTS` 只有自己那一列變動,`$JSC_HOME/assistant/patrol/` 底下有本輪的 `latest.md`、`summary.md`、`summary-row.md`、`newpage.md`、`contents.tsv`,摘要列是五欄、警示來源那一欄有值或寫「無」;兩支腳本不是從 `$JSC_HOME/current` 跑起來時,stderr 會有一行 `[WARN]` 點出實際路徑與應該用的路徑,`$JSC_HOME/assistant/usage-prev.tsv` 換成本輪的累計數,`$JSC_HOME/assistant/patrol.lock` 已經放掉。讓開的那一輪沒有任何寫入跡象。`stop` 之後心跳路徑不存在,`crontab -l` 找不到任何 `# jsc-assist:assistant` 條目。以上都不動別人的排程條目,條目數量前後相同。`status` 無寫入跡象,只有回報內容。四個操作都不動 `tasks/` 底下的檔案,也不動 worktree 與程式碼存取庫。排程的 log 一律在 `$JSC_HOME/assistant/schedule.log`,不落在任何存取庫 |
|
| 可驗證跡象 | `start` 之後 `$JSC_HOME/assistant/heartbeat` 存在,`ts` 是剛才那一輪的時間,`crontab -l` 找得到一筆帶 `# jsc-assist:assistant patrol` 的條目,而且只有一筆,帶 `# jsc-assist:assistant heartbeat` 的舊條目一筆都不剩;那一筆條目裡的 CLI 是絕對路徑,前面帶著 `JSC_GITEA_CONFIRM=yes` 與環境變數快照;install 印出的 `allow_rule=` 都是 `$JSC_HOME/current` 那一組確切路徑,沒有萬用字元,也沒有 `Write(...)`。`patrol` 跑完之後 wiki 的 `MONITOR_{HASH}` 只有三塊:基本資料一字未改、最新一輪換成本輪、摘要表最上面一列是本輪且總列數不超過 24,頁名的 `{HASH}` 是 40 碼大寫十六進位,雜湊來源那一列寫的是不含網域的短主機名;`CONTENTS` 存取庫裡的 `MONITOR_CONTENTS` 只有自己那一列變動,同一台機器從頭到尾只有一列,那一列第一欄是絕對網址連結、不是 `[[...]]`,第 2 欄是裸 HASH、40 碼大寫十六進位、不帶連結,別台機器的列一字不動,`$JSC_HOME/assistant/patrol/` 底下有本輪的 `latest.md`、`summary.md`、`summary-row.md`、`newpage.md`、`contents.tsv`,摘要列是五欄、警示來源那一欄有值或寫「無」;兩支腳本不是從 `$JSC_HOME/current` 跑起來時,stderr 會有一行 `[WARN]` 點出實際路徑與應該用的路徑,`$JSC_HOME/assistant/usage-prev.tsv` 換成本輪的累計數,`$JSC_HOME/assistant/patrol.lock` 已經放掉。讓開的那一輪沒有任何寫入跡象。`stop` 之後心跳路徑不存在,`crontab -l` 找不到任何 `# jsc-assist:assistant` 條目。以上都不動別人的排程條目,條目數量前後相同。`status` 無寫入跡象,只有回報內容。四個操作都不動 `tasks/` 底下的檔案,也不動 worktree 與程式碼存取庫。排程的 log 一律在 `$JSC_HOME/assistant/schedule.log`,不落在任何存取庫 |
|
||||||
|
|||||||
+34
-11
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: assistant
|
name: assistant
|
||||||
description: 'Start, inspect, patrol or stop the background assistant: jsc-hooks/hooks/heartbeat.sh owns the freshness verdict, tools/schedule.sh the system scheduler, tools/patrol.sh one round. The heartbeat is written by a completed round and by nothing else, so the schedule carries the patrol entry only, its period from the heartbeat TTL; start runs one round then installs that entry - absolute CLI path, environment snapshot, unattended write confirmation, which cron lacks - status prints heartbeat, schedule and task book read-only, stop removes the entry before clearing the heartbeat. One round reads four independent sources - skill and chain usage, version gaps and the restart gate, SDLC stage and work-package locks, and the heartbeat''s own report - then rewrites wiki MONITOR_{HASH} through jsc-gitea:wiki as three fixed blocks: basic data untouched, the latest round replaced whole, a 24-row summary table. A round that cannot record its result writes no heartbeat; one that starts while the previous holds the lock stands down. Use when someone starts, patrols or stops the assistant, or asks whether it runs and what is queued; not for environment health checks (jsc-cli:doctor), not for skill usage counts (jsc-log:stats).'
|
description: 'Start, inspect, patrol or stop the background assistant: jsc-hooks/hooks/heartbeat.sh owns the freshness verdict, tools/schedule.sh the system scheduler, tools/patrol.sh one round. The heartbeat is written by a completed round and by nothing else, so the schedule carries the patrol entry only, its period from the heartbeat TTL; start runs one round then installs that entry - absolute CLI path, environment snapshot, unattended write confirmation, which cron lacks - status prints heartbeat, schedule and task book read-only, stop removes the entry before clearing the heartbeat. One round reads four independent sources - skill and chain usage, version gaps and the restart gate, SDLC stage and work-package locks, and the heartbeat''s own report - then rewrites wiki MONITOR_{HASH} through jsc-gitea:wiki as three fixed blocks - basic data untouched, the latest round replaced whole, a 24-row summary table - and upserts its MONITOR_CONTENTS row through jsc-gitea/tools/wiki-contents.sh, which reads the separate CONTENTS wiki repo and links the monitor page by its absolute wiki-url. A round that cannot record its result writes no heartbeat; one that starts while the previous holds the lock stands down. Use when someone starts, patrols or stops the assistant, or asks whether it runs and what is queued; not for environment health checks (jsc-cli:doctor), not for skill usage counts (jsc-log:stats).'
|
||||||
---
|
---
|
||||||
|
|
||||||
# assistant — start, status, patrol, stop
|
# assistant — start, status, patrol, stop
|
||||||
@@ -25,10 +25,11 @@ Every tool below is addressed through `$JSC_HOME/current/{plugin}`, and `$JSC_HO
|
|||||||
| the system scheduler | `$JSC_HOME/current/jsc-assist/tools/schedule.sh` |
|
| the system scheduler | `$JSC_HOME/current/jsc-assist/tools/schedule.sh` |
|
||||||
| the heartbeat | `$JSC_HOME/current/jsc-hooks/hooks/heartbeat.sh` |
|
| the heartbeat | `$JSC_HOME/current/jsc-hooks/hooks/heartbeat.sh` |
|
||||||
| the wiki, through `jsc-gitea:wiki` | `$JSC_HOME/current/jsc-gitea/tools/gitea.sh` |
|
| the wiki, through `jsc-gitea:wiki` | `$JSC_HOME/current/jsc-gitea/tools/gitea.sh` |
|
||||||
|
| the `MONITOR_CONTENTS` row | `$JSC_HOME/current/jsc-gitea/tools/wiki-contents.sh` |
|
||||||
|
|
||||||
**A `Skill(...)` rule permits invoking that skill and nothing more.** Every Bash call inside it is still checked on its own, so `jsc-gitea:wiki` reaching the wiki depends on `gitea.sh` carrying its own rule — without it the round is refused locally, before any request leaves the machine, and the monitor page never gets written.
|
**A `Skill(...)` rule permits invoking that skill and nothing more.** Every Bash call inside it is still checked on its own, so `jsc-gitea:wiki` reaching the wiki depends on `gitea.sh` carrying its own rule, and the directory row depends on `wiki-contents.sh` carrying one too — without them the round is refused locally, before any request leaves the machine, and the page never gets written.
|
||||||
|
|
||||||
**Never build a tool path out of the base directory the CLI hands you in the skill prompt.** That directory points into the plugin cache and carries a version segment, and the permission gate allows exactly the four paths above and nothing else. A cache path is therefore refused silently: the round stops on a permission prompt nobody can answer, records nothing, writes no heartbeat, and the refusal looks exactly like a broken tool. Read the paths off this table every time — not off the prompt, not off a previous transcript, not off `crontab -l`.
|
**Never build a tool path out of the base directory the CLI hands you in the skill prompt.** That directory points into the plugin cache and carries a version segment, and the permission gate allows exactly the five paths above and nothing else. A cache path is therefore refused silently: the round stops on a permission prompt nobody can answer, records nothing, writes no heartbeat, and the refusal looks exactly like a broken tool. Read the paths off this table every time — not off the prompt, not off a previous transcript, not off `crontab -l`.
|
||||||
|
|
||||||
Both scripts check this for themselves: run from anywhere outside `$JSC_HOME/current`, they print a `[WARN]` line on stderr naming the path they were started from and the path they should have been started from, and then carry on. That line means this round is on the wrong path — quote it, fix the path, and do not treat the round's success as proof that the path was fine.
|
Both scripts check this for themselves: run from anywhere outside `$JSC_HOME/current`, they print a `[WARN]` line on stderr naming the path they were started from and the path they should have been started from, and then carry on. That line means this round is on the wrong path — quote it, fix the path, and do not treat the round's success as proof that the path was fine.
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ Four properties of that script matter enough to state here, because a report tha
|
|||||||
- **A written entry is not a running entry.** WSL does not start cron by default, and this is the machine's most likely state. Exit 1 from `install` means the entry is on disk and will never fire. Report that as a failure of the start, name `sudo service cron start`, and say it has to be run again after every WSL restart. Never soften exit 1 into "scheduling is set up".
|
- **A written entry is not a running entry.** WSL does not start cron by default, and this is the machine's most likely state. Exit 1 from `install` means the entry is on disk and will never fire. Report that as a failure of the start, name `sudo service cron start`, and say it has to be run again after every WSL restart. Never soften exit 1 into "scheduling is set up".
|
||||||
- **The log lives at `$JSC_HOME/assistant/schedule.log`**, deliberately outside every repository. Do not offer to move it into a project.
|
- **The log lives at `$JSC_HOME/assistant/schedule.log`**, deliberately outside every repository. Do not offer to move it into a project.
|
||||||
- **The entry runs with no human present.** The command is installed with `</dev/null`, so nothing it runs can block on input. A patrol round that stops to ask for a tool permission hangs that round, and the lock it holds stands the next round down until the lock ages out — which is why `patrol` asks nothing, of anybody, ever.
|
- **The entry runs with no human present.** The command is installed with `</dev/null`, so nothing it runs can block on input. A patrol round that stops to ask for a tool permission hangs that round, and the lock it holds stands the next round down until the lock ages out — which is why `patrol` asks nothing, of anybody, ever.
|
||||||
- **The entry carries its own environment.** cron gives it a short `PATH`, no settings file and no tty, so `schedule.sh` writes three things into the entry: the CLI resolved to an absolute path with `command -v`, a snapshot of the wiki variables taken at install time (`GITEA_HOST`, `GITEA_TOKEN`, `JSC_HOME`, `JSC_ASSISTANT_HEARTBEAT_TTL` and every set `JSC_WIKI_REPO*`), and `JSC_GITEA_CONFIRM=yes`, because the write confirmation only recognises a tty and an unattended round has nobody to confirm. Two consequences belong in every report: the entry holds a copy of the token, so the crontab file has to stay readable by its owner alone, and a changed variable only reaches the entry after another `install`. `install` prints the snapshotted names in `env_snapshot=` and masks the token in every entry it prints — never print an entry read from `crontab -l` yourself.
|
- **The entry carries its own environment.** cron gives it a short `PATH`, no settings file and no tty, so `schedule.sh` writes three things into the entry: the CLI resolved to an absolute path with `command -v`, a snapshot of the wiki variables taken at install time (`GITEA_HOST`, `GITEA_TOKEN`, `JSC_HOME`, `JSC_ASSISTANT_HEARTBEAT_TTL` and every set `JSC_WIKI_REPO*` — `JSC_WIKI_REPO`, `JSC_WIKI_REPO_MONITOR` for the monitor page and `JSC_WIKI_REPO_CONTENTS` for the directory page, which the script picks up from the environment rather than from a hardcoded list), and `JSC_GITEA_CONFIRM=yes`, because the write confirmation only recognises a tty and an unattended round has nobody to confirm. Two consequences belong in every report: the entry holds a copy of the token, so the crontab file has to stay readable by its owner alone, and a changed variable only reaches the entry after another `install`. `install` prints the snapshotted names in `env_snapshot=` and masks the token in every entry it prints — never print an entry read from `crontab -l` yourself.
|
||||||
- **`install` prints the permission rules that round needs.** One `allow_rule=` line each, with `*` in the path's version segment. Hand them to the operator verbatim: an unattended round that hits a permission prompt hangs until the lock ages out, and nobody is there to approve it. `Write(...)` rules do nothing for file writes — only `Edit(...)` is recognised — so never turn a printed `Edit` rule into a `Write` one.
|
- **`install` prints the permission rules that round needs.** One `allow_rule=` line each, with `*` in the path's version segment. Hand them to the operator verbatim: an unattended round that hits a permission prompt hangs until the lock ages out, and nobody is there to approve it. `Write(...)` rules do nothing for file writes — only `Edit(...)` is recognised — so never turn a printed `Edit` rule into a `Write` one.
|
||||||
|
|
||||||
### What a fresh heartbeat actually proves
|
### What a fresh heartbeat actually proves
|
||||||
@@ -133,7 +134,7 @@ The six limits in `AGENTS.md`「助理的界線」 hold for all four operations.
|
|||||||
|
|
||||||
- **This skill never judges a gate.** It maintains the heartbeat and prints what the heartbeat says. Whether a stale heartbeat blocks a skill call is decided by a hook, synchronously and offline; nothing in this skill blocks or waves through anything. 界線 2.
|
- **This skill never judges a gate.** It maintains the heartbeat and prints what the heartbeat says. Whether a stale heartbeat blocks a skill call is decided by a hook, synchronously and offline; nothing in this skill blocks or waves through anything. 界線 2.
|
||||||
- **A patrol round asks nothing.** It runs from cron with nobody present, so there is no one to answer and a question hangs the round. Every branch in the patrol steps below resolves without a question: a missing source is recorded as missing, an ambiguous result is recorded verbatim, and a round that cannot proceed aborts and reports. Never call `jsc-ask:ask` from `patrol`. 界線 1.
|
- **A patrol round asks nothing.** It runs from cron with nobody present, so there is no one to answer and a question hangs the round. Every branch in the patrol steps below resolves without a question: a missing source is recorded as missing, an ambiguous result is recorded verbatim, and a round that cannot proceed aborts and reports. Never call `jsc-ask:ask` from `patrol`. 界線 1.
|
||||||
- **A patrol round rewrites the monitor page as three fixed blocks.** Read the old page back first; keep 本頁基本資料 as it stands, replace 最新一輪 whole, put this round's row on top of the summary table and cut it to 24; then put the whole page. The contents page gets its own row updated and nobody else's. A page that could not be read is a page that does not get written — the summary table only survives if the old one came back. 界線 4.
|
- **A patrol round rewrites the monitor page as three fixed blocks.** Read the old page back first; keep 本頁基本資料 as it stands, replace 最新一輪 whole, put this round's row on top of the summary table and cut it to 24; then put the whole page. The directory page is a separate write in a separate wiki repo, and `wiki-contents.sh` does it: this machine's row is updated and nobody else's. A page that could not be read is a page that does not get written — the summary table only survives if the old one came back. 界線 4.
|
||||||
- **A patrol round reports; it never acts on what it found.** The 待人處理 rows name an entry point for a human. The patrol does not run that entry point, does not fix a hook, does not update a plugin and does not touch a repository. 界線 3 and 界線 6.
|
- **A patrol round reports; it never acts on what it found.** The 待人處理 rows name an entry point for a human. The patrol does not run that entry point, does not fix a hook, does not update a plugin and does not touch a repository. 界線 3 and 界線 6.
|
||||||
- **`stop` clearing the heartbeat and removing the schedule is not a breach of 界線 5「不刪除狀態檔」.** That limit protects state that records work — the task book, worktrees, wiki pages — from a background process nobody is watching. The heartbeat records one fact only, "the last patrol round finished", and the schedule entry is what keeps rounds running, so a `stop` that leaves either behind leaves a lie behind. Clearing both is the whole job of `stop`, and they are the only deletions any operation here performs, both of them entries this skill installed itself. `stop` touches nothing under `tasks/`, nobody else's cron entry, no worktree and no wiki page. Do not "restore" this limit later by taking either removal out of `stop`.
|
- **`stop` clearing the heartbeat and removing the schedule is not a breach of 界線 5「不刪除狀態檔」.** That limit protects state that records work — the task book, worktrees, wiki pages — from a background process nobody is watching. The heartbeat records one fact only, "the last patrol round finished", and the schedule entry is what keeps rounds running, so a `stop` that leaves either behind leaves a lie behind. Clearing both is the whole job of `stop`, and they are the only deletions any operation here performs, both of them entries this skill installed itself. `stop` touches nothing under `tasks/`, nobody else's cron entry, no worktree and no wiki page. Do not "restore" this limit later by taking either removal out of `stop`.
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ That property holds only while nothing fakes a heartbeat. **`write` is called by
|
|||||||
|
|
||||||
`start` proves the loop works before it schedules it: one patrol round first, then the scheduled entry. It installs no daemon and writes no bare heartbeat.
|
`start` proves the loop works before it schedules it: one patrol round first, then the scheduled entry. It installs no daemon and writes no bare heartbeat.
|
||||||
|
|
||||||
1. **Run one patrol round.** Follow every step of the `patrol` operation below, start to finish. This is what writes the first heartbeat — there is no shortcut past it, because a heartbeat that no round produced is exactly the lie this design removes. When that round ends without a heartbeat for any reason (`collect` exit 4, 5 or 6, an empty `hash=`, a failed wiki write, or `finish` exit 2, 4 or 5), the start has failed: report the round's outcome and the code, do not run step 2, and do not claim a started assistant. A round that completed with failed items (`collect` exit 1 or 3) is still a completed round — carry on to step 2 and name the failures in the closing report. Completion condition: `patrol.sh finish` exited 0, or the failure report naming the step and the code has been printed and no start was claimed.
|
1. **Run one patrol round.** Follow every step of the `patrol` operation below, start to finish. This is what writes the first heartbeat — there is no shortcut past it, because a heartbeat that no round produced is exactly the lie this design removes. When that round ends without a heartbeat for any reason (`collect` exit 4, 5 or 6, an empty `hash=`, a failed write of the monitor page, a directory-row failure other than exit 3, or `finish` exit 2, 4 or 5), the start has failed: report the round's outcome and the code, do not run step 2, and do not claim a started assistant. A round that completed with failed items (`collect` exit 1 or 3) is still a completed round — carry on to step 2 and name the failures in the closing report. Completion condition: `patrol.sh finish` exited 0, or the failure report naming the step and the code has been printed and no start was claimed.
|
||||||
|
|
||||||
2. **Confirm the heartbeat.** Run `$JSC_HOME/current/jsc-hooks/hooks/heartbeat.sh report` and read its `state=`, `ts=`, `ttl=`, `pid=`, `cli=`, `session=` and `file=` fields. `state=fresh` is the expected result. Any other state right after a successful round means something rewrote or removed the file in between: report the state, the path and that the heartbeat did not survive its own write, and do not claim a started assistant. Completion condition: the report line was read and either `state=fresh` was recorded with its seven fields, or the mismatch was reported.
|
2. **Confirm the heartbeat.** Run `$JSC_HOME/current/jsc-hooks/hooks/heartbeat.sh report` and read its `state=`, `ts=`, `ttl=`, `pid=`, `cli=`, `session=` and `file=` fields. `state=fresh` is the expected result. Any other state right after a successful round means something rewrote or removed the file in between: report the state, the path and that the heartbeat did not survive its own write, and do not claim a started assistant. Completion condition: the report line was read and either `state=fresh` was recorded with its seven fields, or the mismatch was reported.
|
||||||
|
|
||||||
@@ -171,9 +172,11 @@ One round: read four sources, record the result, then beat. Everything before th
|
|||||||
|
|
||||||
1. **Collect.** Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh collect --trigger 排程` (use `--trigger 手動` when a person asked for this round). Judge the exit code by the patrol.sh table. Exit 4 stands the round down — report the holder and its age from the printed `lock=busy` line, and stop; write no page and no heartbeat. Exit 5 and 6 stop the round the same way, with the code and the stderr text. Exit 0, 1 and 3 all carry on to step 2. Record `round=`, `lock_broken=`, `hash=`, `page=`, `verdict=`, `failed_sources=`, `warn_sources=`, `pending=`, every `item=` line, and the file paths `latest_file=`, `summary_file=`, `summary_row_file=`, `newpage_file=` and `contents_file=`. Completion condition: the round id, the page name and the five file paths are recorded, or the stand-down or the failure was reported and the round stopped.
|
1. **Collect.** Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh collect --trigger 排程` (use `--trigger 手動` when a person asked for this round). Judge the exit code by the patrol.sh table. Exit 4 stands the round down — report the holder and its age from the printed `lock=busy` line, and stop; write no page and no heartbeat. Exit 5 and 6 stop the round the same way, with the code and the stderr text. Exit 0, 1 and 3 all carry on to step 2. Record `round=`, `lock_broken=`, `hash=`, `page=`, `verdict=`, `failed_sources=`, `warn_sources=`, `pending=`, every `item=` line, and the file paths `latest_file=`, `summary_file=`, `summary_row_file=`, `newpage_file=` and `contents_file=`. Completion condition: the round id, the page name and the five file paths are recorded, or the stand-down or the failure was reported and the round stopped.
|
||||||
|
|
||||||
2. **Check the page name.** An empty `hash=` means `jsc-gitea/tools/hash-id` could not be found or could not run, so there is no page to write to and nothing can be recorded. Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report that the round found its results but has nowhere to put them, name `jsc-gitea` as missing, and stop. Never invent a page name — a hand-made name lands the content on a page nobody reads. Completion condition: `page=` holds a `MONITOR_{HASH}` name, or the abort ran and the round was reported as unrecorded.
|
2. **Check the page name.** An empty `hash=` means `jsc-gitea/tools/hash-id` could not be found or could not run, so there is no page to write to and nothing can be recorded. Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report that the round found its results but has nowhere to put them, name `jsc-gitea` as missing, and stop.
|
||||||
|
|
||||||
3. **Rebuild `MONITOR_{HASH}` from its three blocks.** Hand it to `jsc-gitea:wiki` with page type `MONITOR`: read the page back first, then build the new body out of what came back plus this round's files, in this order and with nothing else on the page:
|
**Never invent a page name, and never work the hash out by hand** — a hand-made name lands the content on a page nobody reads. `hash-id` hashes `{host}/{user}` and prints the full 40-character uppercase hexadecimal SHA-1: no truncation to 8, no `H` prefix, and an empty input exits 2 rather than hashing the empty string. So `page=` is either `MONITOR_` plus that 40-character string, exactly as the script printed it, or nothing at all. Completion condition: `page=` holds a `MONITOR_{HASH}` name taken verbatim from `collect`, or the abort ran and the round was reported as unrecorded.
|
||||||
|
|
||||||
|
3. **Rebuild `MONITOR_{HASH}` from its three blocks.** Hand it to `jsc-gitea:wiki` with page type `MONITOR`, so the repo is the one `gitea.sh wiki-repo MONITOR` resolves — `JSC_WIKI_REPO_MONITOR`, then `JSC_WIKI_REPO`, then exit 3. This is the content page and it stays in that repo; only the directory page of step 4 moved to the `CONTENTS` repo, and neither chain ever falls back to the other. Read the page back first, then build the new body out of what came back plus this round's files, in this order and with nothing else on the page:
|
||||||
|
|
||||||
| Block | Where it comes from |
|
| Block | Where it comes from |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -181,13 +184,33 @@ One round: read four sources, record the result, then beat. Everything before th
|
|||||||
| 最新一輪 | the whole content of `latest_file`, replacing the old block entirely |
|
| 最新一輪 | the whole content of `latest_file`, replacing the old block entirely |
|
||||||
| 近 24 輪摘要 | `summary_file`, which already holds the heading, the five-column table header (`巡檢時間`、`本輪判定`、`四項成敗`、`待人處理`、`警示來源`) and this round's row; then the old table's data rows in their old order underneath, cut so the table holds at most 24 rows |
|
| 近 24 輪摘要 | `summary_file`, which already holds the heading, the five-column table header (`巡檢時間`、`本輪判定`、`四項成敗`、`待人處理`、`警示來源`) and this round's row; then the old table's data rows in their old order underneath, cut so the table holds at most 24 rows |
|
||||||
|
|
||||||
Put the whole page. An old-format page — per-round sections stacked up, no summary table — has no rows to carry over: keep its `本頁基本資料` block, drop the stacked sections, let the table start with this round's row, and say in the report that the page was converted. Only exit 4 from the read permits creating the page instead, and then the body is the whole content of `newpage_file`, which already carries all three blocks. Exit 7 and exit 8 mean the old content is unknown: create nothing, write nothing — rebuilding a page from an unknown original throws the summary table away. On any write failure — including exit 3 with no wiki repo configured for `MONITOR`, which the patrol cannot ask about — run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop. **No record, no heartbeat.** Completion condition: the put or the create returned success and the page holds exactly three blocks with the summary table at 24 rows or fewer and this round's row on top, or the abort ran and the round was reported as unrecorded with its exit code.
|
Put the whole page. An old-format page — per-round sections stacked up, no summary table — has no rows to carry over: keep its `本頁基本資料` block, drop the stacked sections, let the table start with this round's row, and say in the report that the page was converted. Only exit 4 from the read permits creating the page instead, and then the body is the whole content of `newpage_file`, which already carries all three blocks. Exit 7 and exit 8 mean the old content is unknown: create nothing, write nothing — rebuilding a page from an unknown original throws the summary table away. On any write failure — including exit 3 with no wiki repo configured for `MONITOR`, which the patrol cannot ask about — run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop. **No record, no heartbeat**, and that verdict belongs to this step alone: the round's result lives on this page, so a repo this step cannot resolve leaves the round with nowhere to be recorded. Step 4 is judged on its own terms. Completion condition: the put or the create returned success and the page holds exactly three blocks with the summary table at 24 rows or fewer and this round's row on top, or the abort ran and the round was reported as unrecorded with its exit code.
|
||||||
|
|
||||||
4. **Update this machine's row in `MONITOR_CONTENTS`.** Take the `row=` line from `contents_file` — it is already the finished table row. Hand it to `jsc-gitea:wiki`: read the whole page, match the row whose 主機 and 帳號 columns both equal this round's `host=` and `user=`, overwrite that row's remaining columns, and put the whole page back. No matching row means append one. Never rebuild this page the way the content page is rebuilt, and never touch another machine's row — every other row here belongs to a machine that is not this one, and one careless whole-page write deletes their records. On failure, run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop. Completion condition: exactly one row carries this machine's 主機 and 帳號 values, every other row is byte-identical to what was read, and the put returned success.
|
4. **Update this machine's row in `MONITOR_CONTENTS`, through `jsc-gitea/tools/wiki-contents.sh`.** That page is a directory every machine writes to, and it lives in the repo `gitea.sh wiki-repo CONTENTS` resolves — `JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3, and never a fallback to `JSC_WIKI_REPO_MONITOR`. The script owns the read-match-write of one row, so never read this page and rebuild it by hand, never write it through `jsc-gitea:wiki`, and never rebuild it the way step 3 rebuilds the content page — every other row here belongs to a machine that is not this one, and one careless whole-page write deletes their records.
|
||||||
|
|
||||||
|
**Finish the row first.** The `row=` line in `contents_file` carries the placeholder `{監控頁絕對網址}` in its first cell, because the directory page and the monitor page now sit in two different wikis: `[[MONITOR_{HASH}]]` resolves only inside one wiki and would dead-link from here while still looking like a link, and the absolute URL cannot be known until step 3 has actually put the page. Run `$JSC_HOME/current/jsc-gitea/tools/gitea.sh wiki-url {the MONITOR repo step 3 resolved} MONITOR_{HASH}`, replace the placeholder with what it prints, and write the finished row to a file. Exit 4 there means step 3's write has not landed — go back to step 3 rather than writing a row. Exit 5 means the page carries no `html_url`: report it and never assemble a URL by hand. Exit 7 or 8: report the code and take the abort row below. **Any other non-zero exit takes the same abort row**, a missing argument included — a URL that never arrived would otherwise leave the link cell holding the raw placeholder, and the row would still be written.
|
||||||
|
|
||||||
|
Then run, with the template as the fifth argument every time:
|
||||||
|
|
||||||
|
`$JSC_HOME/current/jsc-gitea/tools/wiki-contents.sh upsert MONITOR 2 "{HASH}" {row file} $JSC_HOME/current/jsc-assist/templates/monitor-contents.md`
|
||||||
|
|
||||||
|
**The key is column 2, the bare `HASH` cell** — the 40-character string `collect` printed as `hash=`, copied verbatim, with no link, no brackets and no URL around it. The script compares the whole cell text, so column 1 cannot be the key: that cell holds `GITEA_HOST` and the wiki's encoding of the page name, so a changed host, a `JSC_WIKI_REPO_MONITOR` pointed at another repo, or a different URL encoding changes the text and stops it matching. This page is written once every round, so from that moment on every round appends one more row for this same machine and the old row is never updated again. The bare `HASH` depends on `{host}/{user}` alone, which none of those three touch. Column 1's link stays in the row for people to click, and never for matching. A key typed by hand matches nothing either, and appends the same duplicate row.
|
||||||
|
|
||||||
|
| Exit | Do |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | The row is in place. The script prints `updated` or `added` plus the page it wrote — carry that word into the report, and carry on to step 5 |
|
||||||
|
| 1 | The write failed, or the directory page holds no markdown table. Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh abort --round {round}`, report the code, and stop |
|
||||||
|
| 2 | An argument was rejected and nothing was written. A template path that does not exist lands here too, and means the plugin installation is incomplete. Correct the call and run it once more; report a second exit 2 as a defect in this skill, then abort and stop |
|
||||||
|
| 3 | No `CONTENTS` wiki repo is configured. **This one does not stop the round.** Carry on to step 5 and write the heartbeat: the round's result is already on `MONITOR_{HASH}`, and that is exactly what a heartbeat stands for. Report the directory row as not updated, name `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set, and add that to the 待人處理 rows. Never abort a recorded round over the directory page — a missing directory row loses one index line, an aborted round loses the whole round, and the patrol cannot ask anybody for the missing setting |
|
||||||
|
| 4 | The page is absent and no template reached the script. The call above always passes the template as its fifth argument, so this code cannot come out of it — getting it means that argument was dropped, so restore it and run the call once more. A template path that does not exist is rejected as exit 2, never as 4 |
|
||||||
|
| 7 | The token is invalid or lacks permission, so the other machines' rows are unknown. The script wrote nothing, which is what keeps those rows alive. Abort, report the key problem, and stop |
|
||||||
|
| 8 | Some other API failure. Abort, report the status, and stop |
|
||||||
|
|
||||||
|
Completion condition: the script exited 0 and exactly one row carries this machine's bare `HASH` in column 2 with this round's values, or exit 3 was reported as an unwritten directory row and the round carried on, or one of the other non-zero codes — `wiki-url`'s included — was reported after the abort ran.
|
||||||
|
|
||||||
5. **Write the heartbeat.** Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh finish --round {round}`. This is the last step for a reason: it is the only thing that turns a fresh heartbeat into a true statement. Judge the exit code by the patrol.sh table — 2, 4 and 5 all mean the round is recorded but unproven, and each has its own report line there. Completion condition: `finish` exited 0, or the failure was reported as "recorded but no heartbeat" with its code.
|
5. **Write the heartbeat.** Run `$JSC_HOME/current/jsc-assist/tools/patrol.sh finish --round {round}`. This is the last step for a reason: it is the only thing that turns a fresh heartbeat into a true statement. Judge the exit code by the patrol.sh table — 2, 4 and 5 all mean the round is recorded but unproven, and each has its own report line there. Completion condition: `finish` exited 0, or the failure was reported as "recorded but no heartbeat" with its code.
|
||||||
|
|
||||||
6. **Report the round.** Print the round verdict and, when it is `警示`, the `warn_sources=` text that says why — a round can read all four sources and still come out `警示`, and that column is the only place the reason appears; then one line per item with its `status=` and, for a failure, its `note=`; the monitor page name and the contents row that was written; whether the heartbeat was written; and, when `lock_broken=1`, that the previous round's lock was taken over because it had aged past the TTL. Close with the 待人處理 rows from the latest-round block, verbatim, and nothing else — the patrol names an entry point and stops there. Completion condition: all four items appear in the report, the heartbeat outcome is stated as written or not written, and no suggestion in 待人處理 was acted on.
|
6. **Report the round.** Print the round verdict and, when it is `警示`, the `warn_sources=` text that says why — a round can read all four sources and still come out `警示`, and that column is the only place the reason appears; then one line per item with its `status=` and, for a failure, its `note=`; the monitor page name, and the directory row as `updated`, `added`, or not written with the exit code and the reason; whether the heartbeat was written; and, when `lock_broken=1`, that the previous round's lock was taken over because it had aged past the TTL. Close with the 待人處理 rows from the latest-round block, verbatim, and nothing else — the patrol names an entry point and stops there. Completion condition: all four items appear in the report, the heartbeat outcome is stated as written or not written, and no suggestion in 待人處理 was acted on.
|
||||||
|
|
||||||
## status
|
## status
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,54 @@
|
|||||||
# 助理巡檢目錄
|
# 助理巡檢目錄
|
||||||
|
|
||||||
> 由 `jsc-assist` 維護。這是目錄頁 `MONITOR_CONTENTS`。
|
> 由 `jsc-assist` 維護。這是目錄頁 `MONITOR_CONTENTS`,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和監控頁不同庫。
|
||||||
> 一列代表一台機器。雜湊來源是 `{主機名}/{登入帳號}`,所以一台機器一列、一頁,換一支 CLI 不另開列。
|
> 一列代表一台機器。雜湊來源是 `{主機名}/{登入帳號}`,主機名取短的那一段,所以一台機器一列、一頁,換一支 CLI 不另開列。
|
||||||
> `MONITOR_{HASH}` 的 `{HASH}` 交給 `jsc-gitea/tools/hash-id` 產生,雜湊來源見 `jsc-meta` 的 `references/guidelines.md`「Wiki 頁命名總表」。
|
> `MONITOR_{HASH}` 的 `{HASH}` 執行 `jsc-gitea/tools/hash-id {主機名}/{登入帳號}` 取得,原樣採用它印出的完整 40 碼大寫十六進位,不截短、不加前綴(共用 wiki hash 規則,演算法見 `jsc-meta` 的 `references/guidelines.md`)。
|
||||||
|
>
|
||||||
|
> 連結寫法:監控頁那一欄放 `jsc-gitea/tools/gitea.sh wiki-url` 印出的絕對網址,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。
|
||||||
|
>
|
||||||
|
> 比對鍵:第 2 欄的裸 HASH,純文字,不帶連結、不帶網址。連結那一欄是給人看的,不當鍵。
|
||||||
|
|
||||||
| 監控頁 | 主機 | 帳號 | 心跳 | 最後巡檢 | 待辦筆數 | 連續失敗項 |
|
| 監控頁 | HASH | 主機 | 帳號 | 心跳 | 最後巡檢 | 待辦筆數 | 連續失敗項 |
|
||||||
| --- | --- | --- | --- | --- | ---: | ---: |
|
| --- | --- | --- | --- | --- | --- | ---: | ---: |
|
||||||
| [[MONITOR_{HASH}]] | {主機名} | {登入帳號} | {新鮮、過期、不存在 三選一} | {yyyy-MM-dd HH:mm} | {n} | {n} |
|
| [MONITOR_{HASH}]({wiki-url 印出的絕對網址}) | {HASH} | {主機名} | {登入帳號} | {新鮮、過期、不存在 三選一} | {yyyy-MM-dd HH:mm} | {n} | {n} |
|
||||||
|
|
||||||
## 欄位說明
|
## 欄位說明
|
||||||
|
|
||||||
| 欄位 | 內容 | 為什麼留這一欄 |
|
| 欄位 | 內容 | 為什麼留這一欄 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 監控頁 | 指向 `MONITOR_{HASH}` 的同 wiki 連結 | 少了連結就要人自己算雜湊才翻得到內容頁 |
|
| 監控頁 | 指向 `MONITOR_{HASH}` 的絕對網址連結,給人點的,不當比對鍵 | 少了連結就要人自己算雜湊才翻得到內容頁;跨存取庫只有絕對網址連得過去 |
|
||||||
| 主機 | 這台機器的主機名,與雜湊第一段相同 | 比對用的兩欄之一,決定要更新哪一列 |
|
| HASH | `hash-id` 印出的完整 40 碼大寫十六進位,純文字,不加連結、不加網址,也是 upsert 的比對鍵 | 這一格只跟 `{主機名}/{登入帳號}` 有關,換主機位址、換存取庫、換一種網址編碼都不會變。拿含網址的連結當鍵才會對不上,然後同一台機器每輪多附一列 |
|
||||||
| 帳號 | 助理執行時的登入帳號,與雜湊第二段相同 | 比對用的兩欄之一。同一台機器換帳號就是另一個巡檢對象 |
|
| 主機 | 這台機器的短主機名,與雜湊第一段相同 | 一眼看出這一列是哪一台機器 |
|
||||||
|
| 帳號 | 助理執行時的登入帳號,與雜湊第二段相同 | 同一台機器換帳號就是另一個巡檢對象,雜湊也會不同 |
|
||||||
| 心跳 | 巡檢當下(本輪寫入前)的心跳判定,判準只看 `ts` 距現在有沒有超過門檻,預設 300 秒 | 一眼看出這台機器上一輪巡檢有沒有跑完,不必逐頁翻 |
|
| 心跳 | 巡檢當下(本輪寫入前)的心跳判定,判準只看 `ts` 距現在有沒有超過門檻,預設 300 秒 | 一眼看出這台機器上一輪巡檢有沒有跑完,不必逐頁翻 |
|
||||||
| 最後巡檢 | 該頁最新一節的時間戳 | 心跳由巡檢寫,兩欄理當一致;差很多就代表有一輪寫了心跳卻沒寫頁,那是缺陷 |
|
| 最後巡檢 | 該頁最新一輪的時間戳 | 心跳由巡檢寫,兩欄理當一致;差很多就代表有一輪寫了心跳卻沒寫頁,那是缺陷 |
|
||||||
| 待辦筆數 | 待辦簿現有筆數 | 心跳新鮮而筆數為 0,代表助理空轉,沒有東西可跑 |
|
| 待辦筆數 | 待辦簿現有筆數 | 心跳新鮮而筆數為 0,代表助理空轉,沒有東西可跑 |
|
||||||
| 連續失敗項 | 該頁最新一節裡 `fail_count` 大於 0 的筆數 | 待辦簿的項目失敗不會自動暫停,每輪都重試。這一欄讓壞掉的項目在目錄頁就現形 |
|
| 連續失敗項 | 待辦簿裡 `fail_count` 大於 0 的筆數 | 待辦簿的項目失敗不會自動暫停,每輪都重試。這一欄讓壞掉的項目在目錄頁就現形 |
|
||||||
|
|
||||||
## 寫入規則
|
## 寫入規則
|
||||||
|
|
||||||
這一頁是共用目錄,別台機器的列一律原樣保留。
|
這一頁是共用目錄,別台機器的列一律原樣保留。寫入一律用 `jsc-gitea/tools/wiki-contents.sh upsert`,不手工改頁。
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A[整頁讀回來] --> B{讀得到舊內容}
|
A[監控頁已經寫成] --> B[gitea.sh wiki-url 取監控頁絕對網址]
|
||||||
B -- 否 --> C[中止:不新增列,也不寫入]
|
B --> C[組出本機那一列,網址換掉佔位]
|
||||||
B -- 是 --> D{主機與帳號兩欄都對得上}
|
C --> D[wiki-contents.sh upsert MONITOR 第 2 欄的裸 HASH 當鍵]
|
||||||
D -- 是 --> E[只覆寫那一列的其餘欄位]
|
D --> E{舊頁讀得回來}
|
||||||
D -- 否 --> F[新增一列]
|
E -- 是 --> F{HASH 欄對得上}
|
||||||
E --> G[其他機器的列原樣送回]
|
F -- 是 --> G[取代那一列]
|
||||||
F --> G
|
F -- 否 --> H[附加一列]
|
||||||
|
E -- 頁不存在 --> I[用範本建頁,再附加一列]
|
||||||
|
E -- 金鑰失效或 API 失敗 --> J[中止:不建頁、不寫入]
|
||||||
|
G --> K[整頁寫回,別台機器的列原樣送回]
|
||||||
|
H --> K
|
||||||
|
I --> K
|
||||||
```
|
```
|
||||||
|
|
||||||
- 先整頁讀回來,再比對主機與帳號兩欄。
|
- 存取庫走 `gitea.sh wiki-repo CONTENTS`:先 `JSC_WIKI_REPO_CONTENTS`,再 `JSC_WIKI_REPO`,都沒設就結束碼 3,不退回監控頁那一支變數。
|
||||||
- 兩欄都相同就更新那一列,其餘欄位覆寫成本次巡檢結果。
|
- 比對鍵是第 2 欄的裸 HASH,原樣比對整格文字。鍵取 `collect` 印的 `hash=`,自己重打會對不上,結果是同一台機器多出第二列。
|
||||||
- 找不到兩欄都相同的列,才新增一列。
|
- 第一欄的連結不當鍵:那一格含 `GITEA_HOST` 與頁名的網址編碼,主機位址改掉、`JSC_WIKI_REPO_MONITOR` 換了存取庫、或 Gitea 的網址編碼有差,整格文字就變了,鍵跟著對不上。這一頁每 15 分鐘寫一次,對不上的那一刻起每輪多附一列,舊列再也不會更新。
|
||||||
- 只動自己那一列,別台機器的列一個字都不改。
|
- 找得到相同的 HASH 就更新那一列,找不到才附加一列。
|
||||||
- 禁止整頁覆蓋。整頁覆蓋等於刪掉別台機器的紀錄。
|
- 只動自己那一列,別台機器的列一個字都不改。禁止整頁覆蓋——整頁覆蓋等於刪掉別台機器的紀錄。
|
||||||
- 讀不到舊內容就中止,不新增列,也不寫入。
|
- 只有「頁不存在」才准用範本建頁。金鑰失效或 API 失敗一律中止:那兩種情況舊內容是未知的,拿範本蓋上去就是把活著的紀錄整份刪掉。
|
||||||
- 內容頁 `MONITOR_{HASH}` 的寫入語意相反,那頁只附加一節、不覆寫,兩者不要混用。
|
- 內容頁 `MONITOR_{HASH}` 的寫入語意不同:那頁固定三塊,最新一輪整塊換掉,摘要表一輪一列、最新的在最上面、超過 24 列丟最舊的。兩者不要混用。
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。
|
> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。
|
||||||
> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。
|
> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。
|
||||||
> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。
|
> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。
|
||||||
> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,別台機器的列一個字都不動。
|
> 目錄頁 `MONITOR_CONTENTS` 在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和這頁不同庫;那一頁只更新自己那一列,別台機器的列一個字都不動。
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -13,7 +13,7 @@ flowchart LR
|
|||||||
C --> D[換掉最新一輪那一塊]
|
C --> D[換掉最新一輪那一塊]
|
||||||
D --> E[本輪摘要列插到表格最上面,截到 24 列]
|
D --> E[本輪摘要列插到表格最上面,截到 24 列]
|
||||||
E --> F[整頁寫回]
|
E --> F[整頁寫回]
|
||||||
F --> G[更新 MONITOR_CONTENTS 自己那一列]
|
F --> G[wiki-contents.sh upsert 更新目錄頁自己那一列]
|
||||||
G --> H[最後才寫心跳]
|
G --> H[最後才寫心跳]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -135,5 +135,6 @@ flowchart LR
|
|||||||
- 本輪的摘要列插到摘要表最上面,舊的列往下移,超過 24 列就丟掉最舊的那一列。
|
- 本輪的摘要列插到摘要表最上面,舊的列往下移,超過 24 列就丟掉最舊的那一列。
|
||||||
- 三塊重組成一整頁再整頁寫回。除了這三塊,頁上沒有別的東西。
|
- 三塊重組成一整頁再整頁寫回。除了這三塊,頁上沒有別的東西。
|
||||||
- 舊格式的頁(一輪一節疊起來的那種)第一次重組時,基本資料留著,那些節收掉,摘要表從本輪這一列開始,並在回報裡說明。
|
- 舊格式的頁(一輪一節疊起來的那種)第一次重組時,基本資料留著,那些節收掉,摘要表從本輪這一列開始,並在回報裡說明。
|
||||||
- 整頁寫成之後,才回頭更新 `MONITOR_CONTENTS` 自己那一列,別台機器的列一個字都不動。
|
- 整頁寫成之後,才回頭更新目錄頁自己那一列,寫入交給 `jsc-gitea/tools/wiki-contents.sh upsert`,別台機器的列一個字都不動。目錄頁那一欄的連結用絕對網址,兩頁不同庫,`[[...]]` 連不過去。
|
||||||
- 兩頁都寫成之後,才寫這一輪的心跳。任一頁沒寫成就不寫心跳,讓它過期。
|
- 這一頁沒寫成就不寫心跳,讓它過期。心跳代表的是「這一輪的結果記在這一頁上了」。
|
||||||
|
- 目錄頁只是索引。目錄頁的存取庫沒設定(結束碼 3)時照樣寫心跳,並把那一筆列進待人處理;其餘寫入失敗才不寫心跳。
|
||||||
|
|||||||
+37
-5
@@ -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 的輸出 ---
|
# --- collect 的輸出 ---
|
||||||
#
|
#
|
||||||
# stdout 是 key=value,一行一個鍵,供呼叫端逐行取值。監控頁要用的 markdown 不印在
|
# stdout 是 key=value,一行一個鍵,供呼叫端逐行取值。監控頁要用的 markdown 不印在
|
||||||
@@ -95,7 +111,9 @@
|
|||||||
# summary_file= 「近 24 輪摘要」那一塊,表格裡先放本輪這一列,舊頁的資料列接在下面
|
# summary_file= 「近 24 輪摘要」那一塊,表格裡先放本輪這一列,舊頁的資料列接在下面
|
||||||
# summary_row_file= 只有本輪那一列,方便直接插到既有表格最上面
|
# summary_row_file= 只有本輪那一列,方便直接插到既有表格最上面
|
||||||
# newpage_file= MONITOR_{HASH} 不存在時要建的整頁內容,三塊都已經排好
|
# newpage_file= MONITOR_{HASH} 不存在時要建的整頁內容,三塊都已經排好
|
||||||
# contents_file= MONITOR_CONTENTS 那一列的欄位值
|
# contents_file= MONITOR_CONTENTS 那一列的欄位值。row= 就是整列 markdown,第一欄是連結,
|
||||||
|
# 網址的位置留 {監控頁絕對網址} 佔位,由呼叫端換掉,理由見下一段;第二欄是
|
||||||
|
# 裸 HASH,upsert 拿那一欄當鍵,理由見再下一段
|
||||||
#
|
#
|
||||||
# 環境變數:
|
# 環境變數:
|
||||||
# JSC_HOME 助理狀態檔的根目錄,預設 ~/.jsc
|
# JSC_HOME 助理狀態檔的根目錄,預設 ~/.jsc
|
||||||
@@ -665,14 +683,15 @@ compose() {
|
|||||||
printf '> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。\n'
|
printf '> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。\n'
|
||||||
printf '> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。\n'
|
printf '> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。\n'
|
||||||
printf '> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。\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 '```mermaid\nflowchart LR\n'
|
||||||
printf ' A[巡檢一輪] --> B[收攏四項結果]\n'
|
printf ' A[巡檢一輪] --> B[收攏四項結果]\n'
|
||||||
printf ' B --> C[讀回舊頁]\n'
|
printf ' B --> C[讀回舊頁]\n'
|
||||||
printf ' C --> D[換掉最新一輪那一塊]\n'
|
printf ' C --> D[換掉最新一輪那一塊]\n'
|
||||||
printf ' D --> E[本輪摘要列插到表格最上面,截到 24 列]\n'
|
printf ' D --> E[本輪摘要列插到表格最上面,截到 24 列]\n'
|
||||||
printf ' E --> F[整頁寫回]\n'
|
printf ' E --> F[整頁寫回]\n'
|
||||||
printf ' F --> G[更新 MONITOR_CONTENTS 自己那一列]\n'
|
printf ' F --> G[wiki-contents.sh upsert 更新目錄頁自己那一列]\n'
|
||||||
printf ' G --> H[最後才寫心跳]\n'
|
printf ' G --> H[最後才寫心跳]\n'
|
||||||
printf '```\n\n'
|
printf '```\n\n'
|
||||||
printf '## 本頁基本資料\n\n'
|
printf '## 本頁基本資料\n\n'
|
||||||
@@ -695,8 +714,15 @@ compose() {
|
|||||||
printf 'last_patrol=%s\n' "$AT"
|
printf 'last_patrol=%s\n' "$AT"
|
||||||
printf 'tasks_total=%s\n' "$TASKS_TOTAL"
|
printf 'tasks_total=%s\n' "$TASKS_TOTAL"
|
||||||
printf 'tasks_failing=%s\n' "$TASKS_FAILING"
|
printf 'tasks_failing=%s\n' "$TASKS_FAILING"
|
||||||
printf 'row=| [[%s]] | %s | %s | %s | %s | %s | %s |\n' \
|
printf 'hash=%s\n' "$HASH"
|
||||||
"$PAGE" "$HOST" "$USER_NAME" "$HEARTBEAT_STATE" "$AT" "$TASKS_TOTAL" "$TASKS_FAILING"
|
# 第一欄是連結,網址留佔位由呼叫端換掉,理由見檔頭「目錄頁與內容頁分屬兩個存取庫」。
|
||||||
|
# 連結文字先寫死成頁名:頁名這裡就知道,只有網址要等內容頁寫成才查得到。
|
||||||
|
# 第二欄是裸 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"
|
} >"$RD/contents.tsv"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -725,7 +751,13 @@ case "$CMD" in
|
|||||||
[ -n "$TRIGGER" ] || { if [ "${JSC_CLI:-}" = cron ]; then TRIGGER='排程'; else TRIGGER='手動'; fi; }
|
[ -n "$TRIGGER" ] || { if [ "${JSC_CLI:-}" = cron ]; then TRIGGER='排程'; else TRIGGER='手動'; fi; }
|
||||||
case "$TRIGGER" in 排程|事件|手動) ;; *) usage ;; esac
|
case "$TRIGGER" in 排程|事件|手動) ;; *) usage ;; esac
|
||||||
ROUND="$(date +%s)-$$"
|
ROUND="$(date +%s)-$$"
|
||||||
|
# 主機名一律取短的:第一個小數點之前那一段。CHECK_{HASH} 的雜湊來源同樣是
|
||||||
|
# {主機}/{帳號},但那一邊由模型自己填,填進去的多半是短主機名;這一邊如果拿到 FQDN,
|
||||||
|
# 同一台機器就會算出兩個雜湊、開出兩張頁,而且兩張都看起來是對的。
|
||||||
|
# 不用 hostname -s:BusyBox 與部分系統沒有這個旗標,切字串到處都成立。
|
||||||
HOST=$(hostname 2>/dev/null || uname -n 2>/dev/null || printf 'unknown')
|
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')}"
|
USER_NAME="${USER:-$(id -un 2>/dev/null || printf 'unknown')}"
|
||||||
AT=$(date '+%Y-%m-%d %H:%M')
|
AT=$(date '+%Y-%m-%d %H:%M')
|
||||||
|
|
||||||
|
|||||||
+10
-3
@@ -104,7 +104,9 @@
|
|||||||
# JSC_ASSISTANT_HEARTBEAT_TTL 心跳過期門檻,單位秒。巡檢週期由它算出來,也會快照進條目
|
# JSC_ASSISTANT_HEARTBEAT_TTL 心跳過期門檻,單位秒。巡檢週期由它算出來,也會快照進條目
|
||||||
# GITEA_HOST GITEA_TOKEN wiki 連線用。install 當下快照進條目
|
# GITEA_HOST GITEA_TOKEN wiki 連線用。install 當下快照進條目
|
||||||
# JSC_WIKI_REPO wiki 存取庫預設值。install 當下快照進條目
|
# JSC_WIKI_REPO wiki 存取庫預設值。install 當下快照進條目
|
||||||
# JSC_WIKI_REPO_{TYPE} 各頁型的 wiki 存取庫。已設定的全部快照進條目
|
# JSC_WIKI_REPO_{TYPE} 各頁型的 wiki 存取庫。已設定的全部快照進條目。名單是當下從
|
||||||
|
# 環境撈出來的,不寫死,所以新增頁型自動涵蓋,這支不必跟著改。
|
||||||
|
# 目錄頁那一支專用變數也在裡面
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
MARK_PREFIX='# jsc-assist:assistant'
|
MARK_PREFIX='# jsc-assist:assistant'
|
||||||
@@ -290,6 +292,8 @@ patrol_command() {
|
|||||||
|
|
||||||
# 要快照進條目的環境變數名稱。前四支是巡檢那一輪一定要用到的;JSC_WIKI_REPO 系列逐台機器
|
# 要快照進條目的環境變數名稱。前四支是巡檢那一輪一定要用到的;JSC_WIKI_REPO 系列逐台機器
|
||||||
# 不同,直接從現在的環境撈出所有已設定的,不寫死清單。
|
# 不同,直接從現在的環境撈出所有已設定的,不寫死清單。
|
||||||
|
# 不寫死是刻意的:頁型會增加,目錄頁後來也分了自己的存取庫變數,寫死清單就要跟著改一次,
|
||||||
|
# 而漏掉的那一個在排程那一輪是空值,wiki 寫不成、心跳不寫,外面只看得到心跳過期。
|
||||||
snapshot_names() {
|
snapshot_names() {
|
||||||
printf '%s\n' GITEA_HOST GITEA_TOKEN JSC_HOME JSC_ASSISTANT_HEARTBEAT_TTL
|
printf '%s\n' GITEA_HOST GITEA_TOKEN JSC_HOME JSC_ASSISTANT_HEARTBEAT_TTL
|
||||||
env 2>/dev/null \
|
env 2>/dev/null \
|
||||||
@@ -424,7 +428,7 @@ esac
|
|||||||
# --- 裝完要開的權限 ---
|
# --- 裝完要開的權限 ---
|
||||||
|
|
||||||
# 排程那一輪跑在沒有人的工作階段:跳出一次權限詢問就是整輪卡住,卡到鎖逾時才有下一輪。
|
# 排程那一輪跑在沒有人的工作階段:跳出一次權限詢問就是整輪卡住,卡到鎖逾時才有下一輪。
|
||||||
# 這一輪會用到的三支腳本,各印裸路徑、`sh 路徑`、`bash 路徑` 三種呼叫形式——同一支腳本換
|
# 這一輪會用到的每一支腳本,各印裸路徑、`sh 路徑`、`bash 路徑` 三種呼叫形式——同一支腳本換
|
||||||
# 一種叫法就是另一條規則,少印一種就會在那一種叫法上卡住。
|
# 一種叫法就是另一條規則,少印一種就會在那一種叫法上卡住。
|
||||||
# 路徑一律是 $JSC_HOME/current/{外掛名} 那一組,不是這支腳本現在被放在哪裡:規則放行的是
|
# 路徑一律是 $JSC_HOME/current/{外掛名} 那一組,不是這支腳本現在被放在哪裡:規則放行的是
|
||||||
# 那一組路徑,巡檢那一輪也只能用那一組路徑去叫工具,兩邊對得起來才不會被靜靜擋掉。
|
# 那一組路徑,巡檢那一輪也只能用那一組路徑去叫工具,兩邊對得起來才不會被靜靜擋掉。
|
||||||
@@ -432,10 +436,13 @@ print_allow_rules() {
|
|||||||
# gitea.sh 一定要有自己這一條。`Skill(jsc-gitea:wiki)` 只放行「叫用那支技能」,技能裡的
|
# gitea.sh 一定要有自己這一條。`Skill(jsc-gitea:wiki)` 只放行「叫用那支技能」,技能裡的
|
||||||
# 每一個 Bash 呼叫仍然各自受檢,少了這一條,那一輪會在寫監控頁時靜靜被擋——頁寫不成就
|
# 每一個 Bash 呼叫仍然各自受檢,少了這一條,那一輪會在寫監控頁時靜靜被擋——頁寫不成就
|
||||||
# 不寫心跳,外面只看得到心跳過期,看不出是權限擋的。
|
# 不寫心跳,外面只看得到心跳過期,看不出是權限擋的。
|
||||||
|
# 目錄頁那一列改由 wiki-contents.sh 寫,所以它也要有自己這一條:巡檢那一輪會直接叫它,
|
||||||
|
# 少了規則就會停在權限詢問,而那一輪沒有人可以按同意。
|
||||||
for _s in "$CURRENT/jsc-assist/tools/schedule.sh" \
|
for _s in "$CURRENT/jsc-assist/tools/schedule.sh" \
|
||||||
"$CURRENT/jsc-assist/tools/patrol.sh" \
|
"$CURRENT/jsc-assist/tools/patrol.sh" \
|
||||||
"$CURRENT/jsc-hooks/hooks/heartbeat.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(%s:*)\n' "$_s"
|
||||||
printf 'allow_rule=Bash(sh %s:*)\n' "$_s"
|
printf 'allow_rule=Bash(sh %s:*)\n' "$_s"
|
||||||
printf 'allow_rule=Bash(bash %s:*)\n' "$_s"
|
printf 'allow_rule=Bash(bash %s:*)\n' "$_s"
|
||||||
|
|||||||
Reference in New Issue
Block a user