ctos-architecture · git:20260611.7838f52 · 2026-06-11 · sha256 7a3c109443fdc98f

ctos-architecture git:20260611.7838f52B

Immutable. This exact content is served forever at /api/v1/blob/7a3c109443fdc98f.

---
name: ctos-architecture
description: CTOS(ching-tech-os)架構導覽與改動定位指南。當使用者要在 ching-tech-os 加新功能、新前端 App、新後端內建模組、extends 外部模組,或新增 CTOS AI Skill 時使用。提供「改哪一層」決策樹、內建 App 逐檔 checklist、contributes.yaml 四種貢獻規格,以及 CTOS AI Skill 與 Claude Code skill 的區分。
---

# CTOS 架構與功能擴充指南

CTOS 是 FastAPI + PostgreSQL(asyncpg + Alembic)+ 原生 JS(IIFE,無框架)的內部平台。
加功能前先用下面的決策樹定位「要動哪一層」,再照對應 checklist 逐檔處理。
本 skill 只給骨架;**細節以 repo 內文件為準**,動手前先讀「必讀文件」一節列的檔案。

## 1. 決策樹:功能該放哪一層

```
這個需求是?
├─ 修改/擴充既有功能(加欄位、加端點、改 UI)
│   → 直接改既有模組。先查 docs/module-index.md 的
│     「常見修改場景速查」定位檔案,不要新開模組。
│
├─ 全新的平台功能,所有部署都該有(如知識庫、分享管理這類)
│   → 內建新 App:在 backend/src/ching_tech_os/modules.py 的
│     BUILTIN_MODULES 加 entry。見第 2 節 checklist。
│
├─ 需要獨立授權 / 獨立版控 / 客戶特定(如 his、law、erpnext)
│   → extends 外部模組:git submodule + contributes.yaml 宣告,
│     不修改主系統任何程式碼。見第 3 節。
│
└─ 要給 CTOS 的 AI(Bot / AI 助手)新能力(prompt + 工具 + 可選前端 App)
    → CTOS AI Skill:SKILL.md(frontmatter 含 metadata.ctos /
      contributes),由 SkillManager 掃描載入。見第 4 節。
```

判斷補充:

- 模組啟停由 `.env` 的 `ENABLED_MODULES` 控制(`*` 全開,或逗號分隔清單;`core` 永遠啟用)。`modules.py` 的 `is_module_enabled()` 是判斷點。
- extends 模組裡的工具有三種提供方式(in-process `mcp_tools` / 外部 `mcp_servers` / Skill Script),選擇矩陣在 `docs/extends-module.md` 的「三種工具提供方式的選擇」表格,別憑感覺選。

## 2. 內建新 App 逐檔 checklist

### 2.1 後端

- [ ] `backend/src/ching_tech_os/modules.py`:在 `BUILTIN_MODULES` 加 entry。欄位依 `ModuleInfo` TypedDict:
  - `id`(必填,模組 ID,同時是 `ENABLED_MODULES` 的 key)
  - `source: "builtin"`(必填)
  - `routers`:`RouterSpec` 清單,每筆 `{"module": ".api.xxx", "attr": "router", "kwargs": {...}}`,由 `main.py` 動態註冊
  - `mcp_module`:如 `".services.mcp.xxx_tools"`(有 MCP 工具才填)
  - `app_ids` + `app_manifest`:前端 App 清單,每個 app `{"id", "name", "icon"}`,會經 `/api/config/apps` 餵給前端
  - 視需求:`permission_defaults`、`permission_display_names`、`scheduler_jobs`、`lifespan_startup`
- [ ] `api/xxx.py`(路由)、`services/xxx.py`(業務邏輯)、`models/xxx.py`(Pydantic 模型)
- [ ] Migration:`backend/migrations/versions/00X_description.py`(schema 變更一律走 Alembic;`docker/init.sql` 已停用),套用:`cd backend && uv run alembic upgrade head`

### 2.2 前端

- [ ] `frontend/js/xxx.js`:IIFE 模組,需暴露 `open()`(desktop.js lazy-load 後以 `window[globalName].open()` 開啟)
- [ ] `frontend/css/xxx.css`:樣式。**先讀 `frontend/css/main.css` 的變數定義**,用 `--text-*` / `--bg-*` / `--color-*` 變數,禁止硬編碼色值與 fallback 值
- [ ] `frontend/js/desktop.js`:
  - `fallbackApplications` 加 `{ id, name, icon }`(API 失敗時的 fallback;正式清單來自 `/api/config/apps`,由 modules.py 的 `app_manifest` 生成)
  - `fallbackAppLoaders` 加 `{ src: './js/xxx.js', globalName: 'XxxApp' }`(lazy-load 查表)
- [ ] `frontend/index.html`(和 `login.html`):引入 CSS。注意:僅靜態內建資源需引入;lazy-load 的 JS 由 desktop.js 動態注入,不需 `<script>` 標籤。路徑後**不要**加 `?v=` 版本號
- [ ] `frontend/js/icons.js`:如需新圖示,在 `Icons` 物件加 SVG(Material Design Icons)。使用時 `getIcon()` 必須包在 `<span class="icon">` 內

### 2.3 收尾

- [ ] `docs/module-index.md`:更新模組地圖(後端/前端對應表)
- [ ] 子路徑部署檢查:HTML 屬性裡的 `/api/...`(`href` / `src` / `window.open`)要加 `${window.API_BASE || ''}`,詳見 CLAUDE.md「子路徑部署」一節
- [ ] 版本號如要 bump,三處同步:`backend/pyproject.toml`、`src/ching_tech_os/__init__.py`、`main.py` 的 FastAPI `version`

