chore: D22 落地——docs/SDD/wiki/CLAUDE.md 進 repo(Gitea private 預設全 push)
頂層 D22 決策(leo 2026-07-03 拍板):推什麼由開發環境歸屬決定, Gitea private=除機敏值/build 產物/.github 外全 push。 解 T1.5 卡點:雲端工人 clone 拿得到 credential-store-migration.md,可就地改寫 SDD。 機敏掃描兩輪通過(新增 189 檔約 2.1MB,node_modules/dist/wasm 照舊排除)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# Design 補充:設定分層(env > 專案層 > 全域)+ init 非互動
|
||||
|
||||
> 2026-06-04 建立。`sdk-and-website/design.md` 的單檔補充(規則 02 §4.3 允許)。
|
||||
> 來源:`docs/壓測報告.md` §1.2(方案 C)+ §2.2(init 非互動)= 阻斷項 #7、#8。
|
||||
> richblack 2026-06-04 授權:「#7#8 是同一問題,明顯發現的問題當然要解決,完成後就要推。」
|
||||
|
||||
---
|
||||
|
||||
## 1. 問題(壓測實測)
|
||||
|
||||
`acr` v1.1.0 把設定**寫死在全域唯一一份** `~/.arcrun/config.yaml`(`config.ts`:
|
||||
`CONFIG_DIR = join(homedir(), '.arcrun')`),且 `init` 只能 readline 互動。後果:
|
||||
|
||||
- **#8 多帳號**:同一台電腦只能一個 arcrun 身份 → 接案者 / 多公司 / 個人+公司混用做不到。
|
||||
壓測者克難法是覆寫 `HOME`(需讀原始碼才找得到、不直覺)。
|
||||
- **#7 非互動**:init 只能 TTY 問答 → AI / CI 無法用 flag / env 完成。
|
||||
|
||||
兩者本質同一:**設定來源不該只有「全域單檔 + 強制 TTY」一條路**。
|
||||
|
||||
## 2. 設計(richblack 2026-06-04 拍板:三層全上 + init flag/env)
|
||||
|
||||
### 2.1 設定分層優先序(仿 git config / Claude Code MCP)
|
||||
|
||||
```
|
||||
1. 環境變數 ARCRUN_* / CLOUDFLARE_* ← 最高,解 #7(AI/CI 非互動)
|
||||
2. 專案層設定 <就近往上找>/.arcrun.yaml ← 解 #8(接案多帳號),有 fallback
|
||||
3. 全域設定 ~/.arcrun/config.yaml ← 平常自動用(自己的帳號)
|
||||
```
|
||||
|
||||
- **就近往上找**:從 `process.cwd()` 往上層目錄逐層找 `.arcrun.yaml`,找到第一個即用(停在檔案系統根)。
|
||||
→ 自己的專案不放檔 → 自動 fallback 全域;客戶資料夾放檔 → 只在該樹生效,離開自動切回。零心智負擔、不會忘記切換。
|
||||
- **覆蓋是「欄位級 merge」**:高層只覆蓋它有提供的欄位,未提供的欄位 fallback 到低層。
|
||||
(例:專案層只放 `cypher_executor_url`,其餘仍用全域。)
|
||||
|
||||
### 2.2 env 變數對應(最高層,欄位級覆蓋)
|
||||
|
||||
| env | 覆蓋 config 欄位 | 用途 |
|
||||
|---|---|---|
|
||||
| `ARCRUN_MODE` | `mode` | local/standard/self-hosted |
|
||||
| `ARCRUN_API_KEY` | `api_key` | standard |
|
||||
| `ARCRUN_ENCRYPTION_KEY` | `encryption_key` | standard/self-hosted |
|
||||
| `ARCRUN_CYPHER_EXECUTOR_URL` | `cypher_executor_url` | self-hosted 指向自己的 cypher |
|
||||
| `CLOUDFLARE_ACCOUNT_ID` | `cloudflare_account_id` | self-hosted(沿用 wrangler 慣用名)|
|
||||
| `CLOUDFLARE_API_TOKEN` | `cf_api_token` | self-hosted(沿用 wrangler 慣用名)|
|
||||
|
||||
> CF 兩個用 `CLOUDFLARE_*` 而非 `ARCRUN_*`:與 wrangler / deploy.ts 既有慣例一致(deploy.ts 跑 wrangler 時就是設這兩個 env),CI 設一次兩邊通用。
|
||||
|
||||
### 2.3 init 非互動(flag > env > 互動問答)
|
||||
|
||||
`acr init --self-hosted` 取得 account-id / api-token 的順序:
|
||||
1. **flag**:`--account-id <id>` / `--api-token <token>`
|
||||
2. **env**:`CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`
|
||||
3. **互動**:前兩者缺才 readline 問(保留現有 UX)
|
||||
|
||||
> mindset §7 判準:帳號設定**不是**「暴露資料 / 建零件」類風險確認,是單純設定值,flag/env 合法、不違反「非 TTY 拒絕代人類確認」。
|
||||
> 風險確認(exposure_consent 等)仍維持需人類明示,不在本次放寬範圍。
|
||||
|
||||
## 3. 實作(只動 cli/)
|
||||
|
||||
| 檔案 | 動作 |
|
||||
|---|---|
|
||||
| `cli/src/lib/config.ts` | `loadConfig()` 改三層解析:全域 → merge 專案層 `findProjectConfig()` → merge env(`applyEnvOverrides()`)。新增 `acr config --where` 用的 `resolveConfigSources()` |
|
||||
| `cli/src/commands/init.ts` | `cmdInit` / `initSelfHosted` 收 `accountId`/`apiToken` options,缺才走 env 再 fallback 互動 |
|
||||
| `cli/src/index.ts` | init 加 `--account-id <id>` / `--api-token <token>` option |
|
||||
|
||||
**`loadConfig()` 是唯一設定入口**(12 個指令全走它)→ 改它一處,全指令自動受益分層。
|
||||
|
||||
### 3.1 `acr config --where`(壓測 §1.2 建議 #3,避免用錯帳號)
|
||||
|
||||
新增輕量 `acr config` 指令,印出「現在這個資料夾正用哪個帳號 / 設定來自哪一層」:
|
||||
```
|
||||
mode: self-hosted(來源:專案層 /path/客戶A/.arcrun.yaml)
|
||||
cloudflare_account_id: abc...(來源:env CLOUDFLARE_ACCOUNT_ID)
|
||||
cypher_executor_url: https://...(來源:全域 ~/.arcrun/config.yaml)
|
||||
```
|
||||
> 安全價值:部署前一眼確認「沒用錯帳號」。本次一併做(成本低、直接回應壓測痛點)。
|
||||
|
||||
### 3.2 安全:專案層 .arcrun.yaml 含憑證 → 必須 gitignore
|
||||
|
||||
專案層 `.arcrun.yaml` 可能含 `cf_api_token`。`createCredentialsYamlIfMissing()` 既有 gitignore 邏輯
|
||||
擴充為一併忽略 `.arcrun.yaml`(壓測 §1.2 安全附帶發現:憑證進版控 = 帳號外洩)。
|
||||
|
||||
## 4. 驗收標準(客觀證據,mindset §7)
|
||||
|
||||
1. 專案資料夾放 `.arcrun.yaml` → `acr config --where` 顯示來源為該專案層;離開該樹 → 顯示全域。
|
||||
2. `CLOUDFLARE_ACCOUNT_ID=x CLOUDFLARE_API_TOKEN=y acr init --self-hosted` → 不問互動直接跑(#7)。
|
||||
3. `acr init --self-hosted --account-id x --api-token y` → 同上(flag 優先於 env)。
|
||||
4. 三者皆缺 → fallback 互動問答(既有 UX 不破壞)。
|
||||
5. env > 專案層 > 全域 的欄位級覆蓋:單元測試覆蓋三層 merge。
|
||||
6. `npx tsc --noEmit` 全綠。
|
||||
|
||||
## 4.1 實作完成記錄(2026-06-04)
|
||||
|
||||
全部 task 完成,客觀證據如下(mindset §7):
|
||||
|
||||
- [x] `config.ts` 三層解析:`loadConfig()` = 全域 → 專案層 → env 欄位級 merge;新增 `findProjectConfig()`(就近往上找)/ `resolveConfigSources()` / `activeProjectConfigPath()`
|
||||
- [x] `init.ts`:`initSelfHosted` 收 flag/env,缺才互動;gitignore 一併排除 `.arcrun.yaml`
|
||||
- [x] `index.ts`:init 加 `--account-id`/`--api-token`;新增 `acr config [--where]` 指令
|
||||
- [x] 新增 `commands/config.ts`(token 遮罩、來源層標示)
|
||||
- **驗收證據**:
|
||||
- 端對端測試(真實 fs 臨時目錄樹)8/8 通過:深層就近找專案層、未提供欄位 fallback、env 最高層覆蓋、來源層標示、離開專案樹回全域
|
||||
- `acr init --help` 顯示 flag;`acr config --where` 正確標來源 + token 遮罩 `secret_t…`
|
||||
- `npx tsc --noEmit` 全綠
|
||||
|
||||
## 5. 為何不違反鐵律
|
||||
|
||||
- 只動 `cli/`,不碰零件 / cypher-executor 執行路徑 / Service Binding。
|
||||
- flag/env 是帳號設定值,非風險確認(mindset §7 風險確認仍須人類明示)。
|
||||
- 不新增頂層 SDD 目錄(本檔是 sdk-and-website 子系統內單檔補充,rule 02 §4.3)。
|
||||
@@ -0,0 +1,281 @@
|
||||
# Design Document: arcrun SDK Libraries + Website
|
||||
|
||||
## Overview
|
||||
|
||||
本設計涵蓋 arcrun 的三個新增交付物:
|
||||
1. Python SDK lib(`pip install arcrun`)
|
||||
2. JS/TS SDK lib(`npm install arcrun` 或 `@arcrun/sdk`)
|
||||
3. arcrun.dev 網站完善(零件列表、recipe 列表、登入管理)
|
||||
|
||||
**設計原則:修改不重建。** SDK 是 `cypher.arcrun.dev` HTTP API 的 thin wrapper。不在 client 端重新實作任何 server 端已有的邏輯(workflow 執行、credential 注入、auth recipe 解析)。唯一在 client 做的是 AES-GCM 加密(因為 server 的 POST /credentials 期望收到加密後的 payload)。
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### 系統關係圖
|
||||
|
||||
```
|
||||
使用者程式碼
|
||||
├── CLI(acr) → cypher.arcrun.dev(HTTP API)
|
||||
├── Python SDK(arcrun) → cypher.arcrun.dev(HTTP API)
|
||||
└── JS SDK(arcrun / @arcrun/sdk) → cypher.arcrun.dev(HTTP API)
|
||||
|
||||
arcrun.dev 網站(Next.js / Cloudflare Pages)
|
||||
├── /login → /auth/google/start, /auth/github/start(cypher.arcrun.dev)
|
||||
├── /dashboard → /me, /me/api-key/rotate(cypher.arcrun.dev)
|
||||
├── /integrations → /auth-recipes(cypher.arcrun.dev)
|
||||
└── /components → /recipes + 靜態零件清單(embedded)
|
||||
|
||||
cypher.arcrun.dev(Cloudflare Worker — cypher-executor,不改)
|
||||
├── POST /credentials ← 接收 { name, encrypted, iv }
|
||||
├── GET /credentials ← 列出 credential 名稱
|
||||
├── DELETE /credentials/:name ← 刪除 credential
|
||||
├── GET /auth-recipes ← 列出 20 個 auth recipe
|
||||
├── GET /auth-recipes/:service ← 單一 recipe 詳情
|
||||
├── POST /webhooks/named ← 部署 workflow
|
||||
├── POST /webhooks/named/:name/trigger ← 觸發 workflow
|
||||
├── GET /webhooks/named ← 列出 workflow
|
||||
├── POST /register ← 註冊取得 API Key
|
||||
├── GET /me ← 當前用戶資訊
|
||||
└── /auth/* ← OAuth 流程
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python SDK(`arcrun/python-sdk/`)
|
||||
|
||||
### 目錄結構
|
||||
|
||||
```
|
||||
arcrun/python-sdk/
|
||||
├── pyproject.toml ← hatchling build, name="arcrun", deps=[httpx>=0.27, cryptography>=42]
|
||||
├── README.md
|
||||
└── arcrun/
|
||||
├── __init__.py ← from .client import Arcrun
|
||||
├── client.py ← Arcrun class(主入口)
|
||||
├── crypto.py ← AES-GCM 加密(client 端,用 cryptography 套件)
|
||||
├── creds.py ← CredentialsClient(push/list/delete)
|
||||
├── auth.py ← AuthClient(setup/bind/get_token/list_services)
|
||||
└── workflows.py ← WorkflowClient(run/push/list/delete)
|
||||
```
|
||||
|
||||
### API 設計
|
||||
|
||||
```python
|
||||
from arcrun import Arcrun
|
||||
|
||||
# 建構 — api_key 從參數 > 環境變數 > ~/.arcrun/config.yaml 自動取得
|
||||
client = Arcrun()
|
||||
# 或明確指定
|
||||
client = Arcrun(api_key="ak_xxx", encryption_key="hexstring")
|
||||
|
||||
# Auth:設定並綁定服務
|
||||
client.auth.setup("openai", api_key="sk-xxx") # 加密 + 上傳
|
||||
openai_client = client.auth.bind("openai") # 取回 pre-auth client
|
||||
response = openai_client.get("/models") # httpx.Client
|
||||
token = client.auth.get_token("openai") # raw token string
|
||||
services = client.auth.list_services() # [{ service, display_name, ... }]
|
||||
|
||||
# Credentials:低階操作
|
||||
client.creds.push("my_token", "value123")
|
||||
names = client.creds.list()
|
||||
client.creds.delete("my_token")
|
||||
|
||||
# Workflows
|
||||
result = client.workflows.run("my-flow", {"email": "user@example.com"})
|
||||
url = client.workflows.push("my-flow", graph_dict)
|
||||
workflows = client.workflows.list()
|
||||
```
|
||||
|
||||
### Credential 加密流程
|
||||
|
||||
```
|
||||
setup("openai", api_key="sk-xxx")
|
||||
1. GET /auth-recipes/openai → recipe(含 required_secrets, inject)
|
||||
2. 對應 required_secrets[0].key = "openai_api_key"
|
||||
3. crypto.py 用 encryption_key AES-GCM 加密 "sk-xxx"
|
||||
4. POST /credentials → { name: "openai_api_key", encrypted, iv }
|
||||
5. 本地 _cred_cache["openai_api_key"] = "sk-xxx"(供 bind() 用)
|
||||
|
||||
bind("openai")
|
||||
1. GET /auth-recipes/openai → recipe.inject.header = { Authorization: "Bearer {{secret.openai_api_key}}" }
|
||||
2. 用 _cred_cache["openai_api_key"] 替換 template → "Bearer sk-xxx"
|
||||
3. 回傳 AuthenticatedClient(base_url="https://api.openai.com/v1", headers={"Authorization": "Bearer sk-xxx"})
|
||||
```
|
||||
|
||||
**注意**:`bind()` 依賴 `setup()` 在同一 session 建立的 `_cred_cache`。跨 session 使用時(credential 已上傳但 cache 不存在),`bind()` 無法解析 template — 此時 `get_token()` 也無法返回值。**這是已知限制,封測期間先接受。** 長期解法是 server 提供 `/credentials/:name/secret` 解密端點(u6u-core/credentials 已有)。
|
||||
|
||||
### 關鍵差異:crypto.py 的定位
|
||||
|
||||
`crypto.py` 只做 **加密**(encrypt),不做解密。
|
||||
功能等同 `u6u-core/credentials/src/actions/crypto.ts` 的 `encrypt()` 函數。
|
||||
解密只在 server 端發生(cypher-executor 的 `credential-injector.ts` 或 `u6u-core/credentials/getCredentialSecret.ts`)。
|
||||
|
||||
---
|
||||
|
||||
## JS/TS SDK(`arcrun/js-sdk/`)
|
||||
|
||||
### 目錄結構
|
||||
|
||||
```
|
||||
arcrun/js-sdk/
|
||||
├── package.json ← name TBD(arcrun vs @arcrun/sdk),tsup build
|
||||
├── tsconfig.json ← ES2020, NodeNext
|
||||
└── src/
|
||||
├── index.ts ← export class Arcrun
|
||||
├── crypto.ts ← Web Crypto API AES-GCM encrypt(client 端)
|
||||
├── creds.ts ← CredentialsClient(push/list/delete)
|
||||
├── auth.ts ← AuthClient(setup/bind/getToken/listServices)
|
||||
└── workflows.ts ← WorkflowClient(run/push/list/delete)
|
||||
```
|
||||
|
||||
### API 與 Python SDK 對等
|
||||
|
||||
```typescript
|
||||
import { Arcrun } from 'arcrun' // or '@arcrun/sdk'
|
||||
|
||||
const client = new Arcrun() // reads ARCRUN_API_KEY from env
|
||||
|
||||
await client.auth.setup('openai', { api_key: 'sk-xxx' })
|
||||
const oai = await client.auth.bind('openai')
|
||||
const models = await (await oai.get('/models')).json()
|
||||
|
||||
const token = await client.auth.getToken('openai')
|
||||
const services = await client.auth.listServices()
|
||||
|
||||
await client.creds.push('my_token', 'value')
|
||||
const names = await client.creds.list()
|
||||
|
||||
const result = await client.workflows.run('my-flow', { email: 'user@example.com' })
|
||||
```
|
||||
|
||||
### Build 產物
|
||||
|
||||
```
|
||||
dist/
|
||||
├── index.js ← ESM
|
||||
├── index.cjs ← CJS
|
||||
├── index.d.ts ← TypeScript 型別
|
||||
└── index.d.cts
|
||||
```
|
||||
|
||||
### Crypto 實作
|
||||
|
||||
使用 Web Crypto API(`crypto.subtle`),相容 Node 18+ / browsers / CF Workers / Deno:
|
||||
|
||||
```typescript
|
||||
async function encrypt(plaintext: string, hexKey: string): Promise<{ encrypted: string; iv: string }> {
|
||||
const key = await crypto.subtle.importKey('raw', hexToBytes(hexKey), { name: 'AES-GCM' }, false, ['encrypt']);
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, new TextEncoder().encode(plaintext));
|
||||
return { encrypted: toBase64(ciphertext), iv: toBase64(iv.buffer) };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## arcrun.dev 網站
|
||||
|
||||
### 現有狀態(`arcrun/landing/`)
|
||||
|
||||
已完成:
|
||||
- [x] `/` — Hero + Code Demo(Python/JS/HTTP tabs)
|
||||
- [x] `/login` — Google + GitHub OAuth 按鈕(前端 OK,需設 OAuth secrets)
|
||||
- [x] `/dashboard` — API Key 查看/Copy/Rotate/Revoke(依賴 `/me` API)
|
||||
- [x] `/integrations` — 20 個 recipe 靜態卡片
|
||||
- [x] `/api-docs` — Swagger UI CDN 嵌入
|
||||
- [x] `middleware.ts` — 保護 `/dashboard`(未登入 → `/login`)
|
||||
- [x] Cloudflare Pages 部署
|
||||
|
||||
待完成:
|
||||
- [ ] OAuth secrets 設定(`GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` / `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`)
|
||||
- [ ] `/components` 頁面(零件列表 — 21 個 WASM 零件的 input/output/config_example)
|
||||
- [ ] 首頁 code demo 更新為三種使用方式(CLI / Python / JS)
|
||||
- [ ] 登入流程真實驗證
|
||||
|
||||
### 新增頁面:`/components`
|
||||
|
||||
```
|
||||
/components
|
||||
├── 零件卡片(21 個)
|
||||
│ ├── canonical_id
|
||||
│ ├── display_name
|
||||
│ ├── description
|
||||
│ ├── input_schema(required / optional 欄位)
|
||||
│ ├── output_schema
|
||||
│ ├── credentials_required(if any)
|
||||
│ └── config_example(YAML code block)
|
||||
└── 分類篩選(邏輯 / API / 控制流)
|
||||
```
|
||||
|
||||
資料來源:靜態嵌入(從 `registry/components/*/component.contract.yaml` 在 build 時讀取),不依賴 runtime API。
|
||||
|
||||
### OAuth 設定(待 richblack 操作)
|
||||
|
||||
需要在 Cloudflare Worker 設定以下 secrets:
|
||||
|
||||
```bash
|
||||
wrangler secret put GOOGLE_CLIENT_ID --name arcrun-cypher-executor
|
||||
wrangler secret put GOOGLE_CLIENT_SECRET --name arcrun-cypher-executor
|
||||
wrangler secret put GITHUB_CLIENT_ID --name arcrun-cypher-executor
|
||||
wrangler secret put GITHUB_CLIENT_SECRET --name arcrun-cypher-executor
|
||||
wrangler secret put SESSION_SIGNING_SECRET --name arcrun-cypher-executor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## server 端需要的修改
|
||||
|
||||
### cypher-executor 修改(最小化)
|
||||
|
||||
目前 `POST /credentials` 端點(`routes/credentials.ts`)接收 `{ name, encrypted, iv }` 後直接存 KV。
|
||||
|
||||
SDK 需要的改動:
|
||||
|
||||
1. **`GET /auth-recipes` 回應格式**:目前 list 端點回 `{ recipes: [...] }` 但 recipe 的 `service` 欄位是 key — SDK 已在 list_services() 正確處理 ✅
|
||||
|
||||
2. **`GET /auth-recipes/:service` 回應格式**:目前回 `{ success: true, recipe: {...} }` — SDK 需讀 `body.recipe` 而非 body 本身 ✅
|
||||
|
||||
3. **`POST /credentials` 不需改動** — SDK 自己做 AES-GCM 加密後送 `{ name, encrypted, iv }` ✅
|
||||
|
||||
4. **未來**:新增 `GET /credentials/:name/secret` 端點(解密返回 plaintext),讓跨 session 的 `bind()` 能工作。但此端點在 `u6u-core/credentials/src/actions/getCredentialSecret.ts` 已有實作 — 需要在 cypher-executor 整合或 Service Binding 到 u6u-credentials Worker。**封測後再做。**
|
||||
|
||||
---
|
||||
|
||||
## 不做的事(明確排除)
|
||||
|
||||
- ❌ 不在 SDK 裡做 workflow 解析或 YAML 處理 — 那是 CLI 的職責
|
||||
- ❌ 不在 SDK 裡做 server-side 解密 — 解密只在 server 端
|
||||
- ❌ 不建新的 credentials Worker — 用現有的
|
||||
- ❌ 不建新的 KV namespace — 用現有的 CREDENTIALS_KV
|
||||
- ❌ 不改 cypher-executor 的 credential-injector.ts — 那已經完成且測試通過
|
||||
|
||||
---
|
||||
|
||||
## 實作順序
|
||||
|
||||
```
|
||||
Phase 1:Python SDK 重建 + 測試
|
||||
1.1 重建 arcrun/python-sdk/(按本 SDD 的結構)
|
||||
1.2 修正上次的 bug:recipe 回應 wrapper、inject key "header" vs "headers"、secret key mapping
|
||||
1.3 對 cypher.arcrun.dev live 測試全部 API
|
||||
1.4 本地安裝測試(pip install -e .)
|
||||
|
||||
Phase 2:JS SDK 重建 + 測試
|
||||
2.1 重建 arcrun/js-sdk/(按本 SDD 的結構)
|
||||
2.2 同步修正 Python SDK 發現的所有 recipe 格式問題
|
||||
2.3 build(tsup)+ 本地測試
|
||||
|
||||
Phase 3:arcrun.dev 網站補完
|
||||
3.1 新增 /components 頁面
|
||||
3.2 更新首頁 code demo(三種使用方式)
|
||||
3.3 OAuth secrets 設定(需 richblack 操作 GCP / GitHub)
|
||||
3.4 登入流程驗證
|
||||
|
||||
Phase 4:GitHub README + 發布
|
||||
4.1 更新 arcrun/README.md — 三種 Quick Start
|
||||
4.2 pip publish(arcrun)
|
||||
4.3 npm publish(TBD 套件名)
|
||||
4.4 最終驗證:從零開始 pip install / npm install / 打 API
|
||||
```
|
||||
@@ -0,0 +1,132 @@
|
||||
# Design:MCP 統一帳號來源 — 單一 remote MCP + .env 切 MCP URL
|
||||
|
||||
> 2026-06-06 richblack 拍板(推翻本檔初版的「①工具帶參數 / ②自架 worker / ③平台 routing」三方案)。
|
||||
> `sdk-and-website/design.md` 的單檔補充(規則 02 §4.3 允許)。
|
||||
> 來源:壓測報告 §5.1/§5.2/§5.4(薄殼原則)+ richblack 對話釐清。
|
||||
> 對應鐵律:`.claude/rules/07-thin-shell.md` §4(統一帳號來源)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 問題(壓測 §5.2)
|
||||
|
||||
CLI 已能讀三層 config(env > 專案 `.arcrun.yaml` > 全域)切帳號;**MCP 不能**——MCP 用 Cloudflare
|
||||
service binding 焊死平台 `arcrun-cypher-executor`,self-hosted 用戶用 MCP 連不到自己的 cypher。
|
||||
|
||||
**這違反薄殼鐵律**:「切換帳號」這能力做在了 CLI(介面層),MCP 沒跟上 → 證明它不在 API/共用層。
|
||||
|
||||
## 2. 關鍵洞見(richblack):不需要兩套 transport
|
||||
|
||||
> 「self-hosted 用戶也有 CF,把 MCP 放上網對他也沒困難。接案時進客戶專案讀 `.env` 連的是
|
||||
> 客戶專案的雲端 MCP — 這比保留 stdio + 雲端兩套更單純。」
|
||||
|
||||
推論:**所有人都用 remote Worker MCP,差別只在「連哪台 MCP」**。
|
||||
|
||||
- **我自己**:全域 config 的 `mcp_url` 指向我自己的 MCP Worker。
|
||||
- **接案幫客戶**:客戶資料夾 `.arcrun.yaml` / `.env` 放客戶的 `mcp_url`(那台 MCP 綁客戶 cypher)。
|
||||
進客戶資料夾 → 自動連客戶那套。
|
||||
- **SaaS 用戶**:不設 `mcp_url` → fallback 平台預設 MCP,AI 幫他帶 api_key。
|
||||
|
||||
→ **不需要 stdio 本機 MCP、不需要 transport 抽象層、不需要把 Worker 改成讀本機檔。**
|
||||
現有 remote HTTP Worker MCP 形態完全保留。
|
||||
|
||||
## 3. 薄殼原則怎麼落地
|
||||
|
||||
「身份解析(讀哪個帳號/哪台 MCP)」本質在 client(帳號設定是本機檔,cypher/MCP Worker 讀不到)。
|
||||
正解不是「API 去讀 .env」(做不到),而是:
|
||||
|
||||
> **MCP URL 與 cypher URL 一樣,由同一份 config 解析模組(env > 專案 > 全域)決定。**
|
||||
> CLI 讀 `cypher_url`,Claude Code 的 MCP 連線讀 `mcp_url`,**同一份 `.arcrun.yaml` / env、同一個解析邏輯**。
|
||||
> 「切換帳號」這能力只實作一次(config 解析),不再綁死在 CLI。
|
||||
|
||||
### 接線:mcp_url → Claude Code 的 MCP 設定
|
||||
Claude Code 看 `.mcp.json`(專案層)決定連哪台 MCP。所以需要一個東西把
|
||||
「arcrun config 解析出的 `mcp_url`」寫進專案 `.mcp.json`:
|
||||
|
||||
- **`acr mcp-setup`**(新指令):依三層 config 解析出 `mcp_url`,在 cwd 寫 / 更新 `.mcp.json`。
|
||||
- `acr init` 順帶呼叫(裝好就有)。
|
||||
- 「切帳號」= 在客戶資料夾跑 `acr mcp-setup`(讀該資料夾的 `.arcrun.yaml`)→ 產對的 `.mcp.json`。
|
||||
|
||||
## 4. 動到的檔案
|
||||
|
||||
| 檔案 | 動作 | 狀態 |
|
||||
|---|---|---|
|
||||
| `cli/src/lib/config.ts` | `ArcrunConfig` 加 `mcp_url`;`ENV_MAP` 加 `ARCRUN_MCP_URL`;`resolveConfigSources` 含 mcp_url;新增 `DEFAULT_MCP_URL` + `getMcpUrl()` | ✅ 完成 |
|
||||
| `cli/src/commands/mcp-setup.ts`(新增) | `acr mcp-setup`:依 `getMcpUrl()` 寫專案 `.mcp.json` | 進行中 |
|
||||
| `cli/src/index.ts` | 註冊 `acr mcp-setup` | 進行中 |
|
||||
| `cli/src/commands/init.ts` | init 尾端順帶 `acr mcp-setup`(裝好即有) | 進行中 |
|
||||
| **`arcrun/mcp/`(新目錄)** | MCP 從 sibling repo `matrix/arcrun-mcp` **搬進主庫**(與 cli/ 並列)。形態不變(remote Worker),只是進主庫 → 同 repo、同 deploy 掃描、與 cypher API 對齊 | 進行中 |
|
||||
| `mcp/wrangler.toml` | name/route 對齊 arcrun 部署慣例(`workers_dev=true`,deploy.yml/local-deploy.sh 自動掃到) | 進行中 |
|
||||
|
||||
## 5. MCP 搬進 arcrun/mcp/(不改形態,只進主庫)
|
||||
|
||||
- **搬 verbatim**:`src/tools/*`、`src/lib/*`、`src/types.ts`、`src/mcp-handler.ts`、`src/index.ts`、`tests/`。
|
||||
- **形態不變**:仍是 Hono + `WebStandardStreamableHTTPServerTransport` 的 remote Worker(不加 stdio)。
|
||||
- **進主庫的理由**:(a) 與 cypher-executor 同 repo → 改 API 時 MCP 薄殼同步可見、一起 review;
|
||||
(b) 被 deploy 掃描(wrangler.toml)自動部署,納入 release.feature「推送=全部到位」;
|
||||
(c) self-host 用戶 codeload 主庫即得 MCP,能部署自己的 MCP Worker。
|
||||
- **sibling repo `matrix/arcrun-mcp` 去留**:搬進來後,原 sibling 為歷史/過渡;新開發在 `arcrun/mcp/`。
|
||||
|
||||
## 5.5 self-hosted MCP 認證對齊(2026-06-14,HANDOFF §3b)
|
||||
|
||||
> 症狀(mira CC 實測 `arcrun-mcp.leo21c.workers.dev`):self-hosted 用 namespace 明碼連 MCP 一律 401;
|
||||
> CLL 全通。根因:MCP `middleware/partner-auth.ts` 把 Bearer 拿去 KBDB `/partners/:token/info`
|
||||
> **驗證**,namespace 明碼非註冊 partner → 401。而 cypher-executor 的 `X-Arcrun-API-Key`
|
||||
> **不驗證、直接當分區 key**(webhooks-named.ts triggerNamed)→ 這就是「CLI 通、MCP 401」的分歧。
|
||||
|
||||
**決策:① MCP self-hosted 繞 partner 驗證 + ② mcp-setup 寫 headers(兩者缺一不可)。**
|
||||
|
||||
| | 修法 | 為何必須 |
|
||||
|---|---|---|
|
||||
| ① | `partner-auth.ts`:`MULTI_TENANT === 'false'` 時 Bearer = namespace 明碼直接當 `org_namespace`,不打 KBDB partner 驗證(對齊 cypher 的 opaque-key 模型)。官方 SaaS(不設/`"true"`)行為不變 → 官方與 self-host 共用同一份程式碼 | 只做②也沒用:partner 驗證仍擋明碼 |
|
||||
| ② | `mcp-setup.ts`:把 `config.api_key`(self-hosted 存 namespace 明碼)寫進 `.mcp.json` 的 `headers.Authorization: Bearer …`(與 CLI 同一份身份,rule 07 §4) | 只做①也沒用:裸 `.mcp.json` 不送任何 header |
|
||||
|
||||
判定旗標:worker `[vars] MULTI_TENANT`(與 cypher 同名)。
|
||||
|
||||
### 5.5.1 部署注入修補(2026-06-15,HANDOFF §3b-2)
|
||||
|
||||
> 症狀:①②code 正確、官方帳號測綠,但 mira 推 leo21c 端到端**仍 401**。
|
||||
> **根因(非 code bug,是部署注入缺口)**:partner-auth.ts `if (c.env.MULTI_TENANT === 'false')`
|
||||
> 邏輯對,但 worker env 裡 `MULTI_TENANT === undefined`——因為:
|
||||
> - mcp/wrangler.toml 的 `MULTI_TENANT` 原本是**註解掉的**;
|
||||
> - `cli/src/lib/deploy.ts` 的 `injectWranglerConfig` 部署時注入了 KV id / WORKER_SUBDOMAIN / D1 id,
|
||||
> **但沒注入 MULTI_TENANT** → 部署後 `c.env.MULTI_TENANT===undefined ≠ 'false'` → 走 partner-key → 401。
|
||||
> - config 源頭早有(init.ts `multi_tenant:false` + `mode:'self-hosted'`),只是沒被注進 worker。
|
||||
>
|
||||
> **只取消註解 mcp/wrangler.toml 不夠**——那只修「手動 fork」,沒修「acr update 自動部署」(mira 走後者)。
|
||||
> 根因要修在 deploy.ts 注入邏輯。
|
||||
|
||||
**修法(方案①:注 vars 非 secret,符合 self-hosted 零填寫契約)**:
|
||||
|
||||
| 檔案 | 動作 | 狀態 |
|
||||
|---|---|---|
|
||||
| `cli/src/lib/deploy.ts` | `DeployContext` 加 `selfHosted?`;新增 export `injectMultiTenant(toml)`(處理 active/註解/無行三態,加進 `[vars]`);`injectWranglerConfig` 在 `selfHosted` 時呼叫——與 WORKER_SUBDOMAIN/KV 注入同層級 | ✅ 完成 |
|
||||
| `cli/src/commands/init.ts` | deployCtx 帶 `selfHosted: true`(init 本就是 --self-hosted 分支) | ✅ 完成 |
|
||||
| `cli/src/commands/update.ts` | ctx 帶 `selfHosted: config.mode==='self-hosted' \|\| config.multi_tenant===false`(mira 走這條) | ✅ 完成 |
|
||||
| `mcp/wrangler.toml` | `# [vars]`/`# MULTI_TENANT` 改 active `[vars]`(官方不含 MULTI_TENANT=多租戶;注入走 case-3 加行,結構正確在 [vars] 下) | ✅ 完成 |
|
||||
|
||||
**本地驗注入(dry-run,真實 export 函式)**:mcp / cypher-executor 注入後各恰 1 行 active `MULTI_TENANT = "false"` 且在 active `[vars]` 之下 → ✓ PASS。cli tsc exit 0。
|
||||
**端到端(交棒回 mira)**:mira 在 leo21c 重跑 `acr update` 重部 MCP worker(這次帶 MULTI_TENANT=false)→ `curl -H "Authorization: Bearer leo" https://<mcp>.leo21c.workers.dev/mcp` 應 **200 非 401**。官方帳號測不到(不設 MULTI_TENANT)。
|
||||
|
||||
### 5.5.0 原始 code 修法(2026-06-14,①②)
|
||||
|
||||
| 檔案 | 動作 | 狀態 |
|
||||
|---|---|---|
|
||||
| `mcp/src/types.ts` | `Env` 加 `MULTI_TENANT?` | ✅ 完成 |
|
||||
| `mcp/src/middleware/partner-auth.ts` | self-hosted 分支:Bearer 明碼直接當 org_namespace | ✅ 完成 |
|
||||
| `cli/src/commands/mcp-setup.ts` | `.mcp.json` 寫 `headers.Authorization` | ✅ 完成 |
|
||||
|
||||
驗收:mira 用 namespace 明碼連 self-hosted MCP(待 leo21c 部署後實測,HANDOFF §3);官方 SaaS MCP partner-key 路徑回歸不變。
|
||||
|
||||
## 6. 不在範圍(明確排除)
|
||||
|
||||
- ❌ 不加 stdio transport(richblack §2:不需要)。
|
||||
- ❌ 不把 init / config 搬進 MCP(init 是本機 CF 部署動作,MCP Worker 做不到)。
|
||||
- ❌ 不在 MCP 重實作 credential 加密 / workflow 執行(server 職責,rule 02 §3.4)。
|
||||
|
||||
## 7. 驗收(客觀證據,mindset §7)
|
||||
|
||||
1. `cli/` + `mcp/` 各自 `tsc --noEmit` exit 0。
|
||||
2. `acr mcp-setup` 在含 `.arcrun.yaml`(mcp_url=X)的資料夾 → 產出 `.mcp.json` 指向 X;無 mcp_url → 指向 DEFAULT_MCP_URL。
|
||||
3. `acr config --where` 顯示 mcp_url 來源層。
|
||||
4. `mcp/` 被 deploy 掃描掃到(`find wrangler.toml` 命中)。
|
||||
5. self-hosted 用戶在客戶資料夾 `acr mcp-setup` → Claude Code 連客戶 MCP(端到端待 richblack 實測)。
|
||||
@@ -0,0 +1,131 @@
|
||||
# Requirements: arcrun SDK Libraries + Website
|
||||
|
||||
## Introduction
|
||||
|
||||
arcrun 目前有三個使用介面:
|
||||
1. **CLI**(`acr` 指令)— 已完成,用 YAML 定義 workflow 並推送執行
|
||||
2. **Python / JS SDK lib**(本次新增)— `pip install arcrun` / `npm install arcrun`,讓開發者在寫程式時直接用 arcrun 功能
|
||||
3. **arcrun.dev 網站**(本次完成)— 登入取得 API Key、管理 Key、瀏覽零件 / recipe 列表
|
||||
|
||||
**核心原則**:SDK lib 是 `cypher.arcrun.dev` HTTP API 的 thin wrapper。所有業務邏輯(加解密、credential 注入、workflow 執行)都在 server 端完成。Client 端不重做 server 已有的邏輯。
|
||||
|
||||
**現有基礎設施**(不重建,直接使用):
|
||||
- `cypher.arcrun.dev`:cypher-executor Worker(workflow 執行、credential 管理、auth recipe、webhook)
|
||||
- `u6u-core/credentials`:credential Worker(AES-GCM 加解密)— arcrun/credentials 是其 cherry-pick
|
||||
- `arcrun/cli`:CLI 工具(已發布 npm `arcrun@1.1.0`)
|
||||
- `arcrun/landing`:Next.js 前端(已部署 Cloudflare Pages,有 hero/login/dashboard/integrations 骨架)
|
||||
|
||||
---
|
||||
|
||||
## Glossary
|
||||
|
||||
- **SDK lib**:Python / JS 套件,wrapping `cypher.arcrun.dev` HTTP API,安裝後可在程式碼中直接使用
|
||||
- **auth.setup()**:上傳一個服務的 credential(如 Notion token、OpenAI API Key)到 arcrun
|
||||
- **auth.bind()**:取回已設定服務的 pre-authenticated HTTP client
|
||||
- **auth.get_token()**:取回某服務的 raw token(escape hatch,給官方 SDK 用)
|
||||
- **workflows.run()**:觸發已部署的 workflow
|
||||
- **workflows.push()**:上傳 workflow 定義
|
||||
- **Recipe**:描述「如何對某服務認證」的 YAML 設定,存在 RECIPES KV
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1:Python SDK(`pip install arcrun`)
|
||||
|
||||
**User Story:** As a Python 開發者, I want `pip install arcrun` 後在程式碼中使用 arcrun, so that 不用離開寫程式環境就能串接 20+ 服務。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Python SDK SHALL 以 `arcrun` 套件名發布到 PyPI,支援 Python 3.10+。
|
||||
2. THE SDK SHALL 提供以下 API:
|
||||
- `Arcrun(api_key=, base_url=)` — 建構 client,api_key 支援從環境變數 `ARCRUN_API_KEY` 或 `~/.arcrun/config.yaml` 自動讀取
|
||||
- `client.health()` — 健康檢查
|
||||
- `client.auth.list_services()` — 列出可用 auth recipe 服務
|
||||
- `client.auth.setup(service, **kwargs)` — 上傳 credential
|
||||
- `client.auth.bind(service)` — 取得 pre-authenticated HTTP client
|
||||
- `client.auth.get_token(service)` — 取得 raw token
|
||||
- `client.creds.push(name, value)` — 上傳加密 credential
|
||||
- `client.creds.list()` — 列出 credential 名稱
|
||||
- `client.creds.delete(name)` — 刪除 credential
|
||||
- `client.workflows.run(name, input)` — 觸發 workflow
|
||||
- `client.workflows.push(name, graph)` — 上傳 workflow
|
||||
- `client.workflows.list()` — 列出已部署 workflow
|
||||
3. THE SDK 的 credential 加密 SHALL 在 client 端完成(使用 `cryptography` 套件 AES-GCM),然後以 `POST /credentials` 上傳加密後的 `{ name, encrypted, iv }` 到 server。
|
||||
4. THE `auth.bind()` SHALL 從 server 取得 auth recipe 的 inject template,在 client 端用 cache 的 plaintext 值填入,回傳 pre-configured `httpx.Client`。
|
||||
5. THE SDK SHALL 使用 `httpx` 做 HTTP client(async 版使用 `httpx.AsyncClient`)。
|
||||
6. THE SDK 位置 SHALL 為 `arcrun/python-sdk/`,build 系統用 `hatchling`(`pyproject.toml`)。
|
||||
|
||||
---
|
||||
|
||||
### Requirement 2:JavaScript/TypeScript SDK(`npm install arcrun`)
|
||||
|
||||
**User Story:** As a JS/TS 開發者, I want `npm install arcrun` 後在程式碼中使用 arcrun, so that 可以嵌入現有 Node.js / Deno / Cloudflare Workers 專案。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE JS SDK SHALL 以 `arcrun` 套件名發布到 npm,提供 ESM + CJS 雙格式 + TypeScript 型別定義。
|
||||
2. THE SDK SHALL 提供與 Python SDK 對等的 API(camelCase 版):
|
||||
- `new Arcrun({ apiKey?, baseUrl? })` — 讀 `process.env.ARCRUN_API_KEY`
|
||||
- `client.health()` — 回傳 `Promise<unknown>`
|
||||
- `client.auth.listServices()` / `setup()` / `bind()` / `getToken()`
|
||||
- `client.creds.push()` / `list()` / `delete()`
|
||||
- `client.workflows.run()` / `push()` / `list()` / `delete()`
|
||||
3. THE SDK 的 credential 加密 SHALL 使用 Web Crypto API(`crypto.subtle` AES-GCM),相容 Node 18+、browsers、Cloudflare Workers、Deno。
|
||||
4. THE `auth.bind()` SHALL 回傳一個有 `get/post/put/delete/patch` 方法的 `AuthenticatedClient`,base URL + auth headers 已配置。
|
||||
5. THE SDK SHALL 使用原生 `fetch()` API,不依賴外部 HTTP client 套件。
|
||||
6. THE SDK 位置 SHALL 為 `arcrun/js-sdk/`,build 用 `tsup`(ESM + CJS + DTS),`tsconfig.json` target ES2020 + NodeNext module。
|
||||
7. THE JS SDK 套件名與 CLI 套件名衝突(都叫 `arcrun`),SHALL 使用 `@arcrun/sdk` 或由 richblack 決定套件名。
|
||||
|
||||
---
|
||||
|
||||
### Requirement 3:arcrun.dev 網站完成
|
||||
|
||||
**User Story:** As a 潛在用戶, I want 在 arcrun.dev 上登入取得 API Key、瀏覽零件和 recipe 列表, so that 我可以評估 arcrun 是否符合需求並立即開始使用。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE 網站 SHALL 在 `arcrun.dev` 提供以下頁面:
|
||||
- `/` — 首頁 Hero + 三種使用方式(CLI / Python / JS)
|
||||
- `/login` — Google + GitHub OAuth 登入
|
||||
- `/dashboard` — 登入後顯示 API Key(查看/Copy/Rotate/Revoke)
|
||||
- `/integrations` — 列出 20 個 auth recipe 服務,可按分類篩選
|
||||
- `/components` — 列出所有零件(21 個 WASM 零件),顯示 input/output schema、config_example
|
||||
- `/api-docs` — Swagger UI,可直接試打 API
|
||||
2. THE 登入 SHALL 使用 Google + GitHub OAuth,流程走 `cypher.arcrun.dev` 的 `/auth/*` 端點。
|
||||
3. THE 登入後 SHALL 自動對該 email 呼叫 `/register` 取得 API Key(若已有則取回現有 key)。
|
||||
4. THE `/dashboard` SHALL 允許 Rotate(生成新 key)、Revoke(標記失效)、Copy to clipboard。
|
||||
5. THE 網站 SHALL 部署在 Cloudflare Pages(現有 `arcrun/landing`),使用 Next.js App Router。
|
||||
6. THE 首頁 code demo 區 SHALL 包含三個 tab:Python、JavaScript、HTTP/curl,展示三種使用方式。
|
||||
|
||||
---
|
||||
|
||||
### Requirement 4:GitHub README 更新
|
||||
|
||||
**User Story:** As a GitHub 訪客, I want README 清楚說明三種使用方式, so that 我能選擇最適合的方式開始用 arcrun。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE `arcrun/README.md` SHALL 包含三種 Quick Start:
|
||||
- **CLI**:`npm i -g arcrun && acr init && acr push workflow.yaml && acr run`
|
||||
- **Python**:`pip install arcrun && from arcrun import Arcrun && ...`
|
||||
- **JavaScript**:`npm install arcrun && import { Arcrun } from 'arcrun' && ...`
|
||||
2. THE README SHALL 包含完整零件列表(21 個)和 auth recipe 列表(20 個服務)。
|
||||
3. THE README SHALL 連結到 `arcrun.dev`(取得 API Key)和 Swagger UI(API 文件)。
|
||||
|
||||
---
|
||||
|
||||
### Requirement 5:SDK 發布
|
||||
|
||||
**User Story:** As a SDK 使用者, I want 公開安裝並直接使用, so that 不需要從原始碼 build。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Python SDK SHALL 發布到 PyPI,`pip install arcrun` 可安裝。
|
||||
2. THE JS SDK SHALL 發布到 npm,`npm install arcrun`(或 `@arcrun/sdk`)可安裝。
|
||||
3. THE 發布前 SHALL 完成以下測試(對 `cypher.arcrun.dev` live API):
|
||||
- `health()` ✅
|
||||
- `auth.list_services()` ✅
|
||||
- `auth.setup()` + `auth.bind()` ✅(至少一個 static_key 服務如 openai)
|
||||
- `creds.push()` + `creds.list()` ✅
|
||||
- `workflows.list()` ✅
|
||||
@@ -0,0 +1,375 @@
|
||||
# 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)→ 重部署成功。
|
||||
|
||||
---
|
||||
|
||||
## 7.5 壓測修正(2026-06-04):fork 帳號裝不起來的四個阻斷項
|
||||
|
||||
> 來源:`docs/壓測報告.md`(壓測者:一個「完全不懂程式」的人 + AI 自架)。
|
||||
> §3/§6 的 installer 流程在**非官方 CF 帳號**上實測**無法完成部署**。根因與修法如下,全部在本 SDD 範圍內(installer 補洞 + R2 dead storage 清除),不是新架構。
|
||||
|
||||
### 根因
|
||||
各 worker 的 `wrangler.toml`(repo 內)寫死了**只有 arcrun 官方帳號才有的綁定**:
|
||||
- `[[routes]] zone_name = "arcrun.dev"`(每個零件 + cypher-executor)→ fork 沒有此 zone → `wrangler deploy` 找不到 zone 失敗(阻斷 #1)
|
||||
- `[[r2_buckets]]` + `[ai]`(cypher-executor)→ fork 未必有 R2/AI(阻斷 #2)
|
||||
- R2 是 dead storage,卻因 CF R2 首次啟用**強制綁信用卡** → 違背「開源免費自架」(阻斷 #3)
|
||||
- R2 bucket 名 `WASM_BUCKET`(大寫+底線)違反 R2 命名規則 → 證明該碼從未成功跑過(阻斷 #4)
|
||||
|
||||
### 修法(只動 `cli/`,**不刪 repo 內 worker toml**)
|
||||
**關鍵判準**:repo 內 toml 的 `[[routes]]` 是**官方 prod CI 部署**需要的(對外開放零件),直接刪會破壞官方部署。
|
||||
正解是 `deploy.ts` 在**注入暫存副本**時 strip(§3 step 4 本就「不改用戶 repo,改暫存副本」),因為 deploy.ts 只在 self-hosted 路徑跑。
|
||||
|
||||
| 檔案 | 改動 |
|
||||
|---|---|
|
||||
| `cli/src/lib/deploy.ts` | 新增 `stripOfficialOnlyBindings()`:注入時移除 `[[routes]]`/`zone_name`/`[[r2_buckets]]`/`[ai]`。worker 靠 `workers_dev=true` 對外。移除 `REQUIRED_R2_BUCKET` |
|
||||
| `cli/src/commands/init.ts` | 移除 `ensureR2Bucket` 呼叫 + `wasm_bucket` config + token 提示去掉「R2 Edit」+ 結果文案改「7 KV,無需綁卡」 |
|
||||
| `cli/src/lib/cf-api.ts` | 移除 `ensureR2Bucket()` 方法 |
|
||||
| `cli/src/lib/config.ts` | 移除 `wasm_bucket?` 欄位 |
|
||||
| `cli/src/commands/validate.ts` | 錯誤文案「WASM_BUCKET 中找不到」→「registry 中找不到零件」(去命名誤導) |
|
||||
|
||||
→ self-hosted 改為**只需 Workers + KV**(皆免費額度、不綁卡),回歸開源免費承諾。
|
||||
|
||||
### 客觀證據(mindset §7)
|
||||
- `stripOfficialOnlyBindings()` 對真實 `auth_static_key`/`cypher-executor` toml 實測:routes/R2/AI 全 strip,name/workers_dev/kv_namespaces/vars 全保留。
|
||||
- `npx tsc --noEmit` CLI 全綠;`grep R2/WASM_BUCKET src/` 零殘留。
|
||||
|
||||
### 報告其他項(本次未動,記錄待辦)
|
||||
- **#7 init 只能互動式**(readline,無 flag/env)→ AI/CI 不友善。建議支援 `--account-id`/`--api-token` 或讀 env。**待 richblack 決定**。
|
||||
- **#8 無多帳號/專案 scope**(config 寫死全域 `~/.arcrun/config.yaml`)→ 接案者痛點。報告建議專案層 `.arcrun.yaml` 覆蓋全域(git config 模式)。**屬新功能(change),需 richblack 確認後另開 SDD**。
|
||||
- **#5/#6 權限前置驗證 + 錯誤訊息**:移除 R2 後 R2 權限問題自然消失;KV 前置一次驗證可後續優化。
|
||||
|
||||
## 7.6 壓測修正(2026-06-06):seed 下沉成 API 行為(推翻 §5「seed 資料放 CLI」)
|
||||
|
||||
> 來源:壓測報告 §4.1(seed 機制對所有 self-host 用戶都壞)+ §5.5(薄殼原則)。richblack 點名「薄殼原則寫入鐵律」。
|
||||
> **此節推翻 §5「種子資料放 CLI 端」+ §8「只動 cli/」的範圍**——seed 改為 API 行為(rule 07)。
|
||||
|
||||
### 推翻什麼
|
||||
§5 原寫「種子資料放 `cli/src/lib/api-recipe-seeds.ts`(installer 職責)」。壓測證明這是 §4.1 bug 的結構根因:
|
||||
seed 邏輯寫在 CLi `init.ts`(迴圈 POST + `deployFullyOk` gate),registry 一個無關 worker 失敗就連坐讓 seed 永遠被跳過;且 init 從不 seed auth recipe。
|
||||
|
||||
### 改法(seed = API 能力,薄殼只呼叫一次)
|
||||
| 檔案 | 改動 |
|
||||
|---|---|
|
||||
| 新增 `cypher-executor/src/routes/init-seed.ts` | `POST /init/seed`:一次灌 API recipe + auth recipe(冪等、直寫 KV、誠實計數)|
|
||||
| 新增 `cypher-executor/src/lib/api-recipe-seeds.ts` | 種子資料**唯一真相移到 server**(rule 02 §2.2 hook 對 `*-seeds.ts` 整類加例外,richblack 2026-06-06 授權)|
|
||||
| 刪 `cli/src/lib/api-recipe-seeds.ts` | CLI 重複檔刪除(避免漂移)|
|
||||
| `cypher-executor/scripts/seed-api-recipes.ts` | import 改指 server 端;備援路徑 |
|
||||
| `cli/src/commands/init.ts` | `seedApiRecipes` 迴圈 → `callSeedEndpoint` 一次呼叫;移除 deployFullyOk gate |
|
||||
| `cli/src/commands/update.ts` | 重部署後也呼叫 `/init/seed`;改重解析全部 KV id |
|
||||
| `cli/src/lib/deploy.ts` | `REQUIRED_KV_NAMESPACES` 加 `SUBMISSIONS_KV`(修 registry 20/21,§4.1.1)|
|
||||
|
||||
### 對 §8「為何不違反鐵律」的修正
|
||||
§8 原寫「只動 cli/ + scripts/」。現在**也動 cypher-executor/src/**(新增 `/init/seed` route + 種子資料),
|
||||
但**不違反 rule 02 §2.2**:seed 端點只做「讀內嵌種子資料 → 寫 KV」的 routing/資料搬運,不解密/不簽章/不展開 template;
|
||||
種子的 endpoint 字串是資料宣告(`*-seeds.ts` 類豁免),真正 API 呼叫仍走零件路徑。符合薄殼原則(rule 07)。
|
||||
|
||||
## 7.7 壓測修正(2026-06-06):self-hosted 身份 = 明碼 namespace(不發 api_key)
|
||||
|
||||
> 來源:壓測報告 §7.2(seed 通了,但 creds push/push/runtime 全卡「缺少 api_key」)。
|
||||
> richblack 拍板:self-hosted 不需要「祕密 api_key」,因為它只需要 namespace(分區標籤),不需要認證(你的 cypher 在你自己的 CF,無「別人」會冒用)。
|
||||
|
||||
### 根因
|
||||
credential/部署/執行三條路徑全建在多租戶 `{api_key}:cred:{name}` 模型,但 self-hosted(單租戶)init 從不發 api_key →
|
||||
`creds push`/`push` 硬要 api_key 而退出;runtime webhook trigger 也要 `X-Arcrun-API-Key` header(公開表單帶不了)。
|
||||
|
||||
### 設計(richblack 2026-06-06)
|
||||
**self-hosted 的「api_key」= 明碼 namespace,用戶在 `.env` 自填。工具不生成、不 hash、不外傳。**
|
||||
- 兩欄(像 username/password,但都由用戶持有):
|
||||
- `NAMESPACE=leo`(明碼分區標籤,當 KV 前綴;**非密碼**)
|
||||
- `ENCRYPTION_KEY=<64+ hex>`(creds 加密金鑰,用戶自保管;須與 worker secret 一致)
|
||||
- **誠實限制**(mindset §7):namespace 明碼不提供認證。要防外部呼叫 → 對 webhook 加保護(mindset §6);arcrun 不做授權判斷(mindset §3)。
|
||||
- **零分叉**:SaaS 仍走 register 發的真 api_key;self-hosted 走 .env 的 NAMESPACE。**同一條 `api_key` 路徑、同一份 config 解析**,只是值來源不同。
|
||||
|
||||
### 改法
|
||||
| 檔案 | 改動 |
|
||||
|---|---|
|
||||
| `cli/src/lib/config.ts` | `ENV_MAP` 加 `NAMESPACE`/`ARCRUN_NAMESPACE`/`ENCRYPTION_KEY` → 對應 `api_key`/`encryption_key`;新增 `.env` 自動載入(就近往上找,shell > .env,無 dotenv 依賴)|
|
||||
| `cli/src/commands/creds.ts`、`push.ts` | self-hosted 缺值時,引導設 `.env` 的 NAMESPACE/ENCRYPTION_KEY(不再叫去 register)|
|
||||
| `cli/src/commands/init.ts` | self-hosted 結尾改提示「在 .env 設 NAMESPACE + ENCRYPTION_KEY」+「把同把 key wrangler secret put 進 worker;不想自己跑可明示同意我代設」|
|
||||
| `cypher-executor/src/routes/webhooks-named.ts` | 新增 `POST /webhooks/named/:ns/:name/trigger`(namespace 走 path,公開表單免 header);與 header 路徑共用 `triggerNamed`,不分叉 |
|
||||
| `cli/src/commands/push.ts` | self-hosted 輸出 path-based 公開 webhook URL(免 header)|
|
||||
|
||||
### 為何 namespace 進 URL path 安全
|
||||
namespace 是明碼標籤非密碼,放 path 不洩漏任何祕密。真正的祕密是 `ENCRYPTION_KEY`(在 worker secret + 用戶 .env,永不進 URL/path)。要防外部濫用 webhook 是「webhook 保護層」職責,非 namespace。
|
||||
|
||||
## 7.8 壓測修正(2026-06-08):onboarding 四缺陷 → pip 式自我修復安裝(CHANGE,待 review)
|
||||
|
||||
> 來源:Haiku 冷啟動壓測(test_arcrun/2/test_record.md)+ richblack 觀察。
|
||||
> **核心判準([[haiku-capable-is-design-goal]])**:Haiku 撞牆 = 介面缺陷,不是換強模型。
|
||||
> 暴露的四個缺陷都是 onboarding/AI-UX,非功能 bug。**richblack 要的是系統級正解,不是暫時解法/外掛 doctor。**
|
||||
|
||||
### 缺陷清單(壓測實證)
|
||||
|
||||
| # | 缺陷 | 根因 | Haiku 實際行為(撞牆) |
|
||||
|---|------|------|------|
|
||||
| D1 | **安裝不偵測環境**(缺 node/wrangler/CF登入/D1 任一就卡) | init 假設前置齊備,缺了不自己補也不引導 | Haiku 遇 D1 不存在 → **跑去讀原始碼自己想辦法建**(非技術用戶災難) |
|
||||
| D2 | **帳號 scope:AI 繞過 CLI 自己 curl 全域** | config 優先序**已實作**(專案>全域,config.ts:169-193),但 AI 不用 CLI 讀帳號、自己 curl 猜 → 打到全域 | Haiku curl 全域帳號 URL,非當前 project scope |
|
||||
| D3 | **MCP 掛不進不提示重啟** | 安裝完 project scope MCP 顯示無法用,沒告訴用戶「MCP 要重啟 client 才載入」 | 用戶以為壞了 |
|
||||
| D4 | (壓測舊版 1.3.0 無 D1 自動建;1.3.2 已修,但 D1↑ 的「偵測+冪等」更根本) | — | — |
|
||||
|
||||
### 正解:安裝流程內建偵測 + 自我修復 + 冪等(pip 式)—— richblack 2026-06-08 拍板
|
||||
|
||||
**`acr init`(安裝流程本身)= 像 pip:先偵測環境有什麼、版本對不對,才動手;缺什麼 Arcrun 自己負責裝。
|
||||
重跑會檢查後說「什麼也沒動」(冪等)。不另做 doctor——偵測是正規安裝流程的一部分。**
|
||||
|
||||
1. **偵測先於動作**:init 先檢查各前置(node / wrangler / CF 登入 / D1 / KV / migration)+ 版本 →
|
||||
缺的才裝/建、有的跳過、版本不對才升。**不是假設齊備直接動手**。
|
||||
2. **Arcrun 負責裝它需要的**:缺什麼是 Arcrun 的工作,不丟給 AI/用戶自己想辦法(D1 不存在 → init 自己建+套 migration,
|
||||
非讓 AI 讀原始碼)。需人類授權的(建 CF 資源、暴露)仍停下請示(mindset §7 非 TTY 不偽造同意)。
|
||||
3. **冪等可重跑**:重跑檢查後「什麼也沒動」(像 pip install 已裝),不報錯不重建。
|
||||
4. **AI 無腦入口 = 一個 GitHub 連結**:AI 讀 README/連結就照著走,**不繞、不讀原始碼、不自己 curl**。
|
||||
安裝指示集中在一處,AI 跟著做即可。
|
||||
|
||||
### 配套介面(讓 AI 不需也不該猜)
|
||||
|
||||
- **`acr whoami` / `acr config show`**(D2 修法):印「當前生效帳號 + scope 來源(project/global/env)」。
|
||||
AI 無腦問 CLI 拿正確帳號,**不自己 curl**。同步加 **MCP `arcrun_whoami`**(薄殼一致,rule 07 §5)。
|
||||
→ 治本 D2:不是 config 沒實作(已對),是 AI 該用工具讀、不該繞 CLI 猜帳號。
|
||||
- **MCP 掛載後提示重啟**(D3 修法):`acr init` / `acr mcp-setup` 寫完 `.mcp.json` 後,
|
||||
明確印「⚠️ MCP 已設定,**請重啟 IDE/client 才會載入** project scope MCP」。掛不進時引導重啟,不讓用戶以為壞了。
|
||||
|
||||
### 範圍 / 分期(richblack 2026-06-09 授權 P0-P2 執行)
|
||||
|
||||
- [x] **P0**:`acr init` 偵測 + 驗收(pip 式)。新增 `cli/src/lib/preflight.ts`:
|
||||
- `detectEnvironment()` 安裝前偵測 node/wrangler(缺=fatal 停下給補救指令,不假設齊備直接動手);
|
||||
- `verifyInstall()` 裝完實查 CF(KV/D1 listKv/listD1)+ 打 cypher `/health` 確認真就緒,缺哪項報哪項 + 一鍵補裝(acr update 冪等)。
|
||||
- `initSelfHosted` 開頭接 detect(fatal 即 exit 1)、結尾接 verify(未就緒印補裝指引)。冪等沿用 ensureKv/ensureD1。cli tsc exit 0。**2026-06-09 完成**
|
||||
- [x] **P1**:`acr whoami`(`cli/src/commands/whoami.ts`,人讀 + `--json`,印 mode/帳號/連哪台/來源層,薄殼讀 resolveConfigSources)+ MCP `arcrun_whoami`(`mcp/src/tools/arcrun_whoami.ts`,回報 orgNamespace + binding,與 CLI 對齊)。AI 問工具拿身份,不自己 curl 猜。cli+mcp tsc exit 0。**2026-06-09 完成**
|
||||
- [x] **P2**:MCP 掛載重啟提示(D3)。`cli/src/commands/mcp-setup.ts` 寫完 `.mcp.json` 後印「⚠ 請重啟 IDE/client 才會載入 project scope MCP」+ 信任工作區提醒。經 init 也會流到(init 呼叫 cmdMcpSetup)。**2026-06-09 完成**
|
||||
- [~] **P3**:README/GitHub 入口整理成「AI 讀了就照著裝」的單一指引(D 無腦入口)。**部分完成 2026-06-09**:
|
||||
- 新增 repo 根 `.env.example` 範本(CF Account/Token 基礎兩格 + NAMESPACE/ENCRYPTION_KEY + 服務 token 區,
|
||||
每格上面白話說明「去哪申請、怎麼拿」,值留空。`.gitignore` 加 `!.env.example` 放行進 repo)。
|
||||
- `llms.txt` step 3 改成「**你(AI)先 `cp .env.example .env`**,帶用戶填值(用戶只填「=」右邊)」——
|
||||
把「建 .env 結構」從用戶身上挪到 AI,用戶只做貼值。step 4 補 init「安裝驗收」說明。
|
||||
- 仍待:`arcrun.dev/llms.txt` 沒 serve(landing/public 缺檔,404;GitHub repo 內 llms.txt 正常)。
|
||||
test/5 prompt 給 GitHub URL 故不阻擋;arcrun.dev serve 另排。
|
||||
|
||||
> **誠實**:偵測各前置 + 版本比對 + 冪等重跑工程量不小(跨 CLI 多步)。但「裝很久 + AI 一直繞」對非技術用戶
|
||||
> 是致命體驗 → 這是 onboarding 必修不是優化。
|
||||
>
|
||||
> **2026-06-09 補(test_arcrun/4 壓測根因)**:D1/D2/D3 之上還有更致命的一層——壓測時 AI **整個沒跑 `acr init`**
|
||||
> (settings.local.json 沒 allow init),導致 D1/harness/MCP/slash-command 全沒裝(全掛在 cmdInit 內)。
|
||||
> P0 的「偵測+驗收」讓「跑了 init 但環境半殘」能被看見並自癒;但「**根本沒跑 init**」要靠 P3(README/入口
|
||||
> 引導 AI「第一件事就是 acr init」)+ harness 的 mindset/CLAUDE.md 提醒。P3 後續補。
|
||||
|
||||
---
|
||||
|
||||
## 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)。
|
||||
@@ -0,0 +1,170 @@
|
||||
# Implementation Plan: arcrun SDK Libraries + Website
|
||||
|
||||
## Overview
|
||||
|
||||
按 Design 的四個 Phase 實作。原則:修改不重建,SDK 是 HTTP API thin wrapper,加密只在 client 做 encrypt(不做 decrypt)。
|
||||
|
||||
**前置依賴**:必須先完成 `credential-primitives-wasm/tasks.md` 的 Phase 0-3(核心合併 + WASM primitives),確認核心穩定後才開始建三個介面。
|
||||
|
||||
---
|
||||
|
||||
## Phase 0(前置):核心合併 + WASM 改寫
|
||||
|
||||
> 詳見 `.agents/specs/arcrun/credential-primitives-wasm/tasks.md`
|
||||
>
|
||||
> 摘要:
|
||||
> - 合併 u6u-core → arcrun(搬 builtins、刪重複 credentials)
|
||||
> - credential-injector TS → auth_static_key / auth_service_account WASM
|
||||
> - 刪除 component-loader 內建 API recipes TS
|
||||
> - 驗證 20 個 auth recipe 正常運作
|
||||
|
||||
---
|
||||
|
||||
## Phase 1:Python SDK
|
||||
|
||||
- [ ] 1. 建立 `arcrun/python-sdk/` 目錄
|
||||
- [ ] 1.1 `pyproject.toml`:name=arcrun, deps=[httpx>=0.27, cryptography>=42], build-system=hatchling
|
||||
- [ ] 1.2 `arcrun/__init__.py`:`from .client import Arcrun`
|
||||
- [ ] 1.3 `arcrun/crypto.py`:AES-GCM encrypt only(使用 `cryptography` 套件)
|
||||
- [ ] 1.4 `arcrun/creds.py`:CredentialsClient — push(加密 + POST /credentials)、list(GET /credentials)、delete
|
||||
- [ ] 1.5 `arcrun/auth.py`:AuthClient — setup(fetch recipe → match secrets → encrypt → push)、bind(fetch recipe → resolve headers from cache → return AuthenticatedClient)、get_token、list_services
|
||||
- [ ] 1.6 `arcrun/workflows.py`:WorkflowClient — run(POST /webhooks/named/{name}/trigger)、push(POST /webhooks/named)、list(GET /webhooks/named)、delete
|
||||
- [ ] 1.7 `arcrun/client.py`:Arcrun class — 讀 api_key / encryption_key 從 param > env > config.yaml
|
||||
|
||||
- [ ] 2. 修正上次已知的 bug
|
||||
- [ ] 2.1 `_fetch_recipe()` 回應是 `{ success: true, recipe: {...} }`,需讀 `.recipe` 欄位
|
||||
- [ ] 2.2 `inject` 下的 key 是 `header`(singular),不是 `headers`
|
||||
- [ ] 2.3 `required_secrets[].key` 是 prefixed(如 `openai_api_key`),setup() 的 kwargs alias 要能對應
|
||||
- [ ] 2.4 `list_services()` 回應的 recipe 用 `service` 欄位(不是 `service_id`)
|
||||
|
||||
- [ ] 3. 測試(對 cypher.arcrun.dev live API)
|
||||
- [ ] 3.1 `health()` → `{"ok": true}`
|
||||
- [ ] 3.2 `auth.list_services()` → 20 個服務
|
||||
- [ ] 3.3 `auth.setup("openai", api_key="sk-test-dummy")` → 成功
|
||||
- [ ] 3.4 `auth.bind("openai")` → AuthenticatedClient with Authorization header
|
||||
- [ ] 3.5 `auth.get_token("openai")` → "sk-test-dummy"
|
||||
- [ ] 3.6 `creds.push("test_token", "value123")` → 成功
|
||||
- [ ] 3.7 `creds.list()` → 含 "test_token"(注意 KV eventual consistency)
|
||||
- [ ] 3.8 `workflows.list()` → []
|
||||
- [ ] 3.9 cleanup: `creds.delete("test_token")`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2:JS/TS SDK
|
||||
|
||||
- [ ] 4. 建立 `arcrun/js-sdk/` 目錄
|
||||
- [ ] 4.1 `package.json`:name TBD(arcrun vs @arcrun/sdk),deps=devDeps only(tsup, typescript, @types/node)
|
||||
- [ ] 4.2 `tsconfig.json`:ES2020, NodeNext
|
||||
- [ ] 4.3 `src/crypto.ts`:Web Crypto API AES-GCM encrypt only
|
||||
- [ ] 4.4 `src/creds.ts`:CredentialsClient — push/list/delete via fetch
|
||||
- [ ] 4.5 `src/auth.ts`:AuthClient — setup/bind/getToken/listServices
|
||||
- [ ] 4.6 `src/workflows.ts`:WorkflowClient — run/push/list/delete
|
||||
- [ ] 4.7 `src/index.ts`:export class Arcrun + re-exports
|
||||
|
||||
- [ ] 5. 同步修正(與 Python SDK 同樣的 recipe 格式問題)
|
||||
- [ ] 5.1 `_fetchRecipe()` 讀 `body.recipe`
|
||||
- [ ] 5.2 inject key: `header` not `headers`
|
||||
- [ ] 5.3 setup() secret key alias matching
|
||||
- [ ] 5.4 listServices() 用 `service` 欄位
|
||||
|
||||
- [ ] 6. Build + 測試
|
||||
- [ ] 6.1 `tsup` build → dist/index.js + dist/index.cjs + dist/index.d.ts
|
||||
- [ ] 6.2 Node.js 腳本對 live API 測試(同 Python 測試項目)
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:arcrun.dev 網站
|
||||
|
||||
- [ ] 7. 新增 `/components` 頁面
|
||||
- [ ] 7.1 從 `registry/components/*/component.contract.yaml` 讀取 21 個零件資料
|
||||
- [ ] 7.2 卡片顯示:canonical_id, display_name, description, input required/optional, credentials_required, config_example
|
||||
- [ ] 7.3 分類篩選:邏輯類 / API 類 / 控制流類
|
||||
|
||||
- [ ] 8. 更新首頁
|
||||
- [ ] 8.1 Code demo tabs 改為 CLI / Python / JS 三個
|
||||
- [ ] 8.2 CLI tab 展示 `acr init → acr push → acr run`
|
||||
- [ ] 8.3 Python tab 展示 `pip install arcrun → Arcrun() → auth.setup → auth.bind`
|
||||
- [ ] 8.4 JS tab 展示 `npm install arcrun → new Arcrun() → auth.setup → auth.bind`
|
||||
|
||||
- [ ] 9. OAuth 流程補完
|
||||
- [ ] 9.1 確認 cypher-executor 的 `/auth/google/start`、`/auth/github/start`、`/auth/callback` 路由正確
|
||||
- [ ] 9.2 提供 richblack OAuth secrets 設定指令清單
|
||||
- [ ] 9.3 richblack 設定 secrets 後驗證登入流程
|
||||
|
||||
- [ ] 10. 部署
|
||||
- [ ] 10.1 Cloudflare Pages build + deploy
|
||||
- [ ] 10.2 驗證所有頁面可存取
|
||||
|
||||
---
|
||||
|
||||
## Phase 4:README + 發布
|
||||
|
||||
- [ ] 11. 更新 `arcrun/README.md`
|
||||
- [ ] 11.1 三種 Quick Start(CLI / Python / JS)
|
||||
- [ ] 11.2 零件列表(21 個)
|
||||
- [ ] 11.3 Auth Recipe 列表(20 個服務)
|
||||
- [ ] 11.4 連結到 arcrun.dev 和 Swagger UI
|
||||
|
||||
- [ ] 12. 發布
|
||||
- [ ] 12.1 Python SDK:`pip install build && python -m build && twine upload dist/*`
|
||||
- [ ] 12.2 JS SDK:`npm run build && npm publish`
|
||||
- [ ] 12.3 驗證:從零開始 `pip install arcrun` / `npm install arcrun` + hello world
|
||||
|
||||
---
|
||||
|
||||
## Phase 5:acr init --self-hosted installer(2026-06-02 新增)
|
||||
|
||||
> 定稿 design:`self-hosted-init.md`。CLI = installer:建 KV/R2 + 拉預編譯 wasm + wrangler deploy + seed。
|
||||
> 用戶只做:申請 CF 帳號 → 裝 wrangler → 裝 acr → acr init --self-hosted。其餘自動。
|
||||
> 背景:戰法轉 self-hosted 開源(docs/HANDOFF-self-host-harness.md)。
|
||||
|
||||
- [x] 13.1 API recipe 種子 — **位置修正**:種子資料放 `cli/src/lib/api-recipe-seeds.ts`(installer 用,避開 cypher §2.2 hook),seed 腳本 `cypher-executor/scripts/seed-api-recipes.ts`(import 種子,給 prod 補灌)。10 個現役 recipe(kbdb_*/gmail_send/google_sheets_*/telegram_send/line_notify_send)。KBDB Supabase 模式進 seed(finally.click 是 KBDB 端 follow-up,已註於 api-recipe-seeds.ts)
|
||||
- [x] 13.2 `cli/src/lib/cf-api.ts` 新增 `CfAccountClient`:verifyAccess / listKvNamespaces / ensureKvNamespace(冪等)/ ensureR2Bucket(冪等)/ getWorkersSubdomain
|
||||
- [x] 13.3 `cli/src/commands/init.ts` `initSelfHosted()` 改寫:驗 token → 建 7 KV + R2 → 查 subdomain → downloadAndDeploy → 寫 config → seed(部署完成時)→ 印 secret 提示。誠實:部署未自動化時明說,不假綠
|
||||
- [x] 13.4 `cli/src/lib/deploy.ts`:REQUIRED_KV/R2/SECRET 常數 + wranglerAvailable() + **downloadAndDeploy 已補實**(codeload tarball 下載 + 解壓 + discoverWorkerDirs 分 tier + injectWranglerConfig 注入 KV id/subdomain + runWranglerDeploy;部分失敗誠實收集回報,不假綠)
|
||||
- [x] 13.5 `cli/src/commands/update.ts` + index.ts 註冊 `acr update`(self-hosted 重部署,同走 downloadAndDeploy)
|
||||
- [x] 13.6 部署物產製:**改用 commit wasm 進 repo + codeload**(取代 release artifact,richblack 2026-06-02,§6)
|
||||
- `.gitignore` 否定規則放行 `.component-builds/**/component.wasm`(registry 中間產物仍排除)→ 已驗 git check-ignore
|
||||
- rule 05 同步改(記錄推翻「wasm 不 commit」+ trade-off)
|
||||
- commit 22 個 `.component-builds/*/component.wasm` 進 repo
|
||||
- [ ] 13.7 驗收:全新 CF 帳號跑 acr init --self-hosted 全自動;acr push workflow → trigger 2xx + trace(**待 richblack 用第二帳號實測** + push 含 wasm 的 commit 到 GitHub 後 codeload 才拿得到)
|
||||
- [x] 13.8 typecheck:cli `tsc --noEmit` exit 0
|
||||
|
||||
## Phase 6:壓測四橫向問題修正(2026-06-06,richblack 點名)
|
||||
|
||||
> 來源:`/Users/youlinhsieh/Documents/tech_projects/test_arcrun/docs/壓測報告.md`。
|
||||
> 四個結構性問題(非個別 bug):薄殼原則未成鐵律、CLI/MCP 不同步打不同帳號、deploy 未全推、表達不清。
|
||||
|
||||
- [x] 6.1 **薄殼鐵律成文 + hook 強制**:新增 `.claude/rules/07-thin-shell.md`(能力長在 API,介面只暴露);
|
||||
`02-forbidden.md` 第五類 + `CLAUDE.md` 鐵律 8 + 索引;`pre-write-guard.sh` 規則 7.x
|
||||
(擋 cli/src + arcrun-mcp/src 的 seedApiRecipes/seedAuthRecipes、upsert 拼裝、deployFullyOk gate)。
|
||||
- [x] 6.2 **seed 下沉成 API 行為**(薄殼正例 + 修 §4.1 seed bug):
|
||||
- 新增 `cypher-executor/src/routes/init-seed.ts`:`POST /init/seed` 一次灌 API recipe + auth recipe(冪等、直寫 KV、誠實計數),mount 進 index.ts
|
||||
- 種子資料移到 server:`cypher-executor/src/lib/api-recipe-seeds.ts`(唯一真相),刪 CLI 重複 `cli/src/lib/api-recipe-seeds.ts`,`seed-api-recipes.ts` 改 import server 端
|
||||
- CLI `init.ts`:`seedApiRecipes` 迴圈 → `callSeedEndpoint` 薄殼一次呼叫;**移除 deployFullyOk gate**(registry 失敗不再連坐害 seed 被跳過,§4.1 根因);auth recipe 現在一併 seed(§4.1.2 修)
|
||||
- CLI `update.ts`:重部署後也呼叫 `/init/seed`(修 §4.1.3「update 不 seed 卻提示說會 seed」矛盾)+ 改為重解析「全部」KV id(修「只注入 2 個」風險)
|
||||
- [x] 6.3 **hook seed 例外通用化**:`*-seeds.ts` 整類豁免 endpoint/template 檢查(richblack 原則:不為單一零件改全域規則),未來新種子檔自動適用,永不再動 hook。
|
||||
- [x] 6.4 **registry SUBMISSIONS_KV**(修 §2.6/#11「20/21」):加進 `REQUIRED_KV_NAMESPACES` → init/update 會建 + 注入 → registry 部署回 21/21 → 連帶 seed 不再被連坐(§4.1.1)。
|
||||
- [x] 6.5 **Deploy 一致性(Gherkin + 把 npm publish 補進現行機制)**:誠實前提——GH Actions 2026-05-16 停用、公開 repo `.github/` 已移除,現行 deploy 走 `scripts/local-deploy.sh`。故:
|
||||
- `tests/release.feature`(每個 publish target 場景,描述「該到位什麼」)
|
||||
- `scripts/check-release.sh`(一眼看版本/部署狀態,已驗證抓出 CLI 1.2.0≠npm 1.1.0 漂移)
|
||||
- **`scripts/local-deploy.sh` 第 6 段新增 CLI npm publish**(cli/ 變動且 version bump → npm publish;同版跳過、未登入誠實標)。這是真正讓「推送=全部到位」的修法,根因是舊腳本只 wrangler deploy worker 從不 publish CLI。
|
||||
- `.github/workflows/publish-cli.yml` 為預備檔(gitignored、不跑),日後重啟 GH Actions 可啟用。
|
||||
- [x] 6.6 **表達不清 → README 同步現況**:移除 R2 殘留文案(權限表去掉 R2、init 輸出去掉 WASM_BUCKET、「無需綁卡」)、補 `--account-id/--api-token`/env 非互動、補多帳號 `.arcrun.yaml` + `acr config --where`。
|
||||
- [x] 6.7 客觀驗證:cypher-executor + cli 兩端 `tsc --noEmit` exit 0;hook 7.x + *-seeds 例外 + 非 seed 仍擋 全部回歸測試通過。
|
||||
- [x] 6.8 **MCP 帳號架構**(richblack 2026-06-06 拍板「單一 remote MCP + .env 切 MCP URL」,推翻初版三方案):
|
||||
- SDD `mcp-account-source.md` 重寫為實作版。**不加 stdio**(self-hosted 用戶也有 CF,MCP 一律 remote Worker;差別只在連哪台)。
|
||||
- 共用 config 解析:`config.ts` 加 `mcp_url` 三層(env `ARCRUN_MCP_URL` > 專案 `.arcrun.yaml` > 全域)+ `getMcpUrl()` + `DEFAULT_MCP_URL`。「切換帳號」能力收斂到 config 解析(薄殼正解),不再綁 CLI。
|
||||
- **MCP 搬進 `arcrun/mcp/`**(從 sibling repo verbatim 搬;wrangler name→`arcrun-mcp`+`workers_dev=true`;形態不變仍 remote Worker)。deploy 掃描已掃到、tsc 綠。
|
||||
- **新增 `acr mcp-setup`**:依 `getMcpUrl()` 寫專案 `.mcp.json`(remote http MCP)。init 順帶呼叫。接案進客戶資料夾跑一次 → Claude Code 連客戶 MCP。**實測 3 情境(fallback/專案覆蓋/env)+ merge 保留既有 server 全通過**。
|
||||
- `acr --version` 改從 package.json 動態讀(不再 hardcode,根治版本漂移)。
|
||||
- [x] 6.10 **CLI 版本自動化**(richblack:deploy 時自動昇版 + 留記錄,避免忘了改):`local-deploy.sh` deploy CLI 時若版本未 bump → 自動 `npm version patch` + prepend `cli/CHANGELOG.md`(含 commit subject)。已 bump 到 1.3.0 + 建 CHANGELOG.md。
|
||||
- [ ] 6.9 驗收:richblack 跑 `scripts/local-deploy.sh`(worker + MCP + CLI npm 一起);壓測者重跑 `acr init` → recipe/auth-recipe 不再空 + `.mcp.json` 自動產 → 表單→Google Sheets 端到端。
|
||||
- [ ] 6.11 待 richblack 確認:MCP 搬進主庫後**對外正式網址**(現役 `studio.finally.click/mcp`,`DEFAULT_MCP_URL` 暫用此);sibling repo `matrix/arcrun-mcp` 去留(建議標歷史、新開發在 arcrun/mcp/)。
|
||||
|
||||
## Notes
|
||||
|
||||
- JS SDK 套件名需 richblack 決定(`arcrun` 已被 CLI 佔用 → 可能用 `@arcrun/sdk`)
|
||||
- OAuth secrets 設定需 richblack 手動操作(GCP Console + GitHub Settings)
|
||||
- `bind()` 跨 session 限制是已知的,封測期間先接受
|
||||
- credential 加密用的 `encryption_key` 目前由 `/register` 回傳,`acr init` 自動存入 config
|
||||
Reference in New Issue
Block a user