Перейти до вмісту

Запустити headless-сервер

Запустіть cc_server на машині, яка працює постійно, а потім підключіться до неї з настільного застосунку, браузера чи телефона. Сервер — це нативний бінарник, написаний суто на Dart, без рушія Flutter, тож він працює headless на macOS, Linux і Windows. Він володіє базою даних, запускає агентів і виконує всі зовнішні виклики API; клієнти лише відображають.

Кожен випуск публікує стек для самостійного розміщення з чотирьох образів GHCR — бекенд, два статичні тонкі клієнти й необов’язкове реле прив’язки — а також окремий образ cc-server-demo для публічного демо-хоста. Для самостійного розміщення не потрібне ні клонування репозиторію. Закріпіть тег версії (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) або використовуйте latest.

Образ Що це Публікує
ghcr.io/samuelalev/cc-server Бекенд: база даних, агенти, MCP, вебхуки -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:

Що варто знати, перш ніж відкривати будь-що з цього поза loopback:

  • Образ 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 … або блок environment: у compose.

  • /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 сервера, ідентифікатор пристрою та ключ прив’язки. Реле сигналізації так само потребує лише свого порту (SIGNALING_HOST / SIGNALING_PORT можна перевизначити); воно безстанове, не зберігає секретів і є запасним шляхом — телефон використовує loopback, локальну мережу, tailnet або прямий шлях wss://, коли якийсь із них доступний, і звертається до реле лише тоді, коли жоден недоступний. Пропустіть його, якщо ваші клієнти завжди дістаються сервера напряму.

Решта цього посібника збирає сервер із вихідного коду; прапорці та поведінка, які він описує, — ті самі, що їх живлять змінні середовища образу.

  • Dart SDK. Цей репозиторій закріплює свій SDK через fvm, тож додавайте fvm на початок кожної команди.
  • Клонований цей репозиторій.
  • Нативний набір інструментів, потрібний скрипту підготовки — Rust/cargo, а також meson, ninja, pkg-config, git і компілятор C++. Кожен скрипт збірки повідомляє, чого не знайшов і як це встановити.
  • Доступний хост: loopback для локального використання або адреса в локальній мережі чи публічна адреса з TLS для віддалених клієнтів.

Нативні бібліотеки обов’язкові — режиму з обмеженою функціональністю немає. Зберіть їх із кореня репозиторію перед усім іншим:

Вікно термінала
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.

Крок 3: Прив’яжіть клієнт до першого запуску

Розділ із назвою «Крок 3: Прив’яжіть клієнт до першого запуску»

У свіжому каталозі даних немає прив’язаного пристрою й наперед спільного ключа, тож запит клієнта на ключ прив’язки ще нічим задовольнити. Створіть його підкомандою 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; ідентифікатор також визначає рівень пристрою — desktop, ios, android, а будь-що інше — вебклієнт), --label (типово назва платформи плюс ім’я хоста цієї машини), --host (хост, який виводиться в URL, адже loopback недоступний з іншої машини) і --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, у Налаштування → Ви → Ваші пристрої, коли він вам більше не потрібен.

На loopback — типово й цього досить для клієнта на тій самій машині:

Вікно термінала
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

Щоб дістатися з іншої машини, прив’яжіться до всіх інтерфейсів. Прив’язка не до loopback без 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

--insecure ігнорується, коли задано і --tls-cert, і --tls-key — TLS завжди перемагає. Сервер повідомляє стан незашифрованого тексту як insecure: true у GET /healthz, і клієнти позначають таке з’єднання як незахищене.

Сервер працює до SIGINT/SIGTERM, а потім чисто завершує роботу.

Спрямуйте клієнт на URL сервера та ключ прив’язки з кроку 3:

  • Настільний застосунок: на екрані першого запуску (Як має працювати Control Center?) заповніть поля підключення замість Запустити в цій програмі або додайте сервер пізніше в Налаштування → Сервер → Підключення та стан. Див. Під’єднатися до віддаленого сервера.
  • Вебклієнт: відкрийте app.usectrl.dev (або власну копію вебзбірки) і введіть URL сервера, ідентифікатор пристрою та ключ прив’язки.
  • Телефон: прив’яжіть застосунок Remote із запущеного клієнта — див. Прив’язати пристрій.

Крок 6: Підключіть Google Calendar (необов’язково)

Розділ із назвою «Крок 6: Підключіть Google Calendar (необов’язково)»

Щоб синхронізувати календар робочого простору без графічного інтерфейсу, скористайтеся потоком із кодом пристрою:

Вікно термінала
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, тож змінні середовища з ідентифікатором і секретом обов’язкові — прапорця --google-client-id немає. Див. Підключити Google Calendar.

Прапорці, використані вище, — це ті, що потрібні для першого налаштування. Повний набір — 23 пари прапорців і змінних середовища, чотири підкоманди та структура каталогу даних — у довідці CLI cc_server.

Чотири, які найімовірніше вам знадобляться:

Прапорець Типово Значення
--data-dir каталог даних застосунку для користувача в ОС Бази даних, секрети, моделі та кешовані медіа
--port 9030 Порт TCP (0 = тимчасовий)
--bind loopback loopback або any для всіх інтерфейсів (потребує TLS чи --insecure)
--public-url похідний від прив’язки URL RPC, який повідомляється прив’язаним клієнтам. Задайте його явно за проксі, NAT або тунелем

Типовий каталог даних — ~/Library/Application Support/control-center на macOS, %APPDATA%\control-center на Windows і $XDG_DATA_HOME/control-center (інакше ~/.local/share/control-center) на решті платформ. Відносний до робочого каталогу .cc_server використовується лише тоді, коли не вдається визначити ні домашній каталог, ні каталог даних застосунку.

cc_server — це вся поверхня продукту, а не її підмножина: шлях даних repo-RPC і підписки, кінцева точка інструментів MCP за POST /mcp і GET /sse на тому самому порту, вхідні вебхуки, проксі медіа та шрифтів, зворотні виклики єдиного входу, протокол оренди флоту, отримання RSS на боці сервера за 30-хвилинним розкладом і векторний пошук (розширення sqlite_vector зареєстровано як глобальне для процесу автоматичне розширення, що переходить до повнотекстового пошуку лише там, де розширення недоступне).

Диспетчер безстановий: немає прив’язки робочого простору до сесії. Кожен виклик у межах робочого простору несе власний workspace_id, і доступ визначається членством у робочому просторі та роллю того, хто викликає, — а не наявністю ключа прив’язки.

Дві речі, які варто знати, перш ніж відкривати порт поза loopback:

  • MCP закривається за замовчуванням поза хостом. Поверхня MCP без токена відповідає лише на loopback. Прив’язка any цього не розширює — запит ззовні хоста відхиляється з «MCP requires a bearer token to answer non-loopback clients», доки ви не налаштуєте токен (mcp.setToken або Налаштування → Сервер → MCP-сервери).
  • GET /healthz навмисно не вимагає автентифікації, має CORS із дозволом будь-якого джерела й публікує ідентифікатор, ім’я, відбиток ідентичності та версію збірки сервера. Клієнтам потрібен відбиток, щоб закріпити сервер, тож це свідоме рішення — але воно означає, що будь-який доступний cc_server розкриває стільки кожному, хто запитає.