Μετάβαση στο περιεχόμενο

Εκτέλεση headless διακομιστή

Εκτελέστε το cc_server σε ένα μηχάνημα που μένει ανοιχτό και μετά συνδεθείτε σε αυτόν από την εφαρμογή υπολογιστή, ένα πρόγραμμα περιήγησης ή το τηλέφωνό σας. Ο διακομιστής είναι ένα εγγενές δυαδικό αποκλειστικά Dart — χωρίς μηχανή Flutter — οπότε εκτελείται headless σε macOS, Linux ή Windows. Κατέχει τη βάση δεδομένων, εκτελεί τους πράκτορες και κάνει κάθε εξωτερική κλήση API· οι πελάτες απλώς αποδίδουν.

Κάθε έκδοση δημοσιεύει μια στοίβα αυτοφιλοξενίας τεσσάρων εικόνων 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 Ο πελάτης web (στατικό 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, LAN, tailnet ή απευθείας διαδρομή wss:// όταν κάποια είναι προσβάσιμη, και καταφεύγει στον αναμεταδότη μόνο όταν καμία δεν είναι. Παραλείψτε τον αν οι πελάτες σας φτάνουν πάντα απευθείας στον διακομιστή.

Το υπόλοιπο αυτού του οδηγού δημιουργεί τον διακομιστή από τον πηγαίο κώδικα· οι σημαίες και η συμπεριφορά που καλύπτει είναι οι ίδιες που τροφοδοτούν οι μεταβλητές περιβάλλοντος της εικόνας.

  • Το Dart SDK. Αυτό το αποθετήριο κλειδώνει το SDK του με το fvm, οπότε βάλτε πρόθεμα fvm σε κάθε εντολή.
  • Αυτό το αποθετήριο αποσπασμένο τοπικά.
  • Την εγγενή εργαλειοθήκη που χρειάζεται το σενάριο προετοιμασίας — Rust/cargo, καθώς και meson, ninja, pkg-config, git και μεταγλωττιστή C++. Κάθε σενάριο δημιουργίας ονομάζει τι δεν βρίσκει και πώς να το εγκαταστήσετε.
  • Έναν προσβάσιμο κεντρικό υπολογιστή: loopback για τοπική χρήση, ή διεύθυνση LAN/δημόσια με TLS για απομακρυσμένους πελάτες.

Βήμα 1: Προετοιμάστε τις εγγενείς βιβλιοθήκες

Ενότητα με τίτλο «Βήμα 1: Προετοιμάστε τις εγγενείς βιβλιοθήκες»

Οι εγγενείς βιβλιοθήκες είναι υποχρεωτικές — δεν υπάρχει υποβαθμισμένη λειτουργία. Δημιουργήστε τις από τη ρίζα του αποθετηρίου πριν από οτιδήποτε άλλο:

Παράθυρο τερματικού
scripts/natives/build_natives.sh

Αυτό δημιουργεί τα rift, fff, το tree-sitter μαζί με τις γραμματικές του, τα aec, lame, ccpty, cc_watcher, cc_inference και το εγγενές SAML στον κατάλογο build/natives/. Το hook δημιουργίας του 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, οτιδήποτε άλλο είναι πελάτης web), --label (προεπιλέγεται το όνομα της πλατφόρμας συν το όνομα κεντρικού υπολογιστή αυτού του μηχανήματος), --host (ο κεντρικός υπολογιστής που εκτυπώνεται στο URL, αφού το loopback δεν είναι προσβάσιμο από άλλο μηχάνημα) και --client-url. Η εκ νέου εκτέλεση του pair εναλλάσσει το κλειδί αυτής της συσκευής και ρίχνει τις ζωντανές συνεδρίες της. Η σύζευξη δεν δημιουργεί ποτέ χώρο εργασίας: η ροή αρχικής ρύθμισης του πελάτη που συνδέεται ονομάζει και δημιουργεί τον πρώτο.

