# 心音(Kokorone) 以人腦「記憶/情緒/關係」架構為引擎基礎的戀愛陪伴系統。 ## 開發環境 Monorepo 採 pnpm workspace + Turborepo(`pnpm-workspace.yaml` / `turbo.json`)。 ```bash pnpm install # 安裝所有 workspace 套件 pnpm run build # turbo run build(依相依順序建置 shared/db/api,並建置 web 的 production bundle) pnpm run restart # 停止舊的 api/web 行程、重建 TypeScript(tsc -b)、重新啟動並等待健康檢查 pnpm run smoke A # 執行 A 群組冒煙測試;all 表示依序執行 A~R 全部群組 ``` 對外指令介面(`restart` / `smoke`)維持與遷移前相同的腳本名稱;唯一差異是 **pnpm 不會像 npm 一樣吃掉 `--` 分隔符**,`pnpm run smoke -- A` 會把字面上的 `--` 一起傳給腳本而找不到群組,要執行子指令請直接接在腳本名稱後面 (`pnpm run smoke A`),不要加 `--`。 ## 環境變數 複製 `.env.example` 為 `.env` 後依需要調整;`apps/web/.env.local` 另外可設定 `NEXT_PUBLIC_API_URL`(瀏覽器端呼叫 api 用,預設 `http://localhost:3001`)。 | 變數 | 預設值 | 說明 | | --- | --- | --- | | `DATABASE_URL` | `file:./prisma/dev.db` | Prisma 連線字串。R-5 若遷移到 PostgreSQL,改為 `postgresql://user:password@host:5432/db` 並將 `prisma/schema.prisma` 的 `provider` 由 `sqlite` 換成 `postgresql`。 | | `PORT` | `3001` | api(NestJS)監聽埠。 | | `PORT_API` / `PORT_WEB` | `3001` / `3100` | `pnpm run restart`(`scripts/dev-restart.mjs`)啟動 api/web 時使用的埠號。 | | `WEB_ORIGIN` | `http://localhost:3100` | api 的 CORS 允許來源之一;`http://localhost:8081`(Expo web 開發伺服器)已固定加入,不受此變數影響。 | | `NEXT_PUBLIC_API_URL` | `http://localhost:3001` | web(Next.js)呼叫 api 的位址,build time 注入,正式環境需在建置時設定。 | | `LLM_PROVIDER` | `mock` | `mock`(模板庫,冒煙測試固定用這個)/`claude`(呼叫真實 API,見下)。 | | `CLAUDE_BASE_URL` | `http://localhost:3000/api/v1` | OpenAI 相容的 chat completions 端點基底路徑。本專案的部署方式是透過 [CLIProxy](https://gitea.jsc.idv.tw/jiantw83/CLIProxy)(分散式派工代理,本機 3000 埠)轉發到真實 Claude,而非直連 `api.anthropic.com`;若要直連官方 API,把這個值換成 `https://api.anthropic.com/v1` 並確認金鑰/請求格式相容即可,`ClaudeProvider` 的呼叫端程式碼不需要改動。 | | `CLAUDE_API_KEY` | (無) | 呼叫 `CLAUDE_BASE_URL` 用的金鑰,`LLM_PROVIDER=claude` 時必填。 | | `CLAUDE_MODEL` | `claude-sonnet-4-5` | 要求 Provider 使用的模型名稱;CLIProxy 端需要有對應的 Worker 已註冊、能接這個模型的派工,否則會回報「沒有任何可用的 {模型}-{執行者}-{工具} 組合」。 | | `TTS_PROVIDER` | `mock` | `mock`/`real`(P-4 空殼,尚未接上真實供應商,人工確認後才實作)。 | | `PUSH_PROVIDER` | `mock` | `mock`/`expo`(Q-3,`expo` 直接可用,不需金鑰)。 | ## 部署路徑 開發環境:以上服務直接用 `node`/`next dev`/`expo start` 跑在同一台 Linux 主機上 (`pnpm run restart` 一鍵重啟 api/web),**不使用 Docker**——這是本專案的實際 部署選擇,取代原先技術選型草案中「Docker Compose(開發)」的規劃。 正式環境建議路徑: 1. **建置**:`pnpm install --frozen-lockfile && pnpm run build`(turbo 依序建置 `packages/shared`/`packages/db`/`apps/api`,並產出 `apps/web` 的 production bundle)。 2. **資料庫遷移**:`npx prisma migrate deploy`(依 `DATABASE_URL` 指向的資料庫套用 遷移;R-5 遷移到 PostgreSQL 後流程相同,只是連線字串換成 postgresql)。 3. **啟動**: - api:`node apps/api/dist/main.js`(讀取上表環境變數)。 - web:`apps/web` 下 `npx next start -p `(讀取建置時注入的 `NEXT_PUBLIC_API_URL`)。 - mobile:`apps/mobile` 透過 `eas build` 產出實際的 App Store/Google Play 安裝檔,不隨 api/web 一起常駐部署。 4. **程序常駐與重啟**:用 systemd unit(或 pm2)分別管理 api/web 兩個 Node 行程,設定 `Restart=on-failure`;健康檢查沿用 `GET /health` (api)與首頁 200(web),與 `scripts/dev-restart.mjs` 開發環境用的 檢查邏輯一致。**房間自走(T37 起)需要 api 常駐**——排程與 api 同行程 (node-cron,見 `apps/api/src/room/room-scheduler.service.ts`),api 停機 期間房間不會推進,重啟後只會補推進一次接上目前時間,不會補齊停機期間 錯過的輪數;因排程未加分散式鎖,**api 只能起一個實例**,多實例會讓同一 房間被重複推進。 5. **反向代理/TLS**:nginx 或雲端負載平衡器終止 TLS 後轉發到 api/web 對應埠號,對外只曝露 443。 這個路徑刻意不假設 K8s 或容器化,只依賴「Node.js 行程 + systemd + nginx」, 因為這台開發主機本身就是照這個模式跑(`pnpm run restart` 正是這個模式的 開發期簡化版),移植到正式主機時只是把「用 node 直接跑」換成「用 systemd 管理同一組 node 指令」,中間不需要引入額外的容器化工具鏈。