feat(self-hosted): acr init --self-hosted installer + recipe push 把關 + commit 部署 wasm
讓任何 CC 用自己的 CF 帳號一鍵 self-host arcrun(戰法轉 self-hosted 開源)。
Task 1 — acr init --self-hosted installer(用戶只給 CF Account ID + token,其餘自動):
- cli/src/lib/cf-api.ts: CfAccountClient(驗 token / 建 KV 冪等 / 建 R2 / 查 workers.dev subdomain)
- cli/src/lib/deploy.ts: 從 GitHub codeload tarball 拉部署物 → 注入用戶 KV id → wrangler deploy
(tier1 component-builds 先、tier2 cypher-executor/registry 後;部分失敗誠實回報不假綠)
- cli/src/lib/api-recipe-seeds.ts: 10 個現役 API recipe 種子(KBDB 採 Supabase 模式)
- cli/src/commands/init.ts: initSelfHosted() 改寫成 installer 流程
- cli/src/commands/update.ts: acr update(拉新 ref 重部署)
- cypher-executor/scripts/seed-api-recipes.ts: prod 補灌腳本
Task 2 — recipe 入庫把關(封鎖自製零件後,CC 唯一能擴充的是 recipe):
- cli/src/commands/recipe.ts: 新增 probeRecipeEndpoint 打通檢查(提醒級不硬擋,
含模板誠實說明待 run 才知,401/403 標多半缺 credential 非 bug)
- 資料外流提醒沿用既有 obtainExposureConsent(非 TTY 拒絕)
部署物產製:commit 預編譯 wasm 進 repo(推翻 rule 05「wasm 不 commit」):
- .gitignore: 放行 .component-builds/**/component.wasm(registry 中間產物仍排除)
- 只 commit 19 個正當零件 wasm;claude_api / km_writer / kbdb_upsert_block 排除
(非薄殼、是把工作流硬塞進零件,違反 DECISIONS §1,待降級)
- rule 05 同步記錄此慣例變更 + 膨脹 trade-off
SDD: sdk-and-website/self-hosted-init.md(installer 定案)、
component-gatekeeping/recipe-push-gatekeeping.md(recipe 把關)
README 重寫成單一 self-hosted 路徑。CLI typecheck exit 0。
未完(待 richblack):push 此 commit 到 GitHub 後 codeload 才拿得到 wasm;
用第二 CF 帳號端對端驗收 acr init --self-hosted。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
# Design 補充:`acr init --self-hosted` 一鍵自動化(installer 模式)
|
||||
|
||||
> 2026-06-01 初稿 → 2026-06-02 定案改寫(richblack 拍板 installer 形態)。
|
||||
> 本檔是 `sdk-and-website/design.md` 的單檔補充(規則 02 §4.3 允許)。
|
||||
> **狀態:design 已與 richblack 對齊;實作前讀 §6 前置依賴。**
|
||||
> 背景:戰法從 SaaS 轉 self-hosted 開源(docs/HANDOFF-self-host-harness.md §0)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 定案形態(richblack 2026-06-02)
|
||||
|
||||
**arcrun CLI = installer / orchestrator**(類似 rustup / nvm:工具本身小,按需從遠端拉真正內容)。
|
||||
|
||||
### 用戶只做 4 件事,中間什麼都不用懂:
|
||||
1. 申請 CF 帳號
|
||||
2. 安裝 CF CLI(`wrangler`)
|
||||
3. 安裝 arcrun CLI(`npm i -g arcrun`)
|
||||
4. `acr init --self-hosted`(貼 CF Account ID + API Token)→ **完成,其餘看機器跑**
|
||||
|
||||
### CLI 自動做(用戶無感):
|
||||
- 驗 CF token 權限
|
||||
- 建 7 個 KV namespace + 1 個 R2 bucket(冪等)
|
||||
- **從 GitHub release 下載預編譯部署物**(含 24 個 `.wasm` + 各 Worker 的 wrangler.toml + cypher-executor/registry)
|
||||
- 把建好的 KV namespace id 注入各 wrangler.toml + cypher-executor 的 `WORKER_SUBDOMAIN`
|
||||
- **`wrangler deploy` 部署全部 Worker**(用戶已裝 wrangler)
|
||||
- seed auth recipe + API recipe 進 RECIPES KV
|
||||
- 寫回 `~/.arcrun/config.yaml`
|
||||
- 印出「手動 `wrangler secret put ENCRYPTION_KEY` ×3」提示(secret 不自動化,rule 05)
|
||||
|
||||
### 關鍵技術決策(richblack 2026-06-02)
|
||||
| 決策 | 選擇 | 理由 |
|
||||
|---|---|---|
|
||||
| 零件部署物 | **預編譯 `.wasm`**(不在用戶端 build)| 用戶不懂 tinygo、也不該懂。下載即用。 |
|
||||
| 部署工具 | **wrangler**(shell out)| 用戶已裝 CF CLI;self-host 本來就有上傳能力。CLI 不自己重寫 CF Script Upload API。 |
|
||||
| 源碼來源 | **GitHub release tarball**(含預編譯 wasm)| 版本明確、不需用戶有 git、`acr update` 拉新 release 同一條路。 |
|
||||
| 為何不是 git clone | repo **沒 commit `.wasm`**(rule 05 build 產物不 commit)→ clone 拿不到 wasm | 必須走含 wasm 的 release artifact。 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 為什麼是 installer 而非「repo 內掃 wrangler.toml」(推翻初稿)
|
||||
|
||||
初稿假設「用戶在 repo 內跑、CLI 掃 wrangler.toml」。**推翻**,因為:
|
||||
- npm 全域裝的 `acr` 手上**沒有** 24 個 Worker 源碼。
|
||||
- repo 沒 commit `.wasm`(已查證 `git ls-files .component-builds | grep .wasm` = 0)→ 連 clone 都拿不到可部署的 wasm。
|
||||
- 用戶不該需要懂 git / tinygo / repo 結構。
|
||||
|
||||
→ 正解:CLI 當 installer,從 **GitHub release(含預編譯 wasm)** 拉部署物到暫存目錄,在暫存目錄注入 KV id 後 `wrangler deploy`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 流程設計(`initSelfHosted` 改寫)
|
||||
|
||||
```
|
||||
acr init --self-hosted
|
||||
│
|
||||
├─ 1. 問 2 輸入:CF Account ID + CF API Token
|
||||
│ (wrangler 是否已裝?which wrangler;沒裝 → 提示先裝 CF CLI 再來)
|
||||
│ 驗 token:CF API GET /accounts/{id}/tokens/verify + GET /accounts/{id}
|
||||
│ 缺權限(Workers Scripts Edit / KV Edit / R2 Edit)→ exit 1 指出缺哪個 scope
|
||||
│
|
||||
├─ 2. 建資源(冪等:先 list 已存在就重用)
|
||||
│ 7 KV:WEBHOOKS / CREDENTIALS_KV / RECIPES / USERS_KV /
|
||||
│ SESSIONS_KV / ANALYTICS_KV / EXEC_CONTEXT(rule 01 資料儲存表)
|
||||
│ 1 R2:WASM_BUCKET
|
||||
│
|
||||
├─ 3. 下載部署物:GitHub release tarball → 解壓到暫存目錄 (~/.arcrun/.deploy-<ver>/)
|
||||
│ 內含:cypher-executor/ + registry/ + .component-builds/*(每個含預編譯 component.wasm + wrangler.toml)
|
||||
│
|
||||
├─ 4. 注入設定到暫存目錄的 wrangler.toml(不改用戶 repo,改暫存副本)
|
||||
│ - 各 Worker 的 KV binding id ← step 2 建立的
|
||||
│ - cypher-executor [vars] WORKER_SUBDOMAIN ← CF API GET /accounts/{id}/workers/subdomain
|
||||
│
|
||||
├─ 5. 部署:對暫存目錄每個含 wrangler.toml 的 dir,shell out
|
||||
│ `wrangler deploy`(env CLOUDFLARE_API_TOKEN=<token>, CLOUDFLARE_ACCOUNT_ID=<id>)
|
||||
│ 分兩層:tier1 = .component-builds/*(先)→ tier2 = cypher-executor / registry(後)
|
||||
│ 每個 wrangler.toml 已含 workers_dev = true → workers.dev URL 自動啟用
|
||||
│
|
||||
├─ 6. seed recipe 進 RECIPES KV(部署後打新 cypher URL,或直接 CF KV API 寫)
|
||||
│ - auth recipe:重用 AUTH_RECIPE_SEEDS(cypher-executor/src/lib/auth-recipe-seeds.ts)
|
||||
│ - API recipe:新增 seed-api-recipes.ts(見 §5)
|
||||
│
|
||||
├─ 7. 寫回 config(mode: self-hosted + 所有 id + cypher_executor_url = 部署後 workers.dev URL)
|
||||
│
|
||||
└─ 8. 印手動 secret 提示:
|
||||
wrangler secret put ENCRYPTION_KEY --name arcrun-cypher-executor
|
||||
wrangler secret put ENCRYPTION_KEY --name arcrun-auth-static-key
|
||||
wrangler secret put ENCRYPTION_KEY --name arcrun-auth-service-account
|
||||
(三 Worker 共用同一把 key,見 memory: encryption-key-drift-trap)
|
||||
```
|
||||
|
||||
### `acr update`(同一條路,未來新零件)
|
||||
- 拉新 GitHub release → 解壓 → 注入既有 config 的 KV id → wrangler deploy 變動的 Worker。
|
||||
- 第一期至少做到「重跑等效 init 的部署步驟」;diff-only 部署可後續優化。
|
||||
|
||||
---
|
||||
|
||||
## 4. 動到的檔案
|
||||
|
||||
| 檔案 | 動作 |
|
||||
|---|---|
|
||||
| `cli/src/commands/init.ts` | 改寫 `initSelfHosted()`(line 105-131)為 installer 流程 |
|
||||
| `cli/src/lib/cf-api.ts` | 擴充:KV namespace 建立/list、R2 bucket 建立、subdomain 查詢、token verify |
|
||||
| 新增 `cli/src/lib/deploy.ts`(暫定)| 下載 release tarball + 解壓 + 注入 wrangler.toml + shell out wrangler deploy |
|
||||
| 新增 `cli/src/commands/update.ts`(暫定)| `acr update`:拉新 release 重部署 |
|
||||
| 新增 `cli/src/lib/api-recipe-seeds.ts` | API recipe **種子資料**(installer 用;放 CLI 端,**不放 cypher-executor/src**——rule 02 §2.2 hook 擋 cypher-executor TS hard-code endpoint,且 seed 資料本就屬 installer 職責)|
|
||||
| 新增 `cypher-executor/scripts/seed-api-recipes.ts` | seed **腳本**(給 prod 補灌用,import CLI 的種子資料;`scripts/` 不受 §2.2 hook 管)|
|
||||
| `cli/src/index.ts` | 註冊 `acr update` 指令 |
|
||||
|
||||
**不動**:cypher-executor 執行路徑、既有零件 wasm 源碼、config 讀取端(config.ts:52 已支援 self-hosted)。
|
||||
|
||||
---
|
||||
|
||||
## 5. API recipe seed(新增 seed-api-recipes.ts,richblack 2026-06-02 定)
|
||||
|
||||
codebase 只有 auth recipe seed。新增 `seed-api-recipes.ts`,把現役 API recipe hard-code 成種子。
|
||||
|
||||
### 現役 API recipe(從 prod KV 查得,2026-06-01)
|
||||
- `kbdb_get`(+ create_block / patch_block / delete / ingest)→ auth_service: kbdb
|
||||
- `gmail_send` → google_gmail_sa
|
||||
- `google_sheets_append` / `google_sheets_read` → google_sheets_sa
|
||||
- `telegram_send` → telegram
|
||||
- `line_notify_send` → line_notify
|
||||
|
||||
### KBDB recipe 採 Supabase 模式(richblack 2026-06-02)
|
||||
- **KBDB 是 richblack 提供的服務**(跟 arcrun 一樣),採「基礎免費、大量收費」。
|
||||
- KBDB recipe **進 seed**(展示能力 = 引子,Supabase 模式)。使用者要用 → 去 **arcrun 取統一 API Key**(已有 /register 入口),把 key 設成 credential。
|
||||
- ⚠️ **FOLLOW-UP(交 KBDB 端)**:現役 endpoint 是 `kbdb.finally.click{{_path}}`。richblack:這是 KBDB 端要改的問題——KBDB 該用統一對外網址提供大家用,不是 finally.click。**seed 先照現況進;KBDB 端改網址後同步更新 seed。** 此事不擋 init 實作。
|
||||
|
||||
---
|
||||
|
||||
## 6. 部署物產製:commit wasm 進 repo + codeload tarball(richblack 2026-06-02 定案)
|
||||
|
||||
> 此節**取代初稿的「GitHub release artifact」構想**。richblack 拍板更輕的做法:
|
||||
> 直接把預編譯 wasm commit 進 repo,CLI 從 GitHub codeload tarball 拿。不需 release.yml 機制。
|
||||
|
||||
### 6.1 策略
|
||||
|
||||
- **repo 自帶可部署的 wasm**:刪 `.gitignore` 的 `*.wasm` 排除,commit 預編譯 wasm 進 repo。
|
||||
→ repo 本身就是部署來源,CLI 直接拿、用戶用自己的 CF token deploy。
|
||||
- **CLI 走 codeload tarball**:`https://codeload.github.com/richblack/arcrun/tar.gz/{ref}`
|
||||
(ref = main 或 tag)。用戶不需 git、版本可控(tag)。`acr update` 拉新 ref。
|
||||
- **理由**(richblack):「我在我的 CF 能用 = 我已擁有 wasm;用戶指向我的 GitHub 取得 wasm,
|
||||
用他自己的 CF credential deploy。開源,看不看源碼不重要,體驗好最重要。」
|
||||
|
||||
### 6.2 ⚠️ 推翻既有鐵律(rule 05)— 需同步改規則
|
||||
|
||||
`.claude/rules/05-deploy-convention.md` 明文「`.component-builds/{name}/component.wasm` **不 commit 進 repo**
|
||||
(build 產物)」「Phase 1-3 暫時 commit 過,**之後會加 .gitignore 清理**」。
|
||||
**本決策反向**:commit wasm 進 repo(self-host 需 repo 自帶可部署 wasm)。
|
||||
→ **實作時必須同步改 rule 05 + .gitignore**,否則 pre-write hook / 規則與實作打架。
|
||||
→ deploy.yml 的 CI rebuild 步驟仍保留(CI 部署 prod 時用最新 source rebuild,與 commit 的 wasm 不衝突;
|
||||
commit 的 wasm 是給「self-host 用戶 + acr init」用的部署來源)。
|
||||
|
||||
### 6.3 只 commit 部署所需的 wasm(省空間)
|
||||
|
||||
- 實況(2026-06-02 查):`registry/components/*.wasm` 23 個(build 中間產物)+
|
||||
`.component-builds/*/component.wasm` 22 個(部署物),共 **~50MB**。
|
||||
- **部署只需 `.component-builds/*/component.wasm`**(wrangler deploy 認這個)。
|
||||
→ **只 commit `.component-builds/*/component.wasm`(22 個),不必 commit registry 那 23 個**(省一半)。
|
||||
`.gitignore` 改成:保留排除 `registry/components/**/*.wasm`(中間產物),只放行 `.component-builds/**/component.wasm`。
|
||||
- ⚠️ **誠實 trade-off**(mindset §7):commit wasm 進 repo → 每次 wasm rebuild 都在 git 歷史累積二進位,
|
||||
**repo 長期會膨脹**。可接受(self-host 體驗優先),但記錄此代價;未來若膨脹過劇,再考慮 release artifact / git-lfs。
|
||||
|
||||
### 6.3.1 「錯做成零件」的 3 個不 commit(richblack 2026-06-02)
|
||||
|
||||
實際 commit 的是 **19 個正當零件**,不是 22。排除的 3 個:`claude_api` / `km_writer` / `kbdb_upsert_block`。
|
||||
- **原因(richblack 修正「待刪」說法)**:它們**不是 endpoint 薄殼,是把工作流硬塞進零件**(違反 DECISIONS §1)。
|
||||
例:`kbdb_upsert_block` 的 upsert 邏輯應在 KBDB API 那邊(API 提供 upsert endpoint),零件只該驅動它;
|
||||
現在卻把「GET 找→有則 PATCH 無則 POST」整段工作流塞進零件。本質是工作流/recipe,被錯做成零件。
|
||||
- **為何「現在就不 commit」而非「先 commit 之後刪」**:commit 二進位進 git 歷史後,即使日後 `git rm`,
|
||||
歷史裡仍永久殘留(repo 體積已被佔),除非 rewrite history(很麻煩)。**錯誤的東西不灌進永久歷史。**
|
||||
- **落地**:`.gitignore` 放行 22 個後**再排除這 3 個**(後出現規則勝出);`deploy.ts discoverWorkerDirs`
|
||||
只部署「同時有 wrangler.toml + component.wasm」的目錄 → self-host 用戶 codeload 拿到的目錄缺這 3 個 wasm → 自然跳過。
|
||||
- **後續**:這 3 個的降級(變回工作流/recipe)是 BACKLOG 既有待辦,本次不處理,但確保它們不進 self-host 部署來源。
|
||||
|
||||
### 6.4 CLI deploy 流程(deploy.ts downloadAndDeploy 補實作)
|
||||
|
||||
```
|
||||
1. 下載 codeload tarball(ref 預設 main,acr update 可帶 tag)→ 解壓 ~/.arcrun/.deploy-<ref>/
|
||||
2. 讀解壓出的 .component-builds/* + cypher-executor/ + registry/
|
||||
3. 各 wrangler.toml 注入 ctx.kvNamespaceIds + cypher-executor WORKER_SUBDOMAIN
|
||||
4. tier1=.component-builds/*(先)→ tier2=cypher-executor/registry(後)
|
||||
每個 dir:pnpm install(若有 lock)→ CLOUDFLARE_API_TOKEN=<用戶> wrangler deploy
|
||||
5. 回 cypherExecutorUrl = https://arcrun-cypher-executor.<subdomain>.workers.dev
|
||||
```
|
||||
|
||||
注意:tier2(cypher-executor/registry)是 TS,wrangler deploy 會在用戶端用內建 esbuild bundle
|
||||
(不需額外工具,richblack 確認源碼可見不重要、體驗優先 → artifact 含 TS 源碼即可)。
|
||||
|
||||
### 6.5 實作順序
|
||||
|
||||
1. 改 `.gitignore`(放行 `.component-builds/**/component.wasm`)+ commit 22 個 wasm。
|
||||
2. 同步改 rule 05(記錄此決策推翻原慣例)。
|
||||
3. 補實 `deploy.ts downloadAndDeploy`(codeload 下載 + 注入 + wrangler deploy)。
|
||||
4. **在 1-2 完成前,downloadAndDeploy 維持誠實 unimplemented,不假裝(mindset §7)。**
|
||||
|
||||
### 6.6 未來方向:零件按需安裝(richblack 2026-06-02,現在不做)
|
||||
|
||||
- 現在 `acr init --self-hosted` **全裝基礎零件**(22 個一次部署)。簡單、夠用。
|
||||
- **未來若零件數量真的變很多**,再思考「按需安裝」(只裝 workflow 實際用到的零件 / 用戶選裝)。
|
||||
- **現在不做的理由**(DECISIONS 附錄「會不會累積成債」):零件目前少且未來絕大多數是 recipe
|
||||
(不需 deploy)→ 為「零件爆量」做按需安裝基建 = 為不存在的規模做自動化 = 過度工程。
|
||||
零件真的爆量再回頭做,屆時是「未來一次性處理的設計點」,現在不必焦慮。
|
||||
|
||||
---
|
||||
|
||||
## 7. 驗收標準(客觀證據,mindset §7)
|
||||
|
||||
1. richblack 用**全新 CF 帳號** + wrangler 已裝 + 一個 CF API Token 跑 `acr init --self-hosted`
|
||||
→ 全程無手動建 KV / 無手動 clone / 無 tinygo / 無手動填 namespace id。
|
||||
2. 跑完印 secret 提示,richblack 手動 `wrangler secret put ENCRYPTION_KEY` ×3。
|
||||
3. `acr push` 一個含 http_request + 自建 recipe 的 workflow → trigger → **HTTP 2xx + execution trace**。
|
||||
4. 冪等:重跑 init 不重建已存在 KV / 不報錯。
|
||||
5. `acr update` 拉新 codeload tarball(tag)→ 重部署成功。
|
||||
|
||||
---
|
||||
|
||||
## 8. 為何不違反鐵律
|
||||
|
||||
- 只動 `cli/` + 新增 `cypher-executor/scripts/`(seed 腳本,非執行路徑業務邏輯)。
|
||||
- 不在 `registry/components/` 寫 TS;不在 cypher-executor TS 實作 credential/auth/JWT。
|
||||
- 不新增 Service Binding。
|
||||
- secret 不進自動化(§3 step 8 手動)。
|
||||
- 不重寫部署輪子(用 wrangler,不自寫 CF Script Upload)。
|
||||
Reference in New Issue
Block a user