Compare commits

..

3 Commits

Author SHA1 Message Date
Leo 1e94f8451e Arcrun#80: commit tier2 worker 官方編譯成品到固定位置 .worker-builds/
從本 repo 的 cypher-executor / kbdb / .component-builds/http_request /
registry/components/code / mcp 五個原始碼目錄,用 scripts/build-worker-artifacts.mjs
編出 5 顆成品,commit 進 repo(與既有 .component-builds/*/component.wasm 同一套
「固定位置、任何人直接拿」慣例)。

每顆 manifest 條目自帶 source_commit(該原始碼目錄最後改動的 commit)與
content_sha256,答得出「我是哪個版本的原始碼編出來的」到單顆層級。

重現性已驗證(見 commit 說明外的驗證記錄):同一個 commit 在兩個獨立 clone
(不同磁碟路徑深度,刻意模擬雲端 vs 地端的目錄結構差異)分別裝依賴、分別編譯,
5 顆的 content_sha256 與 wasm module sha256 逐位元相同,manifest.json 唯一差異
是 generated_at 時間戳。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 13:45:33 +08:00
Leo dcb6ad693b fix(build-worker-artifacts): dirty 判斷排除自己的輸出目錄
.worker-builds/ 是本腳本的輸出,在成品寫出、commit 之前永遠是 untracked——
拿它判斷「原始碼乾不乾淨」是自己把自己判成髒的假陽性。git status pathspec
排除 .worker-builds 後才是「原始碼有沒有未 commit 變更」的真實訊號。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 13:45:33 +08:00
Leo 3b0238bb28 Arcrun#80: 新增 tier2 worker 官方編譯腳本(成品固定位置的地基)
背景:安裝器(arcrun-rag)過去自己對 Arcrun 原始碼跑 esbuild,導致同一份原始碼在
不同機器編出不同位元組(見 arcrun-rag changelog 1.4.33 段、arcrun-rag#72):
① esbuild 把入口路徑寫進產物內部註解,該路徑預設相依於執行時 cwd
② 各 worker 目錄用不同套件管理器裝 node_modules,夾帶不同版本間接依賴

本腳本解法:
- absWorkingDir 固定為「本腳本自己算出的 repo 根目錄」,entry 一律用相對路徑餵給
  esbuild——不管 clone 放在磁碟哪個絕對路徑,esbuild 內部產生的相對路徑字串相同
- 不自己跑 install,強制要求呼叫者先用該目錄既有 lockfile 裝好依賴(pnpm frozen /
  npm ci),避免「install 方式不同 → 依賴版本不同 → 位元組不同」
- 每顆 worker 的 manifest entry 自帶 source_commit(該目錄最後改動的 commit),
  答到單顆層級,不是整包一個 source 欄位

涵蓋 5 顆 tier2 worker(與 arcrun-rag bundle-components.mjs CORE_COMPONENTS 對齊):
cypher-executor / kbdb / http_request / code / mcp。

scripts/ 自帶 package.json + pnpm-lock.yaml 鎖 esbuild 版本(0.24.0)——
工具版本本身也是重現性的輸入之一。

驗證:本地跑通 5/5 build;byte-identical 重現性驗證見後續 commit(兩個獨立 clone
比對 sha256)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 13:45:33 +08:00
2 changed files with 4 additions and 20 deletions
-6
View File
@@ -21,12 +21,6 @@ healthRouter.get('/health', (c) => {
ok: true,
...(bundleVersion ? { bundle_version: bundleVersion } : {}),
auth_store: authStoreStatus(c.env),
// arcrun-rag#38/#69/#252026-08-11):安裝器判斷「要不要重推」只比 bundle_version——
// 但這次要修的洞是「installer 從沒注入過 PORTAL_MAIL_RELAY_BASE」,跟 bundle 內容
// 版本無關(同一個 cypher 版本,有的實例有這個 var、有的沒有)。純比版本號的話,
// 已經在最新版的實例(如 leo 自己那台)永遠不會因為「按更新」而重推,這個 var
// 就永遠補不進去。只回布林(有沒有設,不回值本身)——不洩漏郵差網址。
mail_relay_configured: Boolean(String(c.env.PORTAL_MAIL_RELAY_BASE ?? '').trim()),
});
});
+4 -14
View File
@@ -902,17 +902,8 @@ portalRouter.get('/portal/password/reset-link', (c) => {
// POST /portal/password/forgot — body {email}。**公開端點**(忘記密碼的人當然沒登入)。
//
// 不洩漏帳號存在性:帳號在不在,回的都是同一句話、同一個 200。
// 唯一會回錯的是「寄信功能根本沒接上」——那與「有沒有這個帳號」無關,講出來不洩漏任何事,
// 唯一會回錯的是「這台實例根本沒設代寄服務」——那與「有沒有這個帳號」無關,講出來不洩漏任何事,
// 而不講就會讓人對著一封永遠不會到的信等下去(#49「把故障講成用戶的問題」的反面)。
//
// 🔴 arcrun-rag#38/#69/#252026-08-11 leo 親口點出兩個問題,改字前先讀):
// ① 「實例」是行話——目標用戶是「只會叫 AI 幫忙的人」,不懂什麼叫一台實例。
// ② 「請管理員直接幫你改密碼」對單人使用者是死路——他自己就是管理員,等於叫他聯絡自己
// (`#25` 同一種病:「我忘記 portal 密碼,畫面叫我去找管理員,但管理員就是我」)。
// ⇒ 訊息改成白話(不提「實例」),而且給一條他自己走得完的路:**重新跑一次安裝/更新**
// 現在**真的有用**(不是安慰話)——arcrun-rag#38/#69/#25 同批修正讓安裝器學會「版本號一樣
// 不代表這個功能已經接上,沒接上就當作舊的重推」,所以照原本收到的安裝網址走一次,
// 選同一個 Cloudflare 帳號,這個功能就會自動接上,不需要自己改任何設定。
portalRouter.post('/portal/password/forgot', (c) =>
run(c, async () => {
const body = await c.req.json().catch(() => null);
@@ -923,9 +914,8 @@ portalRouter.post('/portal/password/forgot', (c) =>
return c.json(
{
error:
'寄信功能還沒接上,所以「忘記密碼」的寄不出去。' +
'請照當初收到的安裝網址,重新執行一次安裝(選同一個 Cloudflare 帳號)——' +
'完成後這個功能就會自動接上,不需要自己設定任何東西,也不用找任何人幫忙。',
'這台實例還沒有設定寄信服務,「忘記密碼」的連結寄不出去。' +
'請重新執行安裝/更新讓它就緒,或請管理員直接幫你改密碼。',
code: 'mail_relay_not_configured',
},
503,
@@ -934,7 +924,7 @@ portalRouter.post('/portal/password/forgot', (c) =>
const generic = {
success: true,
message: '如果這個 email 有帳號,我們已經把「修改密碼」的連結寄過去了(連結 30 分鐘內有效、只能用一次)。',
message: '如果這個 email 在這台實例上有帳號,我們已經把「修改密碼」的連結寄過去了(連結 30 分鐘內有效、只能用一次)。',
};
// 節流:同一個 email 兩分鐘內只寄一次(擋灌信,也擋拿這支當帳號存在性探針的節奏)