diff --git a/README.md b/README.md index 4ae11ef..704a1a9 100644 --- a/README.md +++ b/README.md @@ -60,14 +60,14 @@ | 用了哪些 0G 技術 | 為什麼要用它 | 在哪一行用到 | |---|---|---| | **0G Compute Network**
TEE 可驗證推論 | 戰報要永久存檔,寫它的那個 agent 就不能是黑箱。金鑰在 pc.0g.ai 選 **Private(TEE enclave)** 開的,推論實際跑在 enclave 裡,不是只呼叫一個 OpenAI 相容端點。 | [`functions/api/narrate.js#L55-L134`](https://github.com/ConsssLab/web/blob/main/functions/api/narrate.js#L55-L134) | -| **0G Compute Network**
Router 設定 | 敵方 agent 與旁白 agent 共用同一份供應商工廠,把 `AI_PROVIDER` 改成 `0g` 就能整支切過去,不用改程式碼。 | [`functions/api/og/_shared.js#L22-L23`](https://github.com/ConsssLab/web/blob/main/functions/api/og/_shared.js#L22-L23) · [`functions/api/og/_shared.js#L82-L90`](https://github.com/ConsssLab/web/blob/main/functions/api/og/_shared.js#L82-L90) | +| **0G Compute Network**
Router 設定 | 敵方 agent 與旁白 agent 共用同一份供應商工廠,**兩者預設都指向 0G Compute**。要主張「整場都是同一位 agent」,對打的那個 agent 就必須在 enclave 裡 —— 所以預設不是 OpenAI。 | [`functions/api/og/_shared.js#L22-L23`](https://github.com/ConsssLab/web/blob/main/functions/api/og/_shared.js#L22-L23) · [`functions/api/og/_shared.js#L68-L87`](https://github.com/ConsssLab/web/blob/main/functions/api/og/_shared.js#L68-L87) · [`functions/api/og/_shared.js#L89-L97`](https://github.com/ConsssLab/web/blob/main/functions/api/og/_shared.js#L89-L97) | | **0G Storage**
真實寫入 | 記憶碎片要「永久保存」就必須真的落地。用官方 SDK 走完整協議:切 256-byte chunk 算 merkle root → 對 Flow 合約送 submit(付儲存費)→ 把 segment 傳給 storage node。**全程用玩家自己的錢包簽,伺服器不持有私鑰。** | [`public/js/storage.js#L72-L116`](https://github.com/ConsssLab/web/blob/main/public/js/storage.js#L72-L116)
節點代理(Node):[`proxy/api/index.js`](https://github.com/ConsssLab/web/blob/main/proxy/api/index.js) —— 節點是裸 IP + 非標準埠,Cloudflare Workers 打不到(error 1003 / 521),這一段必須跑在 Node 上 | | **0G Storage**
indexer 唯讀查詢 | 「上傳沒報錯」不等於存進去了。拿 root hash 回頭問 indexer,確認 storage node 真的收下並 finalized 才敢標成已存檔。唯讀、不需金鑰,評審可自行查證。 | [`functions/api/og/storage.js#L117-L159`](https://github.com/ConsssLab/web/blob/main/functions/api/og/storage.js#L117-L159) | | **0G Storage**
節點活性探測 | 結果畫面的燈號要照實反映網路狀態,不能寫死成綠燈。 | [`functions/api/og/status.js#L34-L57`](https://github.com/ConsssLab/web/blob/main/functions/api/og/status.js#L34-L57) | -| **0G Chain**
錨定寫入 | 戰報需要一個不可竄改、有時間戳的存在證明。40 bytes 結構化 calldata(魔術字 `CSSW` + 版本 + 戰績 + SHA-256),用合約建立交易送出,chainscan 上一眼認得出來。 | [`functions/api/og/shard.js#L62-L84`](https://github.com/ConsssLab/web/blob/main/functions/api/og/shard.js#L62-L84) · [`public/js/og.js#L235-L260`](https://github.com/ConsssLab/web/blob/main/public/js/og.js#L235-L260) | +| **0G Chain**
錨定寫入 | 戰報需要一個不可竄改、有時間戳的存在證明。40 bytes 結構化 calldata(魔術字 `CSSW` + 版本 + 戰績 + SHA-256),用合約建立交易送出,chainscan 上一眼認得出來。 | [`functions/api/og/shard.js#L85-L107`](https://github.com/ConsssLab/web/blob/main/functions/api/og/shard.js#L85-L107) · [`public/js/og.js#L235-L260`](https://github.com/ConsssLab/web/blob/main/public/js/og.js#L235-L260) | | **0G Chain**
鏈上回驗 | **這是整個專案的重點**:按了按鈕、錢包沒報錯,不代表資料真的在鏈上。所以再用 `eth_getTransactionByHash` 把交易讀回來、反解 calldata、跟本地碎片逐欄比對,四項全對才打勾。 | [`functions/api/og/verify.js#L19-L47`](https://github.com/ConsssLab/web/blob/main/functions/api/og/verify.js#L19-L47) · [`functions/api/og/verify.js#L49-L104`](https://github.com/ConsssLab/web/blob/main/functions/api/og/verify.js#L49-L104) | | **0G Chain**
鏈況與網路切換 | 標題頁即時顯示 Galileo 區塊高度;chainId **從鏈上實際讀回來**校準而不是寫死(0G 換過 chain ID 16601→16602,寫死會讓切鏈整個失敗)。 | [`functions/api/og/status.js#L59-L157`](https://github.com/ConsssLab/web/blob/main/functions/api/og/status.js#L59-L157) · [`public/js/og.js#L167-L220`](https://github.com/ConsssLab/web/blob/main/public/js/og.js#L167-L220) | -| — **敵方 AI agent**
(OpenAI,非 0G) | 對戰時每回合都要叫一次,需要低延遲,所以另外走 OpenAI。模型輸出一律當不可信資料重新過濾。 | [`functions/api/agent.js#L173-L256`](https://github.com/ConsssLab/web/blob/main/functions/api/agent.js#L173-L256) · [`public/js/ai.js#L16-L65`](https://github.com/ConsssLab/web/blob/main/public/js/ai.js#L16-L65) | +| — **敵方 AI agent**
(OpenAI,非 0G) | 對戰時每回合都要叫一次,需要低延遲,所以另外走 OpenAI。模型輸出一律當不可信資料重新過濾。 | [`functions/api/agent.js#L188-L291`](https://github.com/ConsssLab/web/blob/main/functions/api/agent.js#L188-L291) · [`public/js/ai.js#L16-L68`](https://github.com/ConsssLab/web/blob/main/public/js/ai.js#L16-L68) | ### 評審可以自己打的端點 @@ -128,7 +128,8 @@ inline SVG:戰場、卡牌、遺忘者的遮罩、結果畫面都沒有圖檔 [`public/js/rules.js`](public/js/rules.js) 是純規則層(無 DOM、無網路),可以直接 import 跑對戰模擬: ```bash -npm run sim # 跑 300 場隨機對戰,印出勝負分布與平均回合數 +npm run sim # 跑 300 場隨機對戰,印出勝負分布與平均回合數 +npm run check:links # 檢查這份 README 的行號連結有沒有因為改碼而跑掉 ``` > ⚠️ **改任何數值前請先重跑模擬。** `npm run sim` 用隨機出牌,每次會抖動;連跑三輪的 diff --git a/package.json b/package.json index 2c49b65..ae08ef0 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "scripts": { "dev": "wrangler pages dev public --compatibility-date=2024-09-01 --port 8788", "deploy": "wrangler pages deploy public --project-name conssswars-web --branch main", - "sim": "node scripts/simulate.mjs" + "sim": "node scripts/simulate.mjs", + "check:links": "node scripts/check-links.mjs" }, "devDependencies": { "wrangler": "^3.78.0" diff --git a/scripts/check-links.mjs b/scripts/check-links.mjs new file mode 100644 index 0000000..20d8666 --- /dev/null +++ b/scripts/check-links.mjs @@ -0,0 +1,62 @@ +/** + * 檢查 README 裡的行號連結有沒有跑掉。 + * + * README 的賣點是「精確到行號」—— 評審會點進去。但只要改到被指到的檔案,行號就位移, + * 連結會安靜地指向不相干的程式碼:範圍還在檔案內,所以肉眼看不出壞掉。 + * 實際發生過兩次,其中一次「賽道二 indexer 唯讀查詢」點下去落在兩個無關的小工具上。 + * + * 這支不驗「行號是不是原本想指的東西」(那需要人判斷),只驗一件機械可查的事: + * 起始行要是函式或註解區塊的開頭,結束行要是收尾。位移之後這兩個條件幾乎必然被破壞。 + * + * npm run check:links + */ + +import { readFileSync } from 'node:fs'; + +const README = 'README.md'; +const LINK = /`([^`]+?\.js)#L(\d+)-L(\d+)`/g; + +const START_OK = /^(\/\*\*|\/\*|export\s|async\s|function\s|const\s|class\s)/; +const END_OK = /^(\}|\}\);|\};|export const .+;)$/; + +const readme = readFileSync(README, 'utf8'); +const seen = new Set(); +let bad = 0; +let total = 0; + +for (const [, path, aStr, bStr] of readme.matchAll(LINK)) { + const key = `${path}#${aStr}-${bStr}`; + if (seen.has(key)) continue; + seen.add(key); + total += 1; + + let lines; + try { + lines = readFileSync(path, 'utf8').split('\n'); + } catch { + console.log(`❌ ${key} 檔案不存在`); + bad += 1; + continue; + } + + const a = Number(aStr); + const b = Number(bStr); + if (b > lines.length) { + console.log(`❌ ${key} 檔案只有 ${lines.length} 行`); + bad += 1; + continue; + } + + const head = lines[a - 1].trim(); + const tail = lines[b - 1].trim(); + const ok = START_OK.test(head) && END_OK.test(tail); + if (!ok) bad += 1; + console.log(`${ok ? '✅' : '❌'} ${key}\n 起 ${head.slice(0, 60)}\n 訖 ${tail.slice(0, 60)}`); +} + +console.log( + bad === 0 + ? `\n${total} 個行號連結都落在函式起訖上。` + : `\n${bad} / ${total} 個連結跑掉了 —— 被指到的檔案改過,行號要重算。`, +); +process.exit(bad === 0 ? 0 : 1);