Aller au contenu principal

Vue d'Architecture

AISCouncil est une plateforme de gestion de bots sans hébergement qui s'exécute entièrement dans le navigateur. La sortie de production est un fichier index.html unique (~980 Ko) assemblé à partir de 80 fichiers sources modulaires. Il n'y a pas de dépendances runtime externes — seul JavaScript et WASM sont livrés au navigateur.

Architecture Mono-Fichier

L'application est un fichier HTML unique autonome. Pendant le développement, elle est divisée en parties dans src/ pour des fenêtres de contexte plus petites et une édition ciblée. Le script build.sh concatène les 80 parties en index.html dans un ordre strict.

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 Ko)
src/settings-main.js |
src/miniprograms.js |
src/pwa.js |
src/shell-bottom.js /
avertissement

N'éditez jamais index.html directement. Éditez toujours le fichier correspondant dans src/, puis exécutez ./build.sh pour réassembler.

Espace de Noms Global

Tous les modules s'enregistrent sur l'espace de noms global window.AIS. L'espace de noms est initialisé dans src/core-boot.js :

window.AIS = { version: "2.0.0", type: "aiscouncil" };
AIS.PLATFORM_VERSION = "1.0.0";
AIS.ABI_VERSION = 1;

La communication inter-modules utilise un bus d'événements léger :

AIS.on("event", handler); // s'abonner
AIS.off("event", handler); // se désabonner
AIS.emit("event", data); // publier

Système de Modules : AIS.lazy()

Les modules utilisent un pattern d'hydratation différée de style Qwik. La fonction factory est différée jusqu'à ce que le module soit accédé pour la première fois via un getter de propriété sur AIS :

if (!AIS.Council)
AIS.lazy("Council", function () {
"use strict";
// code du module ici
return { run, renderCouncilMessage, estimateCost };
});

Au premier accès (ex., AIS.Council.run()), le getter se déclenche, exécute la factory, se remplace lui-même par l'objet module retourné et le retourne. Les accès ultérieurs atteignent la valeur simple sans surcoût.

Les modules kernel WASM peuvent toujours remplacer un module JS en définissant AIS.Council = wasmModule avant le premier accès, car la propriété est définie comme configurable: true.

Catégories de Modules

Modules Core (toujours chargés)

