t185/t186:三站 nav bar 補齊+自更新改走 raw(jsDelivr @main ref 解析卡死)

## t185:用戶原本根本進不去 docs
leo:「從用戶角度看,**你沒有 nav bar 用戶怎麼進去**?」
——docs 建好了但首頁零連結,只有知道網址的人找得到=同一個病
(能力做出來了,入口沒出現在用戶會看的地方)。

- rag/install 兩站各加同一條 nav(首頁/安裝/說明文件,當前頁 highlight)
- **下載步驟旁**另加 Mac/Windows 分流連結——入口要放在「用戶正在卡住的地方」,
  不能只靠頁首 nav

實測(線上抓 href,非只看 200):
  rag.arcrun.dev  → /docs/、/docs/start/install-mac/、/docs/start/install-windows/
  install.arcrun.dev → /docs/

## t186:自更新改打 raw.githubusercontent(推翻 t150 二修的判斷)
真兇不是檔案快取,是 **jsDelivr  的 ref 解析(main→commit)被快取**,
 與 purge 都清不掉。08-04 三條路同時比對坐實:
  @main 1.4.9/v0.15.6(卡住,x-cache MISS,MISS 仍吐舊)/@sha 與 raw 皆 1.4.10/v0.15.7
zip 能靠改帶版本號檔名繞開,**manifest.json 檔名寫死在 code 裡繞不了** ⇒ 只能換來源。

D20 合規:t150 那輪否決 raw 的理由「那是實名讀」是**誤判**——
D20 對實名的定義是「URL 帶 token@/clone private 殼」,
daemon 這個請求不帶任何憑證=**匿名讀**,D20:22 明列「 放行,不計數」。
副效益:portal 下載按鈕本來就走 raw ⇒ 兩條路同源,不再有「下載頁對、檢查更新錯」。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-04 19:29:17 +08:00
parent 9fcd82b517
commit 6275daa555
+29 -9
View File
@@ -31,12 +31,30 @@ import (
// manifestBase 是 bundle manifest(含 daemon 區塊)。與安裝器同源,不另開發佈通道。
//
// ⚠️ 必須帶時間戳查詢字串繞快取(2026-07-29 實測):
// jsDelivr 對 `@main` 回 `cache-control: max-age=604800`**7 天**
// 用戶的小幫手會連續七天看不到新版 ⇒ 整套更新機制形同虛設。
// 帶 ?t=<unix> 實測即取得最新內容。
// (不用 raw.githubusercontent:那是「實名讀 GitHub」,受 D20 頻率閘管制。)
const manifestBase = "https://cdn.jsdelivr.net/gh/youlinhsieh/arcrun-rag-bundles@main/manifest.json"
// 🔴 t1862026-08-04):**改打 raw.githubusercontent,不再用 jsDelivr `@main`。**
//
// ── 這推翻了 t150 二修(07-29 `142f1d5`)的判斷,理由如下 ──
//
// 那一輪的真兇是「jsDelivr 對 `@main` 回 7 天**檔案**快取」,解法是加 `?t=<unix>`。
// **今天的病不同**:卡住的是「main → 哪個 commit」這層 **ref 解析**
// 而 `?t=` 與 purge **都清不掉 ref 解析**。08-04 實測三條路同時比對:
// @main → 1.4.9 / v0.15.6 ← 卡住(age 持續增長、purge 回 finished 也沒用)
// @<sha> → 1.4.10 / v0.15.7 ← 正確
// raw → 1.4.10 / v0.15.7 ← 正確
// `x-cache: MISS, MISS` 卻仍吐舊內容 ⇒ 坐實不是檔案快取,是 ref 解析被快取。
// 同一天發作三次(win zip → mac zip → manifest)。zip 能靠「改帶版本號檔名」繞開,
// **manifest.json 不行**——檔名寫死在這裡,換不了 ⇒ 只剩「換來源」一條路。
//
// ── D20 合規(t150 那輪否決 raw 的理由是誤判)──
// 舊註解寫「raw 是實名讀 GitHub,受 D20 頻率閘管制」。
// 但 D20 對實名的定義是「URL 帶 `token@``user:pass@`、或 clone 自己 private 殼」,
// **daemon 這個請求不帶任何憑證=匿名讀**,
// D20 明列「匿名讀(curl 公開 release…)✅ 放行,不計數」
// `system-dev/docs/2-architecture/decisions/D20-github-contact-protocol.md:22`)。
// repo 是公開的,GitHub 不知道請求者是誰 ⇒ 零 flag 風險。
//
// 仍保留 `?t=<unix>`:擋瀏覽器/中間層快取,成本為零。
const manifestBase = "https://raw.githubusercontent.com/youlinhsieh/arcrun-rag-bundles/main/manifest.json"
func manifestURLNoCache() string {
return manifestBase + "?t=" + strconv.FormatInt(time.Now().Unix(), 10)
@@ -128,9 +146,11 @@ func checkForUpdate() updateState {
// downloadURLFor 回傳本平台的下載網址。
func downloadURLFor(rel *daemonRelease) string {
// manifestURLNoCache 的理由:@main 有 7 天快取,下載也要繞
// 否則會抓到舊 zip ⇒ sha256 校驗不符 ⇒ 更新永遠失敗。
base := "https://cdn.jsdelivr.net/gh/youlinhsieh/arcrun-rag-bundles@main/"
// t186:與 manifestBase 同一個理由改走 raw——`@main` 的 ref 解析會卡住
// 抓到舊 zip ⇒ sha256 校驗不符 ⇒ **更新永遠失敗且靜默**(正是 08-04 撞的病)
// portal 的下載按鈕本來就走 rawMac zip 曾大於 jsDelivr 20MB 單檔上限),
// 這裡改過來後**兩條下載路徑同源**,不會再出現「下載頁對、檢查更新錯」的分歧。
base := "https://raw.githubusercontent.com/youlinhsieh/arcrun-rag-bundles/main/"
suffix := "?t=" + strconv.FormatInt(time.Now().Unix(), 10)
if isWindows() {
return base + rel.Win.File + suffix