البنية
نظرة عامة
قسم بعنوان “نظرة عامة”تتبع Control Center بنية نظيفة مرتبة حسب الميزة، بنمط المنافذ والمحوّلات. تنتظم قاعدة الشفرة حول مجالات العمل (الميزات)، ويفرض اختبار بنية آلي قواعد اعتماد صارمة.
عميل خفيف فوق خادم بلا واجهة
قسم بعنوان “عميل خفيف فوق خادم بلا واجهة”Control Center بنية عميل خفيف. لا يفتح أي عميل قاعدة البيانات. تملكها عملية cc_server بلا واجهة: ملف global.db صغير، وملف SQLite واحد لكل مساحة عمل يُفتح عند الحاجة. تقدّم العملية البيانات عبر WebSocket RPC. تطبيق الحاسوب ونسخة الويب ورفيق الهاتف كلها عارضات فوق اتصال RPC واحد.
يختار تطبيق الحاسوب عند أول تشغيل كيف يصل إلى خادمه: محلي (يشغّل cc_server على هذا الجهاز ويتحدث عبر loopback، وهو الافتراضي) أو بعيد (يتصل بـ cc_server في مكان آخر عبر WebSocket آمن). عميل الويب بعيد دائمًا. يختار مُحلّل الإقلاع (bootstrap/server_backend.dart) المسار قبل وجود Riverpod، ثم يستبدل rpcClientProvider بالعميل المتصل، فتقرأ كل ميزة وتكتب عبر الخادم لا عبر قاعدة محلية.
طبقة RPC نفسها بلا حالة: الجلسة مصادَق عليها، وغير مربوطة بمساحة عمل. يحمل كل استدعاء خاص بمساحة عمل workspace_id في وسائطه، ويرفض الموزّع الاستدعاء إذا خلا من هذا المعرّف. بذلك يستطيع عميلان على خادم واحد أن يعملا في مساحتي عمل نشطتين مختلفتين، من غير أن يحتفظ الخادم بـ«مساحة عمل حالية» يختلفان عليها. ويُفرض العزل على كل استدعاء، لا على الاتصال.
هذه مساحة عمل Dart واحدة بملف قفل محسوم، تضم 21 عضوًا: 15 حزمة و6 تطبيقات، وجذرها تطبيق الحاسوب والويب (control_center). التطبيقات الستة: الثنائي الخادم بلا واجهة (cc_server)، ومنفّذ الأسطول بلا واجهة (cc_worker)، وتطبيق الويب التقدّمي للهاتف (cc_remote)، ووسيط مرحّل WebSocket (cc_signaling_server)، ومعرض Widgetbook (cc_gallery)، ومضيف العرض العام (cc_demo_server). الحزم الخمس عشرة: cc_ui (نظام التصميم)، وcc_domain (نواة مشتركة بـ Dart خالص: الكيانات وكائنات القيمة والمنافذ والأحداث وطبقة النطاق لكل ميزة)، وcc_harness (نواة الوكيل المضمّنة: حلقة وكيل بـ Dart خالص تضم الرسائل ومنفذ المزوّد والأدوات والضغط والتوجيه والخطافات والوكلاء الفرعيين، بلا dart:io وبلا أي اعتماد آخر على cc_*)، وcc_harness_runtime (محوّل بيئة تشغيل النواة المضمّنة: مزوّدات بث على Dart VM فقط، ووساطة بيانات اعتماد OAuth/PKCE، ومخازن بيانات الاعتماد، ومجموعة الأدوات العامة، ويشغّل الوكلاء من غير واجهة سطر أوامر خارجية مثبّتة)، وcc_rpc (قنوات نقل العميل)، وcc_host (نواة الخادم)، وcc_data (المستودعات البعيدة)، وcc_persistence (قاعدة البيانات على جهة الخادم)، وcc_server_core (تركيب خادم التطبيق مع بيئة تشغيل الهوية والحضور والأسطول والتقييمات)، وcc_infra (محوّلات على Dart VM فقط)، وcc_mcp (أدوات MCP)، وcc_mcp_client (عميل لخوادم MCP الخارجية، يربط أدواتها بالسجل المحلي)، وcc_markdown (محرك Markdown داخل المستودع)، وcc_natives (ورقة FFI)، وsystem_audio_capture. انظر النشر والعملاء لمصفوفة العملاء الكاملة وما يعمل في كل موضع.
قاعدة الاعتماد
قسم بعنوان “قاعدة الاعتماد”Presentation → Application/Providers → Domain ← Infrastructure- طبقة النطاق: كيانات Dart خالصة، وكائنات قيمة، وواجهات مستودعات، ومنافذ، وخدمات نطاق. بلا أي استيراد من البنية التحتية (لا dio ولا drift ولا نماذج شبكة).
- طبقة العرض: الشاشات والودجات ومُخطِرات الحالة. بلا وصول مباشر إلى drift أو DAO أو طبقة البيانات، وكل شيء يمر عبر موفّري Riverpod → المستودعات.
- طبقة البنية التحتية: التنفيذات الملموسة لمنافذ النطاق وواجهات المستودعات. تعتمد على النطاق، ولا ينعكس الاتجاه.
بنية الميزة
قسم بعنوان “بنية الميزة”الشكل الكامل للميزة:
feature_name/├── data/ # Repository implementations, data sources, services, DTOs, mappers├── domain/ # Entities, repository interfaces, ports, use cases├── presentation/ # Screens, widgets, notifiers└── providers/ # Riverpod providersقلّما تحمل ميزة تحت lib/features/ الطبقات الأربع كلها، والفجوات مُخبرة لا عرضية. ضمّت النواة المشتركة مجلد domain/ لكل ميزة إلى cc_domain، والعميل الخفيف لا يحتفظ بتنفيذات المستودعات، فمعظم الميزات تحت lib/ هي presentation/ وproviders/ ولا شيء سواهما. إذا بقي مجلد data/ أو domain/ في العميل، فذلك لأنه يخص العميل فعلًا.
هذان الاستثناءان مسمّيان:
mcpتقتصر على الموفّرات. تعيش واجهة إعداداتها وحالتها تحتsettings/، وسطح الأدوات نفسه حزمة (cc_mcp) لا مجلد ميزة.orchestrationو**plan_studio** لا تحملان إلاpresentation/وproviders/.
تضع ميزة guardrails طبقة نطاقها في cc_domain/features/guardrails/، خارج التطبيق الجذر، لأن الخادم هو الذي يفرضها. تربط القاعدة ActionClass (أو commandPrefix لأمر الصدفة) بقرار، allow أو prompt أو deny، داخل نطاق. تُخزَّن القواعد لكل مساحة عمل، ويُحسم كل إجراء وكيل تخضع له الضوابط عبر سلسلة السياسة قبل أن يُنفَّذ. يتبع الحسم space > agent > workspace > mode preset > built-in default، وأول نطاق فيه قاعدة مطابقة هو الذي يقرر. يقتصر «الأكثر تقييدًا» على كسر التعادل داخل نطاق واحد، وعلى جمع الأصناف التي يصرّح بها إجراء واحد. السطح الذي يراه المشغّل هو الإعدادات → مساحة العمل → أذونات الوكلاء، على /workspaces/<id>/settings/workspace/permissions. انظر الضوابط للنموذج.
النواة المشتركة
قسم بعنوان “النواة المشتركة”يضم packages/cc_domain/lib/core/domain/ الكيانات والمستودعات المشتركة بين ثلاث ميزات فأكثر. هو Dart خالص بلا أي اعتماد على البنية التحتية، وهذا ما يتيح لعميل Flutter وللثنائي الخادم الخالي من Flutter استيراد التعريفات نفسها:
- الكيانات الأساسية:
Agent،AgentRunLog،Workspace،Repo،ReviewSpaceAssociation - الهوية وتعدد اللاعبين:
User،Principal(نوع مختومUserPrincipal|AgentPrincipal)،WorkspaceMember،WorkspaceRole - كيانات الذاكرة:
MemoryFact،MemoryPolicy،AgentWorkingMemory،MemoryAccessGrant - كائنات القيمة المشتركة:
AgentSkills،AgentRole،Mode،SandboxBackend،SandboxSpec،RunCost - المنافذ المشتركة:
SandboxPort،CredentialBrokerPort،WorkspaceFilesystemPort،GitRepoInspectorPort،EmbeddingPort - خدمات النطاق:
MemoryAccessPolicy،ActivityLogger،MentionResolver،RunLivenessClassifier DomainEventBusوأنواع الأحداث (مساحة العمل/الوكيل، طلب السحب/المراجعة، المراسلة، التذاكر، خط الأنابيب، التنسيق، الذاكرة، التقويم/الاجتماعات، الهوية والعضوية، الرصد)
الهوية وتعدد اللاعبين
قسم بعنوان “الهوية وتعدد اللاعبين”Control Center متعدد المستخدمين: البشر والوكلاء فاعلون متكافئون يوحّدهم Principal. عضو مساحة العمل هو User مربوط بـ Workspace بدور WorkspaceRole (owner/admin/member/viewer/guest). العضوية هي حد الوصول. مفتاح الاقتران لا يقوم مقامها، وتمنع المنح لكل مستودع أن تعطي عضوية مساحة العمل صلاحيات أوسع من مضيف الشفرة. أول مستخدم يُنشأ على خادم جديد يملك التثبيت، وينضم الآخرون بدعوة أو عبر OIDC. إنشاء مساحة عمل يختم ذلك المستخدم بدور owner.
التعاون الآني خادم مرجعي مع فوز آخر كتابة لكل حقل (LWW)، من غير CRDT. الحضور (من الحاضر، وأين موقعه، والمؤشرات، والكتابة، وحالة الوكيل الحية، وتكلفة التشغيل الجارية) مسار عابر لا يُحفظ. تسير الحالة الدائمة على عمود فقري من تعديل متفائل، ثم إعادة إرساء على الخادم، ثم LWW. يشترك البشر والوكلاء في كشف واحد. مع مشغّل بشري واحد يبقى مسار الحضور خاملًا ولا تظهر واجهة الكشف، فلا يكلف تعدد اللاعبين المستخدم المنفرد شيئًا.
المنافذ والمحوّلات
قسم بعنوان “المنافذ والمحوّلات”المنافذ واجهات مجردة في طبقة النطاق، والمحوّلات تنفيذات ملموسة في البنية التحتية. يملك الخادم بلا واجهة البيانات، ولا بد أن يُبنى من غير Flutter، فتعمل المحوّلات كلها تقريبًا على Dart VM فقط وتعيش في packages/cc_infra/lib/src/، مرتبة حسب الوظيفة:
| المنفذ | موقع المحوّل |
|---|---|
SandboxPort |
cc_infra/lib/src/sandboxing/ (native_sandbox_adapter.dart) |
CredentialBrokerPort |
cc_infra/lib/src/sandboxing/ (env_credential_broker.dart, github_fine_grained_broker.dart) |
GitRepoInspectorPort |
cc_infra/lib/src/git/ (git_repo_inspector.dart) |
EmbeddingPort |
cc_infra/lib/src/embedding/ (embedding_service.dart) |
NotificationPort |
lib/core/notifications/ (على جهة العميل: لعرض أحداث الخادم محليًا) |
AgentBackend |
cc_infra/lib/src/dispatch/backends/ (acp_backend.dart, harness_backend.dart, cli_backends.dart) |
TicketProviderPort |
cc_infra/lib/src/tickets/linear/ (LinearTicketAdapter). Jira وClickUp وGitHub Issues محوّلات مزامنة تذاكر في cc_infra/lib/src/tickets/sync/، والتذاكر المحلية تعيش في قاعدة مساحة العمل من غير TicketProviderPort. |
PipelineEnginePort |
cc_domain features/pipelines/domain/services/ (خدمة نطاق بـ Dart خالص، لا تحتاج إلى بنية تحتية) |
يربط جذر تركيب التطبيق (di/providers.dart) منافذ العميل بتنفيذاتها عبر موفّري Riverpod، ويركّب الخادم محوّلاته في cc_server_core.
إدارة الحالة
قسم بعنوان “إدارة الحالة”Riverpod لكل حالة العميل:
Notifier<T>وAsyncNotifier<T>للحالة القابلة للتغييرFutureProvider<T>للبيانات غير المتزامنة- تصل القوائم والصفوف الحية عبر اشتراكات RPC على هيئة
AsyncValue<List<T>>. يستخدم الخادم Drift.watch()داخليًا، ولا يفتح أي عميل Drift - تتلقى أدوات MCP اعتماداتها معاملات منشئ ذات أنواع، ولا تتلقى
Refأبدًا
قاعدة البيانات
قسم بعنوان “قاعدة البيانات”Drift (SQLite)، مقسومة إلى قاعدتين، وكلتاهما في cc_persistence:
GlobalDatabase(<dataDir>/global.db): قائمة قصيرة من جداول تخص الخادم كله فعلًا: سجل مساحات العمل، والمستخدمون والتفضيلات والأجهزة المقترنة، واتصالات تسجيل الدخول الموحّد، وإعدادات الخادم على نطاق التثبيت، وسياسات الإجراءات المُدارة (قيد على نطاق التثبيت لا يستطيع إلا تضييق ضوابط مساحة العمل)، وموجز الأخبار لكل مستخدم، وطابور الأسطول (العمال والمهام وسجل الإسناد)، وفهرسworkspace_routesقبل المصادقة، وبيانات تعريف التثبيت. لا يفتح الإقلاع إلا هذا الملف، فتبقى كلفة البدء ثابتة مهما تراكم تاريخ مساحات العمل. إضافة جدول هنا قرار عزل، واختبار السقاطة يُلزمك بتبريره.WorkspaceDatabase(<dataDir>/<workspaceId>/workspace.db): مجلد واحد لكل مساحة عمل، يحمل ملف SQLite واحدًا وكل ما يخص تلك المساحة وحدها، فتبقى أشجار عمل المحادثة وملفات الوكيل إلى جوار القاعدة وتُحذف معها. يعيش هنا كل شيء تقريبًا: الوكلاء والمساحات والتذاكر والذاكرة وخطوط الأنابيب والاجتماعات ومخطط الشفرة والمراجعات والمستودعات. لا تُفتح الملفات إلا عند أول لمس، عبرWorkspaceDatabaseManager.of(workspaceId)، وتتحمّل عندها كلفةquick_checkوتثبيت FTS والمشغّلات وvector_init.
بدأ النصفان من خط أساس v1 مدموج: يبني onCreate المخطط الحالي، ولا تُضاف خطوة ترحيل إلا إذا لزم حمل قاعدة منشورة إلى الأمام.
ولأن مساحة العمل ملف واحد، فتصديرها جملة VACUUM INTO واحدة (workspace.export)، لا تفريغًا جدولًا بعد جدول. يتبنّى workspace.import هذا الملف، ويستعين بـ workspace_meta المضمّن ليميّز إعادة تبنّي ملفه هو من ملف جاء من تثبيت آخر.
تملك cc_persistence القاعدتين، ولا يفتحهما إلا cc_server. لا يفتح أي عميل قاعدة بيانات، وكل قراءة وكتابة من العميل تمر عبر RPC. تُعرَّف الجداول في packages/cc_persistence/lib/database/tables/، وكائنات DAO في .../daos/. كيانات النطاق Dart خالص، منفصلة عن أصناف جداول Drift، والتحويل في طبقات البيانات. يتولى FTS5 البحث النصي الكامل وتضمينات sqlite_vector، وكلاهما على الخادم فقط. القاعدتان تشغّلان PRAGMA foreign_keys=ON في وضع دفتر WAL.
التوجيه
قسم بعنوان “التوجيه”go_router، وحقيقة واحدة تحكم ما عداها: كل وجهة داخل التطبيق مسبوقة بمساحة العمل على الشكل /workspaces/:workspaceId/…. معرّف مساحة العمل في العنوان هو الذي يحدد مساحة العمل النشطة. يُشتق activeWorkspaceIdProvider من المسار لا العكس، ولذا لا تجد قراءة البيانات المرتبطة بمساحة العمل، خارج مسار مسبوق، معرّفًا تأخذه.
- بناء المسارات دوال في
router/routes.dart، تأخذ كل منها معرّف مساحة العمل وسيطًا أول، لا ثوابت نصية - أربعة مسارات قبل السياق، بلا بادئة:
/splashو/onboardingو/signed-outو/workspaces(منتقي مساحات العمل) - يغلّف
ShellRouteغلاف التطبيق، وتظهر تلك الأسطح الأربعة بملء الشاشة خارجه - حارس المصادقة: الإعداد غير المكتمل ينتقل إلى
/onboarding، والإعداد المكتمل إذا زالت بيانات اعتماد مضيف شفرته ينتقل إلى/signed-out. الاكتمال يعني اتصال مضيف شفرة واحد على الأقل للمستخدم المسجّل دخوله، ووجود مساحة عمل واحدة على الأقل. لا يُشترط أن يكون المضيف GitHub، وسطر أوامرghليس وسيلة مصادقة - بوابة الإعداد الأولي في
features/auth/providers/
الشبكات (على جهة الخادم)
قسم بعنوان “الشبكات (على جهة الخادم)”كل HTTP الخارجي في الخادم. لا يتصل العملاء بـ GitHub أو Linear أو Google مباشرة. يستدعون عمليات RPC على الخادم، وحتى الوسائط البعيدة تُجلب عبر نقطة /proxy/media على الخادم لا من المصدر الأصلي.
- عملاء dio في
packages/cc_infra/lib/src/:GitHubApiClientوGitHubPrClientوGitHubContentClientوGitHubGraphQLClientوGitLabApiClientوBitbucketApiClientوLinearGraphQlClientوGoogleCalendarApiClient - تُحقن رموز المصادقة عبر المعترِضات، ومنها معترِض Google OAuth لكل حساب في
cc_server_coreيجدّد الرموز عند 401 - تُحوَّل الأخطاء كلها عبر
packages/cc_infra/lib/src/network/error_mapper.dartإلى أصناف فرعية منAppExceptionمعرَّفة فيcc_domain
الأمان
قسم بعنوان “الأمان”يحتفظ الخادم ببيانات الاعتماد التي تهم: رموز GitHub ورموز التذاكر لإطلاق الوكلاء، ومفاتيح مزوّدي LLM، ورموز OAuth، كلها تحت مجلد بيانات الخادم أو في بيئة تشغيله. ما بقي في سلسلة مفاتيح العميل هو بيانات اعتماد جهازه ومفتاح الاقتران لخادم بعيد.
- الأسرار في
flutter_secure_storage(سلسلة مفاتيح macOS، ومدير بيانات الاعتماد في Windows، وlibsecret على Linux): بيانات اعتماد جهاز العميل ومفاتيح الاقتران البعيد، لا رموز المزوّدين shared_preferencesللتفضيلات غير الحساسة فقط (السمة والخط والتخطيط)- تُسلَّم أسرار المزوّدين المتبقية من تثبيتات أقدم إلى الخادم مرة واحدة عبر
features/auth/providers/credential_migration.dart، ثم تُحذف من سلسلة مفاتيح العميل
فرض البنية
قسم بعنوان “فرض البنية”تُفحص قيود البنية في test/core/architecture_constraints_test.dart، ويفشل الاختبار إذا انتُهكت قواعد الاعتماد.
مفاهيم ذات صلة
قسم بعنوان “مفاهيم ذات صلة”- مساحات العمل والعزل: كيف يُفرض العزل
- دورة حياة إرسال الوكيل: تدفق الإرسال
- أحداث النطاق: التواصل بين الميزات
- النشر والعملاء: نموذج العميل الخفيف فوق الخادم ومصفوفة العملاء