跳至內容

執行無介面伺服器

在一部長期開機的機器上執行 cc_server,然後從你的桌面、瀏覽器或手機連接它。這部伺服器是純 Dart 原生二進位檔——不含 Flutter 引擎——因此可在 macOS、Linux 或 Windows 上無介面執行。它擁有資料庫、執行代理,並發出所有對外 API 呼叫;用戶端只負責呈現。

每個版本都會發佈一套自託管技術堆疊,共四個 GHCR 映像——後端、兩個靜態精簡用戶端以及可選的配對中繼——另加一個供公開示範主機使用的獨立 cc-server-demo 映像。自託管安裝無須檢出原始碼。請固定版本標籤(ghcr.io/samuelalev/<image>:v0.0.1-rc.1),或跟隨 latest。

映像 它是甚麼 發佈連接埠
ghcr.io/samuelalev/cc-server 後端:資料庫、代理、MCP、webhook -p 9030:9030
ghcr.io/samuelalev/cc-webapp 網頁用戶端(靜態 nginx) -p 8080:8080
ghcr.io/samuelalev/cc-remote 手機夥伴 PWA(靜態 nginx) -p 8081:8080
ghcr.io/samuelalev/cc-signaling-server WebRTC 配對中繼(可選) -p 8788:8788

整個技術堆疊的 compose 檔案如下:

services:
server:
image: ghcr.io/samuelalev/cc-server:latest
ports: ["9030:9030"]
volumes: ["cc_data:/data"]
restart: unless-stopped
web:
image: ghcr.io/samuelalev/cc-webapp:latest
ports: ["8080:8080"]
restart: unless-stopped
remote:
image: ghcr.io/samuelalev/cc-remote:latest
ports: ["8081:8080"]
restart: unless-stopped
signaling:
image: ghcr.io/samuelalev/cc-signaling-server:latest
ports: ["8788:8788"]
restart: unless-stopped
volumes:
cc_data:

在把這一切暴露到回送以外的任何地方之前,有幾件值得知道的事:

  • cc-server 映像隨附 CC_SERVER_INSECURE=1(它必須繫結 0.0.0.0 才能接收已發佈連接埠的流量),這只有在可信網絡上、位於終止 TLS 的反向代理——Caddy、Traefik、nginx 或 Cloudflare Tunnel——之後才安全。若改為在行程內終止 TLS,請掛載憑證並設定 CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY(然後取消設定 CC_SERVER_INSECURE)。這一切都可用 docker run -e … 或 compose 的 environment: 區塊覆寫。

  • /data 是唯一的狀態。 SQLite 資料庫、已配對裝置的密鑰、 下載的模型與會議音訊全都存放在這個磁碟區內——備份它,容器就變得可以隨時丟棄。

  • 在首次連接之前,從映像內部配對用戶端,這相當於容器版的第 3 步:

    終端視窗
    docker run --rm -it -v cc_data:/data \
    --entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair
  • 兩個用戶端映像都是靜態 nginx 主機——執行時無須設定任何東西;每個用戶端的連接表單接受你的伺服器 URL、裝置 ID 與配對金鑰。訊號中繼同樣只需要它的連接埠 (SIGNALING_HOST / SIGNALING_PORT 可覆寫);它是無狀態的、不持有任何密鑰,而且是一條後備途徑——手機在可觸達時會使用回送、LAN、tailnet 或 直接的 wss:// 路徑,只有在全部不可用之時才退回中繼。如果你的用戶端一律能直接觸達伺服器,就略過它。

本指南其餘部分會從原始碼建置伺服器;它所涵蓋的旗標與行為,與映像的環境變數所餵給的是同一批。

  • Dart SDK。此程式庫以 fvm 固定其 SDK,因此每條指令都要加上 fvm 前置詞。
  • 檢出此程式庫。
  • 暫存腳本所需的原生工具鏈——Rust/cargo,加上 meson、ninja、pkg-config、git 以及一個 C++ 編譯器。每個建置腳本都會指出它找不到甚麼,以及該如何安裝。
  • 一個可觸達的主機:本機使用時用回送,或供遠端用戶端使用時用帶 TLS 的 LAN/公開位址。

原生程式庫是必需的——沒有降級模式。請在做任何其他事之前,從程式庫根目錄建置它們:

終端視窗
scripts/natives/build_natives.sh

這會把 rift、fff、tree-sitter 及其文法、aec、lame、 ccpty、cc_watcher、cc_inference 以及 SAML 原生程式庫建置到 build/natives/。cc_server 建置 掛鉤會把暫存在那裏的東西重新輸出到套件組合中,因此略過此步驟,要麼直接令建置失敗,要麼——配合下方的逃生出口——產生一個二進位檔,其啟動前檢查會拒絕啟動並指出缺少的程式庫。

終端視窗
cd apps/cc_server
fvm dart build cli

套件組合會隨二進位檔一併帶上 libsqlite3 與已暫存的原生程式庫——無需系統 SQLite,也無需 Flutter 引擎。可執行檔會落在 ./build/cli/<os_arch>/bundle/bin/cc_server。

全新的資料目錄沒有任何已配對裝置,也沒有預先共用金鑰,因此用戶端的 配對金鑰提示暫時無法滿足。請用 pair 子指令產生一組。它會直接寫入 global.db 與 secrets.json;一個已在提供該資料目錄服務的伺服器,無須重新啟動就會接收這個新裝置:

終端視窗
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030

它會印出三個值,讓你貼到用戶端的連接表單:

Connect a thin client (paste into its connect form):
Server ws://localhost:9030/rpc
Device id web-client
Pairing key <a freshly generated key>

