Aller au contenu
OmniRoute source

🐳 Docker Guide — OmniRoute (Français)

Auto-hĂ©berger en une seule commande ? Consultez le guide d’auto-hĂ©bergement — docker compose -f docker-compose.selfhost.yml up -d (image publiĂ©e + Redis, accessible uniquement via l’interface de bouclage, sans choix de profil). Le dĂ©marrage rapide ci-dessous correspond Ă  l’utilisation d’un conteneur unique pour les utilisateurs qui exĂ©cutent dĂ©jĂ  Redis ailleurs.

FenĂȘtre de terminal
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
FenĂȘtre de terminal
# Copiez et modifiez d’abord .env
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
FenĂȘtre de terminal
# Profil de base (sans outils CLI)
docker compose --profile base up -d
# Profil CLI (Claude Code, Codex et OpenClaw intégrés)
docker compose --profile cli up -d
# Profil hĂŽte (prioritĂ© Ă  Linux ; monte les binaires CLI de l’hĂŽte en lecture seule)
docker compose --profile host up -d
# Profil Web (Chromium/Playwright pour les fournisseurs de sessions Web)
docker compose --profile web up -d
# Combiner la CLI et le service auxiliaire CLIProxyAPI
docker compose --profile cli --profile cliproxyapi up -d

OmniRoute fournit des profils Compose pour les principaux types de déploiement. Choisissez celui qui correspond à votre environnement.

Profil Service Quand l’utiliser Commande
base (par dĂ©faut) omniroute-base Serveur sans interface graphique / environnement d’exĂ©cution minimal, sans CLI de fournisseurs intĂ©grĂ©es docker compose --profile base up -d
cli omniroute-cli Workflows agentiques qui appellent omniroute providers/setup/doctor et les CLI intégrées (Codex, Claude Code, Droid, OpenClaw) docker compose --profile cli up -d
host omniroute-host HĂŽtes Linux nĂ©cessitant un accĂšs de type network_mode aux CLI de l’hĂŽte via le montage en lecture seule de ~/.local/bin, ~/.codex, ~/.claude, etc. docker compose --profile host up -d
cliproxyapi cliproxyapi Exécuter le side-car CLIProxyAPI sur le port 8317 pour le proxy en amont des CLI docker compose --profile cliproxyapi up -d
web omniroute-web Fournisseurs basés sur des sessions Web nécessitant un navigateur : gemini-web, claude-web, claude-turnstile (construit runner-web, Chromium inclus) docker compose --profile web up -d

Plusieurs profils peuvent ĂȘtre combinĂ©s : docker compose --profile cli --profile cliproxyapi up -d.

Configuration des outils CLI de l’hĂŽte lorsqu’OmniRoute s’exĂ©cute dans Docker

Section intitulĂ©e « Configuration des outils CLI de l’hĂŽte lorsqu’OmniRoute s’exĂ©cute dans Docker »

