رفتن به محتوا

اجرای سرور بدون رابط

cc_server را روی ماشینی که همیشه روشن می‌ماند اجرا کنید، سپس از رومیزی، مرورگر یا تلفن‌تان به آن متصل شوید. این سرور یک باینری بومی Dart خالص است — بدون موتور Flutter — پس بدون رابط روی macOS، Linux یا Windows اجرا می‌شود. پایگاه‌داده در اختیار آن است، عامل‌ها را اجرا می‌کند و هر فراخوانی API خارجی را انجام می‌دهد؛ کلاینت‌ها فقط رندر می‌کنند.

اجرا در Docker (ایمیج‌های پیش‌ساخته)

بخش با عنوان “اجرا در Docker (ایمیج‌های پیش‌ساخته)”

هر انتشار یک پشتهٔ خودمیزبان از چهار ایمیج 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:

نکته‌هایی که پیش از بیرون‌بردن هر یک از این‌ها از لوپ‌بک ارزش دانستن دارند:

  • ایمیج cc-server مقدار CC_SERVER_INSECURE=1 را همراه دارد (باید 0.0.0.0 را بایند کند تا ترافیک پورت منتشرشده را دریافت کند)، که تنها پشت یک پروکسی معکوس پایان‌دهندهٔ TLS — Caddy، Traefik، nginx یا یک تونل Cloudflare — روی یک شبکهٔ قابل‌اعتماد ایمن است. برای پایان‌دادن TLS درون خود فرایند، به‌جایش یک گواهی مانت کنید و CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY را تنظیم کنید (سپس CC_SERVER_INSECURE را حذف کنید). همه‌چیز با docker run -e … یا بلوک environment: در compose قابل بازنویسی است.

  • /data تنها وضعیت است. پایگاه‌داده‌های SQLite، کلیدهای محرمانهٔ دستگاه‌های جفت‌شده، مدل‌های دانلودشده و صدای جلسه‌ها همگی در این والیوم زندگی می‌کنند — از آن پشتیبان بگیرید و کانتینر یک‌بارمصرف می‌شود.

  • یک کلاینت را از داخل ایمیج جفت کنید پیش از نخستین اتصال؛ معادل کانتینریِ گام ۳:

    پنجرهٔ ترمینال
    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 قابل بازنویسی)؛ بی‌حالت است، هیچ رازی نگه نمی‌دارد و یک جایگزین است — تلفن وقتی مسیری از لوپ‌بک، LAN، tailnet یا مسیر مستقیم wss:// در دسترس باشد از آن استفاده می‌کند و تنها وقتی هیچ‌کدام در دسترس نباشد به رله بازمی‌گردد. اگر کلاینت‌هایتان همیشه مستقیماً به سرور می‌رسند، از آن صرف‌نظر کنید.

بقیهٔ این راهنما سرور را از کد منبع می‌سازد؛ فلگ‌ها و رفتارهایی که پوشش می‌دهد همان‌هایی هستند که متغیرهای محیطی ایمیج تغذیه‌شان می‌کنند.

  • Dart SDK. این مخزن SDK خود را با fvm تثبیت می‌کند، پس هر فرمان را با fvm آغاز کنید.
  • این مخزن چک‌اوت‌شده باشد.
  • زنجیرهٔ ابزار بومی که اسکریپت آماده‌سازی لازم دارد — Rust/cargo، به‌علاوهٔ meson، ninja، pkg-config، git و یک کامپایلر C++. هر اسکریپت ساخت، هر چه را پیدا نکند و روش نصبش را نام می‌برد.
  • یک میزبان قابل‌دسترسی: لوپ‌بک برای استفادهٔ محلی، یا یک نشانی LAN/عمومی همراه 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 می‌نشیند.

گام ۳: پیش از نخستین راه‌اندازی یک کلاینت را جفت کنید

بخش با عنوان “گام ۳: پیش از نخستین راه‌اندازی یک کلاینت را جفت کنید”

یک پوشهٔ دادهٔ تازه هیچ دستگاه جفت‌شده و هیچ کلید پیش‌اشتراکی ندارد، پس درخواست کلید جفت‌سازی یک کلاینت هنوز برآورده‌شدنی نیست. با زیرفرمان 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 چاپ می‌شود، چون لوپ‌بک از ماشینی دیگر قابل‌دسترسی نیست) و --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 می‌سازد هیچ انقضایی ندارد؛ اعتبارنامه‌ای که در برنامه ساخته می‌شود همیشه پس از ۳۰ روز منقضی می‌شود. وقتی کارتان با دستگاهی که 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 اجرا می‌شود، سپس پاک خاموش می‌شود.