Les modules core sont définis à l'intérieur d'un seul bloc <script> (core-boot.js jusqu'à core-end.js). Ils s'exécutent de façon anticipée au chargement de la page et forment la fondation de l'application.

ModuleFichierObjectif
AIS.Authcore-auth-main.js, core-auth-local.js, core-auth-init.jsConnexion WebAuthn/Passkey + OAuth, gestion de session, cookie d'auth
AIS.Billingcore-billing.jsNiveau d'abonnement, essai, plan géré
AIS.Codeccore-codec.jsEncodage Base80, versionnage VLQ, compression deflate
AIS.Storagecore-storage.jsIndexedDB + SQLite WASM optionnel, offline-first
AIS.Providerscore-providers.js, core-providers-builtin.jsRegistre de fournisseurs LLM, factory de streaming SSE
AIS.UIcore-ui.jsUtilitaires DOM, rendu markdown, notifications toast
AIS.Sessioncore-session.jsCRUD de session de bot (sauvegardé IndexedDB)
AIS.Chatcore-chat.jsHistorique de chat, envoi/réception de messages en streaming
AIS.Configcore-config.jsBindings du panneau de config de bot, sync URL
AIS.Appcore-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.jsContrôleur d'application, init, routage, gestion des bots

Modules Remplaçables par WASM (chargés à la demande)

Chaque module remplaçable par WASM vit dans son propre bloc <script> et utilise le pattern AIS.lazy(). Ils ne s'exécutent que lors du premier accès.

ModuleFichierObjectif
AIS.Registryregistry.jsRegistre de modèles communautaire, cache 24h, fallback GitHub
AIS.Gridgrid.jsRendu de table/carte de modèles (compilé depuis TypeScript)
AIS.Councilcouncil.jsMoteur de délibération multi-modèles (7 styles)
AIS.Wizardwizard.jsAssistant de première exécution en mode double
AIS.Visionvision.jsEntrée d'images pour les modèles de vision (coller/uploader)
AIS.Memorymemory.jsMémoire clé-valeur persistante par bot
AIS.ImageGenimagegen.jsGénération d'images (DALL-E, Grok Imagine, OpenRouter)
AIS.Toolstools.jsNormalisation du format d'appel d'outils/fonctions
AIS.Remindersreminders.jsMessages planifiés via commande /remind
AIS.Themesthemes.jsSystème de thème visuel
AIS.Templatestemplates-registry.jsTemplates de prompts système, écrans de bienvenue
AIS.ModelPickermodel-picker.jsNavigateur de modèles triable

Modules d'Infrastructure (chargés à la demande)

ModuleFichierObjectif
AIS.ModuleLoadermoduleloader.jsCycle de vie de modules hot-swap, cache OPFS
AIS.Pluginsplugins.jsSystème de plugins : validation de manifeste, hooks
AIS.MCPmcp.jsModel Context Protocol (outils + ressources)
AIS.Channelschannels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.jsAdaptateurs de canaux (Telegram, Discord, Matrix, Slack, WhatsApp)
AIS.Sandboxsandbox.jsBac à sable d'outils WASM (Pyodide, QuickJS, SQLite)
AIS.Publishpublish.jsPublication SEO + HTML statique
AIS.Perfperf.jsSurveillance de performance
AIS.P2Pp2p.jsCollaboration P2P via WebRTC + CRDTs

Modules de Plateforme

ModuleFichierObjectif
AIS.Settingssettings-main.jsDialogue de paramètres globaux (toutes sections)
AIS.I18ni18n.jsInternationalisation
AIS.MiniProgramsminiprograms.jsRuntime des mini-programmes (iframes sandboxées)
AIS.Docsdocs.jsVisualiseur de docs en ligne
AIS.Profilesprofiles.jsTemplates de profil/conseil multi-modèles
AIS.Croncron.jsPlanificateur basé sur le navigateur

Architecture CSS

L'application utilise une approche CSS sans classes. Les styles ciblent des éléments HTML sémantiques et des IDs au lieu de classes.

Type de SélecteurExempleCas d'Usage
ID#header, #sidebar-left, #config-bodyConteneurs de layout uniques
Élément sémantique#messages > article, article menu buttonMessages, actions, navigation
Attribut de données[data-from="user"], [data-variant="primary"]Variantes (rôle du message, type de bouton)
Classe d'état.active, .collapsed, .mobile-openÉtats basculés par JS
Classe de composant.council-member-row, .status-dotComposants JS dynamiques créés
Classe utilitaire.f1, .sc, .dimRemplacement de style en ligne ponctuel

Éléments HTML sémantiques utilisés : <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.

info

Les classes sont utilisées uniquement pour trois cas : bascules d'état (.active, .collapsed), composants JS dynamiques (.council-member-row) et raccourcis utilitaires (.f1, .sc). Si vous pouvez cibler un élément avec un sélecteur sémantique ou un attribut de données, faites-le plutôt que d'ajouter une classe.

Architecture de Stockage

Le stockage est réparti entre des stores synchrones et asynchrones :

StockageClésRaison
localStorage (synchrone)ais-theme, ais-apikey-*, ais-user, ais-ollama-endpointLectures synchrones nécessaires au démarrage
IndexedDB (asynchrone, illimité)ais-bots, ais-chat-*, ais-addon-manifestsGrandes données, pas de limite de 5 Mo
SQLite WASM (optionnel, OPFS)Blobs binairesChargé à la demande pour accès concurrent

Au premier démarrage, AIS.Storage.init() migre automatiquement les données de localStorage vers IndexedDB.

Architecture des Fournisseurs

Les fournisseurs sont enregistrés via AIS.Providers.register(). La plupart des fournisseurs utilisent la factory de streaming SSE partagée openaiCompatible(), qui gère :

  • L'analyse des Server-Sent Events
  • Les callbacks de streaming token par token
  • Le support du signal AbortController
  • Le comptage de tokens depuis les en-têtes de réponse

Six fournisseurs intégrés sont enregistrés dans core-providers-builtin.js :

FournisseurStyle APIAuthNotes
AnthropicNatif (API Messages)en-tête x-api-keyFormat de streaming personnalisé
OpenAICompatible OpenAIBearer tokenSSE standard
xAICompatible OpenAIBearer tokenModèles Grok
Google GeminiNatif (API Gemini)paramètre ?key=Évite le preflight CORS
OpenRouterCompatible OpenAIBearer token300+ modèles, niveau gratuit
OllamaCompatible OpenAIAucuneLLMs locaux, détection automatique des modèles

Toutes les clés API sont stockées localement (localStorage['ais-apikey-{provider}']). Les clés sont envoyées directement du navigateur au fournisseur — jamais proxifiées via un serveur.

Principes de Design

La base de code suit des principes de design stricts que chaque changement doit satisfaire :

  1. Natif HTML5 d'abord — Utilisez <dialog>, <output>, attribut hidden, animations CSS avant les équivalents JS.
  2. Zéro polling — Pas de boucles requestAnimationFrame, pas de setInterval, pas de setTimeout pour l'animation. Uniquement piloté par événements.
  3. Listeners passifs — Tous les événements non-annulables utilisent { passive: true }.
  4. Délégation d'événements — Un seul listener sur le conteneur, pas de listeners par élément sur les listes dynamiques.
  5. Police minimum 14px — Tout le texte doit être lisible par les LLMs de vision. Pas de texte en dessous de 14px.
  6. Layout adapté aux VLMs — Cibles de clic 48px+, grands toggles, contraste élevé. Les paramètres et menus affichent tous les éléments principaux sans défilement sur 1920x1080.
  7. Mouvement réduit@media (prefers-reduced-motion: reduce) désactive toutes les animations.
  8. Containment CSScontain: strict sur les barres latérales, content-visibility: auto sur les listes défilables.
astuce

Avant d'ajouter une fonctionnalité, posez les quatre questions de décision :

  1. Le navigateur peut-il faire cela nativement ?
  2. Cela nécessite-t-il un serveur ? (Si oui, rendez-le optionnel.)
  3. Cela passe-t-il à 1 million de bots par appareil ?
  4. Quels sont les coûts mémoire et CPU ?

Kernel WASM

Le kernel WASM optionnel (kernel/) est écrit en Zig et compile à ~5.5 Ko. Il fournit des primitives de bas niveau pour le système de modules : gestion des slots, dispatch des hooks, I/O en ring buffer et write-ahead logging.

Le kernel opère sur un SharedArrayBuffer de 64 Mo avec des régions mémoire définies pour les segments de modules, ring buffers, WAL et arène scratch. Les modules JS peuvent être remplacés par des équivalents WASM au runtime sans casser le contrat de chargement différé.

Architecture API

La plateforme utilise des Workers séparés pour différents domaines :

WorkerDomaineObjectif
aiscouncil-apiapi.aiscouncil.netFacturation, utilisation, distribution de clés, géo
aiscouncil-authauth.aiscouncil.netCallbacks OAuth, vérification de tokens

Les URLs de base API sont définies au démarrage par détection de domaine et peuvent être remplacées via localStorage['ais-api-base'] pour le développement local.