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 /
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.
| Module | Fichier | Objectif |
|---|---|---|
AIS.Auth | core-auth-main.js, core-auth-local.js, core-auth-init.js | Connexion WebAuthn/Passkey + OAuth, gestion de session, cookie d'auth |
AIS.Billing | core-billing.js | Niveau d'abonnement, essai, plan géré |
AIS.Codec | core-codec.js | Encodage Base80, versionnage VLQ, compression deflate |
AIS.Storage | core-storage.js | IndexedDB + SQLite WASM optionnel, offline-first |
AIS.Providers | core-providers.js, core-providers-builtin.js | Registre de fournisseurs LLM, factory de streaming SSE |
AIS.UI | core-ui.js | Utilitaires DOM, rendu markdown, notifications toast |
AIS.Session | core-session.js | CRUD de session de bot (sauvegardé IndexedDB) |
AIS.Chat | core-chat.js | Historique de chat, envoi/réception de messages en streaming |
AIS.Config | core-config.js | Bindings du panneau de config de bot, sync URL |
AIS.App | core-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js | Contrô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.
| Module | Fichier | Objectif |
|---|---|---|
AIS.Registry | registry.js | Registre de modèles communautaire, cache 24h, fallback GitHub |
AIS.Grid | grid.js | Rendu de table/carte de modèles (compilé depuis TypeScript) |
AIS.Council | council.js | Moteur de délibération multi-modèles (7 styles) |
AIS.Wizard | wizard.js | Assistant de première exécution en mode double |
AIS.Vision | vision.js | Entrée d'images pour les modèles de vision (coller/uploader) |
AIS.Memory | memory.js | Mémoire clé-valeur persistante par bot |
AIS.ImageGen | imagegen.js | Génération d'images (DALL-E, Grok Imagine, OpenRouter) |
AIS.Tools | tools.js | Normalisation du format d'appel d'outils/fonctions |
AIS.Reminders | reminders.js | Messages planifiés via commande /remind |
AIS.Themes | themes.js | Système de thème visuel |
AIS.Templates | templates-registry.js | Templates de prompts système, écrans de bienvenue |
AIS.ModelPicker | model-picker.js | Navigateur de modèles triable |
Modules d'Infrastructure (chargés à la demande)
| Module | Fichier | Objectif |
|---|---|---|
AIS.ModuleLoader | moduleloader.js | Cycle de vie de modules hot-swap, cache OPFS |
AIS.Plugins | plugins.js | Système de plugins : validation de manifeste, hooks |
AIS.MCP | mcp.js | Model Context Protocol (outils + ressources) |
AIS.Channels | channels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js | Adaptateurs de canaux (Telegram, Discord, Matrix, Slack, WhatsApp) |
AIS.Sandbox | sandbox.js | Bac à sable d'outils WASM (Pyodide, QuickJS, SQLite) |
AIS.Publish | publish.js | Publication SEO + HTML statique |
AIS.Perf | perf.js | Surveillance de performance |
AIS.P2P | p2p.js | Collaboration P2P via WebRTC + CRDTs |
Modules de Plateforme
| Module | Fichier | Objectif |
|---|---|---|
AIS.Settings | settings-main.js | Dialogue de paramètres globaux (toutes sections) |
AIS.I18n | i18n.js | Internationalisation |
AIS.MiniPrograms | miniprograms.js | Runtime des mini-programmes (iframes sandboxées) |
AIS.Docs | docs.js | Visualiseur de docs en ligne |
AIS.Profiles | profiles.js | Templates de profil/conseil multi-modèles |
AIS.Cron | cron.js | Planificateur 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électeur | Exemple | Cas d'Usage |
|---|---|---|
| ID | #header, #sidebar-left, #config-body | Conteneurs de layout uniques |
| Élément sémantique | #messages > article, article menu button | Messages, 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-dot | Composants JS dynamiques créés |
| Classe utilitaire | .f1, .sc, .dim | Remplacement de style en ligne ponctuel |
Éléments HTML sémantiques utilisés : <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.
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 :
| Stockage | Clés | Raison |
|---|---|---|
| localStorage (synchrone) | ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint | Lectures synchrones nécessaires au démarrage |
| IndexedDB (asynchrone, illimité) | ais-bots, ais-chat-*, ais-addon-manifests | Grandes données, pas de limite de 5 Mo |
| SQLite WASM (optionnel, OPFS) | Blobs binaires | Chargé à 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 :
| Fournisseur | Style API | Auth | Notes |
|---|---|---|---|
| Anthropic | Natif (API Messages) | en-tête x-api-key | Format de streaming personnalisé |
| OpenAI | Compatible OpenAI | Bearer token | SSE standard |
| xAI | Compatible OpenAI | Bearer token | Modèles Grok |
| Google Gemini | Natif (API Gemini) | paramètre ?key= | Évite le preflight CORS |
| OpenRouter | Compatible OpenAI | Bearer token | 300+ modèles, niveau gratuit |
| Ollama | Compatible OpenAI | Aucune | LLMs 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 :
- Natif HTML5 d'abord — Utilisez
<dialog>,<output>, attributhidden, animations CSS avant les équivalents JS. - Zéro polling — Pas de boucles
requestAnimationFrame, pas desetInterval, pas desetTimeoutpour l'animation. Uniquement piloté par événements. - Listeners passifs — Tous les événements non-annulables utilisent
{ passive: true }. - Délégation d'événements — Un seul listener sur le conteneur, pas de listeners par élément sur les listes dynamiques.
- Police minimum 14px — Tout le texte doit être lisible par les LLMs de vision. Pas de texte en dessous de 14px.
- 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.
- Mouvement réduit —
@media (prefers-reduced-motion: reduce)désactive toutes les animations. - Containment CSS —
contain: strictsur les barres latérales,content-visibility: autosur les listes défilables.
Avant d'ajouter une fonctionnalité, posez les quatre questions de décision :
- Le navigateur peut-il faire cela nativement ?
- Cela nécessite-t-il un serveur ? (Si oui, rendez-le optionnel.)
- Cela passe-t-il à 1 million de bots par appareil ?
- 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 :
| Worker | Domaine | Objectif |
|---|---|---|
aiscouncil-api | api.aiscouncil.net | Facturation, utilisation, distribution de clés, géo |
aiscouncil-auth | auth.aiscouncil.net | Callbacks 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.