הפעלת שרת ללא ממשק
הריצו את 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 מפיק מחדש כל מה שהוכן שם אל תוך
החבילה, ולכן דילוג על השלב הזה או מכשיל את הבנייה לחלוטין או — עם פתח המילוט
שלהלן — מייצר בינארי שבדיקת הטרום־עלייה שלו מסרבת להתחיל ומציינת את הספרייה
החסרה.
שלב 2: בניית הבינארי
קטע בשם “שלב 2: בניית הבינארי”cd apps/cc_serverfvm 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 מהגדרות → אישי → המכשירים
שלכם כשסיימתם איתו.
שלב 4: הפעלת השרת
קטע בשם “שלב 4: הפעלת השרת”בלולאה המקומית — ברירת המחדל וכל מה שאתם צריכים ללקוח על אותה מכונה:
./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, ואז נסגר באופן נקי.
שלב 5: חיבור לקוח
קטע בשם “שלב 5: חיבור לקוח”כוונו לקוח אל כתובת השרת ומפתח הצימוד משלב 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נגיש חושף את כל זה לכל מי שמבקש.