mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Les fichiers du chat vivaient dans un pot commun : on changeait de discussion et on revoyait les mêmes pièces jointes, et supprimer une discussion laissait derrière elle tout ce qu'on y avait déposé ou fait écrire à l'agent (seules ses captures, déjà rangées par identifiant, partaient avec). Chaque discussion a désormais son dossier, <workspace>/discussions/<id>/ : - les dépôts (uploads/), les captures (captures/) et ce que l'agent écrit y atterrissent ; le shell et les chemins relatifs du modèle y sont résolus ; - le panneau Fichiers s'ouvre sur ce dossier et n'en sort pas, et se redessine quand la discussion change (bascule ou vidage, signalés par le flux SSE) ; - supprimer une discussion — ou la vider — emporte ses fichiers. Les deux gestes le disent maintenant avant de demander confirmation ; « clear chat » en demandait aucune. La racine du dossier de travail reste la borne de sécurité : les liens des anciens messages (uploads/x.pdf, captures/<id>/y.jpg) continuent d'ouvrir leur fichier par un chemin de repli. Au démarrage, une migration range les captures dans le dossier de leur discussion et rend chaque dépôt à la discussion qui le mentionne dans son journal ; ce que personne ne réclame reste à la racine, atteignable par le bouton « hors discussion » du panneau, qui disparaît une fois le ménage fait. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
94 lines
2.9 KiB
Go
94 lines
2.9 KiB
Go
package loki
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// Dossier de travail du mode agent.
|
|
//
|
|
// Sans ça, les outils write/edit/bash héritent du répertoire courant du PROCESSUS :
|
|
// le modèle écrit "meteo.json", et le fichier atterrit là d'où l'utilisateur a
|
|
// lancé loki — le Bureau quand on double-clique l'installateur, C:\ProgramData\loki\bin
|
|
// quand on lance le binaire installé. Personne ne s'attend à voir un chat déposer
|
|
// des fichiers sur son Bureau. On donne donc à l'agent un dossier à lui : les
|
|
// chemins relatifs y sont résolus, et le shell y démarre. Les chemins ABSOLUS
|
|
// restent honorés tels quels — quand l'utilisateur demande d'écrire dans un
|
|
// dossier précis, ça doit marcher.
|
|
//
|
|
// agentWorkspace est la RACINE, commune à tout : le dossier de travail effectif
|
|
// est celui de la discussion ouverte (convWorkspace, chat_convfiles.go), pour
|
|
// qu'un fichier appartienne à la discussion où il est né et disparaisse avec
|
|
// elle. La racine reste la borne de sécurité des téléchargements.
|
|
|
|
const workspaceEnv = "LOKI_WORKSPACE"
|
|
|
|
var (
|
|
workspaceOnce sync.Once
|
|
workspacePath string
|
|
)
|
|
|
|
// agentWorkspace renvoie le dossier de travail de l'agent, créé au besoin. Il
|
|
// essaie plusieurs emplacements car LokiHome() vaut %ProgramData%\loki sous
|
|
// Windows, qui n'est pas inscriptible par un utilisateur non administrateur.
|
|
func agentWorkspace() string {
|
|
workspaceOnce.Do(func() {
|
|
for _, dir := range workspaceCandidates() {
|
|
if dir == "" {
|
|
continue
|
|
}
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
continue
|
|
}
|
|
// MkdirAll réussit sur un dossier existant même non inscriptible :
|
|
// on vérifie l'écriture réelle.
|
|
f, err := os.CreateTemp(dir, ".probe-*")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
name := f.Name()
|
|
f.Close()
|
|
os.Remove(name)
|
|
workspacePath = dir
|
|
return
|
|
}
|
|
// Dernier recours : le répertoire courant, comportement historique.
|
|
workspacePath, _ = os.Getwd()
|
|
})
|
|
return workspacePath
|
|
}
|
|
|
|
func workspaceCandidates() []string {
|
|
var c []string
|
|
if v := strings.TrimSpace(os.Getenv(workspaceEnv)); v != "" {
|
|
c = append(c, v)
|
|
}
|
|
c = append(c, workspaceDir())
|
|
if home, err := os.UserHomeDir(); err == nil && home != "" {
|
|
c = append(c, filepath.Join(home, "loki", "workspace"))
|
|
}
|
|
c = append(c, filepath.Join(os.TempDir(), "loki-workspace"))
|
|
return c
|
|
}
|
|
|
|
// resolveAgentPath résout un chemin fourni par le modèle. Absolu → inchangé ;
|
|
// "~/x" → dans le home de l'utilisateur ; relatif → dans le dossier de la
|
|
// discussion ouverte.
|
|
func resolveAgentPath(p string) string {
|
|
p = strings.TrimSpace(p)
|
|
if p == "" {
|
|
return p
|
|
}
|
|
if p == "~" || strings.HasPrefix(p, "~/") || strings.HasPrefix(p, `~\`) {
|
|
if home, err := os.UserHomeDir(); err == nil && home != "" {
|
|
return filepath.Join(home, filepath.FromSlash(strings.TrimPrefix(p[1:], "/")))
|
|
}
|
|
}
|
|
if filepath.IsAbs(p) {
|
|
return p
|
|
}
|
|
return filepath.Join(convWorkspace(), filepath.FromSlash(p))
|
|
}
|