diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cdd8895..2320f31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,7 +28,7 @@ jobs:
# par Cocoa (CGO) → les cibles macOS se compilent sur un runner Apple
# (job « macos » ci-dessous, comme dans release.yml).
for t in linux/amd64 linux/arm64 windows/amd64 windows/arm64; do
- GOOS=${t%/*} GOARCH=${t#*/} CGO_ENABLED=0 go build -o /dev/null ./cmd/ajean
+ GOOS=${t%/*} GOARCH=${t#*/} CGO_ENABLED=0 go build -o /dev/null ./cmd/loki
done
macos:
@@ -45,7 +45,7 @@ jobs:
- run: go test ./...
- name: build macOS (CGO, arm64 + amd64)
run: |
- CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o /dev/null ./cmd/ajean
+ CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o /dev/null ./cmd/loki
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
CGO_CFLAGS="-arch x86_64" CGO_LDFLAGS="-arch x86_64" \
- go build -o /dev/null ./cmd/ajean
+ go build -o /dev/null ./cmd/loki
diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml
index 424bdf4..2e99451 100644
--- a/.github/workflows/mac-build.yml
+++ b/.github/workflows/mac-build.yml
@@ -1,4 +1,4 @@
-# Build macOS à la demande, SANS rien publier : produit AJEAN.app en artifact
+# Build macOS à la demande, SANS rien publier : produit Loki.app en artifact
# téléchargeable depuis l'onglet Actions (visible seulement par le dépôt).
# Sert à tester le bundle avant de décider d'une vraie release (release.yml).
name: mac-build
@@ -13,40 +13,40 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- - name: build + bundle AJEAN.app (arm64)
+ - name: build + bundle Loki.app (arm64)
run: |
mkdir -p dist
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
- go build -trimpath -ldflags="-s -w" -o dist/ajean-macos-arm ./cmd/ajean
- mkdir -p /tmp/ajean.iconset
- sips -s format png 'cmd/ajean/icon.ico' --out /tmp/icon.png >/dev/null 2>&1 || true
+ go build -trimpath -ldflags="-s -w" -o dist/loki-macos-arm ./cmd/loki
+ mkdir -p /tmp/loki.iconset
+ sips -s format png 'cmd/loki/icon.ico' --out /tmp/icon.png >/dev/null 2>&1 || true
for s in 16 32 128 256 512; do
- sips -z $s $s /tmp/icon.png --out "/tmp/ajean.iconset/icon_${s}x${s}.png" >/dev/null 2>&1 || true
+ sips -z $s $s /tmp/icon.png --out "/tmp/loki.iconset/icon_${s}x${s}.png" >/dev/null 2>&1 || true
done
- iconutil -c icns /tmp/ajean.iconset -o /tmp/ajean.icns >/dev/null 2>&1 || true
- app="dist/AJEAN.app"
+ iconutil -c icns /tmp/loki.iconset -o /tmp/loki.icns >/dev/null 2>&1 || true
+ app="dist/Loki.app"
mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources"
- cp dist/ajean-macos-arm "$app/Contents/MacOS/ajean"
- chmod +x "$app/Contents/MacOS/ajean"
- cp /tmp/ajean.icns "$app/Contents/Resources/ajean.icns" 2>/dev/null || true
+ cp dist/loki-macos-arm "$app/Contents/MacOS/loki"
+ chmod +x "$app/Contents/MacOS/loki"
+ cp /tmp/loki.icns "$app/Contents/Resources/loki.icns" 2>/dev/null || true
printf '%s\n' \
'' \
'' \
'' \
- 'CFBundleNameAJEAN' \
- 'CFBundleDisplayNameAJEAN' \
- 'CFBundleExecutableajean' \
- 'CFBundleIdentifierlink.ajean.app' \
- 'CFBundleIconFileajean' \
+ 'CFBundleNameLoki' \
+ 'CFBundleDisplayNameLoki' \
+ 'CFBundleExecutableloki' \
+ 'CFBundleIdentifierlink.loki.app' \
+ 'CFBundleIconFileloki' \
'CFBundlePackageTypeAPPL' \
'LSMinimumSystemVersion11.0' \
'LSUIElement' \
'NSHighResolutionCapable' \
'' > "$app/Contents/Info.plist"
codesign --force --deep -s - "$app" || true
- (cd dist && zip -qry ajean-macos-arm.zip AJEAN.app && rm -rf AJEAN.app)
+ (cd dist && zip -qry loki-macos-arm.zip Loki.app && rm -rf Loki.app)
ls -l dist
- uses: actions/upload-artifact@v4
with:
- name: ajean-macos-arm
- path: dist/ajean-macos-arm.zip
+ name: loki-macos-arm
+ path: dist/loki-macos-arm.zip
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ed84f34..9cd15e1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,11 +1,11 @@
# Release : sur tag vX.Y.Z, compile les 6 cibles et publie la release GitHub
-# avec les binaires (ajean-linux, ajean-macos, ajean-windows.exe et leurs
-# variantes -arm) ET le fichier SHA256SUMS que `ajean update` vérifie avant
+# avec les binaires (loki-linux, loki-macos, loki-windows.exe et leurs
+# variantes -arm) ET le fichier SHA256SUMS que `loki update` vérifie avant
# d'installer. Ces noms doivent rester en phase avec assetNameFor (sys_update.go),
# sinon plus aucune machine ne se met à jour.
# ⚠️ NE PAS uploader de binaires à la main en plus : ce workflow écrase les
# assets du même nom (softprops remplace), et des sommes qui ne correspondent
-# plus aux binaires font échouer `ajean update` (vécu sur v0.4.7).
+# plus aux binaires font échouer `loki update` (vécu sur v0.4.7).
#
# Deux jobs de build : macOS a besoin d'un vrai runner Apple (l'icône de barre de
# menus passe par Cocoa → CGO, pas cross-compilable depuis Linux) ; Linux et
@@ -31,43 +31,43 @@ jobs:
mkdir -p dist
# arm64 = natif ; amd64 = cross avec le SDK universel (-arch x86_64).
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
- go build -trimpath -ldflags="-s -w" -o dist/ajean-macos-arm ./cmd/ajean
+ go build -trimpath -ldflags="-s -w" -o dist/loki-macos-arm ./cmd/loki
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
CGO_CFLAGS="-arch x86_64" CGO_LDFLAGS="-arch x86_64" \
- go build -trimpath -ldflags="-s -w" -o dist/ajean-macos ./cmd/ajean
- file dist/ajean-macos*
- - name: bundles AJEAN.app
+ go build -trimpath -ldflags="-s -w" -o dist/loki-macos ./cmd/loki
+ file dist/loki-macos*
+ - name: bundles Loki.app
run: |
# Un binaire nu double-cliqué ouvrirait le Terminal. On publie donc
- # aussi un vrai bundle AJEAN.app (zippé pour garder le bit exécutable) :
- # au clic, le Finder lance Contents/MacOS/ajean, qui détecte le bundle
+ # aussi un vrai bundle Loki.app (zippé pour garder le bit exécutable) :
+ # au clic, le Finder lance Contents/MacOS/loki, qui détecte le bundle
# et bascule en mode « application » (UI web + navigateur + icône dans
- # la barre de menus), comme le double-clic sur ajean.exe sous Windows.
+ # la barre de menus), comme le double-clic sur loki.exe sous Windows.
# LSUIElement : app de barre de menus, aucune icône dans le Dock.
# L'icône part du PNG 1024 généré avec le reste de la marque
- # (cmd/ajean/icon.png, voir tools/gen-icon) : plus de conversion depuis
+ # (cmd/loki/icon.png, voir tools/gen-icon) : plus de conversion depuis
# le .ico, dont l'échec silencieux donnait une app sans icône.
- mkdir -p /tmp/ajean.iconset
+ mkdir -p /tmp/loki.iconset
for s in 16 32 128 256 512; do
- sips -z $s $s 'cmd/ajean/icon.png' --out "/tmp/ajean.iconset/icon_${s}x${s}.png" >/dev/null
+ sips -z $s $s 'cmd/loki/icon.png' --out "/tmp/loki.iconset/icon_${s}x${s}.png" >/dev/null
done
- iconutil -c icns /tmp/ajean.iconset -o /tmp/ajean.icns
+ iconutil -c icns /tmp/loki.iconset -o /tmp/loki.icns
v="${GITHUB_REF_NAME#v}"
- for bin in ajean-macos ajean-macos-arm; do
- app="dist/bundle-$bin/AJEAN.app"
+ for bin in loki-macos loki-macos-arm; do
+ app="dist/bundle-$bin/Loki.app"
mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources"
- cp "dist/$bin" "$app/Contents/MacOS/ajean"
- chmod +x "$app/Contents/MacOS/ajean"
- cp /tmp/ajean.icns "$app/Contents/Resources/ajean.icns" 2>/dev/null || true
+ cp "dist/$bin" "$app/Contents/MacOS/loki"
+ chmod +x "$app/Contents/MacOS/loki"
+ cp /tmp/loki.icns "$app/Contents/Resources/loki.icns" 2>/dev/null || true
printf '%s\n' \
'' \
'' \
'' \
- 'CFBundleNameAJEAN' \
- 'CFBundleDisplayNameAJEAN' \
- 'CFBundleExecutableajean' \
- 'CFBundleIdentifierlink.ajean.app' \
- 'CFBundleIconFileajean' \
+ 'CFBundleNameLoki' \
+ 'CFBundleDisplayNameLoki' \
+ 'CFBundleExecutableloki' \
+ 'CFBundleIdentifierlink.loki.app' \
+ 'CFBundleIconFileloki' \
'CFBundlePackageTypeAPPL' \
"CFBundleShortVersionString$v" \
"CFBundleVersion$v" \
@@ -78,7 +78,7 @@ jobs:
# Signature ad-hoc : sans elle, macOS refuse purement et simplement
# de lancer un binaire arm64 non signé (pas juste un avertissement).
codesign --force --deep -s - "$app" || true
- (cd "dist/bundle-$bin" && zip -qry "../$bin.zip" AJEAN.app)
+ (cd "dist/bundle-$bin" && zip -qry "../$bin.zip" Loki.app)
rm -rf "dist/bundle-$bin"
done
ls -l dist
@@ -98,13 +98,13 @@ jobs:
- name: build Linux + Windows
run: |
mkdir -p dist
- # Noms d'asset : ajean-[-arm][.exe]. Volontairement lisibles, et
- # volontairement DIFFÉRENTS du schéma ajean-- des 0.7 :
+ # Noms d'asset : loki-[-arm][.exe]. Volontairement lisibles, et
+ # volontairement DIFFÉRENTS du schéma loki-- des 0.7 :
# leur mise à jour automatique ne trouve alors aucun asset et échoue
# sans rien remplacer, au lieu d'installer un binaire 0.8 sur une
# machine encore agencée en 0.7 (voir updateAssetName, sys_update.go).
- for t in linux/amd64:ajean-linux linux/arm64:ajean-linux-arm \
- windows/amd64:ajean-windows.exe windows/arm64:ajean-windows-arm.exe; do
+ for t in linux/amd64:loki-linux linux/arm64:loki-linux-arm \
+ windows/amd64:loki-windows.exe windows/arm64:loki-windows-arm.exe; do
target=${t%%:*}; name=${t#*:}
os=${target%/*}; arch=${target#*/}; ldflags="-s -w"
# Windows : sous-système CONSOLE, pour que `cmd` attende la fin du
@@ -112,7 +112,7 @@ jobs:
# fonctionnent. La console créée au double-clic est refermée par le
# programme lui-même (voir setupConsole, sys_console_windows.go).
GOOS=$os GOARCH=$arch CGO_ENABLED=0 \
- go build -trimpath -ldflags="$ldflags" -o "dist/$name" ./cmd/ajean
+ go build -trimpath -ldflags="$ldflags" -o "dist/$name" ./cmd/loki
done
- uses: actions/download-artifact@v4
with:
@@ -120,11 +120,11 @@ jobs:
path: dist
- name: SHA256SUMS
run: |
- chmod +x dist/ajean-macos dist/ajean-macos-arm
- # Tous les assets commencent par « ajean- », zips compris : un seul
+ chmod +x dist/loki-macos dist/loki-macos-arm
+ # Tous les assets commencent par « loki- », zips compris : un seul
# motif suffit. Ajouter « *.zip » les compterait DEUX fois, et un
- # SHA256SUMS à doublons est exactement ce que `ajean update` vérifie.
- (cd dist && sha256sum ajean-* > SHA256SUMS)
+ # SHA256SUMS à doublons est exactement ce que `loki update` vérifie.
+ (cd dist && sha256sum loki-* > SHA256SUMS)
ls -l dist && cat dist/SHA256SUMS
- name: publier la release
uses: softprops/action-gh-release@v2
diff --git a/.gitignore b/.gitignore
index 3710cdb..4244b68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
# Binaires compilés
-/ajean
-/ajean.exe
+/loki
+/loki.exe
/dist/
# Artefacts de build Go
diff --git a/cmd/ajean/icon.ico b/cmd/loki/icon.ico
similarity index 100%
rename from cmd/ajean/icon.ico
rename to cmd/loki/icon.ico
diff --git a/cmd/ajean/icon.png b/cmd/loki/icon.png
similarity index 100%
rename from cmd/ajean/icon.png
rename to cmd/loki/icon.png
diff --git a/cmd/ajean/main.go b/cmd/loki/main.go
similarity index 74%
rename from cmd/ajean/main.go
rename to cmd/loki/main.go
index 2846d3b..985e385 100644
--- a/cmd/ajean/main.go
+++ b/cmd/loki/main.go
@@ -1,4 +1,4 @@
-// Point d'entrée du binaire ajean. Tout le code vit dans internal/ajean ; ce
+// Point d'entrée du binaire loki. Tout le code vit dans internal/loki ; ce
// dossier ne porte que le main() et les ressources Windows (.syso, icône,
// versioninfo) qui doivent résider dans le dossier du package main.
//
@@ -7,11 +7,11 @@
// les .syso après avoir bumpé la version : `go generate ./...`
// (nécessite : go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest)
//
-//go:generate go run github.com/nathaninline/ajean/tools/gen-icon icon.ico
+//go:generate go run github.com/R0m1k3/Loki/tools/gen-icon icon.ico
//go:generate goversioninfo -64 -icon=icon.ico -o resource_windows_amd64.syso versioninfo.json
//go:generate goversioninfo -64 -arm -icon=icon.ico -o resource_windows_arm64.syso versioninfo.json
package main
-import "github.com/nathaninline/ajean/internal/ajean"
+import "github.com/R0m1k3/Loki/internal/loki"
-func main() { ajean.Main() }
+func main() { loki.Main() }
diff --git a/cmd/ajean/resource_windows_amd64.syso b/cmd/loki/resource_windows_amd64.syso
similarity index 100%
rename from cmd/ajean/resource_windows_amd64.syso
rename to cmd/loki/resource_windows_amd64.syso
diff --git a/cmd/ajean/resource_windows_arm64.syso b/cmd/loki/resource_windows_arm64.syso
similarity index 100%
rename from cmd/ajean/resource_windows_arm64.syso
rename to cmd/loki/resource_windows_arm64.syso
diff --git a/cmd/ajean/versioninfo.json b/cmd/loki/versioninfo.json
similarity index 56%
rename from cmd/ajean/versioninfo.json
rename to cmd/loki/versioninfo.json
index 63296b7..9129eed 100644
--- a/cmd/ajean/versioninfo.json
+++ b/cmd/loki/versioninfo.json
@@ -19,14 +19,14 @@
"FileSubType": "00"
},
"StringFileInfo": {
- "CompanyName": "AJEAN contributors",
- "FileDescription": "AJEAN — gestionnaire mono-binaire de serveurs llama.cpp auto-hébergés",
- "InternalName": "ajean",
- "LegalCopyright": "Copyright (c) 2026 AJEAN contributors. MIT License.",
- "OriginalFilename": "ajean.exe",
- "ProductName": "AJEAN",
+ "CompanyName": "Loki contributors",
+ "FileDescription": "Loki — gestionnaire mono-binaire de serveurs llama.cpp auto-hébergés",
+ "InternalName": "loki",
+ "LegalCopyright": "Copyright (c) 2026 Loki contributors. MIT License.",
+ "OriginalFilename": "loki.exe",
+ "ProductName": "Loki",
"ProductVersion": "0.9.4",
- "Comments": "https://github.com/nathaninline/ajean — projet open source (MIT)"
+ "Comments": "https://github.com/R0m1k3/Loki — projet open source (MIT)"
},
"VarFileInfo": {
"Translation": {
diff --git a/go.mod b/go.mod
index 39cfd71..86ba813 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/nathaninline/ajean
+module github.com/R0m1k3/Loki
go 1.25.0
diff --git a/internal/ajean/backend_build.go b/internal/loki/backend_build.go
similarity index 98%
rename from internal/ajean/backend_build.go
rename to internal/loki/backend_build.go
index 53e4918..7dc999e 100644
--- a/internal/ajean/backend_build.go
+++ b/internal/loki/backend_build.go
@@ -1,6 +1,6 @@
// backend_build.go — machinerie de compilation de llama.cpp : détection du
// plan de build (CUDA/ROCm/Metal/Vulkan/CPU), cmake, suivi de progression, logs.
-package ajean
+package loki
import (
"bufio"
@@ -73,7 +73,7 @@ func detectBuildPlan() buildPlan {
// L'UI web embarquée de llama-server exige npm (ou un téléchargement
// d'assets pré-compilés depuis HuggingFace) pour générer un service-worker
// PWA — une dépendance lourde qui casse le build sur une machine sans node.
- // ajean fournit sa propre UI, donc on la désactive : build plus rapide et
+ // loki fournit sa propre UI, donc on la désactive : build plus rapide et
// sans dépendance réseau/npm. BUILD_UI=OFF coupe npm ; USE_PREBUILT_UI=OFF
// coupe le téléchargement d'assets pré-compilés depuis HuggingFace (qui
// échoue sur un réseau restreint et fait planter l'embed). Sur un checkout
@@ -237,7 +237,7 @@ func hintMissingBuildDep(p buildPlan, cfgLog string) {
fmt.Printf(" Relance l'installeur du CUDA Toolkit (installation personnalisée) en cochant « CUDA → Visual Studio Integration »\n")
fmt.Printf(" (Visual Studio avec le workload C++ doit déjà être installé), ou copie les fichiers de\n")
fmt.Printf(" \\extras\\visual_studio_integration\\MSBuildExtensions vers\n")
- fmt.Printf(" \\MSBuild\\Microsoft\\VC\\\\BuildCustomizations, puis relance %s.\n", bold("ajean llamacpp install"))
+ fmt.Printf(" \\MSBuild\\Microsoft\\VC\\\\BuildCustomizations, puis relance %s.\n", bold("loki llamacpp install"))
}
// Linux/CUDA : nvcc trouvé mais en-têtes/cudart introuvables = le toolkit
// complet n'est pas installé (seul le paquet nvcc l'est). On passe déjà
@@ -245,15 +245,15 @@ func hintMissingBuildDep(p buildPlan, cfgLog string) {
// c'est qu'il manque pour de bon.
if p.backend == "cuda" && strings.Contains(log, "CUDA Toolkit not found") {
fmt.Printf("\n%s nvcc est présent mais le CUDA Toolkit complet (en-têtes + cudart) est introuvable.\n", yellow("[dépendance]"))
- fmt.Printf(" Installe le toolkit NVIDIA officiel (il se pose dans /usr/local/cuda), puis relance %s.\n", bold("ajean llamacpp install"))
+ fmt.Printf(" Installe le toolkit NVIDIA officiel (il se pose dans /usr/local/cuda), puis relance %s.\n", bold("loki llamacpp install"))
}
if p.backend == "vulkan" && strings.Contains(log, "SPIRV-Headers") {
fmt.Printf("\n%s dépendance manquante pour le backend %s : les en-têtes SPIR-V (paquet « SPIRV-Headers ») sont introuvables.\n",
yellow("[dépendance]"), green("Vulkan"))
if cmd := pkgInstallHint("spirv-headers"); cmd != "" {
- fmt.Printf(" installe-les puis relance %s : %s\n", bold("ajean llamacpp install"), bold(cmd))
+ fmt.Printf(" installe-les puis relance %s : %s\n", bold("loki llamacpp install"), bold(cmd))
} else {
- fmt.Printf(" installe le paquet de développement « SPIRV-Headers » de ta distribution, puis relance %s.\n", bold("ajean llamacpp install"))
+ fmt.Printf(" installe le paquet de développement « SPIRV-Headers » de ta distribution, puis relance %s.\n", bold("loki llamacpp install"))
}
}
}
diff --git a/internal/ajean/backend_build_cuda_test.go b/internal/loki/backend_build_cuda_test.go
similarity index 99%
rename from internal/ajean/backend_build_cuda_test.go
rename to internal/loki/backend_build_cuda_test.go
index fc200be..4222b47 100644
--- a/internal/ajean/backend_build_cuda_test.go
+++ b/internal/loki/backend_build_cuda_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"errors"
diff --git a/internal/ajean/backend_buildfilter_test.go b/internal/loki/backend_buildfilter_test.go
similarity index 94%
rename from internal/ajean/backend_buildfilter_test.go
rename to internal/loki/backend_buildfilter_test.go
index 491f147..e1ac352 100644
--- a/internal/ajean/backend_buildfilter_test.go
+++ b/internal/loki/backend_buildfilter_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import "testing"
@@ -12,7 +12,7 @@ func TestCompiledFile(t *testing.T) {
`[123/456] Building CUDA object ggml/src/ggml-cuda/CMakeFiles/ggml-cuda.dir/acc.cu.o`: "acc.cu",
// La ligne de commande nvcc géante ne doit PAS être prise pour un fichier.
` C:\...\nvcc.exe -x cu ... -o ggml-cuda.dir\Release\acc.obj "C:\...\acc.cu"`: "",
- `Building Custom Rule C:/ProgramData/ajean/...`: "",
+ `Building Custom Rule C:/ProgramData/loki/...`: "",
`-- UI: running npm install`: "",
}
for in, want := range cases {
diff --git a/internal/ajean/backend_catalog.go b/internal/loki/backend_catalog.go
similarity index 96%
rename from internal/ajean/backend_catalog.go
rename to internal/loki/backend_catalog.go
index 380bae4..1d123a1 100644
--- a/internal/ajean/backend_catalog.go
+++ b/internal/loki/backend_catalog.go
@@ -1,11 +1,11 @@
-package ajean
+package loki
// backend_catalog.go — récupère la liste de modèles curatée servie par ajean.link et la
// combine avec les infos matérielles locales, pour que l'écran d'accueil (à
// venir) propose « en un clic » un modèle adapté à la machine.
//
// La liste vit sur https://ajean.link/models.json (éditée par l'opérateur, voir
-// ajean-relay/models_catalog.go). Si le réseau échoue, on retombe sur un
+// loki-relay/models_catalog.go). Si le réseau échoue, on retombe sur un
// catalogue minimal embarqué (fallbackCatalogJSON) pour ne jamais bloquer.
import (
diff --git a/internal/ajean/backend_config.go b/internal/loki/backend_config.go
similarity index 92%
rename from internal/ajean/backend_config.go
rename to internal/loki/backend_config.go
index a08f83f..ab22b72 100644
--- a/internal/ajean/backend_config.go
+++ b/internal/loki/backend_config.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -18,7 +18,7 @@ import (
func readAPIKey() string { return getStr(bkState, "api_key") }
// authHeader sets the Authorization: Bearer header on req when an API key is
-// configured, so AJEAN's own internal calls (chat/web/bench/test) authenticate
+// configured, so Loki's own internal calls (chat/web/bench/test) authenticate
// against a protected llama-server. No-op when no key is set.
func authHeader(req *http.Request) {
if k := readAPIKey(); k != "" {
@@ -30,7 +30,7 @@ func authHeader(req *http.Request) {
func genAPIKey() string {
buf := make([]byte, 24)
_, _ = rand.Read(buf)
- return "sk-ajean-" + hex.EncodeToString(buf)
+ return "sk-loki-" + hex.EncodeToString(buf)
}
// writeAPIKey enregistre (key != "") ou supprime (key == "") la clé Bearer.
@@ -41,7 +41,7 @@ func writeAPIKey(key string) error {
return putStr(bkState, "api_key", key)
}
-// maskAPIKey renders a key for display: keep the "sk-ajean-" prefix and last 4
+// maskAPIKey renders a key for display: keep the "sk-loki-" prefix and last 4
// chars, elide the middle. Empty in → empty out.
func maskAPIKey(k string) string {
if k == "" {
@@ -57,9 +57,9 @@ func maskAPIKey(k string) string {
// internet, le serveur exige alors « Authorization: Bearer » à chaque
// appel.
//
-// ajean set-api-key définit la clé
-// ajean set-api-key génère une clé aléatoire
-// ajean set-api-key "" supprime la protection
+// loki set-api-key définit la clé
+// loki set-api-key génère une clé aléatoire
+// loki set-api-key "" supprime la protection
func cmdSetAPIKey(args []string) error {
var key string
switch {
@@ -83,7 +83,7 @@ func cmdSetAPIKey(args []string) error {
fmt.Print(dim("[info] redémarrer le service pour appliquer ? [Y/n] "))
sc := bufio.NewScanner(os.Stdin)
if sc.Scan() && strings.HasPrefix(strings.ToLower(strings.TrimSpace(sc.Text())), "n") {
- fmt.Println(dim("[info] pense à lancer 'ajean restart'"))
+ fmt.Println(dim("[info] pense à lancer 'loki restart'"))
return nil
}
return serviceAction("restart")
@@ -105,7 +105,7 @@ func WriteConfig(m map[string]string) error { return replaceKV(bkConfig, m) }
// eux : une paire ouvrante/fermante du même caractère.
//
// Un simple Trim(v, `"`) mangeait le guillemet d'un argument interne — par
-// exemple EXTRA_ARGS=--chat-template-file "/etc/ajean/tpl.jinja" perdait son
+// exemple EXTRA_ARGS=--chat-template-file "/etc/loki/tpl.jinja" perdait son
// guillemet final et repartait déséquilibré dans le preset (issue #17 : contenu
// tronqué / guillemets non appariés à la création d'un preset).
func unquoteValue(v string) string {
diff --git a/internal/ajean/backend_config_quote_test.go b/internal/loki/backend_config_quote_test.go
similarity index 91%
rename from internal/ajean/backend_config_quote_test.go
rename to internal/loki/backend_config_quote_test.go
index 6f23227..00840ab 100644
--- a/internal/ajean/backend_config_quote_test.go
+++ b/internal/loki/backend_config_quote_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"reflect"
@@ -10,7 +10,7 @@ import (
// dans le preset (issue #17).
func TestParseEnvKeepsInnerQuotes(t *testing.T) {
cases := map[string]string{
- `EXTRA_ARGS=--jinja --chat-template-file "/etc/ajean/tpl.jinja"`: `--jinja --chat-template-file "/etc/ajean/tpl.jinja"`,
+ `EXTRA_ARGS=--jinja --chat-template-file "/etc/loki/tpl.jinja"`: `--jinja --chat-template-file "/etc/loki/tpl.jinja"`,
`EXTRA_ARGS="--jinja --flash-attn"`: `--jinja --flash-attn`,
`MODEL='/mnt/d/x.gguf'`: `/mnt/d/x.gguf`,
`MODEL=/mnt/d/x.gguf`: `/mnt/d/x.gguf`,
@@ -31,7 +31,7 @@ func TestParseEnvKeepsInnerQuotes(t *testing.T) {
func TestFormatEnvRoundTrip(t *testing.T) {
in := map[string]string{
"MODEL": "/mnt/mes modèles/x.gguf",
- "EXTRA_ARGS": `--jinja --chat-template-file "/etc/ajean/tpl.jinja"`,
+ "EXTRA_ARGS": `--jinja --chat-template-file "/etc/loki/tpl.jinja"`,
"CTX": "32768",
}
out := parseEnv(formatEnv(in))
diff --git a/internal/ajean/backend_gpu.go b/internal/loki/backend_gpu.go
similarity index 89%
rename from internal/ajean/backend_gpu.go
rename to internal/loki/backend_gpu.go
index b041582..8c0d3d7 100644
--- a/internal/ajean/backend_gpu.go
+++ b/internal/loki/backend_gpu.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -11,10 +11,10 @@ import (
// backend_gpu.go — sélection du/des GPU utilisés par llama-server.
//
-// ajean gpu liste les GPU et montre la sélection courante
-// ajean gpu 1 n'utilise que le GPU d'index 1
-// ajean gpu 0 1 utilise les GPU 0 et 1
-// ajean gpu all réinitialise (tous les GPU visibles)
+// loki gpu liste les GPU et montre la sélection courante
+// loki gpu 1 n'utilise que le GPU d'index 1
+// loki gpu 0 1 utilise les GPU 0 et 1
+// loki gpu all réinitialise (tous les GPU visibles)
//
// La sélection est stockée dans config.env sous CUDA_VISIBLE_DEVICES ; backend_serve.go
// l'exporte (avec CUDA_DEVICE_ORDER=PCI_BUS_ID pour que les index correspondent
@@ -53,7 +53,7 @@ func cmdGPU(args []string) error {
return fmt.Errorf("index GPU invalide: %q (attendu un nombre)", tok)
}
if n < 0 || n >= len(gpus) {
- return fmt.Errorf("index GPU %d hors limites (0..%d) — voir « ajean gpu »", n, len(gpus)-1)
+ return fmt.Errorf("index GPU %d hors limites (0..%d) — voir « loki gpu »", n, len(gpus)-1)
}
idx = append(idx, strconv.Itoa(n))
}
@@ -102,7 +102,7 @@ func gpuList() error {
} else {
fmt.Printf(" Sélection : %s (CUDA_VISIBLE_DEVICES=%s)\n", bold(sel), sel)
}
- fmt.Printf(" %s ajean gpu pour choisir, ajean gpu all pour réinitialiser\n", dim("→"))
+ fmt.Printf(" %s loki gpu pour choisir, loki gpu all pour réinitialiser\n", dim("→"))
return nil
}
@@ -120,7 +120,7 @@ func gpuSet(value string) error {
fmt.Print(dim("[info] redémarrer le service pour appliquer ? [Y/n] "))
sc := bufio.NewScanner(os.Stdin)
if sc.Scan() && strings.HasPrefix(strings.ToLower(strings.TrimSpace(sc.Text())), "n") {
- fmt.Println(dim("[info] pense à lancer 'ajean restart'"))
+ fmt.Println(dim("[info] pense à lancer 'loki restart'"))
return nil
}
return serviceAction("restart")
diff --git a/internal/ajean/backend_llamacpp.go b/internal/loki/backend_llamacpp.go
similarity index 96%
rename from internal/ajean/backend_llamacpp.go
rename to internal/loki/backend_llamacpp.go
index 25834fc..fd62cab 100644
--- a/internal/ajean/backend_llamacpp.go
+++ b/internal/loki/backend_llamacpp.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"fmt"
@@ -9,12 +9,12 @@ import (
// backend_llamacpp.go — gestion du backend llama.cpp (clone, build, mise à jour).
//
-// `ajean llamacpp install` installe un build neuf, détecte automatiquement
+// `loki llamacpp install` installe un build neuf, détecte automatiquement
// l'accélérateur (CUDA / ROCm / Metal / CPU) et la
// compute capability du GPU, puis pointe BIN dessus.
-// `ajean llamacpp update` met à jour le dépôt existant (git pull) et recompile
+// `loki llamacpp update` met à jour le dépôt existant (git pull) et recompile
// avec la bonne config, sans intervention.
-// `ajean llamacpp status` montre le commit courant, le backend détecté et le
+// `loki llamacpp status` montre le commit courant, le backend détecté et le
// retard éventuel sur origin.
const llamacppRepoURL = "https://github.com/ggml-org/llama.cpp.git"
@@ -55,7 +55,7 @@ func cmdLlamacpp(args []string) error {
if err := SetConfigKey("BIN", bin); err != nil {
return fmt.Errorf("binaires installés mais échec d'écriture de BIN : %w", err)
}
- fmt.Printf("%s BIN mis à jour — %s pour appliquer\n", green("✓"), bold("ajean restart"))
+ fmt.Printf("%s BIN mis à jour — %s pour appliquer\n", green("✓"), bold("loki restart"))
return nil
default:
return fmt.Errorf("sous-commande inconnue: %s (install | update | prebuilt | status)", sub)
@@ -68,7 +68,7 @@ func cmdLlamacpp(args []string) error {
// llamacppRepoDir resolves the llama.cpp checkout: derived from config BIN when
// possible (so `update` targets whatever build the service actually runs),
-// otherwise the default under $AJEAN_HOME/backends/llama.cpp.
+// otherwise the default under $LOKI_HOME/backends/llama.cpp.
func llamacppRepoDir() string {
if bin := ReadConfig()["BIN"]; bin != "" {
if real, err := filepath.EvalSymlinks(bin); err == nil {
@@ -161,7 +161,7 @@ func llamacppInstall(args []string) error {
if isDir(filepath.Join(repo, ".git")) {
if !force {
fmt.Printf("%s dépôt déjà présent dans %s\n", yellow("[info]"), repo)
- fmt.Printf(" → %s pour le mettre à jour, ou --force pour repartir de zéro\n", bold("ajean llamacpp update"))
+ fmt.Printf(" → %s pour le mettre à jour, ou --force pour repartir de zéro\n", bold("loki llamacpp update"))
return nil
}
fmt.Printf("%s --force : suppression de %s\n", yellow("[info]"), repo)
@@ -208,7 +208,7 @@ func llamacppInstall(args []string) error {
}
fmt.Printf("%s BIN mis à jour\n", green("✓"))
fmt.Printf("\nProchaines étapes :\n 1. renseigne MODEL : %s\n 2. démarre : %s\n",
- bold("ajean edit"), bold("ajean restart"))
+ bold("loki edit"), bold("loki restart"))
return nil
}
@@ -246,7 +246,7 @@ func llamacppUpdate(args []string) error {
repo := llamacppRepoDir()
if !isDir(filepath.Join(repo, ".git")) {
- return fmt.Errorf("aucun dépôt llama.cpp trouvé (%s).\n → lance d'abord %s", repo, bold("ajean llamacpp install"))
+ return fmt.Errorf("aucun dépôt llama.cpp trouvé (%s).\n → lance d'abord %s", repo, bold("loki llamacpp install"))
}
fmt.Printf("%s dépôt : %s\n", cyan("▶"), repo)
@@ -317,14 +317,14 @@ func llamacppUpdate(args []string) error {
fmt.Printf("\n%s mis à jour : %s → %s\n", green("✓"), oldCommit, newCommit)
if noRestart {
- fmt.Printf("%s --no-restart : pense à lancer %s\n", dim("[info]"), bold("ajean restart"))
+ fmt.Printf("%s --no-restart : pense à lancer %s\n", dim("[info]"), bold("loki restart"))
return nil
}
if svcWasUp {
fmt.Printf("%s redémarrage du service…\n", cyan("▶"))
return serviceAction("start")
}
- fmt.Printf("%s service non démarré auparavant — lance %s quand tu veux\n", dim("[info]"), bold("ajean start"))
+ fmt.Printf("%s service non démarré auparavant — lance %s quand tu veux\n", dim("[info]"), bold("loki start"))
return nil
}
@@ -337,7 +337,7 @@ func llamacppStatus(args []string) error {
fmt.Printf("%s\n", bold("llama.cpp"))
fmt.Printf(" dépôt : %s\n", repo)
if !isDir(filepath.Join(repo, ".git")) {
- fmt.Printf(" %s pas encore installé — %s\n", yellow("état"), bold("ajean llamacpp install"))
+ fmt.Printf(" %s pas encore installé — %s\n", yellow("état"), bold("loki llamacpp install"))
return nil
}
commit := gitOutput(repo, "log", "-1", "--format=%h %ci %s")
@@ -354,7 +354,7 @@ func llamacppStatus(args []string) error {
// Retard sur origin (best-effort, sans fetch réseau).
if branch != "" && branch != "HEAD" {
if behind := gitOutput(repo, "rev-list", "--count", "HEAD..origin/"+branch); behind != "" && behind != "0" {
- fmt.Printf(" maj : %s commit(s) de retard sur origin/%s — %s\n", yellow(behind), branch, bold("ajean llamacpp update"))
+ fmt.Printf(" maj : %s commit(s) de retard sur origin/%s — %s\n", yellow(behind), branch, bold("loki llamacpp update"))
}
}
diff --git a/internal/ajean/backend_model_dirs.go b/internal/loki/backend_model_dirs.go
similarity index 94%
rename from internal/ajean/backend_model_dirs.go
rename to internal/loki/backend_model_dirs.go
index b77611d..976ea2b 100644
--- a/internal/ajean/backend_model_dirs.go
+++ b/internal/loki/backend_model_dirs.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -10,13 +10,13 @@ import (
"strings"
)
-// Les .gguf vivent dans $AJEAN_HOME/models, mais un modèle de 40 Go tient
+// Les .gguf vivent dans $LOKI_HOME/models, mais un modèle de 40 Go tient
// rarement sur le disque système : on autorise des dossiers supplémentaires
// (disque dur externe, second SSD, montage réseau…). La liste est persistée en
// base — PAS dans la configuration, que le changement de preset remplace — et
-// peut aussi venir de $AJEAN_MODEL_DIRS.
+// peut aussi venir de $LOKI_MODEL_DIRS.
-// modelDirsListSep sépare les dossiers dans $AJEAN_MODEL_DIRS (':' sous Unix,
+// modelDirsListSep sépare les dossiers dans $LOKI_MODEL_DIRS (':' sous Unix,
// ';' sous Windows, comme le PATH).
func modelDirsListSep() string {
if runtime.GOOS == "windows" {
@@ -30,7 +30,7 @@ func modelDirsListSep() string {
func extraModelDirs() []string {
var raw []string
getJSON(bkState, "model_dirs", &raw)
- if v := os.Getenv("AJEAN_MODEL_DIRS"); v != "" {
+ if v := os.Getenv("LOKI_MODEL_DIRS"); v != "" {
raw = append(raw, strings.Split(v, modelDirsListSep())...)
}
return dedupDirs(raw, modelsDir())
@@ -92,7 +92,7 @@ func pathWithin(dir, p string) bool {
// baseName renvoie le nom de fichier d'un chemin en acceptant les DEUX
// séparateurs : un config.env écrit sous Windows (C:\models\x.gguf) peut être
-// relu par un ajean Linux, où filepath.Base ne coupe pas sur '\'.
+// relu par un loki Linux, où filepath.Base ne coupe pas sur '\'.
func baseName(p string) string {
p = strings.TrimRight(strings.TrimSpace(p), `/\`)
if i := strings.LastIndexAny(p, `/\`); i >= 0 {
@@ -128,7 +128,7 @@ func resolveServeModelPath(name string) (string, error) {
// absolu) en chemin absolu vers un .gguf. Un chemin absolu n'est accepté que
// s'il est dans un des dossiers déclarés — sinon l'UI web deviendrait un moyen
// de lire/supprimer n'importe quel fichier de la machine. Un simple nom de
-// fichier est cherché dans chaque dossier, AJEAN_HOME en premier.
+// fichier est cherché dans chaque dossier, LOKI_HOME en premier.
func resolveModelPath(name string) (string, error) {
s := strings.TrimSpace(strings.Trim(strings.TrimSpace(name), `"`))
if s == "" {
@@ -218,7 +218,7 @@ func resolveDownloadDir(dir string) (string, error) {
return "", fmt.Errorf("dossier non autorisé : %s — ajoute-le dans « Dossiers de modèles »", abs)
}
-// handleModelDirs : GET liste les dossiers de modèles (AJEAN_HOME + ajoutés),
+// handleModelDirs : GET liste les dossiers de modèles (LOKI_HOME + ajoutés),
// POST {path, action:"add"|"remove"} en ajoute ou en retire un.
func handleModelDirs(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
@@ -269,5 +269,5 @@ func handleModelDirs(w http.ResponseWriter, r *http.Request) {
n := modelDirCount(d)
out = append(out, map[string]any{"path": d, "home": false, "count": n, "exists": n >= 0, "free": diskFree(d)})
}
- sendJSON(w, 200, map[string]any{"ok": true, "dirs": out, "env": os.Getenv("AJEAN_MODEL_DIRS") != ""})
+ sendJSON(w, 200, map[string]any{"ok": true, "dirs": out, "env": os.Getenv("LOKI_MODEL_DIRS") != ""})
}
diff --git a/internal/ajean/backend_model_dirs_test.go b/internal/loki/backend_model_dirs_test.go
similarity index 96%
rename from internal/ajean/backend_model_dirs_test.go
rename to internal/loki/backend_model_dirs_test.go
index fbad3f1..4ca6def 100644
--- a/internal/ajean/backend_model_dirs_test.go
+++ b/internal/loki/backend_model_dirs_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -11,7 +11,7 @@ import (
func TestResolveModelPathExtraDir(t *testing.T) {
testHome(t)
ext := t.TempDir()
- t.Setenv("AJEAN_MODEL_DIRS", "")
+ t.Setenv("LOKI_MODEL_DIRS", "")
extModel := filepath.Join(ext, "gros.gguf")
if err := os.WriteFile(extModel, []byte("x"), 0o644); err != nil {
@@ -42,7 +42,7 @@ func TestResolveModelPathExtraDir(t *testing.T) {
func TestResolveModelPathHomeFirst(t *testing.T) {
testHome(t)
ext := t.TempDir()
- t.Setenv("AJEAN_MODEL_DIRS", ext)
+ t.Setenv("LOKI_MODEL_DIRS", ext)
if err := os.MkdirAll(modelsDir(), 0o755); err != nil {
t.Fatal(err)
}
diff --git a/internal/ajean/backend_models.go b/internal/loki/backend_models.go
similarity index 98%
rename from internal/ajean/backend_models.go
rename to internal/loki/backend_models.go
index 39fae47..d59dd7d 100644
--- a/internal/ajean/backend_models.go
+++ b/internal/loki/backend_models.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
@@ -87,7 +87,7 @@ func detectQuant(content string) string {
}
// downloadDestPath resolves the destination of a downloaded model : le dossier
-// demandé (vide = AJEAN_HOME) parmi les dossiers de modèles déclarés, en
+// demandé (vide = LOKI_HOME) parmi les dossiers de modèles déclarés, en
// refusant tout ce qui en sortirait (path traversal).
func downloadDestPath(name, dir string) (string, error) {
base := filepath.Base(strings.TrimSpace(name))
@@ -146,7 +146,7 @@ func deleteModelFile(name string) error {
return nil
}
-// handleModelDelete deletes a single .gguf from AJEAN_HOME.
+// handleModelDelete deletes a single .gguf from LOKI_HOME.
func handleModelDelete(w http.ResponseWriter, r *http.Request) {
var req struct {
Name string `json:"name"`
@@ -208,10 +208,10 @@ var dlClient = &http.Client{
}
// dlConns is the number of parallel range requests used per download.
-// Overridable with AJEAN_DL_CONNS (1 disables parallelism).
+// Overridable with LOKI_DL_CONNS (1 disables parallelism).
func dlConns() int {
n := 8
- if v := os.Getenv("AJEAN_DL_CONNS"); v != "" {
+ if v := os.Getenv("LOKI_DL_CONNS"); v != "" {
if p, err := strconv.Atoi(v); err == nil && p > 0 {
n = p
}
@@ -256,7 +256,7 @@ func normalizeHFURL(raw string) (string, string, error) {
}
// handleModelDownload kicks off a background download of a .gguf from a URL
-// (typically Hugging Face) into AJEAN_HOME. Progress is polled via
+// (typically Hugging Face) into LOKI_HOME. Progress is polled via
// /api/models/download/status.
func handleModelDownload(w http.ResponseWriter, r *http.Request) {
var req struct {
@@ -412,7 +412,7 @@ func dlRequest(ctx context.Context, dlURL, rng string) (*http.Request, error) {
if k := os.Getenv("HF_TOKEN"); k != "" {
req.Header.Set("Authorization", "Bearer "+k)
}
- req.Header.Set("User-Agent", "ajean/"+Version)
+ req.Header.Set("User-Agent", "loki/"+Version)
req.Header.Set("Accept-Encoding", "identity") // never gzip a .gguf: it breaks ranges
if rng != "" {
req.Header.Set("Range", rng)
diff --git a/internal/ajean/backend_models_dest_test.go b/internal/loki/backend_models_dest_test.go
similarity index 96%
rename from internal/ajean/backend_models_dest_test.go
rename to internal/loki/backend_models_dest_test.go
index ff45b90..f25d7f5 100644
--- a/internal/ajean/backend_models_dest_test.go
+++ b/internal/loki/backend_models_dest_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
// Destination des téléchargements : le dossier doit être choisi parmi les
// dossiers de modèles déclarés (sinon l'UI web écrirait n'importe où), et
@@ -14,7 +14,7 @@ import (
func TestDownloadDestPathDir(t *testing.T) {
testHome(t)
extra := t.TempDir()
- t.Setenv("AJEAN_MODEL_DIRS", extra)
+ t.Setenv("LOKI_MODEL_DIRS", extra)
// Dossier vide → models/.
got, err := downloadDestPath("m.gguf", "")
@@ -77,7 +77,7 @@ func TestDiskFreeAndCheck(t *testing.T) {
func TestCleanStalePartFilesExtraDirs(t *testing.T) {
testHome(t)
extra := t.TempDir()
- t.Setenv("AJEAN_MODEL_DIRS", extra)
+ t.Setenv("LOKI_MODEL_DIRS", extra)
stale := filepath.Join(extra, "coupe.gguf.part")
if err := os.WriteFile(stale, []byte("x"), 0o644); err != nil {
t.Fatal(err)
diff --git a/internal/ajean/backend_models_dl_test.go b/internal/loki/backend_models_dl_test.go
similarity index 98%
rename from internal/ajean/backend_models_dl_test.go
rename to internal/loki/backend_models_dl_test.go
index b92e808..11fc8e3 100644
--- a/internal/ajean/backend_models_dl_test.go
+++ b/internal/loki/backend_models_dl_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -27,7 +27,7 @@ func serveBlob(data []byte, ranges bool) *httptest.Server {
// Un téléchargement annulé ne doit laisser NI le .gguf final NI le .part.
func TestRunDownloadCancelLeavesNothing(t *testing.T) {
- t.Setenv("AJEAN_DL_CONNS", "4")
+ t.Setenv("LOKI_DL_CONNS", "4")
release := make(chan struct{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Range") == "bytes=0-0" { // sonde : réponse immédiate
@@ -99,7 +99,7 @@ func TestCleanStalePartFiles(t *testing.T) {
func TestRunDownloadParallelAndFallback(t *testing.T) {
data := make([]byte, 48<<20) // > 3×dlMinChunk so the split actually kicks in
rand.New(rand.NewSource(1)).Read(data)
- t.Setenv("AJEAN_DL_CONNS", "4")
+ t.Setenv("LOKI_DL_CONNS", "4")
for _, ranges := range []bool{true, false} {
srv := serveBlob(data, ranges)
diff --git a/internal/ajean/backend_models_shards.go b/internal/loki/backend_models_shards.go
similarity index 97%
rename from internal/ajean/backend_models_shards.go
rename to internal/loki/backend_models_shards.go
index b9fce69..d33a8e4 100644
--- a/internal/ajean/backend_models_shards.go
+++ b/internal/loki/backend_models_shards.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"fmt"
@@ -15,7 +15,7 @@ import (
// llama-server n'a besoin QUE de la première : il ouvre les suivantes tout seul,
// à condition qu'elles soient dans le même dossier.
//
-// Tout le reste d'AJEAN traitait chaque tranche comme un modèle indépendant :
+// Tout le reste d'Loki traitait chaque tranche comme un modèle indépendant :
// le lien collé ne rapatriait qu'un fichier sur trois (le moteur mourait sur
// « tensor not found »), la liste du sélecteur montrait trois entrées dont deux
// ne démarrent pas, et supprimer « le modèle » laissait les deux autres tranches
diff --git a/internal/ajean/backend_models_shards_test.go b/internal/loki/backend_models_shards_test.go
similarity index 99%
rename from internal/ajean/backend_models_shards_test.go
rename to internal/loki/backend_models_shards_test.go
index c7e6477..f9f10bc 100644
--- a/internal/ajean/backend_models_shards_test.go
+++ b/internal/loki/backend_models_shards_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
diff --git a/internal/ajean/backend_prebuilt.go b/internal/loki/backend_prebuilt.go
similarity index 99%
rename from internal/ajean/backend_prebuilt.go
rename to internal/loki/backend_prebuilt.go
index 3a4e415..94654d6 100644
--- a/internal/ajean/backend_prebuilt.go
+++ b/internal/loki/backend_prebuilt.go
@@ -8,7 +8,7 @@
// CUDA officiel pour Linux (on retombe sur Vulkan — la compilation locale
// reste la voie CUDA sous Linux). La compilation locale reste indispensable
// pour les forks (ex. PrismML).
-package ajean
+package loki
import (
"archive/tar"
@@ -375,7 +375,7 @@ func prebuiltInstall(logf, phasef func(string)) (string, error) {
logf(fmt.Sprintf("release %s — variant retenu : %s", tag, label))
// Réinstaller à l'identique est inutile SAUF si l'extraction date d'une
- // version de AJEAN qui ignorait les liens des archives (backend installé mais
+ // version de Loki qui ignorait les liens des archives (backend installé mais
// bibliothèques introuvables au lancement) : le marqueur de format force alors
// une ré-extraction propre au lieu d'un « déjà à jour » trompeur.
if cur := prebuiltServerBin(); curTag == tag && prebuiltVersionFormat() == prebuiltFormat && cur != "" {
diff --git a/internal/ajean/backend_prebuilt_reco_test.go b/internal/loki/backend_prebuilt_reco_test.go
similarity index 98%
rename from internal/ajean/backend_prebuilt_reco_test.go
rename to internal/loki/backend_prebuilt_reco_test.go
index 497b132..6f20c6d 100644
--- a/internal/ajean/backend_prebuilt_reco_test.go
+++ b/internal/loki/backend_prebuilt_reco_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"runtime"
diff --git a/internal/ajean/backend_prebuilt_test.go b/internal/loki/backend_prebuilt_test.go
similarity index 99%
rename from internal/ajean/backend_prebuilt_test.go
rename to internal/loki/backend_prebuilt_test.go
index fb4a50e..620512f 100644
--- a/internal/ajean/backend_prebuilt_test.go
+++ b/internal/loki/backend_prebuilt_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"archive/tar"
diff --git a/internal/ajean/backend_presets.go b/internal/loki/backend_presets.go
similarity index 99%
rename from internal/ajean/backend_presets.go
rename to internal/loki/backend_presets.go
index 55d6f41..797b5f0 100644
--- a/internal/ajean/backend_presets.go
+++ b/internal/loki/backend_presets.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -193,7 +193,7 @@ var preservedKeys = []string{"MEM_MODE", "CRAWL4AI_URL", "WEB_ENGINE", "CUDA_VIS
// softPreservedKeys : préservées SEULEMENT si le preset d'arrivée ne les définit
// pas lui-même. CUDA_VISIBLE_DEVICES est dans ce cas : c'est d'ordinaire un
-// choix de machine (`ajean gpu`) qui doit survivre aux bascules, MAIS un preset
+// choix de machine (`loki gpu`) qui doit survivre aux bascules, MAIS un preset
// a le droit de l'imposer — « QWEN 3.6 27B FABLE 2 GPU » réclame `1,0` pour que
// son `--tensor-split 0.965,0.035` ait bien deux cartes en face. Le préserver
// inconditionnellement écrasait cette valeur par l'ancienne (mono-GPU) : le
diff --git a/internal/ajean/backend_presets_test.go b/internal/loki/backend_presets_test.go
similarity index 96%
rename from internal/ajean/backend_presets_test.go
rename to internal/loki/backend_presets_test.go
index 385bd3a..f05a5f0 100644
--- a/internal/ajean/backend_presets_test.go
+++ b/internal/loki/backend_presets_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -36,9 +36,9 @@ func TestPresetFingerprintIgnoresCosmetics(t *testing.T) {
// TestSwitchToPresetGardeLesReglagesMachine : basculer de preset ne doit pas
// effacer les réglages qui décrivent la MACHINE et non le modèle. Le cas vécu :
-// `ajean gpu 1` écrit CUDA_VISIBLE_DEVICES dans config.env, la bascule de preset
+// `loki gpu 1` écrit CUDA_VISIBLE_DEVICES dans config.env, la bascule de preset
// suivante l'écrasait, llama.cpp revoyait les deux cartes et réétalait le modèle
-// sur la petite — pendant que `ajean gpu` réaffichait « auto ».
+// sur la petite — pendant que `loki gpu` réaffichait « auto ».
func TestSwitchToPresetGardeLesReglagesMachine(t *testing.T) {
home := testHome(t)
write := func(p, body string) {
diff --git a/internal/ajean/backend_serve.go b/internal/loki/backend_serve.go
similarity index 91%
rename from internal/ajean/backend_serve.go
rename to internal/loki/backend_serve.go
index ebab289..b65f2ff 100644
--- a/internal/ajean/backend_serve.go
+++ b/internal/loki/backend_serve.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -41,15 +41,15 @@ func cmdServe(args []string) error {
cfg := ReadConfig()
bin := cfg["BIN"]
if bin == "" {
- return fmt.Errorf("BIN non défini — lance « ajean edit »")
+ return fmt.Errorf("BIN non défini — lance « loki edit »")
}
model := cfg["MODEL"]
if model == "" {
- return fmt.Errorf("MODEL non défini — lance « ajean edit »")
+ return fmt.Errorf("MODEL non défini — lance « loki edit »")
}
- // MODEL vaut soit un simple nom de fichier (le .gguf vit dans AJEAN_HOME ou
+ // MODEL vaut soit un simple nom de fichier (le .gguf vit dans LOKI_HOME ou
// dans un dossier déclaré — disque externe…), soit un chemin absolu. Sous
- // systemd/launchd le WorkingDirectory vaut AJEAN_HOME, donc le relatif tombait
+ // systemd/launchd le WorkingDirectory vaut LOKI_HOME, donc le relatif tombait
// juste ; lancé depuis une app de bureau, le répertoire courant est « / » et
// llama-server ne trouvait rien. On résout donc explicitement, quel que soit
// le contexte de lancement.
@@ -69,7 +69,7 @@ func cmdServe(args []string) error {
strings.Join(missing, ", "), filepath.Dir(model), len(shardFamily(filepath.Base(model))))
}
if !filepath.IsAbs(bin) {
- bin = filepath.Join(AjeanHome(), bin)
+ bin = filepath.Join(LokiHome(), bin)
}
// Le moteur précompilé s'installe dans un dossier versionné : un preset écrit
// avant une mise à jour pointe sur une release qui n'existe plus. On le fait
@@ -81,7 +81,7 @@ func cmdServe(args []string) error {
// Linux, PATH on Windows — handled inside execServer.
setLibraryPath(filepath.Dir(bin))
- // Sélection GPU (ajean gpu) : on filtre les devices visibles par llama-server.
+ // Sélection GPU (loki gpu) : on filtre les devices visibles par llama-server.
// CUDA_DEVICE_ORDER=PCI_BUS_ID garantit que les index correspondent à ceux
// affichés par nvidia-smi (sinon CUDA réordonne par "device le plus rapide").
if v := cfg["CUDA_VISIBLE_DEVICES"]; v != "" {
@@ -160,12 +160,12 @@ func cmdServe(args []string) error {
// Vieux moteur (ou fork) qui ne connaît pas le drapeau : le lui passer
// le ferait sortir en erreur au démarrage, donc boucler. On le dit et on
// continue sans — mieux vaut un modèle qui réfléchit qu'un moteur mort.
- fmt.Fprintf(os.Stderr, "[ajean serve] ce moteur ne connaît pas --reasoning : impossible de désactiver le raisonnement\n")
+ fmt.Fprintf(os.Stderr, "[loki serve] ce moteur ne connaît pas --reasoning : impossible de désactiver le raisonnement\n")
}
}
// API_KEY protège le serveur quand il est exposé sur internet : llama-server
// exige alors l'en-tête "Authorization: Bearer ". La clé est lue depuis
- // $AJEAN_HOME/.api_key en priorité (elle survit ainsi aux changements de preset
+ // $LOKI_HOME/.api_key en priorité (elle survit ainsi aux changements de preset
// qui réécrivent config.env), avec config.env comme repli rétro-compatible.
if k := readAPIKey(); k != "" {
llmArgs = append(llmArgs, "--api-key", k)
@@ -176,11 +176,11 @@ func cmdServe(args []string) error {
// together so a path with spaces stays one argument.
llmArgs = append(llmArgs, splitArgs(cfg["EXTRA_ARGS"])...)
- // Working dir = AJEAN_HOME so relative paths in EXTRA_ARGS (e.g. --mmproj
+ // Working dir = LOKI_HOME so relative paths in EXTRA_ARGS (e.g. --mmproj
// mmproj-F16.gguf) still resolve.
- _ = os.Chdir(AjeanHome())
+ _ = os.Chdir(LokiHome())
- fmt.Fprintf(os.Stderr, "[ajean serve] %s model=%s port=%s\n",
+ fmt.Fprintf(os.Stderr, "[loki serve] %s model=%s port=%s\n",
bin, filepath.Base(model), get("PORT", "8080"))
// Hand off to the llama-server process. On Unix this replaces the current
diff --git a/internal/ajean/chat_agent.go b/internal/loki/chat_agent.go
similarity index 95%
rename from internal/ajean/chat_agent.go
rename to internal/loki/chat_agent.go
index 4eb6aba..f67a003 100644
--- a/internal/ajean/chat_agent.go
+++ b/internal/loki/chat_agent.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import "fmt"
@@ -43,7 +43,7 @@ func cmdAgent(args []string) error {
fmt.Printf(" %s %s\n", bold(p.Name), p.Title)
}
default:
- return fmt.Errorf("usage: ajean agent [on|off|status]")
+ return fmt.Errorf("usage: loki agent [on|off|status]")
}
return nil
}
diff --git a/internal/ajean/chat_cmd.go b/internal/loki/chat_cmd.go
similarity index 93%
rename from internal/ajean/chat_cmd.go
rename to internal/loki/chat_cmd.go
index 653a852..f9417da 100644
--- a/internal/ajean/chat_cmd.go
+++ b/internal/loki/chat_cmd.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -12,14 +12,14 @@ import (
// First positional arg (if any) becomes the system prompt.
func cmdChat(args []string) error {
if !healthCheck() {
- return fmt.Errorf("serveur injoignable sur :%d — ajean start d'abord", LLMPort())
+ return fmt.Errorf("serveur injoignable sur :%d — loki start d'abord", LLMPort())
}
sysPrompt := strings.Join(args, " ")
msgs := []Message{}
if sysPrompt != "" {
msgs = append(msgs, Message{Role: "system", Content: sysPrompt})
}
- fmt.Printf("\n%s — /reset pour vider, /sys pour changer le system, /quit ou Ctrl-D pour sortir\n", cyan("ajean chat"))
+ fmt.Printf("\n%s — /reset pour vider, /sys pour changer le system, /quit ou Ctrl-D pour sortir\n", cyan("loki chat"))
if sysPrompt != "" {
fmt.Println(dim("system: " + sysPrompt))
}
@@ -65,7 +65,7 @@ func cmdChat(args []string) error {
inReason := false
var stats *StatsEvent
// Print the assistant prefix once; reasoning is shown inline with a tag.
- fmt.Print(cyan("ajean") + " > ")
+ fmt.Print(cyan("loki") + " > ")
caps := globalCaps()
// Compaction proactive (façon Hermes) : on résume les vieux tours quand
// l'historique dépasse le seuil, au lieu d'imposer un /reset.
@@ -120,7 +120,7 @@ func cmdChat(args []string) error {
fmt.Print("\n")
inReason = false
}
- fmt.Printf("\n%s %s : %s\n%s ", dim(icon+" "+verb), "", magenta(ev.ToolUsed.Label), cyan("ajean")+" >")
+ fmt.Printf("\n%s %s : %s\n%s ", dim(icon+" "+verb), "", magenta(ev.ToolUsed.Label), cyan("loki")+" >")
case ev.Reasoning != "":
if !inReason {
fmt.Print(magenta("[reasoning] ") + dim(""))
@@ -129,7 +129,7 @@ func cmdChat(args []string) error {
fmt.Print(dim(ev.Reasoning))
case ev.Content != "":
if inReason {
- fmt.Print("\n" + cyan("ajean") + " > ")
+ fmt.Print("\n" + cyan("loki") + " > ")
inReason = false
}
full.WriteString(ev.Content)
diff --git a/internal/ajean/chat_compact.go b/internal/loki/chat_compact.go
similarity index 99%
rename from internal/ajean/chat_compact.go
rename to internal/loki/chat_compact.go
index f5903a8..cd41c37 100644
--- a/internal/ajean/chat_compact.go
+++ b/internal/loki/chat_compact.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -159,7 +159,7 @@ func compactWouldTrigger(msgs []Message, knownTokens int) bool {
}
// logCompact trace UNE ligne par décision de compaction sur la sortie d'erreur
-// (donc dans `journalctl -u ajean-ui`). Sans ça, une compaction qui ne se
+// (donc dans `journalctl -u loki-ui`). Sans ça, une compaction qui ne se
// déclenche pas — ou qui se déclenche et n'enlève rien — est invisible : côté
// UI on ne voit qu'une jauge qui reste haute, sans savoir si le seuil n'a pas
// été atteint ou si la réduction a été refusée.
@@ -318,7 +318,7 @@ func compactMessages(ctx context.Context, msgs []Message, caps Caps) ([]Message,
// Garantie de réduction : on n'accepte la compaction que si elle enlève au
// moins ~20% du contexte estimé. Sinon (torse déjà maigre, résumé peu rentable)
- // on la refuse — sans ça, ajean « compactait » à presque chaque message sans
+ // on la refuse — sans ça, loki « compactait » à presque chaque message sans
// vraiment réduire, puis re-déclenchait aussitôt.
before, after := estimateTokens(msgs), estimateTokens(out)
if after > before*4/5 {
@@ -387,7 +387,7 @@ Strict rules: no preamble or conclusion, no verbatim or long quotes, no throwawa
Write the summary in the SAME language as the conversation.`
payload := map[string]any{
- "model": "ajean",
+ "model": "loki",
"messages": []Message{
{Role: "system", Content: sys},
{Role: "user", Content: transcript},
diff --git a/internal/ajean/chat_compact_test.go b/internal/loki/chat_compact_test.go
similarity index 99%
rename from internal/ajean/chat_compact_test.go
rename to internal/loki/chat_compact_test.go
index cd0ae37..2f263b6 100644
--- a/internal/ajean/chat_compact_test.go
+++ b/internal/loki/chat_compact_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"strings"
diff --git a/internal/ajean/chat_conversation.go b/internal/loki/chat_conversation.go
similarity index 99%
rename from internal/ajean/chat_conversation.go
rename to internal/loki/chat_conversation.go
index e5edd14..780c9fe 100644
--- a/internal/ajean/chat_conversation.go
+++ b/internal/loki/chat_conversation.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/chat_conversation_test.go b/internal/loki/chat_conversation_test.go
similarity index 99%
rename from internal/ajean/chat_conversation_test.go
rename to internal/loki/chat_conversation_test.go
index 214f416..5173852 100644
--- a/internal/ajean/chat_conversation_test.go
+++ b/internal/loki/chat_conversation_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/chat_diff.go b/internal/loki/chat_diff.go
similarity index 99%
rename from internal/ajean/chat_diff.go
rename to internal/loki/chat_diff.go
index bd355a0..c8b216d 100644
--- a/internal/ajean/chat_diff.go
+++ b/internal/loki/chat_diff.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
// chat_diff.go — petit diff ligne à ligne pour l'interface : quand l'IA modifie
// un fichier (outil `edit`) ou une page de mémoire (`mem_edit`, `mem_add`), on
diff --git a/internal/ajean/chat_export.go b/internal/loki/chat_export.go
similarity index 92%
rename from internal/ajean/chat_export.go
rename to internal/loki/chat_export.go
index 3c24789..af117a6 100644
--- a/internal/ajean/chat_export.go
+++ b/internal/loki/chat_export.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -13,10 +13,10 @@ import (
// chat_export.go — sortir la conversation de la base.
//
// Jusqu'à la 0.7, l'historique vivait dans un `conversation.json` qu'on pouvait
-// ouvrir, lire et copier pour archive. La 0.8 l'a rangé dans ajean.db (bbolt) :
+// ouvrir, lire et copier pour archive. La 0.8 l'a rangé dans loki.db (bbolt) :
// un fichier binaire, et qui porte de surcroît un verrou EXCLUSIF tant que le
// service tourne — donc impossible ne serait-ce que de le copier sans arrêter
-// AJEAN. Le fil de discussion et les raisonnements sont toujours là, mais plus
+// Loki. Le fil de discussion et les raisonnements sont toujours là, mais plus
// personne ne peut les récupérer. C'est une régression pour qui relisait ses
// échanges, et ce fichier la répare.
//
@@ -80,7 +80,7 @@ func exportOptsFromQuery(q map[string][]string) exportOpts {
// envoyée au modèle, `log` le journal d'affichage (celui qui porte les
// raisonnements, les outils et les vitesses).
type exportPayload struct {
- Version string `json:"ajean_version"`
+ Version string `json:"loki_version"`
ExportedAt string `json:"exported_at"`
CtxUsed int `json:"ctx_used"`
Messages []Message `json:"messages"`
@@ -199,7 +199,7 @@ func (c *Conversation) ExportMarkdown(o exportOpts) string {
}
var b strings.Builder
- fmt.Fprintf(&b, "# Conversation AJEAN\n\nExportée le %s par AJEAN %s.\n",
+ fmt.Fprintf(&b, "# Conversation Loki\n\nExportée le %s par Loki %s.\n",
time.Now().Format("02/01/2006 à 15:04"), Version)
// Un export tronqué ou allégé doit le DIRE : relu six mois plus tard, un fil
// sans ses raisonnements ne doit pas passer pour le fil complet.
@@ -208,12 +208,12 @@ func (c *Conversation) ExportMarkdown(o exportOpts) string {
}
// openBubble : une bulle assistant est ouverte (au moins un bout de réponse a
- // été écrit). Sert à ne poser l'en-tête « AJEAN » qu'une fois par tour, même
+ // été écrit). Sert à ne poser l'en-tête « Loki » qu'une fois par tour, même
// quand la réponse est entrecoupée d'appels d'outils.
openBubble := false
head := func() {
if !openBubble {
- b.WriteString("\n## AJEAN\n")
+ b.WriteString("\n## Loki\n")
openBubble = true
}
}
@@ -303,7 +303,7 @@ func mdInline(v any) string {
// exportFilename : nom proposé au téléchargement, horodaté pour que deux exports
// ne s'écrasent pas dans le dossier de téléchargements.
func exportFilename(ext string) string {
- return "ajean-conversation-" + time.Now().Format("2006-01-02-1504") + "." + ext
+ return "loki-conversation-" + time.Now().Format("2006-01-02-1504") + "." + ext
}
// exportBody rend la conversation selon les options. Renvoie le corps, son type
@@ -318,10 +318,10 @@ func exportBody(o exportOpts) ([]byte, string, string, error) {
// cmdExport écrit la conversation dans un fichier (ou sur la sortie standard).
//
-// ajean export → ajean-conversation-.md dans le dossier courant
-// ajean export --json → idem en JSON
-// ajean export mon-fil.md → nom de fichier imposé (l'extension choisit le format)
-// ajean export - → sur la sortie standard, pour enchaîner un tube
+// loki export → loki-conversation-.md dans le dossier courant
+// loki export --json → idem en JSON
+// loki export mon-fil.md → nom de fichier imposé (l'extension choisit le format)
+// loki export - → sur la sortie standard, pour enchaîner un tube
//
// Options de contenu, les mêmes que la fenêtre d'export de l'interface :
//
@@ -357,12 +357,12 @@ func cmdExport(args []string) error {
o.Turns = n
default:
if strings.HasPrefix(a, "-") && a != "-" {
- return fmt.Errorf("option inconnue : %s (voir « ajean help »)", a)
+ return fmt.Errorf("option inconnue : %s (voir « loki help »)", a)
}
out = a
}
}
- // Une extension explicite l'emporte sur le drapeau : `ajean export fil.json`
+ // Une extension explicite l'emporte sur le drapeau : `loki export fil.json`
// qui produirait du Markdown serait un piège.
if strings.HasSuffix(strings.ToLower(out), ".json") {
o.Format = "json"
diff --git a/internal/ajean/chat_export_test.go b/internal/loki/chat_export_test.go
similarity index 98%
rename from internal/ajean/chat_export_test.go
rename to internal/loki/chat_export_test.go
index 5a446ad..6b4699f 100644
--- a/internal/ajean/chat_export_test.go
+++ b/internal/loki/chat_export_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -37,7 +37,7 @@ func TestExportMarkdownPorteToutLeFil(t *testing.T) {
md := conv.ExportMarkdown(defaultExportOpts())
for _, want := range []string{
"## Vous", "combien font 2+2 ?",
- "## AJEAN",
+ "## Loki",
"Raisonnement", "addition simple",
"bash", "echo 4",
} {
@@ -45,9 +45,9 @@ func TestExportMarkdownPorteToutLeFil(t *testing.T) {
t.Errorf("Markdown exporté sans %q :\n%s", want, md)
}
}
- // L'en-tête AJEAN ne doit apparaître qu'UNE fois pour un tour, même entrecoupé
+ // L'en-tête Loki ne doit apparaître qu'UNE fois pour un tour, même entrecoupé
// d'un appel d'outil.
- if n := strings.Count(md, "\n## AJEAN\n"); n != 1 {
+ if n := strings.Count(md, "\n## Loki\n"); n != 1 {
t.Errorf("en-tête assistant répété %d fois", n)
}
}
diff --git a/internal/ajean/chat_internet.go b/internal/loki/chat_internet.go
similarity index 99%
rename from internal/ajean/chat_internet.go
rename to internal/loki/chat_internet.go
index 061d155..0f4b642 100644
--- a/internal/ajean/chat_internet.go
+++ b/internal/loki/chat_internet.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -18,7 +18,7 @@ import (
// Accès internet de l'IA — port Go de l'extension pi ~/.pi/agent/extensions/web.ts.
//
-// ajean parle à un serveur Crawl4AI (Chrome headless, endpoint /crawl) dont l'URL
+// loki parle à un serveur Crawl4AI (Chrome headless, endpoint /crawl) dont l'URL
// est configurée dans config.env (CRAWL4AI_URL). Quand le mode agent ET l'accès
// internet sont actifs ET que le serveur répond, l'IA dispose de 4 outils :
//
diff --git a/internal/ajean/chat_internet_tools.go b/internal/loki/chat_internet_tools.go
similarity index 93%
rename from internal/ajean/chat_internet_tools.go
rename to internal/loki/chat_internet_tools.go
index 438fd45..80719cb 100644
--- a/internal/ajean/chat_internet_tools.go
+++ b/internal/loki/chat_internet_tools.go
@@ -1,6 +1,6 @@
// chat_internet_tools.go — les 4 outils web exposés au modèle (web_search,
-// web_open, web_read, web_grep) + la sous-commande `ajean internet`.
-package ajean
+// web_open, web_read, web_grep) + la sous-commande `loki internet`.
+package loki
import (
"fmt"
@@ -281,7 +281,7 @@ func toolWebGrep(args map[string]any) string {
entry.url, len(matchIdx), pattern, capped, strings.Join(blocks, "\n\n---\n\n"))
}
-// ─── CLI : ajean internet [on|off|status|engine|url|key] ────────────────────
+// ─── CLI : loki internet [on|off|status|engine|url|key] ────────────────────
func cmdInternet(args []string) error {
sub := ""
@@ -292,7 +292,7 @@ func cmdInternet(args []string) error {
case "on":
// Le moteur intégré ne demande aucun réglage ; Crawl4AI exige un serveur.
if webEngine() == engineCrawl && crawl4aiURL() == "" {
- return fmt.Errorf("configure d'abord l'URL : ajean internet url (ou bascule sur le moteur intégré : ajean internet engine go)")
+ return fmt.Errorf("configure d'abord l'URL : loki internet url (ou bascule sur le moteur intégré : loki internet engine go)")
}
if err := setInternetEnabled(true); err != nil {
return err
@@ -305,7 +305,7 @@ func cmdInternet(args []string) error {
fmt.Println(green("[ok]") + " accès internet désactivé")
case "url":
if len(args) < 2 {
- return fmt.Errorf("usage: ajean internet url (ex: http://localhost:11235)")
+ return fmt.Errorf("usage: loki internet url (ex: http://localhost:11235)")
}
u := strings.TrimRight(strings.TrimSpace(args[1]), "/")
if err := SetConfigKey("CRAWL4AI_URL", u); err != nil {
@@ -317,7 +317,7 @@ func cmdInternet(args []string) error {
fmt.Printf("%s serveur Crawl4AI : %s\n", green("[ok]"), bold(u))
case "key":
if len(args) < 2 {
- return fmt.Errorf("usage: ajean internet key (vide pour l'enlever : ajean internet key \"\")")
+ return fmt.Errorf("usage: loki internet key (vide pour l'enlever : loki internet key \"\")")
}
k := strings.TrimSpace(args[1])
if err := writeCrawlKey(k); err != nil {
@@ -333,7 +333,7 @@ func cmdInternet(args []string) error {
}
case "engine":
if len(args) < 2 {
- return fmt.Errorf("usage: ajean internet engine ")
+ return fmt.Errorf("usage: loki internet engine ")
}
e := strings.ToLower(strings.TrimSpace(args[1]))
if err := setWebEngine(e); err != nil {
@@ -342,7 +342,7 @@ func cmdInternet(args []string) error {
if e == engineGo {
fmt.Println(green("[ok]") + " moteur intégré — aucune installation requise (pas de rendu JavaScript)")
} else {
- fmt.Println(green("[ok]") + " moteur Crawl4AI — configure le serveur : ajean internet url ")
+ fmt.Println(green("[ok]") + " moteur Crawl4AI — configure le serveur : loki internet url ")
}
case "", "status", "list":
state := dim("off")
@@ -358,7 +358,7 @@ func cmdInternet(args []string) error {
fmt.Printf(" moteur : %s\n", bold("crawl4ai"))
u := crawl4aiURL()
if u == "" {
- fmt.Printf(" serveur : %s — configure : ajean internet url \n", dim("(non configuré)"))
+ fmt.Printf(" serveur : %s — configure : loki internet url \n", dim("(non configuré)"))
return nil
}
reach := red("injoignable")
@@ -368,7 +368,7 @@ func cmdInternet(args []string) error {
fmt.Printf(" serveur : %s (%s)\n", bold(u), reach)
fmt.Printf(" outils : web_search, web_open, web_read, web_grep\n")
default:
- return fmt.Errorf("usage: ajean internet [on|off|status|engine |url |key ]")
+ return fmt.Errorf("usage: loki internet [on|off|status|engine |url |key ]")
}
return nil
}
diff --git a/internal/ajean/chat_memory.go b/internal/loki/chat_memory.go
similarity index 97%
rename from internal/ajean/chat_memory.go
rename to internal/loki/chat_memory.go
index c3e4cfe..a53d8ca 100644
--- a/internal/ajean/chat_memory.go
+++ b/internal/loki/chat_memory.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"errors"
@@ -10,7 +10,7 @@ import (
"strings"
)
-// La mémoire de ajean = des fichiers Markdown plats sous memory/.md.
+// La mémoire de loki = des fichiers Markdown plats sous memory/.md.
// L'IA y range ce qu'elle veut retenir entre les sessions (préférences,
// décisions, procédures, infos projet). Quatre outils : mem_search, mem_read,
// mem_add, mem_edit.
@@ -331,7 +331,7 @@ func setMemMode(m MemMode) error {
return SetConfigKey("MEM_MODE", string(m))
}
-// cmdMemory : ajean memory [off|ondemand|always|status]
+// cmdMemory : loki memory [off|ondemand|always|status]
func cmdMemory(args []string) error {
sub := ""
if len(args) > 0 {
@@ -362,7 +362,7 @@ func cmdMemory(args []string) error {
fmt.Printf(" %d page(s) sous %s\n", len(pages), memoryDir())
return nil
default:
- return fmt.Errorf("usage: ajean memory [off|ondemand|always|status]")
+ return fmt.Errorf("usage: loki memory [off|ondemand|always|status]")
}
m := memMode()
fmt.Printf("%s mémoire : %s — %s\n", green("[ok]"), bold(string(m)), label[m])
diff --git a/internal/ajean/chat_prompt_size_test.go b/internal/loki/chat_prompt_size_test.go
similarity index 99%
rename from internal/ajean/chat_prompt_size_test.go
rename to internal/loki/chat_prompt_size_test.go
index fde8d5c..24e5720 100644
--- a/internal/ajean/chat_prompt_size_test.go
+++ b/internal/loki/chat_prompt_size_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
diff --git a/internal/ajean/chat_stuck_test.go b/internal/loki/chat_stuck_test.go
similarity index 99%
rename from internal/ajean/chat_stuck_test.go
rename to internal/loki/chat_stuck_test.go
index 60fa5a0..47f6ef1 100644
--- a/internal/ajean/chat_stuck_test.go
+++ b/internal/loki/chat_stuck_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/chat_tools.go b/internal/loki/chat_tools.go
similarity index 97%
rename from internal/ajean/chat_tools.go
rename to internal/loki/chat_tools.go
index a2c9d42..c300d22 100644
--- a/internal/ajean/chat_tools.go
+++ b/internal/loki/chat_tools.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
@@ -41,9 +41,9 @@ func baseSystemPrompt(caps Caps) string {
// sur-raisonner les modèles à reasoning (Qwen3) : ils émettent leur
// puis le token de fin SANS appeler d'outil (~25-45 % de tours « morts »
// mesurés). Une version courte et directe ramène ça à 0 %. NE PAS regonfler.
- // « AJEAN » avec une majuscule : c'est un nom propre, et le modèle recopie
- // littéralement la casse d'ici quand il se présente (« je suis ajean »).
- b.WriteString("You are AJEAN, an expert assistant operating directly on this machine with real tools.")
+ // « Loki » avec une majuscule : c'est un nom propre, et le modèle recopie
+ // littéralement la casse d'ici quand il se présente (« je suis loki »).
+ b.WriteString("You are Loki, an expert assistant operating directly on this machine with real tools.")
if caps.Mem == MemAlways {
b.WriteString(" You evolve with every conversation: you actively maintain a persistent memory so nothing useful is lost between sessions.")
}
@@ -161,7 +161,7 @@ func machineSystemPrompt(caps Caps) string {
}
b.WriteString(".")
if cwd != "" {
- b.WriteString(" Relative paths in write/edit/bash resolve inside this working folder — put scratch files there. Write outside it ONLY with an absolute path the user explicitly asked for; never scatter files into the folder ajean was launched from.")
+ b.WriteString(" Relative paths in write/edit/bash resolve inside this working folder — put scratch files there. Write outside it ONLY with an absolute path the user explicitly asked for; never scatter files into the folder loki was launched from.")
}
return b.String()
}
@@ -185,7 +185,7 @@ func runShell(parent context.Context, command string, timeoutSec int) string {
ctx, cancel := context.WithTimeout(parent, time.Duration(timeoutSec)*time.Second)
defer cancel()
cmd := newShellCmd(ctx, command)
- // Le shell démarre dans le workspace, pas dans le dossier d'où ajean a été
+ // Le shell démarre dans le workspace, pas dans le dossier d'où loki a été
// lancé : un `> notes.txt` du modèle ne doit pas atterrir sur le Bureau.
//
// Le dossier est résolu UNE fois par process (agentWorkspace), donc s'il
@@ -208,7 +208,7 @@ func runShell(parent context.Context, command string, timeoutSec int) string {
// commande du genre « ./serveur & » rend la main tout de suite mais laisse
// un process en arrière-plan accroché aux tubes : runShell ne revenait alors
// JAMAIS, ni au délai, ni au stop. Le tour restait bloqué à vie, et la seule
- // issue connue était de redémarrer ajean-ui.
+ // issue connue était de redémarrer loki-ui.
cmd.WaitDelay = 2 * time.Second
err := cmd.Run()
switch {
diff --git a/internal/ajean/chat_tools_write_test.go b/internal/loki/chat_tools_write_test.go
similarity index 98%
rename from internal/ajean/chat_tools_write_test.go
rename to internal/loki/chat_tools_write_test.go
index ab07b4d..bb0aeb6 100644
--- a/internal/ajean/chat_tools_write_test.go
+++ b/internal/loki/chat_tools_write_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
diff --git a/internal/ajean/chat_workspace.go b/internal/loki/chat_workspace.go
similarity index 86%
rename from internal/ajean/chat_workspace.go
rename to internal/loki/chat_workspace.go
index 20016fb..faae313 100644
--- a/internal/ajean/chat_workspace.go
+++ b/internal/loki/chat_workspace.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -11,14 +11,14 @@ import (
//
// 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é ajean — le Bureau quand on double-clique l'installateur, C:\ProgramData\ajean\bin
+// 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.
-const workspaceEnv = "AJEAN_WORKSPACE"
+const workspaceEnv = "LOKI_WORKSPACE"
var (
workspaceOnce sync.Once
@@ -26,7 +26,7 @@ var (
)
// agentWorkspace renvoie le dossier de travail de l'agent, créé au besoin. Il
-// essaie plusieurs emplacements car AjeanHome() vaut %ProgramData%\ajean sous
+// 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() {
@@ -62,9 +62,9 @@ func workspaceCandidates() []string {
}
c = append(c, workspaceDir())
if home, err := os.UserHomeDir(); err == nil && home != "" {
- c = append(c, filepath.Join(home, "ajean", "workspace"))
+ c = append(c, filepath.Join(home, "loki", "workspace"))
}
- c = append(c, filepath.Join(os.TempDir(), "ajean-workspace"))
+ c = append(c, filepath.Join(os.TempDir(), "loki-workspace"))
return c
}
diff --git a/internal/ajean/chat_workspace_test.go b/internal/loki/chat_workspace_test.go
similarity index 91%
rename from internal/ajean/chat_workspace_test.go
rename to internal/loki/chat_workspace_test.go
index ae8c295..acb1a52 100644
--- a/internal/ajean/chat_workspace_test.go
+++ b/internal/loki/chat_workspace_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"path/filepath"
@@ -7,7 +7,7 @@ import (
)
// Un chemin relatif du modèle doit atterrir dans le workspace, jamais dans le
-// répertoire courant du processus (Bureau, C:\ProgramData\ajean\bin…).
+// répertoire courant du processus (Bureau, C:\ProgramData\loki\bin…).
func TestResolveAgentPathRelative(t *testing.T) {
got := resolveAgentPath("meteo.json")
want := filepath.Join(agentWorkspace(), "meteo.json")
diff --git a/internal/ajean/cli_app.go b/internal/loki/cli_app.go
similarity index 83%
rename from internal/ajean/cli_app.go
rename to internal/loki/cli_app.go
index cf53a78..1aa7385 100644
--- a/internal/ajean/cli_app.go
+++ b/internal/loki/cli_app.go
@@ -1,12 +1,12 @@
-package ajean
+package loki
-// cli_app.go — expérience « application » de AJEAN.
+// cli_app.go — expérience « application » de Loki.
//
// Quand on double-clique sur le binaire (aucun argument, console fraîche), au
-// lieu d'afficher l'aide dans une console qui se ferme, AJEAN démarre son UI web,
+// lieu d'afficher l'aide dans une console qui se ferme, Loki 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 AJEAN tourne et on le pilote
-// (« Ouvrir AJEAN » / « Quitter »).
+// Windows (voir sys_tray_windows.go) : on voit que Loki tourne et on le pilote
+// (« Ouvrir Loki » / « Quitter »).
import (
"fmt"
@@ -34,13 +34,13 @@ func cmdApp(args []string) error {
addr := fmt.Sprintf("0.0.0.0:%d", appPort)
ln, err := net.Listen("tcp", addr)
if err != nil {
- // AJEAN tourne déjà sur ce port : on ouvre juste l'UI sur l'instance
+ // Loki 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)
+ fmt.Printf("Loki est déjà lancé — ouverture de %s\n", url)
return openBrowser(url)
}
- // UN SEUL process propriétaire de la conversation, comme le service ajean-ui
+ // UN SEUL process propriétaire de la conversation, comme le service loki-ui
// 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
@@ -60,7 +60,7 @@ func cmdApp(args []string) error {
startAppLink(appWebMux)
}()
- sp := showSplash("Lancement d'AJEAN en cours…")
+ sp := showSplash("Lancement d'Loki en cours…")
waitServerReady(url)
_ = openBrowser(url)
time.Sleep(900 * time.Millisecond) // laisse le navigateur s'afficher par-dessus le splash
diff --git a/internal/ajean/doc.go b/internal/loki/doc.go
similarity index 86%
rename from internal/ajean/doc.go
rename to internal/loki/doc.go
index 1b31409..e7f58d1 100644
--- a/internal/ajean/doc.go
+++ b/internal/loki/doc.go
@@ -1,8 +1,8 @@
-// AJEAN — cœur du binaire (package ajean, appelé par cmd/ajean). Go n'autorise
+// Loki — cœur du binaire (package loki, appelé par cmd/loki). Go n'autorise
// pas de sous-dossiers dans un même package : les fichiers sont donc préfixés
// par domaine.
//
-// run.go point d'entrée : dispatch des sous-commandes, AjeanHome() et
+// run.go point d'entrée : dispatch des sous-commandes, LokiHome() et
// l'arborescence de données
// store.go LA base (bbolt) : configuration, préférences, conversation,
// clés, jetons, interrupteurs — tout l'état non éditable à la main
@@ -28,11 +28,11 @@
// supprimer un symbole jugé inutilisé, vérifier `grep` sur les fichiers _darwin.
//
// DEUX SERVICES à l'exécution, un seul binaire :
-// - ajean-engine (« ajean serve ») exec llama-server ;
-// - ajean-ui (« ajean web ») sert l'UI locale, le tunnel du relais et
+// - loki-engine (« loki serve ») exec llama-server ;
+// - loki-ui (« loki web ») sert l'UI locale, le tunnel du relais et
// l'endpoint OpenAI depuis un SEUL process — condition d'une conversation
// unique, celle-ci vivant en mémoire (voir chat_conversation.go).
//
// ui/index.html est GÉNÉRÉ depuis ui/src/ : éditer les sources puis
-// `go generate ./internal/ajean` (voir tools/assemble-ui).
-package ajean
+// `go generate ./internal/loki` (voir tools/assemble-ui).
+package loki
diff --git a/internal/ajean/llm_bench.go b/internal/loki/llm_bench.go
similarity index 99%
rename from internal/ajean/llm_bench.go
rename to internal/loki/llm_bench.go
index 1a29714..53b84dc 100644
--- a/internal/ajean/llm_bench.go
+++ b/internal/loki/llm_bench.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -70,7 +70,7 @@ func runBench(nPrompt, nPredict int) (*benchResult, error) {
// Use the same endpoint your real chat hits, so the comparison is honest
// (chat template, reasoning, OpenAI-compat layer all included).
payload := map[string]any{
- "model": "ajean",
+ "model": "loki",
"messages": []Message{{Role: "user", Content: prompt + "\n\nContinue this passage with another 1000+ words of original varied prose, mixing French and English narrative paragraphs on different topics."}},
"max_tokens": nPredict,
"stream": false,
diff --git a/internal/ajean/llm_client.go b/internal/loki/llm_client.go
similarity index 99%
rename from internal/ajean/llm_client.go
rename to internal/loki/llm_client.go
index f6ced71..02e0fcd 100644
--- a/internal/ajean/llm_client.go
+++ b/internal/loki/llm_client.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -575,7 +575,7 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps
// le contexte — c'est un choix assumé.
for iter := 0; ; iter++ {
payload := map[string]any{
- "model": "ajean",
+ "model": "loki",
"messages": messages,
"stream": true,
"temperature": temperature,
diff --git a/internal/ajean/llm_oai.go b/internal/loki/llm_oai.go
similarity index 93%
rename from internal/ajean/llm_oai.go
rename to internal/loki/llm_oai.go
index a018cce..762ac4b 100644
--- a/internal/ajean/llm_oai.go
+++ b/internal/loki/llm_oai.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -83,12 +83,12 @@ func setOAIPublic(on bool) error { return putBool(bkState, "oai_public", on) }
// oaiTLSConfig renvoie une config TLS qui, à la demande, obtient/renouvelle via
// Let's Encrypt (TLS-ALPN-01) le certificat de tout nom en *.oai.ajean.link, et
// répond elle-même aux challenges ACME. La clé privée est stockée dans
-// $AJEAN_HOME/certs et ne quitte jamais la machine. Toujours construite ; c'est le
+// $LOKI_HOME/certs et ne quitte jamais la machine. Toujours construite ; c'est le
// démux (oaiPublicEnabled, lu en direct) qui décide de router ou non le trafic.
func oaiTLSConfig() *tls.Config {
- certmagic.Default.Storage = &certmagic.FileStorage{Path: filepath.Join(AjeanHome(), "certs")}
+ certmagic.Default.Storage = &certmagic.FileStorage{Path: filepath.Join(LokiHome(), "certs")}
certmagic.DefaultACME.Agreed = true
- certmagic.DefaultACME.Email = os.Getenv("AJEAN_ACME_EMAIL")
+ certmagic.DefaultACME.Email = os.Getenv("LOKI_ACME_EMAIL")
certmagic.DefaultACME.DisableHTTPChallenge = true // pas de :80 accessible (CGNAT) → TLS-ALPN uniquement
magic := certmagic.NewDefault()
magic.OnDemand = &certmagic.OnDemandConfig{
@@ -206,7 +206,7 @@ func selfSignedTLSConfig(host string) (*tls.Config, error) {
// cmdOAI pilote l'accès OpenAI public côté agent.
//
-// ajean oai serve [port] [host] (test local) termine le TLS sur :port avec un
+// loki oai serve [port] [host] (test local) termine le TLS sur :port avec un
// cert auto-signé et proxifie vers llama /v1.
func cmdOAI(args []string) error {
sub := ""
@@ -236,12 +236,12 @@ func cmdOAI(args []string) error {
if err != nil {
return err
}
- fmt.Printf("[ajean oai] front TLS (test, auto-signé) https://%s:%d/v1 → llama :%d\n", host, port, LLMPort())
+ fmt.Printf("[loki oai] front TLS (test, auto-signé) https://%s:%d/v1 → llama :%d\n", host, port, LLMPort())
return runOAIFront(ln, tlsCfg)
default:
- fmt.Println("usage: ajean oai serve [port] [host] (front TLS de test → llama /v1)")
- fmt.Println(" en prod, le front TLS est servi automatiquement dans le tunnel (ajean link)")
- fmt.Println(" quand AJEAN_LINK_ALLOW_OAI=1 ; cert Let's Encrypt via TLS-ALPN-01.")
+ fmt.Println("usage: loki oai serve [port] [host] (front TLS de test → llama /v1)")
+ fmt.Println(" en prod, le front TLS est servi automatiquement dans le tunnel (loki link)")
+ fmt.Println(" quand LOKI_LINK_ALLOW_OAI=1 ; cert Let's Encrypt via TLS-ALPN-01.")
return nil
}
}
diff --git a/internal/ajean/llm_repeat_test.go b/internal/loki/llm_repeat_test.go
similarity index 98%
rename from internal/ajean/llm_repeat_test.go
rename to internal/loki/llm_repeat_test.go
index b9b731a..0cff050 100644
--- a/internal/ajean/llm_repeat_test.go
+++ b/internal/loki/llm_repeat_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"strings"
diff --git a/internal/ajean/llm_stream_cut_test.go b/internal/loki/llm_stream_cut_test.go
similarity index 99%
rename from internal/ajean/llm_stream_cut_test.go
rename to internal/loki/llm_stream_cut_test.go
index 71436b7..dbd8a2b 100644
--- a/internal/ajean/llm_stream_cut_test.go
+++ b/internal/loki/llm_stream_cut_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/llm_testcmd.go b/internal/loki/llm_testcmd.go
similarity index 95%
rename from internal/ajean/llm_testcmd.go
rename to internal/loki/llm_testcmd.go
index 081d2bf..26bbbfb 100644
--- a/internal/ajean/llm_testcmd.go
+++ b/internal/loki/llm_testcmd.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
@@ -14,7 +14,7 @@ func cmdTest(args []string) error {
fmt.Printf("→ GET http://localhost:%d/health … ", port)
if !healthCheck() {
fmt.Println(red("ko"))
- return fmt.Errorf("/health ne répond pas — ajean start d'abord")
+ return fmt.Errorf("/health ne répond pas — loki start d'abord")
}
fmt.Println(green("ok"))
diff --git a/internal/ajean/mcp_client.go b/internal/loki/mcp_client.go
similarity index 99%
rename from internal/ajean/mcp_client.go
rename to internal/loki/mcp_client.go
index 7fcdf02..fd932b1 100644
--- a/internal/ajean/mcp_client.go
+++ b/internal/loki/mcp_client.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
@@ -127,7 +127,7 @@ func (h headerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
// connect établit une session pour la config donnée.
func mcpConnect(ctx context.Context, name string, cfg MCPServerConfig) (*mcpsdk.ClientSession, error) {
- client := mcpsdk.NewClient(&mcpsdk.Implementation{Name: "ajean", Version: Version}, nil)
+ client := mcpsdk.NewClient(&mcpsdk.Implementation{Name: "loki", Version: Version}, nil)
var transport mcpsdk.Transport
switch cfg.Transport() {
diff --git a/internal/ajean/mcp_client_test.go b/internal/loki/mcp_client_test.go
similarity index 91%
rename from internal/ajean/mcp_client_test.go
rename to internal/loki/mcp_client_test.go
index c65207e..a6008c5 100644
--- a/internal/ajean/mcp_client_test.go
+++ b/internal/loki/mcp_client_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os/exec"
@@ -17,7 +17,7 @@ func TestMCPEnsureDeduplicatesConcurrentConnects(t *testing.T) {
// Commande inexistante : la connexion échoue, mais l'entrée du pool est bien
// partagée — c'est ce qu'on teste.
- cfg := MCPServerConfig{Command: "ajean-binaire-inexistant-pour-test", Enabled: true}
+ cfg := MCPServerConfig{Command: "loki-binaire-inexistant-pour-test", Enabled: true}
const callers = 8
var wg sync.WaitGroup
@@ -83,8 +83,8 @@ func TestMCPEndToEnd(t *testing.T) {
t.Fatalf("outil %s introuvable dans %d outils", echoName, len(tools))
}
- out := mcpCall(echoName, map[string]any{"message": "bonjour-ajean"})
- if !strings.Contains(out, "bonjour-ajean") {
+ out := mcpCall(echoName, map[string]any{"message": "bonjour-loki"})
+ if !strings.Contains(out, "bonjour-loki") {
t.Fatalf("echo n'a pas renvoyé le message ; got: %q", out)
}
}
diff --git a/internal/ajean/mcp_config.go b/internal/loki/mcp_config.go
similarity index 95%
rename from internal/ajean/mcp_config.go
rename to internal/loki/mcp_config.go
index 678de75..9ab71fc 100644
--- a/internal/ajean/mcp_config.go
+++ b/internal/loki/mcp_config.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -10,14 +10,14 @@ import (
// Configuration des serveurs MCP (Model Context Protocol).
//
-// ajean peut se connecter à des serveurs MCP tiers pour enrichir la palette
+// loki peut se connecter à des serveurs MCP tiers pour enrichir la palette
// d'outils de l'IA (accès fichiers rapide type desktop-commander, bases de
// données, APIs métier…). Deux transports :
//
-// - stdio : ajean lance un process local (command + args) et parle en
+// - stdio : loki lance un process local (command + args) et parle en
// JSON-RPC sur stdin/stdout. C'est le cas de desktop-commander, du serveur
// filesystem officiel, etc. — lancés via npx/uvx/un binaire.
-// - http : ajean parle à un serveur MCP distant en Streamable HTTP (url +
+// - http : loki parle à un serveur MCP distant en Streamable HTTP (url +
// éventuels en-têtes d'auth).
//
// Le format du fichier mcp.json reprend celui de Claude Desktop (clé
@@ -25,7 +25,7 @@ import (
// leurs configs existantes. On ajoute un champ "enabled" par serveur.
//
// IMPORTANT (sécurité) : un serveur MCP stdio exécute un process arbitraire sur
-// la machine où tourne ajean — même niveau de confiance que l'outil `bash` du
+// la machine où tourne loki — même niveau de confiance que l'outil `bash` du
// mode agent. La configuration MCP est donc réservée au propriétaire local de la
// machine et ne doit JAMAIS être pilotable depuis le relais/accès distant.
diff --git a/internal/ajean/migrate_07.go b/internal/loki/migrate_07.go
similarity index 99%
rename from internal/ajean/migrate_07.go
rename to internal/loki/migrate_07.go
index b4d28ab..48c6dc7 100644
--- a/internal/ajean/migrate_07.go
+++ b/internal/loki/migrate_07.go
@@ -1,8 +1,8 @@
-package ajean
+package loki
// migrate_07.go — CODE TEMPORAIRE, à supprimer d'ici une version ou deux.
//
-// Reprend une installation 0.7.x au moment de `ajean install`. Tout est ici, et
+// Reprend une installation 0.7.x au moment de `loki install`. Tout est ici, et
// rien d'autre n'en dépend : le jour où le parc aura basculé, supprimer ce
// fichier et les deux appels à migrateFrom07() (sys_install_unix.go et
// sys_install_windows.go) suffit. C'est la seule raison d'être de ce fichier —
diff --git a/internal/ajean/migrate_07_test.go b/internal/loki/migrate_07_test.go
similarity index 98%
rename from internal/ajean/migrate_07_test.go
rename to internal/loki/migrate_07_test.go
index c05d13f..1b6626a 100644
--- a/internal/ajean/migrate_07_test.go
+++ b/internal/loki/migrate_07_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -38,7 +38,7 @@ func fake07Home(t *testing.T) string {
write(".agent_enabled", "")
write("webprefs.json", `{"theme":"dark"}`)
write("mcp.json", `{"mcpServers":{"fs":{"command":"npx","enabled":true}}}`)
- write("sysprompt.txt", "Tu es AJEAN.\n")
+ write("sysprompt.txt", "Tu es Loki.\n")
write("conversation.json", `{"seq":42,"messages":[{"role":"user","content":"salut"}]}`)
write("SKILLS/vieux-skill/SKILL.md", "# un skill d'avant\n")
write(".authorized_users", "AABBCCDDEEFF00112233445566778899AABBCCDDEEFF001122334455667788AA\n")
@@ -90,7 +90,7 @@ func TestMigration07(t *testing.T) {
if !agentEnabled() {
t.Error("mode agent perdu")
}
- if readSysPrompt() != "Tu es AJEAN." {
+ if readSysPrompt() != "Tu es Loki." {
t.Errorf("prompt système = %q", readSysPrompt())
}
if loadWebPrefs()["theme"] != "dark" {
diff --git a/internal/ajean/node_api.go b/internal/loki/node_api.go
similarity index 99%
rename from internal/ajean/node_api.go
rename to internal/loki/node_api.go
index 82c929b..80e7230 100644
--- a/internal/ajean/node_api.go
+++ b/internal/loki/node_api.go
@@ -6,7 +6,7 @@
// - ENRÔLEMENT (public) : le poste échange un code d'appairage à usage unique
// contre sa clé d'appareil. Authentifié par le code, pas par la clé de
// pilotage — que le poste ne possède pas.
-package ajean
+package loki
import (
"encoding/base64"
diff --git a/internal/ajean/node_proto.go b/internal/loki/node_proto.go
similarity index 88%
rename from internal/ajean/node_proto.go
rename to internal/loki/node_proto.go
index dc4a4f9..b3dcc79 100644
--- a/internal/ajean/node_proto.go
+++ b/internal/loki/node_proto.go
@@ -1,16 +1,16 @@
// node_proto.go — côté serveur, alias vers le protocole partagé (internal/nodewire)
-// + les helpers de nommage d'outils propres à ajean.
+// + les helpers de nommage d'outils propres à loki.
//
// Le format du fil (Msg, capacités, confinement de chemin) vit dans nodewire,
-// paquet SANS dépendance, pour que le client léger ajean-node le partage sans
-// tirer tout ajean. Ici on ne fait qu'exposer ces symboles sous les noms
+// paquet SANS dépendance, pour que le client léger loki-node le partage sans
+// tirer tout loki. Ici on ne fait qu'exposer ces symboles sous les noms
// historiques du paquet, pour ne pas toucher le reste du code serveur.
-package ajean
+package loki
import (
"strings"
- "github.com/nathaninline/ajean/internal/nodewire"
+ "github.com/R0m1k3/Loki/internal/nodewire"
)
// Capacités (alias des constantes partagées).
diff --git a/internal/ajean/node_proto_test.go b/internal/loki/node_proto_test.go
similarity index 99%
rename from internal/ajean/node_proto_test.go
rename to internal/loki/node_proto_test.go
index a5b861d..1e34dfe 100644
--- a/internal/ajean/node_proto_test.go
+++ b/internal/loki/node_proto_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"path/filepath"
diff --git a/internal/ajean/node_server.go b/internal/loki/node_server.go
similarity index 99%
rename from internal/ajean/node_server.go
rename to internal/loki/node_server.go
index 1d3d157..d75f56f 100644
--- a/internal/ajean/node_server.go
+++ b/internal/loki/node_server.go
@@ -2,7 +2,7 @@
// WebSocket, registre des postes connectés, et exposition de leurs outils à
// l'agent. Le serveur n'exécute JAMAIS rien lui-même ici : il route une demande
// d'outil vers le poste concerné et attend sa réponse.
-package ajean
+package loki
import (
"context"
@@ -18,7 +18,7 @@ import (
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
- "github.com/nathaninline/ajean/internal/nodewire"
+ "github.com/R0m1k3/Loki/internal/nodewire"
)
const (
diff --git a/internal/ajean/node_ws_test.go b/internal/loki/node_ws_test.go
similarity index 98%
rename from internal/ajean/node_ws_test.go
rename to internal/loki/node_ws_test.go
index b9521f5..53311dd 100644
--- a/internal/ajean/node_ws_test.go
+++ b/internal/loki/node_ws_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
@@ -12,7 +12,7 @@ import (
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
- "github.com/nathaninline/ajean/internal/nodewire"
+ "github.com/R0m1k3/Loki/internal/nodewire"
)
// TestNodeE2E exerce tout le chemin : enrôlement SCELLÉ (le sceau du client est
diff --git a/internal/ajean/relay_e2e.go b/internal/loki/relay_e2e.go
similarity index 97%
rename from internal/ajean/relay_e2e.go
rename to internal/loki/relay_e2e.go
index 33aecdb..96ae0de 100644
--- a/internal/ajean/relay_e2e.go
+++ b/internal/loki/relay_e2e.go
@@ -1,11 +1,11 @@
-package ajean
+package loki
// relay_e2e.go — chiffrement bout-en-bout du chat entre le NAVIGATEUR et CET agent, de
// sorte que le relais (ajean.link) ne voie que de l'opaque : « boîte noire ».
//
// Modèle : cet agent a une paire X25519 long-terme (clé privée locale, jamais
// transmise). Sa clé publique est publiée au relais et son EMPREINTE est affichée
-// au `ajean link` — l'utilisateur la confirme dans le portail, ce qui défait tout
+// au `loki link` — l'utilisateur la confirme dans le portail, ce qui défait tout
// MITM du relais. Le navigateur dérive une racine R de son mot de passe (exportKey
// OPAQUE), la SCELLE vers la clé publique de l'agent (le relais ne peut pas
// l'ouvrir), puis chiffre/déchiffre le chat avec une clé dérivée de R.
@@ -32,7 +32,7 @@ import (
"sync"
)
-func e2eKeyPath() string { return filepath.Join(AjeanHome(), ".e2e_key") }
+func e2eKeyPath() string { return filepath.Join(LokiHome(), ".e2e_key") }
var (
e2eOnce sync.Once
@@ -55,7 +55,7 @@ func e2ePrivateKey() (*ecdh.PrivateKey, error) {
e2eErr = err
return
}
- _ = os.MkdirAll(AjeanHome(), 0o755)
+ _ = os.MkdirAll(LokiHome(), 0o755)
if err := os.WriteFile(e2eKeyPath(), []byte(hex.EncodeToString(k.Bytes())+"\n"), 0o600); err != nil {
e2eErr = err
return
diff --git a/internal/ajean/relay_e2eauth.go b/internal/loki/relay_e2eauth.go
similarity index 96%
rename from internal/ajean/relay_e2eauth.go
rename to internal/loki/relay_e2eauth.go
index d56a460..7bf2aae 100644
--- a/internal/ajean/relay_e2eauth.go
+++ b/internal/loki/relay_e2eauth.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
// relay_e2eauth.go — authentification MUTUELLE + anti-rejeu du canal chiffré.
//
@@ -9,7 +9,7 @@ package ajean
// Modèle : l'utilisateur a une identité X25519 dérivée de façon déterministe de son
// mot de passe (exportKey OPAQUE → racine R → uPriv) — le relais ne connaît pas R,
// donc ne peut PAS reproduire cette identité. La clé publique uPub est APPAIRÉE une
-// fois à l'agent via un code affiché par « ajean link » (canal hors-bande : le log du
+// fois à l'agent via un code affiché par « loki link » (canal hors-bande : le log du
// serveur, que le relais ne voit pas ; le code voyage scellé vers l'agent). Ensuite,
// chaque requête est chiffrée avec K = SHA256(ECDH(uPriv, agentPriv) || "authchan"),
// liée à uPub+horodatage (AAD) et protégée contre le rejeu. Seul le vrai utilisateur
@@ -92,7 +92,7 @@ func authorizeUser(uPubHex string) error {
// ---- Codes d'appairage : à la demande, usage unique, TTL 10 min --------------
//
-// Générés par « ajean link code » (ou affichés par « ajean link »), ils sont
+// Générés par « loki link code » (ou affichés par « loki link »), ils sont
// partagés avec le service d'interface (autre process) via la base — sinon les
// deux ne s'accorderaient pas. Stockés HACHÉS (SHA-256) : la base ne révèle
// aucun code. Chaque code expire au bout de 10 min et est consommé (retiré) au
@@ -232,7 +232,7 @@ func replayCheck(uPub string, ts int64, iv string) bool {
// ---- Dérivation de clé + ouverture authentifiée ----------------------------
// e2eAuthKey dérive la clé du canal authentifié pour l'utilisateur uPub :
-// SHA256( ECDH(agentPriv, uPub) || "ajean-authchan-v1" ). Symétrique de la version
+// SHA256( ECDH(agentPriv, uPub) || "loki-authchan-v1" ). Symétrique de la version
// navigateur (WASM) qui calcule ECDH(uPriv, agentPub) = même secret partagé.
func e2eAuthKey(uPubHex string) ([]byte, error) {
priv, err := e2ePrivateKey()
@@ -253,7 +253,7 @@ func e2eAuthKey(uPubHex string) ([]byte, error) {
}
h := sha256.New()
h.Write(ss)
- h.Write([]byte("ajean-authchan-v1"))
+ h.Write([]byte("loki-authchan-v1"))
return h.Sum(nil), nil
}
diff --git a/internal/ajean/relay_e2eauth_test.go b/internal/loki/relay_e2eauth_test.go
similarity index 99%
rename from internal/ajean/relay_e2eauth_test.go
rename to internal/loki/relay_e2eauth_test.go
index a75accf..16c1d34 100644
--- a/internal/ajean/relay_e2eauth_test.go
+++ b/internal/loki/relay_e2eauth_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"strings"
diff --git a/internal/ajean/relay_link.go b/internal/loki/relay_link.go
similarity index 89%
rename from internal/ajean/relay_link.go
rename to internal/loki/relay_link.go
index 0fb8783..d336cd7 100644
--- a/internal/ajean/relay_link.go
+++ b/internal/loki/relay_link.go
@@ -1,16 +1,16 @@
-package ajean
+package loki
-// relay_link.go — `ajean link ` : connecte ce serveur AJEAN au relais public
+// relay_link.go — `loki link ` : connecte ce serveur Loki au relais public
// (ajean.link) par une connexion SORTANTE persistante, pour qu'un utilisateur
// y accède depuis n'importe où sans ouvrir de port (CGNAT, box, etc.).
//
// Principe : l'agent ouvre un WebSocket vers le relais, l'authentifie avec le
// token d'abonnement, puis multiplexe (yamux) ce lien unique en un stream par
-// requête navigateur. Chaque stream est reverse-proxyfié vers le `ajean web`
+// requête navigateur. Chaque stream est reverse-proxyfié vers le `loki web`
// local. Keepalive + reconnexion automatique avec backoff.
//
// Le token est fourni par la boutique à l'achat. Il est mémorisé dans
-// $AJEAN_HOME/.link_token pour que `ajean link` (sans argument) reprenne la
+// $LOKI_HOME/.link_token pour que `loki link` (sans argument) reprenne la
// connexion.
import (
@@ -39,7 +39,7 @@ import (
)
// defaultRelayURL est l'endpoint WebSocket du relais. Surchageable via
-// $AJEAN_LINK_URL (utile pour tester contre un relais local).
+// $LOKI_LINK_URL (utile pour tester contre un relais local).
const defaultRelayURL = "wss://ajean.link/agent"
// machineID renvoie un identifiant stable de la machine, créé au premier appel.
@@ -65,20 +65,20 @@ func removeLinkToken() error { return putStr(bkState, "link_token", "") }
// relayURL resolves the relay WebSocket endpoint (env override → default).
func relayURL() string {
- if u := os.Getenv("AJEAN_LINK_URL"); u != "" {
+ if u := os.Getenv("LOKI_LINK_URL"); u != "" {
return u
}
return defaultRelayURL
}
-// uiUnitName est l'unité qui exécute « ajean web » : l'UI locale, le tunnel du
+// uiUnitName est l'unité qui exécute « loki web » : l'UI locale, le tunnel du
// relais et l'endpoint OpenAI, dans un seul et même process.
-const uiUnitName = "ajean-ui"
+const uiUnitName = "loki-ui"
func uiServiceName() string { return uiUnitName }
// cmdLink ne gère QUE le compte d'accès distant : le jeton, l'appairage, l'état.
-// Le tunnel n'est plus un service à part — il est ouvert par « ajean web » dès
+// Le tunnel n'est plus un service à part — il est ouvert par « loki web » dès
// qu'un jeton est enregistré. Piloter le tunnel, c'est donc piloter le service
// d'interface (voir cmdUI).
func cmdLink(args []string) error {
@@ -88,33 +88,33 @@ func cmdLink(args []string) error {
}
switch sub {
case "":
- // `ajean link` seul : afficher l'aide (NE démarre rien — éviter de prendre
+ // `loki link` seul : afficher l'aide (NE démarre rien — éviter de prendre
// un mot pour un jeton et d'écraser le vrai).
printLinkHelp()
return nil
case "status":
tok := readLinkToken()
if tok == "" {
- fmt.Println(yellow("[info]") + " aucun jeton enregistré — lance: ajean link ")
+ fmt.Println(yellow("[info]") + " aucun jeton enregistré — lance: loki link ")
return nil
}
fmt.Printf("%s jeton enregistré (%s…), relais: %s\n", green("[ok]"), tok[:min(8, len(tok))], relayURL())
if uiServiceActive() {
fmt.Printf("%s service %s actif — le tunnel est ouvert\n", green("[ok]"), uiServiceName())
} else {
- fmt.Printf("%s service %s arrêté — pas de tunnel (ajean ui start)\n", yellow("[info]"), uiServiceName())
+ fmt.Printf("%s service %s arrêté — pas de tunnel (loki ui start)\n", yellow("[info]"), uiServiceName())
}
return nil
case "logout":
if err := removeLinkToken(); err != nil {
return err
}
- fmt.Println(green("[ok]") + " jeton supprimé — « ajean ui restart » pour fermer le tunnel")
+ fmt.Println(green("[ok]") + " jeton supprimé — « loki ui restart » pour fermer le tunnel")
return nil
case "code":
code, err := newPairCode()
if err != nil {
- return fmt.Errorf("génération du code (droits sur %s ?): %w", AjeanHome(), err)
+ return fmt.Errorf("génération du code (droits sur %s ?): %w", LokiHome(), err)
}
fmt.Printf("%s code d'appairage (valable 10 min, à usage unique) :\n %s\n", green("[link]"), bold(code))
return nil
@@ -138,7 +138,7 @@ func cmdLink(args []string) error {
return linkPrintIdentity()
}
-// cmdUI pilote le service d'interface — « ajean web » en arrière-plan : l'UI
+// cmdUI pilote le service d'interface — « loki web » en arrière-plan : l'UI
// locale, le tunnel du relais et l'endpoint OpenAI, servis par le MÊME process.
// C'est ce qui garantit une conversation unique, identique en local et à distance.
func cmdUI(args []string) error {
@@ -160,22 +160,22 @@ func cmdUI(args []string) error {
}
return nil
default:
- return fmt.Errorf("usage: ajean ui [start|stop|restart|status]")
+ return fmt.Errorf("usage: loki ui [start|stop|restart|status]")
}
}
-// printLinkHelp liste les sous-commandes de `ajean link`.
+// printLinkHelp liste les sous-commandes de `loki link`.
func printLinkHelp() {
- fmt.Print(`ajean link — accès distant via le relais ajean.link
+ fmt.Print(`loki link — accès distant via le relais ajean.link
Usage :
- ajean link enregistre le jeton (1re fois / pour le changer) et ouvre le tunnel
- ajean link status état du jeton et du tunnel
- ajean link code génère un code d'appairage (valable 10 min, à usage unique)
- ajean link logout oublie le jeton enregistré
+ loki link enregistre le jeton (1re fois / pour le changer) et ouvre le tunnel
+ loki link status état du jeton et du tunnel
+ loki link code génère un code d'appairage (valable 10 min, à usage unique)
+ loki link logout oublie le jeton enregistré
Le jeton est fourni sur ajean.link. Le tunnel est ouvert par le service
-d'interface (« ajean ui ») dès qu'un jeton est enregistré : il n'y a pas de
+d'interface (« loki ui ») dès qu'un jeton est enregistré : il n'y a pas de
service séparé pour l'accès distant.
`)
}
@@ -189,8 +189,8 @@ func linkPrintIdentity() error {
}
code, err := newPairCode()
if err != nil {
- fmt.Printf("%s code d'appairage indisponible (droits sur %s ?): %v\n", yellow("[link]"), AjeanHome(), err)
- fmt.Printf(" Réessaie : sudo ajean link code\n")
+ fmt.Printf("%s code d'appairage indisponible (droits sur %s ?): %v\n", yellow("[link]"), LokiHome(), err)
+ fmt.Printf(" Réessaie : sudo loki link code\n")
return nil
}
fmt.Printf(" Code d'appairage (valable 10 min, usage unique) : %s\n\n", bold(code))
@@ -208,7 +208,7 @@ func linkPrintIdentity() error {
//
// Sur un poste de bureau, l'app EST ce process propriétaire : elle sert déjà
// :8090, donc elle fait aussi tourner le tunnel elle-même au lieu de déléguer à
-// un worker détaché. L'accès distant vit donc aussi longtemps que AJEAN.app est
+// un worker détaché. L'accès distant vit donc aussi longtemps que Loki.app est
// ouverte — sur un portable qui s'endort, c'est de toute façon la réalité.
var appLink struct {
@@ -310,14 +310,14 @@ func killForeignUIWorker() {
}
// uiPIDPath / uiLogPath : suivi du worker de lien hors systemd (macOS,
-// Windows), où AJEAN est une app de bureau lancée sans droits root.
-func uiPIDPath() string { return filepath.Join(AjeanHome(), uiUnitName+".pid") }
-func uiLogPath() string { return filepath.Join(AjeanHome(), uiUnitName+".log") }
+// Windows), où Loki est une app de bureau lancée sans droits root.
+func uiPIDPath() string { return filepath.Join(LokiHome(), uiUnitName+".pid") }
+func uiLogPath() string { return filepath.Join(LokiHome(), uiUnitName+".log") }
// uiServiceCtl pilote le worker de lien (start/stop/restart). Sous Linux c'est
-// l'unité systemd ajean-ui (avec sudo non interactif si on n'est pas root) ;
+// l'unité systemd loki-ui (avec sudo non interactif si on n'est pas root) ;
// ailleurs — macOS et Windows, où il n'y a ni systemd ni droits root — on lance
-// « ajean link serve » en processus détaché suivi par un fichier PID, comme le
+// « loki link serve » en processus détaché suivi par un fichier PID, comme le
// fait déjà le service principal. Sans ça, l'accès distant restait définitivement
// « arrêté » sur un Mac ou un PC : le token était enregistré mais rien ne
// composait jamais le tunnel.
@@ -422,7 +422,7 @@ func uiUserSvcCtl(action string) error {
if p, err := filepath.EvalSymlinks(self); err == nil {
self = p
}
- if err := os.MkdirAll(AjeanHome(), 0o755); err != nil {
+ if err := os.MkdirAll(LokiHome(), 0o755); err != nil {
return err
}
logf, err := os.OpenFile(uiLogPath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
@@ -434,7 +434,7 @@ func uiUserSvcCtl(action string) error {
cmd := spawnDetached(self, "web")
cmd.Stdout, cmd.Stderr = logf, logf
if err := cmd.Start(); err != nil {
- return fmt.Errorf("démarrage de « ajean web »: %w", err)
+ return fmt.Errorf("démarrage de « loki web »: %w", err)
}
pid := cmd.Process.Pid
// PID + binaire d'origine : la 2e ligne dit QUELLE copie de l'app a lancé ce
@@ -460,7 +460,7 @@ func uiUserSvcCtl(action string) error {
// - /v1/*, /health, /props, /metrics, /slots → llama-server local (endpoint
// compatible OpenAI, avec injection de la clé API locale) → permet de
// brancher OpenCode, Hermes, etc. sur ajean.link/oai//v1
-// - tout le reste → l'UI web de AJEAN (avec injection de la clé de pilotage)
+// - tout le reste → l'UI web de Loki (avec injection de la clé de pilotage)
func newLinkHandler(mux *http.ServeMux) http.Handler {
web := withLocalAuth(mux)
llama := &url.URL{Scheme: "http", Host: fmt.Sprintf("127.0.0.1:%d", LLMPort())}
@@ -484,7 +484,7 @@ func newLinkHandler(mux *http.ServeMux) http.Handler {
// - /api/e2e/chat : chat chiffré (streaming SSE chiffré).
// - /api/e2e/req : proxy de contrôle chiffré (presets, VRAM, skills, service…).
// Tout autre /api/* en clair est REFUSÉ via le tunnel.
- oaiAllowed := os.Getenv("AJEAN_LINK_ALLOW_OAI") == "1"
+ oaiAllowed := os.Getenv("LOKI_LINK_ALLOW_OAI") == "1"
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
if strings.HasPrefix(p, "/api/e2e/") {
@@ -514,7 +514,7 @@ func newLinkHandler(mux *http.ServeMux) http.Handler {
// L'endpoint OpenAI (OpenCode/Hermes) ne peut pas être chiffré navigateur :
// il transiterait en clair par le relais. Désactivé par défaut.
if !oaiAllowed {
- http.Error(w, "endpoint OpenAI désactivé via le relais (transiterait en clair) ; AJEAN_LINK_ALLOW_OAI=1 pour l'autoriser", http.StatusForbidden)
+ http.Error(w, "endpoint OpenAI désactivé via le relais (transiterait en clair) ; LOKI_LINK_ALLOW_OAI=1 pour l'autoriser", http.StatusForbidden)
return
}
lp.ServeHTTP(w, r)
@@ -531,7 +531,7 @@ func newLinkHandler(mux *http.ServeMux) http.Handler {
//
// ⚠️ La clé est relue À CHAQUE REQUÊTE, et surtout pas capturée une fois pour
// toutes à l'ouverture du tunnel. Avec une capture, changer la clé (depuis
-// l'interface ou `ajean set-web-key`) faisait injecter l'ANCIENNE jusqu'au
+// l'interface ou `loki set-web-key`) faisait injecter l'ANCIENNE jusqu'au
// prochain redémarrage du service : tout l'accès distant tombait en 401, et
// comme le chat n'affiche pas le code HTTP d'un flux qui n'arrive jamais, le
// symptôme était un « chargement de la conversation » infini, sans la moindre
@@ -599,7 +599,7 @@ func runLinkSession(ctx context.Context, token string, handler http.Handler, oai
fmt.Printf("%s lien établi ✓\n", green("[link]"))
// Chaque Accept() = un stream. Deux natures possibles :
- // - requête HTTP normale (UI AJEAN / E2E) → servie par `handler` ;
+ // - requête HTTP normale (UI Loki / E2E) → servie par `handler` ;
// - session TLS brute (accès OpenAI public) → terminée par le front TLS.
// On les distingue au 1er octet (0x16 = handshake TLS). Sans OAI activé, tout
// va au HTTP (comportement historique inchangé).
diff --git a/internal/ajean/relay_localauth_test.go b/internal/loki/relay_localauth_test.go
similarity index 95%
rename from internal/ajean/relay_localauth_test.go
rename to internal/loki/relay_localauth_test.go
index 7cdad30..1c06dd3 100644
--- a/internal/ajean/relay_localauth_test.go
+++ b/internal/loki/relay_localauth_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"net/http"
@@ -8,7 +8,7 @@ import (
// Changer la clé de pilotage ne doit PAS couper l'accès distant jusqu'au
// prochain redémarrage. La clé était capturée à l'ouverture du tunnel : après un
-// `ajean set-web-key`, le tunnel injectait l'ancienne, tout passait en 401, et
+// `loki set-web-key`, le tunnel injectait l'ancienne, tout passait en 401, et
// le portail restait bloqué sur « chargement de la conversation » sans jamais
// dire pourquoi.
func TestWithLocalAuthSuitLaCleCourante(t *testing.T) {
diff --git a/internal/ajean/remote.go b/internal/loki/remote.go
similarity index 77%
rename from internal/ajean/remote.go
rename to internal/loki/remote.go
index 9326162..d482e46 100644
--- a/internal/ajean/remote.go
+++ b/internal/loki/remote.go
@@ -1,17 +1,17 @@
-// Sous-commande « ajean remote » — client « poste distant » intégré au binaire
-// principal. On l'installe sur un PC pour que l'IA d'un serveur AJEAN puisse agir
+// Sous-commande « loki remote » — client « poste distant » intégré au binaire
+// principal. On l'installe sur un PC pour que l'IA d'un serveur Loki puisse agir
// dessus (shell/fichiers), chiffré de bout en bout via ajean.link (relais aveugle)
// ou en direct sur le réseau local.
//
-// ajean remote install --machine ID --key CLÉ --code CODE [--allow shell,read,write,list] [--root DIR]
+// loki remote install --machine ID --key CLÉ --code CODE [--allow shell,read,write,list] [--root DIR]
// appaire ce PC et l'installe en service (démarrage auto)
-// ajean remote connect ... idem mais en avant-plan (test)
-// ajean remote run boucle client (utilisé par le service)
-// ajean remote status | uninstall | logout
+// loki remote connect ... idem mais en avant-plan (test)
+// loki remote run boucle client (utilisé par le service)
+// loki remote status | uninstall | logout
//
-// Historiquement un binaire séparé « ajean-remote » ; fusionné dans ce binaire
+// Historiquement un binaire séparé « loki-remote » ; fusionné dans ce binaire
// pour n'avoir qu'une seule application.
-package ajean
+package loki
import (
"context"
@@ -20,7 +20,7 @@ import (
"runtime"
"strings"
- "github.com/nathaninline/ajean/internal/nodeclient"
+ "github.com/R0m1k3/Loki/internal/nodeclient"
)
func cmdRemote(args []string) error {
@@ -59,18 +59,18 @@ func cmdRemote(args []string) error {
}
func remoteUsage() {
- fmt.Print(`ajean remote — piloter CE PC depuis l'IA d'un serveur AJEAN
+ fmt.Print(`loki remote — piloter CE PC depuis l'IA d'un serveur Loki
- ajean remote install --code CODE [options] appaire + installe en service (démarrage auto, invisible)
- ajean remote connect --code CODE [options] idem en avant-plan (pour tester)
- ajean remote status état de la configuration
- ajean remote uninstall retire le service (garde la config)
- ajean remote logout retire le service ET oublie la clé
+ loki remote install --code CODE [options] appaire + installe en service (démarrage auto, invisible)
+ loki remote connect --code CODE [options] idem en avant-plan (pour tester)
+ loki remote status état de la configuration
+ loki remote uninstall retire le service (garde la config)
+ loki remote logout retire le service ET oublie la clé
Options :
--machine ID machine à joindre via ajean.link (fournie par l'UI → Postes distants)
--key CLÉ clé publique de l'agent (le poste scelle vers elle)
- --code CODE code d'appairage (10 min, généré dans l'UI AJEAN → Postes distants)
+ --code CODE code d'appairage (10 min, généré dans l'UI Loki → Postes distants)
--allow LISTE capacités acceptées : shell,read,write,list (défaut : ce que le serveur autorise)
--root DIR dossier autorisé (confine lecture/écriture)
--yes auto-approuve les actions (obligatoire pour le service, sans terminal)
@@ -84,8 +84,8 @@ func remoteConnect(args []string, install bool) error {
cfg, lerr := nodeclient.LoadConfig()
// Un --code est un jeton d'appairage à usage unique : le fournir veut TOUJOURS
// dire « (ré)appaire-moi maintenant ». Sans ce « || f.code != "" », une config
- // résiduelle (test précédent, ancien binaire ajean-remote au même emplacement
- // %ProgramData%\ajean-remote\config.json) faisait sauter l'appairage en silence :
+ // résiduelle (test précédent, ancien binaire loki-remote au même emplacement
+ // %ProgramData%\loki-remote\config.json) faisait sauter l'appairage en silence :
// le service démarrait avec de vieilles clés que le serveur ignore et le poste
// ne remontait jamais, alors que tout affichait « [ok] ».
needEnroll := lerr != nil || cfg.Priv == "" || f.code != ""
@@ -148,7 +148,7 @@ func remoteConnect(args []string, install bool) error {
func remoteRun() error {
cfg, err := nodeclient.LoadConfig()
if err != nil {
- return fmt.Errorf("aucune configuration — lance d'abord « ajean remote install … »")
+ return fmt.Errorf("aucune configuration — lance d'abord « loki remote install … »")
}
return nodeclient.RunService(cfg)
}
diff --git a/internal/ajean/run.go b/internal/loki/run.go
similarity index 80%
rename from internal/ajean/run.go
rename to internal/loki/run.go
index 95058ff..e576889 100644
--- a/internal/ajean/run.go
+++ b/internal/loki/run.go
@@ -1,7 +1,7 @@
-// ajean — single-binary LLM server manager + web UI for llama.cpp deployments.
-// Point d'entrée réel : Main(), appelé par cmd/ajean (les métadonnées Windows
+// loki — single-binary LLM server manager + web UI for llama.cpp deployments.
+// Point d'entrée réel : Main(), appelé par cmd/loki (les métadonnées Windows
// .syso/go:generate vivent là-bas, dans le dossier du package main).
-package ajean
+package loki
import (
"fmt"
@@ -12,7 +12,7 @@ import (
const Version = "0.9.4"
-// Main est le vrai main() du binaire (cmd/ajean ne fait que l'appeler).
+// Main est le vrai main() du binaire (cmd/loki ne fait que l'appeler).
func Main() {
// Rattache la console du terminal parent si on est lancé depuis un shell
// (Windows : binaire GUI). Retourne false au double-clic (aucune console) →
@@ -34,7 +34,7 @@ func Main() {
// lance l'expérience « application » (UI web + navigateur + icône tray) plutôt
// que d'afficher l'aide. Le binaire étant compilé en sous-système GUI, il n'y a
// AUCUNE console à ce stade (donc plus de fenêtre noire). Lancé depuis un shell,
- // `ajean` sans argument garde son comportement d'aide.
+ // `loki` sans argument garde son comportement d'aide.
if noArgs && !haveConsole {
mustExit(cmdApp(args))
return
@@ -107,7 +107,7 @@ func Main() {
case "uninstall":
mustExit(cmdUninstall(args))
case "version", "-v", "--version":
- fmt.Println("ajean", Version)
+ fmt.Println("loki", Version)
case "help", "-h", "--help", "":
printHelp()
default:
@@ -118,14 +118,14 @@ func Main() {
}
func printHelp() {
- fmt.Printf(`ajean %s — manager llama.cpp + UI web (single binary)
+ fmt.Printf(`loki %s — manager llama.cpp + UI web (single binary)
-Usage: ajean [args]
+Usage: loki [args]
-AJEAN tourne en deux services : ajean-engine (le modèle) et ajean-ui
+Loki tourne en deux services : loki-engine (le modèle) et loki-ui
(l'interface web, le tunnel d'accès distant et l'endpoint OpenAI).
-Moteur (ajean-engine) :
+Moteur (loki-engine) :
start | stop | restart gérer le service
status | logs état / logs en direct
enable | disable auto-démarrage au boot
@@ -138,7 +138,7 @@ Moteur (ajean-engine) :
network [on|off|status] rendre l'endpoint OpenAI joignable depuis le réseau local
(HOST + règle de pare-feu sous Windows)
-Interface (ajean-ui) :
+Interface (loki-ui) :
ui [start|stop|restart|status] pilote le service d'interface
web [PORT] sert l'interface au premier plan (défaut :8090) —
ouvre aussi le tunnel si un jeton est enregistré
@@ -160,7 +160,7 @@ Accès distant (ajean.link) :
link code code d'appairage (10 min, usage unique) pour le portail
link status | logout état du jeton / l'oublier
-Poste distant (piloter CE PC depuis l'IA d'un serveur AJEAN) :
+Poste distant (piloter CE PC depuis l'IA d'un serveur Loki) :
remote install --machine ID --key CLÉ --code CODE [--allow shell,read,write,list]
appaire ce PC + installe le service (démarrage auto)
remote connect … idem en avant-plan (pour tester)
@@ -179,8 +179,8 @@ Installation:
serve entrypoint du service : exec llama-server (usage interne)
Env:
- AJEAN_HOME racine des données (défaut : /etc/ajean, %%ProgramData%%\ajean sous Windows)
- EDITOR éditeur pour 'ajean edit' (défaut : nano, notepad sous Windows)
+ LOKI_HOME racine des données (défaut : /etc/loki, %%ProgramData%%\loki sous Windows)
+ EDITOR éditeur pour 'loki edit' (défaut : nano, notepad sous Windows)
`, Version)
}
@@ -191,21 +191,21 @@ func mustExit(err error) {
}
}
-// AjeanHome resolves the AJEAN data directory.
-// Précédence : $AJEAN_HOME → /etc/default/ajean (unix) → défaut plateforme.
-func AjeanHome() string {
- if h := os.Getenv("AJEAN_HOME"); h != "" {
+// LokiHome resolves the Loki data directory.
+// Précédence : $LOKI_HOME → /etc/default/loki (unix) → défaut plateforme.
+func LokiHome() string {
+ if h := os.Getenv("LOKI_HOME"); h != "" {
return h
}
if h := readEtcDefault(); h != "" {
return h
}
- return defaultAjeanHome()
+ return defaultLokiHome()
}
-// readEtcDefault parses /etc/default/ajean for AJEAN_HOME=. Quiet on errors.
+// readEtcDefault parses /etc/default/loki for LOKI_HOME=. Quiet on errors.
func readEtcDefault() string {
- b, err := os.ReadFile("/etc/default/ajean")
+ b, err := os.ReadFile("/etc/default/loki")
if err != nil {
return ""
}
@@ -215,30 +215,30 @@ func readEtcDefault() string {
continue
}
s = strings.TrimPrefix(s, "export ")
- if k, v, ok := strings.Cut(s, "="); ok && strings.TrimSpace(k) == "AJEAN_HOME" {
+ if k, v, ok := strings.Cut(s, "="); ok && strings.TrimSpace(k) == "LOKI_HOME" {
return strings.Trim(strings.TrimSpace(v), "\"'")
}
}
return ""
}
-// Arborescence de $AJEAN_HOME. Elle tient en six dossiers, et rien d'autre :
+// Arborescence de $LOKI_HOME. Elle tient en six dossiers, et rien d'autre :
// tout le reste (config, préférences, conversation, clés, drapeaux) vit dans la
-// base ajean.db — voir store.go.
-func backendsDir() string { return filepath.Join(AjeanHome(), "backends") }
-func binDir() string { return filepath.Join(AjeanHome(), "bin") }
-func presetsDir() string { return filepath.Join(AjeanHome(), "presets") }
-func memoryDir() string { return filepath.Join(AjeanHome(), "memory") }
-func modelsDir() string { return filepath.Join(AjeanHome(), "models") }
-func workspaceDir() string { return filepath.Join(AjeanHome(), "workspace") }
+// base loki.db — voir store.go.
+func backendsDir() string { return filepath.Join(LokiHome(), "backends") }
+func binDir() string { return filepath.Join(LokiHome(), "bin") }
+func presetsDir() string { return filepath.Join(LokiHome(), "presets") }
+func memoryDir() string { return filepath.Join(LokiHome(), "memory") }
+func modelsDir() string { return filepath.Join(LokiHome(), "models") }
+func workspaceDir() string { return filepath.Join(LokiHome(), "workspace") }
// serviceName est le nom de l'unité qui exécute llama-server. Son pendant est
-// uiUnitName (« ajean-ui »), qui sert l'interface et le tunnel.
+// uiUnitName (« loki-ui »), qui sert l'interface et le tunnel.
func serviceName() string {
- if n := os.Getenv("AJEAN_SERVICE"); n != "" {
+ if n := os.Getenv("LOKI_SERVICE"); n != "" {
return n
}
- return "ajean-engine"
+ return "loki-engine"
}
// Color helpers (ANSI). Disabled when stdout is not a TTY.
diff --git a/internal/ajean/store.go b/internal/loki/store.go
similarity index 97%
rename from internal/ajean/store.go
rename to internal/loki/store.go
index 9676291..4c6a91a 100644
--- a/internal/ajean/store.go
+++ b/internal/loki/store.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -13,7 +13,7 @@ import (
bolterrors "go.etcd.io/bbolt/errors"
)
-// store.go — l'unique endroit où AJEAN écrit son état.
+// store.go — l'unique endroit où Loki écrit son état.
//
// Avant, chaque réglage avait son fichier : config.env, webprefs.json,
// conversation.json, .api_key, .link_token, .agent_enabled, model_dirs.json…
@@ -40,7 +40,7 @@ const (
// bbolt pose un verrou EXCLUSIF sur son fichier tant qu'il est ouvert. Or une
// machine installée fait tourner en permanence le service de lien, qui sert le
// tunnel et l'UI : s'il gardait la base ouverte, plus une seule commande ne
-// fonctionnerait à côté — « ajean status », « ajean switch », « ajean edit »
+// fonctionnerait à côté — « loki status », « loki switch », « loki edit »
// échoueraient toutes sur un délai d'attente, sur la machine même où tout est
// censé marcher. On ouvre donc pour la durée d'une opération, puis on referme.
//
@@ -49,7 +49,7 @@ const (
// contention entre process ; dbMu la sérialise à l'intérieur du process.
var dbMu sync.Mutex
-func dbPath() string { return filepath.Join(AjeanHome(), "ajean.db") }
+func dbPath() string { return filepath.Join(LokiHome(), "loki.db") }
// withDB ouvre la base, exécute fn, puis referme — toujours, même en erreur.
func withDB(fn func(*bolt.DB) error) error {
diff --git a/internal/ajean/store_cache_test.go b/internal/loki/store_cache_test.go
similarity index 95%
rename from internal/ajean/store_cache_test.go
rename to internal/loki/store_cache_test.go
index 0d2ee52..1bb80a0 100644
--- a/internal/ajean/store_cache_test.go
+++ b/internal/loki/store_cache_test.go
@@ -1,9 +1,9 @@
-package ajean
+package loki
import "testing"
// Le cache de lecture ne doit jamais servir une valeur périmée après une
-// écriture de ce process : un « ajean edit » suivi d'un « ajean restart » doit
+// écriture de ce process : un « loki edit » suivi d'un « loki restart » doit
// démarrer sur la NOUVELLE configuration.
func TestCacheConfigInvalideParEcriture(t *testing.T) {
testHome(t)
diff --git a/internal/ajean/store_conc_test.go b/internal/loki/store_conc_test.go
similarity index 98%
rename from internal/ajean/store_conc_test.go
rename to internal/loki/store_conc_test.go
index 50eda6e..795bcf5 100644
--- a/internal/ajean/store_conc_test.go
+++ b/internal/loki/store_conc_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"sync"
diff --git a/internal/ajean/store_test.go b/internal/loki/store_test.go
similarity index 93%
rename from internal/ajean/store_test.go
rename to internal/loki/store_test.go
index 1ecd5d2..205b120 100644
--- a/internal/ajean/store_test.go
+++ b/internal/loki/store_test.go
@@ -1,14 +1,14 @@
-package ajean
+package loki
import "testing"
-// testHome donne au test un $AJEAN_HOME à lui, et neutralise le pare-feu : un
+// testHome donne au test un $LOKI_HOME à lui, et neutralise le pare-feu : un
// test ne doit jamais poser (ni retirer) une règle entrante sur la machine qui
// le fait tourner.
func testHome(t *testing.T) string {
t.Helper()
home := t.TempDir()
- t.Setenv("AJEAN_HOME", home)
+ t.Setenv("LOKI_HOME", home)
firewallInert = true
t.Cleanup(func() { firewallInert = false })
return home
diff --git a/internal/ajean/sys_brand_icon.go b/internal/loki/sys_brand_icon.go
similarity index 96%
rename from internal/ajean/sys_brand_icon.go
rename to internal/loki/sys_brand_icon.go
index f38e81d..0f75171 100644
--- a/internal/ajean/sys_brand_icon.go
+++ b/internal/loki/sys_brand_icon.go
@@ -1,6 +1,6 @@
-package ajean
+package loki
-// sys_brand_icon.go — l'icône de la marque AJEAN, rendue à la volée.
+// sys_brand_icon.go — l'icône de la marque Loki, rendue à la volée.
//
// RÉPLIQUE EXACTE du favicon de l'UI web : carré à coins arrondis NOIR + « j »
// blanc (rects (6,3) (6,5) (4,7) sur une grille 12x12). Une seule source pour
@@ -9,7 +9,7 @@ package ajean
// au noir en laissant les icônes système en bleu.
//
// Aucun asset binaire à committer : tout est dessiné ici, et l'icône du .exe est
-// produite par `go generate ./cmd/ajean` (voir tools/gen-icon).
+// produite par `go generate ./cmd/loki` (voir tools/gen-icon).
import (
"bytes"
diff --git a/internal/ajean/sys_console_darwin.go b/internal/loki/sys_console_darwin.go
similarity index 78%
rename from internal/ajean/sys_console_darwin.go
rename to internal/loki/sys_console_darwin.go
index f551ff5..45ea050 100644
--- a/internal/ajean/sys_console_darwin.go
+++ b/internal/loki/sys_console_darwin.go
@@ -1,16 +1,16 @@
//go:build darwin
-package ajean
+package loki
// sys_console_darwin.go — détection du lancement « par clic » sous macOS.
//
// Sur macOS il n'y a pas d'équivalent d'AttachConsole : un binaire lancé depuis
// le Finder hérite quand même de descripteurs stdout/stderr (vers les logs
// système). Le signal fiable, c'est l'emplacement de l'exécutable : la release
-// publie un bundle AJEAN.app dont le binaire vit dans
-// AJEAN.app/Contents/MacOS/ajean. Si on tourne depuis là, c'est un double-clic
+// publie un bundle Loki.app dont le binaire vit dans
+// Loki.app/Contents/MacOS/loki. Si on tourne depuis là, c'est un double-clic
// dans le Finder → expérience « application » (UI web + navigateur), exactement
-// comme le double-clic sur ajean.exe sous Windows.
+// comme le double-clic sur loki.exe sous Windows.
//
// Le même binaire copié dans /usr/local/bin garde évidemment le comportement CLI.
@@ -28,7 +28,7 @@ func setupConsole() bool {
if p, err := filepath.EvalSymlinks(exe); err == nil {
exe = p
}
- // .../AJEAN.app/Contents/MacOS/ajean
+ // .../Loki.app/Contents/MacOS/loki
if strings.HasSuffix(filepath.Dir(exe), "/Contents/MacOS") {
fixFinderPath()
return false
@@ -36,7 +36,7 @@ func setupConsole() bool {
return true
}
-// appWarning signale une situation de lancement qui casse AJEAN de façon peu
+// appWarning signale une situation de lancement qui casse Loki de façon peu
// évidente. Aujourd'hui : l'App Translocation de Gatekeeper. Une app non signée
// ouverte depuis Téléchargements est exécutée depuis une copie en LECTURE SEULE
// sous /private/var/folders/…/AppTranslocation//, à un chemin différent à
@@ -44,7 +44,7 @@ func setupConsole() bool {
// dessus (la seconde trouve le port 8090 occupé et se contente d'ouvrir l'UI de
// la première, souvent une version périmée), des process fantômes issus de
// copies précédentes, et une mise à jour en place impossible.
-// Le remède est côté utilisateur : déplacer AJEAN.app dans /Applications.
+// Le remède est côté utilisateur : déplacer Loki.app dans /Applications.
func appWarning() string {
exe, err := os.Executable()
if err != nil {
@@ -53,14 +53,14 @@ func appWarning() string {
if !strings.Contains(exe, "/AppTranslocation/") {
return ""
}
- return "AJEAN tourne depuis une copie temporaire en lecture seule (App Translocation de macOS). " +
- "Fermez l'app, déplacez AJEAN.app dans le dossier Applications, puis rouvrez-la — " +
+ return "Loki tourne depuis une copie temporaire en lecture seule (App Translocation de macOS). " +
+ "Fermez l'app, déplacez Loki.app dans le dossier Applications, puis rouvrez-la — " +
"sinon chaque lancement crée une instance séparée et les mises à jour du moteur échouent."
}
// fixFinderPath complète le PATH famélique hérité du Finder. Une app lancée par
// LaunchServices reçoit /usr/bin:/bin:/usr/sbin:/sbin — pas les dossiers de
-// Homebrew : sans ça, AJEAN ne voit ni `brew`, ni `cmake`, ni `git`, et la
+// Homebrew : sans ça, Loki ne voit ni `brew`, ni `cmake`, ni `git`, et la
// compilation d'un backend échoue alors que les outils sont bien installés.
func fixFinderPath() {
extra := []string{
diff --git a/internal/ajean/sys_console_other.go b/internal/loki/sys_console_other.go
similarity index 76%
rename from internal/ajean/sys_console_other.go
rename to internal/loki/sys_console_other.go
index 1cb9988..fcd9b80 100644
--- a/internal/ajean/sys_console_other.go
+++ b/internal/loki/sys_console_other.go
@@ -1,12 +1,12 @@
//go:build !windows && !darwin
-package ajean
+package loki
// setupConsole : sous Linux, le process a toujours une vraie console/tty
// standard (stdout hérité). Rien à faire → on signale simplement « on a une
// console ». Les cas « lancé par un clic » sont traités par
-// sys_console_windows.go (double-clic sur ajean.exe) et sys_console_darwin.go
-// (ouverture de AJEAN.app depuis le Finder).
+// sys_console_windows.go (double-clic sur loki.exe) et sys_console_darwin.go
+// (ouverture de Loki.app depuis le Finder).
func setupConsole() bool { return true }
// appWarning : rien à signaler hors macOS (l'App Translocation est spécifique à
diff --git a/internal/ajean/sys_console_windows.go b/internal/loki/sys_console_windows.go
similarity index 92%
rename from internal/ajean/sys_console_windows.go
rename to internal/loki/sys_console_windows.go
index 8921f7f..2339c44 100644
--- a/internal/ajean/sys_console_windows.go
+++ b/internal/loki/sys_console_windows.go
@@ -1,19 +1,19 @@
//go:build windows
-package ajean
+package loki
// sys_console_windows.go — gestion de la console sous Windows.
//
// Le binaire est compilé en sous-système CONSOLE. C'est ce qui fait que `cmd`
// ATTEND la fin du programme : avec un binaire graphique, il rendait la main
-// aussitôt et réaffichait son invite AVANT la sortie d'AJEAN, ce qui donnait
+// aussitôt et réaffichait son invite AVANT la sortie d'Loki, ce qui donnait
//
-// C:\Users\Nathan>ajean
+// C:\Users\Nathan>loki
//
-// C:\Users\Nathan>ajean 0.8.0 — manager llama.cpp + UI web
+// C:\Users\Nathan>loki 0.8.0 — manager llama.cpp + UI web
//
-// La même cause cassait la redirection et les tubes, et rendait `ajean chat`
-// inutilisable depuis un terminal (le shell et AJEAN se disputaient l'entrée).
+// La même cause cassait la redirection et les tubes, et rendait `loki chat`
+// inutilisable depuis un terminal (le shell et Loki se disputaient l'entrée).
//
// EN CONTREPARTIE, Windows alloue une console à tout lancement sans terminal —
// un double-clic. On la referme alors immédiatement : voir setupConsole. Les
diff --git a/internal/ajean/sys_datadir.go b/internal/loki/sys_datadir.go
similarity index 79%
rename from internal/ajean/sys_datadir.go
rename to internal/loki/sys_datadir.go
index 0d3f0ff..c5e51a6 100644
--- a/internal/ajean/sys_datadir.go
+++ b/internal/loki/sys_datadir.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -11,15 +11,15 @@ import (
// modèle de config.env : quatre copies qui divergeaient à la première
// modification. Il n'y en a plus qu'une.
-// dataDirs est l'arborescence complète de $AJEAN_HOME. Rien d'autre n'y est
-// créé : tout le reste vit dans ajean.db.
+// dataDirs est l'arborescence complète de $LOKI_HOME. Rien d'autre n'y est
+// créé : tout le reste vit dans loki.db.
func dataDirs() []string {
- return []string{AjeanHome(), backendsDir(), binDir(), presetsDir(), memoryDir(), modelsDir(), workspaceDir()}
+ return []string{LokiHome(), backendsDir(), binDir(), presetsDir(), memoryDir(), modelsDir(), workspaceDir()}
}
// defaultConfig est la configuration de départ d'une installation neuve. Les
// valeurs sont volontairement incomplètes (BIN et MODEL sont vides) : c'est
-// l'écran d'accueil, ou « ajean llamacpp install », qui les renseigne.
+// l'écran d'accueil, ou « loki llamacpp install », qui les renseigne.
func defaultConfig() map[string]string {
host := "0.0.0.0"
if runtime.GOOS == "windows" {
diff --git a/internal/ajean/sys_disk_other.go b/internal/loki/sys_disk_other.go
similarity index 98%
rename from internal/ajean/sys_disk_other.go
rename to internal/loki/sys_disk_other.go
index 604dd84..2bd914b 100644
--- a/internal/ajean/sys_disk_other.go
+++ b/internal/loki/sys_disk_other.go
@@ -1,6 +1,6 @@
//go:build !windows
-package ajean
+package loki
// sys_disk_other.go — espace libre d'un système de fichiers via statfs(2).
// Bavail (et pas Bfree) : les blocs réservés à root ne sont pas utilisables pour
diff --git a/internal/ajean/sys_disk_windows.go b/internal/loki/sys_disk_windows.go
similarity index 98%
rename from internal/ajean/sys_disk_windows.go
rename to internal/loki/sys_disk_windows.go
index 166bc32..872ab39 100644
--- a/internal/ajean/sys_disk_windows.go
+++ b/internal/loki/sys_disk_windows.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
// sys_disk_windows.go — espace libre d'un volume via GetDiskFreeSpaceExW. On
// veut l'espace réellement disponible pour l'utilisateur courant (quotas
diff --git a/internal/ajean/sys_firewall_other.go b/internal/loki/sys_firewall_other.go
similarity index 97%
rename from internal/ajean/sys_firewall_other.go
rename to internal/loki/sys_firewall_other.go
index 59def08..ec02068 100644
--- a/internal/ajean/sys_firewall_other.go
+++ b/internal/loki/sys_firewall_other.go
@@ -1,6 +1,6 @@
//go:build !windows
-package ajean
+package loki
// Linux et macOS n'ont pas de pare-feu entrant activé par défaut, et quand il y
// en a un (ufw, firewalld, pf) c'est une affaire d'administration système qu'un
diff --git a/internal/ajean/sys_firewall_windows.go b/internal/loki/sys_firewall_windows.go
similarity index 91%
rename from internal/ajean/sys_firewall_windows.go
rename to internal/loki/sys_firewall_windows.go
index 8857a30..7252826 100644
--- a/internal/ajean/sys_firewall_windows.go
+++ b/internal/loki/sys_firewall_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"fmt"
@@ -14,9 +14,9 @@ import (
// Sur une installation Windows fraîche, même avec HOST=0.0.0.0, le pare-feu
// bloque tout ce qui arrive du réseau : l'utilisateur voyait « ça écoute
// partout » et se prenait quand même un délai d'attente depuis son autre
-// machine. AJEAN pose donc la règle lui-même, quand il en a le droit.
+// machine. Loki pose donc la règle lui-même, quand il en a le droit.
//
-// `ajean install` ne réclame PAS les droits administrateur (voir
+// `loki install` ne réclame PAS les droits administrateur (voir
// sys_install_windows.go) : netsh échouera donc souvent. On ne fait pas semblant
// que ça a marché — firewallState relit la règle, et firewallManualHint donne la
// commande exacte à coller dans un terminal administrateur.
@@ -24,7 +24,7 @@ import (
// firewallRuleName identifie nos règles. Une par port : changer PORT dans la
// configuration ne doit pas laisser une règle ouverte sur l'ancien.
func firewallRuleName(port int) string {
- return "AJEAN moteur (port " + strconv.Itoa(port) + ")"
+ return "Loki moteur (port " + strconv.Itoa(port) + ")"
}
// netsh exécute netsh sans faire clignoter de console (hideCmd) et renvoie sa
@@ -71,7 +71,7 @@ func firewallState(port int) string {
}
// firewallManualHint : la commande à coller dans un terminal ADMINISTRATEUR
-// quand AJEAN n'a pas pu poser la règle lui-même.
+// quand Loki n'a pas pu poser la règle lui-même.
func firewallManualHint(port int) string {
return fmt.Sprintf("le pare-feu Windows bloque encore le port %d. Ouvre un terminal ADMINISTRATEUR et lance :\n"+
` netsh advfirewall firewall add rule name="%s" dir=in action=allow protocol=TCP localport=%d profile=private,domain`,
diff --git a/internal/ajean/sys_firstrun_other.go b/internal/loki/sys_firstrun_other.go
similarity index 79%
rename from internal/ajean/sys_firstrun_other.go
rename to internal/loki/sys_firstrun_other.go
index 51df785..490c5e0 100644
--- a/internal/ajean/sys_firstrun_other.go
+++ b/internal/loki/sys_firstrun_other.go
@@ -1,8 +1,8 @@
//go:build !windows
-package ajean
+package loki
-// appFirstRun : hors Windows, `ajean app` est lancé depuis un terminal par
+// appFirstRun : hors Windows, `loki app` est lancé depuis un terminal par
// quelqu'un qui a déjà suivi l'installation — pas de double-clic à désambiguïser,
// donc pas de boîte de dialogue. On se contente de provisionner les données au
// premier lancement. Ne relance jamais (renvoie toujours false).
diff --git a/internal/ajean/sys_firstrun_windows.go b/internal/loki/sys_firstrun_windows.go
similarity index 89%
rename from internal/ajean/sys_firstrun_windows.go
rename to internal/loki/sys_firstrun_windows.go
index dbf32f0..95f6cf2 100644
--- a/internal/ajean/sys_firstrun_windows.go
+++ b/internal/loki/sys_firstrun_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"crypto/sha256"
@@ -22,8 +22,8 @@ import (
// Premier lancement sous Windows.
//
-// Avant : double-cliquer ajean-windows-amd64.exe déclenchait une installation
-// SILENCIEUSE (copie du binaire dans %ProgramData%\ajean\bin, ajout au PATH) dont
+// Avant : double-cliquer loki-windows-amd64.exe déclenchait une installation
+// SILENCIEUSE (copie du binaire dans %ProgramData%\loki\bin, ajout au PATH) dont
// l'utilisateur ne voyait rien — cmdApp appelait cmdInstall sans console pour en
// afficher la sortie. D'où la confusion légitime : est-ce que ça installe ou est-ce
// que ça lance ? Et surtout, deux copies du binaire coexistaient (celle du Bureau,
@@ -32,13 +32,13 @@ import (
//
// Maintenant, deux cas seulement :
//
-// 1. AJEAN n'est pas installé. On installe, sans demander : rester à
+// 1. Loki n'est pas installé. On installe, sans demander : rester à
// l'emplacement du fichier téléchargé ne donne pas une installation
// utilisable, la question n'avait donc qu'une réponse. Copie + PATH +
// raccourcis, un message qui dit ce qui a été fait, et on démarre depuis la
// copie installée.
//
-// 2. AJEAN est déjà installé. Le fichier téléchargé se comporte en installeur de
+// 2. Loki est déjà installé. Le fichier téléchargé se comporte en installeur de
// mise à jour, avec trois situations bien distinctes (voir runAsInstaller) :
// plus récent et application arrêtée, on remplace et on démarre, sans un mot ;
// plus récent mais application EN COURS, on demande s'il faut la fermer pour
@@ -93,7 +93,7 @@ func appFirstRun() bool {
if canonPath(exe) == canonPath(target) {
// On EST l'application installée. On en profite pour garantir que les
// raccourcis existent : sans ça, quelqu'un qui perd son raccourci ne
- // retrouve plus AJEAN, et n'a aucune raison de relancer le fichier
+ // retrouve plus Loki, et n'a aucune raison de relancer le fichier
// téléchargé (qu'il a souvent supprimé) pour le récupérer. En tâche de
// fond, l'appel PowerShell ne doit pas retarder le démarrage.
go ensureShortcuts(target)
@@ -114,18 +114,18 @@ func appFirstRun() bool {
// un fichier, autant dire où le programme s'est installé et qu'il peut
// supprimer ce qu'il a téléchargé.
if _, err := installSelf(filepath.Dir(target)); err != nil {
- // Échec le plus courant : pas les droits sur %ProgramData%. AJEAN reste
+ // Échec le plus courant : pas les droits sur %ProgramData%. Loki reste
// parfaitement utilisable depuis son emplacement actuel, le dossier de
// données étant ailleurs — on démarre donc au lieu d'abandonner.
- messageBox("Installation impossible :\n\n"+err.Error()+"\n\nAJEAN va démarrer depuis l'emplacement actuel.", "AJEAN", mbIconInfo)
+ messageBox("Installation impossible :\n\n"+err.Error()+"\n\nLoki va démarrer depuis l'emplacement actuel.", "Loki", mbIconInfo)
return false
}
_, _ = addToUserPath(filepath.Dir(target))
shortcuts := ensureShortcuts(target)
- messageBox("AJEAN est installé.\n\n"+target+"\n\n"+shortcuts+
+ messageBox("Loki est installé.\n\n"+target+"\n\n"+shortcuts+
"\n\nL'application va démarrer. Vous pouvez supprimer le fichier téléchargé.",
- "AJEAN", mbIconInfo)
+ "Loki", mbIconInfo)
return launch(target)
}
@@ -193,7 +193,7 @@ func launch(target string) bool {
return cmd.Start() == nil
}
-// runAsInstaller : AJEAN est déjà installé et on exécute une AUTRE copie (le
+// runAsInstaller : Loki est déjà installé et on exécute une AUTRE copie (le
// fichier fraîchement téléchargé). Le fichier joue alors le rôle d'installeur de
// mise à jour. Trois situations, trois comportements distincts, parce qu'elles
// n'appellent pas la même décision de l'utilisateur.
@@ -224,37 +224,37 @@ func runAsInstaller(target string) bool {
// serait une régression silencieuse, et démarrer sans rien dire laisserait
// croire qu'on utilise la version qu'on vient de télécharger.
if messageBox(
- "Une version plus récente d'AJEAN est déjà installée sur cet ordinateur.\n\n"+
+ "Une version plus récente d'Loki est déjà installée sur cet ordinateur.\n\n"+
" installée : "+installed+"\n"+
" ce fichier : "+Version+"\n\n"+
"Rien ne sera remplacé.\n\n"+
- "Voulez-vous démarrer AJEAN (version "+installed+") ?\n"+
+ "Voulez-vous démarrer Loki (version "+installed+") ?\n"+
"Répondre Non ferme simplement cette fenêtre.",
- "AJEAN", mbYesNo|mbIconQuestion) != idYes {
+ "Loki", mbYesNo|mbIconQuestion) != idYes {
return true // rien à faire, on quitte sans démarrer
}
ensureShortcuts(target)
return launch(target)
case cmp > 0 && len(running) > 0:
- // Mise à jour disponible, mais AJEAN tourne. Écrire le nouveau binaire
+ // Mise à jour disponible, mais Loki tourne. Écrire le nouveau binaire
// sans redémarrer ne changerait RIEN à ce que l'utilisateur a sous les
// yeux : l'onglet qui s'ouvrirait serait servi par l'ancienne version. On
// pose donc la seule question qui compte.
if messageBox(
- "AJEAN est déjà en cours d'exécution.\n\n"+
+ "Loki est déjà en cours d'exécution.\n\n"+
" version en cours : "+verLabel(installed)+"\n"+
" ce fichier : "+Version+" (plus récente)\n\n"+
- "Fermer AJEAN et le redémarrer pour appliquer la mise à jour ?\n\n"+
- "Répondre Non ouvre AJEAN dans sa version actuelle, sans rien mettre à jour.",
- "Mise à jour d'AJEAN", mbYesNo|mbIconQuestion) != idYes {
+ "Fermer Loki et le redémarrer pour appliquer la mise à jour ?\n\n"+
+ "Répondre Non ouvre Loki dans sa version actuelle, sans rien mettre à jour.",
+ "Mise à jour d'Loki", mbYesNo|mbIconQuestion) != idYes {
ensureShortcuts(target)
return launch(target) // l'instance en cours reprend la main (port occupé)
}
stopProcesses(running)
if err := replaceInstalled(target); err != nil {
- messageBox("La mise à jour a échoué :\n\n"+err.Error()+"\n\nAJEAN va redémarrer dans sa version actuelle.",
- "AJEAN", mbIconInfo)
+ messageBox("La mise à jour a échoué :\n\n"+err.Error()+"\n\nLoki va redémarrer dans sa version actuelle.",
+ "Loki", mbIconInfo)
}
ensureShortcuts(target)
return launch(target)
@@ -282,7 +282,7 @@ func verLabel(v string) string {
// replaceInstalled écrase le binaire installé par celui qu'on exécute. Le
// renommage préalable en .old permet de remplacer un exécutable encore ouvert
-// (même mécanique que replaceBinary pour `ajean update`).
+// (même mécanique que replaceBinary pour `loki update`).
func replaceInstalled(target string) error {
removeOldBinaries(target)
old, err := renameAside(target) // nom unique, cf. renameAside
@@ -298,7 +298,7 @@ func replaceInstalled(target string) error {
}
// runningPIDs liste les processus qui exécutent exactement ce fichier. On compare
-// le CHEMIN, pas le nom : tuer par nom d'image (« ajean.exe ») emporterait aussi
+// le CHEMIN, pas le nom : tuer par nom d'image (« loki.exe ») emporterait aussi
// le processus courant et toute autre copie sans rapport.
//
// La comparaison se fait ICI, sur des chemins canonisés, et non dans le script
@@ -375,10 +375,10 @@ func portBusy(port int) bool {
}
// binaryVersion lit la version dans les ressources du fichier (VS_VERSIONINFO,
-// posées par goversioninfo, cf cmd/ajean/versioninfo.json), comme le fait
+// posées par goversioninfo, cf cmd/loki/versioninfo.json), comme le fait
// l'onglet « Détails » des propriétés Windows.
//
-// ⚠️ NE PAS remplacer par un `ajean version` exécuté : le binaire est compilé en
+// ⚠️ NE PAS remplacer par un `loki version` exécuté : le binaire est compilé en
// sous-système GUI, il s'attache à la console du parent et n'écrit RIEN dans un
// tuyau. La sortie capturée est vide, donc la comparaison de versions échouerait
// toujours en silence et la mise à jour ne se ferait jamais (vérifié).
@@ -429,7 +429,7 @@ type vsFixedFileInfo struct {
FileDateLS uint32
}
-// ensureShortcuts garantit qu'un raccourci « AJEAN » existe dans le menu
+// ensureShortcuts garantit qu'un raccourci « Loki » existe dans le menu
// Démarrer et sur le Bureau, et qu'il pointe sur le bon exécutable. Appelée à
// CHAQUE lancement du fichier téléchargé, pas seulement à l'installation :
// un raccourci perdu (nettoyage, migration de profil, dossier Démarrer
@@ -448,9 +448,9 @@ $progs=Join-Path ([Environment]::GetFolderPath('StartMenu')) 'Programs'
foreach ($d in @($progs, [Environment]::GetFolderPath('Desktop'))) {
if (-not $d) { continue }
if (-not (Test-Path $d)) { continue }
- $lnk=Join-Path $d 'AJEAN.lnk'
+ $lnk=Join-Path $d 'Loki.lnk'
# Un raccourci existant est REPOINTÉ s'il vise autre chose que le binaire
- # canonique — typiquement l'ancien « ajean.exe », qui n'est plus qu'un alias.
+ # canonique — typiquement l'ancien « loki.exe », qui n'est plus qu'un alias.
# Le laisser en l'état condamnait l'utilisateur à relancer indéfiniment une
# version périmée par son propre raccourci.
if (Test-Path $lnk) {
@@ -476,9 +476,9 @@ foreach ($d in @($progs, [Environment]::GetFolderPath('Desktop'))) {
$s=$w.CreateShortcut($lnk)
$s.TargetPath=$t
$s.WorkingDirectory=(Split-Path $t)
- $s.Description='AJEAN, votre IA locale'
+ $s.Description='Loki, votre IA locale'
# Minimisé (7) : le binaire étant en sous-système console, Windows lui alloue
- # une console au lancement. AJEAN la referme aussitôt, mais demander un
+ # une console au lancement. Loki la referme aussitôt, mais demander un
# démarrage minimisé garantit qu'elle n'est jamais peinte à l'écran.
$s.WindowStyle=7
$s.IconLocation="$t,0"
@@ -490,9 +490,9 @@ Write-Output ($done -join ';')`, psQuote(target))
cmd := hideCmd(exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", ps))
out, err := cmd.CombinedOutput()
if err != nil || strings.TrimSpace(string(out)) == "" {
- return "(raccourcis non créés — lancez AJEAN depuis " + target + ")"
+ return "(raccourcis non créés — lancez Loki depuis " + target + ")"
}
- return "Raccourci « AJEAN » ajouté au menu Démarrer et au Bureau."
+ return "Raccourci « Loki » ajouté au menu Démarrer et au Bureau."
}
// removeShortcuts efface les raccourcis posés par ensureShortcuts. Renvoie true
@@ -503,7 +503,7 @@ func removeShortcuts() bool {
$sm=[Environment]::GetFolderPath('StartMenu')
foreach ($d in @($sm, (Join-Path $sm 'Programs'), [Environment]::GetFolderPath('Desktop'))) {
if (-not $d) { continue }
- $p=Join-Path $d 'AJEAN.lnk'
+ $p=Join-Path $d 'Loki.lnk'
if (Test-Path $p) { try { Remove-Item $p -Force; $n++ } catch {} }
}
Write-Output $n`
diff --git a/internal/ajean/sys_firstrun_windows_test.go b/internal/loki/sys_firstrun_windows_test.go
similarity index 89%
rename from internal/ajean/sys_firstrun_windows_test.go
rename to internal/loki/sys_firstrun_windows_test.go
index d4bb084..3150a7d 100644
--- a/internal/ajean/sys_firstrun_windows_test.go
+++ b/internal/loki/sys_firstrun_windows_test.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"os"
@@ -12,7 +12,7 @@ import (
// binaryVersion pilote la mise à jour automatique du binaire installé quand on
// lance le fichier téléchargé : si elle renvoie du vide, plus aucune mise à jour
// n'a lieu, et en silence. C'est exactement ce qui arrivait avec la première
-// implémentation (exécuter `ajean version`, dont la sortie est vide pour un
+// implémentation (exécuter `loki version`, dont la sortie est vide pour un
// binaire en sous-système GUI). D'où ces tests sur la plomberie syscall.
func TestBinaryVersionReadsResource(t *testing.T) {
// kernel32.dll porte toujours une ressource de version.
@@ -28,10 +28,10 @@ func TestBinaryVersionReadsResource(t *testing.T) {
// Sur une machine vierge, le dossier bin n'existe pas encore : installSelf doit
// le créer. Sans ça, le premier lancement échouait sur « open
-// C:\ProgramData\ajean\bin\ajean.exe: The system cannot find the path specified »
-// et AJEAN ne s'installait jamais.
+// C:\ProgramData\loki\bin\loki.exe: The system cannot find the path specified »
+// et Loki ne s'installait jamais.
func TestInstallSelfCreatesBinDir(t *testing.T) {
- binDir := filepath.Join(t.TempDir(), "ajean", "bin") // deux niveaux absents
+ binDir := filepath.Join(t.TempDir(), "loki", "bin") // deux niveaux absents
dst, err := installSelf(binDir)
if err != nil {
t.Fatalf("installSelf sur un dossier absent : %v", err)
@@ -45,7 +45,7 @@ func TestInstallSelfCreatesBinDir(t *testing.T) {
// cible existe déjà, et laisser l'ancien de côté sans le confondre avec la cible.
func TestReplaceInstalled(t *testing.T) {
dir := t.TempDir()
- target := filepath.Join(dir, "ajean.exe")
+ target := filepath.Join(dir, "loki.exe")
if err := os.WriteFile(target, []byte("ancienne version"), 0o755); err != nil {
t.Fatal(err)
}
@@ -81,7 +81,7 @@ func TestBinaryVersionWithoutResource(t *testing.T) {
}
}
-// Reproduit le cas signale en usage : l'alias « ajean.exe » est EN COURS
+// Reproduit le cas signale en usage : l'alias « loki.exe » est EN COURS
// d'execution au moment de la mise a jour. copyExe seul echouait, l'alias
// restait fige sur une version perimee, et tout raccourci le visant relancait
// indefiniment l'ancienne version — qui affichait « une version plus recente
@@ -89,7 +89,7 @@ func TestBinaryVersionWithoutResource(t *testing.T) {
func TestReplaceExeSurchargeUnFichierVerrouille(t *testing.T) {
dir := t.TempDir()
src := filepath.Join(dir, "neuf.exe")
- dst := filepath.Join(dir, "ajean.exe")
+ dst := filepath.Join(dir, "loki.exe")
if err := os.WriteFile(src, []byte("VERSION-NEUVE"), 0o755); err != nil {
t.Fatal(err)
}
@@ -120,7 +120,7 @@ func TestReplaceExeSurchargeUnFichierVerrouille(t *testing.T) {
// echoue apres le renommage, l'ancien fichier revient a sa place.
func TestReplaceExeRestaureSiLaCopieEchoue(t *testing.T) {
dir := t.TempDir()
- dst := filepath.Join(dir, "ajean.exe")
+ dst := filepath.Join(dir, "loki.exe")
if err := os.WriteFile(dst, []byte("a-preserver"), 0o755); err != nil {
t.Fatal(err)
}
diff --git a/internal/ajean/sys_install_darwin.go b/internal/loki/sys_install_darwin.go
similarity index 87%
rename from internal/ajean/sys_install_darwin.go
rename to internal/loki/sys_install_darwin.go
index a555121..74093dc 100644
--- a/internal/ajean/sys_install_darwin.go
+++ b/internal/loki/sys_install_darwin.go
@@ -1,6 +1,6 @@
//go:build darwin
-package ajean
+package loki
import (
"fmt"
@@ -17,7 +17,7 @@ import (
// Implémentation prudente, à valider sur une machine Apple.
// launchdPlistTemplate — champs : Label, binaire, argument, UserName,
-// WorkingDirectory, AJEAN_HOME, StandardOutPath, StandardErrorPath.
+// WorkingDirectory, LOKI_HOME, StandardOutPath, StandardErrorPath.
// KeepAlive/SuccessfulExit=false ≈ Restart=on-failure ; RunAtLoad relance au
// boot une fois chargé avec `-w`.
const launchdPlistTemplate = `
@@ -33,7 +33,7 @@ const launchdPlistTemplate = `
UserName%sWorkingDirectory%sEnvironmentVariables
- AJEAN_HOME%s
+ LOKI_HOME%sRunAtLoadKeepAliveSuccessfulExit
@@ -48,12 +48,12 @@ func unitArgs() map[string]string {
return map[string]string{serviceName(): "serve", uiUnitName: "web"}
}
-func installServices(targetUser, ajeanHome string) error {
+func installServices(targetUser, lokiHome string) error {
exe := installedExePath()
for name, arg := range unitArgs() {
- logPath := filepath.Join(ajeanHome, name+".log")
+ logPath := filepath.Join(lokiHome, name+".log")
plist := fmt.Sprintf(launchdPlistTemplate,
- launchdLabel(name), exe, arg, targetUser, ajeanHome, ajeanHome, logPath, logPath)
+ launchdLabel(name), exe, arg, targetUser, lokiHome, lokiHome, logPath, logPath)
path := launchdPlistPath(name)
if err := os.WriteFile(path, []byte(plist), 0o644); err != nil {
return err
diff --git a/internal/ajean/sys_install_linux.go b/internal/loki/sys_install_linux.go
similarity index 89%
rename from internal/ajean/sys_install_linux.go
rename to internal/loki/sys_install_linux.go
index 769f004..fb2a3d1 100644
--- a/internal/ajean/sys_install_linux.go
+++ b/internal/loki/sys_install_linux.go
@@ -1,6 +1,6 @@
//go:build linux
-package ajean
+package loki
import (
"fmt"
@@ -14,14 +14,14 @@ import (
// sudoersTemplate autorise l'utilisateur à piloter UNE unité sans mot de passe.
// Posé pour les deux : l'interface, qui tourne sans privilèges, doit pouvoir
// redémarrer le moteur (bascule de preset) comme elle-même (nouveau jeton).
-const sudoersTemplate = `# Permet à %[1]s de piloter l'unité %[2]s sans mot de passe (posé par ajean install).
+const sudoersTemplate = `# Permet à %[1]s de piloter l'unité %[2]s sans mot de passe (posé par loki install).
%[1]s ALL=(root) NOPASSWD: /bin/systemctl start %[2]s, /bin/systemctl stop %[2]s, /bin/systemctl restart %[2]s, /bin/systemctl enable %[2]s, /bin/systemctl disable %[2]s
`
// engineUnitTemplate — le moteur : exec llama-server, supervisé directement par
// systemd. Champs : User, WorkingDirectory, ExecStart.
const engineUnitTemplate = `[Unit]
-Description=AJEAN — moteur llama.cpp
+Description=Loki — moteur llama.cpp
After=network.target
[Service]
@@ -47,7 +47,7 @@ WantedBy=multi-user.target
// OpenAI, servis par un SEUL process (donc une seule conversation).
// Champs : unité du moteur (dépendance), User, WorkingDirectory, ExecStart.
const uiUnitTemplate = `[Unit]
-Description=AJEAN — interface web + accès distant
+Description=Loki — interface web + accès distant
After=network-online.target %s.service
Wants=network-online.target
@@ -63,11 +63,11 @@ RestartSec=5
WantedBy=multi-user.target
`
-func installServices(targetUser, ajeanHome string) error {
+func installServices(targetUser, lokiHome string) error {
svc, exe := serviceName(), installedExePath()
units := map[string]string{
- svc: fmt.Sprintf(engineUnitTemplate, targetUser, ajeanHome, exe+" serve"),
- uiUnitName: fmt.Sprintf(uiUnitTemplate, svc, targetUser, ajeanHome, exe+" web"),
+ svc: fmt.Sprintf(engineUnitTemplate, targetUser, lokiHome, exe+" serve"),
+ uiUnitName: fmt.Sprintf(uiUnitTemplate, svc, targetUser, lokiHome, exe+" web"),
}
for name, body := range units {
path := "/etc/systemd/system/" + name + ".service"
diff --git a/internal/ajean/sys_install_unix.go b/internal/loki/sys_install_unix.go
similarity index 73%
rename from internal/ajean/sys_install_unix.go
rename to internal/loki/sys_install_unix.go
index c65ed50..bc8ccac 100644
--- a/internal/ajean/sys_install_unix.go
+++ b/internal/loki/sys_install_unix.go
@@ -1,6 +1,6 @@
//go:build !windows
-package ajean
+package loki
import (
"fmt"
@@ -23,7 +23,7 @@ import (
func cmdInstall(args []string) error {
if os.Geteuid() != 0 {
- return fmt.Errorf("ajean install doit être exécuté en root (sudo ajean install)")
+ return fmt.Errorf("loki install doit être exécuté en root (sudo loki install)")
}
targetUser := os.Getenv("SUDO_USER")
if targetUser == "" {
@@ -38,19 +38,19 @@ func cmdInstall(args []string) error {
if err != nil {
return fmt.Errorf("utilisateur '%s' introuvable: %w", targetUser, err)
}
- ajeanHome := defaultAjeanHome()
- if v := os.Getenv("AJEAN_HOME"); v != "" {
- ajeanHome = v
+ lokiHome := defaultLokiHome()
+ if v := os.Getenv("LOKI_HOME"); v != "" {
+ lokiHome = v
}
fmt.Printf("Installation pour utilisateur %s\n", cyan(targetUser))
- fmt.Printf(" AJEAN_HOME = %s\n", ajeanHome)
+ fmt.Printf(" LOKI_HOME = %s\n", lokiHome)
fmt.Printf(" services = %s + %s\n", serviceName(), uiUnitName)
// 0. Reprise d'une installation 0.7, si c'en est une. AVANT provisionDataDir :
// elle déplace des dossiers entiers, donc la destination ne doit pas
// encore exister. Code temporaire, voir migrate_07.go.
- if err := migrateFrom07(ajeanHome); err != nil {
+ if err := migrateFrom07(lokiHome); err != nil {
return fmt.Errorf("reprise de l'installation 0.7 : %w", err)
}
@@ -60,45 +60,45 @@ func cmdInstall(args []string) error {
}
fmt.Printf(" %s arborescence prête\n", green("✓"))
- // 2. Lien vers /usr/local/bin/ajean.
+ // 2. Lien vers /usr/local/bin/loki.
if err := linkInstalledExe(); err != nil {
return err
}
- // 3. /etc/default/ajean, pour que les invocations root résolvent AJEAN_HOME.
+ // 3. /etc/default/loki, pour que les invocations root résolvent LOKI_HOME.
if err := os.MkdirAll("/etc/default", 0o755); err != nil {
return err
}
- defaults := fmt.Sprintf("# Généré par ajean install — racine des données\nAJEAN_HOME=%s\n", ajeanHome)
- if err := os.WriteFile("/etc/default/ajean", []byte(defaults), 0o644); err != nil {
+ defaults := fmt.Sprintf("# Généré par loki install — racine des données\nLOKI_HOME=%s\n", lokiHome)
+ if err := os.WriteFile("/etc/default/loki", []byte(defaults), 0o644); err != nil {
return err
}
- fmt.Printf(" %s /etc/default/ajean\n", green("✓"))
+ fmt.Printf(" %s /etc/default/loki\n", green("✓"))
// 4. Déclaration des services (systemd ou launchd selon la plateforme).
- if err := installServices(targetUser, ajeanHome); err != nil {
+ if err := installServices(targetUser, lokiHome); err != nil {
return err
}
- // 5. AJEAN_HOME appartient à l'utilisateur cible : les deux services et les
+ // 5. LOKI_HOME appartient à l'utilisateur cible : les deux services et les
// commandes qu'il tape doivent pouvoir écrire dans la base.
- chown(ajeanHome, u)
+ chown(lokiHome, u)
fmt.Println()
fmt.Printf("%s installation terminée.\n", green("[ok]"))
fmt.Printf("\nProchaines étapes :\n")
- fmt.Printf(" 1. installe le moteur : %s (compile llama.cpp, renseigne BIN)\n", bold("sudo -u "+targetUser+" ajean llamacpp install"))
- fmt.Printf(" 2. choisis le modèle : %s (renseigne MODEL, un fichier .gguf)\n", bold("sudo -u "+targetUser+" ajean edit"))
- fmt.Printf(" 3. démarre le moteur : %s\n", bold("sudo ajean start"))
- fmt.Printf(" 4. vérifie : %s\n", bold("ajean test"))
- fmt.Printf(" 5. démarre l'interface : %s (http://localhost:8090)\n", bold("sudo ajean ui start"))
+ fmt.Printf(" 1. installe le moteur : %s (compile llama.cpp, renseigne BIN)\n", bold("sudo -u "+targetUser+" loki llamacpp install"))
+ fmt.Printf(" 2. choisis le modèle : %s (renseigne MODEL, un fichier .gguf)\n", bold("sudo -u "+targetUser+" loki edit"))
+ fmt.Printf(" 3. démarre le moteur : %s\n", bold("sudo loki start"))
+ fmt.Printf(" 4. vérifie : %s\n", bold("loki test"))
+ fmt.Printf(" 5. démarre l'interface : %s (http://localhost:8090)\n", bold("sudo loki ui start"))
return nil
}
-// linkInstalledExe pose /usr/local/bin/ajean sur le binaire courant.
+// linkInstalledExe pose /usr/local/bin/loki sur le binaire courant.
//
// Garde-fou (issue #5) : si on tourne DÉJÀ depuis la cible — l'utilisateur a
-// posé le binaire là puis lancé `sudo ajean install` — il ne faut surtout pas
+// posé le binaire là puis lancé `sudo loki install` — il ne faut surtout pas
// Remove+Symlink sur soi-même : ça effacerait le vrai binaire et créerait un
// lien vers lui-même (« Too many levels of symbolic links »). On compare les
// chemins réels, résolus.
@@ -135,15 +135,15 @@ func linkInstalledExe() error {
func cmdUninstall(args []string) error {
if os.Geteuid() != 0 {
- return fmt.Errorf("ajean uninstall doit être exécuté en root")
+ return fmt.Errorf("loki uninstall doit être exécuté en root")
}
uninstallServices()
- for _, p := range []string{"/etc/default/ajean", installedExePath()} {
+ for _, p := range []string{"/etc/default/loki", installedExePath()} {
if err := os.Remove(p); err == nil {
fmt.Printf(" %s %s\n", green("✓"), p)
}
}
- fmt.Println(dim("(données utilisateur conservées — supprime $AJEAN_HOME manuellement si tu veux purger)"))
+ fmt.Println(dim("(données utilisateur conservées — supprime $LOKI_HOME manuellement si tu veux purger)"))
fmt.Println(green("[ok]") + " désinstallé")
return nil
}
diff --git a/internal/ajean/sys_install_windows.go b/internal/loki/sys_install_windows.go
similarity index 81%
rename from internal/ajean/sys_install_windows.go
rename to internal/loki/sys_install_windows.go
index 1ccd722..278e754 100644
--- a/internal/ajean/sys_install_windows.go
+++ b/internal/loki/sys_install_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"fmt"
@@ -12,21 +12,21 @@ import (
)
// On Windows there's no systemd unit, sudoers, or /usr/local/bin to populate.
-// `ajean install` simply provisions the data directory and a starter config; the
+// `loki install` simply provisions the data directory and a starter config; the
// service itself is managed by the PID-file supervisor in sys_service_windows.go
-// (ajean start / stop / status), which needs no admin rights.
+// (loki start / stop / status), which needs no admin rights.
func cmdInstall(args []string) error {
- ajeanHome := AjeanHome()
+ lokiHome := LokiHome()
fmt.Printf("Installation (Windows)\n")
- fmt.Printf(" AJEAN_HOME = %s\n", ajeanHome)
+ fmt.Printf(" LOKI_HOME = %s\n", lokiHome)
fmt.Printf(" service = %s\n", serviceName())
// 0. Reprise d'une installation 0.7, si c'en est une. AVANT provisionDataDir :
// elle déplace des dossiers entiers, donc la destination ne doit pas
// encore exister. Code temporaire, voir migrate_07.go.
- if err := migrateFrom07(ajeanHome); err != nil {
+ if err := migrateFrom07(lokiHome); err != nil {
return fmt.Errorf("reprise de l'installation 0.7 : %w", err)
}
@@ -37,8 +37,8 @@ func cmdInstall(args []string) error {
}
fmt.Printf(" %s arborescence prête\n", green("✓"))
- // 2. Le binaire va dans AJEAN_HOME\bin, ajouté au PATH utilisateur, pour que
- // `ajean` soit appelable depuis n'importe quel shell (pendant Windows du
+ // 2. Le binaire va dans LOKI_HOME\bin, ajouté au PATH utilisateur, pour que
+ // `loki` soit appelable depuis n'importe quel shell (pendant Windows du
// lien /usr/local/bin créé par l'installateur Unix).
dir := binDir()
onPath := false
@@ -62,40 +62,40 @@ func cmdInstall(args []string) error {
fmt.Println()
fmt.Printf("%s installation terminée.\n", green("[ok]"))
fmt.Printf("\nProchaines étapes :\n")
- fmt.Printf(" 1. édite la config : %s (renseigne BIN, MODEL)\n", bold("ajean edit"))
- fmt.Printf(" 2. démarre le service: %s\n", bold("ajean start"))
- fmt.Printf(" 3. UI web : %s\n", bold("ajean web"))
+ fmt.Printf(" 1. édite la config : %s (renseigne BIN, MODEL)\n", bold("loki edit"))
+ fmt.Printf(" 2. démarre le service: %s\n", bold("loki start"))
+ fmt.Printf(" 3. UI web : %s\n", bold("loki web"))
if onPath {
- fmt.Printf("\n%s ouvre un NOUVEAU terminal pour que 'ajean' soit reconnu (le PATH n'est lu qu'au démarrage du shell).\n", dim("[info]"))
+ fmt.Printf("\n%s ouvre un NOUVEAU terminal pour que 'loki' soit reconnu (le PATH n'est lu qu'au démarrage du shell).\n", dim("[info]"))
} else {
- fmt.Printf("\n%s pour exécuter 'ajean' depuis n'importe où, ajoute son dossier au PATH.\n", dim("[info]"))
+ fmt.Printf("\n%s pour exécuter 'loki' depuis n'importe où, ajoute son dossier au PATH.\n", dim("[info]"))
}
return nil
}
-// installSelf copie l'exécutable en cours dans binDir sous le nom ajean.exe et
+// installSelf copie l'exécutable en cours dans binDir sous le nom loki.exe et
// renvoie le chemin de destination. Sans effet si l'exe tourne déjà depuis là
// (ré-installation).
//
// binDir est créé ici, et pas seulement par l'appelant : sur une machine vierge,
// le premier lancement passait par appFirstRun, qui ne créait que le dossier de
// données et pas son sous-dossier bin. L'utilisateur recevait alors « open
-// C:\ProgramData\ajean\bin\ajean.exe: The system cannot find the path specified »
-// et AJEAN démarrait depuis le fichier téléchargé, sans jamais s'installer.
+// C:\ProgramData\loki\bin\loki.exe: The system cannot find the path specified »
+// et Loki démarrait depuis le fichier téléchargé, sans jamais s'installer.
func installSelf(binDir string) (string, error) {
src, err := os.Executable()
if err != nil {
return "", err
}
src, _ = filepath.EvalSymlinks(src)
- dst := filepath.Join(binDir, "ajean.exe")
+ dst := filepath.Join(binDir, "loki.exe")
if strings.EqualFold(src, dst) {
return dst, nil
}
if err := os.MkdirAll(binDir, 0o755); err != nil {
return "", err
}
- // replaceExe et non copyExe : ajean.exe peut etre en cours d'execution
+ // replaceExe et non copyExe : loki.exe peut etre en cours d'execution
// (service en tache de fond), auquel cas Windows refuse de l'ecraser.
if err := replaceExe(src, dst); err != nil {
return "", err
@@ -107,7 +107,7 @@ func installSelf(binDir string) (string, error) {
//
// Windows refuse d'écraser un .exe en cours d'exécution, mais accepte de le
// RENOMMER : on décale l'ancien puis on écrit le nouveau à sa place. Sans ça,
-// l'alias « ajean.exe » restait figé sur une version périmée dès qu'il tournait
+// l'alias « loki.exe » restait figé sur une version périmée dès qu'il tournait
// au moment de la mise à jour — et comme les raccourcis existants le visent
// encore, chaque lancement relançait l'ancienne version, qui constatait qu'une
// plus récente était installée et le disait. À chaque fois. Constaté en usage.
@@ -209,7 +209,7 @@ func cmdUninstall(args []string) error {
// Stop the background server if it's running.
_ = svcStop(false)
- // Pull AJEAN_HOME\bin off the user PATH (best-effort).
+ // Pull LOKI_HOME\bin off the user PATH (best-effort).
dir := binDir()
if removed, err := removeFromUserPath(dir); err == nil && removed {
fmt.Printf(" %s %s retiré du PATH utilisateur\n", green("✓"), dir)
@@ -217,15 +217,15 @@ func cmdUninstall(args []string) error {
// Raccourcis posés à l'installation (menu Démarrer + Bureau).
if removeShortcuts() {
- fmt.Printf(" %s raccourcis « AJEAN » supprimés\n", green("✓"))
+ fmt.Printf(" %s raccourcis « Loki » supprimés\n", green("✓"))
}
if !keepData {
- ajeanHome := AjeanHome()
- if err := os.RemoveAll(ajeanHome); err != nil {
- return fmt.Errorf("suppression de %s: %w", ajeanHome, err)
+ lokiHome := LokiHome()
+ if err := os.RemoveAll(lokiHome); err != nil {
+ return fmt.Errorf("suppression de %s: %w", lokiHome, err)
}
- fmt.Printf(" %s %s supprimé\n", green("✓"), ajeanHome)
+ fmt.Printf(" %s %s supprimé\n", green("✓"), lokiHome)
} else {
fmt.Println(dim("(données utilisateur conservées — relance avec --purge pour tout supprimer)"))
}
diff --git a/internal/ajean/sys_network.go b/internal/loki/sys_network.go
similarity index 91%
rename from internal/ajean/sys_network.go
rename to internal/loki/sys_network.go
index ac245ea..3bdfe55 100644
--- a/internal/ajean/sys_network.go
+++ b/internal/loki/sys_network.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -11,19 +11,19 @@ import (
//
// Le moteur écoute à l'adresse HOST. Sous Windows, l'installation pose
// HOST=127.0.0.1 : le chat du navigateur marche (il passe par le serveur web
-// d'AJEAN, sur la même machine), mais l'endpoint OpenAI :8080/v1 est INVISIBLE
+// d'Loki, sur la même machine), mais l'endpoint OpenAI :8080/v1 est INVISIBLE
// depuis le reste du réseau. Or c'est très exactement ce que les gens viennent
// chercher : brancher un logiciel tiers sur le modèle qui tourne dans le salon.
//
// Deux choses manquaient, et pas une seule :
// 1. HOST n'était réglable NULLE PART dans l'interface — il fallait connaître
-// « ajean edit » et savoir quoi y écrire.
+// « loki edit » et savoir quoi y écrire.
// 2. Même à 0.0.0.0, le pare-feu Windows bloque les connexions entrantes tant
-// qu'aucune règle n'autorise le port. AJEAN n'en posait aucune.
+// qu'aucune règle n'autorise le port. Loki n'en posait aucune.
//
// D'où un seul interrupteur qui fait les deux, et qui DIT ce qu'il n'a pas pu
// faire (poser une règle de pare-feu exige les droits administrateur, que
-// l'installation d'AJEAN ne réclame pas).
+// l'installation d'Loki ne réclame pas).
// firewallInert coupe tout pilotage du pare-feu. Positionné par les tests : une
// suite qui laisse une règle entrante derrière elle est un effet de bord
@@ -55,7 +55,7 @@ func lanExposed() bool {
return h != hostLocalOnly && h != "::1" && !strings.EqualFold(h, "localhost")
}
-// netStatus est ce que l'interface affiche, et ce que renvoie `ajean network`.
+// netStatus est ce que l'interface affiche, et ce que renvoie `loki network`.
type netStatus struct {
Exposed bool `json:"exposed"` // le moteur écoute-t-il sur le réseau
Host string `json:"host"` // adresse d'écoute effective
@@ -103,7 +103,7 @@ func setLANExposure(on bool) (netStatus, error) {
return networkStatus(), nil
}
-// cmdNetwork : `ajean network [on|off|status]`.
+// cmdNetwork : `loki network [on|off|status]`.
func cmdNetwork(args []string) error {
sub := "status"
if len(args) > 0 {
@@ -121,7 +121,7 @@ func cmdNetwork(args []string) error {
fmt.Printf("%s le moteur n'écoutera plus que sur cette machine\n", green("[ok]"))
}
printNetHint(st)
- fmt.Printf("%s redémarre le moteur pour appliquer : %s\n", dim("[info]"), bold("ajean restart"))
+ fmt.Printf("%s redémarre le moteur pour appliquer : %s\n", dim("[info]"), bold("loki restart"))
return nil
case "status", "":
st := networkStatus()
@@ -136,7 +136,7 @@ func cmdNetwork(args []string) error {
case "ferme":
fmt.Printf(" pare-feu : %s\n", yellow("aucune règle pour ce port"))
default:
- fmt.Printf(" pare-feu : %s\n", dim("non géré par AJEAN sur cette plateforme"))
+ fmt.Printf(" pare-feu : %s\n", dim("non géré par Loki sur cette plateforme"))
}
if st.Exposed {
fmt.Printf(" endpoint OpenAI : %s\n", bold(st.URL))
@@ -144,7 +144,7 @@ func cmdNetwork(args []string) error {
printNetHint(st)
return nil
}
- return fmt.Errorf("usage: ajean network [on|off|status]")
+ return fmt.Errorf("usage: loki network [on|off|status]")
}
func printNetHint(st netStatus) {
diff --git a/internal/ajean/sys_network_test.go b/internal/loki/sys_network_test.go
similarity index 99%
rename from internal/ajean/sys_network_test.go
rename to internal/loki/sys_network_test.go
index 8f1c1b6..98c4193 100644
--- a/internal/ajean/sys_network_test.go
+++ b/internal/loki/sys_network_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
diff --git a/internal/ajean/sys_paths.go b/internal/loki/sys_paths.go
similarity index 78%
rename from internal/ajean/sys_paths.go
rename to internal/loki/sys_paths.go
index 37c0194..829e4e7 100644
--- a/internal/ajean/sys_paths.go
+++ b/internal/loki/sys_paths.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"fmt"
@@ -13,11 +13,11 @@ import (
// emplacements invisibles : on ne sait plus si le .exe qu'on a double-cliqué EST
// l'application ou seulement un installateur, ni où atterrissent la config, les
// modèles et les fichiers que l'agent écrit. On centralise donc la réponse ici,
-// et on l'expose à la fois en ligne de commande (`ajean where`) et dans l'UI.
+// et on l'expose à la fois en ligne de commande (`loki where`) et dans l'UI.
-type ajeanPaths struct {
- Home string `json:"home"` // AJEAN_HOME : racine des données
- Database string `json:"database"` // ajean.db : config, préférences, conversation, clés
+type lokiPaths struct {
+ Home string `json:"home"` // LOKI_HOME : racine des données
+ Database string `json:"database"` // loki.db : config, préférences, conversation, clés
Exe string `json:"exe"` // binaire en cours d'exécution
Installed string `json:"installed"` // binaire installé (peut différer de Exe)
Workspace string `json:"workspace"` // dossier de travail du mode agent
@@ -28,23 +28,23 @@ type ajeanPaths struct {
}
// installedExePath est l'emplacement canonique du binaire après installation.
-// Il diffère par plateforme : sous Unix les installateurs posent /usr/local/bin/ajean
+// Il diffère par plateforme : sous Unix les installateurs posent /usr/local/bin/loki
// (c'est ce que référencent les unités systemd et le plist launchd) ; sous Windows,
-// faute d'équivalent, on utilise AJEAN_HOME\bin ajouté au PATH utilisateur.
+// faute d'équivalent, on utilise LOKI_HOME\bin ajouté au PATH utilisateur.
func installedExePath() string {
if runtime.GOOS == "windows" {
- return filepath.Join(binDir(), "ajean.exe")
+ return filepath.Join(binDir(), "loki.exe")
}
- return "/usr/local/bin/ajean"
+ return "/usr/local/bin/loki"
}
-func currentPaths() ajeanPaths {
+func currentPaths() lokiPaths {
exe, _ := os.Executable()
if resolved, err := filepath.EvalSymlinks(exe); err == nil {
exe = resolved
}
- return ajeanPaths{
- Home: AjeanHome(),
+ return lokiPaths{
+ Home: LokiHome(),
Database: dbPath(),
Exe: exe,
Installed: installedExePath(),
@@ -58,9 +58,9 @@ func currentPaths() ajeanPaths {
func cmdWhere(args []string) error {
p := currentPaths()
- fmt.Printf("Emplacements AJEAN\n\n")
+ fmt.Printf("Emplacements Loki\n\n")
for _, row := range [][2]string{
- {"données (AJEAN_HOME)", p.Home},
+ {"données (LOKI_HOME)", p.Home},
{"base", p.Database},
{"binaire en cours", p.Exe},
{"binaire installé", p.Installed},
diff --git a/internal/ajean/sys_platform_unix.go b/internal/loki/sys_platform_unix.go
similarity index 94%
rename from internal/ajean/sys_platform_unix.go
rename to internal/loki/sys_platform_unix.go
index 103f7d1..1e708da 100644
--- a/internal/ajean/sys_platform_unix.go
+++ b/internal/loki/sys_platform_unix.go
@@ -1,6 +1,6 @@
//go:build unix
-package ajean
+package loki
import (
"context"
@@ -16,22 +16,22 @@ import (
"time"
)
-// defaultAjeanHome est la racine des données quand ni $AJEAN_HOME ni
-// /etc/default/ajean n'en imposent une.
+// defaultLokiHome est la racine des données quand ni $LOKI_HOME ni
+// /etc/default/loki n'en imposent une.
//
-// Linux : /etc/ajean (machine de prod, service systemd lancé par root).
-// macOS : /etc/ajean n'est PAS écrivable par une app lancée depuis le Finder
-// (« mkdir /etc/ajean: permission denied »), et un Mac est une machine de bureau,
+// Linux : /etc/loki (machine de prod, service systemd lancé par root).
+// macOS : /etc/loki n'est PAS écrivable par une app lancée depuis le Finder
+// (« mkdir /etc/loki: permission denied »), et un Mac est une machine de bureau,
// pas un serveur — on retombe donc sur le dossier utilisateur standard, sauf si
-// un `sudo ajean install` a déjà créé /etc/ajean à notre nom.
-func defaultAjeanHome() string { return unixHome("ajean") }
+// un `sudo loki install` a déjà créé /etc/loki à notre nom.
+func defaultLokiHome() string { return unixHome("loki") }
func unixHome(name string) string {
if runtime.GOOS != "darwin" || os.Geteuid() == 0 {
return "/etc/" + name
}
if isWritableDir("/etc/" + name) {
- return "/etc/" + name // installé par `sudo ajean install` puis chown à l'utilisateur
+ return "/etc/" + name // installé par `sudo loki install` puis chown à l'utilisateur
}
if home, err := os.UserHomeDir(); err == nil {
return filepath.Join(home, "Library", "Application Support", name)
@@ -46,7 +46,7 @@ func isWritableDir(dir string) bool {
if err != nil || !fi.IsDir() {
return false
}
- probe := filepath.Join(dir, ".ajean-write-test")
+ probe := filepath.Join(dir, ".loki-write-test")
f, err := os.OpenFile(probe, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return false
@@ -56,7 +56,7 @@ func isWritableDir(dir string) bool {
return true
}
-// defaultEditor is used by `ajean edit` when $EDITOR is unset.
+// defaultEditor is used by `loki edit` when $EDITOR is unset.
func defaultEditor() string { return "nano" }
// hideCmd : no-op sur Unix (pas de fenêtre de console à masquer).
@@ -332,7 +332,7 @@ func ramUsageMB() (used, total int) {
// --- Supervision de processus détachés (worker de lien, service en mode
// utilisateur). Pendant Unix de sys_platform_windows.go.
-// spawnDetached prépare une commande qui survivra à la mort de AJEAN : Setsid la
+// spawnDetached prépare une commande qui survivra à la mort de Loki : Setsid la
// place dans une nouvelle session, donc elle n'est pas tuée avec notre groupe.
func spawnDetached(name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)
diff --git a/internal/ajean/sys_platform_windows.go b/internal/loki/sys_platform_windows.go
similarity index 95%
rename from internal/ajean/sys_platform_windows.go
rename to internal/loki/sys_platform_windows.go
index 64a55b9..1e392bc 100644
--- a/internal/ajean/sys_platform_windows.go
+++ b/internal/loki/sys_platform_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"context"
@@ -15,7 +15,7 @@ import (
)
// init makes the Windows console behave like a modern terminal: UTF-8 so the
-// Unicode glyphs ajean prints (✓ ▶ …) and child-process output don't turn into
+// Unicode glyphs loki prints (✓ ▶ …) and child-process output don't turn into
// mojibake (the default OEM codepage, e.g. cp850, renders "✓" as "├ö"), and VT
// processing so the ANSI colour/cursor escapes (including the build progress
// line) are interpreted instead of printed literally. Best-effort: a redirected
@@ -43,7 +43,7 @@ func init() {
// homeRoot is the parent directory of the data root: %ProgramData% (machine-wide,
// the closest analogue to /etc), falling back to %LOCALAPPDATA% for unprivileged
// setups. Ancien et nouveau dossier partagent ce parent, ce qui garantit que la
-// migration ajean → ajean est un rename intra-volume (voir sys_migrate.go).
+// migration loki → loki est un rename intra-volume (voir sys_migrate.go).
func homeRoot() string {
if pd := os.Getenv("ProgramData"); pd != "" {
return pd
@@ -54,10 +54,10 @@ func homeRoot() string {
return os.TempDir()
}
-// defaultAjeanHome est la racine des données quand $AJEAN_HOME est absent.
-func defaultAjeanHome() string { return filepath.Join(homeRoot(), "ajean") }
+// defaultLokiHome est la racine des données quand $LOKI_HOME est absent.
+func defaultLokiHome() string { return filepath.Join(homeRoot(), "loki") }
-// defaultEditor is used by `ajean edit` when $EDITOR is unset.
+// defaultEditor is used by `loki edit` when $EDITOR is unset.
func defaultEditor() string { return "notepad" }
// openBrowser ouvre l'URL dans le navigateur par défaut. rundll32 évite les
@@ -67,7 +67,7 @@ func openBrowser(url string) error {
}
// hideCmd empêche une commande externe d'ouvrir une fenêtre de console
-// (CREATE_NO_WINDOW). Indispensable quand AJEAN tourne sans console (mode app) :
+// (CREATE_NO_WINDOW). Indispensable quand Loki tourne sans console (mode app) :
// sinon chaque `nvidia-smi`/`git`/… ferait clignoter une fenêtre noire. Fusionne
// avec les flags déjà présents pour ne pas écraser un éventuel détachement.
func hideCmd(cmd *exec.Cmd) *exec.Cmd {
@@ -144,7 +144,7 @@ func libraryPathEnv(dir string) []string {
}
// execServer runs llama-server as a child process and waits for it. Windows has
-// no exec() that replaces the current image, so `ajean serve` stays alive as the
+// no exec() that replaces the current image, so `loki serve` stays alive as the
// parent (this is the detached process the service supervisor tracks).
// args[0] is the binary path; the rest are its arguments.
func execServer(bin string, args []string) error {
@@ -246,7 +246,7 @@ func msvcGenerator() string {
// ensureCompiler makes sure an MSVC C++ toolchain is available, installing the
// Visual Studio 2022 Build Tools (VCTools workload) via winget if not. This is a
-// large download but keeps `ajean llamacpp install` fully unattended on a bare
+// large download but keeps `loki llamacpp install` fully unattended on a bare
// Windows box.
func ensureCompiler() error {
if msvcInstallVersion() != "" {
@@ -368,7 +368,7 @@ Sans elle, CMake échoue sur « No CUDA toolset found ». Pour corriger, au choi
1. relance l'installeur du CUDA Toolkit (installation personnalisée) et coche « CUDA → Visual Studio Integration » — Visual Studio doit déjà être installé à ce moment-là ;
2. ou copie (en admin) les fichiers de
%s
- vers le dossier BuildCustomizations ci-dessus, puis relance ajean llamacpp install`, installPath, src)
+ vers le dossier BuildCustomizations ci-dessus, puis relance loki llamacpp install`, installPath, src)
}
// cudaPathEnv returns the CUDA toolkit env vars the MSBuild CUDA integration
@@ -386,7 +386,7 @@ func cudaPathEnv(toolkitDir string) []string {
}
// newShellCmd builds the command used by the run_shell tool. hideCmd évite un
-// flash de console à chaque commande d'agent quand AJEAN tourne en mode app.
+// flash de console à chaque commande d'agent quand Loki tourne en mode app.
func newShellCmd(ctx context.Context, command string) *exec.Cmd {
return hideCmd(exec.CommandContext(ctx, "cmd", "/C", command))
}
diff --git a/internal/ajean/sys_restart_other.go b/internal/loki/sys_restart_other.go
similarity index 96%
rename from internal/ajean/sys_restart_other.go
rename to internal/loki/sys_restart_other.go
index 7a134b6..b7052e8 100644
--- a/internal/ajean/sys_restart_other.go
+++ b/internal/loki/sys_restart_other.go
@@ -1,6 +1,6 @@
//go:build !windows
-package ajean
+package loki
// Pendant non-Windows de sys_restart_windows.go : sous Linux le redemarrage
// passe par systemd (restartAfterUpdate), qui sait deja relancer proprement un
diff --git a/internal/ajean/sys_restart_windows.go b/internal/loki/sys_restart_windows.go
similarity index 95%
rename from internal/ajean/sys_restart_windows.go
rename to internal/loki/sys_restart_windows.go
index 57f127d..6fa735b 100644
--- a/internal/ajean/sys_restart_windows.go
+++ b/internal/loki/sys_restart_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"fmt"
@@ -17,7 +17,7 @@ import (
// lancée avec une sous-commande interne — qui attend notre disparition, profite
// de cette fenêtre pour terminer la migration, puis relance l'application.
//
-// C'est cette fenêtre qui manquait. Une mise à jour en place laisse AJEAN
+// C'est cette fenêtre qui manquait. Une mise à jour en place laisse Loki
// tourner, et donc le dossier tenu : le renommage échouait avec « Accès refusé »,
// qui sous Windows ne distingue pas un verrou d'un manque de droits. D'où des
// postes qui restaient indéfiniment sur l'ancien nom.
@@ -44,7 +44,7 @@ func scheduleAppRestart() (bool, string) {
time.Sleep(1500 * time.Millisecond) // laisser la réponse atteindre le navigateur
os.Exit(0)
}()
- return true, "AJEAN redémarre — la page se reconnectera toute seule dans quelques secondes."
+ return true, "Loki redémarre — la page se reconnectera toute seule dans quelques secondes."
}
// cmdRestartAfterUpdate est exécuté par l'accompagnateur détaché.
diff --git a/internal/ajean/sys_service.go b/internal/loki/sys_service.go
similarity index 88%
rename from internal/ajean/sys_service.go
rename to internal/loki/sys_service.go
index 23e6511..9a8e80f 100644
--- a/internal/ajean/sys_service.go
+++ b/internal/loki/sys_service.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"fmt"
@@ -19,33 +19,33 @@ import (
// preflightEngine vérifie ce sans quoi le moteur ne PEUT pas démarrer, avant de
// lancer le service. Sinon llama-server sortait en erreur, systemd le relançait
-// toutes les 3 s, et `ajean start` affichait un « activating » rassurant pendant
-// que `ajean test` répondait « /health ne répond pas ». Le diagnostic n'était
+// toutes les 3 s, et `loki start` affichait un « activating » rassurant pendant
+// que `loki test` répondait « /health ne répond pas ». Le diagnostic n'était
// visible que dans le journal.
func preflightEngine() error {
cfg := ReadConfig()
bin := strings.TrimSpace(cfg["BIN"])
if bin == "" {
return fmt.Errorf("BIN non défini — installe un moteur : %s (ou renseigne BIN avec %s)",
- bold("ajean llamacpp install"), bold("ajean edit"))
+ bold("loki llamacpp install"), bold("loki edit"))
}
if !filepath.IsAbs(bin) {
- bin = filepath.Join(AjeanHome(), bin)
+ bin = filepath.Join(LokiHome(), bin)
}
if _, err := os.Stat(prebuiltResolveBin(bin)); err != nil {
- return fmt.Errorf("moteur introuvable : %s — relance %s", bin, bold("ajean llamacpp install"))
+ return fmt.Errorf("moteur introuvable : %s — relance %s", bin, bold("loki llamacpp install"))
}
model := strings.TrimSpace(cfg["MODEL"])
if model == "" {
return fmt.Errorf("MODEL non défini — indique un .gguf avec %s (dossier des modèles : %s)",
- bold("ajean edit"), modelsDir())
+ bold("loki edit"), modelsDir())
}
p, err := resolveServeModelPath(model)
if err != nil {
return fmt.Errorf("MODEL=%s : %w", model, err)
}
if _, err := os.Stat(p); err != nil {
- return fmt.Errorf("modèle introuvable : %s — corrige MODEL avec %s", p, bold("ajean edit"))
+ return fmt.Errorf("modèle introuvable : %s — corrige MODEL avec %s", p, bold("loki edit"))
}
// Modèle en plusieurs fichiers : une tranche manquante ne se voit qu'au moment
// où llama-server réclame un tenseur absent, dans le journal du service.
@@ -56,13 +56,13 @@ func preflightEngine() error {
return nil
}
-// configTemplate est le squelette commenté proposé par `ajean edit` : la
+// configTemplate est le squelette commenté proposé par `loki edit` : la
// configuration vit en base, donc sur une installation neuve le fichier
// temporaire était QUASI VIDE — impossible de deviner quoi écrire. On y déroule
// donc les clés utiles avec leur rôle, les valeurs déjà définies telles quelles,
// les autres commentées.
var configTemplate = []struct{ key, help string }{
- {"BIN", "chemin de llama-server (posé par « ajean llamacpp install »)"},
+ {"BIN", "chemin de llama-server (posé par « loki llamacpp install »)"},
{"MODEL", "nom de fichier .gguf ou chemin complet"},
{"HOST", "adresse d'écoute du moteur (défaut 0.0.0.0)"},
{"PORT", "port du moteur (défaut 8080)"},
@@ -83,9 +83,9 @@ var configTemplate = []struct{ key, help string }{
// configEditorText rend la configuration au format présenté dans $EDITOR.
func configEditorText(cfg map[string]string) string {
var b strings.Builder
- b.WriteString("# Configuration du moteur AJEAN (ajean-engine).\n")
+ b.WriteString("# Configuration du moteur Loki (loki-engine).\n")
b.WriteString("# Une clé par ligne : CLE=valeur. Les lignes commentées (#) sont ignorées :\n")
- b.WriteString("# décommente celles dont tu as besoin. « ajean restart » applique.\n\n")
+ b.WriteString("# décommente celles dont tu as besoin. « loki restart » applique.\n\n")
seen := map[string]bool{}
for _, f := range configTemplate {
seen[f.key] = true
@@ -120,7 +120,7 @@ func editConfig() error {
if editor == "" {
editor = defaultEditor()
}
- tmp, err := os.CreateTemp("", "ajean-config-*.env")
+ tmp, err := os.CreateTemp("", "loki-config-*.env")
if err != nil {
return err
}
@@ -144,7 +144,7 @@ func editConfig() error {
if err := WriteConfig(parseEnv(string(b))); err != nil {
return err
}
- fmt.Println(dim("[info] ajean restart pour appliquer"))
+ fmt.Println(dim("[info] loki restart pour appliquer"))
return nil
}
diff --git a/internal/ajean/sys_service_darwin.go b/internal/loki/sys_service_darwin.go
similarity index 83%
rename from internal/ajean/sys_service_darwin.go
rename to internal/loki/sys_service_darwin.go
index 3a5f105..4ee0fbb 100644
--- a/internal/ajean/sys_service_darwin.go
+++ b/internal/loki/sys_service_darwin.go
@@ -1,6 +1,6 @@
//go:build darwin
-package ajean
+package loki
import (
"fmt"
@@ -18,16 +18,16 @@ import (
// le support macOS était absent (le code systemd/Linux était utilisé par erreur,
// cf. issue #4). Implémentation prudente basée sur launchctl load/unload/list.
-// launchdLabel dérive le label launchd du service (ex. "com.ajean.ajean"), en
-// conservant le préfixe hérité « com.ajean. » tant que c'est LUI qui est
+// launchdLabel dérive le label launchd du service (ex. "com.loki.loki"), en
+// conservant le préfixe hérité « com.loki. » tant que c'est LUI qui est
// réellement installé : le plist d'un daemon déjà chargé ne se renomme pas tout
// seul, et viser le mauvais label reviendrait à ne plus voir le service.
func launchdLabel(svc string) string {
- modern := "com.ajean." + svc
+ modern := "com.loki." + svc
if _, err := os.Stat("/Library/LaunchDaemons/" + modern + ".plist"); err == nil {
return modern
}
- if legacy := "com.ajean." + svc; fileExists("/Library/LaunchDaemons/" + legacy + ".plist") {
+ if legacy := "com.loki." + svc; fileExists("/Library/LaunchDaemons/" + legacy + ".plist") {
return legacy
}
return modern
@@ -44,18 +44,18 @@ func launchdPlistPath(svc string) string {
return "/Library/LaunchDaemons/" + launchdLabel(svc) + ".plist"
}
-// launchdLogPath : sortie standard/erreur du service, sous AJEAN_HOME (accessible
+// launchdLogPath : sortie standard/erreur du service, sous LOKI_HOME (accessible
// en écriture par l'utilisateur du service après le chown de l'installation).
-func launchdLogPath() string { return filepath.Join(AjeanHome(), serviceName()+".log") }
+func launchdLogPath() string { return filepath.Join(LokiHome(), serviceName()+".log") }
// serviceAction mappe start/stop/restart/enable/disable sur launchctl. `load -w`
// (re)active le service ET le rend persistant au boot ; `unload -w` le désactive.
func serviceAction(action string) error {
svc := serviceName()
plist := launchdPlistPath(svc)
- // Pas de LaunchDaemon installé — cas normal d'un Mac de bureau, où AJEAN tourne
- // via AJEAN.app sans jamais passer par `sudo ajean install` : on gère alors le
- // service comme sous Windows, avec un `ajean serve` détaché suivi par un fichier
+ // Pas de LaunchDaemon installé — cas normal d'un Mac de bureau, où Loki tourne
+ // via Loki.app sans jamais passer par `sudo loki install` : on gère alors le
+ // service comme sous Windows, avec un `loki 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 {
@@ -116,7 +116,7 @@ func checkStarted(svc string) error {
fmt.Println(strings.Join(lines, "\n"))
}
fmt.Println("------------------------------------------------")
- fmt.Printf("→ ajean logs pour plus de détails\n→ ajean edit pour corriger config.env\n")
+ fmt.Printf("→ loki logs pour plus de détails\n→ loki edit pour corriger config.env\n")
return fmt.Errorf("service %s non démarré", svc)
}
@@ -143,14 +143,14 @@ func serviceIsActive() bool {
}
// ---------------------------------------------------------------------------
-// Mode utilisateur (sans launchd) — même approche que Windows : `ajean serve` est
-// lancé détaché, son PID va dans AJEAN_HOME/.pid et sa sortie dans
-// AJEAN_HOME/.log. Setsid le détache de notre session pour qu'il survive à
-// la fermeture de AJEAN.app, et permet de tuer tout le groupe (llama-server
+// Mode utilisateur (sans launchd) — même approche que Windows : `loki serve` est
+// lancé détaché, son PID va dans LOKI_HOME/.pid et sa sortie dans
+// LOKI_HOME/.log. Setsid le détache de notre session pour qu'il survive à
+// la fermeture de Loki.app, et permet de tuer tout le groupe (llama-server
// enfant compris) d'un seul signal.
-func pidFilePath() string { return filepath.Join(AjeanHome(), serviceName()+".pid") }
-func logFilePath() string { return filepath.Join(AjeanHome(), serviceName()+".log") }
+func pidFilePath() string { return filepath.Join(LokiHome(), serviceName()+".pid") }
+func logFilePath() string { return filepath.Join(LokiHome(), serviceName()+".log") }
func userSvcAction(action string) error {
switch action {
@@ -173,7 +173,7 @@ func userSvcAction(action string) error {
return nil
case "enable", "disable":
fmt.Printf("%s '%s' réclame un service système : lance %s pour installer le LaunchDaemon.\n",
- yellow("[info]"), action, bold("sudo ajean install"))
+ yellow("[info]"), action, bold("sudo loki install"))
return nil
}
return fmt.Errorf("action inconnue: %s", action)
@@ -188,7 +188,7 @@ func userSvcStart() error {
if err != nil {
return err
}
- if err := os.MkdirAll(AjeanHome(), 0o755); err != nil {
+ if err := os.MkdirAll(LokiHome(), 0o755); err != nil {
return err
}
logf, err := os.OpenFile(logFilePath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
@@ -200,12 +200,12 @@ func userSvcStart() error {
cmd := exec.Command(self, "serve")
cmd.Stdout, cmd.Stderr = logf, logf
// Même répertoire de travail que sous systemd/launchd : les chemins relatifs
- // de config.env (MODEL=…gguf) se résolvent depuis AJEAN_HOME, pas depuis « / »
+ // de config.env (MODEL=…gguf) se résolvent depuis LOKI_HOME, pas depuis « / »
// que nous hérite le Finder.
- cmd.Dir = AjeanHome()
+ cmd.Dir = LokiHome()
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
if err := cmd.Start(); err != nil {
- return fmt.Errorf("démarrage de 'ajean serve': %w", err)
+ return fmt.Errorf("démarrage de 'loki serve': %w", err)
}
pid := cmd.Process.Pid
if err := os.WriteFile(pidFilePath(), []byte(strconv.Itoa(pid)), 0o644); err != nil {
@@ -219,14 +219,14 @@ func userCheckStarted(pid int) error {
time.Sleep(2 * time.Second)
if processAlive(pid) {
fmt.Printf("%s %s: démarré (PID %d)\n", green("[ok]"), serviceName(), pid)
- fmt.Printf(" logs: %s (ajean logs pour suivre)\n", dim(logFilePath()))
+ fmt.Printf(" logs: %s (loki logs pour suivre)\n", dim(logFilePath()))
return nil
}
fmt.Printf("%s %s: le processus s'est arrêté — derniers logs :\n", red("[ERREUR]"), serviceName())
fmt.Println("------------------------------------------------")
fmt.Print(tailFile(logFilePath(), 20))
fmt.Println("------------------------------------------------")
- fmt.Printf("→ ajean logs pour plus de détails\n→ ajean edit pour corriger config.env\n")
+ fmt.Printf("→ loki logs pour plus de détails\n→ loki edit pour corriger config.env\n")
_ = os.Remove(pidFilePath())
return fmt.Errorf("service %s non démarré", serviceName())
}
diff --git a/internal/ajean/sys_service_linux.go b/internal/loki/sys_service_linux.go
similarity index 91%
rename from internal/ajean/sys_service_linux.go
rename to internal/loki/sys_service_linux.go
index dbc4c6a..25a2da9 100644
--- a/internal/ajean/sys_service_linux.go
+++ b/internal/loki/sys_service_linux.go
@@ -1,6 +1,6 @@
//go:build linux
-package ajean
+package loki
import (
"fmt"
@@ -65,9 +65,9 @@ func unitState(svc string) (active, sub string) {
//
// ⚠️ « activating » n'est PAS un succès : avec Restart=on-failure, un moteur qui
// meurt au démarrage repasse en boucle par activating (SubState auto-restart).
-// L'ancien test l'acceptait, et `ajean start` répondait « [ok] activating » sur
-// un service qui ne démarrerait jamais — d'où des rapports « ajean start dit ok
-// mais ajean test ne répond pas ». On distingue donc le SubState, et on laisse
+// L'ancien test l'acceptait, et `loki start` répondait « [ok] activating » sur
+// un service qui ne démarrerait jamais — d'où des rapports « loki start dit ok
+// mais loki test ne répond pas ». On distingue donc le SubState, et on laisse
// au moteur le temps de charger un gros modèle (il reste en activating, mais
// PAS en auto-restart).
func checkStarted(svc string) error {
@@ -86,7 +86,7 @@ func checkStarted(svc string) error {
if state == "activating" && sub != "auto-restart" {
// Chargement en cours (un gros .gguf prend des minutes) : légitime.
fmt.Printf("%s %s: démarrage en cours (chargement du modèle) — %s pour suivre\n",
- green("[ok]"), svc, bold("ajean logs"))
+ green("[ok]"), svc, bold("loki logs"))
return nil
}
what := state
@@ -98,7 +98,7 @@ func checkStarted(svc string) error {
logs, _ := exec.Command("journalctl", "-u", svc, "-n", "20", "--no-pager").Output()
fmt.Print(string(logs))
fmt.Println("------------------------------------------------")
- fmt.Printf("→ ajean logs pour plus de détails\n→ ajean edit pour corriger la configuration (BIN, MODEL…)\n")
+ fmt.Printf("→ loki logs pour plus de détails\n→ loki edit pour corriger la configuration (BIN, MODEL…)\n")
return fmt.Errorf("service %s non démarré", svc)
}
diff --git a/internal/ajean/sys_service_windows.go b/internal/loki/sys_service_windows.go
similarity index 86%
rename from internal/ajean/sys_service_windows.go
rename to internal/loki/sys_service_windows.go
index b725532..e8b1502 100644
--- a/internal/ajean/sys_service_windows.go
+++ b/internal/loki/sys_service_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package ajean
+package loki
import (
"fmt"
@@ -16,8 +16,8 @@ import (
)
// On Windows there is no systemd, so "the service" is a background copy of
-// `ajean serve` that this process launches detached. We track it with a PID file
-// and stream its output to a log file under AJEAN_HOME. This needs no admin
+// `loki serve` that this process launches detached. We track it with a PID file
+// and stream its output to a log file under LOKI_HOME. This needs no admin
// rights and no external tools beyond the always-present tasklist/taskkill.
const (
@@ -26,8 +26,8 @@ const (
createNoWindow = 0x08000000 // CREATE_NO_WINDOW (aucune console pour l'enfant)
)
-func pidFilePath() string { return filepath.Join(AjeanHome(), serviceName()+".pid") }
-func logFilePath() string { return filepath.Join(AjeanHome(), serviceName()+".log") }
+func pidFilePath() string { return filepath.Join(LokiHome(), serviceName()+".pid") }
+func logFilePath() string { return filepath.Join(LokiHome(), serviceName()+".log") }
func serviceAction(action string) error {
switch action {
@@ -59,7 +59,7 @@ func svcStart() error {
if err != nil {
return err
}
- if err := os.MkdirAll(AjeanHome(), 0o755); err != nil {
+ if err := os.MkdirAll(LokiHome(), 0o755); err != nil {
return err
}
logf, err := os.OpenFile(logFilePath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
@@ -71,15 +71,15 @@ func svcStart() error {
cmd := exec.Command(self, "serve")
cmd.Stdout = logf
cmd.Stderr = logf
- cmd.Dir = AjeanHome() // les chemins relatifs de config.env se résolvent depuis AJEAN_HOME
- // createNoWindow + HideWindow : le service enfant (`ajean serve`) ne doit JAMAIS
- // faire clignoter de console noire quand AJEAN est lancé en mode app (double-clic).
+ cmd.Dir = LokiHome() // les chemins relatifs de config.env se résolvent depuis LOKI_HOME
+ // createNoWindow + HideWindow : le service enfant (`loki serve`) ne doit JAMAIS
+ // faire clignoter de console noire quand Loki est lancé en mode app (double-clic).
cmd.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true,
CreationFlags: createNewProcessGroup | detachedProcess | createNoWindow,
}
if err := cmd.Start(); err != nil {
- return fmt.Errorf("démarrage de 'ajean serve': %w", err)
+ return fmt.Errorf("démarrage de 'loki serve': %w", err)
}
pid := cmd.Process.Pid
if err := os.WriteFile(pidFilePath(), []byte(strconv.Itoa(pid)), 0o644); err != nil {
@@ -94,14 +94,14 @@ func checkStarted(pid int) error {
time.Sleep(2 * time.Second)
if processAlive(pid) {
fmt.Printf("%s %s: démarré (PID %d)\n", green("[ok]"), serviceName(), pid)
- fmt.Printf(" logs: %s (ajean logs pour suivre)\n", dim(logFilePath()))
+ fmt.Printf(" logs: %s (loki logs pour suivre)\n", dim(logFilePath()))
return nil
}
fmt.Printf("%s %s: le processus s'est arrêté — derniers logs :\n", red("[ERREUR]"), serviceName())
fmt.Println("------------------------------------------------")
fmt.Print(tailFile(logFilePath(), 20))
fmt.Println("------------------------------------------------")
- fmt.Printf("→ ajean logs pour plus de détails\n→ ajean edit pour corriger config.env\n")
+ fmt.Printf("→ loki logs pour plus de détails\n→ loki edit pour corriger config.env\n")
_ = os.Remove(pidFilePath())
return fmt.Errorf("service %s non démarré", serviceName())
}
diff --git a/internal/ajean/sys_splash_other.go b/internal/loki/sys_splash_other.go
similarity index 94%
rename from internal/ajean/sys_splash_other.go
rename to internal/loki/sys_splash_other.go
index f91f5bc..8e8368a 100644
--- a/internal/ajean/sys_splash_other.go
+++ b/internal/loki/sys_splash_other.go
@@ -1,6 +1,6 @@
//go:build !windows
-package ajean
+package loki
// sys_splash_other.go — pas de fenêtre de démarrage hors Windows (no-op).
diff --git a/internal/ajean/sys_splash_windows.go b/internal/loki/sys_splash_windows.go
similarity index 97%
rename from internal/ajean/sys_splash_windows.go
rename to internal/loki/sys_splash_windows.go
index 9ef2e41..e7e1aad 100644
--- a/internal/ajean/sys_splash_windows.go
+++ b/internal/loki/sys_splash_windows.go
@@ -1,8 +1,8 @@
//go:build windows
-package ajean
+package loki
-// sys_splash_windows.go — écran de démarrage « Lancement de AJEAN… ».
+// sys_splash_windows.go — écran de démarrage « Lancement de Loki… ».
//
// Petite fenêtre sans bordure, à coins arrondis, aux couleurs de la marque
// (fond bleu #1f6feb, logo « j » blanc, texte blanc), affichée le temps que le
@@ -127,7 +127,7 @@ func (s *splash) close() {
}
func createSplashWindow() uintptr {
- className, _ := syscall.UTF16PtrFromString("AJEANSplash")
+ className, _ := syscall.UTF16PtrFromString("LokiSplash")
hInst, _, _ := pGetModuleHandleW.Call(0)
splashClassOnce.Do(func() {
@@ -154,7 +154,7 @@ func createSplashWindow() uintptr {
cy, _, _ := pGetSystemMetrics.Call(smCY)
x := (int(cx) - splashW) / 2
y := (int(cy) - splashH) / 2
- title, _ := syscall.UTF16PtrFromString("AJEAN")
+ title, _ := syscall.UTF16PtrFromString("Loki")
hwnd, _, _ := pCreateWindowExW.Call(
uintptr(exTopmost|exToolwindow),
@@ -243,7 +243,7 @@ func paintSplash(hdc uintptr) {
pSetTextColor.Call(hdc, colWhite)
textX := ox + logo + 26
- drawText(hdc, "AJEAN", -30, 700, rect{textX, 40, splashW - 20, 82})
+ drawText(hdc, "Loki", -30, 700, rect{textX, 40, splashW - 20, 82})
drawText(hdc, "Lancement en cours…", -17, 400, rect{textX, 84, splashW - 20, 118})
}
diff --git a/internal/ajean/sys_tray_darwin.go b/internal/loki/sys_tray_darwin.go
similarity index 79%
rename from internal/ajean/sys_tray_darwin.go
rename to internal/loki/sys_tray_darwin.go
index 4ffe57f..2b69504 100644
--- a/internal/ajean/sys_tray_darwin.go
+++ b/internal/loki/sys_tray_darwin.go
@@ -1,11 +1,11 @@
//go:build darwin
-package ajean
+package loki
-// sys_tray_darwin.go — icône de AJEAN dans la barre de menus macOS (en haut à
+// sys_tray_darwin.go — icône de Loki dans la barre de menus macOS (en haut à
// droite, à côté de l'horloge), pendant exact du system tray Windows : quand on
-// ouvre AJEAN.app, l'UI web s'ouvre dans le navigateur ET l'icône apparaît, avec
-// le menu « Ouvrir AJEAN » / « Quitter ».
+// ouvre Loki.app, l'UI web s'ouvre dans le navigateur ET l'icône apparaît, avec
+// le menu « Ouvrir Loki » / « Quitter ».
//
// Contrairement à Windows, systray passe ici par Cocoa → CGO obligatoire : les
// binaires macOS sont donc compilés sur un runner macOS (voir release.yml), et
@@ -26,10 +26,10 @@ func runTray(url string) {
// colore selon le thème. Indispensable depuis que la marque est noire —
// une icône noire « en dur » est illisible sur une barre de menus sombre.
systray.SetTemplateIcon(brandTemplatePNG(trayIconSize), BrandIconPNG(trayIconSize))
- systray.SetTooltip("AJEAN — votre IA locale")
- mOpen := systray.AddMenuItem("Ouvrir AJEAN", "Ouvrir l'interface")
+ systray.SetTooltip("Loki — votre IA locale")
+ mOpen := systray.AddMenuItem("Ouvrir Loki", "Ouvrir l'interface")
systray.AddSeparator()
- mQuit := systray.AddMenuItem("Quitter", "Arrêter AJEAN")
+ mQuit := systray.AddMenuItem("Quitter", "Arrêter Loki")
go func() {
for {
diff --git a/internal/ajean/sys_tray_other.go b/internal/loki/sys_tray_other.go
similarity index 97%
rename from internal/ajean/sys_tray_other.go
rename to internal/loki/sys_tray_other.go
index d1992c8..5fae370 100644
--- a/internal/ajean/sys_tray_other.go
+++ b/internal/loki/sys_tray_other.go
@@ -1,6 +1,6 @@
//go:build !windows && !darwin
-package ajean
+package loki
// sys_tray_other.go — pas d'icône de zone de notification sous Linux (serveur
// sans session graphique la plupart du temps). On se contente d'ouvrir l'UI dans
diff --git a/internal/ajean/sys_tray_windows.go b/internal/loki/sys_tray_windows.go
similarity index 78%
rename from internal/ajean/sys_tray_windows.go
rename to internal/loki/sys_tray_windows.go
index 0456d26..eae5fc6 100644
--- a/internal/ajean/sys_tray_windows.go
+++ b/internal/loki/sys_tray_windows.go
@@ -1,11 +1,11 @@
//go:build windows
-package ajean
+package loki
-// sys_tray_windows.go — icône de AJEAN dans la zone de notification Windows (system
-// tray). Quand on lance l'app (double-clic ou `ajean app`), l'UI web s'ouvre dans
+// sys_tray_windows.go — icône de Loki dans la zone de notification Windows (system
+// tray). Quand on lance l'app (double-clic ou `loki app`), l'UI web s'ouvre dans
// le navigateur ET une petite icône apparaît près de l'horloge : elle montre que
-// AJEAN tourne et offre un menu « Ouvrir AJEAN » / « Quitter ».
+// Loki tourne et offre un menu « Ouvrir Loki » / « Quitter ».
//
// getlantern/systray est pur Go sur Windows (Win32 via x/sys, aucun CGO) — le
// binaire reste statique. L'équivalent macOS vit dans sys_tray_darwin.go (Cocoa,
@@ -22,11 +22,11 @@ import (
func runTray(url string) {
systray.Run(func() {
systray.SetIcon(trayIcon())
- systray.SetTitle("AJEAN")
- systray.SetTooltip("AJEAN — votre IA locale")
- mOpen := systray.AddMenuItem("Ouvrir AJEAN", "Ouvrir l'interface")
+ systray.SetTitle("Loki")
+ systray.SetTooltip("Loki — votre IA locale")
+ mOpen := systray.AddMenuItem("Ouvrir Loki", "Ouvrir l'interface")
systray.AddSeparator()
- mQuit := systray.AddMenuItem("Quitter", "Arrêter AJEAN et décharger le modèle")
+ mQuit := systray.AddMenuItem("Quitter", "Arrêter Loki et décharger le modèle")
go func() {
for {
diff --git a/internal/ajean/sys_tty.go b/internal/loki/sys_tty.go
similarity index 94%
rename from internal/ajean/sys_tty.go
rename to internal/loki/sys_tty.go
index d3d6329..2839f7a 100644
--- a/internal/ajean/sys_tty.go
+++ b/internal/loki/sys_tty.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import "os"
diff --git a/internal/ajean/sys_update.go b/internal/loki/sys_update.go
similarity index 89%
rename from internal/ajean/sys_update.go
rename to internal/loki/sys_update.go
index aa7b8e0..7147ffb 100644
--- a/internal/ajean/sys_update.go
+++ b/internal/loki/sys_update.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"crypto/sha256"
@@ -18,12 +18,12 @@ import (
"golang.org/x/mod/semver"
)
-// ajean update — met à jour le binaire depuis les releases GitHub du projet.
+// loki update — met à jour le binaire depuis les releases GitHub du projet.
// Périmètre minimal : compare la version, télécharge l'asset correspondant à
// l'OS/arch courant, remplace le binaire en place, puis affiche quoi redémarrer.
// AUCUN redémarrage de service automatique (choix volontaire, plus sûr).
-const updateRepo = "nathaninline/ajean"
+const updateRepo = "R0m1k3/Loki"
type ghRelease struct {
TagName string `json:"tag_name"`
@@ -36,12 +36,12 @@ type ghRelease struct {
}
// updateAssetName est le nom de l'asset de release pour la plateforme courante :
-// ajean-linux, ajean-linux-arm, ajean-macos, ajean-macos-arm, ajean-windows.exe,
-// ajean-windows-arm.exe. Un seul nom publié, un seul nom cherché.
+// loki-linux, loki-linux-arm, loki-macos, loki-macos-arm, loki-windows.exe,
+// loki-windows-arm.exe. Un seul nom publié, un seul nom cherché.
//
// Ces noms sont volontairement lisibles — « macos » plutôt que « darwin », pas
// de « amd64 » pour le cas courant — et volontairement DIFFÉRENTS du schéma
-// ajean-- qu'utilisaient les versions 0.7 : leur mise à jour
+// loki-- qu'utilisaient les versions 0.7 : leur mise à jour
// automatique ne trouve donc aucun asset et échoue sans rien remplacer, au lieu
// d'installer un binaire 0.8 sur une machine encore agencée en 0.7 — ce qui
// laissait le service de lien en boucle d'échec. La 0.7 se met à jour à la main,
@@ -51,7 +51,7 @@ func updateAssetName() string { return assetNameFor(runtime.GOOS, runtime.GOARCH
// assetNameFor est séparée pour être vérifiable sur les six plateformes, et pas
// seulement sur celle qui exécute les tests.
func assetNameFor(goos, goarch string) string {
- name := "ajean-" + map[string]string{
+ name := "loki-" + map[string]string{
"darwin": "macos",
"linux": "linux",
"windows": "windows",
@@ -89,7 +89,7 @@ func ensureV(s string) string {
func fetchLatestRelease() (*ghRelease, error) {
req, _ := http.NewRequest("GET", "https://api.github.com/repos/"+updateRepo+"/releases/latest", nil)
req.Header.Set("Accept", "application/vnd.github+json")
- req.Header.Set("User-Agent", "ajean-update")
+ req.Header.Set("User-Agent", "loki-update")
resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req)
if err != nil {
return nil, err
@@ -114,7 +114,7 @@ type updateInfo struct {
}
// checkForUpdate interroge GitHub et compare à la version courante. Réutilisé
-// par `ajean update` (CLI) et par l'endpoint web /api/update.
+// par `loki update` (CLI) et par l'endpoint web /api/update.
func checkForUpdate() (updateInfo, error) {
info := updateInfo{Current: Version}
rel, err := fetchLatestRelease()
@@ -162,12 +162,12 @@ func applyUpdate() (string, error) {
}
// Contrôle des droits AVANT de télécharger : sans ça, l'échec survient sur le
// os.Create du fichier temporaire et l'utilisateur reçoit un « open
- // /usr/local/bin/.ajean-update.tmp: permission denied » incompréhensible,
- // notamment depuis le bouton de l'UI web (ajean web lancé sans sudo).
+ // /usr/local/bin/.loki-update.tmp: permission denied » incompréhensible,
+ // notamment depuis le bouton de l'UI web (loki web lancé sans sudo).
if err := checkUpdateWritable(exe); err != nil {
return "", err
}
- tmp := filepath.Join(filepath.Dir(exe), ".ajean-update.tmp")
+ tmp := filepath.Join(filepath.Dir(exe), ".loki-update.tmp")
if err := downloadTo(url, tmp); err != nil {
os.Remove(tmp)
@@ -202,16 +202,16 @@ func applyUpdate() (string, error) {
return strings.TrimPrefix(latest, "v"), nil
}
-// updatePermissionError formule le seul message utile quand ajean n'a pas les
+// updatePermissionError formule le seul message utile quand loki n'a pas les
// droits d'écrire son propre binaire : la commande exacte à lancer.
func updatePermissionError(exe string) error {
if runtime.GOOS == "windows" {
// Windows renvoie le MÊME code (5, accès refusé) pour « droits
// insuffisants » et pour « fichier utilisé par un processus » : on nomme
// les deux causes au lieu d'affirmer la mauvaise.
- return fmt.Errorf("impossible de remplacer %s : soit un autre AJEAN utilise ce fichier (ferme l'application et arrête le service, puis réessaie), soit les droits manquent (relance AJEAN en administrateur)", exe)
+ return fmt.Errorf("impossible de remplacer %s : soit un autre Loki utilise ce fichier (ferme l'application et arrête le service, puis réessaie), soit les droits manquent (relance Loki en administrateur)", exe)
}
- return fmt.Errorf("droits insuffisants pour remplacer %s (le binaire appartient à root) — lance la mise à jour en ligne de commande : sudo ajean update", exe)
+ return fmt.Errorf("droits insuffisants pour remplacer %s (le binaire appartient à root) — lance la mise à jour en ligne de commande : sudo loki update", exe)
}
// checkUpdateWritable vérifie qu'on peut écrire dans le dossier du binaire, en
@@ -219,7 +219,7 @@ func updatePermissionError(exe string) error {
// pas : root, groupes, ACL, montage en lecture seule…).
func checkUpdateWritable(exe string) error {
dir := filepath.Dir(exe)
- probe, err := os.CreateTemp(dir, ".ajean-update-probe-*")
+ probe, err := os.CreateTemp(dir, ".loki-update-probe-*")
if err != nil {
if os.IsPermission(err) {
return updatePermissionError(exe)
@@ -245,13 +245,13 @@ func cmdUpdate(args []string) error {
return fmt.Errorf("impossible de contacter GitHub : %w", err)
}
if !info.Available {
- fmt.Printf("ajean est déjà à jour (%s).\n", Version)
+ fmt.Printf("loki est déjà à jour (%s).\n", Version)
return nil
}
fmt.Printf("nouvelle version disponible : %s (actuelle : %s)\n", info.Latest, Version)
fmt.Printf(" %s\n", info.URL)
if checkOnly {
- fmt.Println("lance 'ajean update' pour l'installer.")
+ fmt.Println("lance 'loki update' pour l'installer.")
return nil
}
fmt.Printf("téléchargement de %s…\n", updateAssetName())
@@ -259,14 +259,14 @@ func cmdUpdate(args []string) error {
if err != nil {
return err
}
- fmt.Printf("✓ ajean mis à jour en %s\n", newVer)
+ fmt.Printf("✓ loki mis à jour en %s\n", newVer)
printRestartHint()
return nil
}
func downloadTo(url, dst string) error {
req, _ := http.NewRequest("GET", url, nil)
- req.Header.Set("User-Agent", "ajean-update")
+ req.Header.Set("User-Agent", "loki-update")
req.Header.Set("Accept", "application/octet-stream")
resp, err := (&http.Client{Timeout: 5 * time.Minute}).Do(req)
if err != nil {
@@ -303,7 +303,7 @@ func verifyChecksum(rel *ghRelease, assetName, path string) error {
return nil
}
req, _ := http.NewRequest("GET", sumsURL, nil)
- req.Header.Set("User-Agent", "ajean-update")
+ req.Header.Set("User-Agent", "loki-update")
resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req)
if err != nil {
return fmt.Errorf("téléchargement des sommes de contrôle : %w", err)
@@ -377,7 +377,7 @@ func replaceBinary(exe, tmp string) error {
// ENCORE depuis le .old d'une mise à jour précédente, ce fichier ne peut être ni
// supprimé ni écrasé. Le renommage échoue alors avec « Accès refusé » (errno 5),
// que os.IsPermission rapporte comme un problème de droits — d'où le message
-// « relance AJEAN en administrateur », un conseil FAUX : aucun privilège ne
+// « relance Loki en administrateur », un conseil FAUX : aucun privilège ne
// permet de remplacer l'image d'un exécutable en cours d'exécution. Reproduit
// puis vérifié corrigé sur banc d'essai, deux processus vivants sur les deux
// noms : avec un suffixe unique le renommage passe.
@@ -419,7 +419,7 @@ func cleanupOldBinary() {
removeOldBinaries(exe)
// L'alias herite laisse le meme reliquat quand il etait en cours d'execution
// au moment ou on l'a remplace (voir replaceExe).
- removeOldBinaries(filepath.Join(filepath.Dir(exe), "ajean.exe"))
+ removeOldBinaries(filepath.Join(filepath.Dir(exe), "loki.exe"))
}
// handleUpdateCheck (GET /api/update) : renvoie l'état de mise à jour pour l'UI.
@@ -434,7 +434,7 @@ func handleUpdateCheck(w http.ResponseWriter, r *http.Request) {
// handleUpdateApply (POST /api/update/apply) : télécharge et installe la dernière
// version. Sur un serveur Linux/systemd, relance ensuite AUTOMATIQUEMENT le service
-// d'UI (ajean-ui : il sert l'UI, le chat et le tunnel) pour appliquer la MAJ sans
+// d'UI (loki-ui : il sert l'UI, le chat et le tunnel) pour appliquer la MAJ sans
// avoir à SSH — voir restartAfterUpdate. Renvoie la version + le message de statut.
func handleUpdateApply(w http.ResponseWriter, r *http.Request) {
newVer, err := applyUpdate()
@@ -450,10 +450,10 @@ func handleUpdateApply(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, map[string]any{"ok": true, "version": newVer, "restart": msg, "restarting": restarting})
}
-// restartAfterUpdate relance le service d'UI (uiServiceName, « ajean-ui » : il sert
+// restartAfterUpdate relance le service d'UI (uiServiceName, « loki-ui » : il sert
// l'interface, le chat et le tunnel) juste après une MAJ déclenchée depuis l'UI, pour
// éviter le SSH manuel. Conditions : Linux/systemd + service actif. On NE touche PAS à
-// ajean-engine (llama-server) pour ne pas recharger le modèle (long et inattendu depuis
+// loki-engine (llama-server) pour ne pas recharger le modèle (long et inattendu depuis
// un bouton « mettre à jour »). Le redémarrage est DIFFÉRÉ (la réponse HTTP doit partir
// d'abord) et lancé en --no-block : systemd enregistre le job puis nous arrête/relance ;
// la clé E2E (.e2e_key) survit au restart, donc pas de ré-appairage et l'UI se reconnecte
@@ -474,7 +474,7 @@ func restartAfterUpdate() (bool, string) {
time.Sleep(1500 * time.Millisecond) // laisser la réponse HTTP atteindre le client
// Le service tourne souvent en User=nathan (pas root) : systemctl brut
// échouerait alors (polkit). On repli sur « sudo -n systemctl » comme
- // uiServiceCtl, les sudoers /etc/sudoers.d/ajean-ui autorisant le restart.
+ // uiServiceCtl, les sudoers /etc/sudoers.d/loki-ui autorisant le restart.
// --no-block : on enregistre le job puis on rend la main ; systemd exécute le
// stop/start même si ce process (et le client systemctl) sont tués entre-temps.
bin, args := "systemctl", []string{"--no-block", "restart", uiServiceName()}
@@ -488,7 +488,7 @@ func restartAfterUpdate() (bool, string) {
func restartHintText() string {
if runtime.GOOS == "windows" {
- return "Redémarre AJEAN (quitte puis relance) pour appliquer la mise à jour."
+ return "Redémarre Loki (quitte puis relance) pour appliquer la mise à jour."
}
return "Redémarre pour appliquer : sudo systemctl restart " + uiServiceName() +
" (ajoute " + serviceName() + " si la mise à jour touche le moteur)."
diff --git a/internal/ajean/sys_update_aside_test.go b/internal/loki/sys_update_aside_test.go
similarity index 89%
rename from internal/ajean/sys_update_aside_test.go
rename to internal/loki/sys_update_aside_test.go
index b40b81d..c21f4bb 100644
--- a/internal/ajean/sys_update_aside_test.go
+++ b/internal/loki/sys_update_aside_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"os"
@@ -10,11 +10,11 @@ import (
// Le remplacement du binaire doit fonctionner MÊME si un écartement précédent
// est toujours là et impossible à supprimer (processus encore vivant dessus).
// Avec un nom fixe « .old », le renommage échouait alors en « Accès refusé »,
-// que ajean traduisait par « droits insuffisants, relance en administrateur » :
+// que loki traduisait par « droits insuffisants, relance en administrateur » :
// un conseil faux, aucun privilège ne remplace une image en cours d'exécution.
func TestRenameAsideNeBloquePasSurUnAncienEcartement(t *testing.T) {
dir := t.TempDir()
- exe := filepath.Join(dir, "ajean.exe")
+ exe := filepath.Join(dir, "loki.exe")
if err := os.WriteFile(exe, []byte("v1"), 0o755); err != nil {
t.Fatal(err)
}
@@ -29,7 +29,7 @@ func TestRenameAsideNeBloquePasSurUnAncienEcartement(t *testing.T) {
if aside == exe+".old" {
t.Error("nom d'écartement fixe : c'est précisément ce qui bloquait")
}
- if !strings.HasPrefix(filepath.Base(aside), "ajean.exe.old-") {
+ if !strings.HasPrefix(filepath.Base(aside), "loki.exe.old-") {
t.Errorf("nom inattendu : %s", aside)
}
if _, err := os.Stat(exe); !os.IsNotExist(err) {
@@ -51,7 +51,7 @@ func TestRenameAsideNeBloquePasSurUnAncienEcartement(t *testing.T) {
// Le ménage doit ramasser l'ancien nom fixe ET les noms uniques.
func TestRemoveOldBinaries(t *testing.T) {
dir := t.TempDir()
- exe := filepath.Join(dir, "ajean.exe")
+ exe := filepath.Join(dir, "loki.exe")
for _, n := range []string{exe + ".old", exe + ".old-1", exe + ".old-2"} {
if err := os.WriteFile(n, []byte("x"), 0o644); err != nil {
t.Fatal(err)
diff --git a/internal/ajean/sys_update_asset_test.go b/internal/loki/sys_update_asset_test.go
similarity index 75%
rename from internal/ajean/sys_update_asset_test.go
rename to internal/loki/sys_update_asset_test.go
index ce87a7a..a00946a 100644
--- a/internal/ajean/sys_update_asset_test.go
+++ b/internal/loki/sys_update_asset_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"runtime"
@@ -22,7 +22,7 @@ func relWith(names ...string) *ghRelease {
// est bien celui qui servira à vérifier le SHA-256.
func TestPickAssetTrouveSaPlateforme(t *testing.T) {
want := updateAssetName()
- got, url, size := pickAsset(relWith("SHA256SUMS", "ajean-plan9-386", want))
+ got, url, size := pickAsset(relWith("SHA256SUMS", "loki-plan9-386", want))
if got != want {
t.Fatalf("got %q, attendu %q", got, want)
}
@@ -33,16 +33,16 @@ func TestPickAssetTrouveSaPlateforme(t *testing.T) {
// Aucun asset pour cette plateforme → pas d'URL, l'appelant doit pouvoir le voir.
func TestPickAssetSansCorrespondance(t *testing.T) {
- if _, url, _ := pickAsset(relWith("ajean-plan9-386", "SHA256SUMS")); url != "" {
+ if _, url, _ := pickAsset(relWith("loki-plan9-386", "SHA256SUMS")); url != "" {
t.Fatalf("URL inattendue: %q", url)
}
}
-// Le schéma de nommage ne doit PAS coïncider avec ajean-- : c'est
+// Le schéma de nommage ne doit PAS coïncider avec loki-- : c'est
// ce qui empêche la mise à jour automatique d'une 0.7 de s'installer sur une
// machine encore agencée en 0.7.
func TestNomDAssetDistinctDuSchema07(t *testing.T) {
- legacy := "ajean-" + runtime.GOOS + "-" + runtime.GOARCH
+ legacy := "loki-" + runtime.GOOS + "-" + runtime.GOARCH
if runtime.GOOS == "windows" {
legacy += ".exe"
}
@@ -54,12 +54,12 @@ func TestNomDAssetDistinctDuSchema07(t *testing.T) {
// Les six noms publiés par la release, vérifiés un par un.
func TestNomsDAssetParPlateforme(t *testing.T) {
for _, c := range []struct{ goos, goarch, want string }{
- {"linux", "amd64", "ajean-linux"},
- {"linux", "arm64", "ajean-linux-arm"},
- {"darwin", "amd64", "ajean-macos"},
- {"darwin", "arm64", "ajean-macos-arm"},
- {"windows", "amd64", "ajean-windows.exe"},
- {"windows", "arm64", "ajean-windows-arm.exe"},
+ {"linux", "amd64", "loki-linux"},
+ {"linux", "arm64", "loki-linux-arm"},
+ {"darwin", "amd64", "loki-macos"},
+ {"darwin", "arm64", "loki-macos-arm"},
+ {"windows", "amd64", "loki-windows.exe"},
+ {"windows", "arm64", "loki-windows-arm.exe"},
} {
if got := assetNameFor(c.goos, c.goarch); got != c.want {
t.Errorf("%s/%s → %q, attendu %q", c.goos, c.goarch, got, c.want)
diff --git a/internal/ajean/ui/index.html b/internal/loki/ui/index.html
similarity index 98%
rename from internal/ajean/ui/index.html
rename to internal/loki/ui/index.html
index 364c8dc..5645774 100644
--- a/internal/ajean/ui/index.html
+++ b/internal/loki/ui/index.html
@@ -1,5 +1,5 @@
-
+
@@ -22,7 +22,7 @@
garde `display-mode` en second pour les PWA Android/desktop à manifeste. -->
-
+
@@ -34,7 +34,7 @@
-
+
…
@@ -180,7 +180,7 @@
trois îlots pour un même réglage. -->
-
Adresse locale (réseau) — modèle ajean
+
Adresse locale (réseau) — modèle loki
@@ -216,7 +216,7 @@
Accès distant
-
ajean.link?Accédez à ce serveur AJEAN depuis n'importe où, sans ouvrir de port, à travers le relais ajean.link. Le trafic est chiffré de bout en bout : le relais ne voit rien. Abonnement 4,80 €/mois.
+
ajean.link?Accédez à ce serveur Loki depuis n'importe où, sans ouvrir de port, à travers le relais ajean.link. Le trafic est chiffré de bout en bout : le relais ne voit rien. Abonnement 4,80 €/mois.
Ouvrez l'accès distant à ce serveur en quelques clics, sans terminal.
@@ -291,7 +291,7 @@
Actions
@@ -570,13 +570,13 @@
-
+
Code d'appairage
——
- Sur le PC à piloter, installe ajean (ajean.link/download) puis lance — accès à distance, chiffré de bout en bout :
+ Sur le PC à piloter, installe loki (ajean.link/download) puis lance — accès à distance, chiffré de bout en bout :
Ou en réseau local (connexion directe) :
diff --git a/internal/ajean/ui/src/js/00-md.js b/internal/loki/ui/src/js/00-md.js
similarity index 96%
rename from internal/ajean/ui/src/js/00-md.js
rename to internal/loki/ui/src/js/00-md.js
index a8c52a2..5d738c5 100644
--- a/internal/ajean/ui/src/js/00-md.js
+++ b/internal/loki/ui/src/js/00-md.js
@@ -56,13 +56,13 @@ function toggleSide(){ document.getElementById('side').classList.toggle('open');
let _sysT = null;
function saveSys(){
const v = document.getElementById('sysprompt').value;
- localStorage.setItem('ajean.sys', v);
+ localStorage.setItem('loki.sys', v);
clearTimeout(_sysT);
_sysT = setTimeout(()=>{ jpost('/api/sysprompt', {text:v}).catch(()=>{}); }, 600);
}
async function loadSys(){
try{
const d = await jget('/api/sysprompt');
- if(d && d.ok){ document.getElementById('sysprompt').value = d.text || ''; localStorage.setItem('ajean.sys', d.text || ''); }
+ if(d && d.ok){ document.getElementById('sysprompt').value = d.text || ''; localStorage.setItem('loki.sys', d.text || ''); }
}catch(e){}
}
diff --git a/internal/ajean/ui/src/js/01-theme.js b/internal/loki/ui/src/js/01-theme.js
similarity index 85%
rename from internal/ajean/ui/src/js/01-theme.js
rename to internal/loki/ui/src/js/01-theme.js
index 90bf3b1..ce29184 100644
--- a/internal/ajean/ui/src/js/01-theme.js
+++ b/internal/loki/ui/src/js/01-theme.js
@@ -5,11 +5,11 @@
// être appliquée sans clignotement au chargement, puis alignée sur le serveur
// par loadPrefs() — voir 02-prefs.js.
function applyTheme(id){
- // Tolère les anciens identifiants (ajean, gpt-dark, soft…) : seule compte
+ // Tolère les anciens identifiants (loki, gpt-dark, soft…) : seule compte
// désormais la variante, déduite du suffixe.
const dark = id==='dark' || (typeof id==='string' && id.endsWith('-dark'));
document.documentElement.setAttribute('data-theme', dark?'dark':'light');
- try{ localStorage.setItem('ajean-theme', dark?'dark':'light'); }catch(e){}
+ try{ localStorage.setItem('loki-theme', dark?'dark':'light'); }catch(e){}
const sw=document.getElementById('theme-dark'); if(sw) sw.checked=dark;
}
function toggleThemeDark(){
@@ -17,7 +17,7 @@ function toggleThemeDark(){
savePrefs();
}
function initTheme(){
- let id='light'; try{ id=localStorage.getItem('ajean-theme')||'light'; }catch(e){}
+ let id='light'; try{ id=localStorage.getItem('loki-theme')||'light'; }catch(e){}
applyTheme(id);
}
// ===== Affichage ============================================================
@@ -34,7 +34,7 @@ const VIEW_OPTS=[
function viewOn(id){ return document.documentElement.getAttribute('data-'+id)==='1'; }
function applyView(id, on){
document.documentElement.setAttribute('data-'+id, on?'1':'0');
- try{ localStorage.setItem('ajean-'+id, on?'1':'0'); }catch(e){}
+ try{ localStorage.setItem('loki-'+id, on?'1':'0'); }catch(e){}
const box=document.getElementById('view-'+id); if(box) box.checked=!!on;
// La barre latérale peut rester ouverte quand on repasse en mode fixe.
if(id==='hide-side' && !on){
@@ -46,7 +46,7 @@ function applyView(id, on){
function toggleView(id){ applyView(id, document.getElementById('view-'+id).checked); savePrefs(); }
function initView(){
VIEW_OPTS.forEach(o=>{
- let v=null; try{ v=localStorage.getItem('ajean-'+o.id); }catch(e){}
+ let v=null; try{ v=localStorage.getItem('loki-'+o.id); }catch(e){}
applyView(o.id, v==='1');
});
}
diff --git a/internal/ajean/ui/src/js/02-prefs.js b/internal/loki/ui/src/js/02-prefs.js
similarity index 78%
rename from internal/ajean/ui/src/js/02-prefs.js
rename to internal/loki/ui/src/js/02-prefs.js
index cab0b73..67347d6 100644
--- a/internal/ajean/ui/src/js/02-prefs.js
+++ b/internal/loki/ui/src/js/02-prefs.js
@@ -1,11 +1,11 @@
// ===== Persistance côté serveur (partagée entre appareils) ==================
-// L'apparence est aussi enregistrée sur la machine ajean (/api/prefs) : ainsi le
+// L'apparence est aussi enregistrée sur la machine loki (/api/prefs) : ainsi le
// thème/affichage choisi sur un appareil se retrouve sur tous les autres. Le
// localStorage reste utilisé pour appliquer instantanément au chargement (sans
// flash), puis loadPrefs() aligne sur la valeur du serveur (source de vérité).
function savePrefs(){
let theme='light';
- try{ theme=localStorage.getItem('ajean-theme')||'light'; }catch(e){}
+ try{ theme=localStorage.getItem('loki-theme')||'light'; }catch(e){}
const p={theme};
VIEW_OPTS.forEach(o=>{ p[o.id.replace('-','_')] = viewOn(o.id)?'1':'0'; });
jpost('/api/prefs', p).catch(()=>{});
@@ -14,7 +14,7 @@ async function loadPrefs(){
try{
const p=await jget('/api/prefs');
if(p && p.ok && p.prefs){
- // Un thème enregistré par une version précédente (ajean, gpt-dark…) est
+ // Un thème enregistré par une version précédente (loki, gpt-dark…) est
// normalisé par applyTheme ; on réenregistre pour purger le serveur.
if(p.prefs.theme){
applyTheme(p.prefs.theme);
@@ -27,4 +27,4 @@ async function loadPrefs(){
}
}catch(e){}
}
-document.addEventListener('DOMContentLoaded', ()=>{ initTheme(); initView(); document.getElementById('sysprompt').value = localStorage.getItem('ajean.sys') || ''; loadSys(); restoreChat(); });
+document.addEventListener('DOMContentLoaded', ()=>{ initTheme(); initView(); document.getElementById('sysprompt').value = localStorage.getItem('loki.sys') || ''; loadSys(); restoreChat(); });
diff --git a/internal/ajean/ui/src/js/03-ui-kit.js b/internal/loki/ui/src/js/03-ui-kit.js
similarity index 98%
rename from internal/ajean/ui/src/js/03-ui-kit.js
rename to internal/loki/ui/src/js/03-ui-kit.js
index d17cc73..8d5e574 100644
--- a/internal/ajean/ui/src/js/03-ui-kit.js
+++ b/internal/loki/ui/src/js/03-ui-kit.js
@@ -49,7 +49,7 @@ function _askKey(e){
else if(e.key==='Enter'){ e.preventDefault(); e.stopPropagation(); askResolve(true); }
}
function _openAsk(kind, message, opts){
- // Compat 2 conventions : (message, opts) [UI ajean] ET l'objet unique
+ // Compat 2 conventions : (message, opts) [UI loki] ET l'objet unique
// {title,msg,yes,no,placeholder} passé par e2e.js sur ajean.link (server.html,
// où cette fonction remplace le window.askConfirm du bootstrap boîte noire).
if(message && typeof message==='object'){ const o=message; opts={title:o.title, okText:o.yes, cancelText:o.no, placeholder:o.placeholder, default:o.default, danger:o.danger}; message=o.msg||''; }
diff --git a/internal/ajean/ui/src/js/04-api.js b/internal/loki/ui/src/js/04-api.js
similarity index 86%
rename from internal/ajean/ui/src/js/04-api.js
rename to internal/loki/ui/src/js/04-api.js
index 5afc573..da2b611 100644
--- a/internal/ajean/ui/src/js/04-api.js
+++ b/internal/loki/ui/src/js/04-api.js
@@ -1,13 +1,13 @@
// Clé de pilotage : envoyée en Authorization: Bearer sur chaque appel /api/*.
// Sur un 401 on la (re)demande et on rejoue la requête. Stockée en localStorage.
-let TOKEN = localStorage.getItem('ajean.key') || '';
+let TOKEN = localStorage.getItem('loki.key') || '';
function authHeaders(h){ h = Object.assign({}, h||{}); if(TOKEN) h['Authorization']='Bearer '+TOKEN; return h; }
// Base d'URL : "" en local (page à "/"), "/u/" servie via le relais ajean.link.
// Garde les chemins absolus (/api/…) corrects derrière le tunnel.
const API_BASE = location.pathname.replace(/\/(index\.html)?$/, '');
// Délai maximal d'un appel /api/* ordinaire. Sans lui, une requête que le
// navigateur met en file d'attente (plafond de ~6 connexions par domaine, atteint
-// dès qu'on laisse traîner plusieurs onglets AJEAN) reste suspendue POUR TOUJOURS :
+// dès qu'on laisse traîner plusieurs onglets Loki) reste suspendue POUR TOUJOURS :
// ni réponse, ni erreur, et une UI figée sur « chargement… » sans rien à afficher.
// Mieux vaut une erreur franche. Les flux longs (SSE) passent leur propre signal
// et ne sont donc jamais concernés.
@@ -25,7 +25,7 @@ async function jfetch(u, opts){
let r;
try{ r = await fetch(u, opts); }
catch(e){
- if(e && e.name==='AbortError'){ throw new Error('requête sans réponse après 30 s (trop d\'onglets AJEAN ouverts ?)'); }
+ if(e && e.name==='AbortError'){ throw new Error('requête sans réponse après 30 s (trop d\'onglets Loki ouverts ?)'); }
throw e;
}
finally{ if(timer) clearTimeout(timer); }
@@ -46,10 +46,10 @@ async function jfetch(u, opts){
let _keyAsk = null;
function askKeyOnce(){
if(!_keyAsk){
- _keyAsk = askPrompt('Clé de pilotage AJEAN requise :', {title:'Authentification', placeholder:'clé…'})
+ _keyAsk = askPrompt('Clé de pilotage Loki requise :', {title:'Authentification', placeholder:'clé…'})
.then(k => {
_keyAsk = null;
- if(k){ TOKEN = k.trim(); localStorage.setItem('ajean.key', TOKEN); }
+ if(k){ TOKEN = k.trim(); localStorage.setItem('loki.key', TOKEN); }
return k;
}, e => { _keyAsk = null; throw e; });
}
diff --git a/internal/ajean/ui/src/js/05-status.js b/internal/loki/ui/src/js/05-status.js
similarity index 99%
rename from internal/ajean/ui/src/js/05-status.js
rename to internal/loki/ui/src/js/05-status.js
index f8184b3..acc92af 100644
--- a/internal/ajean/ui/src/js/05-status.js
+++ b/internal/loki/ui/src/js/05-status.js
@@ -64,7 +64,7 @@ async function checkUpdate(){
msg.innerHTML='Nouvelle version v'+r.latest+' disponible. ';
const btn=document.createElement('button'); btn.textContent='Mettre à jour'; btn.onclick=applyUpdate;
msg.appendChild(btn);
- } else { msg.textContent='AJEAN est à jour ✓'; }
+ } else { msg.textContent='Loki est à jour ✓'; }
}catch(e){ msg.textContent='Erreur réseau'; }
b.disabled=false;
}
diff --git a/internal/ajean/ui/src/js/06-settings.js b/internal/loki/ui/src/js/06-settings.js
similarity index 98%
rename from internal/ajean/ui/src/js/06-settings.js
rename to internal/loki/ui/src/js/06-settings.js
index c30feb8..2373e56 100644
--- a/internal/ajean/ui/src/js/06-settings.js
+++ b/internal/loki/ui/src/js/06-settings.js
@@ -9,7 +9,7 @@ const HRESERVE=['cfg','vram','ram-details','presets','internet-status','mcp-list
function reserveHeights(){
HRESERVE.forEach(id=>{
try{
- const h=parseInt(localStorage.getItem('ajean-h-'+id)||'0',10);
+ const h=parseInt(localStorage.getItem('loki-h-'+id)||'0',10);
const el=document.getElementById(id);
if(el && h>0) el.style.minHeight=h+'px';
}catch(e){}
@@ -19,7 +19,7 @@ function releaseHeights(){
HRESERVE.forEach(id=>{
const el=document.getElementById(id); if(!el) return;
el.style.minHeight='';
- try{ localStorage.setItem('ajean-h-'+id, String(el.offsetHeight||0)); }catch(e){}
+ try{ localStorage.setItem('loki-h-'+id, String(el.offsetHeight||0)); }catch(e){}
});
}
document.addEventListener('DOMContentLoaded', reserveHeights);
@@ -98,7 +98,7 @@ async function loadPresets(){
});
}
// « Mode agent » = accès machine + skills réunis en un seul interrupteur.
-// Quand il est actif, un « a » blanc apparaît en fondu devant « ajean » → « ajean ».
+// Quand il est actif, un « a » blanc apparaît en fondu devant « loki » → « loki ».
async function loadAgent(){
const s=await jget('/api/agent');
const on = s.enabled;
@@ -367,7 +367,7 @@ async function downloadExport(url){
// tunnel, où tout arrive en application/json.
const cd = r.headers.get('Content-Disposition') || '';
const m = cd.match(/filename="([^"]+)"/);
- const name = m ? m[1] : 'ajean-conversation.' + (url.includes('format=json') ? 'json' : 'md');
+ const name = m ? m[1] : 'loki-conversation.' + (url.includes('format=json') ? 'json' : 'md');
// ⚠️ blobURL, surtout pas `url` : ce nom est déjà celui du paramètre, et le
// redéclarer ici mettrait la ligne `jfetch(url)` ci-dessus dans la zone morte
// du const — l'export échouerait avant même de partir.
diff --git a/internal/ajean/ui/src/js/07-models.js b/internal/loki/ui/src/js/07-models.js
similarity index 98%
rename from internal/ajean/ui/src/js/07-models.js
rename to internal/loki/ui/src/js/07-models.js
index da38866..ca5aa97 100644
--- a/internal/ajean/ui/src/js/07-models.js
+++ b/internal/loki/ui/src/js/07-models.js
@@ -173,7 +173,7 @@ function fmtSize(b){
}
// Retire les guillemets ENTOURANTS d'une valeur, et seulement eux. Les anciens
// motifs `"?([^"\n]*)"?` s'arrêtaient au premier guillemet INTERNE : une valeur
-// comme `--chat-template-file "/etc/ajean/tpl.jinja"` était lue tronquée, puis
+// comme `--chat-template-file "/etc/loki/tpl.jinja"` était lue tronquée, puis
// réécrite avec des guillemets non appariés (issue #17).
function unquoteVal(v){
v = String(v==null?'':v).trim();
@@ -207,7 +207,7 @@ async function populateModelPicker(){
const list = await jget('/api/models');
const cur = currentModelInTextarea();
// Les modèles peuvent venir de plusieurs dossiers (disque externe…) : on les
- // regroupe par dossier, AJEAN_HOME d'abord (l'API les renvoie dans cet ordre).
+ // regroupe par dossier, LOKI_HOME d'abord (l'API les renvoie dans cet ordre).
const groups = [];
for(const m of (list||[])){
let g = groups.find(x => x.dir === m.dir);
@@ -233,7 +233,7 @@ async function populateModelPicker(){
opts += '';
}
html += groups.length > 1
- ? ''
+ ? ''
: opts;
}
// MODEL pointe vers un fichier qu'on ne trouve dans aucun dossier déclaré :
@@ -244,7 +244,7 @@ async function populateModelPicker(){
sel.innerHTML = html;
if(cur && !matched) setModelDirsOpen(true);
}
-// ---- Dossiers de modèles : AJEAN_HOME + dossiers ajoutés (disque externe…) ---
+// ---- Dossiers de modèles : LOKI_HOME + dossiers ajoutés (disque externe…) ---
// Réglage rare : replié derrière une ligne, comme l'éditeur de .env brut.
function setModelDirsOpen(open){
const b = document.getElementById('m-dirs-body');
@@ -273,7 +273,7 @@ async function populateModelDirs(){
info.className = 'muted';
const n = x.count >= 0 ? (x.count + ' modèle' + (x.count>1?'s':'')) : 'illisible';
const free = x.free >= 0 ? ', ' + fmtSize(x.free) + ' libres' : '';
- info.textContent = '(' + n + free + (x.home ? ', dossier ajean' : '') + ')';
+ info.textContent = '(' + n + free + (x.home ? ', dossier loki' : '') + ')';
row.append(p, info);
if(!x.home){
const del = document.createElement('button');
@@ -386,13 +386,13 @@ function underDir(p, dir){
}
let beFastPath = '', beOptPath = '', beFastDir = '';
async function populateBackend(){
- // Chemins des deux moteurs gérés + liste des backends détectés (dossier ajean).
+ // Chemins des deux moteurs gérés + liste des backends détectés (dossier loki).
let lc = {}; try{ lc = await jget('/api/llamacpp'); }catch(_){}
beFastPath = (lc.prebuilt && lc.prebuilt.bin) || '';
beFastDir = (lc.prebuilt && lc.prebuilt.dir) || '';
beOptPath = lc.bin || '';
// Menu « backend détecté » du mode personnalisé : tout ce qu'on trouve dans
- // le dossier backends de ajean (l'utilisateur peut y déposer son propre build).
+ // le dossier backends de loki (l'utilisateur peut y déposer son propre build).
const detected = await jget('/api/backends');
const sel = document.getElementById('m-backend-detected');
let html = '';
@@ -737,7 +737,7 @@ function populateSettings(){
// --- Décodage spéculatif (--spec-type / --spec-draft-n-max) ----------------
// Sélectionne le type courant. --spec-type accepte en réalité une LISTE séparée
// par des virgules ; une valeur composée (ou un type sorti après cette version
-// de ajean) ne correspondrait à aucune option et serait silencieusement effacée
+// de loki) ne correspondrait à aucune option et serait silencieusement effacée
// au premier changement. On l'ajoute donc telle quelle à la liste plutôt que de
// la perdre.
function setSpecType(v){
@@ -938,7 +938,7 @@ function watchDownload(fname){
await Promise.all([populateModelPicker(), populateMmproj(), populateDlDirs()]);
// Un projecteur (mmproj) se sélectionne dans le champ Vision, un modèle dans
// le sélecteur de modèle — d'après le nom du fichier téléchargé. Le fichier
- // a pu atterrir hors du dossier ajean : l'option porte alors le chemin
+ // a pu atterrir hors du dossier loki : l'option porte alors le chemin
// complet, pas le simple nom de fichier.
const pick = (id, cb)=>{
const s = document.getElementById(id);
diff --git a/internal/ajean/ui/src/js/08-chat-render.js b/internal/loki/ui/src/js/08-chat-render.js
similarity index 99%
rename from internal/ajean/ui/src/js/08-chat-render.js
rename to internal/loki/ui/src/js/08-chat-render.js
index 86ca069..a30cc4a 100644
--- a/internal/ajean/ui/src/js/08-chat-render.js
+++ b/internal/loki/ui/src/js/08-chat-render.js
@@ -115,7 +115,7 @@ function setLabelCounts(el, add, del){
if(del) cnt.appendChild(Object.assign(document.createElement('span'),{className:'d',textContent:'-'+del}));
lab.appendChild(cnt);
}
-// Ligne de mesures sous une réponse (prefill / decode). Les étiquettes VOUS/AJEAN
+// Ligne de mesures sous une réponse (prefill / decode). Les étiquettes VOUS/Loki
// sont masquées dans cette mise en page, donc les chiffres qu'on y écrivait
// avaient disparu : ils ont leur propre ligne, discrète, sous le texte. Masquée
// par la préférence d'affichage « masquer la vitesse de génération » (CSS).
@@ -276,7 +276,7 @@ async function compactContext(){
}
// Persistance de la conversation : on garde user+assistant en localStorage pour
// survivre à un refresh (les bulles tool/reasoning sont éphémères, non stockées).
-function saveChat(){ try{ localStorage.setItem('ajean.chat', JSON.stringify(msgs)); }catch(e){} }
+function saveChat(){ try{ localStorage.setItem('loki.chat', JSON.stringify(msgs)); }catch(e){} }
// Source de vérité = SERVEUR. Au chargement on ouvre le flux d'abonnement
// permanent (connectStream), qui rejoue tout le fil depuis le serveur — texte,
// appels d'outils, vitesses, raisonnement — puis suit le direct. Plus de
diff --git a/internal/ajean/ui/src/js/09-stream.js b/internal/loki/ui/src/js/09-stream.js
similarity index 99%
rename from internal/ajean/ui/src/js/09-stream.js
rename to internal/loki/ui/src/js/09-stream.js
index 434d6ba..e3dfc77 100644
--- a/internal/ajean/ui/src/js/09-stream.js
+++ b/internal/loki/ui/src/js/09-stream.js
@@ -1,5 +1,5 @@
// ===== Conversation SERVEUR (source de vérité, partagée entre appareils) =====
-// L'historique et la génération vivent sur le serveur ajean. Le client ouvre un
+// L'historique et la génération vivent sur le serveur loki. Le client ouvre un
// flux d'ABONNEMENT permanent (SSE) qui rejoue le journal depuis lastSeq puis
// suit le direct. Fermer l'onglet n'arrête plus la génération (détachée côté
// serveur) ; se reconnecter rejoue tout le fil, détails compris.
@@ -212,7 +212,7 @@ function handleDelta(d){
}
// Flux d'abonnement permanent + reconnexion auto (from=lastSeq → pas de
// re-téléchargement complet après une coupure / bascule d'appareil).
-// Un onglet caché RELÂCHE son flux SSE. Sans ça, chaque onglet AJEAN laissé
+// Un onglet caché RELÂCHE son flux SSE. Sans ça, chaque onglet Loki laissé
// ouvert monopolise une des ~6 connexions simultanées autorisées par domaine :
// au-delà, toute requête (journal, installation du moteur…) reste en file
// d'attente sans jamais partir ni échouer — un blocage silencieux très
diff --git a/internal/ajean/ui/src/js/10-llamacpp.js b/internal/loki/ui/src/js/10-llamacpp.js
similarity index 100%
rename from internal/ajean/ui/src/js/10-llamacpp.js
rename to internal/loki/ui/src/js/10-llamacpp.js
diff --git a/internal/ajean/ui/src/js/12-mcp.js b/internal/loki/ui/src/js/12-mcp.js
similarity index 100%
rename from internal/ajean/ui/src/js/12-mcp.js
rename to internal/loki/ui/src/js/12-mcp.js
diff --git a/internal/ajean/ui/src/js/13-remote.js b/internal/loki/ui/src/js/13-remote.js
similarity index 91%
rename from internal/ajean/ui/src/js/13-remote.js
rename to internal/loki/ui/src/js/13-remote.js
index ac2f4fa..890fa6d 100644
--- a/internal/ajean/ui/src/js/13-remote.js
+++ b/internal/loki/ui/src/js/13-remote.js
@@ -1,9 +1,9 @@
// Accès distant (ajean.link) — piloté depuis l'UI, sans terminal.
// « Connecter » ouvre une popup app.ajean.link/connect.html qui gère compte +
// abonnement, puis renvoie une clé de liaison par postMessage. On la POSTe à
-// l'agent local (/api/link/connect) qui fait le `ajean link` (token + service).
+// l'agent local (/api/link/connect) qui fait le `loki link` (token + service).
-const AJEAN_APP_ORIGIN = 'https://app.ajean.link';
+const LOKI_APP_ORIGIN = 'https://app.ajean.link';
function renderRemote(d){
const off = document.getElementById('remote-off'), on = document.getElementById('remote-on');
@@ -38,15 +38,15 @@ async function loadRemote(){
// Ouvre la popup de connexion et attend la clé renvoyée par postMessage.
function remoteConnect(){
const params = new URLSearchParams({ origin: window.location.origin, host: window.location.hostname || 'ce serveur' });
- const url = AJEAN_APP_ORIGIN + '/connect.html?' + params.toString();
- const pop = window.open(url, 'ajean-connect', 'width=440,height=640');
+ const url = LOKI_APP_ORIGIN + '/connect.html?' + params.toString();
+ const pop = window.open(url, 'loki-connect', 'width=440,height=640');
if(!pop){ toast('autorisez les popups pour connecter ajean.link'); return; }
async function onMsg(ev){
// Anti-usurpation : n'accepte QUE des messages du portail ajean.link.
- if(ev.origin !== AJEAN_APP_ORIGIN) return;
+ if(ev.origin !== LOKI_APP_ORIGIN) return;
const d = ev.data || {};
- if(d.type !== 'ajean-link' || !d.token) return;
+ if(d.type !== 'loki-link' || !d.token) return;
window.removeEventListener('message', onMsg);
try{
const r = await jpost('/api/link/connect', { token: d.token });
diff --git a/internal/ajean/ui/src/js/14-attach.js b/internal/loki/ui/src/js/14-attach.js
similarity index 100%
rename from internal/ajean/ui/src/js/14-attach.js
rename to internal/loki/ui/src/js/14-attach.js
diff --git a/internal/ajean/ui/src/js/15-node.js b/internal/loki/ui/src/js/15-node.js
similarity index 98%
rename from internal/ajean/ui/src/js/15-node.js
rename to internal/loki/ui/src/js/15-node.js
index b055708..e6d2379 100644
--- a/internal/ajean/ui/src/js/15-node.js
+++ b/internal/loki/ui/src/js/15-node.js
@@ -131,10 +131,10 @@ async function genNodeCode(){
// Commande d'accès À DISTANCE (partout) via ajean.link : chiffrée de bout en
// bout, le relais reste aveugle. --key = clé publique de l'agent (le poste
// scelle vers elle), --machine = quelle machine joindre.
- const remote = 'ajean remote install https://ajean.link --machine '+r.machine+
+ const remote = 'loki remote install https://ajean.link --machine '+r.machine+
' --key '+r.agent_pub+' --code '+r.code+' --allow '+allow+rootArg;
// Variante RÉSEAU LOCAL (connexion directe, sans passer par le relais).
- const lan = 'ajean remote install '+location.origin+
+ const lan = 'loki remote install '+location.origin+
' --key '+r.agent_pub+' --code '+r.code+' --allow '+allow+rootArg;
document.getElementById('node-pair-cmd').textContent = remote;
const lanEl = document.getElementById('node-pair-cmd-lan');
diff --git a/internal/ajean/ui/src/styles.css b/internal/loki/ui/src/styles.css
similarity index 99%
rename from internal/ajean/ui/src/styles.css
rename to internal/loki/ui/src/styles.css
index 53c4dfb..b409a9b 100644
--- a/internal/ajean/ui/src/styles.css
+++ b/internal/loki/ui/src/styles.css
@@ -101,7 +101,7 @@ option,optgroup{background:var(--panel);color:var(--text)}
.preset-meta .qtag svg,.preset-meta .rtag svg,.preset-meta .btag svg{width:11px;height:11px}
}
h1{margin:0;font-size:18px;color:var(--accent)}
-/* Logo : « AJEAN » dessiné en pixels (SVG 4x5 par lettre, trait d'un module). Le « A »
+/* Logo : « Loki » dessiné en pixels (SVG 4x5 par lettre, trait d'un module). Le « A »
reste un bloc à part : il apparaît en glissant quand le mode agent est actif. */
#brand{display:inline-flex;align-items:center}
#brand svg{height:10px;width:auto;display:block;shape-rendering:crispEdges;fill:currentColor}
@@ -146,7 +146,7 @@ h1{margin:0;font-size:18px;color:var(--accent)}
.ask-ok.danger{border-color:var(--err);color:var(--err)}
#brand-a{display:inline-flex;line-height:0;color:var(--text);margin-right:2px}
/* Mode agent : le A passe en couleur d'accent (il était masqué hors agent,
- la marque se lisant alors « AJEAN »). L'état agent reste signalé par sa
+ la marque se lisant alors « Loki »). L'état agent reste signalé par sa
pastille dans les réglages. */
#brand.agent #brand-a{color:var(--accent)}
/* interrupteur (switch) */
@@ -396,7 +396,7 @@ button:disabled{opacity:.5;cursor:not-allowed}
haut (règles tiroir). Quand la barre est un TIROIR (téléphone, ou mode
« barre en tiroir » sur ordinateur), elle est en position:fixed : le padding
du ne la protège pas, il faut donc reposer les env() ici — sinon
- « AJEAN » et la pastille d'état passent sous l'encoche en PWA iOS. */
+ « Loki » et la pastille d'état passent sous l'encoche en PWA iOS. */
@media (max-width:720px){
.side{padding:calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left))}
}
@@ -933,7 +933,7 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
/* ===== Mise en page de la conversation =============================
Colonne de lecture centrée, réponses SANS bulle (texte nu sur le fond),
question de l'utilisateur en pastille arrondie à droite, composer en
- « pilule », pas d'étiquettes VOUS/AJEAN. Ces règles surchargent la mise en
+ « pilule », pas d'étiquettes VOUS/Loki. Ces règles surchargent la mise en
page générique plus haut : elles doivent donc rester APRÈS elle. */
body{font-size:15px}
h1{color:var(--text);font-weight:600;letter-spacing:-.01em}
@@ -947,7 +947,7 @@ h1{color:var(--text);font-weight:600;letter-spacing:-.01em}
/* Question : pastille arrondie à droite, texte lisible sur fond clair OU sombre. */
#chat .msg.user{background:var(--bubble);color:var(--text);border-radius:20px;padding:11px 18px;max-width:75%;line-height:1.6}
#chat .msg.user .label{color:var(--dim)}
-/* Étiquettes VOUS / AJEAN : inutiles ici (l'alignement suffit), sauf pour les
+/* Étiquettes VOUS / Loki : inutiles ici (l'alignement suffit), sauf pour les
bulles repliables (raisonnement / outils) où le label est le bouton de repli. */
#chat .msg .label{display:none}
#chat .msg.collapsible .label{display:block}
diff --git a/internal/ajean/version_test.go b/internal/loki/version_test.go
similarity index 86%
rename from internal/ajean/version_test.go
rename to internal/loki/version_test.go
index 73d452f..e370cdc 100644
--- a/internal/ajean/version_test.go
+++ b/internal/loki/version_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -7,14 +7,14 @@ import (
"testing"
)
-// La ressource de version Windows (cmd/ajean/versioninfo.json) doit suivre la
+// La ressource de version Windows (cmd/loki/versioninfo.json) doit suivre la
// constante Version. Elles ont divergé en 0.8.0 : la constante était à jour, la
// ressource restait à 0.7.13. Conséquence peu lisible — le binaire installé se
// DÉCLARAIT 0.7.13 auprès de Windows, si bien que relancer le fichier téléchargé
// proposait indéfiniment « appliquer la mise à jour ? » sur une version déjà
// installée. Ce test rend l'oubli impossible.
func TestVersionInfoWindowsSuitLaVersion(t *testing.T) {
- b, err := os.ReadFile("../../cmd/ajean/versioninfo.json")
+ b, err := os.ReadFile("../../cmd/loki/versioninfo.json")
if err != nil {
t.Fatal(err)
}
@@ -35,7 +35,7 @@ func TestVersionInfoWindowsSuitLaVersion(t *testing.T) {
"StringFileInfo.ProductVersion": vi.StringFileInfo.ProductVersion,
} {
if got != Version {
- t.Errorf("%s = %s, attendu %s — pense à `go generate ./cmd/ajean` après un bump", name, got, Version)
+ t.Errorf("%s = %s, attendu %s — pense à `go generate ./cmd/loki` après un bump", name, got, Version)
}
}
}
diff --git a/internal/ajean/web_api.go b/internal/loki/web_api.go
similarity index 98%
rename from internal/ajean/web_api.go
rename to internal/loki/web_api.go
index 060911e..06644d0 100644
--- a/internal/ajean/web_api.go
+++ b/internal/loki/web_api.go
@@ -1,6 +1,6 @@
// web_api.go — handlers REST /api/* (statut, config, presets, modèles,
// mémoire, agent, clés, bench…) du serveur web local.
-package ajean
+package loki
import (
"encoding/json"
@@ -15,7 +15,7 @@ import (
)
func handlePing(w http.ResponseWriter, r *http.Request) {
- sendJSON(w, 200, map[string]any{"ok": true, "service": "ajean", "version": Version})
+ sendJSON(w, 200, map[string]any{"ok": true, "service": "loki", "version": Version})
}
// handleStatus reports service state cross-platform via serviceIsActive
@@ -170,18 +170,18 @@ func handleConfigEnv(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, ReadConfig())
}
-// handleBackends scans AJEAN_HOME/backends// for a llama-server binary,
+// handleBackends scans LOKI_HOME/backends// for a llama-server binary,
// trying common build subpaths (build/bin, build-sm120/bin, bin, .).
// Returns [{name, path}].
func handleBackends(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, listBackendBins())
}
-// listBackendBins scanne AJEAN_HOME/backends// pour un binaire llama-server
+// listBackendBins scanne LOKI_HOME/backends// pour un binaire llama-server
// (essaie les sous-chemins des différents générateurs CMake). Renvoie
// [{name, path}] — un par dossier de backend contenant un binaire.
func listBackendBins() []map[string]any {
- root := AjeanHome() + "/backends"
+ root := LokiHome() + "/backends"
entries, err := os.ReadDir(root)
if err != nil {
return []map[string]any{}
@@ -274,14 +274,14 @@ func handleLlamacppUninstallCustom(w http.ResponseWriter, r *http.Request) {
}
// handleModels lists *.gguf files (size in bytes) for the preset editor's model
-// picker, dans AJEAN_HOME ET dans les dossiers supplémentaires déclarés (disque
+// picker, dans LOKI_HOME ET dans les dossiers supplémentaires déclarés (disque
// externe…). "value" est ce qu'il faut écrire dans MODEL= : un simple nom de
-// fichier pour AJEAN_HOME (compatibilité avec l'existant), le chemin complet
+// fichier pour LOKI_HOME (compatibilité avec l'existant), le chemin complet
// pour un dossier externe.
func handleModels(w http.ResponseWriter, r *http.Request) {
out := []map[string]any{}
seen := map[string]bool{}
- home := AjeanHome()
+ home := LokiHome()
for _, dir := range modelDirs() {
entries, err := os.ReadDir(dir)
if err != nil {
@@ -801,7 +801,7 @@ func handleMCPTest(w http.ResponseWriter, r *http.Request) {
func handleMem(w http.ResponseWriter, r *http.Request) {
name := strings.TrimSpace(r.URL.Query().Get("name"))
if name == "" {
- sendJSON(w, 200, map[string]any{"name": "", "content": "# nouvelle page\n\nNote ici ce que ajean doit retenir entre les sessions.\n"})
+ sendJSON(w, 200, map[string]any{"name": "", "content": "# nouvelle page\n\nNote ici ce que loki doit retenir entre les sessions.\n"})
return
}
c := MemContent(name)
@@ -878,7 +878,7 @@ func handleSwitch(w http.ResponseWriter, r *http.Request) {
// svcHandler returns an HTTP handler that triggers a start/stop/restart through
// the cross-platform serviceAction (systemd sous Linux, supervision PID-file
// sous Windows — voir sys_service_*.go). C'est ce qui permet à un client distant
-// de relancer AJEAN.
+// de relancer Loki.
func svcHandler(action string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := serviceAction(action)
diff --git a/internal/ajean/web_auth.go b/internal/loki/web_auth.go
similarity index 86%
rename from internal/ajean/web_auth.go
rename to internal/loki/web_auth.go
index 6ff9ce9..aad542d 100644
--- a/internal/ajean/web_auth.go
+++ b/internal/loki/web_auth.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"crypto/rand"
@@ -9,7 +9,7 @@ import (
"strings"
)
-// web_auth.go protège l'API de pilotage (ajean web) quand elle est exposée sur
+// web_auth.go protège l'API de pilotage (loki web) quand elle est exposée sur
// internet — c.-à-d. l'API que tout client (navigateur, app mobile, script…)
// utilise pour switcher de preset, redémarrer le service, lire le status, etc.
//
@@ -51,7 +51,7 @@ func requireWebAuth(next http.HandlerFunc) http.HandlerFunc {
return
}
if !checkBearer(r, key) {
- w.Header().Set("WWW-Authenticate", `Bearer realm="ajean"`)
+ w.Header().Set("WWW-Authenticate", `Bearer realm="loki"`)
sendJSON(w, http.StatusUnauthorized, map[string]any{"error": "non autorisé"})
return
}
@@ -74,11 +74,11 @@ func checkBearer(r *http.Request, key string) bool {
return false
}
-// cmdSetWebKey sets (or clears) the control-API key in $AJEAN_HOME/.web_key.
+// cmdSetWebKey sets (or clears) the control-API key in $LOKI_HOME/.web_key.
//
-// ajean set-web-key définit la clé
-// ajean set-web-key génère une clé aléatoire
-// ajean set-web-key "" supprime la protection (API ouverte)
+// loki set-web-key définit la clé
+// loki set-web-key génère une clé aléatoire
+// loki set-web-key "" supprime la protection (API ouverte)
//
// Contrairement à set-api-key, aucun redémarrage n'est nécessaire : le serveur
// web relit la clé à chaque requête.
@@ -90,7 +90,7 @@ func cmdSetWebKey(args []string) error {
if _, err := rand.Read(buf); err != nil {
return err
}
- key = "ajean-web-" + hex.EncodeToString(buf)
+ key = "loki-web-" + hex.EncodeToString(buf)
fmt.Printf("%s clé générée : %s\n", green("[ok]"), bold(key))
case args[0] == "" || args[0] == "off" || args[0] == "none":
key = ""
@@ -106,6 +106,6 @@ func cmdSetWebKey(args []string) error {
}
fmt.Printf("%s clé de pilotage enregistrée\n", green("[ok]"))
fmt.Printf(" les clients doivent envoyer : %s\n", dim("Authorization: Bearer "+key))
- fmt.Printf(" (relance 'ajean web' si le serveur web tourne déjà — non requis, lu à chaud)\n")
+ fmt.Printf(" (relance 'loki web' si le serveur web tourne déjà — non requis, lu à chaud)\n")
return nil
}
diff --git a/internal/ajean/web_chat.go b/internal/loki/web_chat.go
similarity index 99%
rename from internal/ajean/web_chat.go
rename to internal/loki/web_chat.go
index 76ed484..eccfe07 100644
--- a/internal/ajean/web_chat.go
+++ b/internal/loki/web_chat.go
@@ -1,6 +1,6 @@
// web_chat.go — endpoints de chat du serveur web local : envoi/stop/reset,
// flux SSE d'abonnement à la conversation serveur (voir chat_conversation.go).
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/web_chat_caps_test.go b/internal/loki/web_chat_caps_test.go
similarity index 99%
rename from internal/ajean/web_chat_caps_test.go
rename to internal/loki/web_chat_caps_test.go
index 50e5d31..299664c 100644
--- a/internal/ajean/web_chat_caps_test.go
+++ b/internal/loki/web_chat_caps_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import "testing"
diff --git a/internal/ajean/web_devices.go b/internal/loki/web_devices.go
similarity index 99%
rename from internal/ajean/web_devices.go
rename to internal/loki/web_devices.go
index e0766a5..ec1eba6 100644
--- a/internal/ajean/web_devices.go
+++ b/internal/loki/web_devices.go
@@ -9,7 +9,7 @@
// C'est aussi pour ça que le réglage vit dans le PRESET (--device, compris par
// tous les backends) et non dans CUDA_VISIBLE_DEVICES, qui n'a aucun effet sur
// un moteur Vulkan.
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/web_devices_test.go b/internal/loki/web_devices_test.go
similarity index 99%
rename from internal/ajean/web_devices_test.go
rename to internal/loki/web_devices_test.go
index a64a33d..fd6ee51 100644
--- a/internal/ajean/web_devices_test.go
+++ b/internal/loki/web_devices_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import "testing"
diff --git a/internal/ajean/web_fetch_go.go b/internal/loki/web_fetch_go.go
similarity index 99%
rename from internal/ajean/web_fetch_go.go
rename to internal/loki/web_fetch_go.go
index 2a508df..1d96af0 100644
--- a/internal/ajean/web_fetch_go.go
+++ b/internal/loki/web_fetch_go.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"context"
diff --git a/internal/ajean/web_link_api.go b/internal/loki/web_link_api.go
similarity index 92%
rename from internal/ajean/web_link_api.go
rename to internal/loki/web_link_api.go
index 4cce3e5..ec00ad2 100644
--- a/internal/ajean/web_link_api.go
+++ b/internal/loki/web_link_api.go
@@ -1,9 +1,9 @@
-package ajean
+package loki
// web_link_api.go — API locale pilotant l'accès distant (ajean.link) depuis l'UI
-// web de AJEAN, pour éviter le terminal. Le panneau « Accès distant » ouvre une
+// web de Loki, pour éviter le terminal. Le panneau « Accès distant » ouvre une
// popup app.ajean.link/connect.html qui gère compte + paiement puis renvoie une
-// clé de liaison (jl_…) ; l'UI la POSTe ici, et AJEAN fait le `ajean link` tout
+// clé de liaison (jl_…) ; l'UI la POSTe ici, et Loki fait le `loki link` tout
// seul (écrit le token + (re)démarre le service).
//
// Ces routes ne sont accessibles qu'en LOCAL : à travers le tunnel du relais,
@@ -79,7 +79,7 @@ func handleLinkStart(w http.ResponseWriter, r *http.Request) {
}
// handleLinkDisconnect (POST /api/link/disconnect) : arrête le service et oublie
-// la clé (équivalent `ajean link stop` + `ajean link logout`).
+// la clé (équivalent `loki link stop` + `loki link logout`).
func handleLinkDisconnect(w http.ResponseWriter, r *http.Request) {
_ = uiServiceCtl("stop")
_ = removeLinkToken()
@@ -89,7 +89,7 @@ func handleLinkDisconnect(w http.ResponseWriter, r *http.Request) {
// handleLinkPairCode (POST /api/link/paircode) : génère un code d'appairage frais
// (usage unique, TTL 10 min) + l'empreinte E2E, à saisir UNE fois dans le portail
// distant lors de la première connexion (confirmation de la boîte noire). Évite le
-// détour par `ajean link code` en terminal.
+// détour par `loki link code` en terminal.
func handleLinkPairCode(w http.ResponseWriter, r *http.Request) {
code, err := newPairCode()
if err != nil {
diff --git a/internal/ajean/web_llamacpp.go b/internal/loki/web_llamacpp.go
similarity index 99%
rename from internal/ajean/web_llamacpp.go
rename to internal/loki/web_llamacpp.go
index 3709404..343644b 100644
--- a/internal/ajean/web_llamacpp.go
+++ b/internal/loki/web_llamacpp.go
@@ -8,7 +8,7 @@
// POST /api/llamacpp/install → job d'installation {force?}
// POST /api/llamacpp/update → job de mise à jour {clean?}
// GET /api/llamacpp/job?from= → progression du job (phase, lignes, erreur)
-package ajean
+package loki
import (
"encoding/json"
@@ -180,7 +180,7 @@ func handleLlamacpp(w http.ResponseWriter, r *http.Request) {
// s'il porte le numéro d'une release remplacée depuis (chemin versionné).
"in_use": pbBin != "" && prebuiltOwns(cfgBin),
}
- out["backends_dir"] = filepath.Join(AjeanHome(), "backends")
+ out["backends_dir"] = filepath.Join(LokiHome(), "backends")
out["reco"] = recommendedMode(plan.backend)
out["job"] = lcJobSnapshot(0, false)
sendJSON(w, 200, out)
diff --git a/internal/ajean/web_llamacpp_job.go b/internal/loki/web_llamacpp_job.go
similarity index 97%
rename from internal/ajean/web_llamacpp_job.go
rename to internal/loki/web_llamacpp_job.go
index 61c651e..f9b0efd 100644
--- a/internal/ajean/web_llamacpp_job.go
+++ b/internal/loki/web_llamacpp_job.go
@@ -8,7 +8,7 @@
// processus ENFANT, il meurt avec le service. On écrit donc l'état sur disque
// pour pouvoir l'annoncer honnêtement au lieu de laisser l'utilisateur devant
// un écran muet.
-package ajean
+package loki
import (
"encoding/json"
@@ -22,7 +22,7 @@ import (
// L'ENTÊTE du job vit en base ; ses LIGNES restent un fichier, écrit en append.
// Une compilation en produit des milliers : les faire transiter par une
// transaction chacune coûterait un fsync par ligne.
-func lcJobLogPath() string { return filepath.Join(AjeanHome(), "ajean-build.log") }
+func lcJobLogPath() string { return filepath.Join(LokiHome(), "loki-build.log") }
// lcPersisted est la forme sur disque : l'entête du job, sans les lignes (elles
// vivent dans le .log à côté, en append).
diff --git a/internal/ajean/web_prefs.go b/internal/loki/web_prefs.go
similarity index 95%
rename from internal/ajean/web_prefs.go
rename to internal/loki/web_prefs.go
index ec89a1a..a65ffdd 100644
--- a/internal/ajean/web_prefs.go
+++ b/internal/loki/web_prefs.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"encoding/json"
@@ -8,7 +8,7 @@ import (
)
// Préférences d'apparence de l'UI web (thème, mode d'affichage). Stockées côté
-// serveur — sur la machine AJEAN — pour être partagées entre tous les appareils
+// serveur — sur la machine Loki — pour être partagées entre tous les appareils
// qui pilotent la même instance (téléphone, laptop, accès distant ajean.link).
// Avant ça, elles ne vivaient qu'en localStorage, donc par navigateur/appareil.
diff --git a/internal/ajean/web_server.go b/internal/loki/web_server.go
similarity index 96%
rename from internal/ajean/web_server.go
rename to internal/loki/web_server.go
index e97ce8b..5a5e164 100644
--- a/internal/ajean/web_server.go
+++ b/internal/loki/web_server.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bufio"
@@ -45,10 +45,10 @@ func cmdWeb(args []string) error {
return err
}
}
- fmt.Printf("[ajean web] http://%s (Ctrl-C pour arrêter)\n", addr)
+ fmt.Printf("[loki web] http://%s (Ctrl-C pour arrêter)\n", addr)
if readWebKey() == "" {
fmt.Printf("%s API de pilotage NON protégée (aucune clé). Avant de l'exposer sur internet :\n", yellow("[!]"))
- fmt.Printf(" %s\n", bold("ajean set-web-key"))
+ fmt.Printf(" %s\n", bold("loki set-web-key"))
} else {
fmt.Printf("%s API protégée par clé (Authorization: Bearer …)\n", green("[ok]"))
}
@@ -64,7 +64,7 @@ func cmdWeb(args []string) error {
killForeignUIWorker() // rescapé d'une version antérieure ou d'une autre copie
fmt.Printf("%s connexion au relais %s …\n", cyan("[link]"), relayURL())
if fp := e2eFingerprint(); fp != "" {
- fmt.Printf("%s empreinte E2E : %s (code d'appairage : ajean link code)\n", green("[e2e]"), bold(fp))
+ fmt.Printf("%s empreinte E2E : %s (code d'appairage : loki link code)\n", green("[e2e]"), bold(fp))
}
fmt.Printf("%s front OpenAI public prêt (activation en direct via l'UI ; état: %v)\n", green("[oai]"), oaiPublicEnabled())
startAppLink(mux)
@@ -78,13 +78,13 @@ func cmdWeb(args []string) error {
}
// newWebMux construit le routeur HTTP de l'UI web. Extrait de cmdWeb pour être
-// réutilisé par `ajean link`, qui sert ce même mux à travers le tunnel sans
+// réutilisé par `loki link`, qui sert ce même mux à travers le tunnel sans
// repasser par un écouteur TCP local.
var convLoadOnce sync.Once
func newWebMux() *http.ServeMux {
- // Charge l'état de conversation persisté (une fois par process : ajean web ET
- // ajean link serve appellent newWebMux).
+ // Charge l'état de conversation persisté (une fois par process : loki web ET
+ // loki link serve appellent newWebMux).
convLoadOnce.Do(LoadConversation)
// Pré-chauffe les serveurs MCP en tâche de fond : sinon le handshake (plusieurs
// secondes pour un serveur lancé via npx) est payé par le premier message.
@@ -157,7 +157,7 @@ func newWebMux() *http.ServeMux {
api("/api/apikey", handleAPIKey)
api("/api/oai/public", handleOAIPublic)
api("/api/link/status", handleLinkStatus) // état de l'accès distant (ajean.link)
- api("/api/link/connect", handleLinkConnect) // clé de liaison remise par connect.html → ajean link
+ api("/api/link/connect", handleLinkConnect) // clé de liaison remise par connect.html → loki link
api("/api/link/start", handleLinkStart) // (re)démarre le tunnel avec la clé déjà enregistrée
api("/api/link/disconnect", handleLinkDisconnect) // arrête le lien + oublie la clé
api("/api/link/paircode", handleLinkPairCode) // code d'appairage + empreinte pour la 1re connexion
@@ -178,7 +178,7 @@ func newWebMux() *http.ServeMux {
api("/api/network", handleNetwork) // écoute LAN du moteur + pare-feu (Windows)
api("/api/prefs", handleWebPrefs)
api("/api/sysprompt", handleSysPrompt)
- // Alias rétro-compat : l'ancien portail ajean.link (dépôt ajean-relay) pilote
+ // Alias rétro-compat : l'ancien portail ajean.link (dépôt loki-relay) pilote
// encore l'agent via /api/tools* et /api/skills/toggle à travers le tunnel E2E.
// On les mappe sur le mode agent unifié le temps que le portail soit mis à jour.
api("/api/tools", handleAgent)
@@ -303,7 +303,7 @@ func pidOnPort(port int) (int, string) {
// processName returns a short command name for a PID, or "?" if unknown.
func processName(pid int) string {
if runtime.GOOS == "windows" {
- // tasklist CSV : "ajean.exe","1234","Console","1","12 345 K"
+ // tasklist CSV : "loki.exe","1234","Console","1","12 345 K"
out, err := hideCmd(exec.Command("tasklist", "/FI", "PID eq "+strconv.Itoa(pid), "/FO", "CSV", "/NH")).Output()
if err == nil {
if f := strings.SplitN(strings.TrimSpace(string(out)), "\",\"", 2); len(f) == 2 {
diff --git a/internal/ajean/web_sysprompt.go b/internal/loki/web_sysprompt.go
similarity index 99%
rename from internal/ajean/web_sysprompt.go
rename to internal/loki/web_sysprompt.go
index 052fbed..be87280 100644
--- a/internal/ajean/web_sysprompt.go
+++ b/internal/loki/web_sysprompt.go
@@ -5,7 +5,7 @@
// /api/chat/send ne porte que le message → le champ de l'UI n'avait plus aucun
// effet. Il est maintenant lu ici par la génération (chat_conversation.go), et
// InjectSkills (llm_client.go) le fusionne avec le préambule agent.
-package ajean
+package loki
import (
"encoding/json"
diff --git a/internal/ajean/web_upload.go b/internal/loki/web_upload.go
similarity index 99%
rename from internal/ajean/web_upload.go
rename to internal/loki/web_upload.go
index f95141b..a5e6635 100644
--- a/internal/ajean/web_upload.go
+++ b/internal/loki/web_upload.go
@@ -8,7 +8,7 @@
// Le transport est du JSON base64, et non du multipart : c'est la seule forme
// qui traverse le tunnel E2E d'app.ajean.link (relay_e2e.go ne dispatche que des
// corps JSON), donc l'accès distant marche sans code spécifique.
-package ajean
+package loki
import (
"encoding/base64"
@@ -248,7 +248,7 @@ func workspaceRel(abs string) (string, bool) {
// e2eInnerHeader marque une requête dispatchée depuis le proxy chiffré
// (relay_e2e.go). Cf. handleChatFile : c'est le seul moyen pour un handler de
// savoir que sa réponse sera réemballée en JSON.
-const e2eInnerHeader = "X-Ajean-E2E"
+const e2eInnerHeader = "X-Loki-E2E"
// handleChatFile sert un fichier du dossier de travail, en pièce jointe. Le
// client passe le chemin RELATIF que le modèle a écrit dans sa réponse.
diff --git a/internal/ajean/web_upload_test.go b/internal/loki/web_upload_test.go
similarity index 99%
rename from internal/ajean/web_upload_test.go
rename to internal/loki/web_upload_test.go
index da33f8c..de3a4f5 100644
--- a/internal/ajean/web_upload_test.go
+++ b/internal/loki/web_upload_test.go
@@ -1,4 +1,4 @@
-package ajean
+package loki
import (
"bytes"
@@ -127,7 +127,7 @@ func TestChatFileStaysInsideWorkspace(t *testing.T) {
// Le chemin vient du texte du modèle : il peut pointer n'importe où.
for _, bad := range []string{
- "../ajean.db", "../../../etc/passwd", `..\..\secret`,
+ "../loki.db", "../../../etc/passwd", `..\..\secret`,
"/etc/passwd", "C:/Windows/win.ini", "", "uploads/absent.txt",
} {
rec := httptest.NewRecorder()
diff --git a/internal/nodeclient/client.go b/internal/nodeclient/client.go
index d2d9210..69b8d18 100644
--- a/internal/nodeclient/client.go
+++ b/internal/nodeclient/client.go
@@ -1,9 +1,9 @@
// Package nodeclient est le CLIENT léger « poste distant ». Il tourne sur un PC
-// secondaire, s'appaire une fois avec le serveur ajean, puis ouvre une connexion
+// secondaire, s'appaire une fois avec le serveur loki, puis ouvre une connexion
// SORTANTE et exécute — sous contrôle local — les demandes de l'IA.
//
// Volontairement autonome : dépend seulement de nodewire (protocole partagé,
-// zéro dépendance) et de coder/websocket. AUCUN import d'ajean → binaire de
+// zéro dépendance) et de coder/websocket. AUCUN import d'loki → binaire de
// quelques Mo, sans UI ni moteur.
//
// Sécurité côté poste : allowlist locale des capacités, confinement des fichiers
@@ -28,7 +28,7 @@ import (
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
- "github.com/nathaninline/ajean/internal/nodewire"
+ "github.com/R0m1k3/Loki/internal/nodewire"
)
const (
@@ -69,20 +69,20 @@ func (c Config) baseURL() string {
}
// dataDir est le dossier de données du poste. Sur Windows : C:\ProgramData\
-// ajean-remote — MACHINE-WIDE, pour que le service (qui tourne en LocalSystem) lise
+// loki-remote — MACHINE-WIDE, pour que le service (qui tourne en LocalSystem) lise
// la MÊME config que celle écrite par `install` (le profil utilisateur de
// LocalSystem est différent du tien). Ailleurs : dossier de config utilisateur.
func dataDir() string {
if runtime.GOOS == "windows" {
if pd := os.Getenv("ProgramData"); pd != "" {
- return filepath.Join(pd, "ajean-remote")
+ return filepath.Join(pd, "loki-remote")
}
}
dir, err := os.UserConfigDir()
if err != nil || dir == "" {
dir, _ = os.Getwd()
}
- return filepath.Join(dir, "ajean-remote")
+ return filepath.Join(dir, "loki-remote")
}
// ConfigPath renvoie le chemin du fichier de configuration.
@@ -91,7 +91,7 @@ func ConfigPath() string { return filepath.Join(dataDir(), "config.json") }
// InstalledExePath est l'emplacement stable où `install` copie le binaire, pour
// qu'il survive à la suppression du téléchargement.
func InstalledExePath() string {
- name := "ajean-remote"
+ name := "loki-remote"
if runtime.GOOS == "windows" {
name += ".exe"
}
diff --git a/internal/nodeclient/install_other.go b/internal/nodeclient/install_other.go
index 345d9af..0adb619 100644
--- a/internal/nodeclient/install_other.go
+++ b/internal/nodeclient/install_other.go
@@ -40,16 +40,16 @@ func Install() error {
case "darwin":
return installLaunchd(dst)
}
- return fmt.Errorf("installation auto non gérée sur %s — lance « ajean remote connect » via ton propre gestionnaire de démarrage", runtime.GOOS)
+ return fmt.Errorf("installation auto non gérée sur %s — lance « loki remote connect » via ton propre gestionnaire de démarrage", runtime.GOOS)
}
func Uninstall() error {
switch runtime.GOOS {
case "linux":
- _ = exec.Command("systemctl", "--user", "disable", "--now", "ajean-remote.service").Run()
- return os.Remove(filepath.Join(os.Getenv("HOME"), ".config/systemd/user/ajean-remote.service"))
+ _ = exec.Command("systemctl", "--user", "disable", "--now", "loki-remote.service").Run()
+ return os.Remove(filepath.Join(os.Getenv("HOME"), ".config/systemd/user/loki-remote.service"))
case "darwin":
- p := filepath.Join(os.Getenv("HOME"), "Library/LaunchAgents/link.ajean.remote.plist")
+ p := filepath.Join(os.Getenv("HOME"), "Library/LaunchAgents/link.loki.remote.plist")
_ = exec.Command("launchctl", "unload", p).Run()
return os.Remove(p)
}
@@ -57,18 +57,18 @@ func Uninstall() error {
}
func installSystemd(exe string) error {
- unit := "[Unit]\nDescription=AJEAN poste distant\nAfter=network-online.target\n\n" +
+ unit := "[Unit]\nDescription=Loki poste distant\nAfter=network-online.target\n\n" +
"[Service]\nExecStart=" + exe + " remote run\nRestart=always\nRestartSec=5\n\n" +
"[Install]\nWantedBy=default.target\n"
dir := filepath.Join(os.Getenv("HOME"), ".config/systemd/user")
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
- if err := os.WriteFile(filepath.Join(dir, "ajean-remote.service"), []byte(unit), 0o644); err != nil {
+ if err := os.WriteFile(filepath.Join(dir, "loki-remote.service"), []byte(unit), 0o644); err != nil {
return err
}
_ = exec.Command("systemctl", "--user", "daemon-reload").Run()
- if err := exec.Command("systemctl", "--user", "enable", "--now", "ajean-remote.service").Run(); err != nil {
+ if err := exec.Command("systemctl", "--user", "enable", "--now", "loki-remote.service").Run(); err != nil {
return fmt.Errorf("systemctl --user enable: %w (astuce : « loginctl enable-linger %s » pour survivre à la déconnexion)", err, os.Getenv("USER"))
}
return nil
@@ -78,7 +78,7 @@ func installLaunchd(exe string) error {
plist := `
- Labellink.ajean.remote
+ Labellink.loki.remoteProgramArguments` + exe + `remoterunRunAtLoadKeepAlive
@@ -88,7 +88,7 @@ func installLaunchd(exe string) error {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
- p := filepath.Join(dir, "link.ajean.remote.plist")
+ p := filepath.Join(dir, "link.loki.remote.plist")
if err := os.WriteFile(p, []byte(plist), 0o644); err != nil {
return err
}
@@ -103,7 +103,7 @@ func copyFile(src, dst string) error {
// Ré-installation : Linux refuse d'ÉCRASER l'image d'un binaire en cours
// (ETXTBSY « text file busy ») mais autorise à le DÉLIER — le service vivant
// garde son inode ouvert, et le nouveau fichier prend la place du nom. Sans ce
- // unlink, « ajean remote install » relancé échouait tant que le service tournait.
+ // unlink, « loki remote install » relancé échouait tant que le service tournait.
_ = os.Remove(dst)
return os.WriteFile(dst, b, 0o755)
}
diff --git a/internal/nodeclient/install_windows.go b/internal/nodeclient/install_windows.go
index c4d6aa6..8e47f94 100644
--- a/internal/nodeclient/install_windows.go
+++ b/internal/nodeclient/install_windows.go
@@ -12,7 +12,7 @@ import (
"golang.org/x/sys/windows/svc/mgr"
)
-const serviceName = "AjeanRemote"
+const serviceName = "LokiRemote"
// RunService lance la boucle client : en vrai service Windows si on est démarré
// par le gestionnaire de services, sinon en avant-plan (utile pour `run` lancé
@@ -61,11 +61,11 @@ func Install() error {
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
return err
}
- // Ré-installation : un service AjeanRemote déjà présent tient ajean-remote.exe
+ // Ré-installation : un service LokiRemote déjà présent tient loki-remote.exe
// OUVERT (verrou Windows sur l'image d'un processus en cours) → la copie
// échouerait avec « used by another process » et CreateService avec « déjà
// installé ». On le retire d'abord (best-effort : absent = rien à faire), ce qui
- // arrête le service et libère le fichier, rendant « ajean remote install »
+ // arrête le service et libère le fichier, rendant « loki remote install »
// ré-exécutable tel quel — sans imposer un « uninstall » manuel préalable.
_ = Uninstall()
if abs, _ := filepath.Abs(self); abs != dst {
@@ -80,8 +80,8 @@ func Install() error {
defer m.Disconnect()
s, err := m.CreateService(serviceName, dst, mgr.Config{
StartType: mgr.StartAutomatic,
- DisplayName: "AJEAN — poste distant",
- Description: "Pont léger permettant à votre serveur IA AJEAN d'agir sur ce PC.",
+ DisplayName: "Loki — poste distant",
+ Description: "Pont léger permettant à votre serveur IA Loki d'agir sur ce PC.",
}, "remote", "run")
if err != nil {
return fmt.Errorf("création du service: %w", err)
diff --git a/internal/nodewire/crypto.go b/internal/nodewire/crypto.go
index d565537..18e752c 100644
--- a/internal/nodewire/crypto.go
+++ b/internal/nodewire/crypto.go
@@ -10,7 +10,7 @@ package nodewire
// clé = SHA256(shared || ephPub || agentPub)) — donc l'agent ouvre le sceau
// du poste avec son code existant, sans dupliquer la crypto ;
// - un CANAL authentifié à clés statiques : K = SHA256(ECDH(postePriv,
-// agentPub) || "ajean-node-chan-v1"). Le poste et l'agent le calculent tous
+// agentPub) || "loki-node-chan-v1"). Le poste et l'agent le calculent tous
// les deux ; le relais, qui n'a aucune des deux clés privées, non. Chaque
// message est scellé AES-GCM avec un nonce à compteur (anti-rejeu / anti-
// réordonnancement à l'intérieur d'une connexion).
@@ -129,7 +129,7 @@ func ChannelKey(privHex, peerPubHex string) ([]byte, error) {
}
h := sha256.New()
h.Write(ss)
- h.Write([]byte("ajean-node-chan-v1"))
+ h.Write([]byte("loki-node-chan-v1"))
return h.Sum(nil), nil
}
diff --git a/internal/nodewire/wire.go b/internal/nodewire/wire.go
index 082c75d..f5c5935 100644
--- a/internal/nodewire/wire.go
+++ b/internal/nodewire/wire.go
@@ -1,5 +1,5 @@
-// Package nodewire porte le protocole PARTAGÉ entre le serveur ajean et le
-// client léger « poste distant » (ajean-node). Zéro dépendance hors stdlib :
+// Package nodewire porte le protocole PARTAGÉ entre le serveur loki et le
+// client léger « poste distant » (loki-node). Zéro dépendance hors stdlib :
// c'est ce qui permet au client de rester minuscule (il n'embarque ni l'UI, ni
// llama.cpp, ni bbolt, ni MCP — juste ce fichier + coder/websocket).
//
diff --git a/tools/assemble-ui/main.go b/tools/assemble-ui/main.go
index 0b1ec20..184470d 100644
--- a/tools/assemble-ui/main.go
+++ b/tools/assemble-ui/main.go
@@ -1,12 +1,12 @@
-// assemble-ui reconstruit internal/ajean/ui/index.html à partir des sources
-// internal/ajean/ui/src/ (remplace l'ancien ui/assemble.ps1 — multiplateforme).
+// assemble-ui reconstruit internal/loki/ui/index.html à partir des sources
+// internal/loki/ui/src/ (remplace l'ancien ui/assemble.ps1 — multiplateforme).
//
// index.html est un fichier GÉNÉRÉ mais committé : go:embed (web_server.go) et
-// ajean-app/build-server-ui.ps1 le lisent tel quel. Pour modifier l'UI : éditer
+// loki-app/build-server-ui.ps1 le lisent tel quel. Pour modifier l'UI : éditer
// ui/src/ (index.tmpl.html, styles.css, js/NN-*.js concaténés dans l'ordre
// alphabétique, tout en scope global) puis relancer :
//
-// go generate ./internal/ajean (ou : go run ./tools/assemble-ui)
+// go generate ./internal/loki (ou : go run ./tools/assemble-ui)
//
// Les marqueurs @@CSS@@ / @@JS@@ du template sont remplacés avec LEUR fin de
// ligne (LF ou CRLF) ; chaque source apporte ses propres fins de ligne.
@@ -22,7 +22,7 @@ import (
)
func main() {
- uiDir := "internal/ajean/ui"
+ uiDir := "internal/loki/ui"
if len(os.Args) > 1 {
uiDir = os.Args[1]
}
diff --git a/tools/gen-icon/main.go b/tools/gen-icon/main.go
index 73128ec..6281207 100644
--- a/tools/gen-icon/main.go
+++ b/tools/gen-icon/main.go
@@ -1,8 +1,8 @@
-// gen-icon écrit cmd/ajean/icon.ico à partir de l'icône de marque dessinée dans
-// internal/ajean (sys_brand_icon.go), pour que l'icône du .exe soit toujours la
+// gen-icon écrit cmd/loki/icon.ico à partir de l'icône de marque dessinée dans
+// internal/loki (sys_brand_icon.go), pour que l'icône du .exe soit toujours la
// même que le favicon de l'UI et que celle de la zone de notification.
//
-// Lancé par `go generate ./cmd/ajean`, AVANT goversioninfo qui embarque le .ico
+// Lancé par `go generate ./cmd/loki`, AVANT goversioninfo qui embarque le .ico
// dans les ressources Windows.
package main
@@ -11,7 +11,7 @@ import (
"os"
"strings"
- "github.com/nathaninline/ajean/internal/ajean"
+ "github.com/R0m1k3/Loki/internal/loki"
)
func main() {
@@ -21,13 +21,13 @@ func main() {
}
// Les tailles attendues par l'explorateur Windows, de la barre des tâches à
// l'aperçu en grandes icônes.
- write(out, ajean.BrandICO(16, 32, 48, 64, 128, 256))
+ write(out, loki.BrandICO(16, 32, 48, 64, 128, 256))
// PNG 1024 pour le bundle macOS : la CI en tire le .icns via sips/iconutil.
// Elle partait auparavant du .ico, ce qui suppose que sips sache lire un ICO —
// et l'échec y est masqué par « || true », donc une app Mac sans icône passait
// inaperçue. Un PNG ne laisse aucune place au doute.
- write(strings.TrimSuffix(out, ".ico")+".png", ajean.BrandIconPNG(1024))
+ write(strings.TrimSuffix(out, ".ico")+".png", loki.BrandIconPNG(1024))
}
func write(path string, data []byte) {