From 97879238799386205ea9a2df9ba701bf161ca05d Mon Sep 17 00:00:00 2001 From: nathaninline Date: Sat, 25 Jul 2026 11:37:42 +0200 Subject: [PATCH] macOS : service en mode utilisateur quand aucun LaunchDaemon n'est installe Jean.app tourne sans root : launchctl load echouait sur un plist absent et l'UI restait bloquee sur << service arrete >>. On bascule alors sur le meme mecanisme que Windows (jean serve detache + fichier PID), avec setsid pour survivre a la fermeture de l'app et un kill par groupe pour emporter llama-server. Ajoute aussi l'action 'status', qui manquait cote darwin. --- internal/jean/sys_service_darwin.go | 167 ++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/internal/jean/sys_service_darwin.go b/internal/jean/sys_service_darwin.go index 1a2f7ea..c2d046b 100644 --- a/internal/jean/sys_service_darwin.go +++ b/internal/jean/sys_service_darwin.go @@ -7,7 +7,9 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" + "syscall" "time" ) @@ -34,6 +36,14 @@ func launchdLogPath() string { return filepath.Join(JeanHome(), serviceName()+". func serviceAction(action string) error { svc := serviceName() plist := launchdPlistPath(svc) + // Pas de LaunchDaemon installé — cas normal d'un Mac de bureau, où Jean tourne + // via Jean.app sans jamais passer par `sudo jean install` : on gère alors le + // service comme sous Windows, avec un `jean serve` détaché suivi par un fichier + // PID. Aucun droit root requis. Sans ça, `start` échouait sur un plist absent + // et l'UI affichait éternellement « service arrêté ». + if _, err := os.Stat(plist); err != nil { + return userSvcAction(action) + } run := func(args ...string) error { bin, a := "launchctl", args if os.Geteuid() != 0 { @@ -61,6 +71,15 @@ func serviceAction(action string) error { return err } return checkStarted(svc) + case "status": + if serviceIsActive() { + fmt.Printf("%s %s: actif (launchd)\n", green("[ok]"), svc) + } else { + fmt.Printf("%s %s: arrêté\n", yellow("[info]"), svc) + } + fmt.Printf(" logs : %s\n", launchdLogPath()) + fmt.Printf(" config : %s\n", confPath()) + return nil } return fmt.Errorf("action inconnue: %s", action) } @@ -94,10 +113,158 @@ func serviceLogs() error { // serviceIsActive : le service est chargé ET possède un PID courant. `launchctl // list