یک کلاینت را به URL سرور و کلید جفت‌سازی از گام ۳ اشاره دهید:

  • برنامهٔ رومیزی: در صفحهٔ نخستین اجرا (Control Center چطور اجرا شود؟) به‌جای اجرا در این برنامه فیلدهای اتصال را پر کنید، یا بعداً سرور را در تنظیمات ← سرور ← اتصال و وضعیت اضافه کنید. اتصال به سرور راه دور را ببینید.
  • کلاینت وب: app.usectrl.dev (یا نسخهٔ خودتان از ساخت وب) را باز کنید و URL سرور، شناسهٔ دستگاه و کلید جفت‌سازی را وارد کنید.
  • تلفن: اپ Remote را از یک کلاینت در حال اجرا جفت کنید — جفت‌کردن دستگاه را ببینید.

گام ۶: اتصال یک Google Calendar (اختیاری)

بخش با عنوان “گام ۶: اتصال یک 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 را ببینید.

فلگ‌های استفاده‌شده در بالا همان‌هایی هستند که یک راه‌اندازی نخست لازم دارد. سطح کامل — ۲۳ جفت فلگ و متغیر محیطی، چهار زیرفرمان و چیدمان پوشهٔ داده — در مرجع CLI مربوط به cc_server است.

آن چهارتایی که بیشتر از همه به آن‌ها نیاز پیدا می‌کنید:

فلگ پیش‌فرض معنا
--data-dir پوشهٔ دادهٔ برنامهٔ هر کاربر در سیستم‌عامل پایگاه‌داده‌ها، کلیدهای محرمانه، مدل‌ها و رسانهٔ کش‌شده
--port 9030 پورت TCP (0 = موقت)
--bind loopback loopback، یا any برای همهٔ رابط‌ها (نیازمند TLS یا --insecure)
--public-url برگرفته از بایند URL مربوط به RPC که به کلاینت‌های جفت‌شده اعلام می‌شود. پشت پروکسی، NAT یا تونل این را صریح تنظیم کنید

پوشهٔ دادهٔ پیش‌فرض روی macOS مسیر ~/Library/Application Support/control-center، روی Windows مسیر %APPDATA%\control-center و در جاهای دیگر $XDG_DATA_HOME/control-center (وگرنه ~/.local/share/control-center) است. یک .cc_server نسبی به cwd تنها وقتی استفاده می‌شود که هیچ پوشهٔ خانه یا پوشهٔ دادهٔ برنامه‌ای حل نشود.

cc_server تمام سطح محصول است، نه زیرمجموعه‌ای از آن: مسیر داده و اشتراک‌های repo-RPC، نقطهٔ پایانی ابزار MCP در POST /mcp و GET /sse روی همان پورت، وبهوک‌های ورودی، پروکسی‌های رسانه و قلم، بازخوان‌های ورود یکپارچه، پروتکل اجارهٔ ناوگان، واکشی RSS سمت سرور با زمان‌بندی ۳۰ دقیقه‌ای و جستجوی برداری (افزونهٔ sqlite_vector به‌صورت یک افزونهٔ خودکار سراسریِ فرایند ثبت می‌شود و تنها جایی که آن افزونه در دسترس نباشد به جستجوی متن کامل تنزل می‌کند).

توزیع‌کننده بی‌حالت است: هیچ اتصال فضای کاری به‌ازای هر نشست وجود ندارد. هر فراخوانی در محدودهٔ فضای کاری، workspace_id خودش را حمل می‌کند و دسترسی بر پایهٔ عضویت و نقش فراخوان در فضای کاری تعیین می‌شود — نه با در دست داشتن کلید جفت‌سازی.

دو نکته که پیش از بیرون‌بردن پورت از لوپ‌بک باید بدانید:

  • MCP بیرون از میزبان بسته شکست می‌خورد. یک سطح MCP بدون توکن فقط به لوپ‌بک پاسخ می‌دهد. بایند کردن any آن را گسترده نمی‌کند — درخواستی از بیرون میزبان با “MCP requires a bearer token to answer non-loopback clients” رد می‌شود تا وقتی یکی را پیکربندی کنید (mcp.setToken، یا تنظیمات ← سرور ← سرورهای MCP).
  • GET /healthz طبق طراحی بدون احراز هویت است، با CORS عام، و شناسه، نام، اثر انگشت هویت و نسخهٔ ساخت سرور را منتشر می‌کند. کلاینت‌ها برای پین کردن سرور به اثر انگشت نیاز دارند، پس این عمدی است — اما یعنی هر cc_server قابل‌دسترسی، همین مقدار را به هر کسی که بپرسد افشا می‌کند.