# 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_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）。