omniroute setup-codex, setup-claude, config set <tool> et le bouton Enregistrer la configuration du tableau de bord Ă©crivent tous des fichiers tels que ~/.codex/*.config.toml. Ces chemins n’ont de sens que sur la machine oĂč la CLI s’exĂ©cute rĂ©ellement. Si vous les exĂ©cutez dans le conteneur, l’écriture s’effectue dans le rĂ©pertoire personnel du conteneur (/home/node — l’image s’exĂ©cute avec USER node), qu’aucune CLI de l’hĂŽte ne consultera jamais et qui est supprimĂ© dĂšs que le conteneur est recréé.

OmniRoute dĂ©tecte cette situation et refuse l’écriture en fournissant des instructions au lieu de signaler une rĂ©ussite dont vous ne pourriez pas profiter : la CLI se termine avec le code 2 et l’API rĂ©pond 422 avec containerEphemeralTarget: true.

RecommandĂ© : exĂ©cuter la CLI sur l’hĂŽte et OmniRoute dans Docker

Section intitulĂ©e « RecommandĂ© : exĂ©cuter la CLI sur l’hĂŽte et OmniRoute dans Docker »

Le conteneur fournit l’API ; la CLI configure les outils de votre hîte.

FenĂȘtre de terminal
docker compose --profile base up -d
npm install -g omniroute
omniroute connect http://localhost:20128 # fait pointer la CLI vers le conteneur
omniroute setup-codex # écrit dans le véritable ~/.codex de votre hÎte

C’est le choix appropriĂ© lorsque Codex, Claude Code, Cursor ou des outils similaires s’exĂ©cutent sur votre ordinateur portable — ce qui correspond Ă  la configuration habituelle.

Alternative : monter les rĂ©pertoires de configuration de l’hĂŽte avec un montage liĂ© (profil host)

Section intitulĂ©e « Alternative : monter les rĂ©pertoires de configuration de l’hĂŽte avec un montage liĂ© (profil host) »

Si vous souhaitez que le conteneur lui-mĂȘme Ă©crive dans la configuration de votre hĂŽte, montez les rĂ©pertoires et faites pointer CLI_CONFIG_HOME vers la racine du montage. Le profil host le fait dĂ©jĂ  :

environment:
- CLI_CONFIG_HOME=/host-home
- CLI_ALLOW_CONFIG_WRITES=true
volumes:
- ~/.codex:/host-home/.codex:rw
- ~/.claude:/host-home/.claude:rw

C’est le montage liĂ© qui rend le chemin fiable : OmniRoute lit /proc/self/mountinfo et autorise les Ă©critures dans les chemins montĂ©s (ainsi que dans les rĂ©pertoires dont les enfants sont des montages, ce qui correspond exactement Ă  la structure /host-home ci-dessus), tout en continuant de refuser les chemins non montĂ©s.

Solution de dernier recours : configurer les propres CLI du conteneur (Ă  utiliser avec parcimonie)

Section intitulée « Solution de dernier recours : configurer les propres CLI du conteneur (à utiliser avec parcimonie) »

Lorsque les CLI se trouvent rĂ©ellement dans le conteneur (profil cli), l’écriture est intentionnelle. Transmettez --allow-container-write Ă  toute commande setup-*, ou dĂ©finissez OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true pour le serveur. L’écriture est effectuĂ©e avec un avertissement indiquant qu’elle ne survivra pas au conteneur.

Avertissement de sĂ©curitĂ© — profil cli + montage de docker.sock. Le profil cli monte /var/run/docker.sock avec un montage liĂ© afin que le programme de mise Ă  jour automatique exĂ©cutĂ© dans le conteneur puisse recrĂ©er la pile Ă  partir du dĂ©mon de l’hĂŽte (src/lib/system/autoUpdate.ts vĂ©rifie la prĂ©sence de ce socket et ignore le chemin Docker lorsqu’il est absent). Ce socket constitue une frontiĂšre de confiance donnant un accĂšs root Ă  l’hĂŽte : tout ce qui peut y accĂ©der contrĂŽle le dĂ©mon Docker de l’hĂŽte en tant que root — et peut crĂ©er, inspecter, arrĂȘter et supprimer n’importe quel conteneur de l’hĂŽte. ConsĂ©quences :

  1. N’exposez jamais le port du profil cli sur le rĂ©seau. Publiez-le sur 127.0.0.1 (ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:...") — rendre un profil cli accessible depuis le LAN transforme toute RCE au niveau du tableau de bord en compromission complĂšte de l’hĂŽte.
  2. Ne montez aucun rĂ©pertoire supplĂ©mentaire de l’hĂŽte dans le profil cli. Le socket Docker combinĂ© Ă  tout montage supplĂ©mentaire donne au conteneur un accĂšs complet en lecture/Ă©criture Ă  votre systĂšme de fichiers et Ă  la configuration de l’hĂŽte. Si un outil doit accĂ©der Ă  un projet, exĂ©cutez-le localement avec le binaire CLI — ne le montez pas dans le conteneur cli.

Si vous n’avez pas besoin de la mise Ă  jour automatique dans le conteneur, laissez le profil cli dĂ©sactivĂ© (COMPOSE_PROFILES=core,redis ou une valeur plus courte). Les autres profils ne montent pas le socket Docker.

Consultez docs/security/MITM-TPROXY-DECRYPT.md (git ; non compilĂ© dans /docs) pour le modĂšle de menace associĂ© Ă  l’interception MITM, et docs/security/SUPPLY_CHAIN.md pour la chaĂźne de provenance des binaires codex/claude-code/droid/openclaw.

OmniRoute s’appuie sur Redis pour prendre en charge le limiteur de dĂ©bit distribuĂ© et le cache partagĂ©. Le service redis est toujours dĂ©fini dans docker-compose.yml (il n’est associĂ© Ă  aucun profil) et dĂ©marre avec n’importe quel autre profil.

Détail Valeur
Image redis:7-alpine
Nom du conteneur omniroute-redis
Port interne 6379
Port hÎte (remplacement) REDIS_PORT (6379 par défaut)
Adresse hÎte (remplacement) REDIS_BIND_HOST (127.0.0.1 par défaut)
Volume omniroute-redis-data → /data
VĂ©rification de l’état redis-cli ping (intervalle de 10 s)

Variables d’environnement associĂ©es :

  • REDIS_URL — chaĂźne de connexion injectĂ©e dans l’application (redis://redis:6379 par dĂ©faut).
  • REDIS_PORT — mappage du port cĂŽtĂ© hĂŽte pour le conteneur Redis.
  • REDIS_BIND_HOST — interface hĂŽte sur laquelle le port est publiĂ©. Valeur par dĂ©faut : 127.0.0.1.

Pourquoi l’interface de bouclage est utilisĂ©e par dĂ©faut : le conteneur auxiliaire s’exĂ©cute sans requirepass, et les conteneurs de l’application y accĂšdent via le rĂ©seau Compose (redis:6379) — le port publiĂ© sert uniquement aux outils exĂ©cutĂ©s cĂŽtĂ© hĂŽte (redis-cli, une commande locale npm run dev). Une publication sur 0.0.0.0 exposerait une instance Redis non authentifiĂ©e Ă  tous les hĂŽtes de votre rĂ©seau local. Si vous dĂ©finissez REDIS_BIND_HOST=0.0.0.0, ajoutez Ă©galement --requirepass au champ command: du service.

La dĂ©sactivation de Redis n’est pas recommandĂ©e (le limiteur de dĂ©bit basculera vers une solution de secours en mĂ©moire). Si vous devez le faire, supprimez ou commentez le bloc du service redis: dans docker-compose.yml, ou rĂ©duisez son nombre d’instances Ă  zĂ©ro :

FenĂȘtre de terminal
docker compose up -d --scale redis=0

Pour exĂ©cuter un instantanĂ© de production isolĂ© parallĂšlement Ă  l’environnement de dĂ©veloppement, utilisez docker-compose.prod.yml.

Détail Valeur
Fichier docker-compose.prod.yml
Port par défaut du tableau de bord PROD_DASHBOARD_PORT=20130 (mappé vers le port interne ${DASHBOARD_PORT:-20128})
Port par dĂ©faut de l’API PROD_API_PORT=20131
Image omniroute:prod (construite Ă  partir de la cible runner-cli)
Conteneur Redis omniroute-redis-prod (redis:8.6.2, volume dédié redis-prod-data)
Volume de données omniroute-prod-data (nommé et conservé entre les reconstructions)
VĂ©rifications de l’état node healthcheck.mjs + redis-cli ping, avec depends_on conditionnĂ© par l’état de santĂ© de Redis

Utilisation :

FenĂȘtre de terminal
# Construire et démarrer la pile de production
docker compose -f docker-compose.prod.yml up -d --build
# Afficher les journaux en continu
docker compose -f docker-compose.prod.yml logs -f
# ArrĂȘter la pile (conserver les volumes)
docker compose -f docker-compose.prod.yml down

La pile de production s’exĂ©cute parallĂšlement Ă  la configuration Compose de dĂ©veloppement (avec des noms de conteneurs, des ports et des volumes diffĂ©rents). Vous pouvez ainsi poursuivre vos itĂ©rations en local tout en maintenant la production en fonctionnement.

Le dĂ©pĂŽt fournit un Dockerfile multi-Ă©tapes (Dockerfile). Quatre Ă©tapes sont exposĂ©es ; choisissez la bonne target selon votre cas d’utilisation.

Étape Image de base Objectif
builder node:26-trixie-slim Installe les dĂ©pendances (npm ci --legacy-peer-deps) et exĂ©cute npm run build (Turbopack par dĂ©faut — voir Ressources de compilation ci-dessous)
runner-base node:26-trixie-slim Environnement d’exĂ©cution de production avec la sortie autonome de Next.js. Aucun CLI de fournisseur inclus.
runner-cli runner-base Ajoute git, docker.io, docker-compose et les CLI globaux : @openai/codex, @anthropic-ai/claude-code, droid, openclaw. Choisissez cette option pour les workflows agentiques.
runner-web runner-base Ajoute Playwright et un navigateur Chromium (--with-deps) pour les fournisseurs de sessions web : gemini-web, claude-web, claude-turnstile. Choisissez cette option lorsque vous utilisez ces fournisseurs — l’image standard Ă©choue lors des requĂȘtes sans cela (voir la remarque sur -web dans Canaux de publication).

Construisez manuellement une cible spécifique :

FenĂȘtre de terminal
docker build --target runner-base -t omniroute:base .
docker build --target runner-cli -t omniroute:cli .
docker build --target runner-web -t omniroute:web .

Trois arguments de compilation contrĂŽlent les ressources consommĂ©es par l’étape builder. Ils s’appliquent uniquement Ă  la compilation — OMNIROUTE_MEMORY_MB (ci-dessous) est un paramĂštre d’exĂ©cution distinct.

Argument de compilation Valeur par défaut Effet
OMNIROUTE_USE_TURBOPACK 1 Avec 0, utilise webpack pour la compilation. Pic de mémoire plus faible, mais plus lent.
OMNIROUTE_BUILD_MEMORY_MB 6144 Plafond du tas V8 (--max-old-space-size) pour le processus next build lancé.
OMNIROUTE_BUILD_WORKERS 2 Alimente CIRCLE_NODE_TOTAL ; Next en déduit workers = N - 1 pour la collecte des données de page.

OMNIROUTE_BUILD_WORKERS est le paramĂštre Ă  augmenter sur une machine de compilation puissante et celui Ă  suspecter lorsqu’une compilation contrainte Ă©choue aprĂšs ✓ Compiled successfully. Chaque worker de donnĂ©es de page est un processus distinct, tout comme le processus parent next build ; une reproduction sur un VPS actif (problĂšme #7518) a mesurĂ© le pic de RSS de chaque processus Ă  environ 4,5 Go, indĂ©pendamment de l’option de tas NODE_OPTIONS (Turbopack compile dans de la mĂ©moire native/Rust en dehors du tas V8). La valeur par dĂ©faut de 2 (→ 1 worker, 2 processus au total) est dimensionnĂ©e pour les runners hĂ©bergĂ©s par GitHub dotĂ©s de 16 Go / 4 vCPU qu’utilise le pipeline de publication. Avec 8 (→ 7 workers), ce runner a Ă©puisĂ© sa mĂ©moire et buildkit a fait Ă©chouer l’étape avec ResourceExhausted: ... cannot allocate memory ; 3 (→ 2 workers) ne tenait toujours pas une fois le RSS par processus mesurĂ© directement plutĂŽt que dĂ©duit. tests/unit/docker-build-memory-budget.test.ts effectue le calcul Ă  partir de la valeur mesurĂ©e et Ă©choue si l’un des deux paramĂštres dĂ©passe les capacitĂ©s du runner.

Turbopack compile dans de la mĂ©moire Rust native situĂ©e en dehors du tas V8, donc OMNIROUTE_BUILD_MEMORY_MB ne la limite pas. Sur un hĂŽte disposant d’un plafond de mĂ©moire, la compilation est alors interrompue par SIGKILL par le mĂ©canisme OOM killer, sans aucun message d’erreur — elle s’arrĂȘte simplement au milieu de Creating an optimized production build, ce qui ressemble davantage Ă  un blocage qu’à un manque de mĂ©moire. Si les ressources de l’hĂŽte de compilation sont limitĂ©es, changez de bundler :

FenĂȘtre de terminal
docker build --target runner-base \
--build-arg OMNIROUTE_USE_TURBOPACK=0 \
-t omniroute:base .

webpackBuildWorker est activé, donc next build exécute un processus parent et un processus worker, chacun respectant séparément OMNIROUTE_BUILD_MEMORY_MB. Dimensionnez le plafond du conteneur à environ deux fois cette valeur, et non une seule fois.

Mesures effectuées sur cette arborescence (--target runner-base, OMNIROUTE_BUILD_MEMORY_MB=6144) :

Bundler Plafond du conteneur Résultat
Turbopack 8 Gio / 16 Gio Interrompu par l’OOM dans les deux cas, silencieusement
webpack 8 Gio Worker de compilation interrompu par SIGKILL
webpack 12 Gio Réussi, avec un pic à 11,1 Gio

Valeurs par défaut exportées par runner-base : PORT=20128, HOSTNAME=0.0.0.0, OMNIROUTE_MEMORY_MB=1024, NODE_OPTIONS=--max-old-space-size=1024, DATA_DIR=/app/data, OMNIROUTE_MIGRATIONS_DIR=/app/migrations.

Comportement de la mémoire dans Docker :

  • L’image dĂ©finit OMNIROUTE_MEMORY_MB=1024 et en dĂ©rive NODE_OPTIONS=--max-old-space-size=1024.
  • Le processus serveur rĂ©el est dĂ©marrĂ© par le lanceur autonome, qui lit OMNIROUTE_MEMORY_MB et ajoute --max-old-space-size=<OMNIROUTE_MEMORY_MB>.
  • Node utilise la derniĂšre valeur rĂ©pĂ©tĂ©e de --max-old-space-size ; dĂ©finir OMNIROUTE_MEMORY_MB permet donc de contrĂŽler la limite effective du tas dans Docker.
  • Comme l’image dĂ©finit toujours cette variable, la valeur de secours du lanceur, calibrĂ©e en fonction de la RAM, ne s’applique jamais sous Docker. Augmentez-la explicitement en fonction de la charge de travail (tableau ci-dessous). 2048 reste insuffisant pour les requĂȘtes /v1/responses des agents de programmation.

La valeur Docker par dĂ©faut de 1 Gio constitue un minimum pour le tableau de bord et les conversations lĂ©gĂšres, et non une configuration de production. Les corps volumineux de requĂȘtes POST /v1/responses (des centaines de messages, des dizaines d’outils) conservent plusieurs graphes en mĂ©moire pendant la compression. Deux requĂȘtes simultanĂ©es d’environ 3 Mio / 750 000 jetons ont provoquĂ© l’arrĂȘt de V8 avec un espace ancien de 12 Gio (FATAL ERROR: Reached heap limit) et ont Ă©galement dĂ©clenchĂ© une erreur OOM du cgroup avec 16 Gio. Voir #7849.

Dimensionnez la valeur cgroup --memory au-dessus de la taille du tas — les tampons natifs, SQLite et les donnĂ©es intermĂ©diaires de compression rĂ©sident en dehors de V8.

Charge de travail OMNIROUTE_MEMORY_MB Conteneur / cgroup Remarques
Tableau de bord, une conversation lĂ©gĂšre 1024 (valeur par dĂ©faut de l’image) ≄2 Gio
Un agent de programmation (Claude/Codex/Grok) 8192 ≄10 Gio Session /v1/responses unique typique
Deux longues requĂȘtes /v1/responses simultanĂ©es 10240–12288 ≄12–16 Gio ArrĂȘt de V8 mesurĂ© avec un tas d’environ 12 Gio
Trois longs contextes simultanĂ©s ou plus Ă  Ă©viter dans un seul processus sĂ©rialiser / ajouter de la RAM Par dĂ©faut, l’admission des charges lourdes est limitĂ©e Ă  1 requĂȘte en cours ; augmenter cette limite sans ajouter de RAM provoque de nouveau l’arrĂȘt

Sur une machine physique, omniroute serve calibre la limite Ă  environ 35 % de la RAM (bornĂ©e Ă  [512, 4096]) lorsque OMNIROUTE_MEMORY_MB n’est pas dĂ©finie. Docker dĂ©finit toujours cette variable sur 1024 ; ce calibrage n’est donc jamais exĂ©cutĂ© dans l’image officielle.

FenĂȘtre de terminal
docker run -d --name omniroute --restart unless-stopped --stop-timeout 40 \
-e OMNIROUTE_MEMORY_MB=8192 --memory=10g \
-p 127.0.0.1:20128:20128 -v omniroute-data:/app/data diegosouzapw/omniroute:latest

Au-delĂ  des valeurs par dĂ©faut documentĂ©es dans ENVIRONMENT.md, les variables suivantes sont les plus importantes lors de l’exĂ©cution sous Docker :

Variable RÎle Valeur par défaut
OMNIROUTE_WS_BRIDGE_SECRET Secret partagĂ© pour le pont WebSocket. Obligatoire en production — dĂ©finissez-le sur une chaĂźne alĂ©atoire robuste. non dĂ©finie (doit ĂȘtre fournie)
REDIS_URL Chaßne de connexion pour le mécanisme de limitation du débit / le backend de cache redis://redis:6379
REDIS_PORT Port cÎté hÎte pour le conteneur Redis inclus 6379
REDIS_BIND_HOST Interface de l’hĂŽte sur laquelle le port Redis inclus est publiĂ© (interface de bouclage, sauf si vous ajoutez AUTH) 127.0.0.1
AUTO_UPDATE_HOST_REPO_DIR Chemin sur l’hĂŽte montĂ© dans le profil cli Ă  l’emplacement /workspace/omniroute pour les workflows de mise Ă  jour automatique . (rĂ©pertoire actuel)
OMNIROUTE_MEMORY_MB Plafond du tas Node Ă  l’exĂ©cution pour le serveur Docker autonome ; remplace la valeur par dĂ©faut de l’image indiquĂ©e ci-dessus. Agents de codage : 8192 ou plus (voir la RAM d’exĂ©cution). 1024
DASHBOARD_PORT / API_PORT Remplace les ports exposĂ©s du tableau de bord (20128) et de l’API (20129) 20128 / 20129
APP_BIND_HOST Interface de l’hĂŽte sur laquelle docker-compose publie les ports du tableau de bord, de l’API et du WebSocket en direct. Avec REQUIRE_API_KEY=false (valeur par dĂ©faut), 0.0.0.0 expose le proxy /v1 anonyme au rĂ©seau local — n’élargissez l’accĂšs qu’avec REQUIRE_API_KEY=true ou un proxy inverse en amont. 127.0.0.1
CLIPROXY_BIND_HOST Interface de l’hĂŽte sur laquelle docker-compose publie le service auxiliaire cliproxyapi — son volume de donnĂ©es contient les identifiants des fournisseurs. 127.0.0.1
OMNIROUTE_PLUGINS_DIR RĂ©pertoire lu par l’analyseur de plugins Ă  l’exĂ©cution et dans lequel il les installe. DĂ©finissez-le lorsque les plugins sont montĂ©s avec une liaison : la valeur par dĂ©faut suit HOME, qu’une image n’exporte pas nĂ©cessairement. ~/.omniroute/plugins
OMNIROUTE_BASE_PATH Sous-chemin d’URL lorsque l’application est publiĂ©e derriĂšre un proxy inverse (par ex. /omniroute) (vide = racine)
NEXT_PUBLIC_BASE_URL Origine publique du navigateur incluant le sous-chemin (par ex. https://host/omniroute) non définie
PROD_DASHBOARD_PORT Port du tableau de bord cÎté hÎte pour docker-compose.prod.yml 20130
CLIPROXYAPI_PORT Port cÎté hÎte pour le service auxiliaire cliproxyapi 8317

Le basePath de Next.js est compilĂ© dans le bundle autonome. OmniRoute enregistre la valeur intĂ©grĂ©e dans un fichier sentinelle Ă  la racine de l’application (Ă©crit pendant npm run build ; lu par scripts/docker/ensure-docker-base-path.mjs) et la compare Ă  OMNIROUTE_BASE_PATH au dĂ©marrage du conteneur. Lorsque ces valeurs diffĂšrent et que l’image a Ă©tĂ© construite pour la racine du domaine, le point d’entrĂ©e réécrit les manifestes autonomes, les littĂ©raux basePath/assetPrefix intĂ©grĂ©s (Next 16 gĂ©nĂšre les URL des ressources SSR uniquement Ă  partir de assetPrefix — le correctif y rĂ©plique le sous-chemin), les URL de ressources /_next/static intĂ©grĂ©es (manifestes de rĂ©fĂ©rences client, importations de mĂ©dias, pages d’erreur prĂ©-rendues) ainsi que le shim client de process.env avant l’exĂ©cution de node dev/run-standalone.mjs.

DĂ©finissez les deux variables dans .env, puis reconstruisez afin que l’image et l’environnement d’exĂ©cution soient cohĂ©rents :

.env
OMNIROUTE_BASE_PATH=/omniroute
NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute
FenĂȘtre de terminal
docker compose --profile base up -d --build

docker-compose.yml transmet OMNIROUTE_BASE_PATH comme argument de construction Docker et comme variable d’environnement d’exĂ©cution.

Image racine prĂ©construite + sous-chemin Ă  l’exĂ©cution

Section intitulĂ©e « Image racine prĂ©construite + sous-chemin Ă  l’exĂ©cution »

Les images publiĂ©es diegosouzapw/omniroute:* sont construites pour la racine du domaine. Vous pouvez nĂ©anmoins dĂ©finir OMNIROUTE_BASE_PATH Ă  l’exĂ©cution ; le conteneur corrige le bundle une fois au dĂ©marrage. Associez-le Ă  l’origine publique correspondante :

services:
omniroute:
image: diegosouzapw/omniroute:latest
environment:
OMNIROUTE_BASE_PATH: /omniroute
NEXT_PUBLIC_BASE_URL: https://myhostname.example.com/omniroute

Configurez le proxy inverse pour qu’il transmette le chemin externe complet (ne supprimez pas le prĂ©fixe). Traefik doit acheminer PathPrefix(/omniroute) vers le conteneur sans StripPrefix, afin que Next.js reçoive /omniroute/... et serve les ressources depuis /omniroute/_next/....

Le contrĂŽle d’intĂ©gritĂ© Docker interroge le point de terminaison lĂ©ger de cycle de vie /healthz, prĂ©fixĂ© par la valeur active de OMNIROUTE_BASE_PATH. /api/monitoring/health reste disponible pour les diagnostics humains ou de tableaux de bord ; pour que le HEALTHCHECK du conteneur l’utilise Ă  nouveau (par exemple pour imposer un contrĂŽle d’intĂ©gritĂ© approfondi), dĂ©finissez OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health. Ce chemin effectue un contrĂŽle approfondi (base de donnĂ©es + rĂ©sumĂ© de la surveillance) — il convient au HEALTHCHECK peu frĂ©quent de Docker si vous choisissez de le rĂ©activer, mais pas aux intervalles de livenessProbe de Kubernetes.

Pour les orchestrateurs (Kubernetes, Nomad, etc.) :

Sonde À privilĂ©gier À Ă©viter
Vivacité HTTP GET /livez, ou TCP sur le port principal (PORT, défaut 20128) /api/monitoring/health comme contrÎle de vivacité
Disponibilité HTTP GET /healthz Les délais courts qui assimilent une boucle occupée à morte
Approfondie / boüte noire /api/monitoring/health —

/healthz indique l’état du cycle de vie du processus (ok / starting / stopping). /livez vĂ©rifie uniquement que le processus est actif (200 chaque fois que le gestionnaire peut s’exĂ©cuter ; il n’attend pas que le service soit prĂȘt). Les deux s’exĂ©cutent nĂ©anmoins sur la mĂȘme boucle d’évĂ©nements Node que le traitement des requĂȘtes ; les opĂ©rations de catalogue ou de compression intensives en CPU peuvent donc les retarder — occupĂ© ≠ mort. PrivilĂ©giez une sonde de vivacitĂ© TCP si les sondes HTTP expirent. Guide complet sur les sondes : Guide de surveillance — recommandations relatives aux sondes Kubernetes.

OmniRoute peut ĂȘtre exposĂ© de maniĂšre sĂ©curisĂ©e grĂące au provisionnement SSL automatique de Caddy. Assurez-vous que l’enregistrement DNS A de votre domaine pointe vers l’adresse IP de votre serveur.

services:
omniroute:
image: diegosouzapw/omniroute:latest
container_name: omniroute
restart: unless-stopped
volumes:
- omniroute-data:/app/data
environment:
- PORT=20128
# Origine visible par le navigateur pour les rappels OAuth, les liens du tableau de bord et les URL publiques générées.
- NEXT_PUBLIC_BASE_URL=https://your-domain.com
# URL interne de serveur Ă  serveur pour les tĂąches planifiĂ©es et les requĂȘtes vers soi-mĂȘme.
- BASE_URL=http://omniroute:20128
- AUTH_COOKIE_SECURE=true
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
command: caddy reverse-proxy --from https://your-domain.com --to http://omniroute:20128
volumes:
omniroute-data:

Caddy dĂ©finit les en-tĂȘtes de transfert standard pour le conteneur en amont. OmniRoute utilise NEXT_PUBLIC_BASE_URL comme origine publique canonique pour les rappels OAuth et les liens publics gĂ©nĂ©rĂ©s ; les Ă©critures authentifiĂ©es du tableau de bord utilisent des requĂȘtes de mĂȘme origine ainsi qu’une protection CSRF liĂ©e Ă  la session. N’activez OMNIROUTE_TRUST_PROXY que pour les dĂ©ploiements avancĂ©s dans lesquels vous souhaitez dĂ©libĂ©rĂ©ment qu’OmniRoute dĂ©termine l’origine publique Ă  partir d’en-tĂȘtes de transfert approuvĂ©s plutĂŽt que d’une configuration explicite.

La prise en charge du tableau de bord pour les dĂ©ploiements Docker comprend un tunnel rapide Cloudflare activable en un clic dans Dashboard → Endpoints. Lors de la premiĂšre activation, cloudflared est tĂ©lĂ©chargĂ© uniquement si nĂ©cessaire, un tunnel temporaire vers votre point de terminaison /v1 actuel est dĂ©marrĂ©, et l’URL https://*.trycloudflare.com/v1 gĂ©nĂ©rĂ©e s’affiche directement sous votre URL publique habituelle.

Les panneaux de tunnel des points de terminaison (Cloudflare, Tailscale, ngrok) peuvent ĂȘtre affichĂ©s ou masquĂ©s depuis Settings → Appearance sans modifier l’état actif des tunnels.

  • Les URL des tunnels rapides sont temporaires et changent aprĂšs chaque redĂ©marrage.
  • Les tunnels rapides ne sont pas restaurĂ©s automatiquement aprĂšs le redĂ©marrage d’OmniRoute ou du conteneur. RĂ©activez-les depuis le tableau de bord lorsque cela est nĂ©cessaire.
  • L’installation gĂ©rĂ©e prend actuellement en charge Linux, macOS et Windows sur x64 / arm64.
  • Les tunnels rapides gĂ©rĂ©s utilisent par dĂ©faut le transport HTTP/2 afin d’éviter les avertissements bruyants liĂ©s aux tampons UDP de QUIC dans les environnements de conteneurs aux ressources limitĂ©es. DĂ©finissez CLOUDFLARED_PROTOCOL=quic ou auto si vous souhaitez utiliser un autre transport.
  • Les images Docker incluent les autoritĂ©s de certification racines du systĂšme et les transmettent Ă  l’instance gĂ©rĂ©e de cloudflared, ce qui Ă©vite les Ă©checs de validation TLS lorsque le tunnel s’initialise Ă  l’intĂ©rieur du conteneur.
  • DĂ©finissez CLOUDFLARED_BIN=/absolute/path/to/cloudflared si vous souhaitez qu’OmniRoute utilise un binaire existant au lieu d’en tĂ©lĂ©charger un.
Image Tag Taille Description
diegosouzapw/omniroute latest ~250MB Version SemVer stable publiée la plus élevée (pas git main)
diegosouzapw/omniroute 3.8.0 ~250MB Épinglez ce type de tag pour GitOps

Manifeste multiplateforme : linux/amd64 + linux/arm64 natifs (Apple Silicon, AWS Graviton, Raspberry Pi). Docker sĂ©lectionne automatiquement l’architecture correspondante ; passez --platform linux/amd64 si vous devez forcer l’émulation AMD64 sur des hĂŽtes ARM.

OmniRoute publie des canaux Docker distincts pour les versions stables, les tests de la branche de publication active et les builds de développement.

Canal Source Mutabilité Utilisation recommandée
:&lt;version&gt; / :&lt;version&gt;-web Version signée/versionnée Immuable Déploiements en production épinglant une version précise
:latest / :latest-web Version SemVer stable publiĂ©e la plus Ă©levĂ©e Pointeur stable mutable Suit les versions stables aprĂšs une tĂąche de publication SemVer — ne suit pas main ni les commits release/v* non publiĂ©s
:next / :next-web Branche release/v* par défaut actuelle Pointeur de préversion mutable Test des correctifs intégrés à la branche de publication active, mais pas encore inclus dans une version stable
:main / :main-web Branche main Pointeur de dĂ©veloppement mutable Uniquement pour les tests de dĂ©veloppement et d’intĂ©gration

Chaque canal ci-dessus est Ă©galement disponible sous forme de tag -web (:latest-web, :&lt;version&gt;-web, :next-web, :main-web), construit Ă  partir de l’étape runner-web — la mĂȘme image, avec Playwright et un navigateur Chromium en plus. L’image standard est fournie sans Chromium ; gemini-web, claude-web et claude-turnstile en ont besoin.

L’échec est diffĂ©rĂ© et ne se produit pas au dĂ©marrage : ces fournisseurs rĂ©pertorient leurs modĂšles et apparaissent comme connectĂ©s dans le tableau de bord, et seule la premiĂšre requĂȘte Ă©choue avec

[500]: Failed to load external module playwright: Error: Cannot find module
'/app/node_modules/playwright/node_modules/playwright-core/browsers.json'

Si vous utilisez ces fournisseurs, rĂ©cupĂ©rez le tag -web du canal que vous utilisez dĂ©jĂ  — rien d’autre ne change. Pour une installation npm/CLI (sans image Docker), l’élĂ©ment manquant Ă©quivalent est le binaire du navigateur : exĂ©cutez npx playwright install chromium sur l’hĂŽte.

Le canal next est reconstruit Ă  chaque push vers la branche release/v* par dĂ©faut actuelle et est publiĂ© pour AMD64 et ARM64. Les anciennes branches de maintenance ne peuvent pas l’écraser. Ce canal fournit une image rĂ©cupĂ©rable contenant les correctifs fusionnĂ©s dans la branche de publication active avant la crĂ©ation du prochain tag stable.

FenĂȘtre de terminal
docker pull diegosouzapw/omniroute:next
docker pull diegosouzapw/omniroute:next-web

Pour Docker Compose, remplacez le tag d’image utilisĂ© par le profil sĂ©lectionnĂ©, puis rĂ©cupĂ©rez l’image et recrĂ©ez le service :

services:
omniroute:
image: diegosouzapw/omniroute:next
FenĂȘtre de terminal
docker compose pull
docker compose up -d

next est un canal de prĂ©version flottant. Il peut changer Ă  chaque push vers la branche de publication active et n’est pas pris en charge pour une utilisation en production. Épinglez le digest de l’image lors de l’évaluation d’un build spĂ©cifique :

FenĂȘtre de terminal
docker pull diegosouzapw/omniroute:next
docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}'

Avant d’effectuer les tests, sauvegardez le volume de donnĂ©es OmniRoute ou le rĂ©pertoire de donnĂ©es montĂ© par liaison. Pour revenir Ă  la version prĂ©cĂ©dente, restaurez la version stable ou le digest prĂ©cĂ©demment utilisĂ©, puis recrĂ©ez le conteneur :

FenĂȘtre de terminal
docker pull diegosouzapw/omniroute:&lt;stable-version&gt;
docker compose up -d

Un build de branche de publication ne peut jamais dĂ©placer latest ; seule une version sĂ©mantique stable admissible peut promouvoir le pointeur stable. Les images next conservent l’inspection des images de publication et le contrĂŽle bloquant des vulnĂ©rabilitĂ©s CRITICAL.

latest ne garantit pas l’actualitĂ© par rapport Ă  git. Les correctifs fusionnĂ©s dans main ou dans la branche release/v* active ne sont pas inclus dans :latest tant qu’une image SemVer stable n’a pas Ă©tĂ© publiĂ©e et que la tĂąche de publication n’a pas promu :latest (avec le mĂȘme digest que cette version SemVer). Si latest semble figĂ© alors que GitHub affiche dĂ©jĂ  le correctif, rĂ©cupĂ©rez :next pour tester la branche de publication ou attendez le tag SemVer.

Votre objectif Utilisez
GitOps / production ne devant subir aucune dĂ©rive Épinglez :X.Y.Z (ou le digest de l’image)
Suivre les versions stables publiées et accepter une recréation à chaque publication :latest
Tester les commits release/v* non publiés :next (pas en production)
Tester main :main (pas en production)

DisponibilitĂ© : SQLite par dĂ©faut ne prend en charge qu’un seul rĂ©plica

Section intitulĂ©e « DisponibilitĂ© : SQLite par dĂ©faut ne prend en charge qu’un seul rĂ©plica »

La configuration Docker / Kubernetes standard d’OmniRoute repose sur un processus Node + un processus d’écriture SQLite. La haute disponibilitĂ© n’est pas prise en charge avec cette topologie.

Contrainte Conséquence
Processus d’écriture unique N’exĂ©cutez pas plusieurs rĂ©plicas utilisant le mĂȘme fichier SQLite. Cela corrompt la base de donnĂ©es.
RecrĂ©ation / redĂ©marrage / arrĂȘt par HEALTHCHECK Interruption totale des flux SSE en cours, des sessions du tableau de bord et de l’état en mĂ©moire. Chaque client connectĂ© est dĂ©connectĂ©. Les nouvelles requĂȘtes effectuĂ©es pendant la pĂ©riode sans point de terminaison reçoivent du proxy inverse une erreur 502 Bad Gateway: Unknown error, et non du JSON OmniRoute — les clients ne peuvent pas la distinguer d’une dĂ©faillance du fournisseur (#11015).
MĂȘme boucle d’évĂ©nements que /healthz Une opĂ©ration intensive sur le catalogue ou un cycle de compression peut retarder les sondes ; un dĂ©lai d’expiration court redĂ©marre alors le seul rĂ©plica.

Matrice des sondes (voir également les recommandations relatives aux sondes Kubernetes) :

Sonde Cible Ne pas utiliser
Vivacité TCP sur PORT (20128 par défaut), ou HTTP souple /healthz /api/monitoring/health
DisponibilitĂ© HTTP GET /healthz Des dĂ©lais d’expiration courts qui interprĂštent une boucle d’évĂ©nements occupĂ©e comme un arrĂȘt
Approfondie / opérateurs humains /api/monitoring/health La vivacité automatisée du kubelet

Mises Ă  niveau : attendez-vous Ă  ce que chaque session soit interrompue. Drainez les clients si possible ; aucune mise Ă  jour progressive n’est possible avec la configuration SQLite par dĂ©faut. La combinaison de restart: unless-stopped dans Compose et du HEALTHCHECK Docker remplacera Ă©galement l’unique processus lorsque le conteneur est dans l’état Unhealthy — avec le mĂȘme pĂ©rimĂštre d’impact.

Extrait Kubernetes pour un seul rĂ©plica (Recreate est obligatoire ; n’augmentez pas replicas pour un mĂȘme fichier SQLite) :

spec:
replicas: 1
strategy:
type: Recreate
template:
spec:
terminationGracePeriodSeconds: 90
containers:
- name: omniroute
lifecycle:
preStop:
exec:
command: ["/bin/sleep", "15"]
readinessProbe:
httpGet:
path: /healthz
port: 20128
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 20128
periodSeconds: 20

La pause preStop permet Ă  kube de retirer les points de terminaison du Service avant SIGTERM, afin que le nouveau trafic cesse d’atteindre le processus en cours d’arrĂȘt. Les flux SSE /v1/responses en cours disposent d’un dĂ©lai de drainage pouvant atteindre SHUTDOWN_TIMEOUT_MS (30 s par dĂ©faut), grĂące Ă  des baux d’admission renforcĂ©s (#11015). Les nouvelles requĂȘtes qui atteignent encore le processus reçoivent 503 + Retry-After: 5. La pĂ©riode sans point de terminaison propre Ă  Recreate, jusqu’à ce que le processus de remplacement soit Ready, reste une interruption totale — elle est inhĂ©rente Ă  la topologie SQLite et ne rĂ©sulte pas d’une mauvaise configuration des sondes.

Postgres externe / la haute disponibilitĂ© avec plusieurs processus d’écriture ne constitue pas une configuration standard documentĂ©e. Si vous avez besoin de haute disponibilitĂ©, conservez un seul rĂ©plica ou exĂ©cutez une topologie que le projet a testĂ©e et documentĂ©e sĂ©parĂ©ment. Les travaux relatifs Ă  Postgres/MySQL sont suivis dans #8075. En attendant leur livraison, la seule façon prise en charge de multiplier la capacitĂ© pour les requĂȘtes /v1/responses volumineuses consiste Ă  utiliser N processus indĂ©pendants (section suivante), et non replicas > 1 sur un mĂȘme volume.

Mise Ă  l’échelle horizontale : N processus indĂ©pendants

Section intitulĂ©e « Mise Ă  l’échelle horizontale : N processus indĂ©pendants »

Un processus Node correspond Ă  un tas V8. Deux requĂȘtes d’agent de codage POST /v1/responses (RTK + Caveman) simultanĂ©es d’environ 3 MiB / 750 000 tokens font Ă©chouer ce tas aux alentours de 12 Gio (FATAL ERROR: Reached heap limit) et peuvent provoquer une erreur OOM dans un cgroup de 16 Gio. Voir #7849. Cette mesure constitue un avertissement relatif au budget mĂ©moire, et non une limite maximale du produit fixĂ©e Ă  deux longues requĂȘtes /v1/responses simultanĂ©es. L’admission des conversations lourdes est contrĂŽlĂ©e par un budget d’octets entrants calculĂ© automatiquement (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES, src/shared/middleware/admissionBudget.ts), dimensionnĂ© Ă  partir de cette mĂȘme limite V8/cgroup — le remplacer par une valeur supĂ©rieure (ou dĂ©finir l’ancienne limite du nombre de requĂȘtes OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT) sur un processus dĂ©jĂ  dimensionnĂ© rĂ©introduit cet Ă©chec. Les petites conversations, /healthz, /v1/models et MCP ne sont pas soumis Ă  cette limite.

Un seul processus : plus de deux longues requĂȘtes /v1/responses

Section intitulĂ©e « Un seul processus : plus de deux longues requĂȘtes /v1/responses »

Un processus sain (tas infĂ©rieur Ă  OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO, valeur par dĂ©faut 0.75) peut exĂ©cuter plus de deux longues requĂȘtes POST /v1/responses simultanĂ©es lorsque le budget d’octets en cours Ă  l’échelle du processus (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES / #10110) dispose encore de capacitĂ©. Les corps dont la taille est supĂ©rieure ou Ă©gale Ă  OMNIROUTE_CHAT_LARGE_BODY_BYTES (256 Kio par dĂ©faut) utilisent le mĂȘme bail pour charge lourde que les requĂȘtes structurellement lourdes et le mĂȘme mĂ©canisme d’échappement tryAcquireHealthyHeadroom de #10437 (OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM). La prise en charge de dizaines de clients SSE simultanĂ©s et durables (les opĂ©rateurs en ont souvent besoin de 40 Ă  50) relĂšve du budget mĂ©moire — dimensionnez le tas, les emplacements principaux/de rĂ©serve et OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES — et non d’une limite stricte du produit Ă  « 2 maximum ». Un tas sous pression continue de dĂ©lester les requĂȘtes avec une erreur 503 rĂ©essayable afin que le problĂšme #7849 ne rĂ©apparaisse pas.

Pour multiplier les tas (espaces old-space V8 indĂ©pendants) dĂšs aujourd’hui :

À faire À ne pas faire
ExĂ©cuter N conteneurs/pods, chacun avec son propre DATA_DIR / volume DĂ©finir replicas > 1 pour un mĂȘme fichier SQLite
Dimensionner les requĂȘtes lourdes en cours + la rĂ©serve saine en fonction du tas / budget d’octets en cours ; 1 Ă  2 est la valeur par dĂ©faut prudente issue de #7849, et non une limite maximale stricte du produit Attribuer 8× plus de RAM Ă  un processus et une limite de nombre non bornĂ©e
Facultatif : QUOTA_STORE_DRIVER=redis + QUOTA_STORE_REDIS_URL pour des compteurs de quotas partagĂ©s ConsidĂ©rer Redis comme un SQLite partagĂ© — ce n’est pas le cas
Dupliquer les secrets des fournisseurs dans chaque instance (ou accepter des tableaux de bord partitionnĂ©s) S’attendre Ă  disposer d’un seul tableau de bord / journal d’appels pour toutes les instances
Placer n’importe quel Ă©quilibreur de charge en frontal ; une affinitĂ© par clĂ© d’API ou par session suffit Exiger un middleware propre Ă  un fournisseur et tenant compte de la taille

MatĂ©riel : le nombre de longues requĂȘtes /v1/responses simultanĂ©es par instance relĂšve du budget mĂ©moire (tas + octets en cours / #10110). N rĂ©pertoires DATA_DIR indĂ©pendants multiplient toujours les tas : la RAM de l’hĂŽte doit prendre en charge N × cgroup, et non « un pod de 16 Gio avec N=8 ». N’utilisez jamais replicas > 1 sur un mĂȘme fichier SQLite.

Exemple Compose (deux tas, deux volumes — pas deploy.replicas: 2) :

services:
omniroute-a:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-a-data:/app/data]
ports: ["20128:20128"]
omniroute-b:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-b-data:/app/data]
ports: ["20138:20128"]
volumes:
omniroute-a-data:
omniroute-b-data:

La densitĂ© au sein d’un mĂȘme processus (compression hors de l’isolate HTTP) est traitĂ©e dans #11023. Un cluster logique unique reposant sur un Ă©tat durable partagĂ© est traitĂ© dans #8075.

  • Mode WAL de SQLite : Il convient de laisser docker stop se terminer afin qu’OmniRoute puisse rĂ©intĂ©grer les derniĂšres modifications dans storage.sqlite via un point de contrĂŽle. Les fichiers Compose fournis dĂ©finissent dĂ©jĂ  un dĂ©lai d’arrĂȘt de 40 s. Si vous exĂ©cutez directement l’image, conservez --stop-timeout 40.
  • DISABLE_SQLITE_AUTO_BACKUP : DĂ©finissez cette variable sur true si les sauvegardes pĂ©riodiques/avant Ă©criture sont gĂ©rĂ©es en externe. Les migrations de bases de donnĂ©es existantes nĂ©cessitent toujours leur propre instantanĂ© de sĂ©curitĂ© durable ainsi qu’une protection contre les migrations massives.
  • Persistance des donnĂ©es : Montez toujours un volume sur /app/data afin de conserver votre base de donnĂ©es, vos clĂ©s et vos configurations lors des redĂ©marrages du conteneur.
  • Configuration du port : Remplacez la variable d’environnement PORT pour modifier le port par dĂ©faut 20128.

Code source d’OmniRoute (a58000c7685f)

HagiCode

HagiCode est un espace de développement agentique qui associe workflows structurés, exécution multi-agent et vues Hero Dungeon.

Transformez vos idées en logiciels utiles grùce à un workflow agentique plus intelligent, rapide et agréable.

Interface principale de HagiCode en thĂšme clair
  • SmartDes workflows structurĂ©s transforment une intention en parcours exĂ©cutable, de l’idĂ©e Ă  la livraison.
  • EfficientLes workflows multi-agents font avancer recherche, rĂ©alisation et revue en parallĂšle.
  • FunHero Dungeon rend les longues sessions de code plus visuelles et collaboratives.
Visiter HagiCode