v0.2.7 : jean link sûr — 'start' + aide, ne confond plus un mot avec un token

- 'jean link start' démarre le service (sous-commande dédiée)
- 'jean link' seul affiche l'aide des sous-commandes (ne démarre plus)
- un argument n'est traité comme token que s'il commence par 'jl_' ; sinon
  erreur + aide, SANS écraser le token enregistré
  (corrige le bug où 'jean link start'/typo écrasait le token -> 401 -> injoignable)
- message 'déjà en cours' au lieu d'un faux 'starté'
This commit is contained in:
nathaninline committed 2026-06-25 13:55:47 +02:00
1 parent e516ca7301
commit 76f644e449
2 files changed
+47 -12

No files matched your search

+43 -9
View File
@@ -89,10 +89,17 @@ func cmdLink(args []string) error {
sub = args[0] sub = args[0]
} }
switch sub { switch sub {
case "":
// `jean link` seul : afficher l'aide des sous-commandes (NE démarre pas —
// éviter de prendre un mot pour un token et d'écraser le vrai).
printLinkHelp()
return nil
case "serve", "--foreground", "fg": case "serve", "--foreground", "fg":
// Le worker réel (boucle de connexion au relais), pendant de `jean serve`. // Le worker réel (boucle de connexion au relais), pendant de `jean serve`.
// C'est ce que lance l'unité systemd ; il NE doit PAS rendre la main. // C'est ce que lance l'unité systemd ; il NE doit PAS rendre la main.
return runLinkForeground() return runLinkForeground()
case "start":
return startLink(false)
case "status": case "status":
tok := readLinkToken() tok := readLinkToken()
if tok == "" { if tok == "" {
@@ -126,20 +133,30 @@ func cmdLink(args []string) error {
return nil return nil
} }
// Sans sous-commande : (optionnellement) enregistrer le token, puis DÉMARRER le // Un argument restant n'est traité comme TOKEN que s'il en a la forme (`jl_…`).
// service en arrière-plan (ne bloque pas le terminal) et afficher empreinte + code. // Sinon c'est une faute de frappe / sous-commande inconnue : on REFUSE, sans
gotToken := sub != "" // jamais écraser le token enregistré (le bug qui rendait le serveur injoignable).
if gotToken { if !strings.HasPrefix(sub, "jl_") {
if err := saveLinkToken(strings.TrimSpace(sub)); err != nil { fmt.Fprintf(os.Stderr, "%s sous-commande inconnue : %q\n\n", yellow("[link]"), sub)
return err printLinkHelp()
} return fmt.Errorf("sous-commande link inconnue: %s", sub)
} }
if err := saveLinkToken(strings.TrimSpace(sub)); err != nil {
return err
}
// Nouveau token → on (re)démarre le worker pour qu'il le prenne en compte.
return startLink(true)
}
// startLink démarre (ou redémarre si force) le service de lien puis affiche
// l'identité (empreinte + code). Si le service tourne déjà et qu'on ne force pas,
// on le signale au lieu d'un faux « démarré ».
func startLink(force bool) error {
if readLinkToken() == "" { if readLinkToken() == "" {
return fmt.Errorf("aucun token. Usage: jean link <token> (token fourni à l'achat sur la boutique)") return fmt.Errorf("aucun token. Usage: jean link <token> (token fourni à l'achat sur la boutique)")
} }
switch { switch {
case gotToken: case force:
// Un nouveau token n'est pris en compte qu'au redémarrage du worker.
if err := linkServiceCtl("restart"); err != nil { if err := linkServiceCtl("restart"); err != nil {
return err return err
} }
@@ -153,6 +170,23 @@ func cmdLink(args []string) error {
return linkPrintIdentity() return linkPrintIdentity()
} }
// printLinkHelp liste les sous-commandes de `jean link`.
func printLinkHelp() {
fmt.Print(`jean link — accès distant via le relais ajean.link
Usage :
jean link <token> enregistre le token (1re fois / pour le changer) et démarre le lien
jean link start démarre le lien en arrière-plan (service)
jean link restart redémarre le service de lien
jean link stop arrête le service de lien
jean link status état du token et du service
jean link code génère un code d'appairage (valable 10 min, à usage unique)
jean link logout oublie le token enregistré
Le token est fourni sur ajean.link. « jean link » seul affiche cette aide.
`)
}
// linkPrintIdentity affiche l'empreinte E2E (à confirmer une fois) et un code // linkPrintIdentity affiche l'empreinte E2E (à confirmer une fois) et un code
// d'appairage frais (à saisir une fois) pour le portail. // d'appairage frais (à saisir une fois) pour le portail.
func linkPrintIdentity() error { func linkPrintIdentity() error {
+4 -3
View File
@@ -8,7 +8,7 @@ import (
"strings" "strings"
) )
const Version = "0.2.6" const Version = "0.2.7"
func main() { func main() {
args := os.Args[1:] args := os.Args[1:]
@@ -92,10 +92,11 @@ Interaction:
web [PORT] UI web (défaut :8090) — chat + presets + skills + tools web [PORT] UI web (défaut :8090) — chat + presets + skills + tools
Accès distant (ajean.link) : Accès distant (ajean.link) :
link [token] démarre le lien au relais en arrière-plan (service) ; token = 1re fois / pour le changer link <token> enregistre le token et démarre le lien au relais (token = 1re fois / pour le changer)
link restart | stop redémarre / arrête le service de lien link start | restart | stop démarre / redémarre / arrête le service de lien
link code génère un code d'appairage (valable 10 min, à usage unique) pour le portail link code génère un code d'appairage (valable 10 min, à usage unique) pour le portail
link status | logout état du lien / oublier le token link status | logout état du lien / oublier le token
link (sans argument) affiche l'aide des sous-commandes link
link serve exécute le worker au premier plan (utilisé par jean-link.service ; pendant de 'jean serve') link serve exécute le worker au premier plan (utilisé par jean-link.service ; pendant de 'jean serve')
LLM-side outils: LLM-side outils: