Architektur-Übersicht
AISCouncil ist eine Zero-Hosting-Bot-Management-Plattform, die vollständig im Browser läuft. Der Produktions-Output ist eine einzelne index.html-Datei (~980 KB), die aus 80 modularen Quelldateien zusammengesetzt wird. Es gibt keine externen Laufzeit-Abhängigkeiten – nur JavaScript und WASM werden an den Browser ausgeliefert.
Single-File-Architektur
Die App ist eine einzelne in sich geschlossene HTML-Datei. Während der Entwicklung ist sie in src/-Teile für kleinere Kontextfenster und fokussiertes Editieren aufgeteilt. Das build.sh-Skript konkateniert alle 80 Teile zurück in index.html in einer strikten Reihenfolge.
src/shell-head.html \
src/shell-style.html |
src/shell-body.html |
src/core-boot.js |
src/core-auth-main.js | build.sh
... | ---------> index.html (~980 KB)
src/settings-main.js |
src/miniprograms.js |
src/pwa.js |
src/shell-bottom.js /
Bearbeiten Sie index.html niemals direkt. Bearbeiten Sie immer die entsprechende Datei in src/, dann führen Sie ./build.sh aus, um neu zusammenzusetzen.
Globaler Namespace
Alle Module registrieren sich auf dem window.AIS globalen Namespace. Der Namespace wird in src/core-boot.js initialisiert:
window.AIS = { version: "2.0.0", type: "aiscouncil" };
AIS.PLATFORM_VERSION = "1.0.0";
AIS.ABI_VERSION = 1;
Cross-Module-Kommunikation verwendet einen leichtgewichtigen Event-Bus:
AIS.on("event", handler); // abonnieren
AIS.off("event", handler); // abbestellen
AIS.emit("event", data); // veröffentlichen
Modul-System: AIS.lazy()
Module verwenden ein Qwik-ähnliches Lazy-Hydration-Muster. Die Factory-Funktion wird zurückgestellt, bis das Modul erstmals über einen Property-Getter auf AIS zugegriffen wird:
if (!AIS.Council)
AIS.lazy("Council", function () {
"use strict";
// Modul-Code hier
return { run, renderCouncilMessage, estimateCost };
});
Beim ersten Zugriff (z.B. AIS.Council.run()) feuert der Getter, führt die Factory aus, ersetzt sich selbst mit dem zurückgegebenen Modul-Objekt und gibt es zurück. Nachfolgende Zugriffe treffen auf den einfachen Wert ohne Overhead.
WASM-Kernel-Module können ein JS-Modul weiterhin überschreiben, indem sie AIS.Council = wasmModule vor dem ersten Zugriff setzen, da die Property als configurable: true definiert ist.
Modul-Kategorien
Core-Module (immer geladen)
Core-Module sind innerhalb eines einzelnen <script>-Blocks definiert (core-boot.js bis core-end.js). Sie werden beim Laden der Seite eager ausgeführt und bilden das Fundament der App.
| Modul | Datei | Zweck |
|---|---|---|
AIS.Auth | core-auth-main.js, core-auth-local.js, core-auth-init.js | WebAuthn/Passkey + OAuth-Login, Session-Management, Auth-Cookie |
AIS.Billing | core-billing.js | Abonnement-Stufe, Trial, verwalteter Plan |
AIS.Codec | core-codec.js | Base80-Kodierung, VLQ-Versionierung, Deflate-Komprimierung |
AIS.Storage | core-storage.js | IndexedDB + optionales SQLite WASM, Offline-First |
AIS.Providers | core-providers.js, core-providers-builtin.js | LLM-Anbieter-Registry, SSE-Streaming-Factory |
AIS.UI | core-ui.js | DOM-Utilities, Markdown-Renderer, Toast-Benachrichtigungen |
AIS.Session | core-session.js | Bot-Sitzung-CRUD (IndexedDB-gestützt) |
AIS.Chat | core-chat.js | Chat-Verlauf, Streaming-Nachrichten senden/empfangen |
AIS.Config | core-config.js | Bot-Konfigurations-Panel-Bindings, URL-Sync |
AIS.App | core-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js | Anwendungs-Controller, Init, Routing, Bot-Management |
WASM-Ersetzbare Module (lazy-loaded)
Jedes WASM-ersetzbare Modul lebt in seinem eigenen <script>-Block und verwendet das AIS.lazy()-Muster. Sie werden nur ausgeführt, wenn erstmals darauf zugegriffen wird.
| Modul | Datei | Zweck |
|---|---|---|
AIS.Registry | registry.js | Community-Modell-Registry, 24h-Cache, GitHub-Fallback |
AIS.Grid | grid.js | Modell-Tabellen-/Karten-Renderer (aus TypeScript kompiliert) |
AIS.Council | council.js | Multi-Modell-Deliberations-Engine (7 Stile) |
AIS.Wizard | wizard.js | Dual-Mode First-Run-Setup-Wizard |
AIS.Vision | vision.js | Bildeingabe für Vision-Modelle (Einfügen/Hochladen) |
AIS.Memory | memory.js | Persistenter Pro-Bot-Schlüssel-Wert-Speicher |
AIS.ImageGen | imagegen.js | Bildgenerierung (DALL-E, Grok Imagine, OpenRouter) |
AIS.Tools | tools.js | Tool/Function-Calling-Format-Normalisierung |
AIS.Reminders | reminders.js | Geplante Nachrichten via /remind-Befehl |
AIS.Themes | themes.js | Visuelles Theme-System |
AIS.Templates | templates-registry.js | System-Prompt-Templates, Willkommensbildschirme |
AIS.ModelPicker | model-picker.js | Sortierbarer Modell-Browser |
Infrastruktur-Module (lazy-loaded)
| Modul | Datei | Zweck |
|---|---|---|
AIS.ModuleLoader | moduleloader.js | Hot-Swap-Modul-Lebenszyklus, OPFS-Cache |
AIS.Plugins | plugins.js | Plugin-System: Manifest-Validierung, Hooks |
AIS.MCP | mcp.js | Model Context Protocol (Tools + Resources) |
AIS.Channels | channels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js | Kanal-Adapter (Telegram, Discord, Matrix, Slack, WhatsApp) |
AIS.Sandbox | sandbox.js | WASM-Tool-Sandbox (Pyodide, QuickJS, SQLite) |
AIS.Publish | publish.js | SEO + statisches HTML-Publishing |
AIS.Perf | perf.js | Performance-Monitoring |
AIS.P2P | p2p.js | P2P-Kollaboration via WebRTC + CRDTs |
Plattform-Module
| Modul | Datei | Zweck |
|---|---|---|
AIS.Settings | settings-main.js | Globaler Einstellungsdialog (alle Bereiche) |
AIS.I18n | i18n.js | Internationalisierung |
AIS.MiniPrograms | miniprograms.js | Mini-Programm-Laufzeit (gesandboxte iframes) |
AIS.Docs | docs.js | Inline-Docs-Viewer |
AIS.Profiles | profiles.js | Multi-Modell-Profil/Council-Templates |
AIS.Cron | cron.js | Browser-basierter Scheduler |
CSS-Architektur
Die App verwendet einen klassenlosen CSS-Ansatz. Styles zielen auf semantische HTML-Elemente und IDs statt auf Klassen.
| Selektor-Typ | Beispiel | Anwendungsfall |
|---|---|---|
| ID | #header, #sidebar-left, #config-body | Eindeutige Layout-Container |
| Semantisches Element | #messages > article, article menu button | Nachrichten, Aktionen, Navigation |
| Data-Attribut | [data-from="user"], [data-variant="primary"] | Varianten (Nachrichten-Rolle, Button-Typ) |
| State-Klasse | .active, .collapsed, .mobile-open | JS-getoggelte Zustände |
| Komponenten-Klasse | .council-member-row, .status-dot | Dynamische JS-erstellte Komponenten |
| Utility-Klasse | .f1, .sc, .dim | Einmalige Inline-Style-Ersetzung |
Verwendete semantische HTML-Elemente: <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.
Klassen werden nur für drei Fälle verwendet: State-Toggles (.active, .collapsed), dynamische JS-Komponenten (.council-member-row) und Utility-Shorthands (.f1, .sc). Wenn Sie ein Element mit einem semantischen Selektor oder Data-Attribut ansteuern können, tun Sie das statt eine Klasse hinzuzufügen.
Speicher-Architektur
Der Speicher ist zwischen synchronen und asynchronen Stores aufgeteilt:
| Speicher | Schlüssel | Grund |
|---|---|---|
| localStorage (sync) | ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint | Synchrone Lesevorgänge zur Boot-Zeit benötigt |
| IndexedDB (async, unbegrenzt) | ais-bots, ais-chat-*, ais-addon-manifests | Große Daten, kein 5 MB Limit |
| SQLite WASM (optional, OPFS) | Binäre Blobs | Lazy-loaded für gleichzeitigen Zugriff |
Beim ersten Boot migriert AIS.Storage.init() Daten automatisch von localStorage zu IndexedDB.
Anbieter-Architektur
Anbieter werden über AIS.Providers.register() registriert. Die meisten Anbieter verwenden die gemeinsame openaiCompatible() SSE-Streaming-Factory, die Folgendes handhabt:
- Server-Sent Events Parsing
- Token-by-Token-Streaming-Callbacks
- AbortController-Signal-Unterstützung
- Token-Zählung aus Response-Headern
Sechs eingebaute Anbieter sind in core-providers-builtin.js registriert:
| Anbieter | API-Stil | Auth | Hinweise |
|---|---|---|---|
| Anthropic | Native (Messages API) | x-api-key-Header | Benutzerdefiniertes Streaming-Format |
| OpenAI | OpenAI-kompatibel | Bearer-Token | Standard-SSE |
| xAI | OpenAI-kompatibel | Bearer-Token | Grok-Modelle |
| Google Gemini | Native (Gemini API) | ?key=-Query-Param | Vermeidet CORS-Preflight |
| OpenRouter | OpenAI-kompatibel | Bearer-Token | 300+ Modelle, kostenloses Tier |
| Ollama | OpenAI-kompatibel | Keine | Lokale LLMs, erkennt Modelle automatisch |
Alle API-Schlüssel werden lokal gespeichert (localStorage['ais-apikey-{anbieter}']). Schlüssel werden direkt vom Browser an den Anbieter gesendet – nie durch einen Server proxiert.
Design-Prinzipien
Die Codebasis folgt strengen Design-Prinzipien, die jede Änderung erfüllen muss:
- HTML5-nativ zuerst – Verwenden Sie
<dialog>,<output>,hidden-Attribut, CSS-Animationen vor JS-Äquivalenten. - Kein Polling – Keine
requestAnimationFrame-Loops, keinsetInterval, keinsetTimeoutfür Animationen. Nur Event-gesteuert. - Passive Listener – Alle nicht-abbrechbaren Events verwenden
{ passive: true }. - Event-Delegation – Ein einzelner Listener auf dem Container, keine Per-Item-Listener auf dynamischen Listen.
- 14px minimale Schriftgröße – Der gesamte Text muss für Vision-LLMs lesbar sein. Kein Text unter 14px.
- VLM-freundliches Layout – 48px+ Click-Targets, große Toggles, hoher Kontrast. Einstellungen und Menüs zeigen alle primären Items ohne Scrollen auf 1920x1080.
- Reduzierte Bewegung –
@media (prefers-reduced-motion: reduce)deaktiviert alle Animationen. - CSS-Containment –
contain: strictauf Sidebars,content-visibility: autoauf scrollbaren Listen.
Bevor Sie ein Feature hinzufügen, stellen Sie die vier Entscheidungstor-Fragen:
- Kann der Browser das nativ?
- Erfordert dies einen Server? (Wenn ja, machen Sie es optional.)
- Skaliert dies auf 1 Million Bots pro Gerät?
- Was sind die Speicher- und CPU-Kosten?
WASM-Kernel
Der optionale WASM-Kernel (kernel/) ist in Zig geschrieben und kompiliert zu ~5,5 KB. Er stellt Low-Level-Primitiven für das Modul-System bereit: Slot-Management, Hook-Dispatch, Ring-Buffer-I/O und Write-Ahead-Logging.
Der Kernel operiert auf einem 64 MB SharedArrayBuffer mit definierten Speicherregionen für Modul-Segmente, Ring-Buffer, WAL und Scratch-Arena. JS-Module können zur Laufzeit durch WASM-Äquivalente ersetzt werden, ohne den Lazy-Loading-Vertrag zu brechen.
API-Architektur
Die Plattform verwendet aufgeteilte Workers für verschiedene Belange:
| Worker | Domain | Zweck |
|---|---|---|
aiscouncil-api | api.aiscouncil.net | Billing, Nutzung, Key-Vending, Geo |
aiscouncil-auth | auth.aiscouncil.net | OAuth-Callbacks, Token-Verifizierung |
API-Basis-URLs werden beim Boot aus Domain-Erkennung gesetzt und können über localStorage['ais-api-base'] für lokale Entwicklung überschrieben werden.