- .agents/specs/: spec-driven-dev docs for arcrun MVP, auth-recipe, credential-primitives-wasm (active refactor), landing-page, sdk-and-website, u6u-core-mvp, u6u-platform-evolution. - .agents/steerings/tech.md: detailed tech stack rationale. - docs/user_requirements/: long-form requirements incl. credential primitives, pages spec, py strategy analysis. - tests/: end-to-end harness scaffolding. These are the durable context backing CLAUDE.md's SDD protocol. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
11 KiB
Design Document: arcrun SDK Libraries + Website
Overview
本設計涵蓋 arcrun 的三個新增交付物:
- Python SDK lib(
pip install arcrun) - JS/TS SDK lib(
npm install arcrun或@arcrun/sdk) - 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 設計
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 對等
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:
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/)
已完成:
/— Hero + Code Demo(Python/JS/HTTP tabs)/login— Google + GitHub OAuth 按鈕(前端 OK,需設 OAuth secrets)/dashboard— API Key 查看/Copy/Rotate/Revoke(依賴/meAPI)/integrations— 20 個 recipe 靜態卡片/api-docs— Swagger UI CDN 嵌入middleware.ts— 保護/dashboard(未登入 →/login)- 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:
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 需要的改動:
-
GET /auth-recipes回應格式:目前 list 端點回{ recipes: [...] }但 recipe 的service欄位是 key — SDK 已在 list_services() 正確處理 ✅ -
GET /auth-recipes/:service回應格式:目前回{ success: true, recipe: {...} }— SDK 需讀body.recipe而非 body 本身 ✅ -
POST /credentials不需改動 — SDK 自己做 AES-GCM 加密後送{ name, encrypted, iv }✅ -
未來:新增
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