Le Problème : Des Notifications CLI Invisibles sur ChromeOS
Les assistants IA en ligne de commande (CLI) comme OpenCode, Antigravity CLI ou Claude Code sont devenus des outils indispensables. Mais sur ChromeOS via Crostini (le conteneur Linux), un problème récurrent se pose : les notifications notify-send sont envoyées dans le vide et jamais visibles dans le panneau de notification ChromeOS.
Vous lancez une tâche longue (build, analyse, déploiement), vous passez sur un autre bureau virtuel, et quand la tâche se termine... rien. Pas de notification. Vous devez retourner manuellement sur votre terminal pour vérifier l'état.
Pourquoi les Notifications CLI ne Marchent Pas par Défaut
Sur une installation Linux classique, notify-send envoie une notification via D-Bus au daemon de notification (comme Dunst, Mako, ou GNOME Shell). ChromeOS utilise une architecture différente :
CLI → notify-send → D-Bus → ??? (rien d'affiché)
Le problème vient de l'architecture Crostini elle-même. ChromeOS ne fait pas tourner un daemon de notification visible standard. À la place, le daemon Garcon (Chromium OS Garcon Bridge) intercepte les notifications D-Bus comme solution de repli. Mais Garcon ne les forwarde pas à l'interface utilisateur ChromeOS. Les notifications sont acceptées sur D-Bus, retournent un code de succès... et sont immédiatement jetées.
Notre Parcours de Recherche
Première Approche (Échec) : Dunst & paplay
Notre première idée a été d'installer un daemon de notification standard :
sudo apt install dunst libnotify-bin
Résultat : Un panneau rouge intrusif apparaît (interface dunst), pas intégré du tout à ChromeOS. De plus, dunst.service entre en conflit avec Garcon sur le nom D-Bus org.freedesktop.Notifications. Dunst est un panneau "maison" qui ne s'intègre pas au look natif.
On a aussi essayé paplay pour ajouter des sons différenciés :
paplay /usr/share/sounds/freedesktop/stereo/complete.oga
Problème : Le son joue dans le conteneur Linux, pas dans ChromeOS. Si l'utilisateur est sur un autre bureau, il n'entend rien.
Deuxième Approche (Échec) : notify-send sans daemon
En supprimant Dunst, on a cru que Garcon forwardait les notifications à ChromeOS. Le notify-send retournait un exit code 0. Pourtant, rien ne s'affichait.
Cause racine : Garcon possède org.freedesktop.Notifications sur D-Bus et implémente l'interface de notification (GetServerInformation retourne même "dunst" en raison du code interne partagé). Mais son implémentation est un puits noir : il accepte les notifications mais ne les affiche ni ne les forwarde.
La Solution : cros-notificationd
L'Architecture Officielle ChromeOS
Après des recherches approfondies dans le code source ChromiumOS et la documentation Crostini, la solution officielle est apparue :
notify-send → D-Bus → org.freedesktop.Notifications → notificationd → Wayland → Sommelier → ChromeOS
notificationd est un daemon officiel Google qui :
- Écoute sur org.freedesktop.Notifications via D-Bus
- Se connecte au Wayland compositor (Sommelier) via le protocole shell
- Transmet les notifications à ChromeOS qui les affiche dans le panneau natif (bas droite, près de l'horloge)
Pourquoi notificationd n'était Pas Actif
Le package cros-notificationd (version 0.3, ~1 Ko) est un package officiel du dépôt Google Crostini. Il installe :
- Un fichier de service D-Bus : /usr/share/dbus-1/services/org.freedesktop.Notifications.service
- Une unité systemd user : cros-notificationd.service
Mais ce package n'est pas installé par défaut dans les conteneurs Crostini Debian 13 (Trixie). Résultat : Garcon prend le nom D-Bus comme fallback et les notifications disparaissent.
Comment nous Avons Résolu le Problème
Étape 1 : Supprimer les daemons concurrents
Dunst entre en conflit direct avec notificationd :
sudo apt remove dunst
systemctl --user daemon-reload
Étape 2 : Installer cros-notificationd
sudo apt install cros-notificationd
Ce package active le service D-Bus qui démarre notificationd à la demande (demand-activation).
Étape 3 : Redémarrer le conteneur
Un simple redémarrage du terminal Linux suffit. Après redémarrage :
- notificationd réclame org.freedesktop.Notifications sur D-Bus
- Garcon échoue silencieusement à le réclamer (notificationd gagne)
- Tous les appels notify-send sont maintenant forwardés à ChromeOS
Architecture Finale : Notifications Actionnables
Le Plugin OpenCode
Nous avons créé un plugin OpenCode qui s'interrompt au cycle de vie de l'agent :
// ~/.config/opencode/plugins/notification.js
export const NotificationPlugin = async ({ $ }) => {
return {
event: async ({ event }) => {
const icon = `${process.env.HOME}/.config/opencode/icon.png`
const helper = `${process.env.HOME}/.opencode/bin/helper-notify.sh`
if (event.type === "permission.asked") {
await ``${helper} ${icon} critical "OpenCode" "🔔 Action requise"``
}
if (event.type === "session.idle") {
await ``${helper} ${icon} normal "OpenCode" "✅ Tâche terminée"``
}
if (event.type === "session.error") {
await ``${helper} ${icon} critical "OpenCode" "❌ Erreur détectée"``
}
},
}
}
Le Script Helper (Action Clic → Focus Terminal)
Pour rendre les notifications actionnables (clic = focus terminal), nous utilisons la fonctionnalité --action de notify-send :
#!/bin/bash
# ~/.opencode/bin/helper-notify.sh
ICON="$1"
URGENCY="$2"
TITLE="$3"
MESSAGE="$4"
ACTION=$(notify-send -i "$ICON" -u "$URGENCY" -A "default=Focus" -t 0 "$TITLE" "$MESSAGE")
if [ "$ACTION" = "default" ]; then
wmctrl -a "$TITLE" 2>/dev/null || wmctrl -R "Terminal" 2>/dev/null || true
fi
Points clés :
- -A "default=Focus" : rend la notification cliquable avec l'action par défaut "Focus"
- -t 0 : la notification ne se cache jamais toute seule (expire au clic uniquement)
- wmctrl : tente de focus la fenêtre du terminal (X11 via XWayland)
- Sur ChromeOS, le clic sur la notification déclenche aussi le comportement natif de ChromeOS (focus automatique du conteneur)
Icône OpenCode
Pour une intégration parfaite, nous avons récupéré le logo officiel OpenCode (apple-touch-icon 180×180) :
curl -sL -o ~/.config/opencode/icon.png https://opencode.ai/apple-touch-icon-v3.png
Leçons Apprises et Bonnes Pratiques
1. Ne JAMAIS Tuer Garcon
Règle absolue : Garcon est le daemon central d'intégration ChromeOS. Il gère :
- Le lancement des applications Linux depuis le lanceur ChromeOS
- Les types MIME et associations de fichiers
- Le proxy SFTP (partage de fichiers ChromeOS ↔ conteneur)
- Les mises à jour des packages Google Crostini
Si vous stoppez Garcon (systemctl --user stop cros-garcon.service), tout le conteneur s'effondre : les MCP serveurs plantent, le terminal se déconnecte, Chromium headless crashe. Un simple reboot du conteneur suffit pour que notificationd prenne le relais.
2. Vérifier le Daemon de Notification
Après installation, confirmez que notificationd gère les notifications :
# Vérifier qui possède org.freedesktop.Notifications
dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply \
/org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner \
string:org.freedesktop.Notifications
# Vérifier que les actions sont supportées
dbus-send --session --dest=org.freedesktop.Notifications --type=method_call --print-reply \
/org/freedesktop/Notifications org.freedesktop.Notifications.GetCapabilities
La sortie doit montrer :
array [
string "actions"
string "body"
]
3. Utilisez les Notifications Actionnables
Utilisez -A "default=ActionLabel" pour rendre les notifications cliquables. "default" est le mot-clé spécial qui correspond au clic sur le corps de la notification.
4. Gestion des Sons
N'utilisez PAS paplay ou d'autres outils audio dans le conteneur. ChromeOS joue son propre son de notification pour les notifications natives. C'est plus propre, moins intrusif, et parfaitement intégré.
5. Multiples Bureaux Virtuels
Sur ChromeOS, les notifications apparaissent sur le bureau actif et restent dans le panneau de notification (en bas à droite) même après avoir changé de bureau virtuel. L'utilisateur peut cliquer dessus à tout moment pour revenir sur le terminal.
Dépendances et Paquets
| Paquet | Rôle | Taille |
|---|---|---|
| cros-notificationd | Pont D-Bus → Wayland → ChromeOS | 1 Ko |
| libnotify-bin | Client notify-send | Pré-installé |
| sound-theme-freedesktop | Sons système (optionnel) | 200 Ko |
| wmctrl | Focus fenêtre X11 (action clic) | 100 Ko |
Conclusion
Les notifications ChromeOS natives pour outils CLI sont non seulement possibles mais élégantes. La clé était le package officiel cros-notificationd, absent par défaut du conteneur Debian 13 Trixie.
En suivant cette architecture :
- ✅ Notifications visibles dans le panneau ChromeOS (bas droite)
- ✅ Icône personnalisée (OpenCode)
- ✅ Son système ChromeOS (pas de bruit parasites)
- ✅ Clic → focus terminal (quand possible)
- ✅ Auto-dismiss au clic (comportement natif)
- ✅ Trois niveaux d'urgence (normal, critical)
- ✅ Fonctionne sur tous les bureaux virtuels
Le code source complet des plugins est disponible dans ~/.config/opencode/plugins/notification.js et fonctionne avec OpenCode, Antigravity CLI, et tout outil supportant notify-send.