Με το --client-url εκτυπώνει επίσης έναν κωδικό QR σαρώσιμο από τερματικό που ανοίγει τον πελάτη web ήδη συμπληρωμένο:

Παράθυρο τερματικού
./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;) συμπληρώστε τα πεδία σύνδεσης αντί για Εκτέλεση σε αυτή την εφαρμογή, ή προσθέστε τον διακομιστή αργότερα στις Ρυθμίσεις → Διακομιστής → Σύνδεση και κατάσταση. Δείτε Σύνδεση σε απομακρυσμένο διακομιστή.
  • Πελάτης web: ανοίξτε το app.usectrl.dev (ή το δικό σας αντίγραφο του build web) και εισαγάγετε το 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 για έγκριση σε άλλη συσκευή, αποθηκεύει το refresh token στην πλευρά του διακομιστή και τερματίζεται. Ένα build από πηγαίο κώδικα δεν περιλαμβάνει πελάτη Google, οπότε οι μεταβλητές περιβάλλοντος id και secret είναι υποχρεωτικές — δεν υπάρχει σημαία --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 σχετικό με τον τρέχοντα κατάλογο εργασίας χρησιμοποιείται μόνο όταν δεν επιλύεται κατάλογος home ή δεδομένων εφαρμογών.

Ο cc_server είναι ολόκληρη η επιφάνεια του προϊόντος, όχι υποσύνολό της: το μονοπάτι δεδομένων repo-RPC και οι συνδρομές, το τελικό σημείο εργαλείων MCP στο POST /mcp και στο GET /sse στην ίδια θύρα, τα εισερχόμενα webhooks, οι διαμεσολαβητές πολυμέσων και γραμματοσειρών, οι επιστροφές κλήσεων ενιαίας σύνδεσης, το πρωτόκολλο μίσθωσης του στόλου, η ανάκτηση RSS στην πλευρά του διακομιστή σε πρόγραμμα 30 λεπτών και η αναζήτηση διανυσμάτων (η επέκταση sqlite_vector καταχωρίζεται ως αυτόματη επέκταση καθολικής εμβέλειας της διεργασίας, υποβαθμιζόμενη σε αναζήτηση πλήρους κειμένου μόνο όπου η επέκταση δεν είναι διαθέσιμη).

Ο dispatcher είναι χωρίς κατάσταση: δεν υπάρχει δέσμευση χώρου εργασίας ανά συνεδρία. Κάθε κλήση με εμβέλεια χώρου εργασίας μεταφέρει το δικό της workspace_id και η πρόσβαση κρίνεται από τη συμμετοχή και τον ρόλο του καλούντος στον χώρο εργασίας — όχι από την κατοχή κλειδιού σύζευξης.

Δύο πράγματα που πρέπει να γνωρίζετε πριν εκθέσετε τη θύρα πέρα από το loopback:

  • Το MCP αποτυγχάνει κλειστά εκτός κεντρικού υπολογιστή. Μια επιφάνεια MCP χωρίς token απαντά μόνο στο loopback. Η δέσμευση any δεν τη διευρύνει — ένα αίτημα εκτός κεντρικού υπολογιστή απορρίπτεται με «MCP requires a bearer token to answer non-loopback clients» μέχρι να διαμορφώσετε ένα (mcp.setToken, ή Ρυθμίσεις → Διακομιστής → Διακομιστές MCP).
  • Το GET /healthz είναι χωρίς ταυτοποίηση εκ σχεδιασμού, με CORS μπαλαντέρ, και δημοσιεύει το αναγνωριστικό, το όνομα, το αποτύπωμα ταυτότητας και την έκδοση build του διακομιστή. Οι πελάτες χρειάζονται το αποτύπωμα για να καρφιτσώσουν τον διακομιστή, οπότε αυτό είναι σκόπιμο — σημαίνει όμως ότι οποιοσδήποτε προσβάσιμος cc_server αποκαλύπτει τόσα σε όποιον ρωτήσει.