Zum Hauptinhalt springen

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 /
Warnung

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.

ModulDateiZweck
AIS.Authcore-auth-main.js, core-auth-local.js, core-auth-init.jsWebAuthn/Passkey + OAuth-Login, Session-Management, Auth-Cookie
AIS.Billingcore-billing.jsAbonnement-Stufe, Trial, verwalteter Plan
AIS.Codeccore-codec.jsBase80-Kodierung, VLQ-Versionierung, Deflate-Komprimierung
AIS.Storagecore-storage.jsIndexedDB + optionales SQLite WASM, Offline-First
AIS.Providerscore-providers.js, core-providers-builtin.jsLLM-Anbieter-Registry, SSE-Streaming-Factory
AIS.UIcore-ui.jsDOM-Utilities, Markdown-Renderer, Toast-Benachrichtigungen
AIS.Sessioncore-session.jsBot-Sitzung-CRUD (IndexedDB-gestützt)
AIS.Chatcore-chat.jsChat-Verlauf, Streaming-Nachrichten senden/empfangen
AIS.Configcore-config.jsBot-Konfigurations-Panel-Bindings, URL-Sync
AIS.Appcore-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.jsAnwendungs-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.

ModulDateiZweck
AIS.Registryregistry.jsCommunity-Modell-Registry, 24h-Cache, GitHub-Fallback
AIS.Gridgrid.jsModell-Tabellen-/Karten-Renderer (aus TypeScript kompiliert)
AIS.Councilcouncil.jsMulti-Modell-Deliberations-Engine (7 Stile)
AIS.Wizardwizard.jsDual-Mode First-Run-Setup-Wizard
AIS.Visionvision.jsBildeingabe für Vision-Modelle (Einfügen/Hochladen)
AIS.Memorymemory.jsPersistenter Pro-Bot-Schlüssel-Wert-Speicher
AIS.ImageGenimagegen.jsBildgenerierung (DALL-E, Grok Imagine, OpenRouter)
AIS.Toolstools.jsTool/Function-Calling-Format-Normalisierung
AIS.Remindersreminders.jsGeplante Nachrichten via /remind-Befehl
AIS.Themesthemes.jsVisuelles Theme-System
AIS.Templatestemplates-registry.jsSystem-Prompt-Templates, Willkommensbildschirme
AIS.ModelPickermodel-picker.jsSortierbarer Modell-Browser

Infrastruktur-Module (lazy-loaded)

ModulDateiZweck
AIS.ModuleLoadermoduleloader.jsHot-Swap-Modul-Lebenszyklus, OPFS-Cache
AIS.Pluginsplugins.jsPlugin-System: Manifest-Validierung, Hooks
AIS.MCPmcp.jsModel Context Protocol (Tools + Resources)
AIS.Channelschannels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.jsKanal-Adapter (Telegram, Discord, Matrix, Slack, WhatsApp)
AIS.Sandboxsandbox.jsWASM-Tool-Sandbox (Pyodide, QuickJS, SQLite)
AIS.Publishpublish.jsSEO + statisches HTML-Publishing
AIS.Perfperf.jsPerformance-Monitoring
AIS.P2Pp2p.jsP2P-Kollaboration via WebRTC + CRDTs

Plattform-Module

ModulDateiZweck
AIS.Settingssettings-main.jsGlobaler Einstellungsdialog (alle Bereiche)
AIS.I18ni18n.jsInternationalisierung
AIS.MiniProgramsminiprograms.jsMini-Programm-Laufzeit (gesandboxte iframes)
AIS.Docsdocs.jsInline-Docs-Viewer
AIS.Profilesprofiles.jsMulti-Modell-Profil/Council-Templates
AIS.Croncron.jsBrowser-basierter Scheduler

CSS-Architektur

Die App verwendet einen klassenlosen CSS-Ansatz. Styles zielen auf semantische HTML-Elemente und IDs statt auf Klassen.

Selektor-TypBeispielAnwendungsfall
ID#header, #sidebar-left, #config-bodyEindeutige Layout-Container
Semantisches Element#messages > article, article menu buttonNachrichten, Aktionen, Navigation
Data-Attribut[data-from="user"], [data-variant="primary"]Varianten (Nachrichten-Rolle, Button-Typ)
State-Klasse.active, .collapsed, .mobile-openJS-getoggelte Zustände
Komponenten-Klasse.council-member-row, .status-dotDynamische JS-erstellte Komponenten
Utility-Klasse.f1, .sc, .dimEinmalige Inline-Style-Ersetzung

Verwendete semantische HTML-Elemente: <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.

Info

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:

SpeicherSchlüsselGrund
localStorage (sync)ais-theme, ais-apikey-*, ais-user, ais-ollama-endpointSynchrone Lesevorgänge zur Boot-Zeit benötigt
IndexedDB (async, unbegrenzt)ais-bots, ais-chat-*, ais-addon-manifestsGroße Daten, kein 5 MB Limit
SQLite WASM (optional, OPFS)Binäre BlobsLazy-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:

AnbieterAPI-StilAuthHinweise
AnthropicNative (Messages API)x-api-key-HeaderBenutzerdefiniertes Streaming-Format
OpenAIOpenAI-kompatibelBearer-TokenStandard-SSE
xAIOpenAI-kompatibelBearer-TokenGrok-Modelle
Google GeminiNative (Gemini API)?key=-Query-ParamVermeidet CORS-Preflight
OpenRouterOpenAI-kompatibelBearer-Token300+ Modelle, kostenloses Tier
OllamaOpenAI-kompatibelKeineLokale 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:

  1. HTML5-nativ zuerst – Verwenden Sie <dialog>, <output>, hidden-Attribut, CSS-Animationen vor JS-Äquivalenten.
  2. Kein Polling – Keine requestAnimationFrame-Loops, kein setInterval, kein setTimeout für Animationen. Nur Event-gesteuert.
  3. Passive Listener – Alle nicht-abbrechbaren Events verwenden { passive: true }.
  4. Event-Delegation – Ein einzelner Listener auf dem Container, keine Per-Item-Listener auf dynamischen Listen.
  5. 14px minimale Schriftgröße – Der gesamte Text muss für Vision-LLMs lesbar sein. Kein Text unter 14px.
  6. VLM-freundliches Layout – 48px+ Click-Targets, große Toggles, hoher Kontrast. Einstellungen und Menüs zeigen alle primären Items ohne Scrollen auf 1920x1080.
  7. Reduzierte Bewegung@media (prefers-reduced-motion: reduce) deaktiviert alle Animationen.
  8. CSS-Containmentcontain: strict auf Sidebars, content-visibility: auto auf scrollbaren Listen.
Tipp

Bevor Sie ein Feature hinzufügen, stellen Sie die vier Entscheidungstor-Fragen:

  1. Kann der Browser das nativ?
  2. Erfordert dies einen Server? (Wenn ja, machen Sie es optional.)
  3. Skaliert dies auf 1 Million Bots pro Gerät?
  4. 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:

WorkerDomainZweck
aiscouncil-apiapi.aiscouncil.netBilling, Nutzung, Key-Vending, Geo
aiscouncil-authauth.aiscouncil.netOAuth-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.