## 3. extends 外部模組:contributes.yaml 四種貢獻

主系統啟動時掃描 `extends/*/contributes.yaml` 自動整合,模組**不需要也不應該**改主系統程式碼。`module_id` 對應 `ENABLED_MODULES`;不設定則永遠啟用。

### 3.1 四種貢獻的精確 YAML 規格

```yaml
module_id: my-module

# (1) in-process MCP 工具:路徑相對模組根目錄,
#     檔內用 @mcp.tool() 向主系統 FastMCP 註冊;
#     需 DB 時先 await ensure_db_connection()
mcp_tools: core/mcp_tools.py

# (2) 外部 MCP Server:獨立進程、stdio 通訊,
#     ${PROJECT_ROOT} 自動替換為專案根目錄
mcp_servers:
  server-name:
    command: bash
    args: ["-c", "set -a && source ${PROJECT_ROOT}/.env && set +a && uvx some-mcp"]

# (3) 生命週期:主系統啟動/關閉時執行,
#     callable 是相對模組根目錄的 import 路徑(sync/async 皆可),
#     kwargs 的 ${ENV_VAR} 自動替換環境變數
lifespan:
  startup:
    callable: core.some_module.start
    kwargs:
      param1: ${ENV_VAR}
      param2: 30
  shutdown:
    callable: core.some_module.stop

# (4) API 路由:把模組的 FastAPI Router 註冊進主系統,
#     module 是 import 路徑(模組根目錄已在 sys.path)
routers:
  - module: my_router
    attr: router
    kwargs:
      prefix: /api/my-module
      tags: [my-module]
```

### 3.2 現成範例模組對照表

| 模組 | 使用欄位 | 工具提供方式 | 參考重點 |
|------|---------|------------|---------|
| `extends/voice` | `module_id` + `lifespan` + `routers` | 主系統 in-process MCP 工具 | lifespan 預載 Whisper 模型;routers 掛 `/api/voice` TTS 下載 API |
| `extends/printer` | `module_id` + `mcp_servers` | 外部 MCP Server(`uvx printer-mcp`) | 最小 mcp_servers 範例 |
| `extends/his` | `module_id` + `lifespan`(含 `${CTHIS_DATA_PATH}` kwargs) | Skill Script(不用 in-process MCP) | lifespan 背景輪詢 DBF + `skills/*/SKILL.md` 帶前端 App 貢獻 |

另:`extends/law` 是 `module_id` + `mcp_tools`(in-process)的範例,見 `docs/extends-module.md` 的「現有模組參考」表。

注意:模組**根目錄**的 SKILL.md 只是說明文件,SkillManager 不掃描;會被載入的是 `extends/{module}/skills/{name}/SKILL.md`。

## 4. CTOS AI Skill 不是 Claude Code skill

兩者同名 SKILL.md,是**完全不同的東西**,別混:

| | CTOS AI Skill | Claude Code skill |
|---|---|---|
| 給誰用 | CTOS 平台內的 AI(Bot、AI 助手) | 開發者本機的 Claude Code |
| 位置 | `backend/.../skills/`、`extends/*/skills/{name}/`、外部 `~/SDD/external-skills/` | `.claude/skills/`(本檔就是一個) |
| 載入者 | CTOS 的 SkillManager | Claude Code CLI |

CTOS AI Skill 的 frontmatter(規格見 `docs/extends-module.md`):

```yaml
---
name: skill-name
description: 一句話描述
allowed-tools: "tool_a tool_b"        # 空格分隔,控制此 Skill 可用哪些已註冊 MCP 工具
metadata:
  ctos:
    requires_app: app-id              # 需要的前端 App 權限(null = 不需要)
    mcp_servers: ""                   # 需要的外部 MCP Server(空格分隔)
  contributes:                        # 可選:向主系統貢獻前端 App / 權限
    app:
      id: app-id
      name: 顯示名稱
      icon: mdi-xxx
      loader:
        src: frontend/xxx-app.js      # 相對 skill 目錄,由 modules.py 轉成 /api/skills/... 路徑
        globalName: XxxApp
      css: frontend/xxx-app.css
    permissions:
      app-id:
        default: false
        display_name: 顯示名稱
---
```

分工:`contributes.yaml` 的 `mcp_tools` 負責**載入註冊**工具;SKILL.md 的 `allowed-tools` 負責控制 AI 在該情境**可以用哪些**。一個模組可有多個 Skill,各開放不同工具子集。實例參考 `extends/his/skills/his-integration/SKILL.md`(contributes.app + permissions 完整範例)。

## 5. 必讀文件指引

| 時機 | 讀什麼 |
|------|--------|
| 動任何功能前(定位檔案) | `docs/module-index.md` |
| 開發 extends 模組 / CTOS AI Skill | `docs/extends-module.md`(contributes.yaml 完整規格、Skill Script 模式、多租戶、驗證指令、常見問題) |
| 寫程式前的專案規範(migration、版本號三處同步、CSS 變數、子路徑部署、圖示規則) | 根目錄 `CLAUDE.md` |
| 測試與 CI 門檻 | `docs/testing-ci.md`、`.github/workflows/backend-tests.yml` |

本 skill 刻意精簡——上表文件才是單一事實來源,規格細節(完整欄位、錯誤排查)一律回到 repo 文件查證,不要憑本 skill 的摘要硬寫。