דלגו לתוכן

הפעלת שרת ללא ממשק

הריצו את cc_server על מכונה שנשארת דלוקה, ואז התחברו אליו משולחן העבודה, מדפדפן או מהטלפון. השרת הוא בינארי נייטיבי של Dart טהור — בלי מנוע Flutter — ולכן הוא רץ ללא ממשק ב-macOS, ב-Linux וב-Windows. הוא מחזיק במסד הנתונים, מריץ את הסוכנים ומבצע כל קריאת API חיצונית; הלקוחות רק מרנדרים.

הרצה ב-Docker (תמונות מוכנות מראש)

קטע בשם “הרצה ב-Docker (תמונות מוכנות מראש)”

כל גרסה מפרסמת ערימה באירוח עצמי של ארבע תמונות GHCR — ה-backend, שני הלקוחות הדקים הסטטיים וממסר הצימוד האופציונלי — ובנוסף תמונת cc-server-demo נפרדת עבור מארח ההדגמה הציבורי. התקנה באירוח עצמי אינה מחייבת הוצאה של קוד המקור. נעצו תג גרסה (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) או הישארו על latest.

תמונה מה היא מפרסמת
ghcr.io/samuelalev/cc-server ה-backend: מסד נתונים, סוכנים, MCP, webhooks -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 … או עם בלוק ה-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 סטטיים — אין מה להגדיר בזמן ריצה; טופס ההתחברות של כל לקוח מקבל את כתובת השרת, מזהה המכשיר ומפתח הצימוד. ממסר האיתות גם הוא זקוק רק לפורט שלו (SIGNALING_HOST / SIGNALING_PORT ניתנים לעקיפה); הוא חסר מצב, אינו מחזיק סודות והוא נסיגה — טלפון משתמש בלולאה המקומית, ב-LAN, ב-tailnet או בנתיב wss:// ישיר כשאחד מהם נגיש, ורק כשאף אחד מהם אינו נגיש הוא נופל חזרה אל הממסר. דלגו עליו אם הלקוחות שלכם תמיד מגיעים ישירות לשרת.

שאר המדריך הזה בונה את השרת מקוד המקור; הדגלים וההתנהגות שהוא מכסה הם אותם דגלים שמשתני הסביבה של התמונה מזינים.

  • Dart SDK. המאגר הזה נועל את גרסת ה-SDK שלו באמצעות fvm, ולכן הקדימו fvm לכל פקודה.
  • המאגר הזה, כשהוא זמין בהוצאה מקומית.
  • שרשרת הכלים הנייטיבית שסקריפט ההכנה זקוק לה — Rust/cargo, ובנוסף meson, ninja, pkg-config, git ומהדר C++. כל סקריפט בנייה מפרט מה הוא לא מצליח למצוא וכיצד להתקין אותו.
  • מארח נגיש: לולאה מקומית לשימוש מקומי, או כתובת LAN/ציבורית עם TLS ללקוחות מרוחקים.

שלב 1: הכנת הספריות הנייטיביות

קטע בשם “שלב 1: הכנת הספריות הנייטיביות”

הספריות הנייטיביות הן חובה — אין מצב מופחת. בנו אותן משורש המאגר לפני כל דבר אחר:

חלון מסוף
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, מאחר שלולאה מקומית אינה נגישה ממכונה אחרת) ו---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

--insecure נדחה כשגם --tls-cert וגם --tls-key מוגדרים — TLS תמיד מנצח. השרת מפרסם את מצב הטקסט הגלוי כ-insecure: true ב-GET /healthz, והלקוחות מתייגים חיבור כזה כלא מאובטח.

השרת רץ עד SIGINT/SIGTERM, ואז נסגר באופן נקי.

כוונו לקוח אל כתובת השרת ומפתח הצימוד משלב 3:

  • אפליקציית שולחן עבודה: במסך ההפעלה הראשונה (כיצד להריץ את Control Center?) מלאו את שדות ההתחברות במקום הרץ באפליקציה זו, או הוסיפו את השרת מאוחר יותר בהגדרות → שרת → חיבור וסטטוס. ראו התחברות לשרת מרוחק.
  • לקוח הווב: פתחו את app.usectrl.dev (או עותק משלכם של בניית הווב) והזינו את כתובת השרת, מזהה המכשיר ומפתח הצימוד.
  • טלפון: צמדו את אפליקציית 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 נגזרת מהאגדה כתובת ה-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 הוא כל משטח המוצר, לא תת־קבוצה שלו: נתיב הנתונים והמינויים של RPC המאגר, נקודת הקצה של כלי MCP ב-POST /mcp וב-GET /sse על אותו פורט, webhooks נכנסים, פרוקסי המדיה והגופנים, חזרות כניסה מאוחדת, פרוטוקול החכירה של הצי, שליפת RSS בצד השרת לפי לוח זמנים של 30 דקות וחיפוש וקטורי (ההרחבה 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 נגיש חושף את כל זה לכל מי שמבקש.