實用旗標:--device(預設 web-client;這個 id 同時決定裝置層級——desktop、ios、android,其他一律是網頁用戶端)、--label (預設為平台名稱加上本機的主機名稱)、--host(要印在 URL 中的主機,因為回送無法從另一部機器觸達)以及 --client-url。重新執行 pair 會輪替該裝置的金鑰,並丟棄其現行工作階段。配對從不建立工作區:連接的用戶端會在其新手導引中為第一個工作區命名並建立它。

帶 --client-url 時,它也會印出一個可在終端掃描的 QR 碼,開啟已填好資料的網頁用戶端:

終端視窗
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030 \
--bind any --host 192.168.1.42 --client-url https://app.usectrl.dev

由 cc_server pair 簽發的憑證沒有到期時間;在應用程式中簽發的憑證則一律在 30 天後到期。用完之後,請從 設定 → 你 → 你的裝置 撤銷這個由 CLI 簽發的裝置。

在回送上——這是預設,也是同一部機器上的用戶端所需的全部:

終端視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

若要從另一部機器觸達它,請繫結所有網絡介面。沒有 TLS 的非回送繫結 會在啟動時被拒絕(Refusing to bind non-loopback address … without TLS),因此請提供憑證:

終端視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem \
--public-url wss://server.example:9030/rpc

或者,在可信私人網絡上於反向代理中終止 TLS,並在其後選用明文繫結:

終端視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --insecure --public-url wss://server.example/rpc

當 --tls-cert 與 --tls-key 兩者都設定時,--insecure 會被忽略——TLS 一律勝出。伺服器會在 GET /healthz 中把明文狀態公佈為 insecure: true,而用戶端會把這類連線標示為不安全。

伺服器會一直執行到收到 SIGINT/SIGTERM,然後乾淨地關閉。

把用戶端指向第 3 步取得的伺服器 URL 與配對金鑰:

  • 桌面應用程式:在首次執行畫面(Control Center 應如何執行?)填寫連接欄位,而不要選在此應用程式內執行;或稍後在 設定 → 伺服器 → 連接與狀態 加入這部伺服器。參見連接遠端伺服器。
  • 網頁用戶端:開啟 app.usectrl.dev(或你自己的網頁組建複本),然後輸入伺服器 URL、裝置 ID 與配對金鑰。
  • 手機:從執行中的用戶端配對 Remote 應用程式——參見配對裝置。

若要在沒有圖形介面的情況下同步某個工作區的日曆,請使用裝置碼流程:

終端視窗
GOOGLE_OAUTH_CLIENT_ID=<id> GOOGLE_OAUTH_CLIENT_SECRET=<secret> \
./build/cli/<os_arch>/bundle/bin/cc_server calendar connect \
--data-dir ./data --workspace <workspace-id>

它會印出一組代碼與一個 URL,讓你在另一部裝置上批准,然後在伺服器端儲存重新整理權杖並結束。原始碼建置不會隨附任何 Google 用戶端,因此必須提供 id 與 secret 環境變數——沒有 --google-client-id 這個旗標。參見連接 Google Calendar。

上方用到的旗標,就是首次設定所需的那些。完整的介面——23 組旗標與環境變數配對、四個子指令,以及資料目錄的佈局——都在 cc_server CLI 參考 中。

你最可能需要的那四個:

旗標 預設值 含義
--data-dir 作業系統的每位使用者應用程式資料目錄 資料庫、密鑰、模型與快取媒體
--port 9030 TCP 連接埠(0 = 臨時)
--bind loopback loopback,或 any 表示所有網絡介面(需要 TLS 或 --insecure)
--public-url 由繫結推導 向已配對用戶端公佈的 RPC URL。在代理、NAT 或通道後方時,請明確設定它

預設資料目錄在 macOS 上是 ~/Library/Application Support/control-center, 在 Windows 上是 %APPDATA%\control-center,其他地方則是 $XDG_DATA_HOME/control-center(否則為 ~/.local/share/control-center)。只有在無法解析出家目錄或應用程式資料目錄時,才會使用相對於 cwd 的 .cc_server。

cc_server 是完整的產品介面,而不是其中一部分:repo-RPC 資料路徑 與訂閱、位於同一個連接埠上的 POST /mcp 與 GET /sse MCP 工具端點、入站 webhook、媒體與字型代理、單一登入 回呼、機群租約協定、按 30 分鐘排程進行的伺服器端 RSS 擷取,以及向量搜尋(sqlite_vector 擴充功能會註冊為 行程全域的自動擴充功能,只在該擴充功能無法使用的地方降級為全文搜尋)。

派發器是無狀態的:沒有按工作階段的工作區繫結。 每個工作區範圍的呼叫都帶有自己的 workspace_id,而存取權取決於 呼叫者的工作區成員身分與角色——而不是取決於是否持有配對金鑰。

在把連接埠暴露到回送以外之前,有兩件事要知道:

  • MCP 在主機之外失效即拒絕。 沒有權杖的 MCP 介面只回應回送。繫結 any 不會放寬這一點——在你設定一個之前(mcp.setToken,或 設定 → 伺服器 → MCP 伺服器),來自主機之外的請求會被以 “MCP requires a bearer token to answer non-loopback clients” 拒絕。
  • GET /healthz 在設計上未經驗證,它帶萬用字元 CORS,並公佈伺服器的 id、名稱、身分指紋與建置版本。用戶端需要該指紋來固定伺服器,因此這是刻意的——但這確實意味著任何可觸達的 cc_server 都會向任何查問者披露這麼多。