mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
- mode agent : les outils write/edit/bash résolvent les chemins relatifs dans JEAN_HOME/workspace et le shell y démarre, au lieu du répertoire courant du processus (Bureau, ProgramData\jean\bin). Les chemins absolus demandés explicitement restent honorés. - Windows : le double-clic n'installe plus en silence. Boîte de dialogue unique, raccourcis menu Démarrer + Bureau, puis relance depuis la copie installée — un seul binaire fait foi. Désinstallation : retrait des raccourcis. - update : contrôle des droits AVANT le téléchargement (l'échec tombait sur le fichier temporaire, avec un message brut), message donnant la commande exacte, timeout UI porté de 30 s à 10 min. - jean where + bouton « Où sont mes fichiers ? » : emplacements, et alerte quand le binaire lancé n'est pas celui installé. - icônes : source unique (sys_brand_icon.go) pour la zone de notification, la barre de menus macOS et l'icône du .exe, alignée sur le favicon noir. macOS en icône template (une icône noire figée disparaît sur une barre sombre). - CI : le bundle macOS part de icon.png au lieu de convertir le .ico via sips, dont l'échec était masqué par « || true » ; notes de release versionnées.
85 lines
2.8 KiB
Go
85 lines
2.8 KiB
Go
package jean
|
|
|
|
// cli_app.go — expérience « application » de Jean.
|
|
//
|
|
// Quand on double-clique sur le binaire (aucun argument, console fraîche), au
|
|
// lieu d'afficher l'aide dans une console qui se ferme, Jean démarre son UI web,
|
|
// l'ouvre dans le navigateur ET pose une icône dans la zone de notification
|
|
// Windows (voir sys_tray_windows.go) : on voit que Jean tourne et on le pilote
|
|
// (« Ouvrir Jean » / « Quitter »).
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
const appPort = 8090
|
|
|
|
// cmdApp lance l'UI web, l'ouvre dans le navigateur et fait tourner l'icône de
|
|
// la zone de notification (Windows). Sur une machine vierge, il crée d'abord le
|
|
// dossier de données + config.env pour que l'interface s'ouvre quand même
|
|
// (l'écran d'accueil prendra le relais pour le choix du modèle).
|
|
func cmdApp(args []string) error {
|
|
url := fmt.Sprintf("http://localhost:%d", appPort)
|
|
|
|
// Premier lancement : prépare le dossier de données et, sous Windows, propose
|
|
// explicitement l'installation (voir sys_firstrun_windows.go). Si l'app a été
|
|
// relancée depuis la copie installée, ce process n'a plus rien à faire.
|
|
if appFirstRun() {
|
|
return nil
|
|
}
|
|
|
|
addr := fmt.Sprintf("0.0.0.0:%d", appPort)
|
|
ln, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
// Jean tourne déjà sur ce port : on ouvre juste l'UI sur l'instance
|
|
// existante plutôt que d'échouer.
|
|
fmt.Printf("AJEAN est déjà lancé — ouverture de %s\n", url)
|
|
return openBrowser(url)
|
|
}
|
|
|
|
// UN SEUL process propriétaire de la conversation, comme le service jean-link
|
|
// sous Linux : l'app sert l'UI locale ET le tunnel avec le MÊME mux. Deux
|
|
// process qui servent la conversation (objet en mémoire persisté dans
|
|
// conversation.json) donnent deux fils divergents entre le local et
|
|
// app.ajean.link — le bug qu'on a vécu avec le worker détaché.
|
|
appOwnsLink = true
|
|
appWebMux = newWebMux()
|
|
go func() { _ = http.Serve(ln, appWebMux) }()
|
|
|
|
go func() {
|
|
if readLinkToken() == "" {
|
|
return
|
|
}
|
|
// Worker détaché rescapé (version antérieure, autre copie de l'app) : il
|
|
// possède SA propre conversation et écrase la nôtre. On le supprime avant
|
|
// de prendre la main.
|
|
killForeignLinkWorker()
|
|
startAppLink(appWebMux)
|
|
}()
|
|
|
|
sp := showSplash("Lancement d'AJEAN en cours…")
|
|
waitServerReady(url)
|
|
_ = openBrowser(url)
|
|
time.Sleep(900 * time.Millisecond) // laisse le navigateur s'afficher par-dessus le splash
|
|
sp.close()
|
|
|
|
runTray(url) // icône zone de notification ; bloque jusqu'à « Quitter »
|
|
return nil
|
|
}
|
|
|
|
// waitServerReady attend que l'UI réponde (jusqu'à ~4 s) pour n'ouvrir le
|
|
// navigateur qu'une fois le serveur prêt.
|
|
func waitServerReady(url string) {
|
|
c := &http.Client{Timeout: 400 * time.Millisecond}
|
|
for i := 0; i < 20; i++ {
|
|
if resp, err := c.Get(url); err == nil {
|
|
resp.Body.Close()
|
|
return
|
|
}
|
|
time.Sleep(150 * time.Millisecond)
|
|
}
|
|
}
|