Visão Geral da Arquitetura
O AISCouncil é uma plataforma de gerenciamento de bots sem hospedagem que roda inteiramente no navegador. A saída de produção é um único arquivo index.html (~980 KB) montado a partir de 80 arquivos fonte modulares. Não há dependências de tempo de execução externas — apenas JavaScript e WASM são enviados ao navegador.
Arquitetura de Arquivo Único
O aplicativo é um único arquivo HTML autocontido. Durante o desenvolvimento, ele é dividido em partes em src/ para janelas de contexto menores e edição focada. O script build.sh concatena todas as 80 partes de volta em index.html em uma ordem estrita.
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 /
Nunca edite index.html diretamente. Sempre edite o arquivo correspondente em src/, depois execute ./build.sh para remontar.
Namespace Global
Todos os módulos são registrados no namespace global window.AIS. O namespace é inicializado em src/core-boot.js:
window.AIS = { version: "2.0.0", type: "aiscouncil" };
AIS.PLATFORM_VERSION = "1.0.0";
AIS.ABI_VERSION = 1;
A comunicação entre módulos usa um barramento de eventos leve:
AIS.on("event", handler); // subscribe
AIS.off("event", handler); // unsubscribe
AIS.emit("event", data); // publish
Sistema de Módulos: AIS.lazy()
Os módulos usam um padrão de hidratação preguiçosa no estilo Qwik. A função de fábrica é adiada até que o módulo seja acessado pela primeira vez via um getter de propriedade em AIS:
if (!AIS.Council)
AIS.lazy("Council", function () {
"use strict";
// código do módulo aqui
return { run, renderCouncilMessage, estimateCost };
});
No primeiro acesso (ex: AIS.Council.run()), o getter dispara, executa a fábrica, substitui a si mesmo pelo objeto de módulo retornado e o retorna. Acessos subsequentes atingem o valor simples sem sobrecarga.
Módulos de kernel WASM ainda podem substituir um módulo JS definindo AIS.Council = wasmModule antes do primeiro acesso, porque a propriedade é definida como configurable: true.
Categorias de Módulos
Módulos Principais (sempre carregados)
Os módulos principais são definidos dentro de um único bloco <script> (core-boot.js até core-end.js). Eles executam avidamente no carregamento da página e formam a base do aplicativo.
| Módulo | Arquivo | Propósito |
|---|---|---|
AIS.Auth | core-auth-main.js, core-auth-local.js, core-auth-init.js | Login WebAuthn/Passkey + OAuth, gerenciamento de sessão, cookie de autenticação |
AIS.Billing | core-billing.js | Nível de assinatura, trial, plano gerenciado |
AIS.Codec | core-codec.js | Codificação Base80, versionamento VLQ, compressão deflate |
AIS.Storage | core-storage.js | IndexedDB + SQLite WASM opcional, offline-first |
AIS.Providers | core-providers.js, core-providers-builtin.js | Registro de provedores LLM, fábrica de streaming SSE |
AIS.UI | core-ui.js | Utilitários DOM, renderizador markdown, notificações toast |
AIS.Session | core-session.js | CRUD de sessão de bot (backed por IndexedDB) |
AIS.Chat | core-chat.js | Histórico de chat, envio/recebimento de mensagens em streaming |
AIS.Config | core-config.js | Bindings do painel de configuração do bot, sincronização URL |
AIS.App | core-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js | Controlador da aplicação, init, roteamento, gerenciamento de bots |
Módulos Substituíveis por WASM (carregados preguiçosamente)
Cada módulo substituível por WASM reside em seu próprio bloco <script> e usa o padrão AIS.lazy(). Eles só executam quando acessados pela primeira vez.
| Módulo | Arquivo | Propósito |
|---|---|---|
AIS.Registry | registry.js | Registro de modelos da comunidade, cache 24h, fallback GitHub |
AIS.Grid | grid.js | Renderizador de tabela/card de modelos (compilado de TypeScript) |
AIS.Council | council.js | Motor de deliberação multi-modelo (7 estilos) |
AIS.Wizard | wizard.js | Wizard de configuração inicial de modo duplo |
AIS.Vision | vision.js | Entrada de imagem para modelos de visão (colar/upload) |
AIS.Memory | memory.js | Memória chave-valor persistente por bot |
AIS.ImageGen | imagegen.js | Geração de imagens (DALL-E, Grok Imagine, OpenRouter) |
AIS.Tools | tools.js | Normalização de formato de chamada de ferramenta/função |
AIS.Reminders | reminders.js | Mensagens agendadas via comando /remind |
AIS.Themes | themes.js | Sistema de temas visuais |
AIS.Templates | templates-registry.js | Templates de prompt de sistema, telas de boas-vindas |
AIS.ModelPicker | model-picker.js | Navegador de modelos ordenável |
Módulos de Infraestrutura (carregados preguiçosamente)
| Módulo | Arquivo | Propósito |
|---|---|---|
AIS.ModuleLoader | moduleloader.js | Ciclo de vida de módulo hot-swap, cache OPFS |
AIS.Plugins | plugins.js | Sistema de plugins: validação de manifest, hooks |
AIS.MCP | mcp.js | Model Context Protocol (ferramentas + recursos) |
AIS.Channels | channels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js | Adaptadores de canal (Telegram, Discord, Matrix, Slack, WhatsApp) |
AIS.Sandbox | sandbox.js | Sandbox de ferramentas WASM (Pyodide, QuickJS, SQLite) |
AIS.Publish | publish.js | Publicação SEO + HTML estático |
AIS.Perf | perf.js | Monitoramento de performance |
AIS.P2P | p2p.js | Colaboração P2P via WebRTC + CRDTs |
Módulos de Plataforma
| Módulo | Arquivo | Propósito |
|---|---|---|
AIS.Settings | settings-main.js | Diálogo de configurações globais (todas as seções) |
AIS.I18n | i18n.js | Internacionalização |
AIS.MiniPrograms | miniprograms.js | Runtime de mini-programas (iframes sandboxed) |
AIS.Docs | docs.js | Visualizador de docs inline |
AIS.Profiles | profiles.js | Templates de perfil/council multi-modelo |
AIS.Cron | cron.js | Agendador baseado em navegador |
Arquitetura CSS
O aplicativo usa uma abordagem CSS sem classes. Os estilos targetizam elementos HTML semânticos e IDs em vez de classes.
| Tipo de Seletor | Exemplo | Caso de Uso |
|---|---|---|
| ID | #header, #sidebar-left, #config-body | Containers de layout únicos |
| Elemento semântico | #messages > article, article menu button | Mensagens, ações, navegação |
| Atributo de dados | [data-from="user"], [data-variant="primary"] | Variantes (role da mensagem, tipo de botão) |
| Classe de estado | .active, .collapsed, .mobile-open | Estados alternados por JS |
| Classe de componente | .council-member-row, .status-dot | Componentes JS dinâmicos |
| Classe utilitária | .f1, .sc, .dim | Substituição de estilo inline ocasional |
Elementos HTML semânticos usados: <header>, <main>, <aside>, <section>, <footer>, <nav>, <article>, <menu>, <details>, <dialog>, <output>.
Classes são usadas apenas para três casos: alternância de estados (.active, .collapsed), componentes JS dinâmicos (.council-member-row), e atalhos utilitários (.f1, .sc). Se você pode targetizar um elemento com um seletor semântico ou atributo de dados, faça isso em vez de adicionar uma classe.
Arquitetura de Armazenamento
O armazenamento é dividido entre stores síncronos e assíncronos:
| Armazenamento | Chaves | Razão |
|---|---|---|
| localStorage (sync) | ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint | Leituras síncronas necessárias no boot |
| IndexedDB (async, ilimitado) | ais-bots, ais-chat-*, ais-addon-manifests | Dados grandes, sem limite de 5 MB |
| SQLite WASM (opcional, OPFS) | Blobs binários | Carregado preguiçosamente para acesso concorrente |
No primeiro boot, AIS.Storage.init() migra automaticamente dados do localStorage para IndexedDB.
Arquitetura de Provedores
Provedores são registrados via AIS.Providers.register(). A maioria dos provedores usa a fábrica de streaming SSE compartilhada openaiCompatible(), que lida com:
- Parsing de Server-Sent Events
- Callbacks de streaming token por token
- Suporte a sinal AbortController
- Contagem de tokens dos cabeçalhos de resposta
Seis provedores integrados são registrados em core-providers-builtin.js:
| Provedor | Estilo de API | Auth | Notas |
|---|---|---|---|
| Anthropic | Nativo (Messages API) | header x-api-key | Formato de streaming customizado |
| OpenAI | OpenAI-compatible | Bearer token | SSE padrão |
| xAI | OpenAI-compatible | Bearer token | Modelos Grok |
| Google Gemini | Nativo (Gemini API) | param query ?key= | Evita preflight CORS |
| OpenRouter | OpenAI-compatible | Bearer token | 300+ modelos, nível gratuito |
| Ollama | OpenAI-compatible | Nenhum | LLMs locais, auto-detecta modelos |
Todas as chaves de API são armazenadas localmente (localStorage['ais-apikey-{provider}']). As chaves são enviadas diretamente do navegador para o provedor — nunca passadas por proxy através de nenhum servidor.
Princípios de Design
A base de código segue princípios de design estritos que toda mudança deve satisfazer:
- HTML5 nativo primeiro -- Use
<dialog>,<output>, atributohidden, animações CSS antes de equivalentes JS. - Zero polling -- Sem loops
requestAnimationFrame, semsetInterval, semsetTimeoutpara animação. Somente orientado a eventos. - Listeners passivos -- Todos os eventos não canceláveis usam
{ passive: true }. - Delegação de eventos -- Listener único no container, não listeners por item em listas dinâmicas.
- Fonte mínima de 14px -- Todo texto deve ser legível por LLMs de visão. Sem texto abaixo de 14px.
- Layout amigável a VLM -- Alvos de clique 48px+, toggles grandes, alto contraste. Configurações e menus exibem todos os itens principais sem scroll em 1920x1080.
- Movimento reduzido --
@media (prefers-reduced-motion: reduce)desabilita todas as animações. - Contenção CSS --
contain: strictem sidebars,content-visibility: autoem listas scrolláveis.
Antes de adicionar qualquer funcionalidade, faça as quatro perguntas de portão de decisão:
- O navegador pode fazer isso nativamente?
- Isso requer um servidor? (Se sim, torne opcional.)
- Isso escala para 1 milhão de bots por dispositivo?
- Quais são os custos de memória e CPU?
Kernel WASM
O kernel WASM opcional (kernel/) é escrito em Zig e compila para ~5.5 KB. Ele fornece primitivas de baixo nível para o sistema de módulos: gerenciamento de slots, dispatch de hooks, I/O de ring buffer, e write-ahead logging.
O kernel opera em um SharedArrayBuffer de 64 MB com regiões de memória definidas para segmentos de módulo, ring buffers, WAL, e arena scratch. Módulos JS podem ser substituídos por equivalentes WASM em tempo de execução sem quebrar o contrato de carregamento preguiçoso.
Arquitetura de API
A plataforma usa Workers separados para diferentes preocupações:
| Worker | Domínio | Propósito |
|---|---|---|
aiscouncil-api | api.aiscouncil.net | Billing, uso, distribuição de chaves, geo |
aiscouncil-auth | auth.aiscouncil.net | Callbacks OAuth, verificação de token |
URLs base da API são definidas no boot a partir de detecção de domínio e podem ser sobrescritas via localStorage['ais-api-base'] para desenvolvimento local.